power: move power supply drivers to power/supply
authorSebastian Reichel <sre@kernel.org>
Fri, 17 Jun 2016 11:54:32 +0000 (13:54 +0200)
committerSebastian Reichel <sre@kernel.org>
Wed, 10 Aug 2016 23:11:03 +0000 (01:11 +0200)
This moves all power supply drivers from drivers/power/
to drivers/power/supply/. The intention is a cleaner
source tree, since drivers/power/ also contains frameworks
unrelated to power supply, like adaptive voltage scaling.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
156 files changed:
Documentation/power/power_supply_class.txt
MAINTAINERS
drivers/power/88pm860x_battery.c [deleted file]
drivers/power/88pm860x_charger.c [deleted file]
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/ab8500_bmdata.c [deleted file]
drivers/power/ab8500_btemp.c [deleted file]
drivers/power/ab8500_charger.c [deleted file]
drivers/power/ab8500_fg.c [deleted file]
drivers/power/abx500_chargalg.c [deleted file]
drivers/power/act8945a_charger.c [deleted file]
drivers/power/apm_power.c [deleted file]
drivers/power/axp20x_usb_power.c [deleted file]
drivers/power/axp288_charger.c [deleted file]
drivers/power/axp288_fuel_gauge.c [deleted file]
drivers/power/bq2415x_charger.c [deleted file]
drivers/power/bq24190_charger.c [deleted file]
drivers/power/bq24257_charger.c [deleted file]
drivers/power/bq24735-charger.c [deleted file]
drivers/power/bq25890_charger.c [deleted file]
drivers/power/bq27xxx_battery.c [deleted file]
drivers/power/bq27xxx_battery_i2c.c [deleted file]
drivers/power/charger-manager.c [deleted file]
drivers/power/collie_battery.c [deleted file]
drivers/power/da9030_battery.c [deleted file]
drivers/power/da9052-battery.c [deleted file]
drivers/power/da9150-charger.c [deleted file]
drivers/power/da9150-fg.c [deleted file]
drivers/power/ds2760_battery.c [deleted file]
drivers/power/ds2780_battery.c [deleted file]
drivers/power/ds2781_battery.c [deleted file]
drivers/power/ds2782_battery.c [deleted file]
drivers/power/generic-adc-battery.c [deleted file]
drivers/power/goldfish_battery.c [deleted file]
drivers/power/gpio-charger.c [deleted file]
drivers/power/intel_mid_battery.c [deleted file]
drivers/power/ipaq_micro_battery.c [deleted file]
drivers/power/isp1704_charger.c [deleted file]
drivers/power/jz4740-battery.c [deleted file]
drivers/power/lp8727_charger.c [deleted file]
drivers/power/lp8788-charger.c [deleted file]
drivers/power/ltc2941-battery-gauge.c [deleted file]
drivers/power/max14577_charger.c [deleted file]
drivers/power/max17040_battery.c [deleted file]
drivers/power/max17042_battery.c [deleted file]
drivers/power/max77693_charger.c [deleted file]
drivers/power/max8903_charger.c [deleted file]
drivers/power/max8925_power.c [deleted file]
drivers/power/max8997_charger.c [deleted file]
drivers/power/max8998_charger.c [deleted file]
drivers/power/olpc_battery.c [deleted file]
drivers/power/pcf50633-charger.c [deleted file]
drivers/power/pda_power.c [deleted file]
drivers/power/pm2301_charger.c [deleted file]
drivers/power/pm2301_charger.h [deleted file]
drivers/power/pmu_battery.c [deleted file]
drivers/power/power_supply.h [deleted file]
drivers/power/power_supply_core.c [deleted file]
drivers/power/power_supply_leds.c [deleted file]
drivers/power/power_supply_sysfs.c [deleted file]
drivers/power/qcom_smbb.c [deleted file]
drivers/power/rt5033_battery.c [deleted file]
drivers/power/rt9455_charger.c [deleted file]
drivers/power/rx51_battery.c [deleted file]
drivers/power/s3c_adc_battery.c [deleted file]
drivers/power/sbs-battery.c [deleted file]
drivers/power/smb347-charger.c [deleted file]
drivers/power/supply/88pm860x_battery.c [new file with mode: 0644]
drivers/power/supply/88pm860x_charger.c [new file with mode: 0644]
drivers/power/supply/Kconfig [new file with mode: 0644]
drivers/power/supply/Makefile [new file with mode: 0644]
drivers/power/supply/ab8500_bmdata.c [new file with mode: 0644]
drivers/power/supply/ab8500_btemp.c [new file with mode: 0644]
drivers/power/supply/ab8500_charger.c [new file with mode: 0644]
drivers/power/supply/ab8500_fg.c [new file with mode: 0644]
drivers/power/supply/abx500_chargalg.c [new file with mode: 0644]
drivers/power/supply/act8945a_charger.c [new file with mode: 0644]
drivers/power/supply/apm_power.c [new file with mode: 0644]
drivers/power/supply/axp20x_usb_power.c [new file with mode: 0644]
drivers/power/supply/axp288_charger.c [new file with mode: 0644]
drivers/power/supply/axp288_fuel_gauge.c [new file with mode: 0644]
drivers/power/supply/bq2415x_charger.c [new file with mode: 0644]
drivers/power/supply/bq24190_charger.c [new file with mode: 0644]
drivers/power/supply/bq24257_charger.c [new file with mode: 0644]
drivers/power/supply/bq24735-charger.c [new file with mode: 0644]
drivers/power/supply/bq25890_charger.c [new file with mode: 0644]
drivers/power/supply/bq27xxx_battery.c [new file with mode: 0644]
drivers/power/supply/bq27xxx_battery_i2c.c [new file with mode: 0644]
drivers/power/supply/charger-manager.c [new file with mode: 0644]
drivers/power/supply/collie_battery.c [new file with mode: 0644]
drivers/power/supply/da9030_battery.c [new file with mode: 0644]
drivers/power/supply/da9052-battery.c [new file with mode: 0644]
drivers/power/supply/da9150-charger.c [new file with mode: 0644]
drivers/power/supply/da9150-fg.c [new file with mode: 0644]
drivers/power/supply/ds2760_battery.c [new file with mode: 0644]
drivers/power/supply/ds2780_battery.c [new file with mode: 0644]
drivers/power/supply/ds2781_battery.c [new file with mode: 0644]
drivers/power/supply/ds2782_battery.c [new file with mode: 0644]
drivers/power/supply/generic-adc-battery.c [new file with mode: 0644]
drivers/power/supply/goldfish_battery.c [new file with mode: 0644]
drivers/power/supply/gpio-charger.c [new file with mode: 0644]
drivers/power/supply/intel_mid_battery.c [new file with mode: 0644]
drivers/power/supply/ipaq_micro_battery.c [new file with mode: 0644]
drivers/power/supply/isp1704_charger.c [new file with mode: 0644]
drivers/power/supply/jz4740-battery.c [new file with mode: 0644]
drivers/power/supply/lp8727_charger.c [new file with mode: 0644]
drivers/power/supply/lp8788-charger.c [new file with mode: 0644]
drivers/power/supply/ltc2941-battery-gauge.c [new file with mode: 0644]
drivers/power/supply/max14577_charger.c [new file with mode: 0644]
drivers/power/supply/max17040_battery.c [new file with mode: 0644]
drivers/power/supply/max17042_battery.c [new file with mode: 0644]
drivers/power/supply/max77693_charger.c [new file with mode: 0644]
drivers/power/supply/max8903_charger.c [new file with mode: 0644]
drivers/power/supply/max8925_power.c [new file with mode: 0644]
drivers/power/supply/max8997_charger.c [new file with mode: 0644]
drivers/power/supply/max8998_charger.c [new file with mode: 0644]
drivers/power/supply/olpc_battery.c [new file with mode: 0644]
drivers/power/supply/pcf50633-charger.c [new file with mode: 0644]
drivers/power/supply/pda_power.c [new file with mode: 0644]
drivers/power/supply/pm2301_charger.c [new file with mode: 0644]
drivers/power/supply/pm2301_charger.h [new file with mode: 0644]
drivers/power/supply/pmu_battery.c [new file with mode: 0644]
drivers/power/supply/power_supply.h [new file with mode: 0644]
drivers/power/supply/power_supply_core.c [new file with mode: 0644]
drivers/power/supply/power_supply_leds.c [new file with mode: 0644]
drivers/power/supply/power_supply_sysfs.c [new file with mode: 0644]
drivers/power/supply/qcom_smbb.c [new file with mode: 0644]
drivers/power/supply/rt5033_battery.c [new file with mode: 0644]
drivers/power/supply/rt9455_charger.c [new file with mode: 0644]
drivers/power/supply/rx51_battery.c [new file with mode: 0644]
drivers/power/supply/s3c_adc_battery.c [new file with mode: 0644]
drivers/power/supply/sbs-battery.c [new file with mode: 0644]
drivers/power/supply/smb347-charger.c [new file with mode: 0644]
drivers/power/supply/test_power.c [new file with mode: 0644]
drivers/power/supply/tosa_battery.c [new file with mode: 0644]
drivers/power/supply/tps65090-charger.c [new file with mode: 0644]
drivers/power/supply/tps65217_charger.c [new file with mode: 0644]
drivers/power/supply/twl4030_charger.c [new file with mode: 0644]
drivers/power/supply/twl4030_madc_battery.c [new file with mode: 0644]
drivers/power/supply/wm831x_backup.c [new file with mode: 0644]
drivers/power/supply/wm831x_power.c [new file with mode: 0644]
drivers/power/supply/wm8350_power.c [new file with mode: 0644]
drivers/power/supply/wm97xx_battery.c [new file with mode: 0644]
drivers/power/supply/z2_battery.c [new file with mode: 0644]
drivers/power/test_power.c [deleted file]
drivers/power/tosa_battery.c [deleted file]
drivers/power/tps65090-charger.c [deleted file]
drivers/power/tps65217_charger.c [deleted file]
drivers/power/twl4030_charger.c [deleted file]
drivers/power/twl4030_madc_battery.c [deleted file]
drivers/power/wm831x_backup.c [deleted file]
drivers/power/wm831x_power.c [deleted file]
drivers/power/wm8350_power.c [deleted file]
drivers/power/wm97xx_battery.c [deleted file]
drivers/power/z2_battery.c [deleted file]

index 82dacc06e355c1624ca66e5668448f1a767dbfa9..0c72588bd967f22f4e342f07bd3cd1d836ed9282 100644 (file)
@@ -39,8 +39,8 @@ kind of power supply, and can process/present them to a user in consistent
 manner. Results for different power supplies and machines are also directly
 comparable.
 
-See drivers/power/ds2760_battery.c and drivers/power/pda_power.c for the
-example how to declare and handle attributes.
+See drivers/power/supply/ds2760_battery.c and drivers/power/supply/pda_power.c
+for the example how to declare and handle attributes.
 
 
 Units
index 20bb1d00098c70dacad7a9c778087f9319b0c5c6..ca66b4140c50227c829a729a9b90c3d02049ffcc 100644 (file)
@@ -3751,8 +3751,8 @@ F:        drivers/leds/leds-da90??.c
 F:     drivers/mfd/da903x.c
 F:     drivers/mfd/da90??-*.c
 F:     drivers/mfd/da91??-*.c
-F:     drivers/power/da9052-battery.c
-F:     drivers/power/da91??-*.c
+F:     drivers/power/supply/da9052-battery.c
+F:     drivers/power/supply/da91??-*.c
 F:     drivers/regulator/da903x.c
 F:     drivers/regulator/da9???-regulator.[ch]
 F:     drivers/rtc/rtc-da90??.c
@@ -7451,8 +7451,8 @@ MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
 M:     Krzysztof Kozlowski <k.kozlowski@samsung.com>
 L:     linux-pm@vger.kernel.org
 S:     Supported
-F:     drivers/power/max14577_charger.c
-F:     drivers/power/max77693_charger.c
+F:     drivers/power/supply/max14577_charger.c
+F:     drivers/power/supply/max77693_charger.c
 
 MAXIM MAX77802 MULTIFUNCTION PMIC DEVICE DRIVERS
 M:     Javier Martinez Canillas <javier@osg.samsung.com>
@@ -8323,11 +8323,11 @@ R:      Pali Rohár <pali.rohar@gmail.com>
 F:     include/linux/power/bq2415x_charger.h
 F:     include/linux/power/bq27xxx_battery.h
 F:     include/linux/power/isp1704_charger.h
-F:     drivers/power/bq2415x_charger.c
-F:     drivers/power/bq27xxx_battery.c
-F:     drivers/power/bq27xxx_battery_i2c.c
-F:     drivers/power/isp1704_charger.c
-F:     drivers/power/rx51_battery.c
+F:     drivers/power/supply/bq2415x_charger.c
+F:     drivers/power/supply/bq27xxx_battery.c
+F:     drivers/power/supply/bq27xxx_battery_i2c.c
+F:     drivers/power/supply/isp1704_charger.c
+F:     drivers/power/supply/rx51_battery.c
 
 NTB DRIVER CORE
 M:     Jon Mason <jdmason@kudzu.us>
@@ -9334,8 +9334,7 @@ S:        Maintained
 F:     Documentation/devicetree/bindings/power/
 F:     Documentation/devicetree/bindings/power_supply/
 F:     include/linux/power_supply.h
-F:     drivers/power/
-X:     drivers/power/avs/
+F:     drivers/power/supply/
 
 POWER STATE COORDINATION INTERFACE (PSCI)
 M:     Mark Rutland <mark.rutland@arm.com>
@@ -10322,8 +10321,8 @@ F:      drivers/thunderbolt/
 TI BQ27XXX POWER SUPPLY DRIVER
 R:     Andrew F. Davis <afd@ti.com>
 F:     include/linux/power/bq27xxx_battery.h
-F:     drivers/power/bq27xxx_battery.c
-F:     drivers/power/bq27xxx_battery_i2c.c
+F:     drivers/power/supply/bq27xxx_battery.c
+F:     drivers/power/supply/bq27xxx_battery_i2c.c
 
 TIMEKEEPING, CLOCKSOURCE CORE, NTP, ALARMTIMER
 M:     John Stultz <john.stultz@linaro.org>
@@ -11329,6 +11328,16 @@ T:     git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
 S:     Supported
 F:     drivers/mfd/syscon.c
 
+SYSTEM RESET/SHUTDOWN DRIVERS
+M:     Sebastian Reichel <sre@kernel.org>
+M:     Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+M:     David Woodhouse <dwmw2@infradead.org>
+L:     linux-pm@vger.kernel.org
+T:     git git://git.infradead.org/battery-2.6.git
+S:     Maintained
+F:     Documentation/devicetree/bindings/power/reset/
+F:     drivers/power/reset/
+
 SYSV FILESYSTEM
 M:     Christoph Hellwig <hch@infradead.org>
 S:     Maintained
@@ -11677,7 +11686,7 @@ F:      include/linux/platform_data/lp855x.h
 TI LP8727 CHARGER DRIVER
 M:     Milo Kim <milo.kim@ti.com>
 S:     Maintained
-F:     drivers/power/lp8727_charger.c
+F:     drivers/power/supply/lp8727_charger.c
 F:     include/linux/platform_data/lp8727.h
 
 TI LP8788 MFD DRIVER
@@ -11686,7 +11695,7 @@ S:      Maintained
 F:     drivers/iio/adc/lp8788_adc.c
 F:     drivers/leds/leds-lp8788.c
 F:     drivers/mfd/lp8788*.c
-F:     drivers/power/lp8788-charger.c
+F:     drivers/power/supply/lp8788-charger.c
 F:     drivers/regulator/lp8788-*.c
 F:     include/linux/mfd/lp8788*.h
 
@@ -12762,7 +12771,7 @@ F:      drivers/input/touchscreen/wm97*.c
 F:     drivers/mfd/arizona*
 F:     drivers/mfd/wm*.c
 F:     drivers/mfd/cs47l24*
-F:     drivers/power/wm83*.c
+F:     drivers/power/supply/wm83*.c
 F:     drivers/rtc/rtc-wm83*.c
 F:     drivers/regulator/wm8*.c
 F:     drivers/video/backlight/wm83*_bl.c
diff --git a/drivers/power/88pm860x_battery.c b/drivers/power/88pm860x_battery.c
deleted file mode 100644 (file)
index 63c57dc..0000000
+++ /dev/null
@@ -1,1021 +0,0 @@
-/*
- * Battery driver for Marvell 88PM860x PMIC
- *
- * Copyright (c) 2012 Marvell International Ltd.
- * Author:     Jett Zhou <jtzhou@marvell.com>
- *             Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/string.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/88pm860x.h>
-#include <linux/delay.h>
-
-/* bit definitions of Status Query Interface 2 */
-#define STATUS2_CHG                    (1 << 2)
-#define STATUS2_BAT                    (1 << 3)
-#define STATUS2_VBUS                   (1 << 4)
-
-/* bit definitions of Measurement Enable 1 Register */
-#define MEAS1_TINT                     (1 << 3)
-#define MEAS1_GP1                      (1 << 5)
-
-/* bit definitions of Measurement Enable 3 Register */
-#define MEAS3_IBAT                     (1 << 0)
-#define MEAS3_BAT_DET                  (1 << 1)
-#define MEAS3_CC                       (1 << 2)
-
-/* bit definitions of Measurement Off Time Register */
-#define MEAS_OFF_SLEEP_EN              (1 << 1)
-
-/* bit definitions of GPADC Bias Current 2 Register */
-#define GPBIAS2_GPADC1_SET             (2 << 4)
-/* GPADC1 Bias Current value in uA unit */
-#define GPBIAS2_GPADC1_UA              ((GPBIAS2_GPADC1_SET >> 4) * 5 + 1)
-
-/* bit definitions of GPADC Misc 1 Register */
-#define GPMISC1_GPADC_EN               (1 << 0)
-
-/* bit definitions of Charger Control 6 Register */
-#define CC6_BAT_DET_GPADC1             1
-
-/* bit definitions of Coulomb Counter Reading Register */
-#define CCNT_AVG_SEL                   (4 << 3)
-
-/* bit definitions of RTC miscellaneous Register1 */
-#define RTC_SOC_5LSB           (0x1F << 3)
-
-/* bit definitions of RTC Register1 */
-#define RTC_SOC_3MSB           (0x7)
-
-/* bit definitions of Power up Log register */
-#define BAT_WU_LOG                     (1<<6)
-
-/* coulomb counter index */
-#define CCNT_POS1                      0
-#define CCNT_POS2                      1
-#define CCNT_NEG1                      2
-#define CCNT_NEG2                      3
-#define CCNT_SPOS                      4
-#define CCNT_SNEG                      5
-
-/* OCV -- Open Circuit Voltage */
-#define OCV_MODE_ACTIVE                        0
-#define OCV_MODE_SLEEP                 1
-
-/* Vbat range of CC for measuring Rbat */
-#define LOW_BAT_THRESHOLD              3600
-#define VBATT_RESISTOR_MIN             3800
-#define VBATT_RESISTOR_MAX             4100
-
-/* TBAT for batt, TINT for chip itself */
-#define PM860X_TEMP_TINT               (0)
-#define PM860X_TEMP_TBAT               (1)
-
-/*
- * Battery temperature based on NTC resistor, defined
- * corresponding resistor value  -- Ohm / C degeree.
- */
-#define TBAT_NEG_25D           127773  /* -25 */
-#define TBAT_NEG_10D           54564   /* -10 */
-#define TBAT_0D                        32330   /* 0 */
-#define TBAT_10D               19785   /* 10 */
-#define TBAT_20D               12468   /* 20 */
-#define TBAT_30D               8072    /* 30 */
-#define TBAT_40D               5356    /* 40 */
-
-struct pm860x_battery_info {
-       struct pm860x_chip *chip;
-       struct i2c_client *i2c;
-       struct device *dev;
-
-       struct power_supply *battery;
-       struct mutex lock;
-       int status;
-       int irq_cc;
-       int irq_batt;
-       int max_capacity;
-       int resistor;           /* Battery Internal Resistor */
-       int last_capacity;
-       int start_soc;
-       unsigned present:1;
-       unsigned temp_type:1;   /* TINT or TBAT */
-};
-
-struct ccnt {
-       unsigned long long int pos;
-       unsigned long long int neg;
-       unsigned int spos;
-       unsigned int sneg;
-
-       int total_chg;          /* mAh(3.6C) */
-       int total_dischg;       /* mAh(3.6C) */
-};
-
-/*
- * State of Charge.
- * The first number is mAh(=3.6C), and the second number is percent point.
- */
-static int array_soc[][2] = {
-       {4170, 100}, {4154, 99}, {4136, 98}, {4122, 97}, {4107, 96},
-       {4102, 95}, {4088, 94}, {4081, 93}, {4070, 92}, {4060, 91},
-       {4053, 90}, {4044, 89}, {4035, 88}, {4028, 87}, {4019, 86},
-       {4013, 85}, {4006, 84}, {3995, 83}, {3987, 82}, {3982, 81},
-       {3976, 80}, {3968, 79}, {3962, 78}, {3954, 77}, {3946, 76},
-       {3941, 75}, {3934, 74}, {3929, 73}, {3922, 72}, {3916, 71},
-       {3910, 70}, {3904, 69}, {3898, 68}, {3892, 67}, {3887, 66},
-       {3880, 65}, {3874, 64}, {3868, 63}, {3862, 62}, {3854, 61},
-       {3849, 60}, {3843, 59}, {3840, 58}, {3833, 57}, {3829, 56},
-       {3824, 55}, {3818, 54}, {3815, 53}, {3810, 52}, {3808, 51},
-       {3804, 50}, {3801, 49}, {3798, 48}, {3796, 47}, {3792, 46},
-       {3789, 45}, {3785, 44}, {3784, 43}, {3782, 42}, {3780, 41},
-       {3777, 40}, {3776, 39}, {3774, 38}, {3772, 37}, {3771, 36},
-       {3769, 35}, {3768, 34}, {3764, 33}, {3763, 32}, {3760, 31},
-       {3760, 30}, {3754, 29}, {3750, 28}, {3749, 27}, {3744, 26},
-       {3740, 25}, {3734, 24}, {3732, 23}, {3728, 22}, {3726, 21},
-       {3720, 20}, {3716, 19}, {3709, 18}, {3703, 17}, {3698, 16},
-       {3692, 15}, {3683, 14}, {3675, 13}, {3670, 12}, {3665, 11},
-       {3661, 10}, {3649, 9}, {3637, 8}, {3622, 7}, {3609, 6},
-       {3580, 5}, {3558, 4}, {3540, 3}, {3510, 2}, {3429, 1},
-};
-
-static struct ccnt ccnt_data;
-
-/*
- * register 1 bit[7:0] -- bit[11:4] of measured value of voltage
- * register 0 bit[3:0] -- bit[3:0] of measured value of voltage
- */
-static int measure_12bit_voltage(struct pm860x_battery_info *info,
-                                int offset, int *data)
-{
-       unsigned char buf[2];
-       int ret;
-
-       ret = pm860x_bulk_read(info->i2c, offset, 2, buf);
-       if (ret < 0)
-               return ret;
-
-       *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
-       /* V_MEAS(mV) = data * 1.8 * 1000 / (2^12) */
-       *data = ((*data & 0xfff) * 9 * 25) >> 9;
-       return 0;
-}
-
-static int measure_vbatt(struct pm860x_battery_info *info, int state,
-                        int *data)
-{
-       unsigned char buf[5];
-       int ret;
-
-       switch (state) {
-       case OCV_MODE_ACTIVE:
-               ret = measure_12bit_voltage(info, PM8607_VBAT_MEAS1, data);
-               if (ret)
-                       return ret;
-               /* V_BATT_MEAS(mV) = value * 3 * 1.8 * 1000 / (2^12) */
-               *data *= 3;
-               break;
-       case OCV_MODE_SLEEP:
-               /*
-                * voltage value of VBATT in sleep mode is saved in different
-                * registers.
-                * bit[11:10] -- bit[7:6] of LDO9(0x18)
-                * bit[9:8] -- bit[7:6] of LDO8(0x17)
-                * bit[7:6] -- bit[7:6] of LDO7(0x16)
-                * bit[5:4] -- bit[7:6] of LDO6(0x15)
-                * bit[3:0] -- bit[7:4] of LDO5(0x14)
-                */
-               ret = pm860x_bulk_read(info->i2c, PM8607_LDO5, 5, buf);
-               if (ret < 0)
-                       return ret;
-               ret = ((buf[4] >> 6) << 10) | ((buf[3] >> 6) << 8)
-                   | ((buf[2] >> 6) << 6) | ((buf[1] >> 6) << 4)
-                   | (buf[0] >> 4);
-               /* V_BATT_MEAS(mV) = data * 3 * 1.8 * 1000 / (2^12) */
-               *data = ((*data & 0xff) * 27 * 25) >> 9;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-/*
- * Return value is signed data.
- * Negative value means discharging, and positive value means charging.
- */
-static int measure_current(struct pm860x_battery_info *info, int *data)
-{
-       unsigned char buf[2];
-       short s;
-       int ret;
-
-       ret = pm860x_bulk_read(info->i2c, PM8607_IBAT_MEAS1, 2, buf);
-       if (ret < 0)
-               return ret;
-
-       s = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
-       /* current(mA) = value * 0.125 */
-       *data = s >> 3;
-       return 0;
-}
-
-static int set_charger_current(struct pm860x_battery_info *info, int data,
-                              int *old)
-{
-       int ret;
-
-       if (data < 50 || data > 1600 || !old)
-               return -EINVAL;
-
-       data = ((data - 50) / 50) & 0x1f;
-       *old = pm860x_reg_read(info->i2c, PM8607_CHG_CTRL2);
-       *old = (*old & 0x1f) * 50 + 50;
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, data);
-       if (ret < 0)
-               return ret;
-       return 0;
-}
-
-static int read_ccnt(struct pm860x_battery_info *info, int offset,
-                    int *ccnt)
-{
-       unsigned char buf[2];
-       int ret;
-
-       ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7, offset & 7);
-       if (ret < 0)
-               goto out;
-       ret = pm860x_bulk_read(info->i2c, PM8607_CCNT_MEAS1, 2, buf);
-       if (ret < 0)
-               goto out;
-       *ccnt = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
-       return 0;
-out:
-       return ret;
-}
-
-static int calc_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt)
-{
-       unsigned int sum;
-       int ret;
-       int data;
-
-       ret = read_ccnt(info, CCNT_POS1, &data);
-       if (ret)
-               goto out;
-       sum = data & 0xffff;
-       ret = read_ccnt(info, CCNT_POS2, &data);
-       if (ret)
-               goto out;
-       sum |= (data & 0xffff) << 16;
-       ccnt->pos += sum;
-
-       ret = read_ccnt(info, CCNT_NEG1, &data);
-       if (ret)
-               goto out;
-       sum = data & 0xffff;
-       ret = read_ccnt(info, CCNT_NEG2, &data);
-       if (ret)
-               goto out;
-       sum |= (data & 0xffff) << 16;
-       sum = ~sum + 1;         /* since it's negative */
-       ccnt->neg += sum;
-
-       ret = read_ccnt(info, CCNT_SPOS, &data);
-       if (ret)
-               goto out;
-       ccnt->spos += data;
-       ret = read_ccnt(info, CCNT_SNEG, &data);
-       if (ret)
-               goto out;
-
-       /*
-        * charge(mAh)  = count * 1.6984 * 1e(-8)
-        *              = count * 16984 * 1.024 * 1.024 * 1.024 / (2 ^ 40)
-        *              = count * 18236 / (2 ^ 40)
-        */
-       ccnt->total_chg = (int) ((ccnt->pos * 18236) >> 40);
-       ccnt->total_dischg = (int) ((ccnt->neg * 18236) >> 40);
-       return 0;
-out:
-       return ret;
-}
-
-static int clear_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt)
-{
-       int data;
-
-       memset(ccnt, 0, sizeof(*ccnt));
-       /* read to clear ccnt */
-       read_ccnt(info, CCNT_POS1, &data);
-       read_ccnt(info, CCNT_POS2, &data);
-       read_ccnt(info, CCNT_NEG1, &data);
-       read_ccnt(info, CCNT_NEG2, &data);
-       read_ccnt(info, CCNT_SPOS, &data);
-       read_ccnt(info, CCNT_SNEG, &data);
-       return 0;
-}
-
-/* Calculate Open Circuit Voltage */
-static int calc_ocv(struct pm860x_battery_info *info, int *ocv)
-{
-       int ret;
-       int i;
-       int data;
-       int vbatt_avg;
-       int vbatt_sum;
-       int ibatt_avg;
-       int ibatt_sum;
-
-       if (!ocv)
-               return -EINVAL;
-
-       for (i = 0, ibatt_sum = 0, vbatt_sum = 0; i < 10; i++) {
-               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
-               if (ret)
-                       goto out;
-               vbatt_sum += data;
-               ret = measure_current(info, &data);
-               if (ret)
-                       goto out;
-               ibatt_sum += data;
-       }
-       vbatt_avg = vbatt_sum / 10;
-       ibatt_avg = ibatt_sum / 10;
-
-       mutex_lock(&info->lock);
-       if (info->present)
-               *ocv = vbatt_avg - ibatt_avg * info->resistor / 1000;
-       else
-               *ocv = vbatt_avg;
-       mutex_unlock(&info->lock);
-       dev_dbg(info->dev, "VBAT average:%d, OCV:%d\n", vbatt_avg, *ocv);
-       return 0;
-out:
-       return ret;
-}
-
-/* Calculate State of Charge (percent points) */
-static int calc_soc(struct pm860x_battery_info *info, int state, int *soc)
-{
-       int i;
-       int ocv;
-       int count;
-       int ret = -EINVAL;
-
-       if (!soc)
-               return -EINVAL;
-
-       switch (state) {
-       case OCV_MODE_ACTIVE:
-               ret = calc_ocv(info, &ocv);
-               break;
-       case OCV_MODE_SLEEP:
-               ret = measure_vbatt(info, OCV_MODE_SLEEP, &ocv);
-               break;
-       }
-       if (ret)
-               return ret;
-
-       count = ARRAY_SIZE(array_soc);
-       if (ocv < array_soc[count - 1][0]) {
-               *soc = 0;
-               return 0;
-       }
-
-       for (i = 0; i < count; i++) {
-               if (ocv >= array_soc[i][0]) {
-                       *soc = array_soc[i][1];
-                       break;
-               }
-       }
-       return 0;
-}
-
-static irqreturn_t pm860x_coulomb_handler(int irq, void *data)
-{
-       struct pm860x_battery_info *info = data;
-
-       calc_ccnt(info, &ccnt_data);
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t pm860x_batt_handler(int irq, void *data)
-{
-       struct pm860x_battery_info *info = data;
-       int ret;
-
-       mutex_lock(&info->lock);
-       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
-       if (ret & STATUS2_BAT) {
-               info->present = 1;
-               info->temp_type = PM860X_TEMP_TBAT;
-       } else {
-               info->present = 0;
-               info->temp_type = PM860X_TEMP_TINT;
-       }
-       mutex_unlock(&info->lock);
-       /* clear ccnt since battery is attached or dettached */
-       clear_ccnt(info, &ccnt_data);
-       return IRQ_HANDLED;
-}
-
-static void pm860x_init_battery(struct pm860x_battery_info *info)
-{
-       unsigned char buf[2];
-       int ret;
-       int data;
-       int bat_remove;
-       int soc;
-
-       /* measure enable on GPADC1 */
-       data = MEAS1_GP1;
-       if (info->temp_type == PM860X_TEMP_TINT)
-               data |= MEAS1_TINT;
-       ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN1, data, data);
-       if (ret)
-               goto out;
-
-       /* measure enable on IBAT, BAT_DET, CC. IBAT is depend on CC. */
-       data = MEAS3_IBAT | MEAS3_BAT_DET | MEAS3_CC;
-       ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN3, data, data);
-       if (ret)
-               goto out;
-
-       /* measure disable CC in sleep time  */
-       ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME1, 0x82);
-       if (ret)
-               goto out;
-       ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME2, 0x6c);
-       if (ret)
-               goto out;
-
-       /* enable GPADC */
-       ret = pm860x_set_bits(info->i2c, PM8607_GPADC_MISC1,
-                           GPMISC1_GPADC_EN, GPMISC1_GPADC_EN);
-       if (ret < 0)
-               goto out;
-
-       /* detect battery via GPADC1 */
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
-                           CC6_BAT_DET_GPADC1, CC6_BAT_DET_GPADC1);
-       if (ret < 0)
-               goto out;
-
-       ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7 << 3,
-                             CCNT_AVG_SEL);
-       if (ret < 0)
-               goto out;
-
-       /* set GPADC1 bias */
-       ret = pm860x_set_bits(info->i2c, PM8607_GP_BIAS2, 0xF << 4,
-                             GPBIAS2_GPADC1_SET);
-       if (ret < 0)
-               goto out;
-
-       /* check whether battery present) */
-       mutex_lock(&info->lock);
-       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
-       if (ret < 0) {
-               mutex_unlock(&info->lock);
-               goto out;
-       }
-       if (ret & STATUS2_BAT) {
-               info->present = 1;
-               info->temp_type = PM860X_TEMP_TBAT;
-       } else {
-               info->present = 0;
-               info->temp_type = PM860X_TEMP_TINT;
-       }
-       mutex_unlock(&info->lock);
-
-       calc_soc(info, OCV_MODE_ACTIVE, &soc);
-
-       data = pm860x_reg_read(info->i2c, PM8607_POWER_UP_LOG);
-       bat_remove = data & BAT_WU_LOG;
-
-       dev_dbg(info->dev, "battery wake up? %s\n",
-               bat_remove != 0 ? "yes" : "no");
-
-       /* restore SOC from RTC domain register */
-       if (bat_remove == 0) {
-               buf[0] = pm860x_reg_read(info->i2c, PM8607_RTC_MISC2);
-               buf[1] = pm860x_reg_read(info->i2c, PM8607_RTC1);
-               data = ((buf[1] & 0x3) << 5) | ((buf[0] >> 3) & 0x1F);
-               if (data > soc + 15)
-                       info->start_soc = soc;
-               else if (data < soc - 15)
-                       info->start_soc = soc;
-               else
-                       info->start_soc = data;
-               dev_dbg(info->dev, "soc_rtc %d, soc_ocv :%d\n", data, soc);
-       } else {
-               pm860x_set_bits(info->i2c, PM8607_POWER_UP_LOG,
-                               BAT_WU_LOG, BAT_WU_LOG);
-               info->start_soc = soc;
-       }
-       info->last_capacity = info->start_soc;
-       dev_dbg(info->dev, "init soc : %d\n", info->last_capacity);
-out:
-       return;
-}
-
-static void set_temp_threshold(struct pm860x_battery_info *info,
-                              int min, int max)
-{
-       int data;
-
-       /* (tmp << 8) / 1800 */
-       if (min <= 0)
-               data = 0;
-       else
-               data = (min << 8) / 1800;
-       pm860x_reg_write(info->i2c, PM8607_GPADC1_HIGHTH, data);
-       dev_dbg(info->dev, "TEMP_HIGHTH : min: %d, 0x%x\n", min, data);
-
-       if (max <= 0)
-               data = 0xff;
-       else
-               data = (max << 8) / 1800;
-       pm860x_reg_write(info->i2c, PM8607_GPADC1_LOWTH, data);
-       dev_dbg(info->dev, "TEMP_LOWTH:max : %d, 0x%x\n", max, data);
-}
-
-static int measure_temp(struct pm860x_battery_info *info, int *data)
-{
-       int ret;
-       int temp;
-       int min;
-       int max;
-
-       if (info->temp_type == PM860X_TEMP_TINT) {
-               ret = measure_12bit_voltage(info, PM8607_TINT_MEAS1, data);
-               if (ret)
-                       return ret;
-               *data = (*data - 884) * 1000 / 3611;
-       } else {
-               ret = measure_12bit_voltage(info, PM8607_GPADC1_MEAS1, data);
-               if (ret)
-                       return ret;
-               /* meausered Vtbat(mV) / Ibias_current(11uA)*/
-               *data = (*data * 1000) / GPBIAS2_GPADC1_UA;
-
-               if (*data > TBAT_NEG_25D) {
-                       temp = -30;     /* over cold , suppose -30 roughly */
-                       max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
-                       set_temp_threshold(info, 0, max);
-               } else if (*data > TBAT_NEG_10D) {
-                       temp = -15;     /* -15 degree, code */
-                       max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
-                       set_temp_threshold(info, 0, max);
-               } else if (*data > TBAT_0D) {
-                       temp = -5;      /* -5 degree */
-                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
-                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
-                       set_temp_threshold(info, min, max);
-               } else if (*data > TBAT_10D) {
-                       temp = 5;       /* in range of (0, 10) */
-                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
-                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
-                       set_temp_threshold(info, min, max);
-               } else if (*data > TBAT_20D) {
-                       temp = 15;      /* in range of (10, 20) */
-                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
-                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
-                       set_temp_threshold(info, min, max);
-               } else if (*data > TBAT_30D) {
-                       temp = 25;      /* in range of (20, 30) */
-                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
-                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
-                       set_temp_threshold(info, min, max);
-               } else if (*data > TBAT_40D) {
-                       temp = 35;      /* in range of (30, 40) */
-                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
-                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
-                       set_temp_threshold(info, min, max);
-               } else {
-                       min = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
-                       set_temp_threshold(info, min, 0);
-                       temp = 45;      /* over heat ,suppose 45 roughly */
-               }
-
-               dev_dbg(info->dev, "temp_C:%d C,temp_mv:%d mv\n", temp, *data);
-               *data = temp;
-       }
-       return 0;
-}
-
-static int calc_resistor(struct pm860x_battery_info *info)
-{
-       int vbatt_sum1;
-       int vbatt_sum2;
-       int chg_current;
-       int ibatt_sum1;
-       int ibatt_sum2;
-       int data;
-       int ret;
-       int i;
-
-       ret = measure_current(info, &data);
-       /* make sure that charging is launched by data > 0 */
-       if (ret || data < 0)
-               goto out;
-
-       ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
-       if (ret)
-               goto out;
-       /* calculate resistor only in CC charge mode */
-       if (data < VBATT_RESISTOR_MIN || data > VBATT_RESISTOR_MAX)
-               goto out;
-
-       /* current is saved */
-       if (set_charger_current(info, 500, &chg_current))
-               goto out;
-
-       /*
-        * set charge current as 500mA, wait about 500ms till charging
-        * process is launched and stable with the newer charging current.
-        */
-       msleep(500);
-
-       for (i = 0, vbatt_sum1 = 0, ibatt_sum1 = 0; i < 10; i++) {
-               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
-               if (ret)
-                       goto out_meas;
-               vbatt_sum1 += data;
-               ret = measure_current(info, &data);
-               if (ret)
-                       goto out_meas;
-
-               if (data < 0)
-                       ibatt_sum1 = ibatt_sum1 - data; /* discharging */
-               else
-                       ibatt_sum1 = ibatt_sum1 + data; /* charging */
-       }
-
-       if (set_charger_current(info, 100, &ret))
-               goto out_meas;
-       /*
-        * set charge current as 100mA, wait about 500ms till charging
-        * process is launched and stable with the newer charging current.
-        */
-       msleep(500);
-
-       for (i = 0, vbatt_sum2 = 0, ibatt_sum2 = 0; i < 10; i++) {
-               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
-               if (ret)
-                       goto out_meas;
-               vbatt_sum2 += data;
-               ret = measure_current(info, &data);
-               if (ret)
-                       goto out_meas;
-
-               if (data < 0)
-                       ibatt_sum2 = ibatt_sum2 - data; /* discharging */
-               else
-                       ibatt_sum2 = ibatt_sum2 + data; /* charging */
-       }
-
-       /* restore current setting */
-       if (set_charger_current(info, chg_current, &ret))
-               goto out_meas;
-
-       if ((vbatt_sum1 > vbatt_sum2) && (ibatt_sum1 > ibatt_sum2) &&
-                       (ibatt_sum2 > 0)) {
-               /* calculate resistor in discharging case */
-               data = 1000 * (vbatt_sum1 - vbatt_sum2)
-                   / (ibatt_sum1 - ibatt_sum2);
-               if ((data - info->resistor > 0) &&
-                               (data - info->resistor < info->resistor))
-                       info->resistor = data;
-               if ((info->resistor - data > 0) &&
-                               (info->resistor - data < data))
-                       info->resistor = data;
-       }
-       return 0;
-
-out_meas:
-       set_charger_current(info, chg_current, &ret);
-out:
-       return -EINVAL;
-}
-
-static int calc_capacity(struct pm860x_battery_info *info, int *cap)
-{
-       int ret;
-       int data;
-       int ibat;
-       int cap_ocv = 0;
-       int cap_cc = 0;
-
-       ret = calc_ccnt(info, &ccnt_data);
-       if (ret)
-               goto out;
-soc:
-       data = info->max_capacity * info->start_soc / 100;
-       if (ccnt_data.total_dischg - ccnt_data.total_chg <= data) {
-               cap_cc =
-                   data + ccnt_data.total_chg - ccnt_data.total_dischg;
-       } else {
-               clear_ccnt(info, &ccnt_data);
-               calc_soc(info, OCV_MODE_ACTIVE, &info->start_soc);
-               dev_dbg(info->dev, "restart soc = %d !\n",
-                       info->start_soc);
-               goto soc;
-       }
-
-       cap_cc = cap_cc * 100 / info->max_capacity;
-       if (cap_cc < 0)
-               cap_cc = 0;
-       else if (cap_cc > 100)
-               cap_cc = 100;
-
-       dev_dbg(info->dev, "%s, last cap : %d", __func__,
-               info->last_capacity);
-
-       ret = measure_current(info, &ibat);
-       if (ret)
-               goto out;
-       /* Calculate the capacity when discharging(ibat < 0) */
-       if (ibat < 0) {
-               ret = calc_soc(info, OCV_MODE_ACTIVE, &cap_ocv);
-               if (ret)
-                       cap_ocv = info->last_capacity;
-               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
-               if (ret)
-                       goto out;
-               if (data <= LOW_BAT_THRESHOLD) {
-                       /* choose the lower capacity value to report
-                        * between vbat and CC when vbat < 3.6v;
-                        * than 3.6v;
-                        */
-                       *cap = min(cap_ocv, cap_cc);
-               } else {
-                       /* when detect vbat > 3.6v, but cap_cc < 15,and
-                        * cap_ocv is 10% larger than cap_cc, we can think
-                        * CC have some accumulation error, switch to OCV
-                        * to estimate capacity;
-                        * */
-                       if (cap_cc < 15 && cap_ocv - cap_cc > 10)
-                               *cap = cap_ocv;
-                       else
-                               *cap = cap_cc;
-               }
-               /* when discharging, make sure current capacity
-                * is lower than last*/
-               if (*cap > info->last_capacity)
-                       *cap = info->last_capacity;
-       } else {
-               *cap = cap_cc;
-       }
-       info->last_capacity = *cap;
-
-       dev_dbg(info->dev, "%s, cap_ocv:%d cap_cc:%d, cap:%d\n",
-               (ibat < 0) ? "discharging" : "charging",
-                cap_ocv, cap_cc, *cap);
-       /*
-        * store the current capacity to RTC domain register,
-        * after next power up , it will be restored.
-        */
-       pm860x_set_bits(info->i2c, PM8607_RTC_MISC2, RTC_SOC_5LSB,
-                       (*cap & 0x1F) << 3);
-       pm860x_set_bits(info->i2c, PM8607_RTC1, RTC_SOC_3MSB,
-                       ((*cap >> 5) & 0x3));
-       return 0;
-out:
-       return ret;
-}
-
-static void pm860x_external_power_changed(struct power_supply *psy)
-{
-       struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
-
-       calc_resistor(info);
-}
-
-static int pm860x_batt_get_prop(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
-       int data;
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = info->present;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = calc_capacity(info, &data);
-               if (ret)
-                       return ret;
-               if (data < 0)
-                       data = 0;
-               else if (data > 100)
-                       data = 100;
-               /* return 100 if battery is not attached */
-               if (!info->present)
-                       data = 100;
-               val->intval = data;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               /* return real vbatt Voltage */
-               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
-               if (ret)
-                       return ret;
-               val->intval = data * 1000;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               /* return Open Circuit Voltage (not measured voltage) */
-               ret = calc_ocv(info, &data);
-               if (ret)
-                       return ret;
-               val->intval = data * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = measure_current(info, &data);
-               if (ret)
-                       return ret;
-               val->intval = data;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               if (info->present) {
-                       ret = measure_temp(info, &data);
-                       if (ret)
-                               return ret;
-                       data *= 10;
-               } else {
-                       /* Fake Temp 25C Without Battery */
-                       data = 250;
-               }
-               val->intval = data;
-               break;
-       default:
-               return -ENODEV;
-       }
-       return 0;
-}
-
-static int pm860x_batt_set_prop(struct power_supply *psy,
-                                      enum power_supply_property psp,
-                                      const union power_supply_propval *val)
-{
-       struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               clear_ccnt(info, &ccnt_data);
-               info->start_soc = 100;
-               dev_dbg(info->dev, "chg done, update soc = %d\n",
-                       info->start_soc);
-               break;
-       default:
-               return -EPERM;
-       }
-
-       return 0;
-}
-
-
-static enum power_supply_property pm860x_batt_props[] = {
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static const struct power_supply_desc pm860x_battery_desc = {
-       .name                   = "battery-monitor",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = pm860x_batt_props,
-       .num_properties         = ARRAY_SIZE(pm860x_batt_props),
-       .get_property           = pm860x_batt_get_prop,
-       .set_property           = pm860x_batt_set_prop,
-       .external_power_changed = pm860x_external_power_changed,
-};
-
-static int pm860x_battery_probe(struct platform_device *pdev)
-{
-       struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
-       struct pm860x_battery_info *info;
-       struct pm860x_power_pdata *pdata;
-       int ret;
-
-       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-
-       info->irq_cc = platform_get_irq(pdev, 0);
-       if (info->irq_cc <= 0) {
-               dev_err(&pdev->dev, "No IRQ resource!\n");
-               return -EINVAL;
-       }
-
-       info->irq_batt = platform_get_irq(pdev, 1);
-       if (info->irq_batt <= 0) {
-               dev_err(&pdev->dev, "No IRQ resource!\n");
-               return -EINVAL;
-       }
-
-       info->chip = chip;
-       info->i2c =
-           (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
-       info->dev = &pdev->dev;
-       info->status = POWER_SUPPLY_STATUS_UNKNOWN;
-       pdata = pdev->dev.platform_data;
-
-       mutex_init(&info->lock);
-       platform_set_drvdata(pdev, info);
-
-       pm860x_init_battery(info);
-
-       if (pdata && pdata->max_capacity)
-               info->max_capacity = pdata->max_capacity;
-       else
-               info->max_capacity = 1500;      /* set default capacity */
-       if (pdata && pdata->resistor)
-               info->resistor = pdata->resistor;
-       else
-               info->resistor = 300;   /* set default internal resistor */
-
-       info->battery = devm_power_supply_register(&pdev->dev,
-                                                  &pm860x_battery_desc,
-                                                  NULL);
-       if (IS_ERR(info->battery))
-               return PTR_ERR(info->battery);
-       info->battery->dev.parent = &pdev->dev;
-
-       ret = devm_request_threaded_irq(chip->dev, info->irq_cc, NULL,
-                                       pm860x_coulomb_handler, IRQF_ONESHOT,
-                                       "coulomb", info);
-       if (ret < 0) {
-               dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
-                       info->irq_cc, ret);
-               return ret;
-       }
-
-       ret = devm_request_threaded_irq(chip->dev, info->irq_batt, NULL,
-                                       pm860x_batt_handler,
-                                       IRQF_ONESHOT, "battery", info);
-       if (ret < 0) {
-               dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
-                       info->irq_batt, ret);
-               return ret;
-       }
-
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int pm860x_battery_suspend(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
-
-       if (device_may_wakeup(dev))
-               chip->wakeup_flag |= 1 << PM8607_IRQ_CC;
-       return 0;
-}
-
-static int pm860x_battery_resume(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
-
-       if (device_may_wakeup(dev))
-               chip->wakeup_flag &= ~(1 << PM8607_IRQ_CC);
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(pm860x_battery_pm_ops,
-                       pm860x_battery_suspend, pm860x_battery_resume);
-
-static struct platform_driver pm860x_battery_driver = {
-       .driver = {
-                  .name = "88pm860x-battery",
-                  .pm = &pm860x_battery_pm_ops,
-       },
-       .probe = pm860x_battery_probe,
-};
-module_platform_driver(pm860x_battery_driver);
-
-MODULE_DESCRIPTION("Marvell 88PM860x Battery driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c
deleted file mode 100644 (file)
index 2b82e44..0000000
+++ /dev/null
@@ -1,760 +0,0 @@
-/*
- * Battery driver for Marvell 88PM860x PMIC
- *
- * Copyright (c) 2012 Marvell International Ltd.
- * Author:     Jett Zhou <jtzhou@marvell.com>
- *             Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/88pm860x.h>
-#include <linux/delay.h>
-#include <linux/uaccess.h>
-#include <asm/div64.h>
-
-/* bit definitions of Status Query Interface 2 */
-#define STATUS2_CHG            (1 << 2)
-
-/* bit definitions of Reset Out Register */
-#define RESET_SW_PD            (1 << 7)
-
-/* bit definitions of PreReg 1 */
-#define PREREG1_90MA           (0x0)
-#define PREREG1_180MA          (0x1)
-#define PREREG1_450MA          (0x4)
-#define PREREG1_540MA          (0x5)
-#define PREREG1_1350MA         (0xE)
-#define PREREG1_VSYS_4_5V      (3 << 4)
-
-/* bit definitions of Charger Control 1 Register */
-#define CC1_MODE_OFF           (0)
-#define CC1_MODE_PRECHARGE     (1)
-#define CC1_MODE_FASTCHARGE    (2)
-#define CC1_MODE_PULSECHARGE   (3)
-#define CC1_ITERM_20MA         (0 << 2)
-#define CC1_ITERM_60MA         (2 << 2)
-#define CC1_VFCHG_4_2V         (9 << 4)
-
-/* bit definitions of Charger Control 2 Register */
-#define CC2_ICHG_100MA         (0x1)
-#define CC2_ICHG_500MA         (0x9)
-#define CC2_ICHG_1000MA                (0x13)
-
-/* bit definitions of Charger Control 3 Register */
-#define CC3_180MIN_TIMEOUT     (0x6 << 4)
-#define CC3_270MIN_TIMEOUT     (0x7 << 4)
-#define CC3_360MIN_TIMEOUT     (0xA << 4)
-#define CC3_DISABLE_TIMEOUT    (0xF << 4)
-
-/* bit definitions of Charger Control 4 Register */
-#define CC4_IPRE_40MA          (7)
-#define CC4_VPCHG_3_2V         (3 << 4)
-#define CC4_IFCHG_MON_EN       (1 << 6)
-#define CC4_BTEMP_MON_EN       (1 << 7)
-
-/* bit definitions of Charger Control 6 Register */
-#define CC6_BAT_OV_EN          (1 << 2)
-#define CC6_BAT_UV_EN          (1 << 3)
-#define CC6_UV_VBAT_SET                (0x3 << 6)      /* 2.8v */
-
-/* bit definitions of Charger Control 7 Register */
-#define CC7_BAT_REM_EN         (1 << 3)
-#define CC7_IFSM_EN            (1 << 7)
-
-/* bit definitions of Measurement Enable 1 Register */
-#define MEAS1_VBAT             (1 << 0)
-
-/* bit definitions of Measurement Enable 3 Register */
-#define MEAS3_IBAT_EN          (1 << 0)
-#define MEAS3_CC_EN            (1 << 2)
-
-#define FSM_INIT               0
-#define FSM_DISCHARGE          1
-#define FSM_PRECHARGE          2
-#define FSM_FASTCHARGE         3
-
-#define PRECHARGE_THRESHOLD    3100
-#define POWEROFF_THRESHOLD     3400
-#define CHARGE_THRESHOLD       4000
-#define DISCHARGE_THRESHOLD    4180
-
-/* over-temperature on PM8606 setting */
-#define OVER_TEMP_FLAG         (1 << 6)
-#define OVTEMP_AUTORECOVER     (1 << 3)
-
-/* over-voltage protect on vchg setting mv */
-#define VCHG_NORMAL_LOW                4200
-#define VCHG_NORMAL_CHECK      5800
-#define VCHG_NORMAL_HIGH       6000
-#define VCHG_OVP_LOW           5500
-
-struct pm860x_charger_info {
-       struct pm860x_chip *chip;
-       struct i2c_client *i2c;
-       struct i2c_client *i2c_8606;
-       struct device *dev;
-
-       struct power_supply *usb;
-       struct mutex lock;
-       int irq_nums;
-       int irq[7];
-       unsigned state:3;       /* fsm state */
-       unsigned online:1;      /* usb charger */
-       unsigned present:1;     /* battery present */
-       unsigned allowed:1;
-};
-
-static char *pm860x_supplied_to[] = {
-       "battery-monitor",
-};
-
-static int measure_vchg(struct pm860x_charger_info *info, int *data)
-{
-       unsigned char buf[2];
-       int ret = 0;
-
-       ret = pm860x_bulk_read(info->i2c, PM8607_VCHG_MEAS1, 2, buf);
-       if (ret < 0)
-               return ret;
-
-       *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
-       /* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */
-       *data = ((*data & 0xfff) * 9 * 125) >> 9;
-
-       dev_dbg(info->dev, "%s, vchg: %d mv\n", __func__, *data);
-
-       return ret;
-}
-
-static void set_vchg_threshold(struct pm860x_charger_info *info,
-                              int min, int max)
-{
-       int data;
-
-       /* (tmp << 8) * / 5 / 1800 */
-       if (min <= 0)
-               data = 0;
-       else
-               data = (min << 5) / 1125;
-       pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data);
-       dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data);
-
-       if (max <= 0)
-               data = 0xff;
-       else
-               data = (max << 5) / 1125;
-       pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data);
-       dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data);
-
-}
-
-static void set_vbatt_threshold(struct pm860x_charger_info *info,
-                               int min, int max)
-{
-       int data;
-
-       /* (tmp << 8) * 3 / 1800 */
-       if (min <= 0)
-               data = 0;
-       else
-               data = (min << 5) / 675;
-       pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data);
-       dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data);
-
-       if (max <= 0)
-               data = 0xff;
-       else
-               data = (max << 5) / 675;
-       pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data);
-       dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data);
-
-       return;
-}
-
-static int start_precharge(struct pm860x_charger_info *info)
-{
-       int ret;
-
-       dev_dbg(info->dev, "Start Pre-charging!\n");
-       set_vbatt_threshold(info, 0, 0);
-
-       ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
-                              PREREG1_1350MA | PREREG1_VSYS_4_5V);
-       if (ret < 0)
-               goto out;
-       /* stop charging */
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
-                             CC1_MODE_OFF);
-       if (ret < 0)
-               goto out;
-       /* set 270 minutes timeout */
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
-                             CC3_270MIN_TIMEOUT);
-       if (ret < 0)
-               goto out;
-       /* set precharge current, termination voltage, IBAT & TBAT monitor */
-       ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4,
-                              CC4_IPRE_40MA | CC4_VPCHG_3_2V |
-                              CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
-       if (ret < 0)
-               goto out;
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
-                             CC7_BAT_REM_EN | CC7_IFSM_EN,
-                             CC7_BAT_REM_EN | CC7_IFSM_EN);
-       if (ret < 0)
-               goto out;
-       /* trigger precharge */
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
-                             CC1_MODE_PRECHARGE);
-out:
-       return ret;
-}
-
-static int start_fastcharge(struct pm860x_charger_info *info)
-{
-       int ret;
-
-       dev_dbg(info->dev, "Start Fast-charging!\n");
-
-       /* set fastcharge termination current & voltage, disable charging */
-       ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1,
-                              CC1_MODE_OFF | CC1_ITERM_60MA |
-                              CC1_VFCHG_4_2V);
-       if (ret < 0)
-               goto out;
-       ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
-                              PREREG1_540MA | PREREG1_VSYS_4_5V);
-       if (ret < 0)
-               goto out;
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f,
-                             CC2_ICHG_500MA);
-       if (ret < 0)
-               goto out;
-       /* set 270 minutes timeout */
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
-                             CC3_270MIN_TIMEOUT);
-       if (ret < 0)
-               goto out;
-       /* set IBAT & TBAT monitor */
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4,
-                             CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN,
-                             CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
-       if (ret < 0)
-               goto out;
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
-                             CC6_BAT_OV_EN | CC6_BAT_UV_EN |
-                             CC6_UV_VBAT_SET,
-                             CC6_BAT_OV_EN | CC6_BAT_UV_EN |
-                             CC6_UV_VBAT_SET);
-       if (ret < 0)
-               goto out;
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
-                             CC7_BAT_REM_EN | CC7_IFSM_EN,
-                             CC7_BAT_REM_EN | CC7_IFSM_EN);
-       if (ret < 0)
-               goto out;
-       /* launch fast-charge */
-       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
-                             CC1_MODE_FASTCHARGE);
-       /* vchg threshold setting */
-       set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH);
-out:
-       return ret;
-}
-
-static void stop_charge(struct pm860x_charger_info *info, int vbatt)
-{
-       dev_dbg(info->dev, "Stop charging!\n");
-       pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF);
-       if (vbatt > CHARGE_THRESHOLD && info->online)
-               set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
-}
-
-static void power_off_notification(struct pm860x_charger_info *info)
-{
-       dev_dbg(info->dev, "Power-off notification!\n");
-}
-
-static int set_charging_fsm(struct pm860x_charger_info *info)
-{
-       struct power_supply *psy;
-       union power_supply_propval data;
-       unsigned char fsm_state[][16] = { "init", "discharge", "precharge",
-               "fastcharge",
-       };
-       int ret;
-       int vbatt;
-
-       psy = power_supply_get_by_name(pm860x_supplied_to[0]);
-       if (!psy)
-               return -EINVAL;
-       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,
-                       &data);
-       if (ret) {
-               power_supply_put(psy);
-               return ret;
-       }
-       vbatt = data.intval / 1000;
-
-       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data);
-       if (ret) {
-               power_supply_put(psy);
-               return ret;
-       }
-       power_supply_put(psy);
-
-       mutex_lock(&info->lock);
-       info->present = data.intval;
-
-       dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, "
-               "Allowed:%d\n",
-               &fsm_state[info->state][0],
-               (info->online) ? "online" : "N/A",
-               (info->present) ? "present" : "N/A", info->allowed);
-       dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt);
-
-       switch (info->state) {
-       case FSM_INIT:
-               if (info->online && info->present && info->allowed) {
-                       if (vbatt < PRECHARGE_THRESHOLD) {
-                               info->state = FSM_PRECHARGE;
-                               start_precharge(info);
-                       } else if (vbatt > DISCHARGE_THRESHOLD) {
-                               info->state = FSM_DISCHARGE;
-                               stop_charge(info, vbatt);
-                       } else if (vbatt < DISCHARGE_THRESHOLD) {
-                               info->state = FSM_FASTCHARGE;
-                               start_fastcharge(info);
-                       }
-               } else {
-                       if (vbatt < POWEROFF_THRESHOLD) {
-                               power_off_notification(info);
-                       } else {
-                               info->state = FSM_DISCHARGE;
-                               stop_charge(info, vbatt);
-                       }
-               }
-               break;
-       case FSM_PRECHARGE:
-               if (info->online && info->present && info->allowed) {
-                       if (vbatt > PRECHARGE_THRESHOLD) {
-                               info->state = FSM_FASTCHARGE;
-                               start_fastcharge(info);
-                       }
-               } else {
-                       info->state = FSM_DISCHARGE;
-                       stop_charge(info, vbatt);
-               }
-               break;
-       case FSM_FASTCHARGE:
-               if (info->online && info->present && info->allowed) {
-                       if (vbatt < PRECHARGE_THRESHOLD) {
-                               info->state = FSM_PRECHARGE;
-                               start_precharge(info);
-                       }
-               } else {
-                       info->state = FSM_DISCHARGE;
-                       stop_charge(info, vbatt);
-               }
-               break;
-       case FSM_DISCHARGE:
-               if (info->online && info->present && info->allowed) {
-                       if (vbatt < PRECHARGE_THRESHOLD) {
-                               info->state = FSM_PRECHARGE;
-                               start_precharge(info);
-                       } else if (vbatt < DISCHARGE_THRESHOLD) {
-                               info->state = FSM_FASTCHARGE;
-                               start_fastcharge(info);
-                       }
-               } else {
-                       if (vbatt < POWEROFF_THRESHOLD)
-                               power_off_notification(info);
-                       else if (vbatt > CHARGE_THRESHOLD && info->online)
-                               set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
-               }
-               break;
-       default:
-               dev_warn(info->dev, "FSM meets wrong state:%d\n",
-                        info->state);
-               break;
-       }
-       dev_dbg(info->dev,
-               "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n",
-               &fsm_state[info->state][0],
-               (info->online) ? "online" : "N/A",
-               (info->present) ? "present" : "N/A", info->allowed);
-       mutex_unlock(&info->lock);
-
-       return 0;
-}
-
-static irqreturn_t pm860x_charger_handler(int irq, void *data)
-{
-       struct pm860x_charger_info *info = data;
-       int ret;
-
-       mutex_lock(&info->lock);
-       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
-       if (ret < 0) {
-               mutex_unlock(&info->lock);
-               goto out;
-       }
-       if (ret & STATUS2_CHG) {
-               info->online = 1;
-               info->allowed = 1;
-       } else {
-               info->online = 0;
-               info->allowed = 0;
-       }
-       mutex_unlock(&info->lock);
-       dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__,
-               (info->online) ? "online" : "N/A", info->allowed);
-
-       set_charging_fsm(info);
-
-       power_supply_changed(info->usb);
-out:
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t pm860x_temp_handler(int irq, void *data)
-{
-       struct power_supply *psy;
-       struct pm860x_charger_info *info = data;
-       union power_supply_propval temp;
-       int value;
-       int ret;
-
-       psy = power_supply_get_by_name(pm860x_supplied_to[0]);
-       if (!psy)
-               return IRQ_HANDLED;
-       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp);
-       if (ret)
-               goto out;
-       value = temp.intval / 10;
-
-       mutex_lock(&info->lock);
-       /* Temperature < -10 C or >40 C, Will not allow charge */
-       if (value < -10 || value > 40)
-               info->allowed = 0;
-       else
-               info->allowed = 1;
-       dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
-       mutex_unlock(&info->lock);
-
-       set_charging_fsm(info);
-out:
-       power_supply_put(psy);
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t pm860x_exception_handler(int irq, void *data)
-{
-       struct pm860x_charger_info *info = data;
-
-       mutex_lock(&info->lock);
-       info->allowed = 0;
-       mutex_unlock(&info->lock);
-       dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq);
-
-       set_charging_fsm(info);
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t pm860x_done_handler(int irq, void *data)
-{
-       struct pm860x_charger_info *info = data;
-       struct power_supply *psy;
-       union power_supply_propval val;
-       int ret;
-       int vbatt;
-
-       mutex_lock(&info->lock);
-       /* pre-charge done, will transimit to fast-charge stage */
-       if (info->state == FSM_PRECHARGE) {
-               info->allowed = 1;
-               goto out;
-       }
-       /*
-        * Fast charge done, delay to read
-        * the correct status of CHG_DET.
-        */
-       mdelay(5);
-       info->allowed = 0;
-       psy = power_supply_get_by_name(pm860x_supplied_to[0]);
-       if (!psy)
-               goto out;
-       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,
-                       &val);
-       if (ret)
-               goto out_psy_put;
-       vbatt = val.intval / 1000;
-       /*
-        * CHG_DONE interrupt is faster than CHG_DET interrupt when
-        * plug in/out usb, So we can not rely on info->online, we
-        * need check pm8607 status register to check usb is online
-        * or not, then we can decide it is real charge done
-        * automatically or it is triggered by usb plug out;
-        */
-       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
-       if (ret < 0)
-               goto out_psy_put;
-       if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG)
-               power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL,
-                               &val);
-
-out_psy_put:
-       power_supply_put(psy);
-out:
-       mutex_unlock(&info->lock);
-       dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
-       set_charging_fsm(info);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t pm860x_vbattery_handler(int irq, void *data)
-{
-       struct pm860x_charger_info *info = data;
-
-       mutex_lock(&info->lock);
-
-       set_vbatt_threshold(info, 0, 0);
-
-       if (info->present && info->online)
-               info->allowed = 1;
-       else
-               info->allowed = 0;
-       mutex_unlock(&info->lock);
-       dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
-
-       set_charging_fsm(info);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t pm860x_vchg_handler(int irq, void *data)
-{
-       struct pm860x_charger_info *info = data;
-       int vchg = 0;
-
-       if (info->present)
-               goto out;
-
-       measure_vchg(info, &vchg);
-
-       mutex_lock(&info->lock);
-       if (!info->online) {
-               int status;
-               /* check if over-temp on pm8606 or not */
-               status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS);
-               if (status & OVER_TEMP_FLAG) {
-                       /* clear over temp flag and set auto recover */
-                       pm860x_set_bits(info->i2c_8606, PM8606_FLAGS,
-                                       OVER_TEMP_FLAG, OVER_TEMP_FLAG);
-                       pm860x_set_bits(info->i2c_8606,
-                                       PM8606_VSYS,
-                                       OVTEMP_AUTORECOVER,
-                                       OVTEMP_AUTORECOVER);
-                       dev_dbg(info->dev,
-                               "%s, pm8606 over-temp occurred\n", __func__);
-               }
-       }
-
-       if (vchg > VCHG_NORMAL_CHECK) {
-               set_vchg_threshold(info, VCHG_OVP_LOW, 0);
-               info->allowed = 0;
-               dev_dbg(info->dev,
-                       "%s,pm8607 over-vchg occurred,vchg = %dmv\n",
-                       __func__, vchg);
-       } else if (vchg < VCHG_OVP_LOW) {
-               set_vchg_threshold(info, VCHG_NORMAL_LOW,
-                                  VCHG_NORMAL_HIGH);
-               info->allowed = 1;
-               dev_dbg(info->dev,
-                       "%s,pm8607 over-vchg recover,vchg = %dmv\n",
-                       __func__, vchg);
-       }
-       mutex_unlock(&info->lock);
-
-       dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
-       set_charging_fsm(info);
-out:
-       return IRQ_HANDLED;
-}
-
-static int pm860x_usb_get_prop(struct power_supply *psy,
-                              enum power_supply_property psp,
-                              union power_supply_propval *val)
-{
-       struct pm860x_charger_info *info = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (info->state == FSM_FASTCHARGE ||
-                               info->state == FSM_PRECHARGE)
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = info->online;
-               break;
-       default:
-               return -ENODEV;
-       }
-       return 0;
-}
-
-static enum power_supply_property pm860x_usb_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static int pm860x_init_charger(struct pm860x_charger_info *info)
-{
-       int ret;
-
-       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&info->lock);
-       info->state = FSM_INIT;
-       if (ret & STATUS2_CHG) {
-               info->online = 1;
-               info->allowed = 1;
-       } else {
-               info->online = 0;
-               info->allowed = 0;
-       }
-       mutex_unlock(&info->lock);
-
-       set_charging_fsm(info);
-       return 0;
-}
-
-static struct pm860x_irq_desc {
-       const char *name;
-       irqreturn_t (*handler)(int irq, void *data);
-} pm860x_irq_descs[] = {
-       { "usb supply detect", pm860x_charger_handler },
-       { "charge done", pm860x_done_handler },
-       { "charge timeout", pm860x_exception_handler },
-       { "charge fault", pm860x_exception_handler },
-       { "temperature", pm860x_temp_handler },
-       { "vbatt", pm860x_vbattery_handler },
-       { "vchg", pm860x_vchg_handler },
-};
-
-static const struct power_supply_desc pm860x_charger_desc = {
-       .name           = "usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .properties     = pm860x_usb_props,
-       .num_properties = ARRAY_SIZE(pm860x_usb_props),
-       .get_property   = pm860x_usb_get_prop,
-};
-
-static int pm860x_charger_probe(struct platform_device *pdev)
-{
-       struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
-       struct power_supply_config psy_cfg = {};
-       struct pm860x_charger_info *info;
-       int ret;
-       int count;
-       int i;
-       int j;
-
-       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-
-       count = pdev->num_resources;
-       for (i = 0, j = 0; i < count; i++) {
-               info->irq[j] = platform_get_irq(pdev, i);
-               if (info->irq[j] < 0)
-                       continue;
-               j++;
-       }
-       info->irq_nums = j;
-
-       info->chip = chip;
-       info->i2c =
-           (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
-       info->i2c_8606 =
-           (chip->id == CHIP_PM8607) ? chip->companion : chip->client;
-       if (!info->i2c_8606) {
-               dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n");
-               ret = -EINVAL;
-               goto out;
-       }
-       info->dev = &pdev->dev;
-
-       /* set init value for the case we are not using battery */
-       set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW);
-
-       mutex_init(&info->lock);
-       platform_set_drvdata(pdev, info);
-
-       psy_cfg.drv_data = info;
-       psy_cfg.supplied_to = pm860x_supplied_to;
-       psy_cfg.num_supplicants = ARRAY_SIZE(pm860x_supplied_to);
-       info->usb = power_supply_register(&pdev->dev, &pm860x_charger_desc,
-                                         &psy_cfg);
-       if (IS_ERR(info->usb)) {
-               ret = PTR_ERR(info->usb);
-               goto out;
-       }
-
-       pm860x_init_charger(info);
-
-       for (i = 0; i < ARRAY_SIZE(info->irq); i++) {
-               ret = request_threaded_irq(info->irq[i], NULL,
-                       pm860x_irq_descs[i].handler,
-                       IRQF_ONESHOT, pm860x_irq_descs[i].name, info);
-               if (ret < 0) {
-                       dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
-                               info->irq[i], ret);
-                       goto out_irq;
-               }
-       }
-       return 0;
-
-out_irq:
-       power_supply_unregister(info->usb);
-       while (--i >= 0)
-               free_irq(info->irq[i], info);
-out:
-       return ret;
-}
-
-static int pm860x_charger_remove(struct platform_device *pdev)
-{
-       struct pm860x_charger_info *info = platform_get_drvdata(pdev);
-       int i;
-
-       power_supply_unregister(info->usb);
-       for (i = 0; i < info->irq_nums; i++)
-               free_irq(info->irq[i], info);
-       return 0;
-}
-
-static struct platform_driver pm860x_charger_driver = {
-       .driver = {
-                  .name = "88pm860x-charger",
-       },
-       .probe = pm860x_charger_probe,
-       .remove = pm860x_charger_remove,
-};
-module_platform_driver(pm860x_charger_driver);
-
-MODULE_DESCRIPTION("Marvell 88PM860x Charger driver");
-MODULE_LICENSE("GPL");
index acd4a1524a1ec4948198ff6a9229d624979caedb..63454b5cac274d1dede2d55d4ae33b2643ba79fb 100644 (file)
@@ -1,517 +1,3 @@
-menuconfig POWER_SUPPLY
-       bool "Power supply class support"
-       help
-         Say Y here to enable power supply class support. This allows
-         power supply (batteries, AC, USB) monitoring by userspace
-         via sysfs and uevent (if available) and/or APM kernel interface
-         (if selected below).
-
-if POWER_SUPPLY
-
-config POWER_SUPPLY_DEBUG
-       bool "Power supply debug"
-       help
-         Say Y here to enable debugging messages for power supply class
-         and drivers.
-
-config PDA_POWER
-       tristate "Generic PDA/phone power driver"
-       depends on !S390
-       help
-         Say Y here to enable generic power driver for PDAs and phones with
-         one or two external power supplies (AC/USB) connected to main and
-         backup batteries, and optional builtin charger.
-
-config APM_POWER
-       tristate "APM emulation for class batteries"
-       depends on APM_EMULATION
-       help
-         Say Y here to enable support APM status emulation using
-         battery class devices.
-
-config GENERIC_ADC_BATTERY
-       tristate "Generic battery support using IIO"
-       depends on IIO
-       help
-         Say Y here to enable support for the generic battery driver
-         which uses IIO framework to read adc.
-
-config MAX8925_POWER
-       tristate "MAX8925 battery charger support"
-       depends on MFD_MAX8925
-       help
-         Say Y here to enable support for the battery charger in the Maxim
-         MAX8925 PMIC.
-
-config WM831X_BACKUP
-       tristate "WM831X backup battery charger support"
-       depends on MFD_WM831X
-       help
-         Say Y here to enable support for the backup battery charger
-         in the Wolfson Microelectronics WM831x PMICs.
-
-config WM831X_POWER
-       tristate "WM831X PMU support"
-       depends on MFD_WM831X
-       help
-         Say Y here to enable support for the power management unit
-         provided by Wolfson Microelectronics WM831x PMICs.
-
-config WM8350_POWER
-        tristate "WM8350 PMU support"
-        depends on MFD_WM8350
-        help
-          Say Y here to enable support for the power management unit
-         provided by the Wolfson Microelectronics WM8350 PMIC.
-
-config TEST_POWER
-       tristate "Test power driver"
-       help
-         This driver is used for testing. It's safe to say M here.
-
-config BATTERY_88PM860X
-       tristate "Marvell 88PM860x battery driver"
-       depends on MFD_88PM860X
-       help
-         Say Y here to enable battery monitor for Marvell 88PM860x chip.
-
-config BATTERY_ACT8945A
-       tristate "Active-semi ACT8945A charger driver"
-       depends on MFD_ACT8945A || COMPILE_TEST
-       help
-         Say Y here to enable support for power supply provided by
-         Active-semi ActivePath ACT8945A charger.
-
-config BATTERY_DS2760
-       tristate "DS2760 battery driver (HP iPAQ & others)"
-       depends on W1 && W1_SLAVE_DS2760
-       help
-         Say Y here to enable support for batteries with ds2760 chip.
-
-config BATTERY_DS2780
-       tristate "DS2780 battery driver"
-       depends on HAS_IOMEM
-       select W1
-       select W1_SLAVE_DS2780
-       help
-         Say Y here to enable support for batteries with ds2780 chip.
-
-config BATTERY_DS2781
-       tristate "DS2781 battery driver"
-       depends on HAS_IOMEM
-       select W1
-       select W1_SLAVE_DS2781
-       help
-         If you enable this you will have the DS2781 battery driver support.
-
-         The battery monitor chip is used in many batteries/devices
-         as the one who is responsible for charging/discharging/monitoring
-         Li+ batteries.
-
-         If you are unsure, say N.
-
-config BATTERY_DS2782
-       tristate "DS2782/DS2786 standalone gas-gauge"
-       depends on I2C
-       help
-         Say Y here to enable support for the DS2782/DS2786 standalone battery
-         gas-gauge.
-
-config BATTERY_PMU
-       tristate "Apple PMU battery"
-       depends on PPC32 && ADB_PMU
-       help
-         Say Y here to expose battery information on Apple machines
-         through the generic battery class.
-
-config BATTERY_OLPC
-       tristate "One Laptop Per Child battery"
-       depends on X86_32 && OLPC
-       help
-         Say Y to enable support for the battery on the OLPC laptop.
-
-config BATTERY_TOSA
-       tristate "Sharp SL-6000 (tosa) battery"
-       depends on MACH_TOSA && MFD_TC6393XB && TOUCHSCREEN_WM97XX
-       help
-         Say Y to enable support for the battery on the Sharp Zaurus
-         SL-6000 (tosa) models.
-
-config BATTERY_COLLIE
-       tristate "Sharp SL-5500 (collie) battery"
-       depends on SA1100_COLLIE && MCP_UCB1200
-       help
-         Say Y to enable support for the battery on the Sharp Zaurus
-         SL-5500 (collie) models.
-
-config BATTERY_IPAQ_MICRO
-       tristate "iPAQ Atmel Micro ASIC battery driver"
-       depends on MFD_IPAQ_MICRO
-       help
-         Choose this option if you want to monitor battery status on
-         Compaq/HP iPAQ h3100 and h3600.
-
-config BATTERY_WM97XX
-       bool "WM97xx generic battery driver"
-       depends on TOUCHSCREEN_WM97XX=y
-       help
-         Say Y to enable support for battery measured by WM97xx aux port.
-
-config BATTERY_SBS
-        tristate "SBS Compliant gas gauge"
-        depends on I2C
-        help
-         Say Y to include support for SBS battery driver for SBS-compliant
-         gas gauges.
-
-config BATTERY_BQ27XXX
-       tristate "BQ27xxx battery driver"
-       help
-         Say Y here to enable support for batteries with BQ27xxx chips.
-
-config BATTERY_BQ27XXX_I2C
-       tristate "BQ27xxx I2C support"
-       depends on BATTERY_BQ27XXX
-       depends on I2C
-       default y
-       help
-         Say Y here to enable support for batteries with BQ27xxx chips
-         connected over an I2C bus.
-
-config BATTERY_DA9030
-       tristate "DA9030 battery driver"
-       depends on PMIC_DA903X
-       help
-         Say Y here to enable support for batteries charger integrated into
-         DA9030 PMIC.
-
-config BATTERY_DA9052
-       tristate "Dialog DA9052 Battery"
-       depends on PMIC_DA9052
-       help
-         Say Y here to enable support for batteries charger integrated into
-         DA9052 PMIC.
-
-config CHARGER_DA9150
-       tristate "Dialog Semiconductor DA9150 Charger support"
-       depends on MFD_DA9150
-       depends on DA9150_GPADC
-       depends on IIO
-       help
-         Say Y here to enable support for charger unit of the DA9150
-         Integrated Charger & Fuel-Gauge IC.
-
-         This driver can also be built as a module. If so, the module will be
-         called da9150-charger.
-
-config BATTERY_DA9150
-       tristate "Dialog Semiconductor DA9150 Fuel Gauge support"
-       depends on MFD_DA9150
-       help
-         Say Y here to enable support for the Fuel-Gauge unit of the DA9150
-         Integrated Charger & Fuel-Gauge IC
-
-         This driver can also be built as a module. If so, the module will be
-         called da9150-fg.
-
-config AXP288_CHARGER
-       tristate "X-Powers AXP288 Charger"
-       depends on MFD_AXP20X && EXTCON_AXP288
-       help
-         Say yes here to have support X-Power AXP288 power management IC (PMIC)
-         integrated charger.
-
-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
-       help
-         MAX17040 is fuel-gauge systems for lithium-ion (Li+) batteries
-         in handheld and portable equipment. The MAX17040 is configured
-         to operate with a single lithium cell
-
-config BATTERY_MAX17042
-       tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge"
-       depends on I2C
-       select REGMAP_I2C
-       help
-         MAX17042 is fuel-gauge systems for lithium-ion (Li+) batteries
-         in handheld and portable equipment. The MAX17042 is configured
-         to operate with a single lithium cell. MAX8997 and MAX8966 are
-         multi-function devices that include fuel gauages that are compatible
-         with MAX17042. This driver also supports max17047/50 chips which are
-         improved version of max17042.
-
-config BATTERY_Z2
-       tristate "Z2 battery driver"
-       depends on I2C && MACH_ZIPIT2
-       help
-         Say Y to include support for the battery on the Zipit Z2.
-
-config BATTERY_S3C_ADC
-       tristate "Battery driver for Samsung ADC based monitoring"
-       depends on S3C_ADC
-       help
-         Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery
-
-config BATTERY_TWL4030_MADC
-       tristate "TWL4030 MADC battery driver"
-       depends on TWL4030_MADC
-       help
-         Say Y here to enable this dumb driver for batteries managed
-         through the TWL4030 MADC.
-
-config CHARGER_88PM860X
-       tristate "Marvell 88PM860x Charger driver"
-       depends on MFD_88PM860X && BATTERY_88PM860X
-       help
-         Say Y here to enable charger for Marvell 88PM860x chip.
-
-config CHARGER_PCF50633
-       tristate "NXP PCF50633 MBC"
-       depends on MFD_PCF50633
-       help
-        Say Y to include support for NXP PCF50633 Main Battery Charger.
-
-config BATTERY_JZ4740
-       tristate "Ingenic JZ4740 battery"
-       depends on MACH_JZ4740
-       depends on MFD_JZ4740_ADC
-       help
-         Say Y to enable support for the battery on Ingenic JZ4740 based
-         boards.
-
-         This driver can be build as a module. If so, the module will be
-         called jz4740-battery.
-
-config BATTERY_INTEL_MID
-       tristate "Battery driver for Intel MID platforms"
-       depends on INTEL_SCU_IPC && SPI
-       help
-         Say Y here to enable the battery driver on Intel MID
-         platforms.
-
-config BATTERY_RX51
-       tristate "Nokia RX-51 (N900) battery driver"
-       depends on TWL4030_MADC
-       help
-         Say Y here to enable support for battery information on Nokia
-         RX-51, also known as N900 tablet.
-
-config CHARGER_ISP1704
-       tristate "ISP1704 USB Charger Detection"
-       depends on USB_PHY
-       depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
-       help
-         Say Y to enable support for USB Charger Detection with
-         ISP1707/ISP1704 USB transceivers.
-
-config CHARGER_MAX8903
-       tristate "MAX8903 Battery DC-DC Charger for USB and Adapter Power"
-       help
-         Say Y to enable support for the MAX8903 DC-DC charger and sysfs.
-         The driver supports controlling charger-enable and current-limit
-         pins based on the status of charger connections with interrupt
-         handlers.
-
-config CHARGER_TWL4030
-       tristate "OMAP TWL4030 BCI charger driver"
-       depends on IIO && TWL4030_CORE
-       help
-         Say Y here to enable support for TWL4030 Battery Charge Interface.
-
-config CHARGER_LP8727
-       tristate "TI/National Semiconductor LP8727 charger driver"
-       depends on I2C
-       help
-         Say Y here to enable support for LP8727 Charger Driver.
-
-config CHARGER_LP8788
-       tristate "TI LP8788 charger driver"
-       depends on MFD_LP8788
-       depends on LP8788_ADC
-       depends on IIO
-       help
-         Say Y to enable support for the LP8788 linear charger.
-
-config CHARGER_GPIO
-       tristate "GPIO charger"
-       depends on GPIOLIB || COMPILE_TEST
-       help
-         Say Y to include support for chargers which report their online status
-         through a GPIO pin.
-
-         This driver can be build as a module. If so, the module will be
-         called gpio-charger.
-
-config CHARGER_MANAGER
-       bool "Battery charger manager for multiple chargers"
-       depends on REGULATOR
-       select EXTCON
-       help
-          Say Y to enable charger-manager support, which allows multiple
-          chargers attached to a battery and multiple batteries attached to a
-          system. The charger-manager also can monitor charging status in
-          runtime and in suspend-to-RAM by waking up the system periodically
-          with help of suspend_again support.
-
-config CHARGER_MAX14577
-       tristate "Maxim MAX14577/77836 battery charger driver"
-       depends on MFD_MAX14577
-       help
-         Say Y to enable support for the battery charger control sysfs and
-         platform data of MAX14577/77836 MUICs.
-
-config CHARGER_MAX77693
-       tristate "Maxim MAX77693 battery charger driver"
-       depends on MFD_MAX77693
-       help
-         Say Y to enable support for the Maxim MAX77693 battery charger.
-
-config CHARGER_MAX8997
-       tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
-       depends on MFD_MAX8997 && REGULATOR_MAX8997
-       help
-         Say Y to enable support for the battery charger control sysfs and
-         platform data of MAX8997/LP3974 PMICs.
-
-config CHARGER_MAX8998
-       tristate "Maxim MAX8998/LP3974 PMIC battery charger driver"
-       depends on MFD_MAX8998 && REGULATOR_MAX8998
-       help
-         Say Y to enable support for the battery charger control sysfs and
-         platform data of MAX8998/LP3974 PMICs.
-
-config CHARGER_QCOM_SMBB
-       tristate "Qualcomm Switch-Mode Battery Charger and Boost"
-       depends on MFD_SPMI_PMIC || COMPILE_TEST
-       depends on OF
-       depends on EXTCON
-       help
-         Say Y to include support for the Switch-Mode Battery Charger and
-         Boost (SMBB) hardware found in Qualcomm PM8941 PMICs.  The charger
-         is an integrated, single-cell lithium-ion battery charger.  DT
-         configuration is required for loading, see the devicetree
-         documentation for more detail.  The base name for this driver is
-         'pm8941_charger'.
-
-config CHARGER_BQ2415X
-       tristate "TI BQ2415x battery charger driver"
-       depends on I2C
-       help
-         Say Y to enable support for the TI BQ2415x battery charger
-         PMICs.
-
-         You'll need this driver to charge batteries on e.g. Nokia
-         RX-51/N900.
-
-config CHARGER_BQ24190
-       tristate "TI BQ24190 battery charger driver"
-       depends on I2C
-       depends on GPIOLIB || COMPILE_TEST
-       help
-         Say Y to enable support for the TI BQ24190 battery charger.
-
-config CHARGER_BQ24257
-       tristate "TI BQ24250/24251/24257 battery charger driver"
-       depends on I2C
-       depends on GPIOLIB || COMPILE_TEST
-       depends on REGMAP_I2C
-       help
-         Say Y to enable support for the TI BQ24250, BQ24251, and BQ24257 battery
-         chargers.
-
-config CHARGER_BQ24735
-       tristate "TI BQ24735 battery charger support"
-       depends on I2C
-       depends on GPIOLIB || COMPILE_TEST
-       help
-         Say Y to enable support for the TI BQ24735 battery charger.
-
-config CHARGER_BQ25890
-       tristate "TI BQ25890 battery charger driver"
-       depends on I2C
-       depends on GPIOLIB || COMPILE_TEST
-       select REGMAP_I2C
-       help
-         Say Y to enable support for the TI BQ25890 battery charger.
-
-config CHARGER_SMB347
-       tristate "Summit Microelectronics SMB347 Battery Charger"
-       depends on I2C
-       select REGMAP_I2C
-       help
-         Say Y to include support for Summit Microelectronics SMB347
-         Battery Charger.
-
-config CHARGER_TPS65090
-       tristate "TPS65090 battery charger driver"
-       depends on MFD_TPS65090
-       help
-        Say Y here to enable support for battery charging with TPS65090
-        PMIC chips.
-
-config CHARGER_TPS65217
-       tristate "TPS65217 battery charger driver"
-       depends on MFD_TPS65217
-       help
-        Say Y here to enable support for battery charging with TPS65217
-        PMIC chips.
-
-config BATTERY_GAUGE_LTC2941
-       tristate "LTC2941/LTC2943 Battery Gauge Driver"
-       depends on I2C
-       help
-         Say Y here to include support for LTC2941 and LTC2943 Battery
-         Gauge IC. The driver reports the charge count continuously, and
-         measures the voltage and temperature every 10 seconds.
-
-config AB8500_BM
-       bool "AB8500 Battery Management Driver"
-       depends on AB8500_CORE && AB8500_GPADC
-       help
-         Say Y to include support for AB8500 battery management.
-
-config BATTERY_GOLDFISH
-       tristate "Goldfish battery driver"
-       depends on GOLDFISH || COMPILE_TEST
-       depends on HAS_IOMEM
-       help
-         Say Y to enable support for the battery and AC power in the
-         Goldfish emulator.
-
-config BATTERY_RT5033
-       tristate "RT5033 fuel gauge support"
-       depends on MFD_RT5033
-       help
-         This adds support for battery fuel gauge in Richtek RT5033 PMIC.
-         The fuelgauge calculates and determines the battery state of charge
-         according to battery open circuit voltage.
-
-config CHARGER_RT9455
-       tristate "Richtek RT9455 battery charger driver"
-       depends on I2C
-       depends on GPIOLIB || COMPILE_TEST
-       select REGMAP_I2C
-       help
-         Say Y to enable support for Richtek RT9455 battery charger.
-
-config AXP20X_POWER
-       tristate "AXP20x power supply driver"
-       depends on MFD_AXP20X
-       help
-         This driver provides support for the power supply features of
-         AXP20x PMIC.
-
-endif # POWER_SUPPLY
-
-source "drivers/power/reset/Kconfig"
 source "drivers/power/avs/Kconfig"
+source "drivers/power/reset/Kconfig"
+source "drivers/power/supply/Kconfig"
index e46b75d448a5734bfa40ae7a92648fd8feec5162..ff35c712d824c5a756de115b02ab3c5ffef9c6a2 100644 (file)
@@ -1,76 +1,3 @@
-subdir-ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG
-
-power_supply-y                         := power_supply_core.o
-power_supply-$(CONFIG_SYSFS)           += power_supply_sysfs.o
-power_supply-$(CONFIG_LEDS_TRIGGERS)   += power_supply_leds.o
-
-obj-$(CONFIG_POWER_SUPPLY)     += power_supply.o
-obj-$(CONFIG_GENERIC_ADC_BATTERY)      += generic-adc-battery.o
-
-obj-$(CONFIG_PDA_POWER)                += pda_power.o
-obj-$(CONFIG_APM_POWER)                += apm_power.o
-obj-$(CONFIG_AXP20X_POWER)     += axp20x_usb_power.o
-obj-$(CONFIG_MAX8925_POWER)    += max8925_power.o
-obj-$(CONFIG_WM831X_BACKUP)    += wm831x_backup.o
-obj-$(CONFIG_WM831X_POWER)     += wm831x_power.o
-obj-$(CONFIG_WM8350_POWER)     += wm8350_power.o
-obj-$(CONFIG_TEST_POWER)       += test_power.o
-
-obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
-obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
-obj-$(CONFIG_BATTERY_DS2760)   += ds2760_battery.o
-obj-$(CONFIG_BATTERY_DS2780)   += ds2780_battery.o
-obj-$(CONFIG_BATTERY_DS2781)   += ds2781_battery.o
-obj-$(CONFIG_BATTERY_DS2782)   += ds2782_battery.o
-obj-$(CONFIG_BATTERY_GAUGE_LTC2941)    += ltc2941-battery-gauge.o
-obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
-obj-$(CONFIG_BATTERY_PMU)      += pmu_battery.o
-obj-$(CONFIG_BATTERY_OLPC)     += olpc_battery.o
-obj-$(CONFIG_BATTERY_TOSA)     += tosa_battery.o
-obj-$(CONFIG_BATTERY_COLLIE)   += collie_battery.o
-obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
-obj-$(CONFIG_BATTERY_WM97XX)   += wm97xx_battery.o
-obj-$(CONFIG_BATTERY_SBS)      += sbs-battery.o
-obj-$(CONFIG_BATTERY_BQ27XXX)  += bq27xxx_battery.o
-obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
-obj-$(CONFIG_BATTERY_DA9030)   += da9030_battery.o
-obj-$(CONFIG_BATTERY_DA9052)   += da9052-battery.o
-obj-$(CONFIG_CHARGER_DA9150)   += da9150-charger.o
-obj-$(CONFIG_BATTERY_DA9150)   += da9150-fg.o
-obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
-obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
-obj-$(CONFIG_BATTERY_Z2)       += z2_battery.o
-obj-$(CONFIG_BATTERY_RT5033)   += rt5033_battery.o
-obj-$(CONFIG_CHARGER_RT9455)   += rt9455_charger.o
-obj-$(CONFIG_BATTERY_S3C_ADC)  += s3c_adc_battery.o
-obj-$(CONFIG_BATTERY_TWL4030_MADC)     += twl4030_madc_battery.o
-obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
-obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
-obj-$(CONFIG_BATTERY_JZ4740)   += jz4740-battery.o
-obj-$(CONFIG_BATTERY_INTEL_MID)        += intel_mid_battery.o
-obj-$(CONFIG_BATTERY_RX51)     += rx51_battery.o
-obj-$(CONFIG_AB8500_BM)                += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o
-obj-$(CONFIG_CHARGER_ISP1704)  += isp1704_charger.o
-obj-$(CONFIG_CHARGER_MAX8903)  += max8903_charger.o
-obj-$(CONFIG_CHARGER_TWL4030)  += twl4030_charger.o
-obj-$(CONFIG_CHARGER_LP8727)   += lp8727_charger.o
-obj-$(CONFIG_CHARGER_LP8788)   += lp8788-charger.o
-obj-$(CONFIG_CHARGER_GPIO)     += gpio-charger.o
-obj-$(CONFIG_CHARGER_MANAGER)  += charger-manager.o
-obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
-obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
-obj-$(CONFIG_CHARGER_MAX8997)  += max8997_charger.o
-obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
-obj-$(CONFIG_CHARGER_QCOM_SMBB)        += qcom_smbb.o
-obj-$(CONFIG_CHARGER_BQ2415X)  += bq2415x_charger.o
-obj-$(CONFIG_CHARGER_BQ24190)  += bq24190_charger.o
-obj-$(CONFIG_CHARGER_BQ24257)  += bq24257_charger.o
-obj-$(CONFIG_CHARGER_BQ24735)  += bq24735-charger.o
-obj-$(CONFIG_CHARGER_BQ25890)  += bq25890_charger.o
 obj-$(CONFIG_POWER_AVS)                += avs/
-obj-$(CONFIG_CHARGER_SMB347)   += smb347-charger.o
-obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
-obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
 obj-$(CONFIG_POWER_RESET)      += reset/
-obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
-obj-$(CONFIG_AXP288_CHARGER)   += axp288_charger.o
+obj-$(CONFIG_POWER_SUPPLY)     += supply/
diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c
deleted file mode 100644 (file)
index d298645..0000000
+++ /dev/null
@@ -1,605 +0,0 @@
-#include <linux/export.h>
-#include <linux/power_supply.h>
-#include <linux/of.h>
-#include <linux/mfd/abx500.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
-
-/*
- * These are the defined batteries that uses a NTC and ID resistor placed
- * inside of the battery pack.
- * Note that the res_to_temp table must be strictly sorted by falling resistance
- * values to work.
- */
-const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
-       {-5, 53407},
-       { 0, 48594},
-       { 5, 43804},
-       {10, 39188},
-       {15, 34870},
-       {20, 30933},
-       {25, 27422},
-       {30, 24347},
-       {35, 21694},
-       {40, 19431},
-       {45, 17517},
-       {50, 15908},
-       {55, 14561},
-       {60, 13437},
-       {65, 12500},
-};
-EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor);
-
-const int ab8500_temp_tbl_a_size = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor);
-EXPORT_SYMBOL(ab8500_temp_tbl_a_size);
-
-const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
-       {-5, 200000},
-       { 0, 159024},
-       { 5, 151921},
-       {10, 144300},
-       {15, 136424},
-       {20, 128565},
-       {25, 120978},
-       {30, 113875},
-       {35, 107397},
-       {40, 101629},
-       {45,  96592},
-       {50,  92253},
-       {55,  88569},
-       {60,  85461},
-       {65,  82869},
-};
-EXPORT_SYMBOL(ab8500_temp_tbl_b_thermistor);
-
-const int ab8500_temp_tbl_b_size = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor);
-EXPORT_SYMBOL(ab8500_temp_tbl_b_size);
-
-static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
-       {4171,  100},
-       {4114,   95},
-       {4009,   83},
-       {3947,   74},
-       {3907,   67},
-       {3863,   59},
-       {3830,   56},
-       {3813,   53},
-       {3791,   46},
-       {3771,   33},
-       {3754,   25},
-       {3735,   20},
-       {3717,   17},
-       {3681,   13},
-       {3664,    8},
-       {3651,    6},
-       {3635,    5},
-       {3560,    3},
-       {3408,    1},
-       {3247,    0},
-};
-
-static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
-       {4161,  100},
-       {4124,   98},
-       {4044,   90},
-       {4003,   85},
-       {3966,   80},
-       {3933,   75},
-       {3888,   67},
-       {3849,   60},
-       {3813,   55},
-       {3787,   47},
-       {3772,   30},
-       {3751,   25},
-       {3718,   20},
-       {3681,   16},
-       {3660,   14},
-       {3589,   10},
-       {3546,    7},
-       {3495,    4},
-       {3404,    2},
-       {3250,    0},
-};
-
-static const struct abx500_v_to_cap cap_tbl[] = {
-       {4186,  100},
-       {4163,   99},
-       {4114,   95},
-       {4068,   90},
-       {3990,   80},
-       {3926,   70},
-       {3898,   65},
-       {3866,   60},
-       {3833,   55},
-       {3812,   50},
-       {3787,   40},
-       {3768,   30},
-       {3747,   25},
-       {3730,   20},
-       {3705,   15},
-       {3699,   14},
-       {3684,   12},
-       {3672,    9},
-       {3657,    7},
-       {3638,    6},
-       {3556,    4},
-       {3424,    2},
-       {3317,    1},
-       {3094,    0},
-};
-
-/*
- * Note that the res_to_temp table must be strictly sorted by falling
- * resistance values to work.
- */
-static const struct abx500_res_to_temp temp_tbl[] = {
-       {-5, 214834},
-       { 0, 162943},
-       { 5, 124820},
-       {10,  96520},
-       {15,  75306},
-       {20,  59254},
-       {25,  47000},
-       {30,  37566},
-       {35,  30245},
-       {40,  24520},
-       {45,  20010},
-       {50,  16432},
-       {55,  13576},
-       {60,  11280},
-       {65,   9425},
-};
-
-/*
- * Note that the batres_vs_temp table must be strictly sorted by falling
- * temperature values to work.
- */
-static const struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
-       { 40, 120},
-       { 30, 135},
-       { 20, 165},
-       { 10, 230},
-       { 00, 325},
-       {-10, 445},
-       {-20, 595},
-};
-
-/*
- * Note that the batres_vs_temp table must be strictly sorted by falling
- * temperature values to work.
- */
-static const struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
-       { 60, 300},
-       { 30, 300},
-       { 20, 300},
-       { 10, 300},
-       { 00, 300},
-       {-10, 300},
-       {-20, 300},
-};
-
-/* battery resistance table for LI ION 9100 battery */
-static const struct batres_vs_temp temp_to_batres_tbl_9100[] = {
-       { 60, 180},
-       { 30, 180},
-       { 20, 180},
-       { 10, 180},
-       { 00, 180},
-       {-10, 180},
-       {-20, 180},
-};
-
-static struct abx500_battery_type bat_type_thermistor[] = {
-       [BATTERY_UNKNOWN] = {
-               /* First element always represent the UNKNOWN battery */
-               .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
-               .resis_high = 0,
-               .resis_low = 0,
-               .battery_resistance = 300,
-               .charge_full_design = 612,
-               .nominal_voltage = 3700,
-               .termination_vol = 4050,
-               .termination_curr = 200,
-               .recharge_cap = 95,
-               .normal_cur_lvl = 400,
-               .normal_vol_lvl = 4100,
-               .maint_a_cur_lvl = 400,
-               .maint_a_vol_lvl = 4050,
-               .maint_a_chg_timer_h = 60,
-               .maint_b_cur_lvl = 400,
-               .maint_b_vol_lvl = 4000,
-               .maint_b_chg_timer_h = 200,
-               .low_high_cur_lvl = 300,
-               .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-               .r_to_t_tbl = temp_tbl,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-               .v_to_cap_tbl = cap_tbl,
-               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-               .batres_tbl = temp_to_batres_tbl_thermistor,
-       },
-       {
-               .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
-               .resis_high = 53407,
-               .resis_low = 12500,
-               .battery_resistance = 300,
-               .charge_full_design = 900,
-               .nominal_voltage = 3600,
-               .termination_vol = 4150,
-               .termination_curr = 80,
-               .recharge_cap = 95,
-               .normal_cur_lvl = 700,
-               .normal_vol_lvl = 4200,
-               .maint_a_cur_lvl = 600,
-               .maint_a_vol_lvl = 4150,
-               .maint_a_chg_timer_h = 60,
-               .maint_b_cur_lvl = 600,
-               .maint_b_vol_lvl = 4100,
-               .maint_b_chg_timer_h = 200,
-               .low_high_cur_lvl = 300,
-               .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor),
-               .r_to_t_tbl = ab8500_temp_tbl_a_thermistor,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_a_thermistor),
-               .v_to_cap_tbl = cap_tbl_a_thermistor,
-               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-               .batres_tbl = temp_to_batres_tbl_thermistor,
-
-       },
-       {
-               .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
-               .resis_high = 200000,
-               .resis_low = 82869,
-               .battery_resistance = 300,
-               .charge_full_design = 900,
-               .nominal_voltage = 3600,
-               .termination_vol = 4150,
-               .termination_curr = 80,
-               .recharge_cap = 95,
-               .normal_cur_lvl = 700,
-               .normal_vol_lvl = 4200,
-               .maint_a_cur_lvl = 600,
-               .maint_a_vol_lvl = 4150,
-               .maint_a_chg_timer_h = 60,
-               .maint_b_cur_lvl = 600,
-               .maint_b_vol_lvl = 4100,
-               .maint_b_chg_timer_h = 200,
-               .low_high_cur_lvl = 300,
-               .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor),
-               .r_to_t_tbl = ab8500_temp_tbl_b_thermistor,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_b_thermistor),
-               .v_to_cap_tbl = cap_tbl_b_thermistor,
-               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-               .batres_tbl = temp_to_batres_tbl_thermistor,
-       },
-};
-
-static struct abx500_battery_type bat_type_ext_thermistor[] = {
-       [BATTERY_UNKNOWN] = {
-               /* First element always represent the UNKNOWN battery */
-               .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
-               .resis_high = 0,
-               .resis_low = 0,
-               .battery_resistance = 300,
-               .charge_full_design = 612,
-               .nominal_voltage = 3700,
-               .termination_vol = 4050,
-               .termination_curr = 200,
-               .recharge_cap = 95,
-               .normal_cur_lvl = 400,
-               .normal_vol_lvl = 4100,
-               .maint_a_cur_lvl = 400,
-               .maint_a_vol_lvl = 4050,
-               .maint_a_chg_timer_h = 60,
-               .maint_b_cur_lvl = 400,
-               .maint_b_vol_lvl = 4000,
-               .maint_b_chg_timer_h = 200,
-               .low_high_cur_lvl = 300,
-               .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-               .r_to_t_tbl = temp_tbl,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-               .v_to_cap_tbl = cap_tbl,
-               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-               .batres_tbl = temp_to_batres_tbl_thermistor,
-       },
-/*
- * These are the batteries that doesn't have an internal NTC resistor to measure
- * its temperature. The temperature in this case is measure with a NTC placed
- * near the battery but on the PCB.
- */
-       {
-               .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
-               .resis_high = 76000,
-               .resis_low = 53000,
-               .battery_resistance = 300,
-               .charge_full_design = 900,
-               .nominal_voltage = 3700,
-               .termination_vol = 4150,
-               .termination_curr = 100,
-               .recharge_cap = 95,
-               .normal_cur_lvl = 700,
-               .normal_vol_lvl = 4200,
-               .maint_a_cur_lvl = 600,
-               .maint_a_vol_lvl = 4150,
-               .maint_a_chg_timer_h = 60,
-               .maint_b_cur_lvl = 600,
-               .maint_b_vol_lvl = 4100,
-               .maint_b_chg_timer_h = 200,
-               .low_high_cur_lvl = 300,
-               .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-               .r_to_t_tbl = temp_tbl,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-               .v_to_cap_tbl = cap_tbl,
-               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-               .batres_tbl = temp_to_batres_tbl_thermistor,
-       },
-       {
-               .name = POWER_SUPPLY_TECHNOLOGY_LION,
-               .resis_high = 30000,
-               .resis_low = 10000,
-               .battery_resistance = 300,
-               .charge_full_design = 950,
-               .nominal_voltage = 3700,
-               .termination_vol = 4150,
-               .termination_curr = 100,
-               .recharge_cap = 95,
-               .normal_cur_lvl = 700,
-               .normal_vol_lvl = 4200,
-               .maint_a_cur_lvl = 600,
-               .maint_a_vol_lvl = 4150,
-               .maint_a_chg_timer_h = 60,
-               .maint_b_cur_lvl = 600,
-               .maint_b_vol_lvl = 4100,
-               .maint_b_chg_timer_h = 200,
-               .low_high_cur_lvl = 300,
-               .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-               .r_to_t_tbl = temp_tbl,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-               .v_to_cap_tbl = cap_tbl,
-               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-               .batres_tbl = temp_to_batres_tbl_thermistor,
-       },
-       {
-               .name = POWER_SUPPLY_TECHNOLOGY_LION,
-               .resis_high = 95000,
-               .resis_low = 76001,
-               .battery_resistance = 300,
-               .charge_full_design = 950,
-               .nominal_voltage = 3700,
-               .termination_vol = 4150,
-               .termination_curr = 100,
-               .recharge_cap = 95,
-               .normal_cur_lvl = 700,
-               .normal_vol_lvl = 4200,
-               .maint_a_cur_lvl = 600,
-               .maint_a_vol_lvl = 4150,
-               .maint_a_chg_timer_h = 60,
-               .maint_b_cur_lvl = 600,
-               .maint_b_vol_lvl = 4100,
-               .maint_b_chg_timer_h = 200,
-               .low_high_cur_lvl = 300,
-               .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-               .r_to_t_tbl = temp_tbl,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-               .v_to_cap_tbl = cap_tbl,
-               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-               .batres_tbl = temp_to_batres_tbl_thermistor,
-       },
-};
-
-static const struct abx500_bm_capacity_levels cap_levels = {
-       .critical       = 2,
-       .low            = 10,
-       .normal         = 70,
-       .high           = 95,
-       .full           = 100,
-};
-
-static const struct abx500_fg_parameters fg = {
-       .recovery_sleep_timer = 10,
-       .recovery_total_time = 100,
-       .init_timer = 1,
-       .init_discard_time = 5,
-       .init_total_time = 40,
-       .high_curr_time = 60,
-       .accu_charging = 30,
-       .accu_high_curr = 30,
-       .high_curr_threshold = 50,
-       .lowbat_threshold = 3100,
-       .battok_falling_th_sel0 = 2860,
-       .battok_raising_th_sel1 = 2860,
-       .maint_thres = 95,
-       .user_cap_limit = 15,
-       .pcut_enable = 1,
-       .pcut_max_time = 127,
-       .pcut_flag_time = 112,
-       .pcut_max_restart = 15,
-       .pcut_debounce_time = 2,
-};
-
-static const struct abx500_maxim_parameters ab8500_maxi_params = {
-       .ena_maxi = true,
-       .chg_curr = 910,
-       .wait_cycles = 10,
-       .charger_curr_step = 100,
-};
-
-static const struct abx500_maxim_parameters abx540_maxi_params = {
-        .ena_maxi = true,
-        .chg_curr = 3000,
-        .wait_cycles = 10,
-        .charger_curr_step = 200,
-};
-
-static const struct abx500_bm_charger_parameters chg = {
-       .usb_volt_max           = 5500,
-       .usb_curr_max           = 1500,
-       .ac_volt_max            = 7500,
-       .ac_curr_max            = 1500,
-};
-
-/*
- * This array maps the raw hex value to charger output current used by the
- * AB8500 values
- */
-static int ab8500_charge_output_curr_map[] = {
-        100,    200,    300,    400,    500,    600,    700,    800,
-        900,    1000,   1100,   1200,   1300,   1400,   1500,   1500,
-};
-
-static int ab8540_charge_output_curr_map[] = {
-        0,      0,      0,      75,     100,    125,    150,    175,
-        200,    225,    250,    275,    300,    325,    350,    375,
-        400,    425,    450,    475,    500,    525,    550,    575,
-        600,    625,    650,    675,    700,    725,    750,    775,
-        800,    825,    850,    875,    900,    925,    950,    975,
-        1000,   1025,   1050,   1075,   1100,   1125,   1150,   1175,
-        1200,   1225,   1250,   1275,   1300,   1325,   1350,   1375,
-        1400,   1425,   1450,   1500,   1600,   1700,   1900,   2000,
-};
-
-/*
- * This array maps the raw hex value to charger input current used by the
- * AB8500 values
- */
-static int ab8500_charge_input_curr_map[] = {
-        50,     98,     193,    290,    380,    450,    500,    600,
-        700,    800,    900,    1000,   1100,   1300,   1400,   1500,
-};
-
-static int ab8540_charge_input_curr_map[] = {
-        25,     50,     75,     100,    125,    150,    175,    200,
-        225,    250,    275,    300,    325,    350,    375,    400,
-        425,    450,    475,    500,    525,    550,    575,    600,
-        625,    650,    675,    700,    725,    750,    775,    800,
-        825,    850,    875,    900,    925,    950,    975,    1000,
-        1025,   1050,   1075,   1100,   1125,   1150,   1175,   1200,
-        1225,   1250,   1275,   1300,   1325,   1350,   1375,   1400,
-        1425,   1450,   1475,   1500,   1500,   1500,   1500,   1500,
-};
-
-struct abx500_bm_data ab8500_bm_data = {
-       .temp_under             = 3,
-       .temp_low               = 8,
-       .temp_high              = 43,
-       .temp_over              = 48,
-       .main_safety_tmr_h      = 4,
-       .temp_interval_chg      = 20,
-       .temp_interval_nochg    = 120,
-       .usb_safety_tmr_h       = 4,
-       .bkup_bat_v             = BUP_VCH_SEL_2P6V,
-       .bkup_bat_i             = BUP_ICH_SEL_150UA,
-       .no_maintenance         = false,
-       .capacity_scaling       = false,
-       .adc_therm              = ABx500_ADC_THERM_BATCTRL,
-       .chg_unknown_bat        = false,
-       .enable_overshoot       = false,
-       .fg_res                 = 100,
-       .cap_levels             = &cap_levels,
-       .bat_type               = bat_type_thermistor,
-       .n_btypes               = ARRAY_SIZE(bat_type_thermistor),
-       .batt_id                = 0,
-       .interval_charging      = 5,
-       .interval_not_charging  = 120,
-       .temp_hysteresis        = 3,
-       .gnd_lift_resistance    = 34,
-       .chg_output_curr        = ab8500_charge_output_curr_map,
-       .n_chg_out_curr         = ARRAY_SIZE(ab8500_charge_output_curr_map),
-       .maxi                   = &ab8500_maxi_params,
-       .chg_params             = &chg,
-       .fg_params              = &fg,
-        .chg_input_curr         = ab8500_charge_input_curr_map,
-        .n_chg_in_curr          = ARRAY_SIZE(ab8500_charge_input_curr_map),
-};
-
-struct abx500_bm_data ab8540_bm_data = {
-        .temp_under             = 3,
-        .temp_low               = 8,
-        .temp_high              = 43,
-        .temp_over              = 48,
-        .main_safety_tmr_h      = 4,
-        .temp_interval_chg      = 20,
-        .temp_interval_nochg    = 120,
-        .usb_safety_tmr_h       = 4,
-        .bkup_bat_v             = BUP_VCH_SEL_2P6V,
-        .bkup_bat_i             = BUP_ICH_SEL_150UA,
-        .no_maintenance         = false,
-        .capacity_scaling       = false,
-        .adc_therm              = ABx500_ADC_THERM_BATCTRL,
-        .chg_unknown_bat        = false,
-        .enable_overshoot       = false,
-        .fg_res                 = 100,
-        .cap_levels             = &cap_levels,
-        .bat_type               = bat_type_thermistor,
-        .n_btypes               = ARRAY_SIZE(bat_type_thermistor),
-        .batt_id                = 0,
-        .interval_charging      = 5,
-        .interval_not_charging  = 120,
-        .temp_hysteresis        = 3,
-        .gnd_lift_resistance    = 0,
-        .maxi                   = &abx540_maxi_params,
-        .chg_params             = &chg,
-        .fg_params              = &fg,
-        .chg_output_curr        = ab8540_charge_output_curr_map,
-        .n_chg_out_curr         = ARRAY_SIZE(ab8540_charge_output_curr_map),
-        .chg_input_curr         = ab8540_charge_input_curr_map,
-        .n_chg_in_curr          = ARRAY_SIZE(ab8540_charge_input_curr_map),
-};
-
-int ab8500_bm_of_probe(struct device *dev,
-                      struct device_node *np,
-                      struct abx500_bm_data *bm)
-{
-       const struct batres_vs_temp *tmp_batres_tbl;
-       struct device_node *battery_node;
-       const char *btech;
-       int i;
-
-       /* get phandle to 'battery-info' node */
-       battery_node = of_parse_phandle(np, "battery", 0);
-       if (!battery_node) {
-               dev_err(dev, "battery node or reference missing\n");
-               return -EINVAL;
-       }
-
-       btech = of_get_property(battery_node, "stericsson,battery-type", NULL);
-       if (!btech) {
-               dev_warn(dev, "missing property battery-name/type\n");
-               return -EINVAL;
-       }
-
-       if (strncmp(btech, "LION", 4) == 0) {
-               bm->no_maintenance  = true;
-               bm->chg_unknown_bat = true;
-               bm->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
-               bm->bat_type[BATTERY_UNKNOWN].termination_vol    = 4150;
-               bm->bat_type[BATTERY_UNKNOWN].recharge_cap       = 95;
-               bm->bat_type[BATTERY_UNKNOWN].normal_cur_lvl     = 520;
-               bm->bat_type[BATTERY_UNKNOWN].normal_vol_lvl     = 4200;
-       }
-
-       if (of_property_read_bool(battery_node, "thermistor-on-batctrl")) {
-               if (strncmp(btech, "LION", 4) == 0)
-                       tmp_batres_tbl = temp_to_batres_tbl_9100;
-               else
-                       tmp_batres_tbl = temp_to_batres_tbl_thermistor;
-       } else {
-               bm->n_btypes   = 4;
-               bm->bat_type   = bat_type_ext_thermistor;
-               bm->adc_therm  = ABx500_ADC_THERM_BATTEMP;
-               tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
-       }
-
-       /* select the battery resolution table */
-       for (i = 0; i < bm->n_btypes; ++i)
-               bm->bat_type[i].batres_tbl = tmp_batres_tbl;
-
-       of_node_put(battery_node);
-
-       return 0;
-}
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
deleted file mode 100644 (file)
index bf2e5dd..0000000
+++ /dev/null
@@ -1,1212 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * Battery temperature driver for AB8500
- *
- * License Terms: GNU General Public License v2
- * Author:
- *     Johan Palsson <johan.palsson@stericsson.com>
- *     Karl Komierowski <karl.komierowski@stericsson.com>
- *     Arun R Murthy <arun.murthy@stericsson.com>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/completion.h>
-#include <linux/workqueue.h>
-#include <linux/jiffies.h>
-#include <linux/of.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/abx500.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
-#include <linux/mfd/abx500/ab8500-gpadc.h>
-
-#define VTVOUT_V                       1800
-
-#define BTEMP_THERMAL_LOW_LIMIT                -10
-#define BTEMP_THERMAL_MED_LIMIT                0
-#define BTEMP_THERMAL_HIGH_LIMIT_52    52
-#define BTEMP_THERMAL_HIGH_LIMIT_57    57
-#define BTEMP_THERMAL_HIGH_LIMIT_62    62
-
-#define BTEMP_BATCTRL_CURR_SRC_7UA     7
-#define BTEMP_BATCTRL_CURR_SRC_20UA    20
-
-#define BTEMP_BATCTRL_CURR_SRC_16UA    16
-#define BTEMP_BATCTRL_CURR_SRC_18UA    18
-
-#define BTEMP_BATCTRL_CURR_SRC_60UA    60
-#define BTEMP_BATCTRL_CURR_SRC_120UA   120
-
-/**
- * struct ab8500_btemp_interrupts - ab8500 interrupts
- * @name:      name of the interrupt
- * @isr                function pointer to the isr
- */
-struct ab8500_btemp_interrupts {
-       char *name;
-       irqreturn_t (*isr)(int irq, void *data);
-};
-
-struct ab8500_btemp_events {
-       bool batt_rem;
-       bool btemp_high;
-       bool btemp_medhigh;
-       bool btemp_lowmed;
-       bool btemp_low;
-       bool ac_conn;
-       bool usb_conn;
-};
-
-struct ab8500_btemp_ranges {
-       int btemp_high_limit;
-       int btemp_med_limit;
-       int btemp_low_limit;
-};
-
-/**
- * struct ab8500_btemp - ab8500 BTEMP device information
- * @dev:               Pointer to the structure device
- * @node:              List of AB8500 BTEMPs, hence prepared for reentrance
- * @curr_source:       What current source we use, in uA
- * @bat_temp:          Dispatched battery temperature in degree Celcius
- * @prev_bat_temp      Last measured battery temperature in degree Celcius
- * @parent:            Pointer to the struct ab8500
- * @gpadc:             Pointer to the struct gpadc
- * @fg:                        Pointer to the struct fg
- * @bm:                Platform specific battery management information
- * @btemp_psy:         Structure for BTEMP specific battery properties
- * @events:            Structure for information about events triggered
- * @btemp_ranges:      Battery temperature range structure
- * @btemp_wq:          Work queue for measuring the temperature periodically
- * @btemp_periodic_work:       Work for measuring the temperature periodically
- * @initialized:       True if battery id read.
- */
-struct ab8500_btemp {
-       struct device *dev;
-       struct list_head node;
-       int curr_source;
-       int bat_temp;
-       int prev_bat_temp;
-       struct ab8500 *parent;
-       struct ab8500_gpadc *gpadc;
-       struct ab8500_fg *fg;
-       struct abx500_bm_data *bm;
-       struct power_supply *btemp_psy;
-       struct ab8500_btemp_events events;
-       struct ab8500_btemp_ranges btemp_ranges;
-       struct workqueue_struct *btemp_wq;
-       struct delayed_work btemp_periodic_work;
-       bool initialized;
-};
-
-/* BTEMP power supply properties */
-static enum power_supply_property ab8500_btemp_props[] = {
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static LIST_HEAD(ab8500_btemp_list);
-
-/**
- * ab8500_btemp_get() - returns a reference to the primary AB8500 BTEMP
- * (i.e. the first BTEMP in the instance list)
- */
-struct ab8500_btemp *ab8500_btemp_get(void)
-{
-       struct ab8500_btemp *btemp;
-       btemp = list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
-
-       return btemp;
-}
-EXPORT_SYMBOL(ab8500_btemp_get);
-
-/**
- * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
- * @di:                pointer to the ab8500_btemp structure
- * @v_batctrl: measured batctrl voltage
- * @inst_curr: measured instant current
- *
- * This function returns the battery resistance that is
- * derived from the BATCTRL voltage.
- * Returns value in Ohms.
- */
-static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
-       int v_batctrl, int inst_curr)
-{
-       int rbs;
-
-       if (is_ab8500_1p1_or_earlier(di->parent)) {
-               /*
-                * For ABB cut1.0 and 1.1 BAT_CTRL is internally
-                * connected to 1.8V through a 450k resistor
-                */
-               return (450000 * (v_batctrl)) / (1800 - v_batctrl);
-       }
-
-       if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
-               /*
-                * If the battery has internal NTC, we use the current
-                * source to calculate the resistance.
-                */
-               rbs = (v_batctrl * 1000
-                      - di->bm->gnd_lift_resistance * inst_curr)
-                     / di->curr_source;
-       } else {
-               /*
-                * BAT_CTRL is internally
-                * connected to 1.8V through a 80k resistor
-                */
-               rbs = (80000 * (v_batctrl)) / (1800 - v_batctrl);
-       }
-
-       return rbs;
-}
-
-/**
- * ab8500_btemp_read_batctrl_voltage() - measure batctrl voltage
- * @di:                pointer to the ab8500_btemp structure
- *
- * This function returns the voltage on BATCTRL. Returns value in mV.
- */
-static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di)
-{
-       int vbtemp;
-       static int prev;
-
-       vbtemp = ab8500_gpadc_convert(di->gpadc, BAT_CTRL);
-       if (vbtemp < 0) {
-               dev_err(di->dev,
-                       "%s gpadc conversion failed, using previous value",
-                       __func__);
-               return prev;
-       }
-       prev = vbtemp;
-       return vbtemp;
-}
-
-/**
- * ab8500_btemp_curr_source_enable() - enable/disable batctrl current source
- * @di:                pointer to the ab8500_btemp structure
- * @enable:    enable or disable the current source
- *
- * Enable or disable the current sources for the BatCtrl AD channel
- */
-static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
-       bool enable)
-{
-       int curr;
-       int ret = 0;
-
-       /*
-        * BATCTRL current sources are included on AB8500 cut2.0
-        * and future versions
-        */
-       if (is_ab8500_1p1_or_earlier(di->parent))
-               return 0;
-
-       /* Only do this for batteries with internal NTC */
-       if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
-
-               if (is_ab8540(di->parent)) {
-                       if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_60UA)
-                               curr = BAT_CTRL_60U_ENA;
-                       else
-                               curr = BAT_CTRL_120U_ENA;
-               } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
-                       if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA)
-                               curr = BAT_CTRL_16U_ENA;
-                       else
-                               curr = BAT_CTRL_18U_ENA;
-               } else {
-                       if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
-                               curr = BAT_CTRL_7U_ENA;
-                       else
-                               curr = BAT_CTRL_20U_ENA;
-               }
-
-               dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
-
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                       FORCE_BAT_CTRL_CMP_HIGH, FORCE_BAT_CTRL_CMP_HIGH);
-               if (ret) {
-                       dev_err(di->dev, "%s failed setting cmp_force\n",
-                               __func__);
-                       return ret;
-               }
-
-               /*
-                * We have to wait one 32kHz cycle before enabling
-                * the current source, since ForceBatCtrlCmpHigh needs
-                * to be written in a separate cycle
-                */
-               udelay(32);
-
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                       FORCE_BAT_CTRL_CMP_HIGH | curr);
-               if (ret) {
-                       dev_err(di->dev, "%s failed enabling current source\n",
-                               __func__);
-                       goto disable_curr_source;
-               }
-       } else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
-               dev_dbg(di->dev, "Disable BATCTRL curr source\n");
-
-               if (is_ab8540(di->parent)) {
-                       /* Write 0 to the curr bits */
-                       ret = abx500_mask_and_set_register_interruptible(
-                               di->dev,
-                               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                               BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA,
-                               ~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA));
-               } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
-                       /* Write 0 to the curr bits */
-                       ret = abx500_mask_and_set_register_interruptible(
-                               di->dev,
-                               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                               BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
-                               ~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
-               } else {
-                       /* Write 0 to the curr bits */
-                       ret = abx500_mask_and_set_register_interruptible(
-                               di->dev,
-                               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                               BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
-                               ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
-               }
-
-               if (ret) {
-                       dev_err(di->dev, "%s failed disabling current source\n",
-                               __func__);
-                       goto disable_curr_source;
-               }
-
-               /* Enable Pull-Up and comparator */
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                       BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
-                       BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
-               if (ret) {
-                       dev_err(di->dev, "%s failed enabling PU and comp\n",
-                               __func__);
-                       goto enable_pu_comp;
-               }
-
-               /*
-                * We have to wait one 32kHz cycle before disabling
-                * ForceBatCtrlCmpHigh since this needs to be written
-                * in a separate cycle
-                */
-               udelay(32);
-
-               /* Disable 'force comparator' */
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                       FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
-               if (ret) {
-                       dev_err(di->dev, "%s failed disabling force comp\n",
-                               __func__);
-                       goto disable_force_comp;
-               }
-       }
-       return ret;
-
-       /*
-        * We have to try unsetting FORCE_BAT_CTRL_CMP_HIGH one more time
-        * if we got an error above
-        */
-disable_curr_source:
-       if (is_ab8540(di->parent)) {
-               /* Write 0 to the curr bits */
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                       BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA,
-                       ~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA));
-       } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
-               /* Write 0 to the curr bits */
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                       BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
-                       ~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
-       } else {
-               /* Write 0 to the curr bits */
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-                       BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
-                       ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
-       }
-
-       if (ret) {
-               dev_err(di->dev, "%s failed disabling current source\n",
-                       __func__);
-               return ret;
-       }
-enable_pu_comp:
-       /* Enable Pull-Up and comparator */
-       ret = abx500_mask_and_set_register_interruptible(di->dev,
-               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-               BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
-               BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
-       if (ret) {
-               dev_err(di->dev, "%s failed enabling PU and comp\n",
-                       __func__);
-               return ret;
-       }
-
-disable_force_comp:
-       /*
-        * We have to wait one 32kHz cycle before disabling
-        * ForceBatCtrlCmpHigh since this needs to be written
-        * in a separate cycle
-        */
-       udelay(32);
-
-       /* Disable 'force comparator' */
-       ret = abx500_mask_and_set_register_interruptible(di->dev,
-               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-               FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
-       if (ret) {
-               dev_err(di->dev, "%s failed disabling force comp\n",
-                       __func__);
-               return ret;
-       }
-
-       return ret;
-}
-
-/**
- * ab8500_btemp_get_batctrl_res() - get battery resistance
- * @di:                pointer to the ab8500_btemp structure
- *
- * This function returns the battery pack identification resistance.
- * Returns value in Ohms.
- */
-static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
-{
-       int ret;
-       int batctrl = 0;
-       int res;
-       int inst_curr;
-       int i;
-
-       /*
-        * BATCTRL current sources are included on AB8500 cut2.0
-        * and future versions
-        */
-       ret = ab8500_btemp_curr_source_enable(di, true);
-       if (ret) {
-               dev_err(di->dev, "%s curr source enabled failed\n", __func__);
-               return ret;
-       }
-
-       if (!di->fg)
-               di->fg = ab8500_fg_get();
-       if (!di->fg) {
-               dev_err(di->dev, "No fg found\n");
-               return -EINVAL;
-       }
-
-       ret = ab8500_fg_inst_curr_start(di->fg);
-
-       if (ret) {
-               dev_err(di->dev, "Failed to start current measurement\n");
-               return ret;
-       }
-
-       do {
-               msleep(20);
-       } while (!ab8500_fg_inst_curr_started(di->fg));
-
-       i = 0;
-
-       do {
-               batctrl += ab8500_btemp_read_batctrl_voltage(di);
-               i++;
-               msleep(20);
-       } while (!ab8500_fg_inst_curr_done(di->fg));
-       batctrl /= i;
-
-       ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr);
-       if (ret) {
-               dev_err(di->dev, "Failed to finalize current measurement\n");
-               return ret;
-       }
-
-       res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);
-
-       ret = ab8500_btemp_curr_source_enable(di, false);
-       if (ret) {
-               dev_err(di->dev, "%s curr source disable failed\n", __func__);
-               return ret;
-       }
-
-       dev_dbg(di->dev, "%s batctrl: %d res: %d inst_curr: %d samples: %d\n",
-               __func__, batctrl, res, inst_curr, i);
-
-       return res;
-}
-
-/**
- * ab8500_btemp_res_to_temp() - resistance to temperature
- * @di:                pointer to the ab8500_btemp structure
- * @tbl:       pointer to the resiatance to temperature table
- * @tbl_size:  size of the resistance to temperature table
- * @res:       resistance to calculate the temperature from
- *
- * This function returns the battery temperature in degrees Celcius
- * based on the NTC resistance.
- */
-static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
-       const struct abx500_res_to_temp *tbl, int tbl_size, int res)
-{
-       int i, temp;
-       /*
-        * Calculate the formula for the straight line
-        * Simple interpolation if we are within
-        * the resistance table limits, extrapolate
-        * if resistance is outside the limits.
-        */
-       if (res > tbl[0].resist)
-               i = 0;
-       else if (res <= tbl[tbl_size - 1].resist)
-               i = tbl_size - 2;
-       else {
-               i = 0;
-               while (!(res <= tbl[i].resist &&
-                       res > tbl[i + 1].resist))
-                       i++;
-       }
-
-       temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
-               (res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
-       return temp;
-}
-
-/**
- * ab8500_btemp_measure_temp() - measure battery temperature
- * @di:                pointer to the ab8500_btemp structure
- *
- * Returns battery temperature (on success) else the previous temperature
- */
-static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
-{
-       int temp;
-       static int prev;
-       int rbat, rntc, vntc;
-       u8 id;
-
-       id = di->bm->batt_id;
-
-       if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
-                       id != BATTERY_UNKNOWN) {
-
-               rbat = ab8500_btemp_get_batctrl_res(di);
-               if (rbat < 0) {
-                       dev_err(di->dev, "%s get batctrl res failed\n",
-                               __func__);
-                       /*
-                        * Return out-of-range temperature so that
-                        * charging is stopped
-                        */
-                       return BTEMP_THERMAL_LOW_LIMIT;
-               }
-
-               temp = ab8500_btemp_res_to_temp(di,
-                       di->bm->bat_type[id].r_to_t_tbl,
-                       di->bm->bat_type[id].n_temp_tbl_elements, rbat);
-       } else {
-               vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL);
-               if (vntc < 0) {
-                       dev_err(di->dev,
-                               "%s gpadc conversion failed,"
-                               " using previous value\n", __func__);
-                       return prev;
-               }
-               /*
-                * The PCB NTC is sourced from VTVOUT via a 230kOhm
-                * resistor.
-                */
-               rntc = 230000 * vntc / (VTVOUT_V - vntc);
-
-               temp = ab8500_btemp_res_to_temp(di,
-                       di->bm->bat_type[id].r_to_t_tbl,
-                       di->bm->bat_type[id].n_temp_tbl_elements, rntc);
-               prev = temp;
-       }
-       dev_dbg(di->dev, "Battery temperature is %d\n", temp);
-       return temp;
-}
-
-/**
- * ab8500_btemp_id() - Identify the connected battery
- * @di:                pointer to the ab8500_btemp structure
- *
- * This function will try to identify the battery by reading the ID
- * resistor. Some brands use a combined ID resistor with a NTC resistor to
- * both be able to identify and to read the temperature of it.
- */
-static int ab8500_btemp_id(struct ab8500_btemp *di)
-{
-       int res;
-       u8 i;
-       if (is_ab8540(di->parent))
-               di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA;
-       else if (is_ab9540(di->parent) || is_ab8505(di->parent))
-               di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
-       else
-               di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
-
-       di->bm->batt_id = BATTERY_UNKNOWN;
-
-       res =  ab8500_btemp_get_batctrl_res(di);
-       if (res < 0) {
-               dev_err(di->dev, "%s get batctrl res failed\n", __func__);
-               return -ENXIO;
-       }
-
-       /* BATTERY_UNKNOWN is defined on position 0, skip it! */
-       for (i = BATTERY_UNKNOWN + 1; i < di->bm->n_btypes; i++) {
-               if ((res <= di->bm->bat_type[i].resis_high) &&
-                       (res >= di->bm->bat_type[i].resis_low)) {
-                       dev_dbg(di->dev, "Battery detected on %s"
-                               " low %d < res %d < high: %d"
-                               " index: %d\n",
-                               di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ?
-                               "BATCTRL" : "BATTEMP",
-                               di->bm->bat_type[i].resis_low, res,
-                               di->bm->bat_type[i].resis_high, i);
-
-                       di->bm->batt_id = i;
-                       break;
-               }
-       }
-
-       if (di->bm->batt_id == BATTERY_UNKNOWN) {
-               dev_warn(di->dev, "Battery identified as unknown"
-                       ", resistance %d Ohm\n", res);
-               return -ENXIO;
-       }
-
-       /*
-        * We only have to change current source if the
-        * detected type is Type 1.
-        */
-       if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
-           di->bm->batt_id == 1) {
-               if (is_ab8540(di->parent)) {
-                       dev_dbg(di->dev,
-                               "Set BATCTRL current source to 60uA\n");
-                       di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA;
-               } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
-                       dev_dbg(di->dev,
-                               "Set BATCTRL current source to 16uA\n");
-                       di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
-               } else {
-                       dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
-                       di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
-               }
-       }
-
-       return di->bm->batt_id;
-}
-
-/**
- * ab8500_btemp_periodic_work() - Measuring the temperature periodically
- * @work:      pointer to the work_struct structure
- *
- * Work function for measuring the temperature periodically
- */
-static void ab8500_btemp_periodic_work(struct work_struct *work)
-{
-       int interval;
-       int bat_temp;
-       struct ab8500_btemp *di = container_of(work,
-               struct ab8500_btemp, btemp_periodic_work.work);
-
-       if (!di->initialized) {
-               /* Identify the battery */
-               if (ab8500_btemp_id(di) < 0)
-                       dev_warn(di->dev, "failed to identify the battery\n");
-       }
-
-       bat_temp = ab8500_btemp_measure_temp(di);
-       /*
-        * Filter battery temperature.
-        * Allow direct updates on temperature only if two samples result in
-        * same temperature. Else only allow 1 degree change from previous
-        * reported value in the direction of the new measurement.
-        */
-       if ((bat_temp == di->prev_bat_temp) || !di->initialized) {
-               if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {
-                       di->initialized = true;
-                       di->bat_temp = bat_temp;
-                       power_supply_changed(di->btemp_psy);
-               }
-       } else if (bat_temp < di->prev_bat_temp) {
-               di->bat_temp--;
-               power_supply_changed(di->btemp_psy);
-       } else if (bat_temp > di->prev_bat_temp) {
-               di->bat_temp++;
-               power_supply_changed(di->btemp_psy);
-       }
-       di->prev_bat_temp = bat_temp;
-
-       if (di->events.ac_conn || di->events.usb_conn)
-               interval = di->bm->temp_interval_chg;
-       else
-               interval = di->bm->temp_interval_nochg;
-
-       /* Schedule a new measurement */
-       queue_delayed_work(di->btemp_wq,
-               &di->btemp_periodic_work,
-               round_jiffies(interval * HZ));
-}
-
-/**
- * ab8500_btemp_batctrlindb_handler() - battery removal detected
- * @irq:       interrupt number
- * @_di:       void pointer that has to address of ab8500_btemp
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di)
-{
-       struct ab8500_btemp *di = _di;
-       dev_err(di->dev, "Battery removal detected!\n");
-
-       di->events.batt_rem = true;
-       power_supply_changed(di->btemp_psy);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_btemp_templow_handler() - battery temp lower than 10 degrees
- * @irq:       interrupt number
- * @_di:       void pointer that has to address of ab8500_btemp
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
-{
-       struct ab8500_btemp *di = _di;
-
-       if (is_ab8500_3p3_or_earlier(di->parent)) {
-               dev_dbg(di->dev, "Ignore false btemp low irq"
-                       " for ABB cut 1.0, 1.1, 2.0 and 3.3\n");
-       } else {
-               dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
-
-               di->events.btemp_low = true;
-               di->events.btemp_high = false;
-               di->events.btemp_medhigh = false;
-               di->events.btemp_lowmed = false;
-               power_supply_changed(di->btemp_psy);
-       }
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_btemp_temphigh_handler() - battery temp higher than max temp
- * @irq:       interrupt number
- * @_di:       void pointer that has to address of ab8500_btemp
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di)
-{
-       struct ab8500_btemp *di = _di;
-
-       dev_crit(di->dev, "Battery temperature is higher than MAX temp\n");
-
-       di->events.btemp_high = true;
-       di->events.btemp_medhigh = false;
-       di->events.btemp_lowmed = false;
-       di->events.btemp_low = false;
-       power_supply_changed(di->btemp_psy);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_btemp_lowmed_handler() - battery temp between low and medium
- * @irq:       interrupt number
- * @_di:       void pointer that has to address of ab8500_btemp
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di)
-{
-       struct ab8500_btemp *di = _di;
-
-       dev_dbg(di->dev, "Battery temperature is between low and medium\n");
-
-       di->events.btemp_lowmed = true;
-       di->events.btemp_medhigh = false;
-       di->events.btemp_high = false;
-       di->events.btemp_low = false;
-       power_supply_changed(di->btemp_psy);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_btemp_medhigh_handler() - battery temp between medium and high
- * @irq:       interrupt number
- * @_di:       void pointer that has to address of ab8500_btemp
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di)
-{
-       struct ab8500_btemp *di = _di;
-
-       dev_dbg(di->dev, "Battery temperature is between medium and high\n");
-
-       di->events.btemp_medhigh = true;
-       di->events.btemp_lowmed = false;
-       di->events.btemp_high = false;
-       di->events.btemp_low = false;
-       power_supply_changed(di->btemp_psy);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_btemp_periodic() - Periodic temperature measurements
- * @di:                pointer to the ab8500_btemp structure
- * @enable:    enable or disable periodic temperature measurements
- *
- * Starts of stops periodic temperature measurements. Periodic measurements
- * should only be done when a charger is connected.
- */
-static void ab8500_btemp_periodic(struct ab8500_btemp *di,
-       bool enable)
-{
-       dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",
-               enable);
-       /*
-        * Make sure a new measurement is done directly by cancelling
-        * any pending work
-        */
-       cancel_delayed_work_sync(&di->btemp_periodic_work);
-
-       if (enable)
-               queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
-}
-
-/**
- * ab8500_btemp_get_temp() - get battery temperature
- * @di:                pointer to the ab8500_btemp structure
- *
- * Returns battery temperature
- */
-int ab8500_btemp_get_temp(struct ab8500_btemp *di)
-{
-       int temp = 0;
-
-       /*
-        * The BTEMP events are not reliabe on AB8500 cut3.3
-        * and prior versions
-        */
-       if (is_ab8500_3p3_or_earlier(di->parent)) {
-               temp = di->bat_temp * 10;
-       } else {
-               if (di->events.btemp_low) {
-                       if (temp > di->btemp_ranges.btemp_low_limit)
-                               temp = di->btemp_ranges.btemp_low_limit * 10;
-                       else
-                               temp = di->bat_temp * 10;
-               } else if (di->events.btemp_high) {
-                       if (temp < di->btemp_ranges.btemp_high_limit)
-                               temp = di->btemp_ranges.btemp_high_limit * 10;
-                       else
-                               temp = di->bat_temp * 10;
-               } else if (di->events.btemp_lowmed) {
-                       if (temp > di->btemp_ranges.btemp_med_limit)
-                               temp = di->btemp_ranges.btemp_med_limit * 10;
-                       else
-                               temp = di->bat_temp * 10;
-               } else if (di->events.btemp_medhigh) {
-                       if (temp < di->btemp_ranges.btemp_med_limit)
-                               temp = di->btemp_ranges.btemp_med_limit * 10;
-                       else
-                               temp = di->bat_temp * 10;
-               } else
-                       temp = di->bat_temp * 10;
-       }
-       return temp;
-}
-EXPORT_SYMBOL(ab8500_btemp_get_temp);
-
-/**
- * ab8500_btemp_get_batctrl_temp() - get the temperature
- * @btemp:      pointer to the btemp structure
- *
- * Returns the batctrl temperature in millidegrees
- */
-int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp)
-{
-       return btemp->bat_temp * 1000;
-}
-EXPORT_SYMBOL(ab8500_btemp_get_batctrl_temp);
-
-/**
- * ab8500_btemp_get_property() - get the btemp properties
- * @psy:        pointer to the power_supply structure
- * @psp:        pointer to the power_supply_property structure
- * @val:        pointer to the power_supply_propval union
- *
- * This function gets called when an application tries to get the btemp
- * properties by reading the sysfs files.
- * online:     presence of the battery
- * present:    presence of the battery
- * technology: battery technology
- * temp:       battery temperature
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_btemp_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       struct ab8500_btemp *di = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-       case POWER_SUPPLY_PROP_ONLINE:
-               if (di->events.batt_rem)
-                       val->intval = 0;
-               else
-                       val->intval = 1;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = di->bm->bat_type[di->bm->batt_id].name;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = ab8500_btemp_get_temp(di);
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
-{
-       struct power_supply *psy;
-       struct power_supply *ext = dev_get_drvdata(dev);
-       const char **supplicants = (const char **)ext->supplied_to;
-       struct ab8500_btemp *di;
-       union power_supply_propval ret;
-       int j;
-
-       psy = (struct power_supply *)data;
-       di = power_supply_get_drvdata(psy);
-
-       /*
-        * For all psy where the name of your driver
-        * appears in any supplied_to
-        */
-       j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
-       if (j < 0)
-               return 0;
-
-       /* Go through all properties for the psy */
-       for (j = 0; j < ext->desc->num_properties; j++) {
-               enum power_supply_property prop;
-               prop = ext->desc->properties[j];
-
-               if (power_supply_get_property(ext, prop, &ret))
-                       continue;
-
-               switch (prop) {
-               case POWER_SUPPLY_PROP_PRESENT:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_MAINS:
-                               /* AC disconnected */
-                               if (!ret.intval && di->events.ac_conn) {
-                                       di->events.ac_conn = false;
-                               }
-                               /* AC connected */
-                               else if (ret.intval && !di->events.ac_conn) {
-                                       di->events.ac_conn = true;
-                                       if (!di->events.usb_conn)
-                                               ab8500_btemp_periodic(di, true);
-                               }
-                               break;
-                       case POWER_SUPPLY_TYPE_USB:
-                               /* USB disconnected */
-                               if (!ret.intval && di->events.usb_conn) {
-                                       di->events.usb_conn = false;
-                               }
-                               /* USB connected */
-                               else if (ret.intval && !di->events.usb_conn) {
-                                       di->events.usb_conn = true;
-                                       if (!di->events.ac_conn)
-                                               ab8500_btemp_periodic(di, true);
-                               }
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-               default:
-                       break;
-               }
-       }
-       return 0;
-}
-
-/**
- * ab8500_btemp_external_power_changed() - callback for power supply changes
- * @psy:       pointer to the structure power_supply
- *
- * This function is pointing to the function pointer external_power_changed
- * of the structure power_supply.
- * This function gets executed when there is a change in the external power
- * supply to the btemp.
- */
-static void ab8500_btemp_external_power_changed(struct power_supply *psy)
-{
-       struct ab8500_btemp *di = power_supply_get_drvdata(psy);
-
-       class_for_each_device(power_supply_class, NULL,
-               di->btemp_psy, ab8500_btemp_get_ext_psy_data);
-}
-
-/* ab8500 btemp driver interrupts and their respective isr */
-static struct ab8500_btemp_interrupts ab8500_btemp_irq[] = {
-       {"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler},
-       {"BTEMP_LOW", ab8500_btemp_templow_handler},
-       {"BTEMP_HIGH", ab8500_btemp_temphigh_handler},
-       {"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler},
-       {"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler},
-};
-
-#if defined(CONFIG_PM)
-static int ab8500_btemp_resume(struct platform_device *pdev)
-{
-       struct ab8500_btemp *di = platform_get_drvdata(pdev);
-
-       ab8500_btemp_periodic(di, true);
-
-       return 0;
-}
-
-static int ab8500_btemp_suspend(struct platform_device *pdev,
-       pm_message_t state)
-{
-       struct ab8500_btemp *di = platform_get_drvdata(pdev);
-
-       ab8500_btemp_periodic(di, false);
-
-       return 0;
-}
-#else
-#define ab8500_btemp_suspend      NULL
-#define ab8500_btemp_resume       NULL
-#endif
-
-static int ab8500_btemp_remove(struct platform_device *pdev)
-{
-       struct ab8500_btemp *di = platform_get_drvdata(pdev);
-       int i, irq;
-
-       /* Disable interrupts */
-       for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
-               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
-               free_irq(irq, di);
-       }
-
-       /* Delete the work queue */
-       destroy_workqueue(di->btemp_wq);
-
-       flush_scheduled_work();
-       power_supply_unregister(di->btemp_psy);
-
-       return 0;
-}
-
-static char *supply_interface[] = {
-       "ab8500_chargalg",
-       "ab8500_fg",
-};
-
-static const struct power_supply_desc ab8500_btemp_desc = {
-       .name                   = "ab8500_btemp",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = ab8500_btemp_props,
-       .num_properties         = ARRAY_SIZE(ab8500_btemp_props),
-       .get_property           = ab8500_btemp_get_property,
-       .external_power_changed = ab8500_btemp_external_power_changed,
-};
-
-static int ab8500_btemp_probe(struct platform_device *pdev)
-{
-       struct device_node *np = pdev->dev.of_node;
-       struct abx500_bm_data *plat = pdev->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       struct ab8500_btemp *di;
-       int irq, i, ret = 0;
-       u8 val;
-
-       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di) {
-               dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__);
-               return -ENOMEM;
-       }
-
-       if (!plat) {
-               dev_err(&pdev->dev, "no battery management data supplied\n");
-               return -EINVAL;
-       }
-       di->bm = plat;
-
-       if (np) {
-               ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
-               if (ret) {
-                       dev_err(&pdev->dev, "failed to get battery information\n");
-                       return ret;
-               }
-       }
-
-       /* get parent data */
-       di->dev = &pdev->dev;
-       di->parent = dev_get_drvdata(pdev->dev.parent);
-       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-
-       di->initialized = false;
-
-       psy_cfg.supplied_to = supply_interface;
-       psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
-       psy_cfg.drv_data = di;
-
-       /* Create a work queue for the btemp */
-       di->btemp_wq =
-               create_singlethread_workqueue("ab8500_btemp_wq");
-       if (di->btemp_wq == NULL) {
-               dev_err(di->dev, "failed to create work queue\n");
-               return -ENOMEM;
-       }
-
-       /* Init work for measuring temperature periodically */
-       INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
-               ab8500_btemp_periodic_work);
-
-       /* Set BTEMP thermal limits. Low and Med are fixed */
-       di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
-       di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-               AB8500_BTEMP_HIGH_TH, &val);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               goto free_btemp_wq;
-       }
-       switch (val) {
-       case BTEMP_HIGH_TH_57_0:
-       case BTEMP_HIGH_TH_57_1:
-               di->btemp_ranges.btemp_high_limit =
-                       BTEMP_THERMAL_HIGH_LIMIT_57;
-               break;
-       case BTEMP_HIGH_TH_52:
-               di->btemp_ranges.btemp_high_limit =
-                       BTEMP_THERMAL_HIGH_LIMIT_52;
-               break;
-       case BTEMP_HIGH_TH_62:
-               di->btemp_ranges.btemp_high_limit =
-                       BTEMP_THERMAL_HIGH_LIMIT_62;
-               break;
-       }
-
-       /* Register BTEMP power supply class */
-       di->btemp_psy = power_supply_register(di->dev, &ab8500_btemp_desc,
-                                             &psy_cfg);
-       if (IS_ERR(di->btemp_psy)) {
-               dev_err(di->dev, "failed to register BTEMP psy\n");
-               ret = PTR_ERR(di->btemp_psy);
-               goto free_btemp_wq;
-       }
-
-       /* Register interrupts */
-       for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
-               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
-               ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr,
-                       IRQF_SHARED | IRQF_NO_SUSPEND,
-                       ab8500_btemp_irq[i].name, di);
-
-               if (ret) {
-                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
-                               , ab8500_btemp_irq[i].name, irq, ret);
-                       goto free_irq;
-               }
-               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
-                       ab8500_btemp_irq[i].name, irq, ret);
-       }
-
-       platform_set_drvdata(pdev, di);
-
-       /* Kick off periodic temperature measurements */
-       ab8500_btemp_periodic(di, true);
-       list_add_tail(&di->node, &ab8500_btemp_list);
-
-       return ret;
-
-free_irq:
-       power_supply_unregister(di->btemp_psy);
-
-       /* We also have to free all successfully registered irqs */
-       for (i = i - 1; i >= 0; i--) {
-               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
-               free_irq(irq, di);
-       }
-free_btemp_wq:
-       destroy_workqueue(di->btemp_wq);
-       return ret;
-}
-
-static const struct of_device_id ab8500_btemp_match[] = {
-       { .compatible = "stericsson,ab8500-btemp", },
-       { },
-};
-
-static struct platform_driver ab8500_btemp_driver = {
-       .probe = ab8500_btemp_probe,
-       .remove = ab8500_btemp_remove,
-       .suspend = ab8500_btemp_suspend,
-       .resume = ab8500_btemp_resume,
-       .driver = {
-               .name = "ab8500-btemp",
-               .of_match_table = ab8500_btemp_match,
-       },
-};
-
-static int __init ab8500_btemp_init(void)
-{
-       return platform_driver_register(&ab8500_btemp_driver);
-}
-
-static void __exit ab8500_btemp_exit(void)
-{
-       platform_driver_unregister(&ab8500_btemp_driver);
-}
-
-device_initcall(ab8500_btemp_init);
-module_exit(ab8500_btemp_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
-MODULE_ALIAS("platform:ab8500-btemp");
-MODULE_DESCRIPTION("AB8500 battery temperature driver");
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
deleted file mode 100644 (file)
index 30de5d4..0000000
+++ /dev/null
@@ -1,3765 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * Charger driver for AB8500
- *
- * License Terms: GNU General Public License v2
- * Author:
- *     Johan Palsson <johan.palsson@stericsson.com>
- *     Karl Komierowski <karl.komierowski@stericsson.com>
- *     Arun R Murthy <arun.murthy@stericsson.com>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/notifier.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/completion.h>
-#include <linux/regulator/consumer.h>
-#include <linux/err.h>
-#include <linux/workqueue.h>
-#include <linux/kobject.h>
-#include <linux/of.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
-#include <linux/mfd/abx500/ab8500-gpadc.h>
-#include <linux/mfd/abx500/ux500_chargalg.h>
-#include <linux/usb/otg.h>
-#include <linux/mutex.h>
-
-/* Charger constants */
-#define NO_PW_CONN                     0
-#define AC_PW_CONN                     1
-#define USB_PW_CONN                    2
-
-#define MAIN_WDOG_ENA                  0x01
-#define MAIN_WDOG_KICK                 0x02
-#define MAIN_WDOG_DIS                  0x00
-#define CHARG_WD_KICK                  0x01
-#define MAIN_CH_ENA                    0x01
-#define MAIN_CH_NO_OVERSHOOT_ENA_N     0x02
-#define USB_CH_ENA                     0x01
-#define USB_CHG_NO_OVERSHOOT_ENA_N     0x02
-#define MAIN_CH_DET                    0x01
-#define MAIN_CH_CV_ON                  0x04
-#define USB_CH_CV_ON                   0x08
-#define VBUS_DET_DBNC100               0x02
-#define VBUS_DET_DBNC1                 0x01
-#define OTP_ENABLE_WD                  0x01
-#define DROP_COUNT_RESET               0x01
-#define USB_CH_DET                     0x01
-
-#define MAIN_CH_INPUT_CURR_SHIFT       4
-#define VBUS_IN_CURR_LIM_SHIFT         4
-#define AB8540_VBUS_IN_CURR_LIM_SHIFT  2
-#define AUTO_VBUS_IN_CURR_LIM_SHIFT    4
-#define AB8540_AUTO_VBUS_IN_CURR_MASK  0x3F
-#define VBUS_IN_CURR_LIM_RETRY_SET_TIME        30 /* seconds */
-
-#define LED_INDICATOR_PWM_ENA          0x01
-#define LED_INDICATOR_PWM_DIS          0x00
-#define LED_IND_CUR_5MA                        0x04
-#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
-
-/* HW failure constants */
-#define MAIN_CH_TH_PROT                        0x02
-#define VBUS_CH_NOK                    0x08
-#define USB_CH_TH_PROT                 0x02
-#define VBUS_OVV_TH                    0x01
-#define MAIN_CH_NOK                    0x01
-#define VBUS_DET                       0x80
-
-#define MAIN_CH_STATUS2_MAINCHGDROP            0x80
-#define MAIN_CH_STATUS2_MAINCHARGERDETDBNC     0x40
-#define USB_CH_VBUSDROP                                0x40
-#define USB_CH_VBUSDETDBNC                     0x01
-
-/* UsbLineStatus register bit masks */
-#define AB8500_USB_LINK_STATUS         0x78
-#define AB8505_USB_LINK_STATUS         0xF8
-#define AB8500_STD_HOST_SUSP           0x18
-#define USB_LINK_STATUS_SHIFT          3
-
-/* Watchdog timeout constant */
-#define WD_TIMER                       0x30 /* 4min */
-#define WD_KICK_INTERVAL               (60 * HZ)
-
-/* Lowest charger voltage is 3.39V -> 0x4E */
-#define LOW_VOLT_REG                   0x4E
-
-/* Step up/down delay in us */
-#define STEP_UDELAY                    1000
-
-#define CHARGER_STATUS_POLL 10 /* in ms */
-
-#define CHG_WD_INTERVAL                        (60 * HZ)
-
-#define AB8500_SW_CONTROL_FALLBACK     0x03
-/* Wait for enumeration before charing in us */
-#define WAIT_ACA_RID_ENUMERATION       (5 * 1000)
-/*External charger control*/
-#define AB8500_SYS_CHARGER_CONTROL_REG         0x52
-#define EXTERNAL_CHARGER_DISABLE_REG_VAL       0x03
-#define EXTERNAL_CHARGER_ENABLE_REG_VAL                0x07
-
-/* UsbLineStatus register - usb types */
-enum ab8500_charger_link_status {
-       USB_STAT_NOT_CONFIGURED,
-       USB_STAT_STD_HOST_NC,
-       USB_STAT_STD_HOST_C_NS,
-       USB_STAT_STD_HOST_C_S,
-       USB_STAT_HOST_CHG_NM,
-       USB_STAT_HOST_CHG_HS,
-       USB_STAT_HOST_CHG_HS_CHIRP,
-       USB_STAT_DEDICATED_CHG,
-       USB_STAT_ACA_RID_A,
-       USB_STAT_ACA_RID_B,
-       USB_STAT_ACA_RID_C_NM,
-       USB_STAT_ACA_RID_C_HS,
-       USB_STAT_ACA_RID_C_HS_CHIRP,
-       USB_STAT_HM_IDGND,
-       USB_STAT_RESERVED,
-       USB_STAT_NOT_VALID_LINK,
-       USB_STAT_PHY_EN,
-       USB_STAT_SUP_NO_IDGND_VBUS,
-       USB_STAT_SUP_IDGND_VBUS,
-       USB_STAT_CHARGER_LINE_1,
-       USB_STAT_CARKIT_1,
-       USB_STAT_CARKIT_2,
-       USB_STAT_ACA_DOCK_CHARGER,
-};
-
-enum ab8500_usb_state {
-       AB8500_BM_USB_STATE_RESET_HS,   /* HighSpeed Reset */
-       AB8500_BM_USB_STATE_RESET_FS,   /* FullSpeed/LowSpeed Reset */
-       AB8500_BM_USB_STATE_CONFIGURED,
-       AB8500_BM_USB_STATE_SUSPEND,
-       AB8500_BM_USB_STATE_RESUME,
-       AB8500_BM_USB_STATE_MAX,
-};
-
-/* VBUS input current limits supported in AB8500 in mA */
-#define USB_CH_IP_CUR_LVL_0P05         50
-#define USB_CH_IP_CUR_LVL_0P09         98
-#define USB_CH_IP_CUR_LVL_0P19         193
-#define USB_CH_IP_CUR_LVL_0P29         290
-#define USB_CH_IP_CUR_LVL_0P38         380
-#define USB_CH_IP_CUR_LVL_0P45         450
-#define USB_CH_IP_CUR_LVL_0P5          500
-#define USB_CH_IP_CUR_LVL_0P6          600
-#define USB_CH_IP_CUR_LVL_0P7          700
-#define USB_CH_IP_CUR_LVL_0P8          800
-#define USB_CH_IP_CUR_LVL_0P9          900
-#define USB_CH_IP_CUR_LVL_1P0          1000
-#define USB_CH_IP_CUR_LVL_1P1          1100
-#define USB_CH_IP_CUR_LVL_1P3          1300
-#define USB_CH_IP_CUR_LVL_1P4          1400
-#define USB_CH_IP_CUR_LVL_1P5          1500
-
-#define VBAT_TRESH_IP_CUR_RED          3800
-
-#define to_ab8500_charger_usb_device_info(x) container_of((x), \
-       struct ab8500_charger, usb_chg)
-#define to_ab8500_charger_ac_device_info(x) container_of((x), \
-       struct ab8500_charger, ac_chg)
-
-/**
- * struct ab8500_charger_interrupts - ab8500 interupts
- * @name:      name of the interrupt
- * @isr                function pointer to the isr
- */
-struct ab8500_charger_interrupts {
-       char *name;
-       irqreturn_t (*isr)(int irq, void *data);
-};
-
-struct ab8500_charger_info {
-       int charger_connected;
-       int charger_online;
-       int charger_voltage;
-       int cv_active;
-       bool wd_expired;
-       int charger_current;
-};
-
-struct ab8500_charger_event_flags {
-       bool mainextchnotok;
-       bool main_thermal_prot;
-       bool usb_thermal_prot;
-       bool vbus_ovv;
-       bool usbchargernotok;
-       bool chgwdexp;
-       bool vbus_collapse;
-       bool vbus_drop_end;
-};
-
-struct ab8500_charger_usb_state {
-       int usb_current;
-       int usb_current_tmp;
-       enum ab8500_usb_state state;
-       enum ab8500_usb_state state_tmp;
-       spinlock_t usb_lock;
-};
-
-struct ab8500_charger_max_usb_in_curr {
-       int usb_type_max;
-       int set_max;
-       int calculated_max;
-};
-
-/**
- * struct ab8500_charger - ab8500 Charger device information
- * @dev:               Pointer to the structure device
- * @vbus_detected:     VBUS detected
- * @vbus_detected_start:
- *                     VBUS detected during startup
- * @ac_conn:           This will be true when the AC charger has been plugged
- * @vddadc_en_ac:      Indicate if VDD ADC supply is enabled because AC
- *                     charger is enabled
- * @vddadc_en_usb:     Indicate if VDD ADC supply is enabled because USB
- *                     charger is enabled
- * @vbat               Battery voltage
- * @old_vbat           Previously measured battery voltage
- * @usb_device_is_unrecognised USB device is unrecognised by the hardware
- * @autopower          Indicate if we should have automatic pwron after pwrloss
- * @autopower_cfg      platform specific power config support for "pwron after pwrloss"
- * @invalid_charger_detect_state State when forcing AB to use invalid charger
- * @is_aca_rid:                Incicate if accessory is ACA type
- * @current_stepping_sessions:
- *                     Counter for current stepping sessions
- * @parent:            Pointer to the struct ab8500
- * @gpadc:             Pointer to the struct gpadc
- * @bm:                Platform specific battery management information
- * @flags:             Structure for information about events triggered
- * @usb_state:         Structure for usb stack information
- * @max_usb_in_curr:   Max USB charger input current
- * @ac_chg:            AC charger power supply
- * @usb_chg:           USB charger power supply
- * @ac:                        Structure that holds the AC charger properties
- * @usb:               Structure that holds the USB charger properties
- * @regu:              Pointer to the struct regulator
- * @charger_wq:                Work queue for the IRQs and checking HW state
- * @usb_ipt_crnt_lock: Lock to protect VBUS input current setting from mutuals
- * @pm_lock:           Lock to prevent system to suspend
- * @check_vbat_work    Work for checking vbat threshold to adjust vbus current
- * @check_hw_failure_work:     Work for checking HW state
- * @check_usbchgnotok_work:    Work for checking USB charger not ok status
- * @kick_wd_work:              Work for kicking the charger watchdog in case
- *                             of ABB rev 1.* due to the watchog logic bug
- * @ac_charger_attached_work:  Work for checking if AC charger is still
- *                             connected
- * @usb_charger_attached_work: Work for checking if USB charger is still
- *                             connected
- * @ac_work:                   Work for checking AC charger connection
- * @detect_usb_type_work:      Work for detecting the USB type connected
- * @usb_link_status_work:      Work for checking the new USB link status
- * @usb_state_changed_work:    Work for checking USB state
- * @attach_work:               Work for detecting USB type
- * @vbus_drop_end_work:                Work for detecting VBUS drop end
- * @check_main_thermal_prot_work:
- *                             Work for checking Main thermal status
- * @check_usb_thermal_prot_work:
- *                             Work for checking USB thermal status
- * @charger_attached_mutex:    For controlling the wakelock
- */
-struct ab8500_charger {
-       struct device *dev;
-       bool vbus_detected;
-       bool vbus_detected_start;
-       bool ac_conn;
-       bool vddadc_en_ac;
-       bool vddadc_en_usb;
-       int vbat;
-       int old_vbat;
-       bool usb_device_is_unrecognised;
-       bool autopower;
-       bool autopower_cfg;
-       int invalid_charger_detect_state;
-       int is_aca_rid;
-       atomic_t current_stepping_sessions;
-       struct ab8500 *parent;
-       struct ab8500_gpadc *gpadc;
-       struct abx500_bm_data *bm;
-       struct ab8500_charger_event_flags flags;
-       struct ab8500_charger_usb_state usb_state;
-       struct ab8500_charger_max_usb_in_curr max_usb_in_curr;
-       struct ux500_charger ac_chg;
-       struct ux500_charger usb_chg;
-       struct ab8500_charger_info ac;
-       struct ab8500_charger_info usb;
-       struct regulator *regu;
-       struct workqueue_struct *charger_wq;
-       struct mutex usb_ipt_crnt_lock;
-       struct delayed_work check_vbat_work;
-       struct delayed_work check_hw_failure_work;
-       struct delayed_work check_usbchgnotok_work;
-       struct delayed_work kick_wd_work;
-       struct delayed_work usb_state_changed_work;
-       struct delayed_work attach_work;
-       struct delayed_work ac_charger_attached_work;
-       struct delayed_work usb_charger_attached_work;
-       struct delayed_work vbus_drop_end_work;
-       struct work_struct ac_work;
-       struct work_struct detect_usb_type_work;
-       struct work_struct usb_link_status_work;
-       struct work_struct check_main_thermal_prot_work;
-       struct work_struct check_usb_thermal_prot_work;
-       struct usb_phy *usb_phy;
-       struct notifier_block nb;
-       struct mutex charger_attached_mutex;
-};
-
-/* AC properties */
-static enum power_supply_property ab8500_charger_ac_props[] = {
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-};
-
-/* USB properties */
-static enum power_supply_property ab8500_charger_usb_props[] = {
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-};
-
-/*
- * Function for enabling and disabling sw fallback mode
- * should always be disabled when no charger is connected.
- */
-static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
-               bool fallback)
-{
-       u8 val;
-       u8 reg;
-       u8 bank;
-       u8 bit;
-       int ret;
-
-       dev_dbg(di->dev, "SW Fallback: %d\n", fallback);
-
-       if (is_ab8500(di->parent)) {
-               bank = 0x15;
-               reg = 0x0;
-               bit = 3;
-       } else {
-               bank = AB8500_SYS_CTRL1_BLOCK;
-               reg = AB8500_SW_CONTROL_FALLBACK;
-               bit = 0;
-       }
-
-       /* read the register containing fallback bit */
-       ret = abx500_get_register_interruptible(di->dev, bank, reg, &val);
-       if (ret < 0) {
-               dev_err(di->dev, "%d read failed\n", __LINE__);
-               return;
-       }
-
-       if (is_ab8500(di->parent)) {
-               /* enable the OPT emulation registers */
-               ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x2);
-               if (ret) {
-                       dev_err(di->dev, "%d write failed\n", __LINE__);
-                       goto disable_otp;
-               }
-       }
-
-       if (fallback)
-               val |= (1 << bit);
-       else
-               val &= ~(1 << bit);
-
-       /* write back the changed fallback bit value to register */
-       ret = abx500_set_register_interruptible(di->dev, bank, reg, val);
-       if (ret) {
-               dev_err(di->dev, "%d write failed\n", __LINE__);
-       }
-
-disable_otp:
-       if (is_ab8500(di->parent)) {
-               /* disable the set OTP registers again */
-               ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x0);
-               if (ret) {
-                       dev_err(di->dev, "%d write failed\n", __LINE__);
-               }
-       }
-}
-
-/**
- * ab8500_power_supply_changed - a wrapper with local extentions for
- * power_supply_changed
- * @di:          pointer to the ab8500_charger structure
- * @psy:  pointer to power_supply_that have changed.
- *
- */
-static void ab8500_power_supply_changed(struct ab8500_charger *di,
-                                       struct power_supply *psy)
-{
-       if (di->autopower_cfg) {
-               if (!di->usb.charger_connected &&
-                   !di->ac.charger_connected &&
-                   di->autopower) {
-                       di->autopower = false;
-                       ab8500_enable_disable_sw_fallback(di, false);
-               } else if (!di->autopower &&
-                          (di->ac.charger_connected ||
-                           di->usb.charger_connected)) {
-                       di->autopower = true;
-                       ab8500_enable_disable_sw_fallback(di, true);
-               }
-       }
-       power_supply_changed(psy);
-}
-
-static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
-       bool connected)
-{
-       if (connected != di->usb.charger_connected) {
-               dev_dbg(di->dev, "USB connected:%i\n", connected);
-               di->usb.charger_connected = connected;
-
-               if (!connected)
-                       di->flags.vbus_drop_end = false;
-
-               sysfs_notify(&di->usb_chg.psy->dev.kobj, NULL, "present");
-
-               if (connected) {
-                       mutex_lock(&di->charger_attached_mutex);
-                       mutex_unlock(&di->charger_attached_mutex);
-
-                       if (is_ab8500(di->parent))
-                               queue_delayed_work(di->charger_wq,
-                                          &di->usb_charger_attached_work,
-                                          HZ);
-               } else {
-                       cancel_delayed_work_sync(&di->usb_charger_attached_work);
-                       mutex_lock(&di->charger_attached_mutex);
-                       mutex_unlock(&di->charger_attached_mutex);
-               }
-       }
-}
-
-/**
- * ab8500_charger_get_ac_voltage() - get ac charger voltage
- * @di:                pointer to the ab8500_charger structure
- *
- * Returns ac charger voltage (on success)
- */
-static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di)
-{
-       int vch;
-
-       /* Only measure voltage if the charger is connected */
-       if (di->ac.charger_connected) {
-               vch = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_V);
-               if (vch < 0)
-                       dev_err(di->dev, "%s gpadc conv failed,\n", __func__);
-       } else {
-               vch = 0;
-       }
-       return vch;
-}
-
-/**
- * ab8500_charger_ac_cv() - check if the main charger is in CV mode
- * @di:                pointer to the ab8500_charger structure
- *
- * Returns ac charger CV mode (on success) else error code
- */
-static int ab8500_charger_ac_cv(struct ab8500_charger *di)
-{
-       u8 val;
-       int ret = 0;
-
-       /* Only check CV mode if the charger is online */
-       if (di->ac.charger_online) {
-               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_CH_STATUS1_REG, &val);
-               if (ret < 0) {
-                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-                       return 0;
-               }
-
-               if (val & MAIN_CH_CV_ON)
-                       ret = 1;
-               else
-                       ret = 0;
-       }
-
-       return ret;
-}
-
-/**
- * ab8500_charger_get_vbus_voltage() - get vbus voltage
- * @di:                pointer to the ab8500_charger structure
- *
- * This function returns the vbus voltage.
- * Returns vbus voltage (on success)
- */
-static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di)
-{
-       int vch;
-
-       /* Only measure voltage if the charger is connected */
-       if (di->usb.charger_connected) {
-               vch = ab8500_gpadc_convert(di->gpadc, VBUS_V);
-               if (vch < 0)
-                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
-       } else {
-               vch = 0;
-       }
-       return vch;
-}
-
-/**
- * ab8500_charger_get_usb_current() - get usb charger current
- * @di:                pointer to the ab8500_charger structure
- *
- * This function returns the usb charger current.
- * Returns usb current (on success) and error code on failure
- */
-static int ab8500_charger_get_usb_current(struct ab8500_charger *di)
-{
-       int ich;
-
-       /* Only measure current if the charger is online */
-       if (di->usb.charger_online) {
-               ich = ab8500_gpadc_convert(di->gpadc, USB_CHARGER_C);
-               if (ich < 0)
-                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
-       } else {
-               ich = 0;
-       }
-       return ich;
-}
-
-/**
- * ab8500_charger_get_ac_current() - get ac charger current
- * @di:                pointer to the ab8500_charger structure
- *
- * This function returns the ac charger current.
- * Returns ac current (on success) and error code on failure.
- */
-static int ab8500_charger_get_ac_current(struct ab8500_charger *di)
-{
-       int ich;
-
-       /* Only measure current if the charger is online */
-       if (di->ac.charger_online) {
-               ich = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_C);
-               if (ich < 0)
-                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
-       } else {
-               ich = 0;
-       }
-       return ich;
-}
-
-/**
- * ab8500_charger_usb_cv() - check if the usb charger is in CV mode
- * @di:                pointer to the ab8500_charger structure
- *
- * Returns ac charger CV mode (on success) else error code
- */
-static int ab8500_charger_usb_cv(struct ab8500_charger *di)
-{
-       int ret;
-       u8 val;
-
-       /* Only check CV mode if the charger is online */
-       if (di->usb.charger_online) {
-               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_CH_USBCH_STAT1_REG, &val);
-               if (ret < 0) {
-                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-                       return 0;
-               }
-
-               if (val & USB_CH_CV_ON)
-                       ret = 1;
-               else
-                       ret = 0;
-       } else {
-               ret = 0;
-       }
-
-       return ret;
-}
-
-/**
- * ab8500_charger_detect_chargers() - Detect the connected chargers
- * @di:                pointer to the ab8500_charger structure
- * @probe:     if probe, don't delay and wait for HW
- *
- * Returns the type of charger connected.
- * For USB it will not mean we can actually charge from it
- * but that there is a USB cable connected that we have to
- * identify. This is used during startup when we don't get
- * interrupts of the charger detection
- *
- * Returns an integer value, that means,
- * NO_PW_CONN  no power supply is connected
- * AC_PW_CONN  if the AC power supply is connected
- * USB_PW_CONN  if the USB power supply is connected
- * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
- */
-static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
-{
-       int result = NO_PW_CONN;
-       int ret;
-       u8 val;
-
-       /* Check for AC charger */
-       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-               AB8500_CH_STATUS1_REG, &val);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               return ret;
-       }
-
-       if (val & MAIN_CH_DET)
-               result = AC_PW_CONN;
-
-       /* Check for USB charger */
-
-       if (!probe) {
-               /*
-                * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
-                * when disconnecting ACA even though no
-                * charger was connected. Try waiting a little
-                * longer than the 100 ms of VBUS_DET_DBNC100...
-                */
-               msleep(110);
-       }
-       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-               AB8500_CH_USBCH_STAT1_REG, &val);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               return ret;
-       }
-       dev_dbg(di->dev,
-               "%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__,
-               val);
-       if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
-               result |= USB_PW_CONN;
-
-       return result;
-}
-
-/**
- * ab8500_charger_max_usb_curr() - get the max curr for the USB type
- * @di:                        pointer to the ab8500_charger structure
- * @link_status:       the identified USB type
- *
- * Get the maximum current that is allowed to be drawn from the host
- * based on the USB type.
- * Returns error code in case of failure else 0 on success
- */
-static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
-               enum ab8500_charger_link_status link_status)
-{
-       int ret = 0;
-
-       di->usb_device_is_unrecognised = false;
-
-       /*
-        * Platform only supports USB 2.0.
-        * This means that charging current from USB source
-        * is maximum 500 mA. Every occurence of USB_STAT_*_HOST_*
-        * should set USB_CH_IP_CUR_LVL_0P5.
-        */
-
-       switch (link_status) {
-       case USB_STAT_STD_HOST_NC:
-       case USB_STAT_STD_HOST_C_NS:
-       case USB_STAT_STD_HOST_C_S:
-               dev_dbg(di->dev, "USB Type - Standard host is "
-                       "detected through USB driver\n");
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
-               di->is_aca_rid = 0;
-               break;
-       case USB_STAT_HOST_CHG_HS_CHIRP:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
-               di->is_aca_rid = 0;
-               break;
-       case USB_STAT_HOST_CHG_HS:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
-               di->is_aca_rid = 0;
-               break;
-       case USB_STAT_ACA_RID_C_HS:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P9;
-               di->is_aca_rid = 0;
-               break;
-       case USB_STAT_ACA_RID_A:
-               /*
-                * Dedicated charger level minus maximum current accessory
-                * can consume (900mA). Closest level is 500mA
-                */
-               dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
-               di->is_aca_rid = 1;
-               break;
-       case USB_STAT_ACA_RID_B:
-               /*
-                * Dedicated charger level minus 120mA (20mA for ACA and
-                * 100mA for potential accessory). Closest level is 1300mA
-                */
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P3;
-               dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
-                               di->max_usb_in_curr.usb_type_max);
-               di->is_aca_rid = 1;
-               break;
-       case USB_STAT_HOST_CHG_NM:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
-               di->is_aca_rid = 0;
-               break;
-       case USB_STAT_DEDICATED_CHG:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
-               di->is_aca_rid = 0;
-               break;
-       case USB_STAT_ACA_RID_C_HS_CHIRP:
-       case USB_STAT_ACA_RID_C_NM:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
-               di->is_aca_rid = 1;
-               break;
-       case USB_STAT_NOT_CONFIGURED:
-               if (di->vbus_detected) {
-                       di->usb_device_is_unrecognised = true;
-                       dev_dbg(di->dev, "USB Type - Legacy charger.\n");
-                       di->max_usb_in_curr.usb_type_max =
-                                               USB_CH_IP_CUR_LVL_1P5;
-                       break;
-               }
-       case USB_STAT_HM_IDGND:
-               dev_err(di->dev, "USB Type - Charging not allowed\n");
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
-               ret = -ENXIO;
-               break;
-       case USB_STAT_RESERVED:
-               if (is_ab8500(di->parent)) {
-                       di->flags.vbus_collapse = true;
-                       dev_err(di->dev, "USB Type - USB_STAT_RESERVED "
-                                               "VBUS has collapsed\n");
-                       ret = -ENXIO;
-                       break;
-               } else {
-                       dev_dbg(di->dev, "USB Type - Charging not allowed\n");
-                       di->max_usb_in_curr.usb_type_max =
-                                               USB_CH_IP_CUR_LVL_0P05;
-                       dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
-                               link_status,
-                               di->max_usb_in_curr.usb_type_max);
-                       ret = -ENXIO;
-                       break;
-               }
-       case USB_STAT_CARKIT_1:
-       case USB_STAT_CARKIT_2:
-       case USB_STAT_ACA_DOCK_CHARGER:
-       case USB_STAT_CHARGER_LINE_1:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
-               dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
-                               di->max_usb_in_curr.usb_type_max);
-               break;
-       case USB_STAT_NOT_VALID_LINK:
-               dev_err(di->dev, "USB Type invalid - try charging anyway\n");
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
-               break;
-
-       default:
-               dev_err(di->dev, "USB Type - Unknown\n");
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
-               ret = -ENXIO;
-               break;
-       };
-
-       di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
-       dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
-               link_status, di->max_usb_in_curr.set_max);
-
-       return ret;
-}
-
-/**
- * ab8500_charger_read_usb_type() - read the type of usb connected
- * @di:                pointer to the ab8500_charger structure
- *
- * Detect the type of the plugged USB
- * Returns error code in case of failure else 0 on success
- */
-static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
-{
-       int ret;
-       u8 val;
-
-       ret = abx500_get_register_interruptible(di->dev,
-               AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG, &val);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               return ret;
-       }
-       if (is_ab8500(di->parent))
-               ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-                       AB8500_USB_LINE_STAT_REG, &val);
-       else
-               ret = abx500_get_register_interruptible(di->dev,
-                       AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               return ret;
-       }
-
-       /* get the USB type */
-       if (is_ab8500(di->parent))
-               val = (val & AB8500_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
-       else
-               val = (val & AB8505_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
-       ret = ab8500_charger_max_usb_curr(di,
-               (enum ab8500_charger_link_status) val);
-
-       return ret;
-}
-
-/**
- * ab8500_charger_detect_usb_type() - get the type of usb connected
- * @di:                pointer to the ab8500_charger structure
- *
- * Detect the type of the plugged USB
- * Returns error code in case of failure else 0 on success
- */
-static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
-{
-       int i, ret;
-       u8 val;
-
-       /*
-        * On getting the VBUS rising edge detect interrupt there
-        * is a 250ms delay after which the register UsbLineStatus
-        * is filled with valid data.
-        */
-       for (i = 0; i < 10; i++) {
-               msleep(250);
-               ret = abx500_get_register_interruptible(di->dev,
-                       AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
-                       &val);
-               dev_dbg(di->dev, "%s AB8500_IT_SOURCE21_REG %x\n",
-                       __func__, val);
-               if (ret < 0) {
-                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-                       return ret;
-               }
-
-               if (is_ab8500(di->parent))
-                       ret = abx500_get_register_interruptible(di->dev,
-                               AB8500_USB, AB8500_USB_LINE_STAT_REG, &val);
-               else
-                       ret = abx500_get_register_interruptible(di->dev,
-                               AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
-               if (ret < 0) {
-                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-                       return ret;
-               }
-               dev_dbg(di->dev, "%s AB8500_USB_LINE_STAT_REG %x\n", __func__,
-                       val);
-               /*
-                * Until the IT source register is read the UsbLineStatus
-                * register is not updated, hence doing the same
-                * Revisit this:
-                */
-
-               /* get the USB type */
-               if (is_ab8500(di->parent))
-                       val = (val & AB8500_USB_LINK_STATUS) >>
-                                                       USB_LINK_STATUS_SHIFT;
-               else
-                       val = (val & AB8505_USB_LINK_STATUS) >>
-                                                       USB_LINK_STATUS_SHIFT;
-               if (val)
-                       break;
-       }
-       ret = ab8500_charger_max_usb_curr(di,
-               (enum ab8500_charger_link_status) val);
-
-       return ret;
-}
-
-/*
- * This array maps the raw hex value to charger voltage used by the AB8500
- * Values taken from the UM0836
- */
-static int ab8500_charger_voltage_map[] = {
-       3500 ,
-       3525 ,
-       3550 ,
-       3575 ,
-       3600 ,
-       3625 ,
-       3650 ,
-       3675 ,
-       3700 ,
-       3725 ,
-       3750 ,
-       3775 ,
-       3800 ,
-       3825 ,
-       3850 ,
-       3875 ,
-       3900 ,
-       3925 ,
-       3950 ,
-       3975 ,
-       4000 ,
-       4025 ,
-       4050 ,
-       4060 ,
-       4070 ,
-       4080 ,
-       4090 ,
-       4100 ,
-       4110 ,
-       4120 ,
-       4130 ,
-       4140 ,
-       4150 ,
-       4160 ,
-       4170 ,
-       4180 ,
-       4190 ,
-       4200 ,
-       4210 ,
-       4220 ,
-       4230 ,
-       4240 ,
-       4250 ,
-       4260 ,
-       4270 ,
-       4280 ,
-       4290 ,
-       4300 ,
-       4310 ,
-       4320 ,
-       4330 ,
-       4340 ,
-       4350 ,
-       4360 ,
-       4370 ,
-       4380 ,
-       4390 ,
-       4400 ,
-       4410 ,
-       4420 ,
-       4430 ,
-       4440 ,
-       4450 ,
-       4460 ,
-       4470 ,
-       4480 ,
-       4490 ,
-       4500 ,
-       4510 ,
-       4520 ,
-       4530 ,
-       4540 ,
-       4550 ,
-       4560 ,
-       4570 ,
-       4580 ,
-       4590 ,
-       4600 ,
-};
-
-static int ab8500_voltage_to_regval(int voltage)
-{
-       int i;
-
-       /* Special case for voltage below 3.5V */
-       if (voltage < ab8500_charger_voltage_map[0])
-               return LOW_VOLT_REG;
-
-       for (i = 1; i < ARRAY_SIZE(ab8500_charger_voltage_map); i++) {
-               if (voltage < ab8500_charger_voltage_map[i])
-                       return i - 1;
-       }
-
-       /* If not last element, return error */
-       i = ARRAY_SIZE(ab8500_charger_voltage_map) - 1;
-       if (voltage == ab8500_charger_voltage_map[i])
-               return i;
-       else
-               return -1;
-}
-
-static int ab8500_current_to_regval(struct ab8500_charger *di, int curr)
-{
-       int i;
-
-       if (curr < di->bm->chg_output_curr[0])
-               return 0;
-
-       for (i = 0; i < di->bm->n_chg_out_curr; i++) {
-               if (curr < di->bm->chg_output_curr[i])
-                       return i - 1;
-       }
-
-       /* If not last element, return error */
-       i = di->bm->n_chg_out_curr - 1;
-       if (curr == di->bm->chg_output_curr[i])
-               return i;
-       else
-               return -1;
-}
-
-static int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr)
-{
-       int i;
-
-       if (curr < di->bm->chg_input_curr[0])
-               return 0;
-
-       for (i = 0; i < di->bm->n_chg_in_curr; i++) {
-               if (curr < di->bm->chg_input_curr[i])
-                       return i - 1;
-       }
-
-       /* If not last element, return error */
-       i = di->bm->n_chg_in_curr - 1;
-       if (curr == di->bm->chg_input_curr[i])
-               return i;
-       else
-               return -1;
-}
-
-/**
- * ab8500_charger_get_usb_cur() - get usb current
- * @di:                pointer to the ab8500_charger structre
- *
- * The usb stack provides the maximum current that can be drawn from
- * the standard usb host. This will be in mA.
- * This function converts current in mA to a value that can be written
- * to the register. Returns -1 if charging is not allowed
- */
-static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
-{
-       int ret = 0;
-       switch (di->usb_state.usb_current) {
-       case 100:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P09;
-               break;
-       case 200:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P19;
-               break;
-       case 300:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P29;
-               break;
-       case 400:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P38;
-               break;
-       case 500:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
-               break;
-       default:
-               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
-               ret = -EPERM;
-               break;
-       };
-       di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
-       return ret;
-}
-
-/**
- * ab8500_charger_check_continue_stepping() - Check to allow stepping
- * @di:                pointer to the ab8500_charger structure
- * @reg:       select what charger register to check
- *
- * Check if current stepping should be allowed to continue.
- * Checks if charger source has not collapsed. If it has, further stepping
- * is not allowed.
- */
-static bool ab8500_charger_check_continue_stepping(struct ab8500_charger *di,
-                                                  int reg)
-{
-       if (reg == AB8500_USBCH_IPT_CRNTLVL_REG)
-               return !di->flags.vbus_drop_end;
-       else
-               return true;
-}
-
-/**
- * ab8500_charger_set_current() - set charger current
- * @di:                pointer to the ab8500_charger structure
- * @ich:       charger current, in mA
- * @reg:       select what charger register to set
- *
- * Set charger current.
- * There is no state machine in the AB to step up/down the charger
- * current to avoid dips and spikes on MAIN, VBUS and VBAT when
- * charging is started. Instead we need to implement
- * this charger current step-up/down here.
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_set_current(struct ab8500_charger *di,
-       int ich, int reg)
-{
-       int ret = 0;
-       int curr_index, prev_curr_index, shift_value, i;
-       u8 reg_value;
-       u32 step_udelay;
-       bool no_stepping = false;
-
-       atomic_inc(&di->current_stepping_sessions);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-               reg, &reg_value);
-       if (ret < 0) {
-               dev_err(di->dev, "%s read failed\n", __func__);
-               goto exit_set_current;
-       }
-
-       switch (reg) {
-       case AB8500_MCH_IPT_CURLVL_REG:
-               shift_value = MAIN_CH_INPUT_CURR_SHIFT;
-               prev_curr_index = (reg_value >> shift_value);
-               curr_index = ab8500_current_to_regval(di, ich);
-               step_udelay = STEP_UDELAY;
-               if (!di->ac.charger_connected)
-                       no_stepping = true;
-               break;
-       case AB8500_USBCH_IPT_CRNTLVL_REG:
-               if (is_ab8540(di->parent))
-                       shift_value = AB8540_VBUS_IN_CURR_LIM_SHIFT;
-               else
-                       shift_value = VBUS_IN_CURR_LIM_SHIFT;
-               prev_curr_index = (reg_value >> shift_value);
-               curr_index = ab8500_vbus_in_curr_to_regval(di, ich);
-               step_udelay = STEP_UDELAY * 100;
-
-               if (!di->usb.charger_connected)
-                       no_stepping = true;
-               break;
-       case AB8500_CH_OPT_CRNTLVL_REG:
-               shift_value = 0;
-               prev_curr_index = (reg_value >> shift_value);
-               curr_index = ab8500_current_to_regval(di, ich);
-               step_udelay = STEP_UDELAY;
-               if (curr_index && (curr_index - prev_curr_index) > 1)
-                       step_udelay *= 100;
-
-               if (!di->usb.charger_connected && !di->ac.charger_connected)
-                       no_stepping = true;
-
-               break;
-       default:
-               dev_err(di->dev, "%s current register not valid\n", __func__);
-               ret = -ENXIO;
-               goto exit_set_current;
-       }
-
-       if (curr_index < 0) {
-               dev_err(di->dev, "requested current limit out-of-range\n");
-               ret = -ENXIO;
-               goto exit_set_current;
-       }
-
-       /* only update current if it's been changed */
-       if (prev_curr_index == curr_index) {
-               dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
-                       __func__, reg);
-               ret = 0;
-               goto exit_set_current;
-       }
-
-       dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
-               __func__, ich, reg);
-
-       if (no_stepping) {
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                                       reg, (u8)curr_index << shift_value);
-               if (ret)
-                       dev_err(di->dev, "%s write failed\n", __func__);
-       } else if (prev_curr_index > curr_index) {
-               for (i = prev_curr_index - 1; i >= curr_index; i--) {
-                       dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
-                               (u8) i << shift_value, reg);
-                       ret = abx500_set_register_interruptible(di->dev,
-                               AB8500_CHARGER, reg, (u8)i << shift_value);
-                       if (ret) {
-                               dev_err(di->dev, "%s write failed\n", __func__);
-                               goto exit_set_current;
-                       }
-                       if (i != curr_index)
-                               usleep_range(step_udelay, step_udelay * 2);
-               }
-       } else {
-               bool allow = true;
-               for (i = prev_curr_index + 1; i <= curr_index && allow; i++) {
-                       dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
-                               (u8)i << shift_value, reg);
-                       ret = abx500_set_register_interruptible(di->dev,
-                               AB8500_CHARGER, reg, (u8)i << shift_value);
-                       if (ret) {
-                               dev_err(di->dev, "%s write failed\n", __func__);
-                               goto exit_set_current;
-                       }
-                       if (i != curr_index)
-                               usleep_range(step_udelay, step_udelay * 2);
-
-                       allow = ab8500_charger_check_continue_stepping(di, reg);
-               }
-       }
-
-exit_set_current:
-       atomic_dec(&di->current_stepping_sessions);
-
-       return ret;
-}
-
-/**
- * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
- * @di:                pointer to the ab8500_charger structure
- * @ich_in:    charger input current limit
- *
- * Sets the current that can be drawn from the USB host
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
-               int ich_in)
-{
-       int min_value;
-       int ret;
-
-       /* We should always use to lowest current limit */
-       min_value = min(di->bm->chg_params->usb_curr_max, ich_in);
-       if (di->max_usb_in_curr.set_max > 0)
-               min_value = min(di->max_usb_in_curr.set_max, min_value);
-
-       if (di->usb_state.usb_current >= 0)
-               min_value = min(di->usb_state.usb_current, min_value);
-
-       switch (min_value) {
-       case 100:
-               if (di->vbat < VBAT_TRESH_IP_CUR_RED)
-                       min_value = USB_CH_IP_CUR_LVL_0P05;
-               break;
-       case 500:
-               if (di->vbat < VBAT_TRESH_IP_CUR_RED)
-                       min_value = USB_CH_IP_CUR_LVL_0P45;
-               break;
-       default:
-               break;
-       }
-
-       dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value);
-
-       mutex_lock(&di->usb_ipt_crnt_lock);
-       ret = ab8500_charger_set_current(di, min_value,
-               AB8500_USBCH_IPT_CRNTLVL_REG);
-       mutex_unlock(&di->usb_ipt_crnt_lock);
-
-       return ret;
-}
-
-/**
- * ab8500_charger_set_main_in_curr() - set main charger input current
- * @di:                pointer to the ab8500_charger structure
- * @ich_in:    input charger current, in mA
- *
- * Set main charger input current.
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_set_main_in_curr(struct ab8500_charger *di,
-       int ich_in)
-{
-       return ab8500_charger_set_current(di, ich_in,
-               AB8500_MCH_IPT_CURLVL_REG);
-}
-
-/**
- * ab8500_charger_set_output_curr() - set charger output current
- * @di:                pointer to the ab8500_charger structure
- * @ich_out:   output charger current, in mA
- *
- * Set charger output current.
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_set_output_curr(struct ab8500_charger *di,
-       int ich_out)
-{
-       return ab8500_charger_set_current(di, ich_out,
-               AB8500_CH_OPT_CRNTLVL_REG);
-}
-
-/**
- * ab8500_charger_led_en() - turn on/off chargign led
- * @di:                pointer to the ab8500_charger structure
- * @on:                flag to turn on/off the chargign led
- *
- * Power ON/OFF charging LED indication
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_led_en(struct ab8500_charger *di, int on)
-{
-       int ret;
-
-       if (on) {
-               /* Power ON charging LED indicator, set LED current to 5mA */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_LED_INDICATOR_PWM_CTRL,
-                       (LED_IND_CUR_5MA | LED_INDICATOR_PWM_ENA));
-               if (ret) {
-                       dev_err(di->dev, "Power ON LED failed\n");
-                       return ret;
-               }
-               /* LED indicator PWM duty cycle 252/256 */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_LED_INDICATOR_PWM_DUTY,
-                       LED_INDICATOR_PWM_DUTY_252_256);
-               if (ret) {
-                       dev_err(di->dev, "Set LED PWM duty cycle failed\n");
-                       return ret;
-               }
-       } else {
-               /* Power off charging LED indicator */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_LED_INDICATOR_PWM_CTRL,
-                       LED_INDICATOR_PWM_DIS);
-               if (ret) {
-                       dev_err(di->dev, "Power-off LED failed\n");
-                       return ret;
-               }
-       }
-
-       return ret;
-}
-
-/**
- * ab8500_charger_ac_en() - enable or disable ac charging
- * @di:                pointer to the ab8500_charger structure
- * @enable:    enable/disable flag
- * @vset:      charging voltage
- * @iset:      charging current
- *
- * Enable/Disable AC/Mains charging and turns on/off the charging led
- * respectively.
- **/
-static int ab8500_charger_ac_en(struct ux500_charger *charger,
-       int enable, int vset, int iset)
-{
-       int ret;
-       int volt_index;
-       int curr_index;
-       int input_curr_index;
-       u8 overshoot = 0;
-
-       struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
-
-       if (enable) {
-               /* Check if AC is connected */
-               if (!di->ac.charger_connected) {
-                       dev_err(di->dev, "AC charger not connected\n");
-                       return -ENXIO;
-               }
-
-               /* Enable AC charging */
-               dev_dbg(di->dev, "Enable AC: %dmV %dmA\n", vset, iset);
-
-               /*
-                * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
-                * will be triggered everytime we enable the VDD ADC supply.
-                * This will turn off charging for a short while.
-                * It can be avoided by having the supply on when
-                * there is a charger enabled. Normally the VDD ADC supply
-                * is enabled everytime a GPADC conversion is triggered. We will
-                * force it to be enabled from this driver to have
-                * the GPADC module independant of the AB8500 chargers
-                */
-               if (!di->vddadc_en_ac) {
-                       ret = regulator_enable(di->regu);
-                       if (ret)
-                               dev_warn(di->dev,
-                                       "Failed to enable regulator\n");
-                       else
-                               di->vddadc_en_ac = true;
-               }
-
-               /* Check if the requested voltage or current is valid */
-               volt_index = ab8500_voltage_to_regval(vset);
-               curr_index = ab8500_current_to_regval(di, iset);
-               input_curr_index = ab8500_current_to_regval(di,
-                       di->bm->chg_params->ac_curr_max);
-               if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) {
-                       dev_err(di->dev,
-                               "Charger voltage or current too high, "
-                               "charging not started\n");
-                       return -ENXIO;
-               }
-
-               /* ChVoltLevel: maximum battery charging voltage */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
-               if (ret) {
-                       dev_err(di->dev, "%s write failed\n", __func__);
-                       return ret;
-               }
-               /* MainChInputCurr: current that can be drawn from the charger*/
-               ret = ab8500_charger_set_main_in_curr(di,
-                       di->bm->chg_params->ac_curr_max);
-               if (ret) {
-                       dev_err(di->dev, "%s Failed to set MainChInputCurr\n",
-                               __func__);
-                       return ret;
-               }
-               /* ChOutputCurentLevel: protected output current */
-               ret = ab8500_charger_set_output_curr(di, iset);
-               if (ret) {
-                       dev_err(di->dev, "%s "
-                               "Failed to set ChOutputCurentLevel\n",
-                               __func__);
-                       return ret;
-               }
-
-               /* Check if VBAT overshoot control should be enabled */
-               if (!di->bm->enable_overshoot)
-                       overshoot = MAIN_CH_NO_OVERSHOOT_ENA_N;
-
-               /* Enable Main Charger */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_MCH_CTRL1, MAIN_CH_ENA | overshoot);
-               if (ret) {
-                       dev_err(di->dev, "%s write failed\n", __func__);
-                       return ret;
-               }
-
-               /* Power on charging LED indication */
-               ret = ab8500_charger_led_en(di, true);
-               if (ret < 0)
-                       dev_err(di->dev, "failed to enable LED\n");
-
-               di->ac.charger_online = 1;
-       } else {
-               /* Disable AC charging */
-               if (is_ab8500_1p1_or_earlier(di->parent)) {
-                       /*
-                        * For ABB revision 1.0 and 1.1 there is a bug in the
-                        * watchdog logic. That means we have to continously
-                        * kick the charger watchdog even when no charger is
-                        * connected. This is only valid once the AC charger
-                        * has been enabled. This is a bug that is not handled
-                        * by the algorithm and the watchdog have to be kicked
-                        * by the charger driver when the AC charger
-                        * is disabled
-                        */
-                       if (di->ac_conn) {
-                               queue_delayed_work(di->charger_wq,
-                                       &di->kick_wd_work,
-                                       round_jiffies(WD_KICK_INTERVAL));
-                       }
-
-                       /*
-                        * We can't turn off charging completely
-                        * due to a bug in AB8500 cut1.
-                        * If we do, charging will not start again.
-                        * That is why we set the lowest voltage
-                        * and current possible
-                        */
-                       ret = abx500_set_register_interruptible(di->dev,
-                               AB8500_CHARGER,
-                               AB8500_CH_VOLT_LVL_REG, CH_VOL_LVL_3P5);
-                       if (ret) {
-                               dev_err(di->dev,
-                                       "%s write failed\n", __func__);
-                               return ret;
-                       }
-
-                       ret = ab8500_charger_set_output_curr(di, 0);
-                       if (ret) {
-                               dev_err(di->dev, "%s "
-                                       "Failed to set ChOutputCurentLevel\n",
-                                       __func__);
-                               return ret;
-                       }
-               } else {
-                       ret = abx500_set_register_interruptible(di->dev,
-                               AB8500_CHARGER,
-                               AB8500_MCH_CTRL1, 0);
-                       if (ret) {
-                               dev_err(di->dev,
-                                       "%s write failed\n", __func__);
-                               return ret;
-                       }
-               }
-
-               ret = ab8500_charger_led_en(di, false);
-               if (ret < 0)
-                       dev_err(di->dev, "failed to disable LED\n");
-
-               di->ac.charger_online = 0;
-               di->ac.wd_expired = false;
-
-               /* Disable regulator if enabled */
-               if (di->vddadc_en_ac) {
-                       regulator_disable(di->regu);
-                       di->vddadc_en_ac = false;
-               }
-
-               dev_dbg(di->dev, "%s Disabled AC charging\n", __func__);
-       }
-       ab8500_power_supply_changed(di, di->ac_chg.psy);
-
-       return ret;
-}
-
-/**
- * ab8500_charger_usb_en() - enable usb charging
- * @di:                pointer to the ab8500_charger structure
- * @enable:    enable/disable flag
- * @vset:      charging voltage
- * @ich_out:   charger output current
- *
- * Enable/Disable USB charging and turns on/off the charging led respectively.
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_usb_en(struct ux500_charger *charger,
-       int enable, int vset, int ich_out)
-{
-       int ret;
-       int volt_index;
-       int curr_index;
-       u8 overshoot = 0;
-
-       struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
-
-       if (enable) {
-               /* Check if USB is connected */
-               if (!di->usb.charger_connected) {
-                       dev_err(di->dev, "USB charger not connected\n");
-                       return -ENXIO;
-               }
-
-               /*
-                * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
-                * will be triggered everytime we enable the VDD ADC supply.
-                * This will turn off charging for a short while.
-                * It can be avoided by having the supply on when
-                * there is a charger enabled. Normally the VDD ADC supply
-                * is enabled everytime a GPADC conversion is triggered. We will
-                * force it to be enabled from this driver to have
-                * the GPADC module independant of the AB8500 chargers
-                */
-               if (!di->vddadc_en_usb) {
-                       ret = regulator_enable(di->regu);
-                       if (ret)
-                               dev_warn(di->dev,
-                                       "Failed to enable regulator\n");
-                       else
-                               di->vddadc_en_usb = true;
-               }
-
-               /* Enable USB charging */
-               dev_dbg(di->dev, "Enable USB: %dmV %dmA\n", vset, ich_out);
-
-               /* Check if the requested voltage or current is valid */
-               volt_index = ab8500_voltage_to_regval(vset);
-               curr_index = ab8500_current_to_regval(di, ich_out);
-               if (volt_index < 0 || curr_index < 0) {
-                       dev_err(di->dev,
-                               "Charger voltage or current too high, "
-                               "charging not started\n");
-                       return -ENXIO;
-               }
-
-               /* ChVoltLevel: max voltage upto which battery can be charged */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
-               if (ret) {
-                       dev_err(di->dev, "%s write failed\n", __func__);
-                       return ret;
-               }
-               /* Check if VBAT overshoot control should be enabled */
-               if (!di->bm->enable_overshoot)
-                       overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
-
-               /* Enable USB Charger */
-               dev_dbg(di->dev,
-                       "Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
-               if (ret) {
-                       dev_err(di->dev, "%s write failed\n", __func__);
-                       return ret;
-               }
-
-               /* If success power on charging LED indication */
-               ret = ab8500_charger_led_en(di, true);
-               if (ret < 0)
-                       dev_err(di->dev, "failed to enable LED\n");
-
-               di->usb.charger_online = 1;
-
-               /* USBChInputCurr: current that can be drawn from the usb */
-               ret = ab8500_charger_set_vbus_in_curr(di,
-                                       di->max_usb_in_curr.usb_type_max);
-               if (ret) {
-                       dev_err(di->dev, "setting USBChInputCurr failed\n");
-                       return ret;
-               }
-
-               /* ChOutputCurentLevel: protected output current */
-               ret = ab8500_charger_set_output_curr(di, ich_out);
-               if (ret) {
-                       dev_err(di->dev, "%s "
-                               "Failed to set ChOutputCurentLevel\n",
-                               __func__);
-                       return ret;
-               }
-
-               queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
-
-       } else {
-               /* Disable USB charging */
-               dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_CHARGER,
-                       AB8500_USBCH_CTRL1_REG, 0);
-               if (ret) {
-                       dev_err(di->dev,
-                               "%s write failed\n", __func__);
-                       return ret;
-               }
-
-               ret = ab8500_charger_led_en(di, false);
-               if (ret < 0)
-                       dev_err(di->dev, "failed to disable LED\n");
-               /* USBChInputCurr: current that can be drawn from the usb */
-               ret = ab8500_charger_set_vbus_in_curr(di, 0);
-               if (ret) {
-                       dev_err(di->dev, "setting USBChInputCurr failed\n");
-                       return ret;
-               }
-
-               /* ChOutputCurentLevel: protected output current */
-               ret = ab8500_charger_set_output_curr(di, 0);
-               if (ret) {
-                       dev_err(di->dev, "%s "
-                               "Failed to reset ChOutputCurentLevel\n",
-                               __func__);
-                       return ret;
-               }
-               di->usb.charger_online = 0;
-               di->usb.wd_expired = false;
-
-               /* Disable regulator if enabled */
-               if (di->vddadc_en_usb) {
-                       regulator_disable(di->regu);
-                       di->vddadc_en_usb = false;
-               }
-
-               dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
-
-               /* Cancel any pending Vbat check work */
-               cancel_delayed_work(&di->check_vbat_work);
-
-       }
-       ab8500_power_supply_changed(di, di->usb_chg.psy);
-
-       return ret;
-}
-
-static int ab8500_external_charger_prepare(struct notifier_block *charger_nb,
-                               unsigned long event, void *data)
-{
-       int ret;
-       struct device *dev = data;
-       /*Toggle External charger control pin*/
-       ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
-                                 AB8500_SYS_CHARGER_CONTROL_REG,
-                                 EXTERNAL_CHARGER_DISABLE_REG_VAL);
-       if (ret < 0) {
-               dev_err(dev, "write reg failed %d\n", ret);
-               goto out;
-       }
-       ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
-                                 AB8500_SYS_CHARGER_CONTROL_REG,
-                                 EXTERNAL_CHARGER_ENABLE_REG_VAL);
-       if (ret < 0)
-               dev_err(dev, "Write reg failed %d\n", ret);
-
-out:
-       return ret;
-}
-
-/**
- * ab8500_charger_usb_check_enable() - enable usb charging
- * @charger:   pointer to the ux500_charger structure
- * @vset:      charging voltage
- * @iset:      charger output current
- *
- * Check if the VBUS charger has been disconnected and reconnected without
- * AB8500 rising an interrupt. Returns 0 on success.
- */
-static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
-       int vset, int iset)
-{
-       u8 usbch_ctrl1 = 0;
-       int ret = 0;
-
-       struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
-
-       if (!di->usb.charger_connected)
-               return ret;
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-                               AB8500_USBCH_CTRL1_REG, &usbch_ctrl1);
-       if (ret < 0) {
-               dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
-               return ret;
-       }
-       dev_dbg(di->dev, "USB charger ctrl: 0x%02x\n", usbch_ctrl1);
-
-       if (!(usbch_ctrl1 & USB_CH_ENA)) {
-               dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
-
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                                       AB8500_CHARGER, AB8500_CHARGER_CTRL,
-                                       DROP_COUNT_RESET, DROP_COUNT_RESET);
-               if (ret < 0) {
-                       dev_err(di->dev, "ab8500 write failed %d\n", __LINE__);
-                       return ret;
-               }
-
-               ret = ab8500_charger_usb_en(&di->usb_chg, true, vset, iset);
-               if (ret < 0) {
-                       dev_err(di->dev, "Failed to enable VBUS charger %d\n",
-                                       __LINE__);
-                       return ret;
-               }
-       }
-       return ret;
-}
-
-/**
- * ab8500_charger_ac_check_enable() - enable usb charging
- * @charger:   pointer to the ux500_charger structure
- * @vset:      charging voltage
- * @iset:      charger output current
- *
- * Check if the AC charger has been disconnected and reconnected without
- * AB8500 rising an interrupt. Returns 0 on success.
- */
-static int ab8500_charger_ac_check_enable(struct ux500_charger *charger,
-       int vset, int iset)
-{
-       u8 mainch_ctrl1 = 0;
-       int ret = 0;
-
-       struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
-
-       if (!di->ac.charger_connected)
-               return ret;
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-                               AB8500_MCH_CTRL1, &mainch_ctrl1);
-       if (ret < 0) {
-               dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
-               return ret;
-       }
-       dev_dbg(di->dev, "AC charger ctrl: 0x%02x\n", mainch_ctrl1);
-
-       if (!(mainch_ctrl1 & MAIN_CH_ENA)) {
-               dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
-
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                                       AB8500_CHARGER, AB8500_CHARGER_CTRL,
-                                       DROP_COUNT_RESET, DROP_COUNT_RESET);
-
-               if (ret < 0) {
-                       dev_err(di->dev, "ab8500 write failed %d\n", __LINE__);
-                       return ret;
-               }
-
-               ret = ab8500_charger_ac_en(&di->usb_chg, true, vset, iset);
-               if (ret < 0) {
-                       dev_err(di->dev, "failed to enable AC charger %d\n",
-                               __LINE__);
-                       return ret;
-               }
-       }
-       return ret;
-}
-
-/**
- * ab8500_charger_watchdog_kick() - kick charger watchdog
- * @di:                pointer to the ab8500_charger structure
- *
- * Kick charger watchdog
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_watchdog_kick(struct ux500_charger *charger)
-{
-       int ret;
-       struct ab8500_charger *di;
-
-       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
-               di = to_ab8500_charger_ac_device_info(charger);
-       else if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
-               di = to_ab8500_charger_usb_device_info(charger);
-       else
-               return -ENXIO;
-
-       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-               AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
-       if (ret)
-               dev_err(di->dev, "Failed to kick WD!\n");
-
-       return ret;
-}
-
-/**
- * ab8500_charger_update_charger_current() - update charger current
- * @di:                pointer to the ab8500_charger structure
- *
- * Update the charger output current for the specified charger
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
-               int ich_out)
-{
-       int ret;
-       struct ab8500_charger *di;
-
-       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
-               di = to_ab8500_charger_ac_device_info(charger);
-       else if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
-               di = to_ab8500_charger_usb_device_info(charger);
-       else
-               return -ENXIO;
-
-       ret = ab8500_charger_set_output_curr(di, ich_out);
-       if (ret) {
-               dev_err(di->dev, "%s "
-                       "Failed to set ChOutputCurentLevel\n",
-                       __func__);
-               return ret;
-       }
-
-       /* Reset the main and usb drop input current measurement counter */
-       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                               AB8500_CHARGER_CTRL, DROP_COUNT_RESET);
-       if (ret) {
-               dev_err(di->dev, "%s write failed\n", __func__);
-               return ret;
-       }
-
-       return ret;
-}
-
-/**
- * ab8540_charger_power_path_enable() - enable usb power path mode
- * @charger:   pointer to the ux500_charger structure
- * @enable:    enable/disable flag
- *
- * Enable or disable the power path for usb mode
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8540_charger_power_path_enable(struct ux500_charger *charger,
-               bool enable)
-{
-       int ret;
-       struct ab8500_charger *di;
-
-       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
-               di = to_ab8500_charger_usb_device_info(charger);
-       else
-               return -ENXIO;
-
-       ret = abx500_mask_and_set_register_interruptible(di->dev,
-                               AB8500_CHARGER, AB8540_USB_PP_MODE_REG,
-                               BUS_POWER_PATH_MODE_ENA, enable);
-       if (ret) {
-               dev_err(di->dev, "%s write failed\n", __func__);
-               return ret;
-       }
-
-       return ret;
-}
-
-
-/**
- * ab8540_charger_usb_pre_chg_enable() - enable usb pre change
- * @charger:   pointer to the ux500_charger structure
- * @enable:    enable/disable flag
- *
- * Enable or disable the pre-chage for usb mode
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8540_charger_usb_pre_chg_enable(struct ux500_charger *charger,
-               bool enable)
-{
-       int ret;
-       struct ab8500_charger *di;
-
-       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
-               di = to_ab8500_charger_usb_device_info(charger);
-       else
-               return -ENXIO;
-
-       ret = abx500_mask_and_set_register_interruptible(di->dev,
-                               AB8500_CHARGER, AB8540_USB_PP_CHR_REG,
-                               BUS_POWER_PATH_PRECHG_ENA, enable);
-       if (ret) {
-               dev_err(di->dev, "%s write failed\n", __func__);
-               return ret;
-       }
-
-       return ret;
-}
-
-static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data)
-{
-       struct power_supply *psy;
-       struct power_supply *ext = dev_get_drvdata(dev);
-       const char **supplicants = (const char **)ext->supplied_to;
-       struct ab8500_charger *di;
-       union power_supply_propval ret;
-       int j;
-       struct ux500_charger *usb_chg;
-
-       usb_chg = (struct ux500_charger *)data;
-       psy = usb_chg->psy;
-
-       di = to_ab8500_charger_usb_device_info(usb_chg);
-
-       /* For all psy where the driver name appears in any supplied_to */
-       j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
-       if (j < 0)
-               return 0;
-
-       /* Go through all properties for the psy */
-       for (j = 0; j < ext->desc->num_properties; j++) {
-               enum power_supply_property prop;
-               prop = ext->desc->properties[j];
-
-               if (power_supply_get_property(ext, prop, &ret))
-                       continue;
-
-               switch (prop) {
-               case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               di->vbat = ret.intval / 1000;
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-               default:
-                       break;
-               }
-       }
-       return 0;
-}
-
-/**
- * ab8500_charger_check_vbat_work() - keep vbus current within spec
- * @work       pointer to the work_struct structure
- *
- * Due to a asic bug it is necessary to lower the input current to the vbus
- * charger when charging with at some specific levels. This issue is only valid
- * for below a certain battery voltage. This function makes sure that the
- * the allowed current limit isn't exceeded.
- */
-static void ab8500_charger_check_vbat_work(struct work_struct *work)
-{
-       int t = 10;
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, check_vbat_work.work);
-
-       class_for_each_device(power_supply_class, NULL,
-               di->usb_chg.psy, ab8500_charger_get_ext_psy_data);
-
-       /* First run old_vbat is 0. */
-       if (di->old_vbat == 0)
-               di->old_vbat = di->vbat;
-
-       if (!((di->old_vbat <= VBAT_TRESH_IP_CUR_RED &&
-               di->vbat <= VBAT_TRESH_IP_CUR_RED) ||
-               (di->old_vbat > VBAT_TRESH_IP_CUR_RED &&
-               di->vbat > VBAT_TRESH_IP_CUR_RED))) {
-
-               dev_dbg(di->dev, "Vbat did cross threshold, curr: %d, new: %d,"
-                       " old: %d\n", di->max_usb_in_curr.usb_type_max,
-                       di->vbat, di->old_vbat);
-               ab8500_charger_set_vbus_in_curr(di,
-                                       di->max_usb_in_curr.usb_type_max);
-               power_supply_changed(di->usb_chg.psy);
-       }
-
-       di->old_vbat = di->vbat;
-
-       /*
-        * No need to check the battery voltage every second when not close to
-        * the threshold.
-        */
-       if (di->vbat < (VBAT_TRESH_IP_CUR_RED + 100) &&
-               (di->vbat > (VBAT_TRESH_IP_CUR_RED - 100)))
-                       t = 1;
-
-       queue_delayed_work(di->charger_wq, &di->check_vbat_work, t * HZ);
-}
-
-/**
- * ab8500_charger_check_hw_failure_work() - check main charger failure
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for checking the main charger status
- */
-static void ab8500_charger_check_hw_failure_work(struct work_struct *work)
-{
-       int ret;
-       u8 reg_value;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, check_hw_failure_work.work);
-
-       /* Check if the status bits for HW failure is still active */
-       if (di->flags.mainextchnotok) {
-               ret = abx500_get_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
-               if (ret < 0) {
-                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-                       return;
-               }
-               if (!(reg_value & MAIN_CH_NOK)) {
-                       di->flags.mainextchnotok = false;
-                       ab8500_power_supply_changed(di, di->ac_chg.psy);
-               }
-       }
-       if (di->flags.vbus_ovv) {
-               ret = abx500_get_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG,
-                       &reg_value);
-               if (ret < 0) {
-                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-                       return;
-               }
-               if (!(reg_value & VBUS_OVV_TH)) {
-                       di->flags.vbus_ovv = false;
-                       ab8500_power_supply_changed(di, di->usb_chg.psy);
-               }
-       }
-       /* If we still have a failure, schedule a new check */
-       if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
-               queue_delayed_work(di->charger_wq,
-                       &di->check_hw_failure_work, round_jiffies(HZ));
-       }
-}
-
-/**
- * ab8500_charger_kick_watchdog_work() - kick the watchdog
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for kicking the charger watchdog.
- *
- * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
- * logic. That means we have to continously kick the charger
- * watchdog even when no charger is connected. This is only
- * valid once the AC charger has been enabled. This is
- * a bug that is not handled by the algorithm and the
- * watchdog have to be kicked by the charger driver
- * when the AC charger is disabled
- */
-static void ab8500_charger_kick_watchdog_work(struct work_struct *work)
-{
-       int ret;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, kick_wd_work.work);
-
-       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-               AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
-       if (ret)
-               dev_err(di->dev, "Failed to kick WD!\n");
-
-       /* Schedule a new watchdog kick */
-       queue_delayed_work(di->charger_wq,
-               &di->kick_wd_work, round_jiffies(WD_KICK_INTERVAL));
-}
-
-/**
- * ab8500_charger_ac_work() - work to get and set main charger status
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for checking the main charger status
- */
-static void ab8500_charger_ac_work(struct work_struct *work)
-{
-       int ret;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, ac_work);
-
-       /*
-        * Since we can't be sure that the events are received
-        * synchronously, we have the check if the main charger is
-        * connected by reading the status register
-        */
-       ret = ab8500_charger_detect_chargers(di, false);
-       if (ret < 0)
-               return;
-
-       if (ret & AC_PW_CONN) {
-               di->ac.charger_connected = 1;
-               di->ac_conn = true;
-       } else {
-               di->ac.charger_connected = 0;
-       }
-
-       ab8500_power_supply_changed(di, di->ac_chg.psy);
-       sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present");
-}
-
-static void ab8500_charger_usb_attached_work(struct work_struct *work)
-{
-       struct ab8500_charger *di = container_of(work,
-                                                struct ab8500_charger,
-                                                usb_charger_attached_work.work);
-       int usbch = (USB_CH_VBUSDROP | USB_CH_VBUSDETDBNC);
-       int ret, i;
-       u8 statval;
-
-       for (i = 0; i < 10; i++) {
-               ret = abx500_get_register_interruptible(di->dev,
-                                                       AB8500_CHARGER,
-                                                       AB8500_CH_USBCH_STAT1_REG,
-                                                       &statval);
-               if (ret < 0) {
-                       dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
-                       goto reschedule;
-               }
-               if ((statval & usbch) != usbch)
-                       goto reschedule;
-
-               msleep(CHARGER_STATUS_POLL);
-       }
-
-       ab8500_charger_usb_en(&di->usb_chg, 0, 0, 0);
-
-       mutex_lock(&di->charger_attached_mutex);
-       mutex_unlock(&di->charger_attached_mutex);
-
-       return;
-
-reschedule:
-       queue_delayed_work(di->charger_wq,
-                          &di->usb_charger_attached_work,
-                          HZ);
-}
-
-static void ab8500_charger_ac_attached_work(struct work_struct *work)
-{
-
-       struct ab8500_charger *di = container_of(work,
-                                                struct ab8500_charger,
-                                                ac_charger_attached_work.work);
-       int mainch = (MAIN_CH_STATUS2_MAINCHGDROP |
-                     MAIN_CH_STATUS2_MAINCHARGERDETDBNC);
-       int ret, i;
-       u8 statval;
-
-       for (i = 0; i < 10; i++) {
-               ret = abx500_get_register_interruptible(di->dev,
-                                                       AB8500_CHARGER,
-                                                       AB8500_CH_STATUS2_REG,
-                                                       &statval);
-               if (ret < 0) {
-                       dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
-                       goto reschedule;
-               }
-
-               if ((statval & mainch) != mainch)
-                       goto reschedule;
-
-               msleep(CHARGER_STATUS_POLL);
-       }
-
-       ab8500_charger_ac_en(&di->ac_chg, 0, 0, 0);
-       queue_work(di->charger_wq, &di->ac_work);
-
-       mutex_lock(&di->charger_attached_mutex);
-       mutex_unlock(&di->charger_attached_mutex);
-
-       return;
-
-reschedule:
-       queue_delayed_work(di->charger_wq,
-                          &di->ac_charger_attached_work,
-                          HZ);
-}
-
-/**
- * ab8500_charger_detect_usb_type_work() - work to detect USB type
- * @work:      Pointer to the work_struct structure
- *
- * Detect the type of USB plugged
- */
-static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
-{
-       int ret;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, detect_usb_type_work);
-
-       /*
-        * Since we can't be sure that the events are received
-        * synchronously, we have the check if is
-        * connected by reading the status register
-        */
-       ret = ab8500_charger_detect_chargers(di, false);
-       if (ret < 0)
-               return;
-
-       if (!(ret & USB_PW_CONN)) {
-               dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__);
-               di->vbus_detected = false;
-               ab8500_charger_set_usb_connected(di, false);
-               ab8500_power_supply_changed(di, di->usb_chg.psy);
-       } else {
-               dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
-               di->vbus_detected = true;
-
-               if (is_ab8500_1p1_or_earlier(di->parent)) {
-                       ret = ab8500_charger_detect_usb_type(di);
-                       if (!ret) {
-                               ab8500_charger_set_usb_connected(di, true);
-                               ab8500_power_supply_changed(di,
-                                                           di->usb_chg.psy);
-                       }
-               } else {
-                       /*
-                        * For ABB cut2.0 and onwards we have an IRQ,
-                        * USB_LINK_STATUS that will be triggered when the USB
-                        * link status changes. The exception is USB connected
-                        * during startup. Then we don't get a
-                        * USB_LINK_STATUS IRQ
-                        */
-                       if (di->vbus_detected_start) {
-                               di->vbus_detected_start = false;
-                               ret = ab8500_charger_detect_usb_type(di);
-                               if (!ret) {
-                                       ab8500_charger_set_usb_connected(di,
-                                               true);
-                                       ab8500_power_supply_changed(di,
-                                               di->usb_chg.psy);
-                               }
-                       }
-               }
-       }
-}
-
-/**
- * ab8500_charger_usb_link_attach_work() - work to detect USB type
- * @work:      pointer to the work_struct structure
- *
- * Detect the type of USB plugged
- */
-static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
-{
-       struct ab8500_charger *di =
-               container_of(work, struct ab8500_charger, attach_work.work);
-       int ret;
-
-       /* Update maximum input current if USB enumeration is not detected */
-       if (!di->usb.charger_online) {
-               ret = ab8500_charger_set_vbus_in_curr(di,
-                                       di->max_usb_in_curr.usb_type_max);
-               if (ret)
-                       return;
-       }
-
-       ab8500_charger_set_usb_connected(di, true);
-       ab8500_power_supply_changed(di, di->usb_chg.psy);
-}
-
-/**
- * ab8500_charger_usb_link_status_work() - work to detect USB type
- * @work:      pointer to the work_struct structure
- *
- * Detect the type of USB plugged
- */
-static void ab8500_charger_usb_link_status_work(struct work_struct *work)
-{
-       int detected_chargers;
-       int ret;
-       u8 val;
-       u8 link_status;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, usb_link_status_work);
-
-       /*
-        * Since we can't be sure that the events are received
-        * synchronously, we have the check if  is
-        * connected by reading the status register
-        */
-       detected_chargers = ab8500_charger_detect_chargers(di, false);
-       if (detected_chargers < 0)
-               return;
-
-       /*
-        * Some chargers that breaks the USB spec is
-        * identified as invalid by AB8500 and it refuse
-        * to start the charging process. but by jumping
-        * thru a few hoops it can be forced to start.
-        */
-       if (is_ab8500(di->parent))
-               ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-                                       AB8500_USB_LINE_STAT_REG, &val);
-       else
-               ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-                                       AB8500_USB_LINK1_STAT_REG, &val);
-
-       if (ret >= 0)
-               dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
-       else
-               dev_dbg(di->dev, "Error reading USB link status\n");
-
-       if (is_ab8500(di->parent))
-               link_status = AB8500_USB_LINK_STATUS;
-       else
-               link_status = AB8505_USB_LINK_STATUS;
-
-       if (detected_chargers & USB_PW_CONN) {
-               if (((val & link_status) >> USB_LINK_STATUS_SHIFT) ==
-                               USB_STAT_NOT_VALID_LINK &&
-                               di->invalid_charger_detect_state == 0) {
-                       dev_dbg(di->dev,
-                                       "Invalid charger detected, state= 0\n");
-                       /*Enable charger*/
-                       abx500_mask_and_set_register_interruptible(di->dev,
-                                       AB8500_CHARGER, AB8500_USBCH_CTRL1_REG,
-                                       USB_CH_ENA, USB_CH_ENA);
-                       /*Enable charger detection*/
-                       abx500_mask_and_set_register_interruptible(di->dev,
-                                       AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
-                                       USB_CH_DET, USB_CH_DET);
-                       di->invalid_charger_detect_state = 1;
-                       /*exit and wait for new link status interrupt.*/
-                       return;
-
-               }
-               if (di->invalid_charger_detect_state == 1) {
-                       dev_dbg(di->dev,
-                                       "Invalid charger detected, state= 1\n");
-                       /*Stop charger detection*/
-                       abx500_mask_and_set_register_interruptible(di->dev,
-                                       AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
-                                       USB_CH_DET, 0x00);
-                       /*Check link status*/
-                       if (is_ab8500(di->parent))
-                               ret = abx500_get_register_interruptible(di->dev,
-                                       AB8500_USB, AB8500_USB_LINE_STAT_REG,
-                                       &val);
-                       else
-                               ret = abx500_get_register_interruptible(di->dev,
-                                       AB8500_USB, AB8500_USB_LINK1_STAT_REG,
-                                       &val);
-
-                       dev_dbg(di->dev, "USB link status= 0x%02x\n",
-                               (val & link_status) >> USB_LINK_STATUS_SHIFT);
-                       di->invalid_charger_detect_state = 2;
-               }
-       } else {
-               di->invalid_charger_detect_state = 0;
-       }
-
-       if (!(detected_chargers & USB_PW_CONN)) {
-               di->vbus_detected = false;
-               ab8500_charger_set_usb_connected(di, false);
-               ab8500_power_supply_changed(di, di->usb_chg.psy);
-               return;
-       }
-
-       dev_dbg(di->dev,"%s di->vbus_detected = true\n",__func__);
-       di->vbus_detected = true;
-       ret = ab8500_charger_read_usb_type(di);
-       if (ret) {
-               if (ret == -ENXIO) {
-                       /* No valid charger type detected */
-                       ab8500_charger_set_usb_connected(di, false);
-                       ab8500_power_supply_changed(di, di->usb_chg.psy);
-               }
-               return;
-       }
-
-       if (di->usb_device_is_unrecognised) {
-               dev_dbg(di->dev,
-                       "Potential Legacy Charger device. "
-                       "Delay work for %d msec for USB enum "
-                       "to finish",
-                       WAIT_ACA_RID_ENUMERATION);
-               queue_delayed_work(di->charger_wq,
-                                  &di->attach_work,
-                                  msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
-       } else if (di->is_aca_rid == 1) {
-               /* Only wait once */
-               di->is_aca_rid++;
-               dev_dbg(di->dev,
-                       "%s Wait %d msec for USB enum to finish",
-                       __func__, WAIT_ACA_RID_ENUMERATION);
-               queue_delayed_work(di->charger_wq,
-                                  &di->attach_work,
-                                  msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
-       } else {
-               queue_delayed_work(di->charger_wq,
-                                  &di->attach_work,
-                                  0);
-       }
-}
-
-static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
-{
-       int ret;
-       unsigned long flags;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, usb_state_changed_work.work);
-
-       if (!di->vbus_detected) {
-               dev_dbg(di->dev,
-                       "%s !di->vbus_detected\n",
-                       __func__);
-               return;
-       }
-
-       spin_lock_irqsave(&di->usb_state.usb_lock, flags);
-       di->usb_state.state = di->usb_state.state_tmp;
-       di->usb_state.usb_current = di->usb_state.usb_current_tmp;
-       spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
-
-       dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
-               __func__, di->usb_state.state, di->usb_state.usb_current);
-
-       switch (di->usb_state.state) {
-       case AB8500_BM_USB_STATE_RESET_HS:
-       case AB8500_BM_USB_STATE_RESET_FS:
-       case AB8500_BM_USB_STATE_SUSPEND:
-       case AB8500_BM_USB_STATE_MAX:
-               ab8500_charger_set_usb_connected(di, false);
-               ab8500_power_supply_changed(di, di->usb_chg.psy);
-               break;
-
-       case AB8500_BM_USB_STATE_RESUME:
-               /*
-                * when suspend->resume there should be delay
-                * of 1sec for enabling charging
-                */
-               msleep(1000);
-               /* Intentional fall through */
-       case AB8500_BM_USB_STATE_CONFIGURED:
-               /*
-                * USB is configured, enable charging with the charging
-                * input current obtained from USB driver
-                */
-               if (!ab8500_charger_get_usb_cur(di)) {
-                       /* Update maximum input current */
-                       ret = ab8500_charger_set_vbus_in_curr(di,
-                                       di->max_usb_in_curr.usb_type_max);
-                       if (ret)
-                               return;
-
-                       ab8500_charger_set_usb_connected(di, true);
-                       ab8500_power_supply_changed(di, di->usb_chg.psy);
-               }
-               break;
-
-       default:
-               break;
-       };
-}
-
-/**
- * ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for checking the USB charger Not OK status
- */
-static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
-{
-       int ret;
-       u8 reg_value;
-       bool prev_status;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, check_usbchgnotok_work.work);
-
-       /* Check if the status bit for usbchargernotok is still active */
-       ret = abx500_get_register_interruptible(di->dev,
-               AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               return;
-       }
-       prev_status = di->flags.usbchargernotok;
-
-       if (reg_value & VBUS_CH_NOK) {
-               di->flags.usbchargernotok = true;
-               /* Check again in 1sec */
-               queue_delayed_work(di->charger_wq,
-                       &di->check_usbchgnotok_work, HZ);
-       } else {
-               di->flags.usbchargernotok = false;
-               di->flags.vbus_collapse = false;
-       }
-
-       if (prev_status != di->flags.usbchargernotok)
-               ab8500_power_supply_changed(di, di->usb_chg.psy);
-}
-
-/**
- * ab8500_charger_check_main_thermal_prot_work() - check main thermal status
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for checking the Main thermal prot status
- */
-static void ab8500_charger_check_main_thermal_prot_work(
-       struct work_struct *work)
-{
-       int ret;
-       u8 reg_value;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, check_main_thermal_prot_work);
-
-       /* Check if the status bit for main_thermal_prot is still active */
-       ret = abx500_get_register_interruptible(di->dev,
-               AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               return;
-       }
-       if (reg_value & MAIN_CH_TH_PROT)
-               di->flags.main_thermal_prot = true;
-       else
-               di->flags.main_thermal_prot = false;
-
-       ab8500_power_supply_changed(di, di->ac_chg.psy);
-}
-
-/**
- * ab8500_charger_check_usb_thermal_prot_work() - check usb thermal status
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for checking the USB thermal prot status
- */
-static void ab8500_charger_check_usb_thermal_prot_work(
-       struct work_struct *work)
-{
-       int ret;
-       u8 reg_value;
-
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, check_usb_thermal_prot_work);
-
-       /* Check if the status bit for usb_thermal_prot is still active */
-       ret = abx500_get_register_interruptible(di->dev,
-               AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               return;
-       }
-       if (reg_value & USB_CH_TH_PROT)
-               di->flags.usb_thermal_prot = true;
-       else
-               di->flags.usb_thermal_prot = false;
-
-       ab8500_power_supply_changed(di, di->usb_chg.psy);
-}
-
-/**
- * ab8500_charger_mainchunplugdet_handler() - main charger unplugged
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev, "Main charger unplugged\n");
-       queue_work(di->charger_wq, &di->ac_work);
-
-       cancel_delayed_work_sync(&di->ac_charger_attached_work);
-       mutex_lock(&di->charger_attached_mutex);
-       mutex_unlock(&di->charger_attached_mutex);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_mainchplugdet_handler() - main charger plugged
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev, "Main charger plugged\n");
-       queue_work(di->charger_wq, &di->ac_work);
-
-       mutex_lock(&di->charger_attached_mutex);
-       mutex_unlock(&di->charger_attached_mutex);
-
-       if (is_ab8500(di->parent))
-               queue_delayed_work(di->charger_wq,
-                          &di->ac_charger_attached_work,
-                          HZ);
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_mainextchnotok_handler() - main charger not ok
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_mainextchnotok_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev, "Main charger not ok\n");
-       di->flags.mainextchnotok = true;
-       ab8500_power_supply_changed(di, di->ac_chg.psy);
-
-       /* Schedule a new HW failure check */
-       queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_mainchthprotr_handler() - Die temp is above main charger
- * thermal protection threshold
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_mainchthprotr_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev,
-               "Die temp above Main charger thermal protection threshold\n");
-       queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_mainchthprotf_handler() - Die temp is below main charger
- * thermal protection threshold
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev,
-               "Die temp ok for Main charger thermal protection threshold\n");
-       queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
-
-       return IRQ_HANDLED;
-}
-
-static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
-{
-       struct ab8500_charger *di = container_of(work,
-               struct ab8500_charger, vbus_drop_end_work.work);
-       int ret, curr;
-       u8 reg_value;
-
-       di->flags.vbus_drop_end = false;
-
-       /* Reset the drop counter */
-       abx500_set_register_interruptible(di->dev,
-                                 AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
-
-       if (is_ab8540(di->parent))
-               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-                               AB8540_CH_USBCH_STAT3_REG, &reg_value);
-       else
-               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
-                               AB8500_CH_USBCH_STAT2_REG, &reg_value);
-       if (ret < 0) {
-               dev_err(di->dev, "%s read failed\n", __func__);
-               return;
-       }
-
-       if (is_ab8540(di->parent))
-               curr = di->bm->chg_input_curr[
-                       reg_value & AB8540_AUTO_VBUS_IN_CURR_MASK];
-       else
-               curr = di->bm->chg_input_curr[
-                       reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT];
-
-       if (di->max_usb_in_curr.calculated_max != curr) {
-               /* USB source is collapsing */
-               di->max_usb_in_curr.calculated_max = curr;
-               dev_dbg(di->dev,
-                        "VBUS input current limiting to %d mA\n",
-                        di->max_usb_in_curr.calculated_max);
-       } else {
-               /*
-                * USB source can not give more than this amount.
-                * Taking more will collapse the source.
-                */
-               di->max_usb_in_curr.set_max =
-                       di->max_usb_in_curr.calculated_max;
-               dev_dbg(di->dev,
-                        "VBUS input current limited to %d mA\n",
-                        di->max_usb_in_curr.set_max);
-       }
-
-       if (di->usb.charger_connected)
-               ab8500_charger_set_vbus_in_curr(di,
-                                       di->max_usb_in_curr.usb_type_max);
-}
-
-/**
- * ab8500_charger_vbusdetf_handler() - VBUS falling detected
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       di->vbus_detected = false;
-       dev_dbg(di->dev, "VBUS falling detected\n");
-       queue_work(di->charger_wq, &di->detect_usb_type_work);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_vbusdetr_handler() - VBUS rising detected
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_vbusdetr_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       di->vbus_detected = true;
-       dev_dbg(di->dev, "VBUS rising detected\n");
-
-       queue_work(di->charger_wq, &di->detect_usb_type_work);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_usblinkstatus_handler() - USB link status has changed
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_usblinkstatus_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev, "USB link status changed\n");
-
-       queue_work(di->charger_wq, &di->usb_link_status_work);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_usbchthprotr_handler() - Die temp is above usb charger
- * thermal protection threshold
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_usbchthprotr_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev,
-               "Die temp above USB charger thermal protection threshold\n");
-       queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_usbchthprotf_handler() - Die temp is below usb charger
- * thermal protection threshold
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_usbchthprotf_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev,
-               "Die temp ok for USB charger thermal protection threshold\n");
-       queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_usbchargernotokr_handler() - USB charger not ok detected
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_usbchargernotokr_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev, "Not allowed USB charger detected\n");
-       queue_delayed_work(di->charger_wq, &di->check_usbchgnotok_work, 0);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_chwdexp_handler() - Charger watchdog expired
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev, "Charger watchdog expired\n");
-
-       /*
-        * The charger that was online when the watchdog expired
-        * needs to be restarted for charging to start again
-        */
-       if (di->ac.charger_online) {
-               di->ac.wd_expired = true;
-               ab8500_power_supply_changed(di, di->ac_chg.psy);
-       }
-       if (di->usb.charger_online) {
-               di->usb.wd_expired = true;
-               ab8500_power_supply_changed(di, di->usb_chg.psy);
-       }
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_vbuschdropend_handler() - VBUS drop removed
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev, "VBUS charger drop ended\n");
-       di->flags.vbus_drop_end = true;
-
-       /*
-        * VBUS might have dropped due to bad connection.
-        * Schedule a new input limit set to the value SW requests.
-        */
-       queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
-                          round_jiffies(VBUS_IN_CURR_LIM_RETRY_SET_TIME * HZ));
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_charger structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_charger_vbusovv_handler(int irq, void *_di)
-{
-       struct ab8500_charger *di = _di;
-
-       dev_dbg(di->dev, "VBUS overvoltage detected\n");
-       di->flags.vbus_ovv = true;
-       ab8500_power_supply_changed(di, di->usb_chg.psy);
-
-       /* Schedule a new HW failure check */
-       queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_charger_ac_get_property() - get the ac/mains properties
- * @psy:       pointer to the power_supply structure
- * @psp:       pointer to the power_supply_property structure
- * @val:       pointer to the power_supply_propval union
- *
- * This function gets called when an application tries to get the ac/mains
- * properties by reading the sysfs files.
- * AC/Mains properties are online, present and voltage.
- * online:     ac/mains charging is in progress or not
- * present:    presence of the ac/mains
- * voltage:    AC/Mains voltage
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_ac_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       struct ab8500_charger *di;
-       int ret;
-
-       di = to_ab8500_charger_ac_device_info(psy_to_ux500_charger(psy));
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (di->flags.mainextchnotok)
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               else if (di->ac.wd_expired || di->usb.wd_expired)
-                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
-               else if (di->flags.main_thermal_prot)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = di->ac.charger_online;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = di->ac.charger_connected;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = ab8500_charger_get_ac_voltage(di);
-               if (ret >= 0)
-                       di->ac.charger_voltage = ret;
-               /* On error, use previous value */
-               val->intval = di->ac.charger_voltage * 1000;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               /*
-                * This property is used to indicate when CV mode is entered
-                * for the AC charger
-                */
-               di->ac.cv_active = ab8500_charger_ac_cv(di);
-               val->intval = di->ac.cv_active;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = ab8500_charger_get_ac_current(di);
-               if (ret >= 0)
-                       di->ac.charger_current = ret;
-               val->intval = di->ac.charger_current * 1000;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-/**
- * ab8500_charger_usb_get_property() - get the usb properties
- * @psy:        pointer to the power_supply structure
- * @psp:        pointer to the power_supply_property structure
- * @val:        pointer to the power_supply_propval union
- *
- * This function gets called when an application tries to get the usb
- * properties by reading the sysfs files.
- * USB properties are online, present and voltage.
- * online:     usb charging is in progress or not
- * present:    presence of the usb
- * voltage:    vbus voltage
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_charger_usb_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       struct ab8500_charger *di;
-       int ret;
-
-       di = to_ab8500_charger_usb_device_info(psy_to_ux500_charger(psy));
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (di->flags.usbchargernotok)
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               else if (di->ac.wd_expired || di->usb.wd_expired)
-                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
-               else if (di->flags.usb_thermal_prot)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else if (di->flags.vbus_ovv)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = di->usb.charger_online;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = di->usb.charger_connected;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = ab8500_charger_get_vbus_voltage(di);
-               if (ret >= 0)
-                       di->usb.charger_voltage = ret;
-               val->intval = di->usb.charger_voltage * 1000;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               /*
-                * This property is used to indicate when CV mode is entered
-                * for the USB charger
-                */
-               di->usb.cv_active = ab8500_charger_usb_cv(di);
-               val->intval = di->usb.cv_active;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = ab8500_charger_get_usb_current(di);
-               if (ret >= 0)
-                       di->usb.charger_current = ret;
-               val->intval = di->usb.charger_current * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               /*
-                * This property is used to indicate when VBUS has collapsed
-                * due to too high output current from the USB charger
-                */
-               if (di->flags.vbus_collapse)
-                       val->intval = 1;
-               else
-                       val->intval = 0;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-/**
- * ab8500_charger_init_hw_registers() - Set up charger related registers
- * @di:                pointer to the ab8500_charger structure
- *
- * Set up charger OVV, watchdog and maximum voltage registers as well as
- * charging of the backup battery
- */
-static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
-{
-       int ret = 0;
-       u8 bup_vch_range = 0, vbup33_vrtcn = 0;
-
-       /* Setup maximum charger current and voltage for ABB cut2.0 */
-       if (!is_ab8500_1p1_or_earlier(di->parent)) {
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_CHARGER,
-                       AB8500_CH_VOLT_LVL_MAX_REG, CH_VOL_LVL_4P6);
-               if (ret) {
-                       dev_err(di->dev,
-                               "failed to set CH_VOLT_LVL_MAX_REG\n");
-                       goto out;
-               }
-
-               if (is_ab8540(di->parent))
-                       ret = abx500_set_register_interruptible(di->dev,
-                               AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG,
-                               CH_OP_CUR_LVL_2P);
-               else
-                       ret = abx500_set_register_interruptible(di->dev,
-                               AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG,
-                               CH_OP_CUR_LVL_1P6);
-               if (ret) {
-                       dev_err(di->dev,
-                               "failed to set CH_OPT_CRNTLVL_MAX_REG\n");
-                       goto out;
-               }
-       }
-
-       if (is_ab9540_2p0(di->parent) || is_ab9540_3p0(di->parent)
-        || is_ab8505_2p0(di->parent) || is_ab8540(di->parent))
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER,
-                       AB8500_USBCH_CTRL2_REG,
-                       VBUS_AUTO_IN_CURR_LIM_ENA,
-                       VBUS_AUTO_IN_CURR_LIM_ENA);
-       else
-               /*
-                * VBUS OVV set to 6.3V and enable automatic current limitation
-                */
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_CHARGER,
-                       AB8500_USBCH_CTRL2_REG,
-                       VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
-       if (ret) {
-               dev_err(di->dev,
-                       "failed to set automatic current limitation\n");
-               goto out;
-       }
-
-       /* Enable main watchdog in OTP */
-       ret = abx500_set_register_interruptible(di->dev,
-               AB8500_OTP_EMUL, AB8500_OTP_CONF_15, OTP_ENABLE_WD);
-       if (ret) {
-               dev_err(di->dev, "failed to enable main WD in OTP\n");
-               goto out;
-       }
-
-       /* Enable main watchdog */
-       ret = abx500_set_register_interruptible(di->dev,
-               AB8500_SYS_CTRL2_BLOCK,
-               AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_ENA);
-       if (ret) {
-               dev_err(di->dev, "faile to enable main watchdog\n");
-               goto out;
-       }
-
-       /*
-        * Due to internal synchronisation, Enable and Kick watchdog bits
-        * cannot be enabled in a single write.
-        * A minimum delay of 2*32 kHz period (62.5µs) must be inserted
-        * between writing Enable then Kick bits.
-        */
-       udelay(63);
-
-       /* Kick main watchdog */
-       ret = abx500_set_register_interruptible(di->dev,
-               AB8500_SYS_CTRL2_BLOCK,
-               AB8500_MAIN_WDOG_CTRL_REG,
-               (MAIN_WDOG_ENA | MAIN_WDOG_KICK));
-       if (ret) {
-               dev_err(di->dev, "failed to kick main watchdog\n");
-               goto out;
-       }
-
-       /* Disable main watchdog */
-       ret = abx500_set_register_interruptible(di->dev,
-               AB8500_SYS_CTRL2_BLOCK,
-               AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_DIS);
-       if (ret) {
-               dev_err(di->dev, "failed to disable main watchdog\n");
-               goto out;
-       }
-
-       /* Set watchdog timeout */
-       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-               AB8500_CH_WD_TIMER_REG, WD_TIMER);
-       if (ret) {
-               dev_err(di->dev, "failed to set charger watchdog timeout\n");
-               goto out;
-       }
-
-       ret = ab8500_charger_led_en(di, false);
-       if (ret < 0) {
-               dev_err(di->dev, "failed to disable LED\n");
-               goto out;
-       }
-
-       /* Backup battery voltage and current */
-       if (di->bm->bkup_bat_v > BUP_VCH_SEL_3P1V)
-               bup_vch_range = BUP_VCH_RANGE;
-       if (di->bm->bkup_bat_v == BUP_VCH_SEL_3P3V)
-               vbup33_vrtcn = VBUP33_VRTCN;
-
-       ret = abx500_set_register_interruptible(di->dev,
-               AB8500_RTC,
-               AB8500_RTC_BACKUP_CHG_REG,
-               (di->bm->bkup_bat_v & 0x3) | di->bm->bkup_bat_i);
-       if (ret) {
-               dev_err(di->dev, "failed to setup backup battery charging\n");
-               goto out;
-       }
-       if (is_ab8540(di->parent)) {
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_RTC,
-                       AB8500_RTC_CTRL1_REG,
-                       bup_vch_range | vbup33_vrtcn);
-               if (ret) {
-                       dev_err(di->dev,
-                               "failed to setup backup battery charging\n");
-                       goto out;
-               }
-       }
-
-       /* Enable backup battery charging */
-       abx500_mask_and_set_register_interruptible(di->dev,
-               AB8500_RTC, AB8500_RTC_CTRL_REG,
-               RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
-       if (ret < 0)
-               dev_err(di->dev, "%s mask and set failed\n", __func__);
-
-       if (is_ab8540(di->parent)) {
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8540_USB_PP_MODE_REG,
-                       BUS_VSYS_VOL_SELECT_MASK, BUS_VSYS_VOL_SELECT_3P6V);
-               if (ret) {
-                       dev_err(di->dev,
-                               "failed to setup usb power path vsys voltage\n");
-                       goto out;
-               }
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_CHARGER, AB8540_USB_PP_CHR_REG,
-                       BUS_PP_PRECHG_CURRENT_MASK, 0);
-               if (ret) {
-                       dev_err(di->dev,
-                               "failed to setup usb power path prechage current\n");
-                       goto out;
-               }
-       }
-
-out:
-       return ret;
-}
-
-/*
- * ab8500 charger driver interrupts and their respective isr
- */
-static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
-       {"MAIN_CH_UNPLUG_DET", ab8500_charger_mainchunplugdet_handler},
-       {"MAIN_CHARGE_PLUG_DET", ab8500_charger_mainchplugdet_handler},
-       {"MAIN_EXT_CH_NOT_OK", ab8500_charger_mainextchnotok_handler},
-       {"MAIN_CH_TH_PROT_R", ab8500_charger_mainchthprotr_handler},
-       {"MAIN_CH_TH_PROT_F", ab8500_charger_mainchthprotf_handler},
-       {"VBUS_DET_F", ab8500_charger_vbusdetf_handler},
-       {"VBUS_DET_R", ab8500_charger_vbusdetr_handler},
-       {"USB_LINK_STATUS", ab8500_charger_usblinkstatus_handler},
-       {"USB_CH_TH_PROT_R", ab8500_charger_usbchthprotr_handler},
-       {"USB_CH_TH_PROT_F", ab8500_charger_usbchthprotf_handler},
-       {"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
-       {"VBUS_OVV", ab8500_charger_vbusovv_handler},
-       {"CH_WD_EXP", ab8500_charger_chwdexp_handler},
-       {"VBUS_CH_DROP_END", ab8500_charger_vbuschdropend_handler},
-};
-
-static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
-               unsigned long event, void *power)
-{
-       struct ab8500_charger *di =
-               container_of(nb, struct ab8500_charger, nb);
-       enum ab8500_usb_state bm_usb_state;
-       unsigned mA = *((unsigned *)power);
-
-       if (!di)
-               return NOTIFY_DONE;
-
-       if (event != USB_EVENT_VBUS) {
-               dev_dbg(di->dev, "not a standard host, returning\n");
-               return NOTIFY_DONE;
-       }
-
-       /* TODO: State is fabricate  here. See if charger really needs USB
-        * state or if mA is enough
-        */
-       if ((di->usb_state.usb_current == 2) && (mA > 2))
-               bm_usb_state = AB8500_BM_USB_STATE_RESUME;
-       else if (mA == 0)
-               bm_usb_state = AB8500_BM_USB_STATE_RESET_HS;
-       else if (mA == 2)
-               bm_usb_state = AB8500_BM_USB_STATE_SUSPEND;
-       else if (mA >= 8) /* 8, 100, 500 */
-               bm_usb_state = AB8500_BM_USB_STATE_CONFIGURED;
-       else /* Should never occur */
-               bm_usb_state = AB8500_BM_USB_STATE_RESET_FS;
-
-       dev_dbg(di->dev, "%s usb_state: 0x%02x mA: %d\n",
-               __func__, bm_usb_state, mA);
-
-       spin_lock(&di->usb_state.usb_lock);
-       di->usb_state.state_tmp = bm_usb_state;
-       di->usb_state.usb_current_tmp = mA;
-       spin_unlock(&di->usb_state.usb_lock);
-
-       /*
-        * wait for some time until you get updates from the usb stack
-        * and negotiations are completed
-        */
-       queue_delayed_work(di->charger_wq, &di->usb_state_changed_work, HZ/2);
-
-       return NOTIFY_OK;
-}
-
-#if defined(CONFIG_PM)
-static int ab8500_charger_resume(struct platform_device *pdev)
-{
-       int ret;
-       struct ab8500_charger *di = platform_get_drvdata(pdev);
-
-       /*
-        * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
-        * logic. That means we have to continously kick the charger
-        * watchdog even when no charger is connected. This is only
-        * valid once the AC charger has been enabled. This is
-        * a bug that is not handled by the algorithm and the
-        * watchdog have to be kicked by the charger driver
-        * when the AC charger is disabled
-        */
-       if (di->ac_conn && is_ab8500_1p1_or_earlier(di->parent)) {
-               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                       AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
-               if (ret)
-                       dev_err(di->dev, "Failed to kick WD!\n");
-
-               /* If not already pending start a new timer */
-               queue_delayed_work(di->charger_wq, &di->kick_wd_work,
-                                  round_jiffies(WD_KICK_INTERVAL));
-       }
-
-       /* If we still have a HW failure, schedule a new check */
-       if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
-               queue_delayed_work(di->charger_wq,
-                       &di->check_hw_failure_work, 0);
-       }
-
-       if (di->flags.vbus_drop_end)
-               queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work, 0);
-
-       return 0;
-}
-
-static int ab8500_charger_suspend(struct platform_device *pdev,
-       pm_message_t state)
-{
-       struct ab8500_charger *di = platform_get_drvdata(pdev);
-
-       /* Cancel any pending jobs */
-       cancel_delayed_work(&di->check_hw_failure_work);
-       cancel_delayed_work(&di->vbus_drop_end_work);
-
-       flush_delayed_work(&di->attach_work);
-       flush_delayed_work(&di->usb_charger_attached_work);
-       flush_delayed_work(&di->ac_charger_attached_work);
-       flush_delayed_work(&di->check_usbchgnotok_work);
-       flush_delayed_work(&di->check_vbat_work);
-       flush_delayed_work(&di->kick_wd_work);
-
-       flush_work(&di->usb_link_status_work);
-       flush_work(&di->ac_work);
-       flush_work(&di->detect_usb_type_work);
-
-       if (atomic_read(&di->current_stepping_sessions))
-               return -EAGAIN;
-
-       return 0;
-}
-#else
-#define ab8500_charger_suspend      NULL
-#define ab8500_charger_resume       NULL
-#endif
-
-static struct notifier_block charger_nb = {
-       .notifier_call = ab8500_external_charger_prepare,
-};
-
-static int ab8500_charger_remove(struct platform_device *pdev)
-{
-       struct ab8500_charger *di = platform_get_drvdata(pdev);
-       int i, irq, ret;
-
-       /* Disable AC charging */
-       ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
-
-       /* Disable USB charging */
-       ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
-
-       /* Disable interrupts */
-       for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
-               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
-               free_irq(irq, di);
-       }
-
-       /* Backup battery voltage and current disable */
-       ret = abx500_mask_and_set_register_interruptible(di->dev,
-               AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
-       if (ret < 0)
-               dev_err(di->dev, "%s mask and set failed\n", __func__);
-
-       usb_unregister_notifier(di->usb_phy, &di->nb);
-       usb_put_phy(di->usb_phy);
-
-       /* Delete the work queue */
-       destroy_workqueue(di->charger_wq);
-
-       /* Unregister external charger enable notifier */
-       if (!di->ac_chg.enabled)
-               blocking_notifier_chain_unregister(
-                       &charger_notifier_list, &charger_nb);
-
-       flush_scheduled_work();
-       if (di->usb_chg.enabled)
-               power_supply_unregister(di->usb_chg.psy);
-
-       if (di->ac_chg.enabled && !di->ac_chg.external)
-               power_supply_unregister(di->ac_chg.psy);
-
-       return 0;
-}
-
-static char *supply_interface[] = {
-       "ab8500_chargalg",
-       "ab8500_fg",
-       "ab8500_btemp",
-};
-
-static const struct power_supply_desc ab8500_ac_chg_desc = {
-       .name           = "ab8500_ac",
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-       .properties     = ab8500_charger_ac_props,
-       .num_properties = ARRAY_SIZE(ab8500_charger_ac_props),
-       .get_property   = ab8500_charger_ac_get_property,
-};
-
-static const struct power_supply_desc ab8500_usb_chg_desc = {
-       .name           = "ab8500_usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .properties     = ab8500_charger_usb_props,
-       .num_properties = ARRAY_SIZE(ab8500_charger_usb_props),
-       .get_property   = ab8500_charger_usb_get_property,
-};
-
-static int ab8500_charger_probe(struct platform_device *pdev)
-{
-       struct device_node *np = pdev->dev.of_node;
-       struct abx500_bm_data *plat = pdev->dev.platform_data;
-       struct power_supply_config ac_psy_cfg = {}, usb_psy_cfg = {};
-       struct ab8500_charger *di;
-       int irq, i, charger_status, ret = 0, ch_stat;
-
-       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di) {
-               dev_err(&pdev->dev, "%s no mem for ab8500_charger\n", __func__);
-               return -ENOMEM;
-       }
-
-       if (!plat) {
-               dev_err(&pdev->dev, "no battery management data supplied\n");
-               return -EINVAL;
-       }
-       di->bm = plat;
-
-       if (np) {
-               ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
-               if (ret) {
-                       dev_err(&pdev->dev, "failed to get battery information\n");
-                       return ret;
-               }
-               di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
-       } else
-               di->autopower_cfg = false;
-
-       /* get parent data */
-       di->dev = &pdev->dev;
-       di->parent = dev_get_drvdata(pdev->dev.parent);
-       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-
-       /* initialize lock */
-       spin_lock_init(&di->usb_state.usb_lock);
-       mutex_init(&di->usb_ipt_crnt_lock);
-
-       di->autopower = false;
-       di->invalid_charger_detect_state = 0;
-
-       /* AC and USB supply config */
-       ac_psy_cfg.supplied_to = supply_interface;
-       ac_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
-       ac_psy_cfg.drv_data = &di->ac_chg;
-       usb_psy_cfg.supplied_to = supply_interface;
-       usb_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
-       usb_psy_cfg.drv_data = &di->usb_chg;
-
-       /* AC supply */
-       /* ux500_charger sub-class */
-       di->ac_chg.ops.enable = &ab8500_charger_ac_en;
-       di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable;
-       di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
-       di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
-       di->ac_chg.max_out_volt = ab8500_charger_voltage_map[
-               ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
-       di->ac_chg.max_out_curr =
-               di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1];
-       di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
-       di->ac_chg.enabled = di->bm->ac_enabled;
-       di->ac_chg.external = false;
-
-       /*notifier for external charger enabling*/
-       if (!di->ac_chg.enabled)
-               blocking_notifier_chain_register(
-                       &charger_notifier_list, &charger_nb);
-
-       /* USB supply */
-       /* ux500_charger sub-class */
-       di->usb_chg.ops.enable = &ab8500_charger_usb_en;
-       di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable;
-       di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
-       di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
-       di->usb_chg.ops.pp_enable = &ab8540_charger_power_path_enable;
-       di->usb_chg.ops.pre_chg_enable = &ab8540_charger_usb_pre_chg_enable;
-       di->usb_chg.max_out_volt = ab8500_charger_voltage_map[
-               ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
-       di->usb_chg.max_out_curr =
-               di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1];
-       di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
-       di->usb_chg.enabled = di->bm->usb_enabled;
-       di->usb_chg.external = false;
-       di->usb_chg.power_path = di->bm->usb_power_path;
-       di->usb_state.usb_current = -1;
-
-       /* Create a work queue for the charger */
-       di->charger_wq =
-               create_singlethread_workqueue("ab8500_charger_wq");
-       if (di->charger_wq == NULL) {
-               dev_err(di->dev, "failed to create work queue\n");
-               return -ENOMEM;
-       }
-
-       mutex_init(&di->charger_attached_mutex);
-
-       /* Init work for HW failure check */
-       INIT_DEFERRABLE_WORK(&di->check_hw_failure_work,
-               ab8500_charger_check_hw_failure_work);
-       INIT_DEFERRABLE_WORK(&di->check_usbchgnotok_work,
-               ab8500_charger_check_usbchargernotok_work);
-
-       INIT_DELAYED_WORK(&di->ac_charger_attached_work,
-                         ab8500_charger_ac_attached_work);
-       INIT_DELAYED_WORK(&di->usb_charger_attached_work,
-                         ab8500_charger_usb_attached_work);
-
-       /*
-        * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
-        * logic. That means we have to continously kick the charger
-        * watchdog even when no charger is connected. This is only
-        * valid once the AC charger has been enabled. This is
-        * a bug that is not handled by the algorithm and the
-        * watchdog have to be kicked by the charger driver
-        * when the AC charger is disabled
-        */
-       INIT_DEFERRABLE_WORK(&di->kick_wd_work,
-               ab8500_charger_kick_watchdog_work);
-
-       INIT_DEFERRABLE_WORK(&di->check_vbat_work,
-               ab8500_charger_check_vbat_work);
-
-       INIT_DELAYED_WORK(&di->attach_work,
-               ab8500_charger_usb_link_attach_work);
-
-       INIT_DELAYED_WORK(&di->usb_state_changed_work,
-               ab8500_charger_usb_state_changed_work);
-
-       INIT_DELAYED_WORK(&di->vbus_drop_end_work,
-               ab8500_charger_vbus_drop_end_work);
-
-       /* Init work for charger detection */
-       INIT_WORK(&di->usb_link_status_work,
-               ab8500_charger_usb_link_status_work);
-       INIT_WORK(&di->ac_work, ab8500_charger_ac_work);
-       INIT_WORK(&di->detect_usb_type_work,
-               ab8500_charger_detect_usb_type_work);
-
-       /* Init work for checking HW status */
-       INIT_WORK(&di->check_main_thermal_prot_work,
-               ab8500_charger_check_main_thermal_prot_work);
-       INIT_WORK(&di->check_usb_thermal_prot_work,
-               ab8500_charger_check_usb_thermal_prot_work);
-
-       /*
-        * VDD ADC supply needs to be enabled from this driver when there
-        * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
-        * interrupts during charging
-        */
-       di->regu = devm_regulator_get(di->dev, "vddadc");
-       if (IS_ERR(di->regu)) {
-               ret = PTR_ERR(di->regu);
-               dev_err(di->dev, "failed to get vddadc regulator\n");
-               goto free_charger_wq;
-       }
-
-
-       /* Initialize OVV, and other registers */
-       ret = ab8500_charger_init_hw_registers(di);
-       if (ret) {
-               dev_err(di->dev, "failed to initialize ABB registers\n");
-               goto free_charger_wq;
-       }
-
-       /* Register AC charger class */
-       if (di->ac_chg.enabled) {
-               di->ac_chg.psy = power_supply_register(di->dev,
-                                                      &ab8500_ac_chg_desc,
-                                                      &ac_psy_cfg);
-               if (IS_ERR(di->ac_chg.psy)) {
-                       dev_err(di->dev, "failed to register AC charger\n");
-                       ret = PTR_ERR(di->ac_chg.psy);
-                       goto free_charger_wq;
-               }
-       }
-
-       /* Register USB charger class */
-       if (di->usb_chg.enabled) {
-               di->usb_chg.psy = power_supply_register(di->dev,
-                                                       &ab8500_usb_chg_desc,
-                                                       &usb_psy_cfg);
-               if (IS_ERR(di->usb_chg.psy)) {
-                       dev_err(di->dev, "failed to register USB charger\n");
-                       ret = PTR_ERR(di->usb_chg.psy);
-                       goto free_ac;
-               }
-       }
-
-       di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
-       if (IS_ERR_OR_NULL(di->usb_phy)) {
-               dev_err(di->dev, "failed to get usb transceiver\n");
-               ret = -EINVAL;
-               goto free_usb;
-       }
-       di->nb.notifier_call = ab8500_charger_usb_notifier_call;
-       ret = usb_register_notifier(di->usb_phy, &di->nb);
-       if (ret) {
-               dev_err(di->dev, "failed to register usb notifier\n");
-               goto put_usb_phy;
-       }
-
-       /* Identify the connected charger types during startup */
-       charger_status = ab8500_charger_detect_chargers(di, true);
-       if (charger_status & AC_PW_CONN) {
-               di->ac.charger_connected = 1;
-               di->ac_conn = true;
-               ab8500_power_supply_changed(di, di->ac_chg.psy);
-               sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present");
-       }
-
-       if (charger_status & USB_PW_CONN) {
-               di->vbus_detected = true;
-               di->vbus_detected_start = true;
-               queue_work(di->charger_wq,
-                       &di->detect_usb_type_work);
-       }
-
-       /* Register interrupts */
-       for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
-               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
-               ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr,
-                       IRQF_SHARED | IRQF_NO_SUSPEND,
-                       ab8500_charger_irq[i].name, di);
-
-               if (ret != 0) {
-                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
-                               , ab8500_charger_irq[i].name, irq, ret);
-                       goto free_irq;
-               }
-               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
-                       ab8500_charger_irq[i].name, irq, ret);
-       }
-
-       platform_set_drvdata(pdev, di);
-
-       mutex_lock(&di->charger_attached_mutex);
-
-       ch_stat = ab8500_charger_detect_chargers(di, false);
-
-       if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
-               if (is_ab8500(di->parent))
-                       queue_delayed_work(di->charger_wq,
-                                          &di->ac_charger_attached_work,
-                                          HZ);
-       }
-       if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) {
-               if (is_ab8500(di->parent))
-                       queue_delayed_work(di->charger_wq,
-                                          &di->usb_charger_attached_work,
-                                          HZ);
-       }
-
-       mutex_unlock(&di->charger_attached_mutex);
-
-       return ret;
-
-free_irq:
-       usb_unregister_notifier(di->usb_phy, &di->nb);
-
-       /* We also have to free all successfully registered irqs */
-       for (i = i - 1; i >= 0; i--) {
-               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
-               free_irq(irq, di);
-       }
-put_usb_phy:
-       usb_put_phy(di->usb_phy);
-free_usb:
-       if (di->usb_chg.enabled)
-               power_supply_unregister(di->usb_chg.psy);
-free_ac:
-       if (di->ac_chg.enabled)
-               power_supply_unregister(di->ac_chg.psy);
-free_charger_wq:
-       destroy_workqueue(di->charger_wq);
-       return ret;
-}
-
-static const struct of_device_id ab8500_charger_match[] = {
-       { .compatible = "stericsson,ab8500-charger", },
-       { },
-};
-
-static struct platform_driver ab8500_charger_driver = {
-       .probe = ab8500_charger_probe,
-       .remove = ab8500_charger_remove,
-       .suspend = ab8500_charger_suspend,
-       .resume = ab8500_charger_resume,
-       .driver = {
-               .name = "ab8500-charger",
-               .of_match_table = ab8500_charger_match,
-       },
-};
-
-static int __init ab8500_charger_init(void)
-{
-       return platform_driver_register(&ab8500_charger_driver);
-}
-
-static void __exit ab8500_charger_exit(void)
-{
-       platform_driver_unregister(&ab8500_charger_driver);
-}
-
-subsys_initcall_sync(ab8500_charger_init);
-module_exit(ab8500_charger_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
-MODULE_ALIAS("platform:ab8500-charger");
-MODULE_DESCRIPTION("AB8500 charger management driver");
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
deleted file mode 100644 (file)
index 5a36cf8..0000000
+++ /dev/null
@@ -1,3272 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson AB 2012
- *
- * Main and Back-up battery management driver.
- *
- * Note: Backup battery management is required in case of Li-Ion battery and not
- * for capacitive battery. HREF boards have capacitive battery and hence backup
- * battery management is not used and the supported code is available in this
- * driver.
- *
- * License Terms: GNU General Public License v2
- * Author:
- *     Johan Palsson <johan.palsson@stericsson.com>
- *     Karl Komierowski <karl.komierowski@stericsson.com>
- *     Arun R Murthy <arun.murthy@stericsson.com>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/kobject.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/time64.h>
-#include <linux/of.h>
-#include <linux/completion.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/abx500.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
-#include <linux/mfd/abx500/ab8500-gpadc.h>
-#include <linux/kernel.h>
-
-#define MILLI_TO_MICRO                 1000
-#define FG_LSB_IN_MA                   1627
-#define QLSB_NANO_AMP_HOURS_X10                1071
-#define INS_CURR_TIMEOUT               (3 * HZ)
-
-#define SEC_TO_SAMPLE(S)               (S * 4)
-
-#define NBR_AVG_SAMPLES                        20
-
-#define LOW_BAT_CHECK_INTERVAL         (HZ / 16) /* 62.5 ms */
-
-#define VALID_CAPACITY_SEC             (45 * 60) /* 45 minutes */
-#define BATT_OK_MIN                    2360 /* mV */
-#define BATT_OK_INCREMENT              50 /* mV */
-#define BATT_OK_MAX_NR_INCREMENTS      0xE
-
-/* FG constants */
-#define BATT_OVV                       0x01
-
-#define interpolate(x, x1, y1, x2, y2) \
-       ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
-
-/**
- * struct ab8500_fg_interrupts - ab8500 fg interupts
- * @name:      name of the interrupt
- * @isr                function pointer to the isr
- */
-struct ab8500_fg_interrupts {
-       char *name;
-       irqreturn_t (*isr)(int irq, void *data);
-};
-
-enum ab8500_fg_discharge_state {
-       AB8500_FG_DISCHARGE_INIT,
-       AB8500_FG_DISCHARGE_INITMEASURING,
-       AB8500_FG_DISCHARGE_INIT_RECOVERY,
-       AB8500_FG_DISCHARGE_RECOVERY,
-       AB8500_FG_DISCHARGE_READOUT_INIT,
-       AB8500_FG_DISCHARGE_READOUT,
-       AB8500_FG_DISCHARGE_WAKEUP,
-};
-
-static char *discharge_state[] = {
-       "DISCHARGE_INIT",
-       "DISCHARGE_INITMEASURING",
-       "DISCHARGE_INIT_RECOVERY",
-       "DISCHARGE_RECOVERY",
-       "DISCHARGE_READOUT_INIT",
-       "DISCHARGE_READOUT",
-       "DISCHARGE_WAKEUP",
-};
-
-enum ab8500_fg_charge_state {
-       AB8500_FG_CHARGE_INIT,
-       AB8500_FG_CHARGE_READOUT,
-};
-
-static char *charge_state[] = {
-       "CHARGE_INIT",
-       "CHARGE_READOUT",
-};
-
-enum ab8500_fg_calibration_state {
-       AB8500_FG_CALIB_INIT,
-       AB8500_FG_CALIB_WAIT,
-       AB8500_FG_CALIB_END,
-};
-
-struct ab8500_fg_avg_cap {
-       int avg;
-       int samples[NBR_AVG_SAMPLES];
-       time64_t time_stamps[NBR_AVG_SAMPLES];
-       int pos;
-       int nbr_samples;
-       int sum;
-};
-
-struct ab8500_fg_cap_scaling {
-       bool enable;
-       int cap_to_scale[2];
-       int disable_cap_level;
-       int scaled_cap;
-};
-
-struct ab8500_fg_battery_capacity {
-       int max_mah_design;
-       int max_mah;
-       int mah;
-       int permille;
-       int level;
-       int prev_mah;
-       int prev_percent;
-       int prev_level;
-       int user_mah;
-       struct ab8500_fg_cap_scaling cap_scale;
-};
-
-struct ab8500_fg_flags {
-       bool fg_enabled;
-       bool conv_done;
-       bool charging;
-       bool fully_charged;
-       bool force_full;
-       bool low_bat_delay;
-       bool low_bat;
-       bool bat_ovv;
-       bool batt_unknown;
-       bool calibrate;
-       bool user_cap;
-       bool batt_id_received;
-};
-
-struct inst_curr_result_list {
-       struct list_head list;
-       int *result;
-};
-
-/**
- * struct ab8500_fg - ab8500 FG device information
- * @dev:               Pointer to the structure device
- * @node:              a list of AB8500 FGs, hence prepared for reentrance
- * @irq                        holds the CCEOC interrupt number
- * @vbat:              Battery voltage in mV
- * @vbat_nom:          Nominal battery voltage in mV
- * @inst_curr:         Instantenous battery current in mA
- * @avg_curr:          Average battery current in mA
- * @bat_temp           battery temperature
- * @fg_samples:                Number of samples used in the FG accumulation
- * @accu_charge:       Accumulated charge from the last conversion
- * @recovery_cnt:      Counter for recovery mode
- * @high_curr_cnt:     Counter for high current mode
- * @init_cnt:          Counter for init mode
- * @low_bat_cnt                Counter for number of consecutive low battery measures
- * @nbr_cceoc_irq_cnt  Counter for number of CCEOC irqs received since enabled
- * @recovery_needed:   Indicate if recovery is needed
- * @high_curr_mode:    Indicate if we're in high current mode
- * @init_capacity:     Indicate if initial capacity measuring should be done
- * @turn_off_fg:       True if fg was off before current measurement
- * @calib_state                State during offset calibration
- * @discharge_state:   Current discharge state
- * @charge_state:      Current charge state
- * @ab8500_fg_started  Completion struct used for the instant current start
- * @ab8500_fg_complete Completion struct used for the instant current reading
- * @flags:             Structure for information about events triggered
- * @bat_cap:           Structure for battery capacity specific parameters
- * @avg_cap:           Average capacity filter
- * @parent:            Pointer to the struct ab8500
- * @gpadc:             Pointer to the struct gpadc
- * @bm:                Platform specific battery management information
- * @fg_psy:            Structure that holds the FG specific battery properties
- * @fg_wq:             Work queue for running the FG algorithm
- * @fg_periodic_work:  Work to run the FG algorithm periodically
- * @fg_low_bat_work:   Work to check low bat condition
- * @fg_reinit_work     Work used to reset and reinitialise the FG algorithm
- * @fg_work:           Work to run the FG algorithm instantly
- * @fg_acc_cur_work:   Work to read the FG accumulator
- * @fg_check_hw_failure_work:  Work for checking HW state
- * @cc_lock:           Mutex for locking the CC
- * @fg_kobject:                Structure of type kobject
- */
-struct ab8500_fg {
-       struct device *dev;
-       struct list_head node;
-       int irq;
-       int vbat;
-       int vbat_nom;
-       int inst_curr;
-       int avg_curr;
-       int bat_temp;
-       int fg_samples;
-       int accu_charge;
-       int recovery_cnt;
-       int high_curr_cnt;
-       int init_cnt;
-       int low_bat_cnt;
-       int nbr_cceoc_irq_cnt;
-       bool recovery_needed;
-       bool high_curr_mode;
-       bool init_capacity;
-       bool turn_off_fg;
-       enum ab8500_fg_calibration_state calib_state;
-       enum ab8500_fg_discharge_state discharge_state;
-       enum ab8500_fg_charge_state charge_state;
-       struct completion ab8500_fg_started;
-       struct completion ab8500_fg_complete;
-       struct ab8500_fg_flags flags;
-       struct ab8500_fg_battery_capacity bat_cap;
-       struct ab8500_fg_avg_cap avg_cap;
-       struct ab8500 *parent;
-       struct ab8500_gpadc *gpadc;
-       struct abx500_bm_data *bm;
-       struct power_supply *fg_psy;
-       struct workqueue_struct *fg_wq;
-       struct delayed_work fg_periodic_work;
-       struct delayed_work fg_low_bat_work;
-       struct delayed_work fg_reinit_work;
-       struct work_struct fg_work;
-       struct work_struct fg_acc_cur_work;
-       struct delayed_work fg_check_hw_failure_work;
-       struct mutex cc_lock;
-       struct kobject fg_kobject;
-};
-static LIST_HEAD(ab8500_fg_list);
-
-/**
- * ab8500_fg_get() - returns a reference to the primary AB8500 fuel gauge
- * (i.e. the first fuel gauge in the instance list)
- */
-struct ab8500_fg *ab8500_fg_get(void)
-{
-       struct ab8500_fg *fg;
-
-       if (list_empty(&ab8500_fg_list))
-               return NULL;
-
-       fg = list_first_entry(&ab8500_fg_list, struct ab8500_fg, node);
-       return fg;
-}
-
-/* Main battery properties */
-static enum power_supply_property ab8500_fg_props[] = {
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
-       POWER_SUPPLY_PROP_ENERGY_FULL,
-       POWER_SUPPLY_PROP_ENERGY_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-};
-
-/*
- * This array maps the raw hex value to lowbat voltage used by the AB8500
- * Values taken from the UM0836
- */
-static int ab8500_fg_lowbat_voltage_map[] = {
-       2300 ,
-       2325 ,
-       2350 ,
-       2375 ,
-       2400 ,
-       2425 ,
-       2450 ,
-       2475 ,
-       2500 ,
-       2525 ,
-       2550 ,
-       2575 ,
-       2600 ,
-       2625 ,
-       2650 ,
-       2675 ,
-       2700 ,
-       2725 ,
-       2750 ,
-       2775 ,
-       2800 ,
-       2825 ,
-       2850 ,
-       2875 ,
-       2900 ,
-       2925 ,
-       2950 ,
-       2975 ,
-       3000 ,
-       3025 ,
-       3050 ,
-       3075 ,
-       3100 ,
-       3125 ,
-       3150 ,
-       3175 ,
-       3200 ,
-       3225 ,
-       3250 ,
-       3275 ,
-       3300 ,
-       3325 ,
-       3350 ,
-       3375 ,
-       3400 ,
-       3425 ,
-       3450 ,
-       3475 ,
-       3500 ,
-       3525 ,
-       3550 ,
-       3575 ,
-       3600 ,
-       3625 ,
-       3650 ,
-       3675 ,
-       3700 ,
-       3725 ,
-       3750 ,
-       3775 ,
-       3800 ,
-       3825 ,
-       3850 ,
-       3850 ,
-};
-
-static u8 ab8500_volt_to_regval(int voltage)
-{
-       int i;
-
-       if (voltage < ab8500_fg_lowbat_voltage_map[0])
-               return 0;
-
-       for (i = 0; i < ARRAY_SIZE(ab8500_fg_lowbat_voltage_map); i++) {
-               if (voltage < ab8500_fg_lowbat_voltage_map[i])
-                       return (u8) i - 1;
-       }
-
-       /* If not captured above, return index of last element */
-       return (u8) ARRAY_SIZE(ab8500_fg_lowbat_voltage_map) - 1;
-}
-
-/**
- * ab8500_fg_is_low_curr() - Low or high current mode
- * @di:                pointer to the ab8500_fg structure
- * @curr:      the current to base or our decision on
- *
- * Low current mode if the current consumption is below a certain threshold
- */
-static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
-{
-       /*
-        * We want to know if we're in low current mode
-        */
-       if (curr > -di->bm->fg_params->high_curr_threshold)
-               return true;
-       else
-               return false;
-}
-
-/**
- * ab8500_fg_add_cap_sample() - Add capacity to average filter
- * @di:                pointer to the ab8500_fg structure
- * @sample:    the capacity in mAh to add to the filter
- *
- * A capacity is added to the filter and a new mean capacity is calculated and
- * returned
- */
-static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
-{
-       struct timespec64 ts64;
-       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
-
-       getnstimeofday64(&ts64);
-
-       do {
-               avg->sum += sample - avg->samples[avg->pos];
-               avg->samples[avg->pos] = sample;
-               avg->time_stamps[avg->pos] = ts64.tv_sec;
-               avg->pos++;
-
-               if (avg->pos == NBR_AVG_SAMPLES)
-                       avg->pos = 0;
-
-               if (avg->nbr_samples < NBR_AVG_SAMPLES)
-                       avg->nbr_samples++;
-
-               /*
-                * Check the time stamp for each sample. If too old,
-                * replace with latest sample
-                */
-       } while (ts64.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
-
-       avg->avg = avg->sum / avg->nbr_samples;
-
-       return avg->avg;
-}
-
-/**
- * ab8500_fg_clear_cap_samples() - Clear average filter
- * @di:                pointer to the ab8500_fg structure
- *
- * The capacity filter is is reset to zero.
- */
-static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di)
-{
-       int i;
-       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
-
-       avg->pos = 0;
-       avg->nbr_samples = 0;
-       avg->sum = 0;
-       avg->avg = 0;
-
-       for (i = 0; i < NBR_AVG_SAMPLES; i++) {
-               avg->samples[i] = 0;
-               avg->time_stamps[i] = 0;
-       }
-}
-
-/**
- * ab8500_fg_fill_cap_sample() - Fill average filter
- * @di:                pointer to the ab8500_fg structure
- * @sample:    the capacity in mAh to fill the filter with
- *
- * The capacity filter is filled with a capacity in mAh
- */
-static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
-{
-       int i;
-       struct timespec64 ts64;
-       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
-
-       getnstimeofday64(&ts64);
-
-       for (i = 0; i < NBR_AVG_SAMPLES; i++) {
-               avg->samples[i] = sample;
-               avg->time_stamps[i] = ts64.tv_sec;
-       }
-
-       avg->pos = 0;
-       avg->nbr_samples = NBR_AVG_SAMPLES;
-       avg->sum = sample * NBR_AVG_SAMPLES;
-       avg->avg = sample;
-}
-
-/**
- * ab8500_fg_coulomb_counter() - enable coulomb counter
- * @di:                pointer to the ab8500_fg structure
- * @enable:    enable/disable
- *
- * Enable/Disable coulomb counter.
- * On failure returns negative value.
- */
-static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
-{
-       int ret = 0;
-       mutex_lock(&di->cc_lock);
-       if (enable) {
-               /* To be able to reprogram the number of samples, we have to
-                * first stop the CC and then enable it again */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8500_RTC_CC_CONF_REG, 0x00);
-               if (ret)
-                       goto cc_err;
-
-               /* Program the samples */
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
-                       di->fg_samples);
-               if (ret)
-                       goto cc_err;
-
-               /* Start the CC */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8500_RTC_CC_CONF_REG,
-                       (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
-               if (ret)
-                       goto cc_err;
-
-               di->flags.fg_enabled = true;
-       } else {
-               /* Clear any pending read requests */
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
-                       (RESET_ACCU | READ_REQ), 0);
-               if (ret)
-                       goto cc_err;
-
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL, 0);
-               if (ret)
-                       goto cc_err;
-
-               /* Stop the CC */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8500_RTC_CC_CONF_REG, 0);
-               if (ret)
-                       goto cc_err;
-
-               di->flags.fg_enabled = false;
-
-       }
-       dev_dbg(di->dev, " CC enabled: %d Samples: %d\n",
-               enable, di->fg_samples);
-
-       mutex_unlock(&di->cc_lock);
-
-       return ret;
-cc_err:
-       dev_err(di->dev, "%s Enabling coulomb counter failed\n", __func__);
-       mutex_unlock(&di->cc_lock);
-       return ret;
-}
-
-/**
- * ab8500_fg_inst_curr_start() - start battery instantaneous current
- * @di:         pointer to the ab8500_fg structure
- *
- * Returns 0 or error code
- * Note: This is part "one" and has to be called before
- * ab8500_fg_inst_curr_finalize()
- */
-int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
-{
-       u8 reg_val;
-       int ret;
-
-       mutex_lock(&di->cc_lock);
-
-       di->nbr_cceoc_irq_cnt = 0;
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-               AB8500_RTC_CC_CONF_REG, &reg_val);
-       if (ret < 0)
-               goto fail;
-
-       if (!(reg_val & CC_PWR_UP_ENA)) {
-               dev_dbg(di->dev, "%s Enable FG\n", __func__);
-               di->turn_off_fg = true;
-
-               /* Program the samples */
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
-                       SEC_TO_SAMPLE(10));
-               if (ret)
-                       goto fail;
-
-               /* Start the CC */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8500_RTC_CC_CONF_REG,
-                       (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
-               if (ret)
-                       goto fail;
-       } else {
-               di->turn_off_fg = false;
-       }
-
-       /* Return and WFI */
-       reinit_completion(&di->ab8500_fg_started);
-       reinit_completion(&di->ab8500_fg_complete);
-       enable_irq(di->irq);
-
-       /* Note: cc_lock is still locked */
-       return 0;
-fail:
-       mutex_unlock(&di->cc_lock);
-       return ret;
-}
-
-/**
- * ab8500_fg_inst_curr_started() - check if fg conversion has started
- * @di:         pointer to the ab8500_fg structure
- *
- * Returns 1 if conversion started, 0 if still waiting
- */
-int ab8500_fg_inst_curr_started(struct ab8500_fg *di)
-{
-       return completion_done(&di->ab8500_fg_started);
-}
-
-/**
- * ab8500_fg_inst_curr_done() - check if fg conversion is done
- * @di:         pointer to the ab8500_fg structure
- *
- * Returns 1 if conversion done, 0 if still waiting
- */
-int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
-{
-       return completion_done(&di->ab8500_fg_complete);
-}
-
-/**
- * ab8500_fg_inst_curr_finalize() - battery instantaneous current
- * @di:         pointer to the ab8500_fg structure
- * @res:       battery instantenous current(on success)
- *
- * Returns 0 or an error code
- * Note: This is part "two" and has to be called at earliest 250 ms
- * after ab8500_fg_inst_curr_start()
- */
-int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
-{
-       u8 low, high;
-       int val;
-       int ret;
-       unsigned long timeout;
-
-       if (!completion_done(&di->ab8500_fg_complete)) {
-               timeout = wait_for_completion_timeout(
-                       &di->ab8500_fg_complete,
-                       INS_CURR_TIMEOUT);
-               dev_dbg(di->dev, "Finalize time: %d ms\n",
-                       jiffies_to_msecs(INS_CURR_TIMEOUT - timeout));
-               if (!timeout) {
-                       ret = -ETIME;
-                       disable_irq(di->irq);
-                       di->nbr_cceoc_irq_cnt = 0;
-                       dev_err(di->dev, "completion timed out [%d]\n",
-                               __LINE__);
-                       goto fail;
-               }
-       }
-
-       disable_irq(di->irq);
-       di->nbr_cceoc_irq_cnt = 0;
-
-       ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
-                       READ_REQ, READ_REQ);
-
-       /* 100uS between read request and read is needed */
-       usleep_range(100, 100);
-
-       /* Read CC Sample conversion value Low and high */
-       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
-               AB8500_GASG_CC_SMPL_CNVL_REG,  &low);
-       if (ret < 0)
-               goto fail;
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
-               AB8500_GASG_CC_SMPL_CNVH_REG,  &high);
-       if (ret < 0)
-               goto fail;
-
-       /*
-        * negative value for Discharging
-        * convert 2's compliment into decimal
-        */
-       if (high & 0x10)
-               val = (low | (high << 8) | 0xFFFFE000);
-       else
-               val = (low | (high << 8));
-
-       /*
-        * Convert to unit value in mA
-        * Full scale input voltage is
-        * 63.160mV => LSB = 63.160mV/(4096*res) = 1.542mA
-        * Given a 250ms conversion cycle time the LSB corresponds
-        * to 107.1 nAh. Convert to current by dividing by the conversion
-        * time in hours (250ms = 1 / (3600 * 4)h)
-        * 107.1nAh assumes 10mOhm, but fg_res is in 0.1mOhm
-        */
-       val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) /
-               (1000 * di->bm->fg_res);
-
-       if (di->turn_off_fg) {
-               dev_dbg(di->dev, "%s Disable FG\n", __func__);
-
-               /* Clear any pending read requests */
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
-               if (ret)
-                       goto fail;
-
-               /* Stop the CC */
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8500_RTC_CC_CONF_REG, 0);
-               if (ret)
-                       goto fail;
-       }
-       mutex_unlock(&di->cc_lock);
-       (*res) = val;
-
-       return 0;
-fail:
-       mutex_unlock(&di->cc_lock);
-       return ret;
-}
-
-/**
- * ab8500_fg_inst_curr_blocking() - battery instantaneous current
- * @di:         pointer to the ab8500_fg structure
- * @res:       battery instantenous current(on success)
- *
- * Returns 0 else error code
- */
-int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
-{
-       int ret;
-       unsigned long timeout;
-       int res = 0;
-
-       ret = ab8500_fg_inst_curr_start(di);
-       if (ret) {
-               dev_err(di->dev, "Failed to initialize fg_inst\n");
-               return 0;
-       }
-
-       /* Wait for CC to actually start */
-       if (!completion_done(&di->ab8500_fg_started)) {
-               timeout = wait_for_completion_timeout(
-                       &di->ab8500_fg_started,
-                       INS_CURR_TIMEOUT);
-               dev_dbg(di->dev, "Start time: %d ms\n",
-                       jiffies_to_msecs(INS_CURR_TIMEOUT - timeout));
-               if (!timeout) {
-                       ret = -ETIME;
-                       dev_err(di->dev, "completion timed out [%d]\n",
-                               __LINE__);
-                       goto fail;
-               }
-       }
-
-       ret = ab8500_fg_inst_curr_finalize(di, &res);
-       if (ret) {
-               dev_err(di->dev, "Failed to finalize fg_inst\n");
-               return 0;
-       }
-
-       dev_dbg(di->dev, "%s instant current: %d", __func__, res);
-       return res;
-fail:
-       disable_irq(di->irq);
-       mutex_unlock(&di->cc_lock);
-       return ret;
-}
-
-/**
- * ab8500_fg_acc_cur_work() - average battery current
- * @work:      pointer to the work_struct structure
- *
- * Updated the average battery current obtained from the
- * coulomb counter.
- */
-static void ab8500_fg_acc_cur_work(struct work_struct *work)
-{
-       int val;
-       int ret;
-       u8 low, med, high;
-
-       struct ab8500_fg *di = container_of(work,
-               struct ab8500_fg, fg_acc_cur_work);
-
-       mutex_lock(&di->cc_lock);
-       ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
-               AB8500_GASG_CC_NCOV_ACCU_CTRL, RD_NCONV_ACCU_REQ);
-       if (ret)
-               goto exit;
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
-               AB8500_GASG_CC_NCOV_ACCU_LOW,  &low);
-       if (ret < 0)
-               goto exit;
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
-               AB8500_GASG_CC_NCOV_ACCU_MED,  &med);
-       if (ret < 0)
-               goto exit;
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
-               AB8500_GASG_CC_NCOV_ACCU_HIGH, &high);
-       if (ret < 0)
-               goto exit;
-
-       /* Check for sign bit in case of negative value, 2's compliment */
-       if (high & 0x10)
-               val = (low | (med << 8) | (high << 16) | 0xFFE00000);
-       else
-               val = (low | (med << 8) | (high << 16));
-
-       /*
-        * Convert to uAh
-        * Given a 250ms conversion cycle time the LSB corresponds
-        * to 112.9 nAh.
-        * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
-        */
-       di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10) /
-               (100 * di->bm->fg_res);
-
-       /*
-        * Convert to unit value in mA
-        * by dividing by the conversion
-        * time in hours (= samples / (3600 * 4)h)
-        * and multiply with 1000
-        */
-       di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
-               (1000 * di->bm->fg_res * (di->fg_samples / 4));
-
-       di->flags.conv_done = true;
-
-       mutex_unlock(&di->cc_lock);
-
-       queue_work(di->fg_wq, &di->fg_work);
-
-       dev_dbg(di->dev, "fg_res: %d, fg_samples: %d, gasg: %d, accu_charge: %d \n",
-                               di->bm->fg_res, di->fg_samples, val, di->accu_charge);
-       return;
-exit:
-       dev_err(di->dev,
-               "Failed to read or write gas gauge registers\n");
-       mutex_unlock(&di->cc_lock);
-       queue_work(di->fg_wq, &di->fg_work);
-}
-
-/**
- * ab8500_fg_bat_voltage() - get battery voltage
- * @di:                pointer to the ab8500_fg structure
- *
- * Returns battery voltage(on success) else error code
- */
-static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
-{
-       int vbat;
-       static int prev;
-
-       vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V);
-       if (vbat < 0) {
-               dev_err(di->dev,
-                       "%s gpadc conversion failed, using previous value\n",
-                       __func__);
-               return prev;
-       }
-
-       prev = vbat;
-       return vbat;
-}
-
-/**
- * ab8500_fg_volt_to_capacity() - Voltage based capacity
- * @di:                pointer to the ab8500_fg structure
- * @voltage:   The voltage to convert to a capacity
- *
- * Returns battery capacity in per mille based on voltage
- */
-static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
-{
-       int i, tbl_size;
-       const struct abx500_v_to_cap *tbl;
-       int cap = 0;
-
-       tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl,
-       tbl_size = di->bm->bat_type[di->bm->batt_id].n_v_cap_tbl_elements;
-
-       for (i = 0; i < tbl_size; ++i) {
-               if (voltage > tbl[i].voltage)
-                       break;
-       }
-
-       if ((i > 0) && (i < tbl_size)) {
-               cap = interpolate(voltage,
-                       tbl[i].voltage,
-                       tbl[i].capacity * 10,
-                       tbl[i-1].voltage,
-                       tbl[i-1].capacity * 10);
-       } else if (i == 0) {
-               cap = 1000;
-       } else {
-               cap = 0;
-       }
-
-       dev_dbg(di->dev, "%s Vbat: %d, Cap: %d per mille",
-               __func__, voltage, cap);
-
-       return cap;
-}
-
-/**
- * ab8500_fg_uncomp_volt_to_capacity() - Uncompensated voltage based capacity
- * @di:                pointer to the ab8500_fg structure
- *
- * Returns battery capacity based on battery voltage that is not compensated
- * for the voltage drop due to the load
- */
-static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
-{
-       di->vbat = ab8500_fg_bat_voltage(di);
-       return ab8500_fg_volt_to_capacity(di, di->vbat);
-}
-
-/**
- * ab8500_fg_battery_resistance() - Returns the battery inner resistance
- * @di:                pointer to the ab8500_fg structure
- *
- * Returns battery inner resistance added with the fuel gauge resistor value
- * to get the total resistance in the whole link from gnd to bat+ node.
- */
-static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
-{
-       int i, tbl_size;
-       const struct batres_vs_temp *tbl;
-       int resist = 0;
-
-       tbl = di->bm->bat_type[di->bm->batt_id].batres_tbl;
-       tbl_size = di->bm->bat_type[di->bm->batt_id].n_batres_tbl_elements;
-
-       for (i = 0; i < tbl_size; ++i) {
-               if (di->bat_temp / 10 > tbl[i].temp)
-                       break;
-       }
-
-       if ((i > 0) && (i < tbl_size)) {
-               resist = interpolate(di->bat_temp / 10,
-                       tbl[i].temp,
-                       tbl[i].resist,
-                       tbl[i-1].temp,
-                       tbl[i-1].resist);
-       } else if (i == 0) {
-               resist = tbl[0].resist;
-       } else {
-               resist = tbl[tbl_size - 1].resist;
-       }
-
-       dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d"
-           " fg resistance %d, total: %d (mOhm)\n",
-               __func__, di->bat_temp, resist, di->bm->fg_res / 10,
-               (di->bm->fg_res / 10) + resist);
-
-       /* fg_res variable is in 0.1mOhm */
-       resist += di->bm->fg_res / 10;
-
-       return resist;
-}
-
-/**
- * ab8500_fg_load_comp_volt_to_capacity() - Load compensated voltage based capacity
- * @di:                pointer to the ab8500_fg structure
- *
- * Returns battery capacity based on battery voltage that is load compensated
- * for the voltage drop
- */
-static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
-{
-       int vbat_comp, res;
-       int i = 0;
-       int vbat = 0;
-
-       ab8500_fg_inst_curr_start(di);
-
-       do {
-               vbat += ab8500_fg_bat_voltage(di);
-               i++;
-               usleep_range(5000, 6000);
-       } while (!ab8500_fg_inst_curr_done(di));
-
-       ab8500_fg_inst_curr_finalize(di, &di->inst_curr);
-
-       di->vbat = vbat / i;
-       res = ab8500_fg_battery_resistance(di);
-
-       /* Use Ohms law to get the load compensated voltage */
-       vbat_comp = di->vbat - (di->inst_curr * res) / 1000;
-
-       dev_dbg(di->dev, "%s Measured Vbat: %dmV,Compensated Vbat %dmV, "
-               "R: %dmOhm, Current: %dmA Vbat Samples: %d\n",
-               __func__, di->vbat, vbat_comp, res, di->inst_curr, i);
-
-       return ab8500_fg_volt_to_capacity(di, vbat_comp);
-}
-
-/**
- * ab8500_fg_convert_mah_to_permille() - Capacity in mAh to permille
- * @di:                pointer to the ab8500_fg structure
- * @cap_mah:   capacity in mAh
- *
- * Converts capacity in mAh to capacity in permille
- */
-static int ab8500_fg_convert_mah_to_permille(struct ab8500_fg *di, int cap_mah)
-{
-       return (cap_mah * 1000) / di->bat_cap.max_mah_design;
-}
-
-/**
- * ab8500_fg_convert_permille_to_mah() - Capacity in permille to mAh
- * @di:                pointer to the ab8500_fg structure
- * @cap_pm:    capacity in permille
- *
- * Converts capacity in permille to capacity in mAh
- */
-static int ab8500_fg_convert_permille_to_mah(struct ab8500_fg *di, int cap_pm)
-{
-       return cap_pm * di->bat_cap.max_mah_design / 1000;
-}
-
-/**
- * ab8500_fg_convert_mah_to_uwh() - Capacity in mAh to uWh
- * @di:                pointer to the ab8500_fg structure
- * @cap_mah:   capacity in mAh
- *
- * Converts capacity in mAh to capacity in uWh
- */
-static int ab8500_fg_convert_mah_to_uwh(struct ab8500_fg *di, int cap_mah)
-{
-       u64 div_res;
-       u32 div_rem;
-
-       div_res = ((u64) cap_mah) * ((u64) di->vbat_nom);
-       div_rem = do_div(div_res, 1000);
-
-       /* Make sure to round upwards if necessary */
-       if (div_rem >= 1000 / 2)
-               div_res++;
-
-       return (int) div_res;
-}
-
-/**
- * ab8500_fg_calc_cap_charging() - Calculate remaining capacity while charging
- * @di:                pointer to the ab8500_fg structure
- *
- * Return the capacity in mAh based on previous calculated capcity and the FG
- * accumulator register value. The filter is filled with this capacity
- */
-static int ab8500_fg_calc_cap_charging(struct ab8500_fg *di)
-{
-       dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
-               __func__,
-               di->bat_cap.mah,
-               di->accu_charge);
-
-       /* Capacity should not be less than 0 */
-       if (di->bat_cap.mah + di->accu_charge > 0)
-               di->bat_cap.mah += di->accu_charge;
-       else
-               di->bat_cap.mah = 0;
-       /*
-        * We force capacity to 100% once when the algorithm
-        * reports that it's full.
-        */
-       if (di->bat_cap.mah >= di->bat_cap.max_mah_design ||
-               di->flags.force_full) {
-               di->bat_cap.mah = di->bat_cap.max_mah_design;
-       }
-
-       ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
-       di->bat_cap.permille =
-               ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
-
-       /* We need to update battery voltage and inst current when charging */
-       di->vbat = ab8500_fg_bat_voltage(di);
-       di->inst_curr = ab8500_fg_inst_curr_blocking(di);
-
-       return di->bat_cap.mah;
-}
-
-/**
- * ab8500_fg_calc_cap_discharge_voltage() - Capacity in discharge with voltage
- * @di:                pointer to the ab8500_fg structure
- * @comp:      if voltage should be load compensated before capacity calc
- *
- * Return the capacity in mAh based on the battery voltage. The voltage can
- * either be load compensated or not. This value is added to the filter and a
- * new mean value is calculated and returned.
- */
-static int ab8500_fg_calc_cap_discharge_voltage(struct ab8500_fg *di, bool comp)
-{
-       int permille, mah;
-
-       if (comp)
-               permille = ab8500_fg_load_comp_volt_to_capacity(di);
-       else
-               permille = ab8500_fg_uncomp_volt_to_capacity(di);
-
-       mah = ab8500_fg_convert_permille_to_mah(di, permille);
-
-       di->bat_cap.mah = ab8500_fg_add_cap_sample(di, mah);
-       di->bat_cap.permille =
-               ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
-
-       return di->bat_cap.mah;
-}
-
-/**
- * ab8500_fg_calc_cap_discharge_fg() - Capacity in discharge with FG
- * @di:                pointer to the ab8500_fg structure
- *
- * Return the capacity in mAh based on previous calculated capcity and the FG
- * accumulator register value. This value is added to the filter and a
- * new mean value is calculated and returned.
- */
-static int ab8500_fg_calc_cap_discharge_fg(struct ab8500_fg *di)
-{
-       int permille_volt, permille;
-
-       dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
-               __func__,
-               di->bat_cap.mah,
-               di->accu_charge);
-
-       /* Capacity should not be less than 0 */
-       if (di->bat_cap.mah + di->accu_charge > 0)
-               di->bat_cap.mah += di->accu_charge;
-       else
-               di->bat_cap.mah = 0;
-
-       if (di->bat_cap.mah >= di->bat_cap.max_mah_design)
-               di->bat_cap.mah = di->bat_cap.max_mah_design;
-
-       /*
-        * Check against voltage based capacity. It can not be lower
-        * than what the uncompensated voltage says
-        */
-       permille = ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
-       permille_volt = ab8500_fg_uncomp_volt_to_capacity(di);
-
-       if (permille < permille_volt) {
-               di->bat_cap.permille = permille_volt;
-               di->bat_cap.mah = ab8500_fg_convert_permille_to_mah(di,
-                       di->bat_cap.permille);
-
-               dev_dbg(di->dev, "%s voltage based: perm %d perm_volt %d\n",
-                       __func__,
-                       permille,
-                       permille_volt);
-
-               ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
-       } else {
-               ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
-               di->bat_cap.permille =
-                       ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
-       }
-
-       return di->bat_cap.mah;
-}
-
-/**
- * ab8500_fg_capacity_level() - Get the battery capacity level
- * @di:                pointer to the ab8500_fg structure
- *
- * Get the battery capacity level based on the capacity in percent
- */
-static int ab8500_fg_capacity_level(struct ab8500_fg *di)
-{
-       int ret, percent;
-
-       percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
-
-       if (percent <= di->bm->cap_levels->critical ||
-               di->flags.low_bat)
-               ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-       else if (percent <= di->bm->cap_levels->low)
-               ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-       else if (percent <= di->bm->cap_levels->normal)
-               ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-       else if (percent <= di->bm->cap_levels->high)
-               ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
-       else
-               ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-
-       return ret;
-}
-
-/**
- * ab8500_fg_calculate_scaled_capacity() - Capacity scaling
- * @di:                pointer to the ab8500_fg structure
- *
- * Calculates the capacity to be shown to upper layers. Scales the capacity
- * to have 100% as a reference from the actual capacity upon removal of charger
- * when charging is in maintenance mode.
- */
-static int ab8500_fg_calculate_scaled_capacity(struct ab8500_fg *di)
-{
-       struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale;
-       int capacity = di->bat_cap.prev_percent;
-
-       if (!cs->enable)
-               return capacity;
-
-       /*
-        * As long as we are in fully charge mode scale the capacity
-        * to show 100%.
-        */
-       if (di->flags.fully_charged) {
-               cs->cap_to_scale[0] = 100;
-               cs->cap_to_scale[1] =
-                       max(capacity, di->bm->fg_params->maint_thres);
-               dev_dbg(di->dev, "Scale cap with %d/%d\n",
-                        cs->cap_to_scale[0], cs->cap_to_scale[1]);
-       }
-
-       /* Calculates the scaled capacity. */
-       if ((cs->cap_to_scale[0] != cs->cap_to_scale[1])
-                                       && (cs->cap_to_scale[1] > 0))
-               capacity = min(100,
-                                DIV_ROUND_CLOSEST(di->bat_cap.prev_percent *
-                                                cs->cap_to_scale[0],
-                                                cs->cap_to_scale[1]));
-
-       if (di->flags.charging) {
-               if (capacity < cs->disable_cap_level) {
-                       cs->disable_cap_level = capacity;
-                       dev_dbg(di->dev, "Cap to stop scale lowered %d%%\n",
-                               cs->disable_cap_level);
-               } else if (!di->flags.fully_charged) {
-                       if (di->bat_cap.prev_percent >=
-                           cs->disable_cap_level) {
-                               dev_dbg(di->dev, "Disabling scaled capacity\n");
-                               cs->enable = false;
-                               capacity = di->bat_cap.prev_percent;
-                       } else {
-                               dev_dbg(di->dev,
-                                       "Waiting in cap to level %d%%\n",
-                                       cs->disable_cap_level);
-                               capacity = cs->disable_cap_level;
-                       }
-               }
-       }
-
-       return capacity;
-}
-
-/**
- * ab8500_fg_update_cap_scalers() - Capacity scaling
- * @di:                pointer to the ab8500_fg structure
- *
- * To be called when state change from charge<->discharge to update
- * the capacity scalers.
- */
-static void ab8500_fg_update_cap_scalers(struct ab8500_fg *di)
-{
-       struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale;
-
-       if (!cs->enable)
-               return;
-       if (di->flags.charging) {
-               di->bat_cap.cap_scale.disable_cap_level =
-                       di->bat_cap.cap_scale.scaled_cap;
-               dev_dbg(di->dev, "Cap to stop scale at charge %d%%\n",
-                               di->bat_cap.cap_scale.disable_cap_level);
-       } else {
-               if (cs->scaled_cap != 100) {
-                       cs->cap_to_scale[0] = cs->scaled_cap;
-                       cs->cap_to_scale[1] = di->bat_cap.prev_percent;
-               } else {
-                       cs->cap_to_scale[0] = 100;
-                       cs->cap_to_scale[1] =
-                               max(di->bat_cap.prev_percent,
-                                   di->bm->fg_params->maint_thres);
-               }
-
-               dev_dbg(di->dev, "Cap to scale at discharge %d/%d\n",
-                               cs->cap_to_scale[0], cs->cap_to_scale[1]);
-       }
-}
-
-/**
- * ab8500_fg_check_capacity_limits() - Check if capacity has changed
- * @di:                pointer to the ab8500_fg structure
- * @init:      capacity is allowed to go up in init mode
- *
- * Check if capacity or capacity limit has changed and notify the system
- * about it using the power_supply framework
- */
-static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
-{
-       bool changed = false;
-       int percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
-
-       di->bat_cap.level = ab8500_fg_capacity_level(di);
-
-       if (di->bat_cap.level != di->bat_cap.prev_level) {
-               /*
-                * We do not allow reported capacity level to go up
-                * unless we're charging or if we're in init
-                */
-               if (!(!di->flags.charging && di->bat_cap.level >
-                       di->bat_cap.prev_level) || init) {
-                       dev_dbg(di->dev, "level changed from %d to %d\n",
-                               di->bat_cap.prev_level,
-                               di->bat_cap.level);
-                       di->bat_cap.prev_level = di->bat_cap.level;
-                       changed = true;
-               } else {
-                       dev_dbg(di->dev, "level not allowed to go up "
-                               "since no charger is connected: %d to %d\n",
-                               di->bat_cap.prev_level,
-                               di->bat_cap.level);
-               }
-       }
-
-       /*
-        * If we have received the LOW_BAT IRQ, set capacity to 0 to initiate
-        * shutdown
-        */
-       if (di->flags.low_bat) {
-               dev_dbg(di->dev, "Battery low, set capacity to 0\n");
-               di->bat_cap.prev_percent = 0;
-               di->bat_cap.permille = 0;
-               percent = 0;
-               di->bat_cap.prev_mah = 0;
-               di->bat_cap.mah = 0;
-               changed = true;
-       } else if (di->flags.fully_charged) {
-               /*
-                * We report 100% if algorithm reported fully charged
-                * and show 100% during maintenance charging (scaling).
-                */
-               if (di->flags.force_full) {
-                       di->bat_cap.prev_percent = percent;
-                       di->bat_cap.prev_mah = di->bat_cap.mah;
-
-                       changed = true;
-
-                       if (!di->bat_cap.cap_scale.enable &&
-                                               di->bm->capacity_scaling) {
-                               di->bat_cap.cap_scale.enable = true;
-                               di->bat_cap.cap_scale.cap_to_scale[0] = 100;
-                               di->bat_cap.cap_scale.cap_to_scale[1] =
-                                               di->bat_cap.prev_percent;
-                               di->bat_cap.cap_scale.disable_cap_level = 100;
-                       }
-               } else if (di->bat_cap.prev_percent != percent) {
-                       dev_dbg(di->dev,
-                               "battery reported full "
-                               "but capacity dropping: %d\n",
-                               percent);
-                       di->bat_cap.prev_percent = percent;
-                       di->bat_cap.prev_mah = di->bat_cap.mah;
-
-                       changed = true;
-               }
-       } else if (di->bat_cap.prev_percent != percent) {
-               if (percent == 0) {
-                       /*
-                        * We will not report 0% unless we've got
-                        * the LOW_BAT IRQ, no matter what the FG
-                        * algorithm says.
-                        */
-                       di->bat_cap.prev_percent = 1;
-                       percent = 1;
-
-                       changed = true;
-               } else if (!(!di->flags.charging &&
-                       percent > di->bat_cap.prev_percent) || init) {
-                       /*
-                        * We do not allow reported capacity to go up
-                        * unless we're charging or if we're in init
-                        */
-                       dev_dbg(di->dev,
-                               "capacity changed from %d to %d (%d)\n",
-                               di->bat_cap.prev_percent,
-                               percent,
-                               di->bat_cap.permille);
-                       di->bat_cap.prev_percent = percent;
-                       di->bat_cap.prev_mah = di->bat_cap.mah;
-
-                       changed = true;
-               } else {
-                       dev_dbg(di->dev, "capacity not allowed to go up since "
-                               "no charger is connected: %d to %d (%d)\n",
-                               di->bat_cap.prev_percent,
-                               percent,
-                               di->bat_cap.permille);
-               }
-       }
-
-       if (changed) {
-               if (di->bm->capacity_scaling) {
-                       di->bat_cap.cap_scale.scaled_cap =
-                               ab8500_fg_calculate_scaled_capacity(di);
-
-                       dev_info(di->dev, "capacity=%d (%d)\n",
-                               di->bat_cap.prev_percent,
-                               di->bat_cap.cap_scale.scaled_cap);
-               }
-               power_supply_changed(di->fg_psy);
-               if (di->flags.fully_charged && di->flags.force_full) {
-                       dev_dbg(di->dev, "Battery full, notifying.\n");
-                       di->flags.force_full = false;
-                       sysfs_notify(&di->fg_kobject, NULL, "charge_full");
-               }
-               sysfs_notify(&di->fg_kobject, NULL, "charge_now");
-       }
-}
-
-static void ab8500_fg_charge_state_to(struct ab8500_fg *di,
-       enum ab8500_fg_charge_state new_state)
-{
-       dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n",
-               di->charge_state,
-               charge_state[di->charge_state],
-               new_state,
-               charge_state[new_state]);
-
-       di->charge_state = new_state;
-}
-
-static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
-       enum ab8500_fg_discharge_state new_state)
-{
-       dev_dbg(di->dev, "Disharge state from %d [%s] to %d [%s]\n",
-               di->discharge_state,
-               discharge_state[di->discharge_state],
-               new_state,
-               discharge_state[new_state]);
-
-       di->discharge_state = new_state;
-}
-
-/**
- * ab8500_fg_algorithm_charging() - FG algorithm for when charging
- * @di:                pointer to the ab8500_fg structure
- *
- * Battery capacity calculation state machine for when we're charging
- */
-static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
-{
-       /*
-        * If we change to discharge mode
-        * we should start with recovery
-        */
-       if (di->discharge_state != AB8500_FG_DISCHARGE_INIT_RECOVERY)
-               ab8500_fg_discharge_state_to(di,
-                       AB8500_FG_DISCHARGE_INIT_RECOVERY);
-
-       switch (di->charge_state) {
-       case AB8500_FG_CHARGE_INIT:
-               di->fg_samples = SEC_TO_SAMPLE(
-                       di->bm->fg_params->accu_charging);
-
-               ab8500_fg_coulomb_counter(di, true);
-               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT);
-
-               break;
-
-       case AB8500_FG_CHARGE_READOUT:
-               /*
-                * Read the FG and calculate the new capacity
-                */
-               mutex_lock(&di->cc_lock);
-               if (!di->flags.conv_done && !di->flags.force_full) {
-                       /* Wasn't the CC IRQ that got us here */
-                       mutex_unlock(&di->cc_lock);
-                       dev_dbg(di->dev, "%s CC conv not done\n",
-                               __func__);
-
-                       break;
-               }
-               di->flags.conv_done = false;
-               mutex_unlock(&di->cc_lock);
-
-               ab8500_fg_calc_cap_charging(di);
-
-               break;
-
-       default:
-               break;
-       }
-
-       /* Check capacity limits */
-       ab8500_fg_check_capacity_limits(di, false);
-}
-
-static void force_capacity(struct ab8500_fg *di)
-{
-       int cap;
-
-       ab8500_fg_clear_cap_samples(di);
-       cap = di->bat_cap.user_mah;
-       if (cap > di->bat_cap.max_mah_design) {
-               dev_dbg(di->dev, "Remaining cap %d can't be bigger than total"
-                       " %d\n", cap, di->bat_cap.max_mah_design);
-               cap = di->bat_cap.max_mah_design;
-       }
-       ab8500_fg_fill_cap_sample(di, di->bat_cap.user_mah);
-       di->bat_cap.permille = ab8500_fg_convert_mah_to_permille(di, cap);
-       di->bat_cap.mah = cap;
-       ab8500_fg_check_capacity_limits(di, true);
-}
-
-static bool check_sysfs_capacity(struct ab8500_fg *di)
-{
-       int cap, lower, upper;
-       int cap_permille;
-
-       cap = di->bat_cap.user_mah;
-
-       cap_permille = ab8500_fg_convert_mah_to_permille(di,
-               di->bat_cap.user_mah);
-
-       lower = di->bat_cap.permille - di->bm->fg_params->user_cap_limit * 10;
-       upper = di->bat_cap.permille + di->bm->fg_params->user_cap_limit * 10;
-
-       if (lower < 0)
-               lower = 0;
-       /* 1000 is permille, -> 100 percent */
-       if (upper > 1000)
-               upper = 1000;
-
-       dev_dbg(di->dev, "Capacity limits:"
-               " (Lower: %d User: %d Upper: %d) [user: %d, was: %d]\n",
-               lower, cap_permille, upper, cap, di->bat_cap.mah);
-
-       /* If within limits, use the saved capacity and exit estimation...*/
-       if (cap_permille > lower && cap_permille < upper) {
-               dev_dbg(di->dev, "OK! Using users cap %d uAh now\n", cap);
-               force_capacity(di);
-               return true;
-       }
-       dev_dbg(di->dev, "Capacity from user out of limits, ignoring");
-       return false;
-}
-
-/**
- * ab8500_fg_algorithm_discharging() - FG algorithm for when discharging
- * @di:                pointer to the ab8500_fg structure
- *
- * Battery capacity calculation state machine for when we're discharging
- */
-static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
-{
-       int sleep_time;
-
-       /* If we change to charge mode we should start with init */
-       if (di->charge_state != AB8500_FG_CHARGE_INIT)
-               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
-
-       switch (di->discharge_state) {
-       case AB8500_FG_DISCHARGE_INIT:
-               /* We use the FG IRQ to work on */
-               di->init_cnt = 0;
-               di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
-               ab8500_fg_coulomb_counter(di, true);
-               ab8500_fg_discharge_state_to(di,
-                       AB8500_FG_DISCHARGE_INITMEASURING);
-
-               /* Intentional fallthrough */
-       case AB8500_FG_DISCHARGE_INITMEASURING:
-               /*
-                * Discard a number of samples during startup.
-                * After that, use compensated voltage for a few
-                * samples to get an initial capacity.
-                * Then go to READOUT
-                */
-               sleep_time = di->bm->fg_params->init_timer;
-
-               /* Discard the first [x] seconds */
-               if (di->init_cnt > di->bm->fg_params->init_discard_time) {
-                       ab8500_fg_calc_cap_discharge_voltage(di, true);
-
-                       ab8500_fg_check_capacity_limits(di, true);
-               }
-
-               di->init_cnt += sleep_time;
-               if (di->init_cnt > di->bm->fg_params->init_total_time)
-                       ab8500_fg_discharge_state_to(di,
-                               AB8500_FG_DISCHARGE_READOUT_INIT);
-
-               break;
-
-       case AB8500_FG_DISCHARGE_INIT_RECOVERY:
-               di->recovery_cnt = 0;
-               di->recovery_needed = true;
-               ab8500_fg_discharge_state_to(di,
-                       AB8500_FG_DISCHARGE_RECOVERY);
-
-               /* Intentional fallthrough */
-
-       case AB8500_FG_DISCHARGE_RECOVERY:
-               sleep_time = di->bm->fg_params->recovery_sleep_timer;
-
-               /*
-                * We should check the power consumption
-                * If low, go to READOUT (after x min) or
-                * RECOVERY_SLEEP if time left.
-                * If high, go to READOUT
-                */
-               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
-
-               if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
-                       if (di->recovery_cnt >
-                               di->bm->fg_params->recovery_total_time) {
-                               di->fg_samples = SEC_TO_SAMPLE(
-                                       di->bm->fg_params->accu_high_curr);
-                               ab8500_fg_coulomb_counter(di, true);
-                               ab8500_fg_discharge_state_to(di,
-                                       AB8500_FG_DISCHARGE_READOUT);
-                               di->recovery_needed = false;
-                       } else {
-                               queue_delayed_work(di->fg_wq,
-                                       &di->fg_periodic_work,
-                                       sleep_time * HZ);
-                       }
-                       di->recovery_cnt += sleep_time;
-               } else {
-                       di->fg_samples = SEC_TO_SAMPLE(
-                               di->bm->fg_params->accu_high_curr);
-                       ab8500_fg_coulomb_counter(di, true);
-                       ab8500_fg_discharge_state_to(di,
-                               AB8500_FG_DISCHARGE_READOUT);
-               }
-               break;
-
-       case AB8500_FG_DISCHARGE_READOUT_INIT:
-               di->fg_samples = SEC_TO_SAMPLE(
-                       di->bm->fg_params->accu_high_curr);
-               ab8500_fg_coulomb_counter(di, true);
-               ab8500_fg_discharge_state_to(di,
-                               AB8500_FG_DISCHARGE_READOUT);
-               break;
-
-       case AB8500_FG_DISCHARGE_READOUT:
-               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
-
-               if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
-                       /* Detect mode change */
-                       if (di->high_curr_mode) {
-                               di->high_curr_mode = false;
-                               di->high_curr_cnt = 0;
-                       }
-
-                       if (di->recovery_needed) {
-                               ab8500_fg_discharge_state_to(di,
-                                       AB8500_FG_DISCHARGE_INIT_RECOVERY);
-
-                               queue_delayed_work(di->fg_wq,
-                                       &di->fg_periodic_work, 0);
-
-                               break;
-                       }
-
-                       ab8500_fg_calc_cap_discharge_voltage(di, true);
-               } else {
-                       mutex_lock(&di->cc_lock);
-                       if (!di->flags.conv_done) {
-                               /* Wasn't the CC IRQ that got us here */
-                               mutex_unlock(&di->cc_lock);
-                               dev_dbg(di->dev, "%s CC conv not done\n",
-                                       __func__);
-
-                               break;
-                       }
-                       di->flags.conv_done = false;
-                       mutex_unlock(&di->cc_lock);
-
-                       /* Detect mode change */
-                       if (!di->high_curr_mode) {
-                               di->high_curr_mode = true;
-                               di->high_curr_cnt = 0;
-                       }
-
-                       di->high_curr_cnt +=
-                               di->bm->fg_params->accu_high_curr;
-                       if (di->high_curr_cnt >
-                               di->bm->fg_params->high_curr_time)
-                               di->recovery_needed = true;
-
-                       ab8500_fg_calc_cap_discharge_fg(di);
-               }
-
-               ab8500_fg_check_capacity_limits(di, false);
-
-               break;
-
-       case AB8500_FG_DISCHARGE_WAKEUP:
-               ab8500_fg_calc_cap_discharge_voltage(di, true);
-
-               di->fg_samples = SEC_TO_SAMPLE(
-                       di->bm->fg_params->accu_high_curr);
-               ab8500_fg_coulomb_counter(di, true);
-               ab8500_fg_discharge_state_to(di,
-                               AB8500_FG_DISCHARGE_READOUT);
-
-               ab8500_fg_check_capacity_limits(di, false);
-
-               break;
-
-       default:
-               break;
-       }
-}
-
-/**
- * ab8500_fg_algorithm_calibrate() - Internal columb counter offset calibration
- * @di:                pointer to the ab8500_fg structure
- *
- */
-static void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di)
-{
-       int ret;
-
-       switch (di->calib_state) {
-       case AB8500_FG_CALIB_INIT:
-               dev_dbg(di->dev, "Calibration ongoing...\n");
-
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
-                       CC_INT_CAL_N_AVG_MASK, CC_INT_CAL_SAMPLES_8);
-               if (ret < 0)
-                       goto err;
-
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
-                       CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA);
-               if (ret < 0)
-                       goto err;
-               di->calib_state = AB8500_FG_CALIB_WAIT;
-               break;
-       case AB8500_FG_CALIB_END:
-               ret = abx500_mask_and_set_register_interruptible(di->dev,
-                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
-                       CC_MUXOFFSET, CC_MUXOFFSET);
-               if (ret < 0)
-                       goto err;
-               di->flags.calibrate = false;
-               dev_dbg(di->dev, "Calibration done...\n");
-               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
-               break;
-       case AB8500_FG_CALIB_WAIT:
-               dev_dbg(di->dev, "Calibration WFI\n");
-       default:
-               break;
-       }
-       return;
-err:
-       /* Something went wrong, don't calibrate then */
-       dev_err(di->dev, "failed to calibrate the CC\n");
-       di->flags.calibrate = false;
-       di->calib_state = AB8500_FG_CALIB_INIT;
-       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
-}
-
-/**
- * ab8500_fg_algorithm() - Entry point for the FG algorithm
- * @di:                pointer to the ab8500_fg structure
- *
- * Entry point for the battery capacity calculation state machine
- */
-static void ab8500_fg_algorithm(struct ab8500_fg *di)
-{
-       if (di->flags.calibrate)
-               ab8500_fg_algorithm_calibrate(di);
-       else {
-               if (di->flags.charging)
-                       ab8500_fg_algorithm_charging(di);
-               else
-                       ab8500_fg_algorithm_discharging(di);
-       }
-
-       dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d %d "
-               "%d %d %d %d %d %d %d\n",
-               di->bat_cap.max_mah_design,
-               di->bat_cap.max_mah,
-               di->bat_cap.mah,
-               di->bat_cap.permille,
-               di->bat_cap.level,
-               di->bat_cap.prev_mah,
-               di->bat_cap.prev_percent,
-               di->bat_cap.prev_level,
-               di->vbat,
-               di->inst_curr,
-               di->avg_curr,
-               di->accu_charge,
-               di->flags.charging,
-               di->charge_state,
-               di->discharge_state,
-               di->high_curr_mode,
-               di->recovery_needed);
-}
-
-/**
- * ab8500_fg_periodic_work() - Run the FG state machine periodically
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for periodic work
- */
-static void ab8500_fg_periodic_work(struct work_struct *work)
-{
-       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
-               fg_periodic_work.work);
-
-       if (di->init_capacity) {
-               /* Get an initial capacity calculation */
-               ab8500_fg_calc_cap_discharge_voltage(di, true);
-               ab8500_fg_check_capacity_limits(di, true);
-               di->init_capacity = false;
-
-               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
-       } else if (di->flags.user_cap) {
-               if (check_sysfs_capacity(di)) {
-                       ab8500_fg_check_capacity_limits(di, true);
-                       if (di->flags.charging)
-                               ab8500_fg_charge_state_to(di,
-                                       AB8500_FG_CHARGE_INIT);
-                       else
-                               ab8500_fg_discharge_state_to(di,
-                                       AB8500_FG_DISCHARGE_READOUT_INIT);
-               }
-               di->flags.user_cap = false;
-               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
-       } else
-               ab8500_fg_algorithm(di);
-
-}
-
-/**
- * ab8500_fg_check_hw_failure_work() - Check OVV_BAT condition
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for checking the OVV_BAT condition
- */
-static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
-{
-       int ret;
-       u8 reg_value;
-
-       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
-               fg_check_hw_failure_work.work);
-
-       /*
-        * If we have had a battery over-voltage situation,
-        * check ovv-bit to see if it should be reset.
-        */
-       ret = abx500_get_register_interruptible(di->dev,
-               AB8500_CHARGER, AB8500_CH_STAT_REG,
-               &reg_value);
-       if (ret < 0) {
-               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
-               return;
-       }
-       if ((reg_value & BATT_OVV) == BATT_OVV) {
-               if (!di->flags.bat_ovv) {
-                       dev_dbg(di->dev, "Battery OVV\n");
-                       di->flags.bat_ovv = true;
-                       power_supply_changed(di->fg_psy);
-               }
-               /* Not yet recovered from ovv, reschedule this test */
-               queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
-                                  HZ);
-               } else {
-                       dev_dbg(di->dev, "Battery recovered from OVV\n");
-                       di->flags.bat_ovv = false;
-                       power_supply_changed(di->fg_psy);
-       }
-}
-
-/**
- * ab8500_fg_low_bat_work() - Check LOW_BAT condition
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for checking the LOW_BAT condition
- */
-static void ab8500_fg_low_bat_work(struct work_struct *work)
-{
-       int vbat;
-
-       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
-               fg_low_bat_work.work);
-
-       vbat = ab8500_fg_bat_voltage(di);
-
-       /* Check if LOW_BAT still fulfilled */
-       if (vbat < di->bm->fg_params->lowbat_threshold) {
-               /* Is it time to shut down? */
-               if (di->low_bat_cnt < 1) {
-                       di->flags.low_bat = true;
-                       dev_warn(di->dev, "Shut down pending...\n");
-               } else {
-                       /*
-                       * Else we need to re-schedule this check to be able to detect
-                       * if the voltage increases again during charging or
-                       * due to decreasing load.
-                       */
-                       di->low_bat_cnt--;
-                       dev_warn(di->dev, "Battery voltage still LOW\n");
-                       queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
-                               round_jiffies(LOW_BAT_CHECK_INTERVAL));
-               }
-       } else {
-               di->flags.low_bat_delay = false;
-               di->low_bat_cnt = 10;
-               dev_warn(di->dev, "Battery voltage OK again\n");
-       }
-
-       /* This is needed to dispatch LOW_BAT */
-       ab8500_fg_check_capacity_limits(di, false);
-}
-
-/**
- * ab8500_fg_battok_calc - calculate the bit pattern corresponding
- * to the target voltage.
- * @di:       pointer to the ab8500_fg structure
- * @target    target voltage
- *
- * Returns bit pattern closest to the target voltage
- * valid return values are 0-14. (0-BATT_OK_MAX_NR_INCREMENTS)
- */
-
-static int ab8500_fg_battok_calc(struct ab8500_fg *di, int target)
-{
-       if (target > BATT_OK_MIN +
-               (BATT_OK_INCREMENT * BATT_OK_MAX_NR_INCREMENTS))
-               return BATT_OK_MAX_NR_INCREMENTS;
-       if (target < BATT_OK_MIN)
-               return 0;
-       return (target - BATT_OK_MIN) / BATT_OK_INCREMENT;
-}
-
-/**
- * ab8500_fg_battok_init_hw_register - init battok levels
- * @di:       pointer to the ab8500_fg structure
- *
- */
-
-static int ab8500_fg_battok_init_hw_register(struct ab8500_fg *di)
-{
-       int selected;
-       int sel0;
-       int sel1;
-       int cbp_sel0;
-       int cbp_sel1;
-       int ret;
-       int new_val;
-
-       sel0 = di->bm->fg_params->battok_falling_th_sel0;
-       sel1 = di->bm->fg_params->battok_raising_th_sel1;
-
-       cbp_sel0 = ab8500_fg_battok_calc(di, sel0);
-       cbp_sel1 = ab8500_fg_battok_calc(di, sel1);
-
-       selected = BATT_OK_MIN + cbp_sel0 * BATT_OK_INCREMENT;
-
-       if (selected != sel0)
-               dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n",
-                       sel0, selected, cbp_sel0);
-
-       selected = BATT_OK_MIN + cbp_sel1 * BATT_OK_INCREMENT;
-
-       if (selected != sel1)
-               dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n",
-                       sel1, selected, cbp_sel1);
-
-       new_val = cbp_sel0 | (cbp_sel1 << 4);
-
-       dev_dbg(di->dev, "using: %x %d %d\n", new_val, cbp_sel0, cbp_sel1);
-       ret = abx500_set_register_interruptible(di->dev, AB8500_SYS_CTRL2_BLOCK,
-               AB8500_BATT_OK_REG, new_val);
-       return ret;
-}
-
-/**
- * ab8500_fg_instant_work() - Run the FG state machine instantly
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for instant work
- */
-static void ab8500_fg_instant_work(struct work_struct *work)
-{
-       struct ab8500_fg *di = container_of(work, struct ab8500_fg, fg_work);
-
-       ab8500_fg_algorithm(di);
-}
-
-/**
- * ab8500_fg_cc_data_end_handler() - end of data conversion isr.
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_fg structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
-{
-       struct ab8500_fg *di = _di;
-       if (!di->nbr_cceoc_irq_cnt) {
-               di->nbr_cceoc_irq_cnt++;
-               complete(&di->ab8500_fg_started);
-       } else {
-               di->nbr_cceoc_irq_cnt = 0;
-               complete(&di->ab8500_fg_complete);
-       }
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_fg_cc_int_calib_handler () - end of calibration isr.
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_fg structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di)
-{
-       struct ab8500_fg *di = _di;
-       di->calib_state = AB8500_FG_CALIB_END;
-       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_fg structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di)
-{
-       struct ab8500_fg *di = _di;
-
-       queue_work(di->fg_wq, &di->fg_acc_cur_work);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_fg_batt_ovv_handler() - Battery OVV occured
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_fg structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
-{
-       struct ab8500_fg *di = _di;
-
-       dev_dbg(di->dev, "Battery OVV\n");
-
-       /* Schedule a new HW failure check */
-       queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_fg_lowbatf_handler() - Battery voltage is below LOW threshold
- * @irq:       interrupt number
- * @_di:       pointer to the ab8500_fg structure
- *
- * Returns IRQ status(IRQ_HANDLED)
- */
-static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di)
-{
-       struct ab8500_fg *di = _di;
-
-       /* Initiate handling in ab8500_fg_low_bat_work() if not already initiated. */
-       if (!di->flags.low_bat_delay) {
-               dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
-               di->flags.low_bat_delay = true;
-               /*
-                * Start a timer to check LOW_BAT again after some time
-                * This is done to avoid shutdown on single voltage dips
-                */
-               queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
-                       round_jiffies(LOW_BAT_CHECK_INTERVAL));
-       }
-       return IRQ_HANDLED;
-}
-
-/**
- * ab8500_fg_get_property() - get the fg properties
- * @psy:       pointer to the power_supply structure
- * @psp:       pointer to the power_supply_property structure
- * @val:       pointer to the power_supply_propval union
- *
- * This function gets called when an application tries to get the
- * fg properties by reading the sysfs files.
- * voltage_now:                battery voltage
- * current_now:                battery instant current
- * current_avg:                battery average current
- * charge_full_design: capacity where battery is considered full
- * charge_now:         battery capacity in nAh
- * capacity:           capacity in percent
- * capacity_level:     capacity level
- *
- * Returns error code in case of failure else 0 on success
- */
-static int ab8500_fg_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       /*
-        * If battery is identified as unknown and charging of unknown
-        * batteries is disabled, we always report 100% capacity and
-        * capacity level UNKNOWN, since we can't calculate
-        * remaining capacity
-        */
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (di->flags.bat_ovv)
-                       val->intval = BATT_OVV_VALUE * 1000;
-               else
-                       val->intval = di->vbat * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               val->intval = di->inst_curr * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               val->intval = di->avg_curr * 1000;
-               break;
-       case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
-               val->intval = ab8500_fg_convert_mah_to_uwh(di,
-                               di->bat_cap.max_mah_design);
-               break;
-       case POWER_SUPPLY_PROP_ENERGY_FULL:
-               val->intval = ab8500_fg_convert_mah_to_uwh(di,
-                               di->bat_cap.max_mah);
-               break;
-       case POWER_SUPPLY_PROP_ENERGY_NOW:
-               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
-                               di->flags.batt_id_received)
-                       val->intval = ab8500_fg_convert_mah_to_uwh(di,
-                                       di->bat_cap.max_mah);
-               else
-                       val->intval = ab8500_fg_convert_mah_to_uwh(di,
-                                       di->bat_cap.prev_mah);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               val->intval = di->bat_cap.max_mah_design;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               val->intval = di->bat_cap.max_mah;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
-                               di->flags.batt_id_received)
-                       val->intval = di->bat_cap.max_mah;
-               else
-                       val->intval = di->bat_cap.prev_mah;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
-                               di->flags.batt_id_received)
-                       val->intval = 100;
-               else
-                       val->intval = di->bat_cap.prev_percent;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
-                               di->flags.batt_id_received)
-                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-               else
-                       val->intval = di->bat_cap.prev_level;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
-{
-       struct power_supply *psy;
-       struct power_supply *ext = dev_get_drvdata(dev);
-       const char **supplicants = (const char **)ext->supplied_to;
-       struct ab8500_fg *di;
-       union power_supply_propval ret;
-       int j;
-
-       psy = (struct power_supply *)data;
-       di = power_supply_get_drvdata(psy);
-
-       /*
-        * For all psy where the name of your driver
-        * appears in any supplied_to
-        */
-       j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
-       if (j < 0)
-               return 0;
-
-       /* Go through all properties for the psy */
-       for (j = 0; j < ext->desc->num_properties; j++) {
-               enum power_supply_property prop;
-               prop = ext->desc->properties[j];
-
-               if (power_supply_get_property(ext, prop, &ret))
-                       continue;
-
-               switch (prop) {
-               case POWER_SUPPLY_PROP_STATUS:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               switch (ret.intval) {
-                               case POWER_SUPPLY_STATUS_UNKNOWN:
-                               case POWER_SUPPLY_STATUS_DISCHARGING:
-                               case POWER_SUPPLY_STATUS_NOT_CHARGING:
-                                       if (!di->flags.charging)
-                                               break;
-                                       di->flags.charging = false;
-                                       di->flags.fully_charged = false;
-                                       if (di->bm->capacity_scaling)
-                                               ab8500_fg_update_cap_scalers(di);
-                                       queue_work(di->fg_wq, &di->fg_work);
-                                       break;
-                               case POWER_SUPPLY_STATUS_FULL:
-                                       if (di->flags.fully_charged)
-                                               break;
-                                       di->flags.fully_charged = true;
-                                       di->flags.force_full = true;
-                                       /* Save current capacity as maximum */
-                                       di->bat_cap.max_mah = di->bat_cap.mah;
-                                       queue_work(di->fg_wq, &di->fg_work);
-                                       break;
-                               case POWER_SUPPLY_STATUS_CHARGING:
-                                       if (di->flags.charging &&
-                                               !di->flags.fully_charged)
-                                               break;
-                                       di->flags.charging = true;
-                                       di->flags.fully_charged = false;
-                                       if (di->bm->capacity_scaling)
-                                               ab8500_fg_update_cap_scalers(di);
-                                       queue_work(di->fg_wq, &di->fg_work);
-                                       break;
-                               };
-                       default:
-                               break;
-                       };
-                       break;
-               case POWER_SUPPLY_PROP_TECHNOLOGY:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               if (!di->flags.batt_id_received &&
-                                   di->bm->batt_id != BATTERY_UNKNOWN) {
-                                       const struct abx500_battery_type *b;
-
-                                       b = &(di->bm->bat_type[di->bm->batt_id]);
-
-                                       di->flags.batt_id_received = true;
-
-                                       di->bat_cap.max_mah_design =
-                                               MILLI_TO_MICRO *
-                                               b->charge_full_design;
-
-                                       di->bat_cap.max_mah =
-                                               di->bat_cap.max_mah_design;
-
-                                       di->vbat_nom = b->nominal_voltage;
-                               }
-
-                               if (ret.intval)
-                                       di->flags.batt_unknown = false;
-                               else
-                                       di->flags.batt_unknown = true;
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-               case POWER_SUPPLY_PROP_TEMP:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               if (di->flags.batt_id_received)
-                                       di->bat_temp = ret.intval;
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-               default:
-                       break;
-               }
-       }
-       return 0;
-}
-
-/**
- * ab8500_fg_init_hw_registers() - Set up FG related registers
- * @di:                pointer to the ab8500_fg structure
- *
- * Set up battery OVV, low battery voltage registers
- */
-static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
-{
-       int ret;
-
-       /* Set VBAT OVV threshold */
-       ret = abx500_mask_and_set_register_interruptible(di->dev,
-               AB8500_CHARGER,
-               AB8500_BATT_OVV,
-               BATT_OVV_TH_4P75,
-               BATT_OVV_TH_4P75);
-       if (ret) {
-               dev_err(di->dev, "failed to set BATT_OVV\n");
-               goto out;
-       }
-
-       /* Enable VBAT OVV detection */
-       ret = abx500_mask_and_set_register_interruptible(di->dev,
-               AB8500_CHARGER,
-               AB8500_BATT_OVV,
-               BATT_OVV_ENA,
-               BATT_OVV_ENA);
-       if (ret) {
-               dev_err(di->dev, "failed to enable BATT_OVV\n");
-               goto out;
-       }
-
-       /* Low Battery Voltage */
-       ret = abx500_set_register_interruptible(di->dev,
-               AB8500_SYS_CTRL2_BLOCK,
-               AB8500_LOW_BAT_REG,
-               ab8500_volt_to_regval(
-                       di->bm->fg_params->lowbat_threshold) << 1 |
-               LOW_BAT_ENABLE);
-       if (ret) {
-               dev_err(di->dev, "%s write failed\n", __func__);
-               goto out;
-       }
-
-       /* Battery OK threshold */
-       ret = ab8500_fg_battok_init_hw_register(di);
-       if (ret) {
-               dev_err(di->dev, "BattOk init write failed.\n");
-               goto out;
-       }
-
-       if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
-                       abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
-                       || is_ab8540(di->parent)) {
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time);
-
-               if (ret) {
-                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__);
-                       goto out;
-               };
-
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time);
-
-               if (ret) {
-                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
-                       goto out;
-               };
-
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart);
-
-               if (ret) {
-                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__);
-                       goto out;
-               };
-
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time);
-
-               if (ret) {
-                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__);
-                       goto out;
-               };
-
-               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                       AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable);
-
-               if (ret) {
-                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__);
-                       goto out;
-               };
-       }
-out:
-       return ret;
-}
-
-/**
- * ab8500_fg_external_power_changed() - callback for power supply changes
- * @psy:       pointer to the structure power_supply
- *
- * This function is the entry point of the pointer external_power_changed
- * of the structure power_supply.
- * This function gets executed when there is a change in any external power
- * supply that this driver needs to be notified of.
- */
-static void ab8500_fg_external_power_changed(struct power_supply *psy)
-{
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       class_for_each_device(power_supply_class, NULL,
-               di->fg_psy, ab8500_fg_get_ext_psy_data);
-}
-
-/**
- * abab8500_fg_reinit_work() - work to reset the FG algorithm
- * @work:      pointer to the work_struct structure
- *
- * Used to reset the current battery capacity to be able to
- * retrigger a new voltage base capacity calculation. For
- * test and verification purpose.
- */
-static void ab8500_fg_reinit_work(struct work_struct *work)
-{
-       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
-               fg_reinit_work.work);
-
-       if (di->flags.calibrate == false) {
-               dev_dbg(di->dev, "Resetting FG state machine to init.\n");
-               ab8500_fg_clear_cap_samples(di);
-               ab8500_fg_calc_cap_discharge_voltage(di, true);
-               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
-               ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
-               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
-
-       } else {
-               dev_err(di->dev, "Residual offset calibration ongoing "
-                       "retrying..\n");
-               /* Wait one second until next try*/
-               queue_delayed_work(di->fg_wq, &di->fg_reinit_work,
-                       round_jiffies(1));
-       }
-}
-
-/* Exposure to the sysfs interface */
-
-struct ab8500_fg_sysfs_entry {
-       struct attribute attr;
-       ssize_t (*show)(struct ab8500_fg *, char *);
-       ssize_t (*store)(struct ab8500_fg *, const char *, size_t);
-};
-
-static ssize_t charge_full_show(struct ab8500_fg *di, char *buf)
-{
-       return sprintf(buf, "%d\n", di->bat_cap.max_mah);
-}
-
-static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf,
-                                size_t count)
-{
-       unsigned long charge_full;
-       ssize_t ret;
-
-       ret = kstrtoul(buf, 10, &charge_full);
-
-       dev_dbg(di->dev, "Ret %zd charge_full %lu", ret, charge_full);
-
-       if (!ret) {
-               di->bat_cap.max_mah = (int) charge_full;
-               ret = count;
-       }
-       return ret;
-}
-
-static ssize_t charge_now_show(struct ab8500_fg *di, char *buf)
-{
-       return sprintf(buf, "%d\n", di->bat_cap.prev_mah);
-}
-
-static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf,
-                                size_t count)
-{
-       unsigned long charge_now;
-       ssize_t ret;
-
-       ret = kstrtoul(buf, 10, &charge_now);
-
-       dev_dbg(di->dev, "Ret %zd charge_now %lu was %d",
-               ret, charge_now, di->bat_cap.prev_mah);
-
-       if (!ret) {
-               di->bat_cap.user_mah = (int) charge_now;
-               di->flags.user_cap = true;
-               ret = count;
-               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
-       }
-       return ret;
-}
-
-static struct ab8500_fg_sysfs_entry charge_full_attr =
-       __ATTR(charge_full, 0644, charge_full_show, charge_full_store);
-
-static struct ab8500_fg_sysfs_entry charge_now_attr =
-       __ATTR(charge_now, 0644, charge_now_show, charge_now_store);
-
-static ssize_t
-ab8500_fg_show(struct kobject *kobj, struct attribute *attr, char *buf)
-{
-       struct ab8500_fg_sysfs_entry *entry;
-       struct ab8500_fg *di;
-
-       entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr);
-       di = container_of(kobj, struct ab8500_fg, fg_kobject);
-
-       if (!entry->show)
-               return -EIO;
-
-       return entry->show(di, buf);
-}
-static ssize_t
-ab8500_fg_store(struct kobject *kobj, struct attribute *attr, const char *buf,
-               size_t count)
-{
-       struct ab8500_fg_sysfs_entry *entry;
-       struct ab8500_fg *di;
-
-       entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr);
-       di = container_of(kobj, struct ab8500_fg, fg_kobject);
-
-       if (!entry->store)
-               return -EIO;
-
-       return entry->store(di, buf, count);
-}
-
-static const struct sysfs_ops ab8500_fg_sysfs_ops = {
-       .show = ab8500_fg_show,
-       .store = ab8500_fg_store,
-};
-
-static struct attribute *ab8500_fg_attrs[] = {
-       &charge_full_attr.attr,
-       &charge_now_attr.attr,
-       NULL,
-};
-
-static struct kobj_type ab8500_fg_ktype = {
-       .sysfs_ops = &ab8500_fg_sysfs_ops,
-       .default_attrs = ab8500_fg_attrs,
-};
-
-/**
- * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry
- * @di:                pointer to the struct ab8500_chargalg
- *
- * This function removes the entry in sysfs.
- */
-static void ab8500_fg_sysfs_exit(struct ab8500_fg *di)
-{
-       kobject_del(&di->fg_kobject);
-}
-
-/**
- * ab8500_chargalg_sysfs_init() - init of sysfs entry
- * @di:                pointer to the struct ab8500_chargalg
- *
- * This function adds an entry in sysfs.
- * Returns error code in case of failure else 0(on success)
- */
-static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
-{
-       int ret = 0;
-
-       ret = kobject_init_and_add(&di->fg_kobject,
-               &ab8500_fg_ktype,
-               NULL, "battery");
-       if (ret < 0)
-               dev_err(di->dev, "failed to create sysfs entry\n");
-
-       return ret;
-}
-
-static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
-                            struct device_attribute *attr,
-                            char *buf)
-{
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-               AB8505_RTC_PCUT_FLAG_TIME_REG, &reg_value);
-
-       if (ret < 0) {
-               dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
-               goto fail;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
-
-fail:
-       return ret;
-}
-
-static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       int ret;
-       long unsigned reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       reg_value = simple_strtoul(buf, NULL, 10);
-
-       if (reg_value > 0x7F) {
-               dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
-               goto fail;
-       }
-
-       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-               AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
-
-       if (ret < 0)
-               dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
-
-fail:
-       return count;
-}
-
-static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
-                            struct device_attribute *attr,
-                            char *buf)
-{
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-               AB8505_RTC_PCUT_MAX_TIME_REG, &reg_value);
-
-       if (ret < 0) {
-               dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n");
-               goto fail;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
-
-fail:
-       return ret;
-
-}
-
-static ssize_t ab8505_powercut_maxtime_write(struct device *dev,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       int ret;
-       int reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       reg_value = simple_strtoul(buf, NULL, 10);
-       if (reg_value > 0x7F) {
-               dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n");
-               goto fail;
-       }
-
-       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-               AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value);
-
-       if (ret < 0)
-               dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n");
-
-fail:
-       return count;
-}
-
-static ssize_t ab8505_powercut_restart_read(struct device *dev,
-                            struct device_attribute *attr,
-                            char *buf)
-{
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-               AB8505_RTC_PCUT_RESTART_REG, &reg_value);
-
-       if (ret < 0) {
-               dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
-               goto fail;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF));
-
-fail:
-       return ret;
-}
-
-static ssize_t ab8505_powercut_restart_write(struct device *dev,
-                                            struct device_attribute *attr,
-                                            const char *buf, size_t count)
-{
-       int ret;
-       int reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       reg_value = simple_strtoul(buf, NULL, 10);
-       if (reg_value > 0xF) {
-               dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n");
-               goto fail;
-       }
-
-       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value);
-
-       if (ret < 0)
-               dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n");
-
-fail:
-       return count;
-
-}
-
-static ssize_t ab8505_powercut_timer_read(struct device *dev,
-                                         struct device_attribute *attr,
-                                         char *buf)
-{
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_TIME_REG, &reg_value);
-
-       if (ret < 0) {
-               dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n");
-               goto fail;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
-
-fail:
-       return ret;
-}
-
-static ssize_t ab8505_powercut_restart_counter_read(struct device *dev,
-                                                   struct device_attribute *attr,
-                                                   char *buf)
-{
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_RESTART_REG, &reg_value);
-
-       if (ret < 0) {
-               dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
-               goto fail;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4);
-
-fail:
-       return ret;
-}
-
-static ssize_t ab8505_powercut_read(struct device *dev,
-                                   struct device_attribute *attr,
-                                   char *buf)
-{
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
-
-       if (ret < 0)
-               goto fail;
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1));
-
-fail:
-       return ret;
-}
-
-static ssize_t ab8505_powercut_write(struct device *dev,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       int ret;
-       int reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       reg_value = simple_strtoul(buf, NULL, 10);
-       if (reg_value > 0x1) {
-               dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n");
-               goto fail;
-       }
-
-       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value);
-
-       if (ret < 0)
-               dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n");
-
-fail:
-       return count;
-}
-
-static ssize_t ab8505_powercut_flag_read(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_CTL_STATUS_REG,  &reg_value);
-
-       if (ret < 0) {
-               dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
-               goto fail;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4));
-
-fail:
-       return ret;
-}
-
-static ssize_t ab8505_powercut_debounce_read(struct device *dev,
-                                            struct device_attribute *attr,
-                                            char *buf)
-{
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_DEBOUNCE_REG,  &reg_value);
-
-       if (ret < 0) {
-               dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n");
-               goto fail;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7));
-
-fail:
-       return ret;
-}
-
-static ssize_t ab8505_powercut_debounce_write(struct device *dev,
-                                             struct device_attribute *attr,
-                                             const char *buf, size_t count)
-{
-       int ret;
-       int reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       reg_value = simple_strtoul(buf, NULL, 10);
-       if (reg_value > 0x7) {
-               dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n");
-               goto fail;
-       }
-
-       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value);
-
-       if (ret < 0)
-               dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n");
-
-fail:
-       return count;
-}
-
-static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
-                                                 struct device_attribute *attr,
-                                                 char *buf)
-{
-       int ret;
-       u8 reg_value;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct ab8500_fg *di = power_supply_get_drvdata(psy);
-
-       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
-                                               AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
-
-       if (ret < 0) {
-               dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
-               goto fail;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5));
-
-fail:
-       return ret;
-}
-
-static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
-       __ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP),
-               ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write),
-       __ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP),
-               ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write),
-       __ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP),
-               ab8505_powercut_restart_read, ab8505_powercut_restart_write),
-       __ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL),
-       __ATTR(powercut_restart_counter, S_IRUGO,
-               ab8505_powercut_restart_counter_read, NULL),
-       __ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
-               ab8505_powercut_read, ab8505_powercut_write),
-       __ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
-       __ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP),
-               ab8505_powercut_debounce_read, ab8505_powercut_debounce_write),
-       __ATTR(powercut_enable_status, S_IRUGO,
-               ab8505_powercut_enable_status_read, NULL),
-};
-
-static int ab8500_fg_sysfs_psy_create_attrs(struct ab8500_fg *di)
-{
-       unsigned int i;
-
-       if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
-            abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
-           || is_ab8540(di->parent)) {
-               for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
-                       if (device_create_file(&di->fg_psy->dev,
-                                              &ab8505_fg_sysfs_psy_attrs[i]))
-                               goto sysfs_psy_create_attrs_failed_ab8505;
-       }
-       return 0;
-sysfs_psy_create_attrs_failed_ab8505:
-       dev_err(&di->fg_psy->dev, "Failed creating sysfs psy attrs for ab8505.\n");
-       while (i--)
-               device_remove_file(&di->fg_psy->dev,
-                                  &ab8505_fg_sysfs_psy_attrs[i]);
-
-       return -EIO;
-}
-
-static void ab8500_fg_sysfs_psy_remove_attrs(struct ab8500_fg *di)
-{
-       unsigned int i;
-
-       if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
-            abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
-           || is_ab8540(di->parent)) {
-               for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
-                       (void)device_remove_file(&di->fg_psy->dev,
-                                                &ab8505_fg_sysfs_psy_attrs[i]);
-       }
-}
-
-/* Exposure to the sysfs interface <<END>> */
-
-#if defined(CONFIG_PM)
-static int ab8500_fg_resume(struct platform_device *pdev)
-{
-       struct ab8500_fg *di = platform_get_drvdata(pdev);
-
-       /*
-        * Change state if we're not charging. If we're charging we will wake
-        * up on the FG IRQ
-        */
-       if (!di->flags.charging) {
-               ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_WAKEUP);
-               queue_work(di->fg_wq, &di->fg_work);
-       }
-
-       return 0;
-}
-
-static int ab8500_fg_suspend(struct platform_device *pdev,
-       pm_message_t state)
-{
-       struct ab8500_fg *di = platform_get_drvdata(pdev);
-
-       flush_delayed_work(&di->fg_periodic_work);
-       flush_work(&di->fg_work);
-       flush_work(&di->fg_acc_cur_work);
-       flush_delayed_work(&di->fg_reinit_work);
-       flush_delayed_work(&di->fg_low_bat_work);
-       flush_delayed_work(&di->fg_check_hw_failure_work);
-
-       /*
-        * If the FG is enabled we will disable it before going to suspend
-        * only if we're not charging
-        */
-       if (di->flags.fg_enabled && !di->flags.charging)
-               ab8500_fg_coulomb_counter(di, false);
-
-       return 0;
-}
-#else
-#define ab8500_fg_suspend      NULL
-#define ab8500_fg_resume       NULL
-#endif
-
-static int ab8500_fg_remove(struct platform_device *pdev)
-{
-       int ret = 0;
-       struct ab8500_fg *di = platform_get_drvdata(pdev);
-
-       list_del(&di->node);
-
-       /* Disable coulomb counter */
-       ret = ab8500_fg_coulomb_counter(di, false);
-       if (ret)
-               dev_err(di->dev, "failed to disable coulomb counter\n");
-
-       destroy_workqueue(di->fg_wq);
-       ab8500_fg_sysfs_exit(di);
-
-       flush_scheduled_work();
-       ab8500_fg_sysfs_psy_remove_attrs(di);
-       power_supply_unregister(di->fg_psy);
-       return ret;
-}
-
-/* ab8500 fg driver interrupts and their respective isr */
-static struct ab8500_fg_interrupts ab8500_fg_irq_th[] = {
-       {"NCONV_ACCU", ab8500_fg_cc_convend_handler},
-       {"BATT_OVV", ab8500_fg_batt_ovv_handler},
-       {"LOW_BAT_F", ab8500_fg_lowbatf_handler},
-       {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler},
-};
-
-static struct ab8500_fg_interrupts ab8500_fg_irq_bh[] = {
-       {"CCEOC", ab8500_fg_cc_data_end_handler},
-};
-
-static char *supply_interface[] = {
-       "ab8500_chargalg",
-       "ab8500_usb",
-};
-
-static const struct power_supply_desc ab8500_fg_desc = {
-       .name                   = "ab8500_fg",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = ab8500_fg_props,
-       .num_properties         = ARRAY_SIZE(ab8500_fg_props),
-       .get_property           = ab8500_fg_get_property,
-       .external_power_changed = ab8500_fg_external_power_changed,
-};
-
-static int ab8500_fg_probe(struct platform_device *pdev)
-{
-       struct device_node *np = pdev->dev.of_node;
-       struct abx500_bm_data *plat = pdev->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       struct ab8500_fg *di;
-       int i, irq;
-       int ret = 0;
-
-       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di) {
-               dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__);
-               return -ENOMEM;
-       }
-
-       if (!plat) {
-               dev_err(&pdev->dev, "no battery management data supplied\n");
-               return -EINVAL;
-       }
-       di->bm = plat;
-
-       if (np) {
-               ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
-               if (ret) {
-                       dev_err(&pdev->dev, "failed to get battery information\n");
-                       return ret;
-               }
-       }
-
-       mutex_init(&di->cc_lock);
-
-       /* get parent data */
-       di->dev = &pdev->dev;
-       di->parent = dev_get_drvdata(pdev->dev.parent);
-       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-
-       psy_cfg.supplied_to = supply_interface;
-       psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
-       psy_cfg.drv_data = di;
-
-       di->bat_cap.max_mah_design = MILLI_TO_MICRO *
-               di->bm->bat_type[di->bm->batt_id].charge_full_design;
-
-       di->bat_cap.max_mah = di->bat_cap.max_mah_design;
-
-       di->vbat_nom = di->bm->bat_type[di->bm->batt_id].nominal_voltage;
-
-       di->init_capacity = true;
-
-       ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
-       ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
-
-       /* Create a work queue for running the FG algorithm */
-       di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
-       if (di->fg_wq == NULL) {
-               dev_err(di->dev, "failed to create work queue\n");
-               return -ENOMEM;
-       }
-
-       /* Init work for running the fg algorithm instantly */
-       INIT_WORK(&di->fg_work, ab8500_fg_instant_work);
-
-       /* Init work for getting the battery accumulated current */
-       INIT_WORK(&di->fg_acc_cur_work, ab8500_fg_acc_cur_work);
-
-       /* Init work for reinitialising the fg algorithm */
-       INIT_DEFERRABLE_WORK(&di->fg_reinit_work,
-               ab8500_fg_reinit_work);
-
-       /* Work delayed Queue to run the state machine */
-       INIT_DEFERRABLE_WORK(&di->fg_periodic_work,
-               ab8500_fg_periodic_work);
-
-       /* Work to check low battery condition */
-       INIT_DEFERRABLE_WORK(&di->fg_low_bat_work,
-               ab8500_fg_low_bat_work);
-
-       /* Init work for HW failure check */
-       INIT_DEFERRABLE_WORK(&di->fg_check_hw_failure_work,
-               ab8500_fg_check_hw_failure_work);
-
-       /* Reset battery low voltage flag */
-       di->flags.low_bat = false;
-
-       /* Initialize low battery counter */
-       di->low_bat_cnt = 10;
-
-       /* Initialize OVV, and other registers */
-       ret = ab8500_fg_init_hw_registers(di);
-       if (ret) {
-               dev_err(di->dev, "failed to initialize registers\n");
-               goto free_inst_curr_wq;
-       }
-
-       /* Consider battery unknown until we're informed otherwise */
-       di->flags.batt_unknown = true;
-       di->flags.batt_id_received = false;
-
-       /* Register FG power supply class */
-       di->fg_psy = power_supply_register(di->dev, &ab8500_fg_desc, &psy_cfg);
-       if (IS_ERR(di->fg_psy)) {
-               dev_err(di->dev, "failed to register FG psy\n");
-               ret = PTR_ERR(di->fg_psy);
-               goto free_inst_curr_wq;
-       }
-
-       di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
-       ab8500_fg_coulomb_counter(di, true);
-
-       /*
-        * Initialize completion used to notify completion and start
-        * of inst current
-        */
-       init_completion(&di->ab8500_fg_started);
-       init_completion(&di->ab8500_fg_complete);
-
-       /* Register primary interrupt handlers */
-       for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) {
-               irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name);
-               ret = request_irq(irq, ab8500_fg_irq_th[i].isr,
-                                 IRQF_SHARED | IRQF_NO_SUSPEND,
-                                 ab8500_fg_irq_th[i].name, di);
-
-               if (ret != 0) {
-                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n",
-                               ab8500_fg_irq_th[i].name, irq, ret);
-                       goto free_irq;
-               }
-               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
-                       ab8500_fg_irq_th[i].name, irq, ret);
-       }
-
-       /* Register threaded interrupt handler */
-       irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name);
-       ret = request_threaded_irq(irq, NULL, ab8500_fg_irq_bh[0].isr,
-                               IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
-                       ab8500_fg_irq_bh[0].name, di);
-
-       if (ret != 0) {
-               dev_err(di->dev, "failed to request %s IRQ %d: %d\n",
-                       ab8500_fg_irq_bh[0].name, irq, ret);
-               goto free_irq;
-       }
-       dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
-               ab8500_fg_irq_bh[0].name, irq, ret);
-
-       di->irq = platform_get_irq_byname(pdev, "CCEOC");
-       disable_irq(di->irq);
-       di->nbr_cceoc_irq_cnt = 0;
-
-       platform_set_drvdata(pdev, di);
-
-       ret = ab8500_fg_sysfs_init(di);
-       if (ret) {
-               dev_err(di->dev, "failed to create sysfs entry\n");
-               goto free_irq;
-       }
-
-       ret = ab8500_fg_sysfs_psy_create_attrs(di);
-       if (ret) {
-               dev_err(di->dev, "failed to create FG psy\n");
-               ab8500_fg_sysfs_exit(di);
-               goto free_irq;
-       }
-
-       /* Calibrate the fg first time */
-       di->flags.calibrate = true;
-       di->calib_state = AB8500_FG_CALIB_INIT;
-
-       /* Use room temp as default value until we get an update from driver. */
-       di->bat_temp = 210;
-
-       /* Run the FG algorithm */
-       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
-
-       list_add_tail(&di->node, &ab8500_fg_list);
-
-       return ret;
-
-free_irq:
-       power_supply_unregister(di->fg_psy);
-
-       /* We also have to free all registered irqs */
-       for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) {
-               irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name);
-               free_irq(irq, di);
-       }
-       irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name);
-       free_irq(irq, di);
-free_inst_curr_wq:
-       destroy_workqueue(di->fg_wq);
-       return ret;
-}
-
-static const struct of_device_id ab8500_fg_match[] = {
-       { .compatible = "stericsson,ab8500-fg", },
-       { },
-};
-
-static struct platform_driver ab8500_fg_driver = {
-       .probe = ab8500_fg_probe,
-       .remove = ab8500_fg_remove,
-       .suspend = ab8500_fg_suspend,
-       .resume = ab8500_fg_resume,
-       .driver = {
-               .name = "ab8500-fg",
-               .of_match_table = ab8500_fg_match,
-       },
-};
-
-static int __init ab8500_fg_init(void)
-{
-       return platform_driver_register(&ab8500_fg_driver);
-}
-
-static void __exit ab8500_fg_exit(void)
-{
-       platform_driver_unregister(&ab8500_fg_driver);
-}
-
-subsys_initcall_sync(ab8500_fg_init);
-module_exit(ab8500_fg_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
-MODULE_ALIAS("platform:ab8500-fg");
-MODULE_DESCRIPTION("AB8500 Fuel Gauge driver");
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
deleted file mode 100644 (file)
index d9104b1..0000000
+++ /dev/null
@@ -1,2166 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2012
- * Copyright (c) 2012 Sony Mobile Communications AB
- *
- * Charging algorithm driver for abx500 variants
- *
- * License Terms: GNU General Public License v2
- * Authors:
- *     Johan Palsson <johan.palsson@stericsson.com>
- *     Karl Komierowski <karl.komierowski@stericsson.com>
- *     Arun R Murthy <arun.murthy@stericsson.com>
- *     Author: Imre Sunyi <imre.sunyi@sonymobile.com>
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/hrtimer.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/completion.h>
-#include <linux/workqueue.h>
-#include <linux/kobject.h>
-#include <linux/of.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/abx500.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ux500_chargalg.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
-#include <linux/notifier.h>
-
-/* Watchdog kick interval */
-#define CHG_WD_INTERVAL                        (6 * HZ)
-
-/* End-of-charge criteria counter */
-#define EOC_COND_CNT                   10
-
-/* One hour expressed in seconds */
-#define ONE_HOUR_IN_SECONDS            3600
-
-/* Five minutes expressed in seconds */
-#define FIVE_MINUTES_IN_SECONDS        300
-
-/* Plus margin for the low battery threshold */
-#define BAT_PLUS_MARGIN                (100)
-
-#define CHARGALG_CURR_STEP_LOW         0
-#define CHARGALG_CURR_STEP_HIGH        100
-
-enum abx500_chargers {
-       NO_CHG,
-       AC_CHG,
-       USB_CHG,
-};
-
-struct abx500_chargalg_charger_info {
-       enum abx500_chargers conn_chg;
-       enum abx500_chargers prev_conn_chg;
-       enum abx500_chargers online_chg;
-       enum abx500_chargers prev_online_chg;
-       enum abx500_chargers charger_type;
-       bool usb_chg_ok;
-       bool ac_chg_ok;
-       int usb_volt;
-       int usb_curr;
-       int ac_volt;
-       int ac_curr;
-       int usb_vset;
-       int usb_iset;
-       int ac_vset;
-       int ac_iset;
-};
-
-struct abx500_chargalg_suspension_status {
-       bool suspended_change;
-       bool ac_suspended;
-       bool usb_suspended;
-};
-
-struct abx500_chargalg_current_step_status {
-       bool curr_step_change;
-       int curr_step;
-};
-
-struct abx500_chargalg_battery_data {
-       int temp;
-       int volt;
-       int avg_curr;
-       int inst_curr;
-       int percent;
-};
-
-enum abx500_chargalg_states {
-       STATE_HANDHELD_INIT,
-       STATE_HANDHELD,
-       STATE_CHG_NOT_OK_INIT,
-       STATE_CHG_NOT_OK,
-       STATE_HW_TEMP_PROTECT_INIT,
-       STATE_HW_TEMP_PROTECT,
-       STATE_NORMAL_INIT,
-       STATE_USB_PP_PRE_CHARGE,
-       STATE_NORMAL,
-       STATE_WAIT_FOR_RECHARGE_INIT,
-       STATE_WAIT_FOR_RECHARGE,
-       STATE_MAINTENANCE_A_INIT,
-       STATE_MAINTENANCE_A,
-       STATE_MAINTENANCE_B_INIT,
-       STATE_MAINTENANCE_B,
-       STATE_TEMP_UNDEROVER_INIT,
-       STATE_TEMP_UNDEROVER,
-       STATE_TEMP_LOWHIGH_INIT,
-       STATE_TEMP_LOWHIGH,
-       STATE_SUSPENDED_INIT,
-       STATE_SUSPENDED,
-       STATE_OVV_PROTECT_INIT,
-       STATE_OVV_PROTECT,
-       STATE_SAFETY_TIMER_EXPIRED_INIT,
-       STATE_SAFETY_TIMER_EXPIRED,
-       STATE_BATT_REMOVED_INIT,
-       STATE_BATT_REMOVED,
-       STATE_WD_EXPIRED_INIT,
-       STATE_WD_EXPIRED,
-};
-
-static const char *states[] = {
-       "HANDHELD_INIT",
-       "HANDHELD",
-       "CHG_NOT_OK_INIT",
-       "CHG_NOT_OK",
-       "HW_TEMP_PROTECT_INIT",
-       "HW_TEMP_PROTECT",
-       "NORMAL_INIT",
-       "USB_PP_PRE_CHARGE",
-       "NORMAL",
-       "WAIT_FOR_RECHARGE_INIT",
-       "WAIT_FOR_RECHARGE",
-       "MAINTENANCE_A_INIT",
-       "MAINTENANCE_A",
-       "MAINTENANCE_B_INIT",
-       "MAINTENANCE_B",
-       "TEMP_UNDEROVER_INIT",
-       "TEMP_UNDEROVER",
-       "TEMP_LOWHIGH_INIT",
-       "TEMP_LOWHIGH",
-       "SUSPENDED_INIT",
-       "SUSPENDED",
-       "OVV_PROTECT_INIT",
-       "OVV_PROTECT",
-       "SAFETY_TIMER_EXPIRED_INIT",
-       "SAFETY_TIMER_EXPIRED",
-       "BATT_REMOVED_INIT",
-       "BATT_REMOVED",
-       "WD_EXPIRED_INIT",
-       "WD_EXPIRED",
-};
-
-struct abx500_chargalg_events {
-       bool batt_unknown;
-       bool mainextchnotok;
-       bool batt_ovv;
-       bool batt_rem;
-       bool btemp_underover;
-       bool btemp_lowhigh;
-       bool main_thermal_prot;
-       bool usb_thermal_prot;
-       bool main_ovv;
-       bool vbus_ovv;
-       bool usbchargernotok;
-       bool safety_timer_expired;
-       bool maintenance_timer_expired;
-       bool ac_wd_expired;
-       bool usb_wd_expired;
-       bool ac_cv_active;
-       bool usb_cv_active;
-       bool vbus_collapsed;
-};
-
-/**
- * struct abx500_charge_curr_maximization - Charger maximization parameters
- * @original_iset:     the non optimized/maximised charger current
- * @current_iset:      the charging current used at this moment
- * @test_delta_i:      the delta between the current we want to charge and the
-                       current that is really going into the battery
- * @condition_cnt:     number of iterations needed before a new charger current
-                       is set
- * @max_current:       maximum charger current
- * @wait_cnt:          to avoid too fast current step down in case of charger
- *                     voltage collapse, we insert this delay between step
- *                     down
- * @level:             tells in how many steps the charging current has been
-                       increased
- */
-struct abx500_charge_curr_maximization {
-       int original_iset;
-       int current_iset;
-       int test_delta_i;
-       int condition_cnt;
-       int max_current;
-       int wait_cnt;
-       u8 level;
-};
-
-enum maxim_ret {
-       MAXIM_RET_NOACTION,
-       MAXIM_RET_CHANGE,
-       MAXIM_RET_IBAT_TOO_HIGH,
-};
-
-/**
- * struct abx500_chargalg - abx500 Charging algorithm device information
- * @dev:               pointer to the structure device
- * @charge_status:     battery operating status
- * @eoc_cnt:           counter used to determine end-of_charge
- * @maintenance_chg:   indicate if maintenance charge is active
- * @t_hyst_norm                temperature hysteresis when the temperature has been
- *                     over or under normal limits
- * @t_hyst_lowhigh     temperature hysteresis when the temperature has been
- *                     over or under the high or low limits
- * @charge_state:      current state of the charging algorithm
- * @ccm                        charging current maximization parameters
- * @chg_info:          information about connected charger types
- * @batt_data:         data of the battery
- * @susp_status:       current charger suspension status
- * @bm:                Platform specific battery management information
- * @curr_status:       Current step status for over-current protection
- * @parent:            pointer to the struct abx500
- * @chargalg_psy:      structure that holds the battery properties exposed by
- *                     the charging algorithm
- * @events:            structure for information about events triggered
- * @chargalg_wq:               work queue for running the charging algorithm
- * @chargalg_periodic_work:    work to run the charging algorithm periodically
- * @chargalg_wd_work:          work to kick the charger watchdog periodically
- * @chargalg_work:             work to run the charging algorithm instantly
- * @safety_timer:              charging safety timer
- * @maintenance_timer:         maintenance charging timer
- * @chargalg_kobject:          structure of type kobject
- */
-struct abx500_chargalg {
-       struct device *dev;
-       int charge_status;
-       int eoc_cnt;
-       bool maintenance_chg;
-       int t_hyst_norm;
-       int t_hyst_lowhigh;
-       enum abx500_chargalg_states charge_state;
-       struct abx500_charge_curr_maximization ccm;
-       struct abx500_chargalg_charger_info chg_info;
-       struct abx500_chargalg_battery_data batt_data;
-       struct abx500_chargalg_suspension_status susp_status;
-       struct ab8500 *parent;
-       struct abx500_chargalg_current_step_status curr_status;
-       struct abx500_bm_data *bm;
-       struct power_supply *chargalg_psy;
-       struct ux500_charger *ac_chg;
-       struct ux500_charger *usb_chg;
-       struct abx500_chargalg_events events;
-       struct workqueue_struct *chargalg_wq;
-       struct delayed_work chargalg_periodic_work;
-       struct delayed_work chargalg_wd_work;
-       struct work_struct chargalg_work;
-       struct hrtimer safety_timer;
-       struct hrtimer maintenance_timer;
-       struct kobject chargalg_kobject;
-};
-
-/*External charger prepare notifier*/
-BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
-
-/* Main battery properties */
-static enum power_supply_property abx500_chargalg_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-};
-
-struct abx500_chargalg_sysfs_entry {
-       struct attribute attr;
-       ssize_t (*show)(struct abx500_chargalg *, char *);
-       ssize_t (*store)(struct abx500_chargalg *, const char *, size_t);
-};
-
-/**
- * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
- * @timer:     pointer to the hrtimer structure
- *
- * This function gets called when the safety timer for the charger
- * expires
- */
-static enum hrtimer_restart
-abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
-{
-       struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
-                                                 safety_timer);
-       dev_err(di->dev, "Safety timer expired\n");
-       di->events.safety_timer_expired = true;
-
-       /* Trigger execution of the algorithm instantly */
-       queue_work(di->chargalg_wq, &di->chargalg_work);
-
-       return HRTIMER_NORESTART;
-}
-
-/**
- * abx500_chargalg_maintenance_timer_expired() - Expiration of
- * the maintenance timer
- * @timer:     pointer to the timer structure
- *
- * This function gets called when the maintenence timer
- * expires
- */
-static enum hrtimer_restart
-abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
-{
-
-       struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
-                                                 maintenance_timer);
-
-       dev_dbg(di->dev, "Maintenance timer expired\n");
-       di->events.maintenance_timer_expired = true;
-
-       /* Trigger execution of the algorithm instantly */
-       queue_work(di->chargalg_wq, &di->chargalg_work);
-
-       return HRTIMER_NORESTART;
-}
-
-/**
- * abx500_chargalg_state_to() - Change charge state
- * @di:                pointer to the abx500_chargalg structure
- *
- * This function gets called when a charge state change should occur
- */
-static void abx500_chargalg_state_to(struct abx500_chargalg *di,
-       enum abx500_chargalg_states state)
-{
-       dev_dbg(di->dev,
-               "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n",
-               di->charge_state == state ? "NO" : "YES",
-               di->charge_state,
-               states[di->charge_state],
-               state,
-               states[state]);
-
-       di->charge_state = state;
-}
-
-static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
-{
-       switch (di->charge_state) {
-       case STATE_NORMAL:
-       case STATE_MAINTENANCE_A:
-       case STATE_MAINTENANCE_B:
-               break;
-       default:
-               return 0;
-       }
-
-       if (di->chg_info.charger_type & USB_CHG) {
-               return di->usb_chg->ops.check_enable(di->usb_chg,
-                         di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
-                         di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
-       } else if ((di->chg_info.charger_type & AC_CHG) &&
-                  !(di->ac_chg->external)) {
-               return di->ac_chg->ops.check_enable(di->ac_chg,
-                         di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
-                         di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
-       }
-       return 0;
-}
-
-/**
- * abx500_chargalg_check_charger_connection() - Check charger connection change
- * @di:                pointer to the abx500_chargalg structure
- *
- * This function will check if there is a change in the charger connection
- * and change charge state accordingly. AC has precedence over USB.
- */
-static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
-{
-       if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg ||
-               di->susp_status.suspended_change) {
-               /*
-                * Charger state changed or suspension
-                * has changed since last update
-                */
-               if ((di->chg_info.conn_chg & AC_CHG) &&
-                       !di->susp_status.ac_suspended) {
-                       dev_dbg(di->dev, "Charging source is AC\n");
-                       if (di->chg_info.charger_type != AC_CHG) {
-                               di->chg_info.charger_type = AC_CHG;
-                               abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-                       }
-               } else if ((di->chg_info.conn_chg & USB_CHG) &&
-                       !di->susp_status.usb_suspended) {
-                       dev_dbg(di->dev, "Charging source is USB\n");
-                       di->chg_info.charger_type = USB_CHG;
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               } else if (di->chg_info.conn_chg &&
-                       (di->susp_status.ac_suspended ||
-                       di->susp_status.usb_suspended)) {
-                       dev_dbg(di->dev, "Charging is suspended\n");
-                       di->chg_info.charger_type = NO_CHG;
-                       abx500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
-               } else {
-                       dev_dbg(di->dev, "Charging source is OFF\n");
-                       di->chg_info.charger_type = NO_CHG;
-                       abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
-               }
-               di->chg_info.prev_conn_chg = di->chg_info.conn_chg;
-               di->susp_status.suspended_change = false;
-       }
-       return di->chg_info.conn_chg;
-}
-
-/**
- * abx500_chargalg_check_current_step_status() - Check charging current
- * step status.
- * @di:                pointer to the abx500_chargalg structure
- *
- * This function will check if there is a change in the charging current step
- * and change charge state accordingly.
- */
-static void abx500_chargalg_check_current_step_status
-       (struct abx500_chargalg *di)
-{
-       if (di->curr_status.curr_step_change)
-               abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-       di->curr_status.curr_step_change = false;
-}
-
-/**
- * abx500_chargalg_start_safety_timer() - Start charging safety timer
- * @di:                pointer to the abx500_chargalg structure
- *
- * The safety timer is used to avoid overcharging of old or bad batteries.
- * There are different timers for AC and USB
- */
-static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
-{
-       /* Charger-dependent expiration time in hours*/
-       int timer_expiration = 0;
-
-       switch (di->chg_info.charger_type) {
-       case AC_CHG:
-               timer_expiration = di->bm->main_safety_tmr_h;
-               break;
-
-       case USB_CHG:
-               timer_expiration = di->bm->usb_safety_tmr_h;
-               break;
-
-       default:
-               dev_err(di->dev, "Unknown charger to charge from\n");
-               break;
-       }
-
-       di->events.safety_timer_expired = false;
-       hrtimer_set_expires_range(&di->safety_timer,
-               ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
-               ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
-       hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);
-}
-
-/**
- * abx500_chargalg_stop_safety_timer() - Stop charging safety timer
- * @di:                pointer to the abx500_chargalg structure
- *
- * The safety timer is stopped whenever the NORMAL state is exited
- */
-static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
-{
-       if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
-               di->events.safety_timer_expired = false;
-}
-
-/**
- * abx500_chargalg_start_maintenance_timer() - Start charging maintenance timer
- * @di:                pointer to the abx500_chargalg structure
- * @duration:  duration of ther maintenance timer in hours
- *
- * The maintenance timer is used to maintain the charge in the battery once
- * the battery is considered full. These timers are chosen to match the
- * discharge curve of the battery
- */
-static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
-       int duration)
-{
-       hrtimer_set_expires_range(&di->maintenance_timer,
-               ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
-               ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
-       di->events.maintenance_timer_expired = false;
-       hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
-}
-
-/**
- * abx500_chargalg_stop_maintenance_timer() - Stop maintenance timer
- * @di:                pointer to the abx500_chargalg structure
- *
- * The maintenance timer is stopped whenever maintenance ends or when another
- * state is entered
- */
-static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
-{
-       if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
-               di->events.maintenance_timer_expired = false;
-}
-
-/**
- * abx500_chargalg_kick_watchdog() - Kick charger watchdog
- * @di:                pointer to the abx500_chargalg structure
- *
- * The charger watchdog have to be kicked periodically whenever the charger is
- * on, else the ABB will reset the system
- */
-static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
-{
-       /* Check if charger exists and kick watchdog if charging */
-       if (di->ac_chg && di->ac_chg->ops.kick_wd &&
-           di->chg_info.online_chg & AC_CHG) {
-               /*
-                * If AB charger watchdog expired, pm2xxx charging
-                * gets disabled. To be safe, kick both AB charger watchdog
-                * and pm2xxx watchdog.
-                */
-               if (di->ac_chg->external &&
-                   di->usb_chg && di->usb_chg->ops.kick_wd)
-                       di->usb_chg->ops.kick_wd(di->usb_chg);
-
-               return di->ac_chg->ops.kick_wd(di->ac_chg);
-       }
-       else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
-                       di->chg_info.online_chg & USB_CHG)
-               return di->usb_chg->ops.kick_wd(di->usb_chg);
-
-       return -ENXIO;
-}
-
-/**
- * abx500_chargalg_ac_en() - Turn on/off the AC charger
- * @di:                pointer to the abx500_chargalg structure
- * @enable:    charger on/off
- * @vset:      requested charger output voltage
- * @iset:      requested charger output current
- *
- * The AC charger will be turned on/off with the requested charge voltage and
- * current
- */
-static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
-       int vset, int iset)
-{
-       static int abx500_chargalg_ex_ac_enable_toggle;
-
-       if (!di->ac_chg || !di->ac_chg->ops.enable)
-               return -ENXIO;
-
-       /* Select maximum of what both the charger and the battery supports */
-       if (di->ac_chg->max_out_volt)
-               vset = min(vset, di->ac_chg->max_out_volt);
-       if (di->ac_chg->max_out_curr)
-               iset = min(iset, di->ac_chg->max_out_curr);
-
-       di->chg_info.ac_iset = iset;
-       di->chg_info.ac_vset = vset;
-
-       /* Enable external charger */
-       if (enable && di->ac_chg->external &&
-           !abx500_chargalg_ex_ac_enable_toggle) {
-               blocking_notifier_call_chain(&charger_notifier_list,
-                                            0, di->dev);
-               abx500_chargalg_ex_ac_enable_toggle++;
-       }
-
-       return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
-}
-
-/**
- * abx500_chargalg_usb_en() - Turn on/off the USB charger
- * @di:                pointer to the abx500_chargalg structure
- * @enable:    charger on/off
- * @vset:      requested charger output voltage
- * @iset:      requested charger output current
- *
- * The USB charger will be turned on/off with the requested charge voltage and
- * current
- */
-static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
-       int vset, int iset)
-{
-       if (!di->usb_chg || !di->usb_chg->ops.enable)
-               return -ENXIO;
-
-       /* Select maximum of what both the charger and the battery supports */
-       if (di->usb_chg->max_out_volt)
-               vset = min(vset, di->usb_chg->max_out_volt);
-       if (di->usb_chg->max_out_curr)
-               iset = min(iset, di->usb_chg->max_out_curr);
-
-       di->chg_info.usb_iset = iset;
-       di->chg_info.usb_vset = vset;
-
-       return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
-}
-
- /**
- * ab8540_chargalg_usb_pp_en() - Enable/ disable USB power path
- * @di:                pointer to the abx500_chargalg structure
- * @enable:    power path enable/disable
- *
- * The USB power path will be enable/ disable
- */
-static int ab8540_chargalg_usb_pp_en(struct abx500_chargalg *di, bool enable)
-{
-       if (!di->usb_chg || !di->usb_chg->ops.pp_enable)
-               return -ENXIO;
-
-       return di->usb_chg->ops.pp_enable(di->usb_chg, enable);
-}
-
-/**
- * ab8540_chargalg_usb_pre_chg_en() - Enable/ disable USB pre-charge
- * @di:                pointer to the abx500_chargalg structure
- * @enable:    USB pre-charge enable/disable
- *
- * The USB USB pre-charge will be enable/ disable
- */
-static int ab8540_chargalg_usb_pre_chg_en(struct abx500_chargalg *di,
-                                         bool enable)
-{
-       if (!di->usb_chg || !di->usb_chg->ops.pre_chg_enable)
-               return -ENXIO;
-
-       return di->usb_chg->ops.pre_chg_enable(di->usb_chg, enable);
-}
-
-/**
- * abx500_chargalg_update_chg_curr() - Update charger current
- * @di:                pointer to the abx500_chargalg structure
- * @iset:      requested charger output current
- *
- * The charger output current will be updated for the charger
- * that is currently in use
- */
-static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di,
-               int iset)
-{
-       /* Check if charger exists and update current if charging */
-       if (di->ac_chg && di->ac_chg->ops.update_curr &&
-                       di->chg_info.charger_type & AC_CHG) {
-               /*
-                * Select maximum of what both the charger
-                * and the battery supports
-                */
-               if (di->ac_chg->max_out_curr)
-                       iset = min(iset, di->ac_chg->max_out_curr);
-
-               di->chg_info.ac_iset = iset;
-
-               return di->ac_chg->ops.update_curr(di->ac_chg, iset);
-       } else if (di->usb_chg && di->usb_chg->ops.update_curr &&
-                       di->chg_info.charger_type & USB_CHG) {
-               /*
-                * Select maximum of what both the charger
-                * and the battery supports
-                */
-               if (di->usb_chg->max_out_curr)
-                       iset = min(iset, di->usb_chg->max_out_curr);
-
-               di->chg_info.usb_iset = iset;
-
-               return di->usb_chg->ops.update_curr(di->usb_chg, iset);
-       }
-
-       return -ENXIO;
-}
-
-/**
- * abx500_chargalg_stop_charging() - Stop charging
- * @di:                pointer to the abx500_chargalg structure
- *
- * This function is called from any state where charging should be stopped.
- * All charging is disabled and all status parameters and timers are changed
- * accordingly
- */
-static void abx500_chargalg_stop_charging(struct abx500_chargalg *di)
-{
-       abx500_chargalg_ac_en(di, false, 0, 0);
-       abx500_chargalg_usb_en(di, false, 0, 0);
-       abx500_chargalg_stop_safety_timer(di);
-       abx500_chargalg_stop_maintenance_timer(di);
-       di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       di->maintenance_chg = false;
-       cancel_delayed_work(&di->chargalg_wd_work);
-       power_supply_changed(di->chargalg_psy);
-}
-
-/**
- * abx500_chargalg_hold_charging() - Pauses charging
- * @di:                pointer to the abx500_chargalg structure
- *
- * This function is called in the case where maintenance charging has been
- * disabled and instead a battery voltage mode is entered to check when the
- * battery voltage has reached a certain recharge voltage
- */
-static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
-{
-       abx500_chargalg_ac_en(di, false, 0, 0);
-       abx500_chargalg_usb_en(di, false, 0, 0);
-       abx500_chargalg_stop_safety_timer(di);
-       abx500_chargalg_stop_maintenance_timer(di);
-       di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
-       di->maintenance_chg = false;
-       cancel_delayed_work(&di->chargalg_wd_work);
-       power_supply_changed(di->chargalg_psy);
-}
-
-/**
- * abx500_chargalg_start_charging() - Start the charger
- * @di:                pointer to the abx500_chargalg structure
- * @vset:      requested charger output voltage
- * @iset:      requested charger output current
- *
- * A charger will be enabled depending on the requested charger type that was
- * detected previously.
- */
-static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
-       int vset, int iset)
-{
-       switch (di->chg_info.charger_type) {
-       case AC_CHG:
-               dev_dbg(di->dev,
-                       "AC parameters: Vset %d, Ich %d\n", vset, iset);
-               abx500_chargalg_usb_en(di, false, 0, 0);
-               abx500_chargalg_ac_en(di, true, vset, iset);
-               break;
-
-       case USB_CHG:
-               dev_dbg(di->dev,
-                       "USB parameters: Vset %d, Ich %d\n", vset, iset);
-               abx500_chargalg_ac_en(di, false, 0, 0);
-               abx500_chargalg_usb_en(di, true, vset, iset);
-               break;
-
-       default:
-               dev_err(di->dev, "Unknown charger to charge from\n");
-               break;
-       }
-}
-
-/**
- * abx500_chargalg_check_temp() - Check battery temperature ranges
- * @di:                pointer to the abx500_chargalg structure
- *
- * The battery temperature is checked against the predefined limits and the
- * charge state is changed accordingly
- */
-static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
-{
-       if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) &&
-               di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) {
-               /* Temp OK! */
-               di->events.btemp_underover = false;
-               di->events.btemp_lowhigh = false;
-               di->t_hyst_norm = 0;
-               di->t_hyst_lowhigh = 0;
-       } else {
-               if (((di->batt_data.temp >= di->bm->temp_high) &&
-                       (di->batt_data.temp <
-                               (di->bm->temp_over - di->t_hyst_lowhigh))) ||
-                       ((di->batt_data.temp >
-                               (di->bm->temp_under + di->t_hyst_lowhigh)) &&
-                       (di->batt_data.temp <= di->bm->temp_low))) {
-                       /* TEMP minor!!!!! */
-                       di->events.btemp_underover = false;
-                       di->events.btemp_lowhigh = true;
-                       di->t_hyst_norm = di->bm->temp_hysteresis;
-                       di->t_hyst_lowhigh = 0;
-               } else if (di->batt_data.temp <= di->bm->temp_under ||
-                       di->batt_data.temp >= di->bm->temp_over) {
-                       /* TEMP major!!!!! */
-                       di->events.btemp_underover = true;
-                       di->events.btemp_lowhigh = false;
-                       di->t_hyst_norm = 0;
-                       di->t_hyst_lowhigh = di->bm->temp_hysteresis;
-               } else {
-               /* Within hysteresis */
-               dev_dbg(di->dev, "Within hysteresis limit temp: %d "
-                               "hyst_lowhigh %d, hyst normal %d\n",
-                               di->batt_data.temp, di->t_hyst_lowhigh,
-                               di->t_hyst_norm);
-               }
-       }
-}
-
-/**
- * abx500_chargalg_check_charger_voltage() - Check charger voltage
- * @di:                pointer to the abx500_chargalg structure
- *
- * Charger voltage is checked against maximum limit
- */
-static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
-{
-       if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max)
-               di->chg_info.usb_chg_ok = false;
-       else
-               di->chg_info.usb_chg_ok = true;
-
-       if (di->chg_info.ac_volt > di->bm->chg_params->ac_volt_max)
-               di->chg_info.ac_chg_ok = false;
-       else
-               di->chg_info.ac_chg_ok = true;
-
-}
-
-/**
- * abx500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
- * @di:                pointer to the abx500_chargalg structure
- *
- * End-of-charge criteria is fulfilled when the battery voltage is above a
- * certain limit and the battery current is below a certain limit for a
- * predefined number of consecutive seconds. If true, the battery is full
- */
-static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
-{
-       if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
-               di->charge_state == STATE_NORMAL &&
-               !di->maintenance_chg && (di->batt_data.volt >=
-               di->bm->bat_type[di->bm->batt_id].termination_vol ||
-               di->events.usb_cv_active || di->events.ac_cv_active) &&
-               di->batt_data.avg_curr <
-               di->bm->bat_type[di->bm->batt_id].termination_curr &&
-               di->batt_data.avg_curr > 0) {
-               if (++di->eoc_cnt >= EOC_COND_CNT) {
-                       di->eoc_cnt = 0;
-                       if ((di->chg_info.charger_type & USB_CHG) &&
-                          (di->usb_chg->power_path))
-                               ab8540_chargalg_usb_pp_en(di, true);
-                       di->charge_status = POWER_SUPPLY_STATUS_FULL;
-                       di->maintenance_chg = true;
-                       dev_dbg(di->dev, "EOC reached!\n");
-                       power_supply_changed(di->chargalg_psy);
-               } else {
-                       dev_dbg(di->dev,
-                               " EOC limit reached for the %d"
-                               " time, out of %d before EOC\n",
-                               di->eoc_cnt,
-                               EOC_COND_CNT);
-               }
-       } else {
-               di->eoc_cnt = 0;
-       }
-}
-
-static void init_maxim_chg_curr(struct abx500_chargalg *di)
-{
-       di->ccm.original_iset =
-               di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
-       di->ccm.current_iset =
-               di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
-       di->ccm.test_delta_i = di->bm->maxi->charger_curr_step;
-       di->ccm.max_current = di->bm->maxi->chg_curr;
-       di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
-       di->ccm.level = 0;
-}
-
-/**
- * abx500_chargalg_chg_curr_maxim - increases the charger current to
- *                     compensate for the system load
- * @di         pointer to the abx500_chargalg structure
- *
- * This maximization function is used to raise the charger current to get the
- * battery current as close to the optimal value as possible. The battery
- * current during charging is affected by the system load
- */
-static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
-{
-       int delta_i;
-
-       if (!di->bm->maxi->ena_maxi)
-               return MAXIM_RET_NOACTION;
-
-       delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
-
-       if (di->events.vbus_collapsed) {
-               dev_dbg(di->dev, "Charger voltage has collapsed %d\n",
-                               di->ccm.wait_cnt);
-               if (di->ccm.wait_cnt == 0) {
-                       dev_dbg(di->dev, "lowering current\n");
-                       di->ccm.wait_cnt++;
-                       di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
-                       di->ccm.max_current =
-                               di->ccm.current_iset - di->ccm.test_delta_i;
-                       di->ccm.current_iset = di->ccm.max_current;
-                       di->ccm.level--;
-                       return MAXIM_RET_CHANGE;
-               } else {
-                       dev_dbg(di->dev, "waiting\n");
-                       /* Let's go in here twice before lowering curr again */
-                       di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 3;
-                       return MAXIM_RET_NOACTION;
-               }
-       }
-
-       di->ccm.wait_cnt = 0;
-
-       if ((di->batt_data.inst_curr > di->ccm.original_iset)) {
-               dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
-                       " (limit %dmA) (current iset: %dmA)!\n",
-                       di->batt_data.inst_curr, di->ccm.original_iset,
-                       di->ccm.current_iset);
-
-               if (di->ccm.current_iset == di->ccm.original_iset)
-                       return MAXIM_RET_NOACTION;
-
-               di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
-               di->ccm.current_iset = di->ccm.original_iset;
-               di->ccm.level = 0;
-
-               return MAXIM_RET_IBAT_TOO_HIGH;
-       }
-
-       if (delta_i > di->ccm.test_delta_i &&
-               (di->ccm.current_iset + di->ccm.test_delta_i) <
-               di->ccm.max_current) {
-               if (di->ccm.condition_cnt-- == 0) {
-                       /* Increse the iset with cco.test_delta_i */
-                       di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
-                       di->ccm.current_iset += di->ccm.test_delta_i;
-                       di->ccm.level++;
-                       dev_dbg(di->dev, " Maximization needed, increase"
-                               " with %d mA to %dmA (Optimal ibat: %d)"
-                               " Level %d\n",
-                               di->ccm.test_delta_i,
-                               di->ccm.current_iset,
-                               di->ccm.original_iset,
-                               di->ccm.level);
-                       return MAXIM_RET_CHANGE;
-               } else {
-                       return MAXIM_RET_NOACTION;
-               }
-       }  else {
-               di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
-               return MAXIM_RET_NOACTION;
-       }
-}
-
-static void handle_maxim_chg_curr(struct abx500_chargalg *di)
-{
-       enum maxim_ret ret;
-       int result;
-
-       ret = abx500_chargalg_chg_curr_maxim(di);
-       switch (ret) {
-       case MAXIM_RET_CHANGE:
-               result = abx500_chargalg_update_chg_curr(di,
-                       di->ccm.current_iset);
-               if (result)
-                       dev_err(di->dev, "failed to set chg curr\n");
-               break;
-       case MAXIM_RET_IBAT_TOO_HIGH:
-               result = abx500_chargalg_update_chg_curr(di,
-                       di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
-               if (result)
-                       dev_err(di->dev, "failed to set chg curr\n");
-               break;
-
-       case MAXIM_RET_NOACTION:
-       default:
-               /* Do nothing..*/
-               break;
-       }
-}
-
-static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
-{
-       struct power_supply *psy;
-       struct power_supply *ext = dev_get_drvdata(dev);
-       const char **supplicants = (const char **)ext->supplied_to;
-       struct abx500_chargalg *di;
-       union power_supply_propval ret;
-       int j;
-       bool capacity_updated = false;
-
-       psy = (struct power_supply *)data;
-       di = power_supply_get_drvdata(psy);
-       /* For all psy where the driver name appears in any supplied_to */
-       j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
-       if (j < 0)
-               return 0;
-
-       /*
-        *  If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its
-        * property because of handling that sysfs entry on its own, this is
-        * the place to get the battery capacity.
-        */
-       if (!power_supply_get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) {
-               di->batt_data.percent = ret.intval;
-               capacity_updated = true;
-       }
-
-       /* Go through all properties for the psy */
-       for (j = 0; j < ext->desc->num_properties; j++) {
-               enum power_supply_property prop;
-               prop = ext->desc->properties[j];
-
-               /*
-                * Initialize chargers if not already done.
-                * The ab8500_charger*/
-               if (!di->ac_chg &&
-                       ext->desc->type == POWER_SUPPLY_TYPE_MAINS)
-                       di->ac_chg = psy_to_ux500_charger(ext);
-               else if (!di->usb_chg &&
-                       ext->desc->type == POWER_SUPPLY_TYPE_USB)
-                       di->usb_chg = psy_to_ux500_charger(ext);
-
-               if (power_supply_get_property(ext, prop, &ret))
-                       continue;
-               switch (prop) {
-               case POWER_SUPPLY_PROP_PRESENT:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               /* Battery present */
-                               if (ret.intval)
-                                       di->events.batt_rem = false;
-                               /* Battery removed */
-                               else
-                                       di->events.batt_rem = true;
-                               break;
-                       case POWER_SUPPLY_TYPE_MAINS:
-                               /* AC disconnected */
-                               if (!ret.intval &&
-                                       (di->chg_info.conn_chg & AC_CHG)) {
-                                       di->chg_info.prev_conn_chg =
-                                               di->chg_info.conn_chg;
-                                       di->chg_info.conn_chg &= ~AC_CHG;
-                               }
-                               /* AC connected */
-                               else if (ret.intval &&
-                                       !(di->chg_info.conn_chg & AC_CHG)) {
-                                       di->chg_info.prev_conn_chg =
-                                               di->chg_info.conn_chg;
-                                       di->chg_info.conn_chg |= AC_CHG;
-                               }
-                               break;
-                       case POWER_SUPPLY_TYPE_USB:
-                               /* USB disconnected */
-                               if (!ret.intval &&
-                                       (di->chg_info.conn_chg & USB_CHG)) {
-                                       di->chg_info.prev_conn_chg =
-                                               di->chg_info.conn_chg;
-                                       di->chg_info.conn_chg &= ~USB_CHG;
-                               }
-                               /* USB connected */
-                               else if (ret.intval &&
-                                       !(di->chg_info.conn_chg & USB_CHG)) {
-                                       di->chg_info.prev_conn_chg =
-                                               di->chg_info.conn_chg;
-                                       di->chg_info.conn_chg |= USB_CHG;
-                               }
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-
-               case POWER_SUPPLY_PROP_ONLINE:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               break;
-                       case POWER_SUPPLY_TYPE_MAINS:
-                               /* AC offline */
-                               if (!ret.intval &&
-                                       (di->chg_info.online_chg & AC_CHG)) {
-                                       di->chg_info.prev_online_chg =
-                                               di->chg_info.online_chg;
-                                       di->chg_info.online_chg &= ~AC_CHG;
-                               }
-                               /* AC online */
-                               else if (ret.intval &&
-                                       !(di->chg_info.online_chg & AC_CHG)) {
-                                       di->chg_info.prev_online_chg =
-                                               di->chg_info.online_chg;
-                                       di->chg_info.online_chg |= AC_CHG;
-                                       queue_delayed_work(di->chargalg_wq,
-                                               &di->chargalg_wd_work, 0);
-                               }
-                               break;
-                       case POWER_SUPPLY_TYPE_USB:
-                               /* USB offline */
-                               if (!ret.intval &&
-                                       (di->chg_info.online_chg & USB_CHG)) {
-                                       di->chg_info.prev_online_chg =
-                                               di->chg_info.online_chg;
-                                       di->chg_info.online_chg &= ~USB_CHG;
-                               }
-                               /* USB online */
-                               else if (ret.intval &&
-                                       !(di->chg_info.online_chg & USB_CHG)) {
-                                       di->chg_info.prev_online_chg =
-                                               di->chg_info.online_chg;
-                                       di->chg_info.online_chg |= USB_CHG;
-                                       queue_delayed_work(di->chargalg_wq,
-                                               &di->chargalg_wd_work, 0);
-                               }
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-
-               case POWER_SUPPLY_PROP_HEALTH:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               break;
-                       case POWER_SUPPLY_TYPE_MAINS:
-                               switch (ret.intval) {
-                               case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
-                                       di->events.mainextchnotok = true;
-                                       di->events.main_thermal_prot = false;
-                                       di->events.main_ovv = false;
-                                       di->events.ac_wd_expired = false;
-                                       break;
-                               case POWER_SUPPLY_HEALTH_DEAD:
-                                       di->events.ac_wd_expired = true;
-                                       di->events.mainextchnotok = false;
-                                       di->events.main_ovv = false;
-                                       di->events.main_thermal_prot = false;
-                                       break;
-                               case POWER_SUPPLY_HEALTH_COLD:
-                               case POWER_SUPPLY_HEALTH_OVERHEAT:
-                                       di->events.main_thermal_prot = true;
-                                       di->events.mainextchnotok = false;
-                                       di->events.main_ovv = false;
-                                       di->events.ac_wd_expired = false;
-                                       break;
-                               case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
-                                       di->events.main_ovv = true;
-                                       di->events.mainextchnotok = false;
-                                       di->events.main_thermal_prot = false;
-                                       di->events.ac_wd_expired = false;
-                                       break;
-                               case POWER_SUPPLY_HEALTH_GOOD:
-                                       di->events.main_thermal_prot = false;
-                                       di->events.mainextchnotok = false;
-                                       di->events.main_ovv = false;
-                                       di->events.ac_wd_expired = false;
-                                       break;
-                               default:
-                                       break;
-                               }
-                               break;
-
-                       case POWER_SUPPLY_TYPE_USB:
-                               switch (ret.intval) {
-                               case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
-                                       di->events.usbchargernotok = true;
-                                       di->events.usb_thermal_prot = false;
-                                       di->events.vbus_ovv = false;
-                                       di->events.usb_wd_expired = false;
-                                       break;
-                               case POWER_SUPPLY_HEALTH_DEAD:
-                                       di->events.usb_wd_expired = true;
-                                       di->events.usbchargernotok = false;
-                                       di->events.usb_thermal_prot = false;
-                                       di->events.vbus_ovv = false;
-                                       break;
-                               case POWER_SUPPLY_HEALTH_COLD:
-                               case POWER_SUPPLY_HEALTH_OVERHEAT:
-                                       di->events.usb_thermal_prot = true;
-                                       di->events.usbchargernotok = false;
-                                       di->events.vbus_ovv = false;
-                                       di->events.usb_wd_expired = false;
-                                       break;
-                               case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
-                                       di->events.vbus_ovv = true;
-                                       di->events.usbchargernotok = false;
-                                       di->events.usb_thermal_prot = false;
-                                       di->events.usb_wd_expired = false;
-                                       break;
-                               case POWER_SUPPLY_HEALTH_GOOD:
-                                       di->events.usbchargernotok = false;
-                                       di->events.usb_thermal_prot = false;
-                                       di->events.vbus_ovv = false;
-                                       di->events.usb_wd_expired = false;
-                                       break;
-                               default:
-                                       break;
-                               }
-                       default:
-                               break;
-                       }
-                       break;
-
-               case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               di->batt_data.volt = ret.intval / 1000;
-                               break;
-                       case POWER_SUPPLY_TYPE_MAINS:
-                               di->chg_info.ac_volt = ret.intval / 1000;
-                               break;
-                       case POWER_SUPPLY_TYPE_USB:
-                               di->chg_info.usb_volt = ret.intval / 1000;
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-
-               case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_MAINS:
-                               /* AVG is used to indicate when we are
-                                * in CV mode */
-                               if (ret.intval)
-                                       di->events.ac_cv_active = true;
-                               else
-                                       di->events.ac_cv_active = false;
-
-                               break;
-                       case POWER_SUPPLY_TYPE_USB:
-                               /* AVG is used to indicate when we are
-                                * in CV mode */
-                               if (ret.intval)
-                                       di->events.usb_cv_active = true;
-                               else
-                                       di->events.usb_cv_active = false;
-
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-
-               case POWER_SUPPLY_PROP_TECHNOLOGY:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               if (ret.intval)
-                                       di->events.batt_unknown = false;
-                               else
-                                       di->events.batt_unknown = true;
-
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-
-               case POWER_SUPPLY_PROP_TEMP:
-                       di->batt_data.temp = ret.intval / 10;
-                       break;
-
-               case POWER_SUPPLY_PROP_CURRENT_NOW:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_MAINS:
-                                       di->chg_info.ac_curr =
-                                               ret.intval / 1000;
-                                       break;
-                       case POWER_SUPPLY_TYPE_USB:
-                                       di->chg_info.usb_curr =
-                                               ret.intval / 1000;
-                               break;
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               di->batt_data.inst_curr = ret.intval / 1000;
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-
-               case POWER_SUPPLY_PROP_CURRENT_AVG:
-                       switch (ext->desc->type) {
-                       case POWER_SUPPLY_TYPE_BATTERY:
-                               di->batt_data.avg_curr = ret.intval / 1000;
-                               break;
-                       case POWER_SUPPLY_TYPE_USB:
-                               if (ret.intval)
-                                       di->events.vbus_collapsed = true;
-                               else
-                                       di->events.vbus_collapsed = false;
-                               break;
-                       default:
-                               break;
-                       }
-                       break;
-               case POWER_SUPPLY_PROP_CAPACITY:
-                       if (!capacity_updated)
-                               di->batt_data.percent = ret.intval;
-                       break;
-               default:
-                       break;
-               }
-       }
-       return 0;
-}
-
-/**
- * abx500_chargalg_external_power_changed() - callback for power supply changes
- * @psy:       pointer to the structure power_supply
- *
- * This function is the entry point of the pointer external_power_changed
- * of the structure power_supply.
- * This function gets executed when there is a change in any external power
- * supply that this driver needs to be notified of.
- */
-static void abx500_chargalg_external_power_changed(struct power_supply *psy)
-{
-       struct abx500_chargalg *di = power_supply_get_drvdata(psy);
-
-       /*
-        * Trigger execution of the algorithm instantly and read
-        * all power_supply properties there instead
-        */
-       queue_work(di->chargalg_wq, &di->chargalg_work);
-}
-
-/**
- * abx500_chargalg_algorithm() - Main function for the algorithm
- * @di:                pointer to the abx500_chargalg structure
- *
- * This is the main control function for the charging algorithm.
- * It is called periodically or when something happens that will
- * trigger a state change
- */
-static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
-{
-       int charger_status;
-       int ret;
-       int curr_step_lvl;
-
-       /* Collect data from all power_supply class devices */
-       class_for_each_device(power_supply_class, NULL,
-               di->chargalg_psy, abx500_chargalg_get_ext_psy_data);
-
-       abx500_chargalg_end_of_charge(di);
-       abx500_chargalg_check_temp(di);
-       abx500_chargalg_check_charger_voltage(di);
-
-       charger_status = abx500_chargalg_check_charger_connection(di);
-       abx500_chargalg_check_current_step_status(di);
-
-       if (is_ab8500(di->parent)) {
-               ret = abx500_chargalg_check_charger_enable(di);
-               if (ret < 0)
-                       dev_err(di->dev, "Checking charger is enabled error"
-                                       ": Returned Value %d\n", ret);
-       }
-
-       /*
-        * First check if we have a charger connected.
-        * Also we don't allow charging of unknown batteries if configured
-        * this way
-        */
-       if (!charger_status ||
-               (di->events.batt_unknown && !di->bm->chg_unknown_bat)) {
-               if (di->charge_state != STATE_HANDHELD) {
-                       di->events.safety_timer_expired = false;
-                       abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
-               }
-       }
-
-       /* If suspended, we should not continue checking the flags */
-       else if (di->charge_state == STATE_SUSPENDED_INIT ||
-               di->charge_state == STATE_SUSPENDED) {
-               /* We don't do anything here, just don,t continue */
-       }
-
-       /* Safety timer expiration */
-       else if (di->events.safety_timer_expired) {
-               if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED)
-                       abx500_chargalg_state_to(di,
-                               STATE_SAFETY_TIMER_EXPIRED_INIT);
-       }
-       /*
-        * Check if any interrupts has occured
-        * that will prevent us from charging
-        */
-
-       /* Battery removed */
-       else if (di->events.batt_rem) {
-               if (di->charge_state != STATE_BATT_REMOVED)
-                       abx500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
-       }
-       /* Main or USB charger not ok. */
-       else if (di->events.mainextchnotok || di->events.usbchargernotok) {
-               /*
-                * If vbus_collapsed is set, we have to lower the charger
-                * current, which is done in the normal state below
-                */
-               if (di->charge_state != STATE_CHG_NOT_OK &&
-                               !di->events.vbus_collapsed)
-                       abx500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
-       }
-       /* VBUS, Main or VBAT OVV. */
-       else if (di->events.vbus_ovv ||
-                       di->events.main_ovv ||
-                       di->events.batt_ovv ||
-                       !di->chg_info.usb_chg_ok ||
-                       !di->chg_info.ac_chg_ok) {
-               if (di->charge_state != STATE_OVV_PROTECT)
-                       abx500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
-       }
-       /* USB Thermal, stop charging */
-       else if (di->events.main_thermal_prot ||
-               di->events.usb_thermal_prot) {
-               if (di->charge_state != STATE_HW_TEMP_PROTECT)
-                       abx500_chargalg_state_to(di,
-                               STATE_HW_TEMP_PROTECT_INIT);
-       }
-       /* Battery temp over/under */
-       else if (di->events.btemp_underover) {
-               if (di->charge_state != STATE_TEMP_UNDEROVER)
-                       abx500_chargalg_state_to(di,
-                               STATE_TEMP_UNDEROVER_INIT);
-       }
-       /* Watchdog expired */
-       else if (di->events.ac_wd_expired ||
-               di->events.usb_wd_expired) {
-               if (di->charge_state != STATE_WD_EXPIRED)
-                       abx500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
-       }
-       /* Battery temp high/low */
-       else if (di->events.btemp_lowhigh) {
-               if (di->charge_state != STATE_TEMP_LOWHIGH)
-                       abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
-       }
-
-       dev_dbg(di->dev,
-               "[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d "
-               "State %s Active_chg %d Chg_status %d AC %d USB %d "
-               "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d "
-               "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n",
-               di->batt_data.volt,
-               di->batt_data.avg_curr,
-               di->batt_data.inst_curr,
-               di->batt_data.temp,
-               di->batt_data.percent,
-               di->maintenance_chg,
-               states[di->charge_state],
-               di->chg_info.charger_type,
-               di->charge_status,
-               di->chg_info.conn_chg & AC_CHG,
-               di->chg_info.conn_chg & USB_CHG,
-               di->chg_info.online_chg & AC_CHG,
-               di->chg_info.online_chg & USB_CHG,
-               di->events.ac_cv_active,
-               di->events.usb_cv_active,
-               di->chg_info.ac_curr,
-               di->chg_info.usb_curr,
-               di->chg_info.ac_vset,
-               di->chg_info.ac_iset,
-               di->chg_info.usb_vset,
-               di->chg_info.usb_iset);
-
-       switch (di->charge_state) {
-       case STATE_HANDHELD_INIT:
-               abx500_chargalg_stop_charging(di);
-               di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
-               abx500_chargalg_state_to(di, STATE_HANDHELD);
-               /* Intentional fallthrough */
-
-       case STATE_HANDHELD:
-               break;
-
-       case STATE_SUSPENDED_INIT:
-               if (di->susp_status.ac_suspended)
-                       abx500_chargalg_ac_en(di, false, 0, 0);
-               if (di->susp_status.usb_suspended)
-                       abx500_chargalg_usb_en(di, false, 0, 0);
-               abx500_chargalg_stop_safety_timer(di);
-               abx500_chargalg_stop_maintenance_timer(di);
-               di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               di->maintenance_chg = false;
-               abx500_chargalg_state_to(di, STATE_SUSPENDED);
-               power_supply_changed(di->chargalg_psy);
-               /* Intentional fallthrough */
-
-       case STATE_SUSPENDED:
-               /* CHARGING is suspended */
-               break;
-
-       case STATE_BATT_REMOVED_INIT:
-               abx500_chargalg_stop_charging(di);
-               abx500_chargalg_state_to(di, STATE_BATT_REMOVED);
-               /* Intentional fallthrough */
-
-       case STATE_BATT_REMOVED:
-               if (!di->events.batt_rem)
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-
-       case STATE_HW_TEMP_PROTECT_INIT:
-               abx500_chargalg_stop_charging(di);
-               abx500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
-               /* Intentional fallthrough */
-
-       case STATE_HW_TEMP_PROTECT:
-               if (!di->events.main_thermal_prot &&
-                               !di->events.usb_thermal_prot)
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-
-       case STATE_OVV_PROTECT_INIT:
-               abx500_chargalg_stop_charging(di);
-               abx500_chargalg_state_to(di, STATE_OVV_PROTECT);
-               /* Intentional fallthrough */
-
-       case STATE_OVV_PROTECT:
-               if (!di->events.vbus_ovv &&
-                               !di->events.main_ovv &&
-                               !di->events.batt_ovv &&
-                               di->chg_info.usb_chg_ok &&
-                               di->chg_info.ac_chg_ok)
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-
-       case STATE_CHG_NOT_OK_INIT:
-               abx500_chargalg_stop_charging(di);
-               abx500_chargalg_state_to(di, STATE_CHG_NOT_OK);
-               /* Intentional fallthrough */
-
-       case STATE_CHG_NOT_OK:
-               if (!di->events.mainextchnotok &&
-                               !di->events.usbchargernotok)
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-
-       case STATE_SAFETY_TIMER_EXPIRED_INIT:
-               abx500_chargalg_stop_charging(di);
-               abx500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
-               /* Intentional fallthrough */
-
-       case STATE_SAFETY_TIMER_EXPIRED:
-               /* We exit this state when charger is removed */
-               break;
-
-       case STATE_NORMAL_INIT:
-               if ((di->chg_info.charger_type & USB_CHG) &&
-                               di->usb_chg->power_path) {
-                       if (di->batt_data.volt >
-                           (di->bm->fg_params->lowbat_threshold +
-                            BAT_PLUS_MARGIN)) {
-                               ab8540_chargalg_usb_pre_chg_en(di, false);
-                               ab8540_chargalg_usb_pp_en(di, false);
-                       } else {
-                               ab8540_chargalg_usb_pp_en(di, true);
-                               ab8540_chargalg_usb_pre_chg_en(di, true);
-                               abx500_chargalg_state_to(di,
-                                       STATE_USB_PP_PRE_CHARGE);
-                               break;
-                       }
-               }
-
-               if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW)
-                       abx500_chargalg_stop_charging(di);
-               else {
-                       curr_step_lvl = di->bm->bat_type[
-                               di->bm->batt_id].normal_cur_lvl
-                               * di->curr_status.curr_step
-                               / CHARGALG_CURR_STEP_HIGH;
-                       abx500_chargalg_start_charging(di,
-                               di->bm->bat_type[di->bm->batt_id]
-                               .normal_vol_lvl, curr_step_lvl);
-               }
-
-               abx500_chargalg_state_to(di, STATE_NORMAL);
-               abx500_chargalg_start_safety_timer(di);
-               abx500_chargalg_stop_maintenance_timer(di);
-               init_maxim_chg_curr(di);
-               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
-               di->eoc_cnt = 0;
-               di->maintenance_chg = false;
-               power_supply_changed(di->chargalg_psy);
-
-               break;
-
-       case STATE_USB_PP_PRE_CHARGE:
-               if (di->batt_data.volt >
-                       (di->bm->fg_params->lowbat_threshold +
-                       BAT_PLUS_MARGIN))
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-
-       case STATE_NORMAL:
-               handle_maxim_chg_curr(di);
-               if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
-                       di->maintenance_chg) {
-                       if (di->bm->no_maintenance)
-                               abx500_chargalg_state_to(di,
-                                       STATE_WAIT_FOR_RECHARGE_INIT);
-                       else
-                               abx500_chargalg_state_to(di,
-                                       STATE_MAINTENANCE_A_INIT);
-               }
-               break;
-
-       /* This state will be used when the maintenance state is disabled */
-       case STATE_WAIT_FOR_RECHARGE_INIT:
-               abx500_chargalg_hold_charging(di);
-               abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
-               /* Intentional fallthrough */
-
-       case STATE_WAIT_FOR_RECHARGE:
-               if (di->batt_data.percent <=
-                   di->bm->bat_type[di->bm->batt_id].
-                   recharge_cap)
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-
-       case STATE_MAINTENANCE_A_INIT:
-               abx500_chargalg_stop_safety_timer(di);
-               abx500_chargalg_start_maintenance_timer(di,
-                       di->bm->bat_type[
-                               di->bm->batt_id].maint_a_chg_timer_h);
-               abx500_chargalg_start_charging(di,
-                       di->bm->bat_type[
-                               di->bm->batt_id].maint_a_vol_lvl,
-                       di->bm->bat_type[
-                               di->bm->batt_id].maint_a_cur_lvl);
-               abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
-               power_supply_changed(di->chargalg_psy);
-               /* Intentional fallthrough*/
-
-       case STATE_MAINTENANCE_A:
-               if (di->events.maintenance_timer_expired) {
-                       abx500_chargalg_stop_maintenance_timer(di);
-                       abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
-               }
-               break;
-
-       case STATE_MAINTENANCE_B_INIT:
-               abx500_chargalg_start_maintenance_timer(di,
-                       di->bm->bat_type[
-                               di->bm->batt_id].maint_b_chg_timer_h);
-               abx500_chargalg_start_charging(di,
-                       di->bm->bat_type[
-                               di->bm->batt_id].maint_b_vol_lvl,
-                       di->bm->bat_type[
-                               di->bm->batt_id].maint_b_cur_lvl);
-               abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
-               power_supply_changed(di->chargalg_psy);
-               /* Intentional fallthrough*/
-
-       case STATE_MAINTENANCE_B:
-               if (di->events.maintenance_timer_expired) {
-                       abx500_chargalg_stop_maintenance_timer(di);
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               }
-               break;
-
-       case STATE_TEMP_LOWHIGH_INIT:
-               abx500_chargalg_start_charging(di,
-                       di->bm->bat_type[
-                               di->bm->batt_id].low_high_vol_lvl,
-                       di->bm->bat_type[
-                               di->bm->batt_id].low_high_cur_lvl);
-               abx500_chargalg_stop_maintenance_timer(di);
-               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
-               abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
-               power_supply_changed(di->chargalg_psy);
-               /* Intentional fallthrough */
-
-       case STATE_TEMP_LOWHIGH:
-               if (!di->events.btemp_lowhigh)
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-
-       case STATE_WD_EXPIRED_INIT:
-               abx500_chargalg_stop_charging(di);
-               abx500_chargalg_state_to(di, STATE_WD_EXPIRED);
-               /* Intentional fallthrough */
-
-       case STATE_WD_EXPIRED:
-               if (!di->events.ac_wd_expired &&
-                               !di->events.usb_wd_expired)
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-
-       case STATE_TEMP_UNDEROVER_INIT:
-               abx500_chargalg_stop_charging(di);
-               abx500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
-               /* Intentional fallthrough */
-
-       case STATE_TEMP_UNDEROVER:
-               if (!di->events.btemp_underover)
-                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-               break;
-       }
-
-       /* Start charging directly if the new state is a charge state */
-       if (di->charge_state == STATE_NORMAL_INIT ||
-                       di->charge_state == STATE_MAINTENANCE_A_INIT ||
-                       di->charge_state == STATE_MAINTENANCE_B_INIT)
-               queue_work(di->chargalg_wq, &di->chargalg_work);
-}
-
-/**
- * abx500_chargalg_periodic_work() - Periodic work for the algorithm
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for the charging algorithm
- */
-static void abx500_chargalg_periodic_work(struct work_struct *work)
-{
-       struct abx500_chargalg *di = container_of(work,
-               struct abx500_chargalg, chargalg_periodic_work.work);
-
-       abx500_chargalg_algorithm(di);
-
-       /*
-        * If a charger is connected then the battery has to be monitored
-        * frequently, else the work can be delayed.
-        */
-       if (di->chg_info.conn_chg)
-               queue_delayed_work(di->chargalg_wq,
-                       &di->chargalg_periodic_work,
-                       di->bm->interval_charging * HZ);
-       else
-               queue_delayed_work(di->chargalg_wq,
-                       &di->chargalg_periodic_work,
-                       di->bm->interval_not_charging * HZ);
-}
-
-/**
- * abx500_chargalg_wd_work() - periodic work to kick the charger watchdog
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for kicking the charger watchdog
- */
-static void abx500_chargalg_wd_work(struct work_struct *work)
-{
-       int ret;
-       struct abx500_chargalg *di = container_of(work,
-               struct abx500_chargalg, chargalg_wd_work.work);
-
-       dev_dbg(di->dev, "abx500_chargalg_wd_work\n");
-
-       ret = abx500_chargalg_kick_watchdog(di);
-       if (ret < 0)
-               dev_err(di->dev, "failed to kick watchdog\n");
-
-       queue_delayed_work(di->chargalg_wq,
-               &di->chargalg_wd_work, CHG_WD_INTERVAL);
-}
-
-/**
- * abx500_chargalg_work() - Work to run the charging algorithm instantly
- * @work:      pointer to the work_struct structure
- *
- * Work queue function for calling the charging algorithm
- */
-static void abx500_chargalg_work(struct work_struct *work)
-{
-       struct abx500_chargalg *di = container_of(work,
-               struct abx500_chargalg, chargalg_work);
-
-       abx500_chargalg_algorithm(di);
-}
-
-/**
- * abx500_chargalg_get_property() - get the chargalg properties
- * @psy:       pointer to the power_supply structure
- * @psp:       pointer to the power_supply_property structure
- * @val:       pointer to the power_supply_propval union
- *
- * This function gets called when an application tries to get the
- * chargalg properties by reading the sysfs files.
- * status:     charging/discharging/full/unknown
- * health:     health of the battery
- * Returns error code in case of failure else 0 on success
- */
-static int abx500_chargalg_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       struct abx500_chargalg *di = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = di->charge_status;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (di->events.batt_ovv) {
-                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               } else if (di->events.btemp_underover) {
-                       if (di->batt_data.temp <= di->bm->temp_under)
-                               val->intval = POWER_SUPPLY_HEALTH_COLD;
-                       else
-                               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               } else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED ||
-                          di->charge_state == STATE_SAFETY_TIMER_EXPIRED_INIT) {
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               } else {
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-/* Exposure to the sysfs interface */
-
-static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di,
-                                             char *buf)
-{
-       return sprintf(buf, "%d\n", di->curr_status.curr_step);
-}
-
-static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
-                                              const char *buf, size_t length)
-{
-       long int param;
-       int ret;
-
-       ret = kstrtol(buf, 10, &param);
-       if (ret < 0)
-               return ret;
-
-       di->curr_status.curr_step = param;
-       if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW &&
-               di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) {
-               di->curr_status.curr_step_change = true;
-               queue_work(di->chargalg_wq, &di->chargalg_work);
-       } else
-               dev_info(di->dev, "Wrong current step\n"
-                       "Enter 0. Disable AC/USB Charging\n"
-                       "1--100. Set AC/USB charging current step\n"
-                       "100. Enable AC/USB Charging\n");
-
-       return strlen(buf);
-}
-
-
-static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
-                                      char *buf)
-{
-       return sprintf(buf, "%d\n",
-                      di->susp_status.ac_suspended &&
-                      di->susp_status.usb_suspended);
-}
-
-static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
-       const char *buf, size_t length)
-{
-       long int param;
-       int ac_usb;
-       int ret;
-
-       ret = kstrtol(buf, 10, &param);
-       if (ret < 0)
-               return ret;
-
-       ac_usb = param;
-       switch (ac_usb) {
-       case 0:
-               /* Disable charging */
-               di->susp_status.ac_suspended = true;
-               di->susp_status.usb_suspended = true;
-               di->susp_status.suspended_change = true;
-               /* Trigger a state change */
-               queue_work(di->chargalg_wq,
-                       &di->chargalg_work);
-               break;
-       case 1:
-               /* Enable AC Charging */
-               di->susp_status.ac_suspended = false;
-               di->susp_status.suspended_change = true;
-               /* Trigger a state change */
-               queue_work(di->chargalg_wq,
-                       &di->chargalg_work);
-               break;
-       case 2:
-               /* Enable USB charging */
-               di->susp_status.usb_suspended = false;
-               di->susp_status.suspended_change = true;
-               /* Trigger a state change */
-               queue_work(di->chargalg_wq,
-                       &di->chargalg_work);
-               break;
-       default:
-               dev_info(di->dev, "Wrong input\n"
-                       "Enter 0. Disable AC/USB Charging\n"
-                       "1. Enable AC charging\n"
-                       "2. Enable USB Charging\n");
-       };
-       return strlen(buf);
-}
-
-static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger =
-       __ATTR(chargalg, 0644, abx500_chargalg_en_show,
-                               abx500_chargalg_en_store);
-
-static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step =
-       __ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show,
-                                       abx500_chargalg_curr_step_store);
-
-static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
-       struct attribute *attr, char *buf)
-{
-       struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
-               struct abx500_chargalg_sysfs_entry, attr);
-
-       struct abx500_chargalg *di = container_of(kobj,
-               struct abx500_chargalg, chargalg_kobject);
-
-       if (!entry->show)
-               return -EIO;
-
-       return entry->show(di, buf);
-}
-
-static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
-       struct attribute *attr, const char *buf, size_t length)
-{
-       struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
-               struct abx500_chargalg_sysfs_entry, attr);
-
-       struct abx500_chargalg *di = container_of(kobj,
-               struct abx500_chargalg, chargalg_kobject);
-
-       if (!entry->store)
-               return -EIO;
-
-       return entry->store(di, buf, length);
-}
-
-static struct attribute *abx500_chargalg_chg[] = {
-       &abx500_chargalg_en_charger.attr,
-       &abx500_chargalg_curr_step.attr,
-       NULL,
-};
-
-static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
-       .show = abx500_chargalg_sysfs_show,
-       .store = abx500_chargalg_sysfs_charger,
-};
-
-static struct kobj_type abx500_chargalg_ktype = {
-       .sysfs_ops = &abx500_chargalg_sysfs_ops,
-       .default_attrs = abx500_chargalg_chg,
-};
-
-/**
- * abx500_chargalg_sysfs_exit() - de-init of sysfs entry
- * @di:                pointer to the struct abx500_chargalg
- *
- * This function removes the entry in sysfs.
- */
-static void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di)
-{
-       kobject_del(&di->chargalg_kobject);
-}
-
-/**
- * abx500_chargalg_sysfs_init() - init of sysfs entry
- * @di:                pointer to the struct abx500_chargalg
- *
- * This function adds an entry in sysfs.
- * Returns error code in case of failure else 0(on success)
- */
-static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di)
-{
-       int ret = 0;
-
-       ret = kobject_init_and_add(&di->chargalg_kobject,
-               &abx500_chargalg_ktype,
-               NULL, "abx500_chargalg");
-       if (ret < 0)
-               dev_err(di->dev, "failed to create sysfs entry\n");
-
-       return ret;
-}
-/* Exposure to the sysfs interface <<END>> */
-
-#if defined(CONFIG_PM)
-static int abx500_chargalg_resume(struct platform_device *pdev)
-{
-       struct abx500_chargalg *di = platform_get_drvdata(pdev);
-
-       /* Kick charger watchdog if charging (any charger online) */
-       if (di->chg_info.online_chg)
-               queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
-
-       /*
-        * Run the charging algorithm directly to be sure we don't
-        * do it too seldom
-        */
-       queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
-
-       return 0;
-}
-
-static int abx500_chargalg_suspend(struct platform_device *pdev,
-       pm_message_t state)
-{
-       struct abx500_chargalg *di = platform_get_drvdata(pdev);
-
-       if (di->chg_info.online_chg)
-               cancel_delayed_work_sync(&di->chargalg_wd_work);
-
-       cancel_delayed_work_sync(&di->chargalg_periodic_work);
-
-       return 0;
-}
-#else
-#define abx500_chargalg_suspend      NULL
-#define abx500_chargalg_resume       NULL
-#endif
-
-static int abx500_chargalg_remove(struct platform_device *pdev)
-{
-       struct abx500_chargalg *di = platform_get_drvdata(pdev);
-
-       /* sysfs interface to enable/disbale charging from user space */
-       abx500_chargalg_sysfs_exit(di);
-
-       hrtimer_cancel(&di->safety_timer);
-       hrtimer_cancel(&di->maintenance_timer);
-
-       cancel_delayed_work_sync(&di->chargalg_periodic_work);
-       cancel_delayed_work_sync(&di->chargalg_wd_work);
-       cancel_work_sync(&di->chargalg_work);
-
-       /* Delete the work queue */
-       destroy_workqueue(di->chargalg_wq);
-
-       power_supply_unregister(di->chargalg_psy);
-
-       return 0;
-}
-
-static char *supply_interface[] = {
-       "ab8500_fg",
-};
-
-static const struct power_supply_desc abx500_chargalg_desc = {
-       .name                   = "abx500_chargalg",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = abx500_chargalg_props,
-       .num_properties         = ARRAY_SIZE(abx500_chargalg_props),
-       .get_property           = abx500_chargalg_get_property,
-       .external_power_changed = abx500_chargalg_external_power_changed,
-};
-
-static int abx500_chargalg_probe(struct platform_device *pdev)
-{
-       struct device_node *np = pdev->dev.of_node;
-       struct abx500_bm_data *plat = pdev->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       struct abx500_chargalg *di;
-       int ret = 0;
-
-       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di) {
-               dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__);
-               return -ENOMEM;
-       }
-
-       if (!plat) {
-               dev_err(&pdev->dev, "no battery management data supplied\n");
-               return -EINVAL;
-       }
-       di->bm = plat;
-
-       if (np) {
-               ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
-               if (ret) {
-                       dev_err(&pdev->dev, "failed to get battery information\n");
-                       return ret;
-               }
-       }
-
-       /* get device struct and parent */
-       di->dev = &pdev->dev;
-       di->parent = dev_get_drvdata(pdev->dev.parent);
-
-       psy_cfg.supplied_to = supply_interface;
-       psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
-       psy_cfg.drv_data = di;
-
-       /* Initilialize safety timer */
-       hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
-       di->safety_timer.function = abx500_chargalg_safety_timer_expired;
-
-       /* Initilialize maintenance timer */
-       hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
-       di->maintenance_timer.function =
-               abx500_chargalg_maintenance_timer_expired;
-
-       /* Create a work queue for the chargalg */
-       di->chargalg_wq =
-               create_singlethread_workqueue("abx500_chargalg_wq");
-       if (di->chargalg_wq == NULL) {
-               dev_err(di->dev, "failed to create work queue\n");
-               return -ENOMEM;
-       }
-
-       /* Init work for chargalg */
-       INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work,
-               abx500_chargalg_periodic_work);
-       INIT_DEFERRABLE_WORK(&di->chargalg_wd_work,
-               abx500_chargalg_wd_work);
-
-       /* Init work for chargalg */
-       INIT_WORK(&di->chargalg_work, abx500_chargalg_work);
-
-       /* To detect charger at startup */
-       di->chg_info.prev_conn_chg = -1;
-
-       /* Register chargalg power supply class */
-       di->chargalg_psy = power_supply_register(di->dev, &abx500_chargalg_desc,
-                                                &psy_cfg);
-       if (IS_ERR(di->chargalg_psy)) {
-               dev_err(di->dev, "failed to register chargalg psy\n");
-               ret = PTR_ERR(di->chargalg_psy);
-               goto free_chargalg_wq;
-       }
-
-       platform_set_drvdata(pdev, di);
-
-       /* sysfs interface to enable/disable charging from user space */
-       ret = abx500_chargalg_sysfs_init(di);
-       if (ret) {
-               dev_err(di->dev, "failed to create sysfs entry\n");
-               goto free_psy;
-       }
-       di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH;
-
-       /* Run the charging algorithm */
-       queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
-
-       dev_info(di->dev, "probe success\n");
-       return ret;
-
-free_psy:
-       power_supply_unregister(di->chargalg_psy);
-free_chargalg_wq:
-       destroy_workqueue(di->chargalg_wq);
-       return ret;
-}
-
-static const struct of_device_id ab8500_chargalg_match[] = {
-       { .compatible = "stericsson,ab8500-chargalg", },
-       { },
-};
-
-static struct platform_driver abx500_chargalg_driver = {
-       .probe = abx500_chargalg_probe,
-       .remove = abx500_chargalg_remove,
-       .suspend = abx500_chargalg_suspend,
-       .resume = abx500_chargalg_resume,
-       .driver = {
-               .name = "ab8500-chargalg",
-               .of_match_table = ab8500_chargalg_match,
-       },
-};
-
-module_platform_driver(abx500_chargalg_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
-MODULE_ALIAS("platform:abx500-chargalg");
-MODULE_DESCRIPTION("abx500 battery charging algorithm");
diff --git a/drivers/power/act8945a_charger.c b/drivers/power/act8945a_charger.c
deleted file mode 100644 (file)
index b5c00e4..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Power supply driver for the Active-semi ACT8945A PMIC
- *
- * Copyright (C) 2015 Atmel Corporation
- *
- * Author: Wenyou Yang <wenyou.yang@atmel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/regmap.h>
-
-static const char *act8945a_charger_model = "ACT8945A";
-static const char *act8945a_charger_manufacturer = "Active-semi";
-
-/**
- * ACT8945A Charger Register Map
- */
-
-/* 0x70: Reserved */
-#define ACT8945A_APCH_CFG              0x71
-#define ACT8945A_APCH_STATUS           0x78
-#define ACT8945A_APCH_CTRL             0x79
-#define ACT8945A_APCH_STATE            0x7A
-
-/* ACT8945A_APCH_CFG */
-#define APCH_CFG_OVPSET                        (0x3 << 0)
-#define APCH_CFG_OVPSET_6V6            (0x0 << 0)
-#define APCH_CFG_OVPSET_7V             (0x1 << 0)
-#define APCH_CFG_OVPSET_7V5            (0x2 << 0)
-#define APCH_CFG_OVPSET_8V             (0x3 << 0)
-#define APCH_CFG_PRETIMO               (0x3 << 2)
-#define APCH_CFG_PRETIMO_40_MIN                (0x0 << 2)
-#define APCH_CFG_PRETIMO_60_MIN                (0x1 << 2)
-#define APCH_CFG_PRETIMO_80_MIN                (0x2 << 2)
-#define APCH_CFG_PRETIMO_DISABLED      (0x3 << 2)
-#define APCH_CFG_TOTTIMO               (0x3 << 4)
-#define APCH_CFG_TOTTIMO_3_HOUR                (0x0 << 4)
-#define APCH_CFG_TOTTIMO_4_HOUR                (0x1 << 4)
-#define APCH_CFG_TOTTIMO_5_HOUR                (0x2 << 4)
-#define APCH_CFG_TOTTIMO_DISABLED      (0x3 << 4)
-#define APCH_CFG_SUSCHG                        (0x1 << 7)
-
-#define APCH_STATUS_CHGDAT             BIT(0)
-#define APCH_STATUS_INDAT              BIT(1)
-#define APCH_STATUS_TEMPDAT            BIT(2)
-#define APCH_STATUS_TIMRDAT            BIT(3)
-#define APCH_STATUS_CHGSTAT            BIT(4)
-#define APCH_STATUS_INSTAT             BIT(5)
-#define APCH_STATUS_TEMPSTAT           BIT(6)
-#define APCH_STATUS_TIMRSTAT           BIT(7)
-
-#define APCH_CTRL_CHGEOCOUT            BIT(0)
-#define APCH_CTRL_INDIS                        BIT(1)
-#define APCH_CTRL_TEMPOUT              BIT(2)
-#define APCH_CTRL_TIMRPRE              BIT(3)
-#define APCH_CTRL_CHGEOCIN             BIT(4)
-#define APCH_CTRL_INCON                        BIT(5)
-#define APCH_CTRL_TEMPIN               BIT(6)
-#define APCH_CTRL_TIMRTOT              BIT(7)
-
-#define APCH_STATE_ACINSTAT            (0x1 << 1)
-#define APCH_STATE_CSTATE              (0x3 << 4)
-#define APCH_STATE_CSTATE_SHIFT                4
-#define APCH_STATE_CSTATE_DISABLED     0x00
-#define APCH_STATE_CSTATE_EOC          0x01
-#define APCH_STATE_CSTATE_FAST         0x02
-#define APCH_STATE_CSTATE_PRE          0x03
-
-struct act8945a_charger {
-       struct regmap *regmap;
-       bool battery_temperature;
-};
-
-static int act8945a_get_charger_state(struct regmap *regmap, int *val)
-{
-       int ret;
-       unsigned int status, state;
-
-       ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
-       if (ret < 0)
-               return ret;
-
-       ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
-       if (ret < 0)
-               return ret;
-
-       state &= APCH_STATE_CSTATE;
-       state >>= APCH_STATE_CSTATE_SHIFT;
-
-       if (state == APCH_STATE_CSTATE_EOC) {
-               if (status & APCH_STATUS_CHGDAT)
-                       *val = POWER_SUPPLY_STATUS_FULL;
-               else
-                       *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       } else if ((state == APCH_STATE_CSTATE_FAST) ||
-                  (state == APCH_STATE_CSTATE_PRE)) {
-               *val = POWER_SUPPLY_STATUS_CHARGING;
-       } else {
-               *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       }
-
-       return 0;
-}
-
-static int act8945a_get_charge_type(struct regmap *regmap, int *val)
-{
-       int ret;
-       unsigned int state;
-
-       ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
-       if (ret < 0)
-               return ret;
-
-       state &= APCH_STATE_CSTATE;
-       state >>= APCH_STATE_CSTATE_SHIFT;
-
-       switch (state) {
-       case APCH_STATE_CSTATE_PRE:
-               *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-               break;
-       case APCH_STATE_CSTATE_FAST:
-               *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
-               break;
-       case APCH_STATE_CSTATE_EOC:
-       case APCH_STATE_CSTATE_DISABLED:
-       default:
-               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
-       }
-
-       return 0;
-}
-
-static int act8945a_get_battery_health(struct act8945a_charger *charger,
-                                      struct regmap *regmap, int *val)
-{
-       int ret;
-       unsigned int status;
-
-       ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
-       if (ret < 0)
-               return ret;
-
-       if (charger->battery_temperature && !(status & APCH_STATUS_TEMPDAT))
-               *val = POWER_SUPPLY_HEALTH_OVERHEAT;
-       else if (!(status & APCH_STATUS_INDAT))
-               *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-       else if (status & APCH_STATUS_TIMRDAT)
-               *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
-       else
-               *val = POWER_SUPPLY_HEALTH_GOOD;
-
-       return 0;
-}
-
-static enum power_supply_property act8945a_charger_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER
-};
-
-static int act8945a_charger_get_property(struct power_supply *psy,
-                                        enum power_supply_property prop,
-                                        union power_supply_propval *val)
-{
-       struct act8945a_charger *charger = power_supply_get_drvdata(psy);
-       struct regmap *regmap = charger->regmap;
-       int ret = 0;
-
-       switch (prop) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = act8945a_get_charger_state(regmap, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               ret = act8945a_get_charge_type(regmap, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = act8945a_get_battery_health(charger,
-                                                 regmap, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = act8945a_charger_model;
-               break;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = act8945a_charger_manufacturer;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return ret;
-}
-
-static const struct power_supply_desc act8945a_charger_desc = {
-       .name           = "act8945a-charger",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property   = act8945a_charger_get_property,
-       .properties     = act8945a_charger_props,
-       .num_properties = ARRAY_SIZE(act8945a_charger_props),
-};
-
-#define DEFAULT_TOTAL_TIME_OUT         3
-#define DEFAULT_PRE_TIME_OUT           40
-#define DEFAULT_INPUT_OVP_THRESHOLD    6600
-
-static int act8945a_charger_config(struct device *dev,
-                                  struct act8945a_charger *charger)
-{
-       struct device_node *np = dev->of_node;
-       enum of_gpio_flags flags;
-       struct regmap *regmap = charger->regmap;
-
-       u32 total_time_out;
-       u32 pre_time_out;
-       u32 input_voltage_threshold;
-       int chglev_pin;
-
-       unsigned int value = 0;
-
-       if (!np) {
-               dev_err(dev, "no charger of node\n");
-               return -EINVAL;
-       }
-
-       charger->battery_temperature = of_property_read_bool(np,
-                               "active-semi,check-battery-temperature");
-
-       chglev_pin = of_get_named_gpio_flags(np,
-                               "active-semi,chglev-gpios", 0, &flags);
-
-       if (gpio_is_valid(chglev_pin)) {
-               gpio_set_value(chglev_pin,
-                              ((flags == OF_GPIO_ACTIVE_LOW) ? 0 : 1));
-       }
-
-       if (of_property_read_u32(np,
-                                "active-semi,input-voltage-threshold-microvolt",
-                                &input_voltage_threshold))
-               input_voltage_threshold = DEFAULT_INPUT_OVP_THRESHOLD;
-
-       if (of_property_read_u32(np,
-                                "active-semi,precondition-timeout",
-                                &pre_time_out))
-               pre_time_out = DEFAULT_PRE_TIME_OUT;
-
-       if (of_property_read_u32(np, "active-semi,total-timeout",
-                                &total_time_out))
-               total_time_out = DEFAULT_TOTAL_TIME_OUT;
-
-       switch (input_voltage_threshold) {
-       case 8000:
-               value |= APCH_CFG_OVPSET_8V;
-               break;
-       case 7500:
-               value |= APCH_CFG_OVPSET_7V5;
-               break;
-       case 7000:
-               value |= APCH_CFG_OVPSET_7V;
-               break;
-       case 6600:
-       default:
-               value |= APCH_CFG_OVPSET_6V6;
-               break;
-       }
-
-       switch (pre_time_out) {
-       case 60:
-               value |= APCH_CFG_PRETIMO_60_MIN;
-               break;
-       case 80:
-               value |= APCH_CFG_PRETIMO_80_MIN;
-               break;
-       case 0:
-               value |= APCH_CFG_PRETIMO_DISABLED;
-               break;
-       case 40:
-       default:
-               value |= APCH_CFG_PRETIMO_40_MIN;
-               break;
-       }
-
-       switch (total_time_out) {
-       case 4:
-               value |= APCH_CFG_TOTTIMO_4_HOUR;
-               break;
-       case 5:
-               value |= APCH_CFG_TOTTIMO_5_HOUR;
-               break;
-       case 0:
-               value |= APCH_CFG_TOTTIMO_DISABLED;
-               break;
-       case 3:
-       default:
-               value |= APCH_CFG_TOTTIMO_3_HOUR;
-               break;
-       }
-
-       return regmap_write(regmap, ACT8945A_APCH_CFG, value);
-}
-
-static int act8945a_charger_probe(struct platform_device *pdev)
-{
-       struct act8945a_charger *charger;
-       struct power_supply *psy;
-       struct power_supply_config psy_cfg = {};
-       int ret;
-
-       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
-       if (!charger)
-               return -ENOMEM;
-
-       charger->regmap = dev_get_regmap(pdev->dev.parent, NULL);
-       if (!charger->regmap) {
-               dev_err(&pdev->dev, "Parent did not provide regmap\n");
-               return -EINVAL;
-       }
-
-       ret = act8945a_charger_config(pdev->dev.parent, charger);
-       if (ret)
-               return ret;
-
-       psy_cfg.of_node = pdev->dev.parent->of_node;
-       psy_cfg.drv_data = charger;
-
-       psy = devm_power_supply_register(&pdev->dev,
-                                        &act8945a_charger_desc,
-                                        &psy_cfg);
-       if (IS_ERR(psy)) {
-               dev_err(&pdev->dev, "failed to register power supply\n");
-               return PTR_ERR(psy);
-       }
-
-       return 0;
-}
-
-static struct platform_driver act8945a_charger_driver = {
-       .driver = {
-               .name = "act8945a-charger",
-       },
-       .probe  = act8945a_charger_probe,
-};
-module_platform_driver(act8945a_charger_driver);
-
-MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");
-MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c
deleted file mode 100644 (file)
index 9d1a7fb..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * Copyright Â© 2007 Anton Vorontsov <cbou@mail.ru>
- * Copyright Â© 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
- *
- * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
- *
- * Use consistent with the GNU GPL is permitted,
- * provided that this copyright notice is
- * preserved in its entirety in all copies and derived works.
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/power_supply.h>
-#include <linux/apm-emulation.h>
-
-
-#define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
-                        POWER_SUPPLY_PROP_##prop, val))
-
-#define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \
-                                                        prop, val))
-
-#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
-
-static DEFINE_MUTEX(apm_mutex);
-static struct power_supply *main_battery;
-
-enum apm_source {
-       SOURCE_ENERGY,
-       SOURCE_CHARGE,
-       SOURCE_VOLTAGE,
-};
-
-struct find_bat_param {
-       struct power_supply *main;
-       struct power_supply *bat;
-       struct power_supply *max_charge_bat;
-       struct power_supply *max_energy_bat;
-       union power_supply_propval full;
-       int max_charge;
-       int max_energy;
-};
-
-static int __find_main_battery(struct device *dev, void *data)
-{
-       struct find_bat_param *bp = (struct find_bat_param *)data;
-
-       bp->bat = dev_get_drvdata(dev);
-
-       if (bp->bat->desc->use_for_apm) {
-               /* nice, we explicitly asked to report this battery. */
-               bp->main = bp->bat;
-               return 1;
-       }
-
-       if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
-                       !PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
-               if (bp->full.intval > bp->max_charge) {
-                       bp->max_charge_bat = bp->bat;
-                       bp->max_charge = bp->full.intval;
-               }
-       } else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
-                       !PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
-               if (bp->full.intval > bp->max_energy) {
-                       bp->max_energy_bat = bp->bat;
-                       bp->max_energy = bp->full.intval;
-               }
-       }
-       return 0;
-}
-
-static void find_main_battery(void)
-{
-       struct find_bat_param bp;
-       int error;
-
-       memset(&bp, 0, sizeof(struct find_bat_param));
-       main_battery = NULL;
-       bp.main = main_battery;
-
-       error = class_for_each_device(power_supply_class, NULL, &bp,
-                                     __find_main_battery);
-       if (error) {
-               main_battery = bp.main;
-               return;
-       }
-
-       if ((bp.max_energy_bat && bp.max_charge_bat) &&
-                       (bp.max_energy_bat != bp.max_charge_bat)) {
-               /* try guess battery with more capacity */
-               if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
-                             &bp.full)) {
-                       if (bp.max_energy > bp.max_charge * bp.full.intval)
-                               main_battery = bp.max_energy_bat;
-                       else
-                               main_battery = bp.max_charge_bat;
-               } else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
-                                                                 &bp.full)) {
-                       if (bp.max_charge > bp.max_energy / bp.full.intval)
-                               main_battery = bp.max_charge_bat;
-                       else
-                               main_battery = bp.max_energy_bat;
-               } else {
-                       /* give up, choice any */
-                       main_battery = bp.max_energy_bat;
-               }
-       } else if (bp.max_charge_bat) {
-               main_battery = bp.max_charge_bat;
-       } else if (bp.max_energy_bat) {
-               main_battery = bp.max_energy_bat;
-       } else {
-               /* give up, try the last if any */
-               main_battery = bp.bat;
-       }
-}
-
-static int do_calculate_time(int status, enum apm_source source)
-{
-       union power_supply_propval full;
-       union power_supply_propval empty;
-       union power_supply_propval cur;
-       union power_supply_propval I;
-       enum power_supply_property full_prop;
-       enum power_supply_property full_design_prop;
-       enum power_supply_property empty_prop;
-       enum power_supply_property empty_design_prop;
-       enum power_supply_property cur_avg_prop;
-       enum power_supply_property cur_now_prop;
-
-       if (MPSY_PROP(CURRENT_AVG, &I)) {
-               /* if battery can't report average value, use momentary */
-               if (MPSY_PROP(CURRENT_NOW, &I))
-                       return -1;
-       }
-
-       if (!I.intval)
-               return 0;
-
-       switch (source) {
-       case SOURCE_CHARGE:
-               full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
-               full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
-               empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
-               empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
-               cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
-               cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
-               break;
-       case SOURCE_ENERGY:
-               full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
-               full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
-               empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
-               empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
-               cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
-               cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
-               break;
-       case SOURCE_VOLTAGE:
-               full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
-               full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
-               empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
-               empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
-               cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
-               cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
-               break;
-       default:
-               printk(KERN_ERR "Unsupported source: %d\n", source);
-               return -1;
-       }
-
-       if (_MPSY_PROP(full_prop, &full)) {
-               /* if battery can't report this property, use design value */
-               if (_MPSY_PROP(full_design_prop, &full))
-                       return -1;
-       }
-
-       if (_MPSY_PROP(empty_prop, &empty)) {
-               /* if battery can't report this property, use design value */
-               if (_MPSY_PROP(empty_design_prop, &empty))
-                       empty.intval = 0;
-       }
-
-       if (_MPSY_PROP(cur_avg_prop, &cur)) {
-               /* if battery can't report average value, use momentary */
-               if (_MPSY_PROP(cur_now_prop, &cur))
-                       return -1;
-       }
-
-       if (status == POWER_SUPPLY_STATUS_CHARGING)
-               return ((cur.intval - full.intval) * 60L) / I.intval;
-       else
-               return -((cur.intval - empty.intval) * 60L) / I.intval;
-}
-
-static int calculate_time(int status)
-{
-       int time;
-
-       time = do_calculate_time(status, SOURCE_ENERGY);
-       if (time != -1)
-               return time;
-
-       time = do_calculate_time(status, SOURCE_CHARGE);
-       if (time != -1)
-               return time;
-
-       time = do_calculate_time(status, SOURCE_VOLTAGE);
-       if (time != -1)
-               return time;
-
-       return -1;
-}
-
-static int calculate_capacity(enum apm_source source)
-{
-       enum power_supply_property full_prop, empty_prop;
-       enum power_supply_property full_design_prop, empty_design_prop;
-       enum power_supply_property now_prop, avg_prop;
-       union power_supply_propval empty, full, cur;
-       int ret;
-
-       switch (source) {
-       case SOURCE_CHARGE:
-               full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
-               empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
-               full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
-               empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
-               now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
-               avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
-               break;
-       case SOURCE_ENERGY:
-               full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
-               empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
-               full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
-               empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
-               now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
-               avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
-               break;
-       case SOURCE_VOLTAGE:
-               full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
-               empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
-               full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
-               empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
-               now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
-               avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
-               break;
-       default:
-               printk(KERN_ERR "Unsupported source: %d\n", source);
-               return -1;
-       }
-
-       if (_MPSY_PROP(full_prop, &full)) {
-               /* if battery can't report this property, use design value */
-               if (_MPSY_PROP(full_design_prop, &full))
-                       return -1;
-       }
-
-       if (_MPSY_PROP(avg_prop, &cur)) {
-               /* if battery can't report average value, use momentary */
-               if (_MPSY_PROP(now_prop, &cur))
-                       return -1;
-       }
-
-       if (_MPSY_PROP(empty_prop, &empty)) {
-               /* if battery can't report this property, use design value */
-               if (_MPSY_PROP(empty_design_prop, &empty))
-                       empty.intval = 0;
-       }
-
-       if (full.intval - empty.intval)
-               ret =  ((cur.intval - empty.intval) * 100L) /
-                      (full.intval - empty.intval);
-       else
-               return -1;
-
-       if (ret > 100)
-               return 100;
-       else if (ret < 0)
-               return 0;
-
-       return ret;
-}
-
-static void apm_battery_apm_get_power_status(struct apm_power_info *info)
-{
-       union power_supply_propval status;
-       union power_supply_propval capacity, time_to_full, time_to_empty;
-
-       mutex_lock(&apm_mutex);
-       find_main_battery();
-       if (!main_battery) {
-               mutex_unlock(&apm_mutex);
-               return;
-       }
-
-       /* status */
-
-       if (MPSY_PROP(STATUS, &status))
-               status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
-
-       /* ac line status */
-
-       if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
-           (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
-           (status.intval == POWER_SUPPLY_STATUS_FULL))
-               info->ac_line_status = APM_AC_ONLINE;
-       else
-               info->ac_line_status = APM_AC_OFFLINE;
-
-       /* battery life (i.e. capacity, in percents) */
-
-       if (MPSY_PROP(CAPACITY, &capacity) == 0) {
-               info->battery_life = capacity.intval;
-       } else {
-               /* try calculate using energy */
-               info->battery_life = calculate_capacity(SOURCE_ENERGY);
-               /* if failed try calculate using charge instead */
-               if (info->battery_life == -1)
-                       info->battery_life = calculate_capacity(SOURCE_CHARGE);
-               if (info->battery_life == -1)
-                       info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
-       }
-
-       /* charging status */
-
-       if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
-               info->battery_status = APM_BATTERY_STATUS_CHARGING;
-       } else {
-               if (info->battery_life > 50)
-                       info->battery_status = APM_BATTERY_STATUS_HIGH;
-               else if (info->battery_life > 5)
-                       info->battery_status = APM_BATTERY_STATUS_LOW;
-               else
-                       info->battery_status = APM_BATTERY_STATUS_CRITICAL;
-       }
-       info->battery_flag = info->battery_status;
-
-       /* time */
-
-       info->units = APM_UNITS_MINS;
-
-       if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
-               if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
-                               !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
-                       info->time = time_to_full.intval / 60;
-               else
-                       info->time = calculate_time(status.intval);
-       } else {
-               if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
-                             !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
-                       info->time = time_to_empty.intval / 60;
-               else
-                       info->time = calculate_time(status.intval);
-       }
-
-       mutex_unlock(&apm_mutex);
-}
-
-static int __init apm_battery_init(void)
-{
-       printk(KERN_INFO "APM Battery Driver\n");
-
-       apm_get_power_status = apm_battery_apm_get_power_status;
-       return 0;
-}
-
-static void __exit apm_battery_exit(void)
-{
-       apm_get_power_status = NULL;
-}
-
-module_init(apm_battery_init);
-module_exit(apm_battery_exit);
-
-MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
-MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/axp20x_usb_power.c b/drivers/power/axp20x_usb_power.c
deleted file mode 100644 (file)
index 6af6feb..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * AXP20x PMIC USB power supply status driver
- *
- * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
- * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org>
- *
- * 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;  either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/device.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mfd/axp20x.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-
-#define DRVNAME "axp20x-usb-power-supply"
-
-#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
-#define AXP20X_PWR_STATUS_VBUS_USED    BIT(4)
-
-#define AXP20X_USB_STATUS_VBUS_VALID   BIT(2)
-
-#define AXP20X_VBUS_VHOLD_uV(b)                (4000000 + (((b) >> 3) & 7) * 100000)
-#define AXP20X_VBUS_CLIMIT_MASK                3
-#define AXP20X_VBUC_CLIMIT_900mA       0
-#define AXP20X_VBUC_CLIMIT_500mA       1
-#define AXP20X_VBUC_CLIMIT_100mA       2
-#define AXP20X_VBUC_CLIMIT_NONE                3
-
-#define AXP20X_ADC_EN1_VBUS_CURR       BIT(2)
-#define AXP20X_ADC_EN1_VBUS_VOLT       BIT(3)
-
-#define AXP20X_VBUS_MON_VBUS_VALID     BIT(3)
-
-struct axp20x_usb_power {
-       struct device_node *np;
-       struct regmap *regmap;
-       struct power_supply *supply;
-};
-
-static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
-{
-       struct axp20x_usb_power *power = devid;
-
-       power_supply_changed(power->supply);
-
-       return IRQ_HANDLED;
-}
-
-static int axp20x_usb_power_get_property(struct power_supply *psy,
-       enum power_supply_property psp, union power_supply_propval *val)
-{
-       struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
-       unsigned int input, v;
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
-               ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
-               if (ret)
-                       return ret;
-
-               val->intval = AXP20X_VBUS_VHOLD_uV(v);
-               return 0;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = axp20x_read_variable_width(power->regmap,
-                                                AXP20X_VBUS_V_ADC_H, 12);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = ret * 1700; /* 1 step = 1.7 mV */
-               return 0;
-       case POWER_SUPPLY_PROP_CURRENT_MAX:
-               ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
-               if (ret)
-                       return ret;
-
-               switch (v & AXP20X_VBUS_CLIMIT_MASK) {
-               case AXP20X_VBUC_CLIMIT_100mA:
-                       if (of_device_is_compatible(power->np,
-                                       "x-powers,axp202-usb-power-supply")) {
-                               val->intval = 100000;
-                       } else {
-                               val->intval = -1; /* No 100mA limit */
-                       }
-                       break;
-               case AXP20X_VBUC_CLIMIT_500mA:
-                       val->intval = 500000;
-                       break;
-               case AXP20X_VBUC_CLIMIT_900mA:
-                       val->intval = 900000;
-                       break;
-               case AXP20X_VBUC_CLIMIT_NONE:
-                       val->intval = -1;
-                       break;
-               }
-               return 0;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = axp20x_read_variable_width(power->regmap,
-                                                AXP20X_VBUS_I_ADC_H, 12);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = ret * 375; /* 1 step = 0.375 mA */
-               return 0;
-       default:
-               break;
-       }
-
-       /* All the properties below need the input-status reg value */
-       ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input);
-       if (ret)
-               return ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
-                       val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
-                       break;
-               }
-
-               val->intval = POWER_SUPPLY_HEALTH_GOOD;
-
-               if (of_device_is_compatible(power->np,
-                               "x-powers,axp202-usb-power-supply")) {
-                       ret = regmap_read(power->regmap,
-                                         AXP20X_USB_OTG_STATUS, &v);
-                       if (ret)
-                               return ret;
-
-                       if (!(v & AXP20X_USB_STATUS_VBUS_VALID))
-                               val->intval =
-                                       POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               }
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property axp20x_usb_power_properties[] = {
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_MAX,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-};
-
-static enum power_supply_property axp22x_usb_power_properties[] = {
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN,
-       POWER_SUPPLY_PROP_CURRENT_MAX,
-};
-
-static const struct power_supply_desc axp20x_usb_power_desc = {
-       .name = "axp20x-usb",
-       .type = POWER_SUPPLY_TYPE_USB,
-       .properties = axp20x_usb_power_properties,
-       .num_properties = ARRAY_SIZE(axp20x_usb_power_properties),
-       .get_property = axp20x_usb_power_get_property,
-};
-
-static const struct power_supply_desc axp22x_usb_power_desc = {
-       .name = "axp20x-usb",
-       .type = POWER_SUPPLY_TYPE_USB,
-       .properties = axp22x_usb_power_properties,
-       .num_properties = ARRAY_SIZE(axp22x_usb_power_properties),
-       .get_property = axp20x_usb_power_get_property,
-};
-
-static int axp20x_usb_power_probe(struct platform_device *pdev)
-{
-       struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
-       struct power_supply_config psy_cfg = {};
-       struct axp20x_usb_power *power;
-       static const char * const axp20x_irq_names[] = { "VBUS_PLUGIN",
-               "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL };
-       static const char * const axp22x_irq_names[] = {
-               "VBUS_PLUGIN", "VBUS_REMOVAL", NULL };
-       static const char * const *irq_names;
-       const struct power_supply_desc *usb_power_desc;
-       int i, irq, ret;
-
-       if (!of_device_is_available(pdev->dev.of_node))
-               return -ENODEV;
-
-       if (!axp20x) {
-               dev_err(&pdev->dev, "Parent drvdata not set\n");
-               return -EINVAL;
-       }
-
-       power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
-       if (!power)
-               return -ENOMEM;
-
-       power->np = pdev->dev.of_node;
-       power->regmap = axp20x->regmap;
-
-       if (of_device_is_compatible(power->np,
-                       "x-powers,axp202-usb-power-supply")) {
-               /* Enable vbus valid checking */
-               ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
-                                        AXP20X_VBUS_MON_VBUS_VALID,
-                                        AXP20X_VBUS_MON_VBUS_VALID);
-               if (ret)
-                       return ret;
-
-               /* Enable vbus voltage and current measurement */
-               ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
-                       AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT,
-                       AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT);
-               if (ret)
-                       return ret;
-
-               usb_power_desc = &axp20x_usb_power_desc;
-               irq_names = axp20x_irq_names;
-       } else if (of_device_is_compatible(power->np,
-                       "x-powers,axp221-usb-power-supply")) {
-               usb_power_desc = &axp22x_usb_power_desc;
-               irq_names = axp22x_irq_names;
-       } else {
-               dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
-                       axp20x->variant);
-               return -EINVAL;
-       }
-
-       psy_cfg.of_node = pdev->dev.of_node;
-       psy_cfg.drv_data = power;
-
-       power->supply = devm_power_supply_register(&pdev->dev, usb_power_desc,
-                                                  &psy_cfg);
-       if (IS_ERR(power->supply))
-               return PTR_ERR(power->supply);
-
-       /* Request irqs after registering, as irqs may trigger immediately */
-       for (i = 0; irq_names[i]; i++) {
-               irq = platform_get_irq_byname(pdev, irq_names[i]);
-               if (irq < 0) {
-                       dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
-                                irq_names[i], irq);
-                       continue;
-               }
-               irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
-               ret = devm_request_any_context_irq(&pdev->dev, irq,
-                               axp20x_usb_power_irq, 0, DRVNAME, power);
-               if (ret < 0)
-                       dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
-                                irq_names[i], ret);
-       }
-
-       return 0;
-}
-
-static const struct of_device_id axp20x_usb_power_match[] = {
-       { .compatible = "x-powers,axp202-usb-power-supply" },
-       { .compatible = "x-powers,axp221-usb-power-supply" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
-
-static struct platform_driver axp20x_usb_power_driver = {
-       .probe = axp20x_usb_power_probe,
-       .driver = {
-               .name = DRVNAME,
-               .of_match_table = axp20x_usb_power_match,
-       },
-};
-
-module_platform_driver(axp20x_usb_power_driver);
-
-MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
-MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/axp288_charger.c b/drivers/power/axp288_charger.c
deleted file mode 100644 (file)
index 4030eeb..0000000
+++ /dev/null
@@ -1,970 +0,0 @@
-/*
- * axp288_charger.c - X-power AXP288 PMIC Charger driver
- *
- * Copyright (C) 2014 Intel Corporation
- * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed 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/device.h>
-#include <linux/regmap.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/usb/otg.h>
-#include <linux/notifier.h>
-#include <linux/power_supply.h>
-#include <linux/notifier.h>
-#include <linux/property.h>
-#include <linux/mfd/axp20x.h>
-#include <linux/extcon.h>
-
-#define PS_STAT_VBUS_TRIGGER           (1 << 0)
-#define PS_STAT_BAT_CHRG_DIR           (1 << 2)
-#define PS_STAT_VBAT_ABOVE_VHOLD       (1 << 3)
-#define PS_STAT_VBUS_VALID             (1 << 4)
-#define PS_STAT_VBUS_PRESENT           (1 << 5)
-
-#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 VBUS_ISPOUT_CUR_LIM_MASK       0x03
-#define VBUS_ISPOUT_CUR_LIM_BIT_POS    0
-#define VBUS_ISPOUT_CUR_LIM_900MA      0x0     /* 900mA */
-#define VBUS_ISPOUT_CUR_LIM_1500MA     0x1     /* 1500mA */
-#define VBUS_ISPOUT_CUR_LIM_2000MA     0x2     /* 2000mA */
-#define VBUS_ISPOUT_CUR_NO_LIM         0x3     /* 2500mA */
-#define VBUS_ISPOUT_VHOLD_SET_MASK     0x31
-#define VBUS_ISPOUT_VHOLD_SET_BIT_POS  0x3
-#define VBUS_ISPOUT_VHOLD_SET_OFFSET   4000    /* 4000mV */
-#define VBUS_ISPOUT_VHOLD_SET_LSB_RES  100     /* 100mV */
-#define VBUS_ISPOUT_VHOLD_SET_4300MV   0x3     /* 4300mV */
-#define VBUS_ISPOUT_VBUS_PATH_DIS      (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 CNTL2_CC_TIMEOUT_MASK          0x3     /* 2 bits */
-#define CNTL2_CC_TIMEOUT_OFFSET                6       /* 6 Hrs */
-#define CNTL2_CC_TIMEOUT_LSB_RES       2       /* 2 Hrs */
-#define CNTL2_CC_TIMEOUT_12HRS         0x3     /* 12 Hrs */
-#define CNTL2_CHGLED_TYPEB             (1 << 4)
-#define CNTL2_CHG_OUT_TURNON           (1 << 5)
-#define CNTL2_PC_TIMEOUT_MASK          0xC0
-#define CNTL2_PC_TIMEOUT_OFFSET                40      /* 40 mins */
-#define CNTL2_PC_TIMEOUT_LSB_RES       10      /* 10 mins */
-#define CNTL2_PC_TIMEOUT_70MINS                0x3
-
-#define CHRG_ILIM_TEMP_LOOP_EN         (1 << 3)
-#define CHRG_VBUS_ILIM_MASK            0xf0
-#define CHRG_VBUS_ILIM_BIT_POS         4
-#define CHRG_VBUS_ILIM_100MA           0x0     /* 100mA */
-#define CHRG_VBUS_ILIM_500MA           0x1     /* 500mA */
-#define CHRG_VBUS_ILIM_900MA           0x2     /* 900mA */
-#define CHRG_VBUS_ILIM_1500MA          0x3     /* 1500mA */
-#define CHRG_VBUS_ILIM_2000MA          0x4     /* 2000mA */
-#define CHRG_VBUS_ILIM_2500MA          0x5     /* 2500mA */
-#define CHRG_VBUS_ILIM_3000MA          0x6     /* 3000mA */
-
-#define CHRG_VLTFC_0C                  0xA5    /* 0 DegC */
-#define CHRG_VHTFC_45C                 0x1F    /* 45 DegC */
-
-#define BAT_IRQ_CFG_CHRG_DONE          (1 << 2)
-#define BAT_IRQ_CFG_CHRG_START         (1 << 3)
-#define BAT_IRQ_CFG_BAT_SAFE_EXIT      (1 << 4)
-#define BAT_IRQ_CFG_BAT_SAFE_ENTER     (1 << 5)
-#define BAT_IRQ_CFG_BAT_DISCON         (1 << 6)
-#define BAT_IRQ_CFG_BAT_CONN           (1 << 7)
-#define BAT_IRQ_CFG_BAT_MASK           0xFC
-
-#define TEMP_IRQ_CFG_QCBTU             (1 << 4)
-#define TEMP_IRQ_CFG_CBTU              (1 << 5)
-#define TEMP_IRQ_CFG_QCBTO             (1 << 6)
-#define TEMP_IRQ_CFG_CBTO              (1 << 7)
-#define TEMP_IRQ_CFG_MASK              0xF0
-
-#define FG_CNTL_OCV_ADJ_EN             (1 << 3)
-
-#define CV_4100MV                      4100    /* 4100mV */
-#define CV_4150MV                      4150    /* 4150mV */
-#define CV_4200MV                      4200    /* 4200mV */
-#define CV_4350MV                      4350    /* 4350mV */
-
-#define CC_200MA                       200     /*  200mA */
-#define CC_600MA                       600     /*  600mA */
-#define CC_800MA                       800     /*  800mA */
-#define CC_1000MA                      1000    /* 1000mA */
-#define CC_1600MA                      1600    /* 1600mA */
-#define CC_2000MA                      2000    /* 2000mA */
-
-#define ILIM_100MA                     100     /* 100mA */
-#define ILIM_500MA                     500     /* 500mA */
-#define ILIM_900MA                     900     /* 900mA */
-#define ILIM_1500MA                    1500    /* 1500mA */
-#define ILIM_2000MA                    2000    /* 2000mA */
-#define ILIM_2500MA                    2500    /* 2500mA */
-#define ILIM_3000MA                    3000    /* 3000mA */
-
-#define AXP288_EXTCON_DEV_NAME         "axp288_extcon"
-
-enum {
-       VBUS_OV_IRQ = 0,
-       CHARGE_DONE_IRQ,
-       CHARGE_CHARGING_IRQ,
-       BAT_SAFE_QUIT_IRQ,
-       BAT_SAFE_ENTER_IRQ,
-       QCBTU_IRQ,
-       CBTU_IRQ,
-       QCBTO_IRQ,
-       CBTO_IRQ,
-       CHRG_INTR_END,
-};
-
-struct axp288_chrg_info {
-       struct platform_device *pdev;
-       struct axp20x_chrg_pdata *pdata;
-       struct regmap *regmap;
-       struct regmap_irq_chip_data *regmap_irqc;
-       int irq[CHRG_INTR_END];
-       struct power_supply *psy_usb;
-       struct mutex lock;
-
-       /* OTG/Host mode */
-       struct {
-               struct work_struct work;
-               struct extcon_dev *cable;
-               struct notifier_block id_nb;
-               bool id_short;
-       } otg;
-
-       /* SDP/CDP/DCP USB charging cable notifications */
-       struct {
-               struct extcon_dev *edev;
-               bool connected;
-               enum power_supply_type chg_type;
-               struct notifier_block nb;
-               struct work_struct work;
-       } cable;
-
-       int health;
-       int inlmt;
-       int cc;
-       int cv;
-       int max_cc;
-       int max_cv;
-       bool online;
-       bool present;
-       bool enable_charger;
-       bool is_charger_enabled;
-};
-
-static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
-{
-       u8 reg_val;
-       int ret;
-
-       if (cc < CHRG_CCCV_CC_OFFSET)
-               cc = CHRG_CCCV_CC_OFFSET;
-       else if (cc > info->max_cc)
-               cc = info->max_cc;
-
-       reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
-       cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
-       reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
-
-       ret = regmap_update_bits(info->regmap,
-                               AXP20X_CHRG_CTRL1,
-                               CHRG_CCCV_CC_MASK, reg_val);
-       if (ret >= 0)
-               info->cc = cc;
-
-       return ret;
-}
-
-static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
-{
-       u8 reg_val;
-       int ret;
-
-       if (cv <= CV_4100MV) {
-               reg_val = CHRG_CCCV_CV_4100MV;
-               cv = CV_4100MV;
-       } else if (cv <= CV_4150MV) {
-               reg_val = CHRG_CCCV_CV_4150MV;
-               cv = CV_4150MV;
-       } else if (cv <= CV_4200MV) {
-               reg_val = CHRG_CCCV_CV_4200MV;
-               cv = CV_4200MV;
-       } else {
-               reg_val = CHRG_CCCV_CV_4350MV;
-               cv = CV_4350MV;
-       }
-
-       reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
-
-       ret = regmap_update_bits(info->regmap,
-                               AXP20X_CHRG_CTRL1,
-                               CHRG_CCCV_CV_MASK, reg_val);
-
-       if (ret >= 0)
-               info->cv = cv;
-
-       return ret;
-}
-
-static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
-                                          int inlmt)
-{
-       int ret;
-       unsigned int val;
-       u8 reg_val;
-
-       /* Read in limit register */
-       ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val);
-       if (ret < 0)
-               goto set_inlmt_fail;
-
-       if (inlmt <= ILIM_100MA) {
-               reg_val = CHRG_VBUS_ILIM_100MA;
-               inlmt = ILIM_100MA;
-       } else if (inlmt <= ILIM_500MA) {
-               reg_val = CHRG_VBUS_ILIM_500MA;
-               inlmt = ILIM_500MA;
-       } else if (inlmt <= ILIM_900MA) {
-               reg_val = CHRG_VBUS_ILIM_900MA;
-               inlmt = ILIM_900MA;
-       } else if (inlmt <= ILIM_1500MA) {
-               reg_val = CHRG_VBUS_ILIM_1500MA;
-               inlmt = ILIM_1500MA;
-       } else if (inlmt <= ILIM_2000MA) {
-               reg_val = CHRG_VBUS_ILIM_2000MA;
-               inlmt = ILIM_2000MA;
-       } else if (inlmt <= ILIM_2500MA) {
-               reg_val = CHRG_VBUS_ILIM_2500MA;
-               inlmt = ILIM_2500MA;
-       } else {
-               reg_val = CHRG_VBUS_ILIM_3000MA;
-               inlmt = ILIM_3000MA;
-       }
-
-       reg_val = (val & ~CHRG_VBUS_ILIM_MASK)
-                       | (reg_val << CHRG_VBUS_ILIM_BIT_POS);
-       ret = regmap_write(info->regmap, AXP20X_CHRG_BAK_CTRL, reg_val);
-       if (ret >= 0)
-               info->inlmt = inlmt;
-       else
-               dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
-
-
-set_inlmt_fail:
-       return ret;
-}
-
-static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
-                                                               bool enable)
-{
-       int ret;
-
-       if (enable)
-               ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
-                                       VBUS_ISPOUT_VBUS_PATH_DIS, 0);
-       else
-               ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
-                       VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
-
-       if (ret < 0)
-               dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
-
-
-       return ret;
-}
-
-static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
-                                                               bool enable)
-{
-       int ret;
-
-       if (enable)
-               ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
-                               CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
-       else
-               ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
-                               CHRG_CCCV_CHG_EN, 0);
-       if (ret < 0)
-               dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
-       else
-               info->is_charger_enabled = enable;
-
-       return ret;
-}
-
-static int axp288_charger_is_present(struct axp288_chrg_info *info)
-{
-       int ret, present = 0;
-       unsigned int val;
-
-       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
-       if (ret < 0)
-               return ret;
-
-       if (val & PS_STAT_VBUS_PRESENT)
-               present = 1;
-       return present;
-}
-
-static int axp288_charger_is_online(struct axp288_chrg_info *info)
-{
-       int ret, online = 0;
-       unsigned int val;
-
-       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
-       if (ret < 0)
-               return ret;
-
-       if (val & PS_STAT_VBUS_VALID)
-               online = 1;
-       return online;
-}
-
-static int axp288_get_charger_health(struct axp288_chrg_info *info)
-{
-       int ret, pwr_stat, chrg_stat;
-       int health = POWER_SUPPLY_HEALTH_UNKNOWN;
-       unsigned int val;
-
-       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
-       if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT))
-               goto health_read_fail;
-       else
-               pwr_stat = val;
-
-       ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val);
-       if (ret < 0)
-               goto health_read_fail;
-       else
-               chrg_stat = val;
-
-       if (!(pwr_stat & PS_STAT_VBUS_VALID))
-               health = POWER_SUPPLY_HEALTH_DEAD;
-       else if (chrg_stat & CHRG_STAT_PMIC_OTP)
-               health = POWER_SUPPLY_HEALTH_OVERHEAT;
-       else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE)
-               health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
-       else
-               health = POWER_SUPPLY_HEALTH_GOOD;
-
-health_read_fail:
-       return health;
-}
-
-static int axp288_charger_usb_set_property(struct power_supply *psy,
-                                   enum power_supply_property psp,
-                                   const union power_supply_propval *val)
-{
-       struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
-       int ret = 0;
-       int scaled_val;
-
-       mutex_lock(&info->lock);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               scaled_val = min(val->intval, info->max_cc);
-               scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
-               ret = axp288_charger_set_cc(info, scaled_val);
-               if (ret < 0)
-                       dev_warn(&info->pdev->dev, "set charge current failed\n");
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               scaled_val = min(val->intval, info->max_cv);
-               scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
-               ret = axp288_charger_set_cv(info, scaled_val);
-               if (ret < 0)
-                       dev_warn(&info->pdev->dev, "set charge voltage failed\n");
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       mutex_unlock(&info->lock);
-       return ret;
-}
-
-static int axp288_charger_usb_get_property(struct power_supply *psy,
-                                   enum power_supply_property psp,
-                                   union power_supply_propval *val)
-{
-       struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
-       int ret = 0;
-
-       mutex_lock(&info->lock);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-               /* Check for OTG case first */
-               if (info->otg.id_short) {
-                       val->intval = 0;
-                       break;
-               }
-               ret = axp288_charger_is_present(info);
-               if (ret < 0)
-                       goto psy_get_prop_fail;
-               info->present = ret;
-               val->intval = info->present;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               /* Check for OTG case first */
-               if (info->otg.id_short) {
-                       val->intval = 0;
-                       break;
-               }
-               ret = axp288_charger_is_online(info);
-               if (ret < 0)
-                       goto psy_get_prop_fail;
-               info->online = ret;
-               val->intval = info->online;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               val->intval = axp288_get_charger_health(info);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               val->intval = info->cc * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
-               val->intval = info->max_cc * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               val->intval = info->cv * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
-               val->intval = info->max_cv * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
-               val->intval = info->inlmt * 1000;
-               break;
-       default:
-               ret = -EINVAL;
-               goto psy_get_prop_fail;
-       }
-
-psy_get_prop_fail:
-       mutex_unlock(&info->lock);
-       return ret;
-}
-
-static int axp288_charger_property_is_writeable(struct power_supply *psy,
-               enum power_supply_property psp)
-{
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               ret = 1;
-               break;
-       default:
-               ret = 0;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property axp288_usb_props[] = {
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_TYPE,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
-};
-
-static const struct power_supply_desc axp288_charger_desc = {
-       .name                   = "axp288_charger",
-       .type                   = POWER_SUPPLY_TYPE_USB,
-       .properties             = axp288_usb_props,
-       .num_properties         = ARRAY_SIZE(axp288_usb_props),
-       .get_property           = axp288_charger_usb_get_property,
-       .set_property           = axp288_charger_usb_set_property,
-       .property_is_writeable  = axp288_charger_property_is_writeable,
-};
-
-static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
-{
-       struct axp288_chrg_info *info = dev;
-       int i;
-
-       for (i = 0; i < CHRG_INTR_END; i++) {
-               if (info->irq[i] == irq)
-                       break;
-       }
-
-       if (i >= CHRG_INTR_END) {
-               dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
-               return IRQ_NONE;
-       }
-
-       switch (i) {
-       case VBUS_OV_IRQ:
-               dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
-               break;
-       case CHARGE_DONE_IRQ:
-               dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
-               break;
-       case CHARGE_CHARGING_IRQ:
-               dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
-               break;
-       case BAT_SAFE_QUIT_IRQ:
-               dev_dbg(&info->pdev->dev,
-                       "Quit Safe Mode(restart timer) Charging IRQ\n");
-               break;
-       case BAT_SAFE_ENTER_IRQ:
-               dev_dbg(&info->pdev->dev,
-                       "Enter Safe Mode(timer expire) Charging IRQ\n");
-               break;
-       case QCBTU_IRQ:
-               dev_dbg(&info->pdev->dev,
-                       "Quit Battery Under Temperature(CHRG) INTR\n");
-               break;
-       case CBTU_IRQ:
-               dev_dbg(&info->pdev->dev,
-                       "Hit Battery Under Temperature(CHRG) INTR\n");
-               break;
-       case QCBTO_IRQ:
-               dev_dbg(&info->pdev->dev,
-                       "Quit Battery Over Temperature(CHRG) INTR\n");
-               break;
-       case CBTO_IRQ:
-               dev_dbg(&info->pdev->dev,
-                       "Hit Battery Over Temperature(CHRG) INTR\n");
-               break;
-       default:
-               dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
-               goto out;
-       }
-
-       power_supply_changed(info->psy_usb);
-out:
-       return IRQ_HANDLED;
-}
-
-static void axp288_charger_extcon_evt_worker(struct work_struct *work)
-{
-       struct axp288_chrg_info *info =
-           container_of(work, struct axp288_chrg_info, cable.work);
-       int ret, current_limit;
-       bool changed = false;
-       struct extcon_dev *edev = info->cable.edev;
-       bool old_connected = info->cable.connected;
-
-       /* Determine cable/charger type */
-       if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_SDP) > 0) {
-               dev_dbg(&info->pdev->dev, "USB SDP charger  is connected");
-               info->cable.connected = true;
-               info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
-       } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_CDP) > 0) {
-               dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
-               info->cable.connected = true;
-               info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
-       } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_DCP) > 0) {
-               dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
-               info->cable.connected = true;
-               info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
-       } else {
-               if (old_connected)
-                       dev_dbg(&info->pdev->dev, "USB charger disconnected");
-               info->cable.connected = false;
-               info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
-       }
-
-       /* Cable status changed */
-       if (old_connected != info->cable.connected)
-               changed = true;
-
-       if (!changed)
-               return;
-
-       mutex_lock(&info->lock);
-
-       if (info->is_charger_enabled && !info->cable.connected) {
-               info->enable_charger = false;
-               ret = axp288_charger_enable_charger(info, info->enable_charger);
-               if (ret < 0)
-                       dev_err(&info->pdev->dev,
-                               "cannot disable charger (%d)", ret);
-
-       } else if (!info->is_charger_enabled && info->cable.connected) {
-               switch (info->cable.chg_type) {
-               case POWER_SUPPLY_TYPE_USB:
-                       current_limit = ILIM_500MA;
-                       break;
-               case POWER_SUPPLY_TYPE_USB_CDP:
-                       current_limit = ILIM_1500MA;
-                       break;
-               case POWER_SUPPLY_TYPE_USB_DCP:
-                       current_limit = ILIM_2000MA;
-                       break;
-               default:
-                       /* Unknown */
-                       current_limit = 0;
-                       break;
-               }
-
-               /* Set vbus current limit first, then enable charger */
-               ret = axp288_charger_set_vbus_inlmt(info, current_limit);
-               if (ret < 0) {
-                       dev_err(&info->pdev->dev,
-                               "error setting current limit (%d)", ret);
-               } else {
-                       info->enable_charger = (current_limit > 0);
-                       ret = axp288_charger_enable_charger(info,
-                                                       info->enable_charger);
-                       if (ret < 0)
-                               dev_err(&info->pdev->dev,
-                                       "cannot enable charger (%d)", ret);
-               }
-       }
-
-       if (changed)
-               info->health = axp288_get_charger_health(info);
-
-       mutex_unlock(&info->lock);
-
-       if (changed)
-               power_supply_changed(info->psy_usb);
-}
-
-static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
-                                         unsigned long event, void *param)
-{
-       struct axp288_chrg_info *info =
-           container_of(nb, struct axp288_chrg_info, cable.nb);
-
-       schedule_work(&info->cable.work);
-
-       return NOTIFY_OK;
-}
-
-static void axp288_charger_otg_evt_worker(struct work_struct *work)
-{
-       struct axp288_chrg_info *info =
-           container_of(work, struct axp288_chrg_info, otg.work);
-       int ret;
-
-       /* Disable VBUS path before enabling the 5V boost */
-       ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
-       if (ret < 0)
-               dev_warn(&info->pdev->dev, "vbus path disable failed\n");
-}
-
-static int axp288_charger_handle_otg_evt(struct notifier_block *nb,
-                                  unsigned long event, void *param)
-{
-       struct axp288_chrg_info *info =
-           container_of(nb, struct axp288_chrg_info, otg.id_nb);
-       struct extcon_dev *edev = info->otg.cable;
-       int usb_host = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
-
-       dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
-                               usb_host ? "attached" : "detached");
-
-       /*
-        * Set usb_id_short flag to avoid running charger detection logic
-        * in case usb host.
-        */
-       info->otg.id_short = usb_host;
-       schedule_work(&info->otg.work);
-
-       return NOTIFY_OK;
-}
-
-static void charger_init_hw_regs(struct axp288_chrg_info *info)
-{
-       int ret, cc, cv;
-       unsigned int val;
-
-       /* Program temperature thresholds */
-       ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
-       if (ret < 0)
-               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
-                                                       AXP20X_V_LTF_CHRG, ret);
-
-       ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
-       if (ret < 0)
-               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
-                                                       AXP20X_V_HTF_CHRG, ret);
-
-       /* Do not turn-off charger o/p after charge cycle ends */
-       ret = regmap_update_bits(info->regmap,
-                               AXP20X_CHRG_CTRL2,
-                               CNTL2_CHG_OUT_TURNON, 1);
-       if (ret < 0)
-               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
-                                               AXP20X_CHRG_CTRL2, ret);
-
-       /* Enable interrupts */
-       ret = regmap_update_bits(info->regmap,
-                               AXP20X_IRQ2_EN,
-                               BAT_IRQ_CFG_BAT_MASK, 1);
-       if (ret < 0)
-               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
-                                               AXP20X_IRQ2_EN, ret);
-
-       ret = regmap_update_bits(info->regmap, AXP20X_IRQ3_EN,
-                               TEMP_IRQ_CFG_MASK, 1);
-       if (ret < 0)
-               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
-                                               AXP20X_IRQ3_EN, ret);
-
-       /* Setup ending condition for charging to be 10% of I(chrg) */
-       ret = regmap_update_bits(info->regmap,
-                               AXP20X_CHRG_CTRL1,
-                               CHRG_CCCV_ITERM_20P, 0);
-       if (ret < 0)
-               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
-                                               AXP20X_CHRG_CTRL1, ret);
-
-       /* Disable OCV-SOC curve calibration */
-       ret = regmap_update_bits(info->regmap,
-                               AXP20X_CC_CTRL,
-                               FG_CNTL_OCV_ADJ_EN, 0);
-       if (ret < 0)
-               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
-                                               AXP20X_CC_CTRL, ret);
-
-       /* Init charging current and voltage */
-       info->max_cc = info->pdata->max_cc;
-       info->max_cv = info->pdata->max_cv;
-
-       /* Read current charge voltage and current limit */
-       ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
-       if (ret < 0) {
-               /* Assume default if cannot read */
-               info->cc = info->pdata->def_cc;
-               info->cv = info->pdata->def_cv;
-       } else {
-               /* Determine charge voltage */
-               cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
-               switch (cv) {
-               case CHRG_CCCV_CV_4100MV:
-                       info->cv = CV_4100MV;
-                       break;
-               case CHRG_CCCV_CV_4150MV:
-                       info->cv = CV_4150MV;
-                       break;
-               case CHRG_CCCV_CV_4200MV:
-                       info->cv = CV_4200MV;
-                       break;
-               case CHRG_CCCV_CV_4350MV:
-                       info->cv = CV_4350MV;
-                       break;
-               default:
-                       info->cv = INT_MAX;
-                       break;
-               }
-
-               /* Determine charge current limit */
-               cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
-               cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
-               info->cc = cc;
-
-               /* Program default charging voltage and current */
-               cc = min(info->pdata->def_cc, info->max_cc);
-               cv = min(info->pdata->def_cv, info->max_cv);
-
-               ret = axp288_charger_set_cc(info, cc);
-               if (ret < 0)
-                       dev_warn(&info->pdev->dev,
-                                       "error(%d) in setting CC\n", ret);
-
-               ret = axp288_charger_set_cv(info, cv);
-               if (ret < 0)
-                       dev_warn(&info->pdev->dev,
-                                       "error(%d) in setting CV\n", ret);
-       }
-}
-
-static int axp288_charger_probe(struct platform_device *pdev)
-{
-       int ret, i, pirq;
-       struct axp288_chrg_info *info;
-       struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
-       struct power_supply_config charger_cfg = {};
-
-       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->pdata = pdev->dev.platform_data;
-
-       if (!info->pdata) {
-               /* Try ACPI provided pdata via device properties */
-               if (!device_property_present(&pdev->dev,
-                                               "axp288_charger_data\n"))
-                       dev_err(&pdev->dev, "failed to get platform data\n");
-               return -ENODEV;
-       }
-
-       info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
-       if (info->cable.edev == NULL) {
-               dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
-                       AXP288_EXTCON_DEV_NAME);
-               return -EPROBE_DEFER;
-       }
-
-       /* Register for extcon notification */
-       INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
-       info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
-       ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
-                                       &info->cable.nb);
-       if (ret) {
-               dev_err(&info->pdev->dev,
-                       "failed to register extcon notifier for SDP %d\n", ret);
-               return ret;
-       }
-
-       ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
-                                       &info->cable.nb);
-       if (ret) {
-               dev_err(&info->pdev->dev,
-                       "failed to register extcon notifier for CDP %d\n", ret);
-               extcon_unregister_notifier(info->cable.edev,
-                               EXTCON_CHG_USB_SDP, &info->cable.nb);
-               return ret;
-       }
-
-       ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
-                                       &info->cable.nb);
-       if (ret) {
-               dev_err(&info->pdev->dev,
-                       "failed to register extcon notifier for DCP %d\n", ret);
-               extcon_unregister_notifier(info->cable.edev,
-                               EXTCON_CHG_USB_SDP, &info->cable.nb);
-               extcon_unregister_notifier(info->cable.edev,
-                               EXTCON_CHG_USB_CDP, &info->cable.nb);
-               return ret;
-       }
-
-       platform_set_drvdata(pdev, info);
-       mutex_init(&info->lock);
-
-       /* Register with power supply class */
-       charger_cfg.drv_data = info;
-       info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc,
-                                               &charger_cfg);
-       if (IS_ERR(info->psy_usb)) {
-               dev_err(&pdev->dev, "failed to register power supply charger\n");
-               ret = PTR_ERR(info->psy_usb);
-               goto psy_reg_failed;
-       }
-
-       /* Register for OTG notification */
-       INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
-       info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
-       ret = extcon_register_notifier(info->otg.cable, EXTCON_USB_HOST,
-                                      &info->otg.id_nb);
-       if (ret)
-               dev_warn(&pdev->dev, "failed to register otg notifier\n");
-
-       if (info->otg.cable)
-               info->otg.id_short = extcon_get_cable_state_(
-                                       info->otg.cable, EXTCON_USB_HOST);
-
-       /* Register charger interrupts */
-       for (i = 0; i < CHRG_INTR_END; 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,
-                               "failed to get virtual interrupt=%d\n", pirq);
-                       ret = info->irq[i];
-                       goto intr_reg_failed;
-               }
-               ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
-                                       NULL, axp288_charger_irq_thread_handler,
-                                       IRQF_ONESHOT, info->pdev->name, info);
-               if (ret) {
-                       dev_err(&pdev->dev, "failed to request interrupt=%d\n",
-                                                               info->irq[i]);
-                       goto intr_reg_failed;
-               }
-       }
-
-       charger_init_hw_regs(info);
-
-       return 0;
-
-intr_reg_failed:
-       if (info->otg.cable)
-               extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST,
-                                       &info->otg.id_nb);
-       power_supply_unregister(info->psy_usb);
-psy_reg_failed:
-       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
-                                       &info->cable.nb);
-       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
-                                       &info->cable.nb);
-       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
-                                       &info->cable.nb);
-       return ret;
-}
-
-static int axp288_charger_remove(struct platform_device *pdev)
-{
-       struct axp288_chrg_info *info =  dev_get_drvdata(&pdev->dev);
-
-       if (info->otg.cable)
-               extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST,
-                                       &info->otg.id_nb);
-
-       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
-                                       &info->cable.nb);
-       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
-                                       &info->cable.nb);
-       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
-                                       &info->cable.nb);
-       power_supply_unregister(info->psy_usb);
-
-       return 0;
-}
-
-static struct platform_driver axp288_charger_driver = {
-       .probe = axp288_charger_probe,
-       .remove = axp288_charger_remove,
-       .driver = {
-               .name = "axp288_charger",
-       },
-};
-
-module_platform_driver(axp288_charger_driver);
-
-MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
-MODULE_DESCRIPTION("X-power AXP288 Charger Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/axp288_fuel_gauge.c b/drivers/power/axp288_fuel_gauge.c
deleted file mode 100644 (file)
index 50c0110..0000000
+++ /dev/null
@@ -1,1155 +0,0 @@
-/*
- * 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 = power_supply_get_drvdata(ps);
-       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 = power_supply_get_drvdata(ps);
-       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 = power_supply_get_drvdata(psy);
-
-       power_supply_changed(info->bat);
-}
-
-static const struct power_supply_desc fuel_gauge_desc = {
-       .name                   = DEV_NAME,
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = fuel_gauge_props,
-       .num_properties         = ARRAY_SIZE(fuel_gauge_props),
-       .get_property           = fuel_gauge_get_property,
-       .set_property           = fuel_gauge_set_property,
-       .property_is_writeable  = fuel_gauge_property_is_writeable,
-       .external_power_changed = fuel_gauge_external_power_changed,
-};
-
-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 = 0;
-       struct axp288_fg_info *info;
-       struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
-       struct power_supply_config psy_cfg = {};
-
-       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);
-
-       psy_cfg.drv_data = info;
-       info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
-       if (IS_ERR(info->bat)) {
-               ret = PTR_ERR(info->bat);
-               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 const 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("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
-MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>");
-MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c
deleted file mode 100644 (file)
index 73e2f0b..0000000
+++ /dev/null
@@ -1,1815 +0,0 @@
-/*
- * bq2415x charger driver
- *
- * Copyright (C) 2011-2013  Pali Rohár <pali.rohar@gmail.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * Datasheets:
- * http://www.ti.com/product/bq24150
- * http://www.ti.com/product/bq24150a
- * http://www.ti.com/product/bq24152
- * http://www.ti.com/product/bq24153
- * http://www.ti.com/product/bq24153a
- * http://www.ti.com/product/bq24155
- * http://www.ti.com/product/bq24157s
- * http://www.ti.com/product/bq24158
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/param.h>
-#include <linux/err.h>
-#include <linux/workqueue.h>
-#include <linux/sysfs.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/idr.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/acpi.h>
-
-#include <linux/power/bq2415x_charger.h>
-
-/* timeout for resetting chip timer */
-#define BQ2415X_TIMER_TIMEOUT          10
-
-#define BQ2415X_REG_STATUS             0x00
-#define BQ2415X_REG_CONTROL            0x01
-#define BQ2415X_REG_VOLTAGE            0x02
-#define BQ2415X_REG_VENDER             0x03
-#define BQ2415X_REG_CURRENT            0x04
-
-/* reset state for all registers */
-#define BQ2415X_RESET_STATUS           BIT(6)
-#define BQ2415X_RESET_CONTROL          (BIT(4)|BIT(5))
-#define BQ2415X_RESET_VOLTAGE          (BIT(1)|BIT(3))
-#define BQ2415X_RESET_CURRENT          (BIT(0)|BIT(3)|BIT(7))
-
-/* status register */
-#define BQ2415X_BIT_TMR_RST            7
-#define BQ2415X_BIT_OTG                        7
-#define BQ2415X_BIT_EN_STAT            6
-#define BQ2415X_MASK_STAT              (BIT(4)|BIT(5))
-#define BQ2415X_SHIFT_STAT             4
-#define BQ2415X_BIT_BOOST              3
-#define BQ2415X_MASK_FAULT             (BIT(0)|BIT(1)|BIT(2))
-#define BQ2415X_SHIFT_FAULT            0
-
-/* control register */
-#define BQ2415X_MASK_LIMIT             (BIT(6)|BIT(7))
-#define BQ2415X_SHIFT_LIMIT            6
-#define BQ2415X_MASK_VLOWV             (BIT(4)|BIT(5))
-#define BQ2415X_SHIFT_VLOWV            4
-#define BQ2415X_BIT_TE                 3
-#define BQ2415X_BIT_CE                 2
-#define BQ2415X_BIT_HZ_MODE            1
-#define BQ2415X_BIT_OPA_MODE           0
-
-/* voltage register */
-#define BQ2415X_MASK_VO                (BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
-#define BQ2415X_SHIFT_VO               2
-#define BQ2415X_BIT_OTG_PL             1
-#define BQ2415X_BIT_OTG_EN             0
-
-/* vender register */
-#define BQ2415X_MASK_VENDER            (BIT(5)|BIT(6)|BIT(7))
-#define BQ2415X_SHIFT_VENDER           5
-#define BQ2415X_MASK_PN                        (BIT(3)|BIT(4))
-#define BQ2415X_SHIFT_PN               3
-#define BQ2415X_MASK_REVISION          (BIT(0)|BIT(1)|BIT(2))
-#define BQ2415X_SHIFT_REVISION         0
-
-/* current register */
-#define BQ2415X_MASK_RESET             BIT(7)
-#define BQ2415X_MASK_VI_CHRG           (BIT(4)|BIT(5)|BIT(6))
-#define BQ2415X_SHIFT_VI_CHRG          4
-/* N/A                                 BIT(3) */
-#define BQ2415X_MASK_VI_TERM           (BIT(0)|BIT(1)|BIT(2))
-#define BQ2415X_SHIFT_VI_TERM          0
-
-
-enum bq2415x_command {
-       BQ2415X_TIMER_RESET,
-       BQ2415X_OTG_STATUS,
-       BQ2415X_STAT_PIN_STATUS,
-       BQ2415X_STAT_PIN_ENABLE,
-       BQ2415X_STAT_PIN_DISABLE,
-       BQ2415X_CHARGE_STATUS,
-       BQ2415X_BOOST_STATUS,
-       BQ2415X_FAULT_STATUS,
-
-       BQ2415X_CHARGE_TERMINATION_STATUS,
-       BQ2415X_CHARGE_TERMINATION_ENABLE,
-       BQ2415X_CHARGE_TERMINATION_DISABLE,
-       BQ2415X_CHARGER_STATUS,
-       BQ2415X_CHARGER_ENABLE,
-       BQ2415X_CHARGER_DISABLE,
-       BQ2415X_HIGH_IMPEDANCE_STATUS,
-       BQ2415X_HIGH_IMPEDANCE_ENABLE,
-       BQ2415X_HIGH_IMPEDANCE_DISABLE,
-       BQ2415X_BOOST_MODE_STATUS,
-       BQ2415X_BOOST_MODE_ENABLE,
-       BQ2415X_BOOST_MODE_DISABLE,
-
-       BQ2415X_OTG_LEVEL,
-       BQ2415X_OTG_ACTIVATE_HIGH,
-       BQ2415X_OTG_ACTIVATE_LOW,
-       BQ2415X_OTG_PIN_STATUS,
-       BQ2415X_OTG_PIN_ENABLE,
-       BQ2415X_OTG_PIN_DISABLE,
-
-       BQ2415X_VENDER_CODE,
-       BQ2415X_PART_NUMBER,
-       BQ2415X_REVISION,
-};
-
-enum bq2415x_chip {
-       BQUNKNOWN,
-       BQ24150,
-       BQ24150A,
-       BQ24151,
-       BQ24151A,
-       BQ24152,
-       BQ24153,
-       BQ24153A,
-       BQ24155,
-       BQ24156,
-       BQ24156A,
-       BQ24157S,
-       BQ24158,
-};
-
-static char *bq2415x_chip_name[] = {
-       "unknown",
-       "bq24150",
-       "bq24150a",
-       "bq24151",
-       "bq24151a",
-       "bq24152",
-       "bq24153",
-       "bq24153a",
-       "bq24155",
-       "bq24156",
-       "bq24156a",
-       "bq24157s",
-       "bq24158",
-};
-
-struct bq2415x_device {
-       struct device *dev;
-       struct bq2415x_platform_data init_data;
-       struct power_supply *charger;
-       struct power_supply_desc charger_desc;
-       struct delayed_work work;
-       struct device_node *notify_node;
-       struct notifier_block nb;
-       enum bq2415x_mode reported_mode;/* mode reported by hook function */
-       enum bq2415x_mode mode;         /* currently configured mode */
-       enum bq2415x_chip chip;
-       const char *timer_error;
-       char *model;
-       char *name;
-       int autotimer;  /* 1 - if driver automatically reset timer, 0 - not */
-       int automode;   /* 1 - enabled, 0 - disabled; -1 - not supported */
-       int id;
-};
-
-/* each registered chip must have unique id */
-static DEFINE_IDR(bq2415x_id);
-
-static DEFINE_MUTEX(bq2415x_id_mutex);
-static DEFINE_MUTEX(bq2415x_timer_mutex);
-static DEFINE_MUTEX(bq2415x_i2c_mutex);
-
-/**** i2c read functions ****/
-
-/* read value from register */
-static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg)
-{
-       struct i2c_client *client = to_i2c_client(bq->dev);
-       struct i2c_msg msg[2];
-       u8 val;
-       int ret;
-
-       if (!client->adapter)
-               return -ENODEV;
-
-       msg[0].addr = client->addr;
-       msg[0].flags = 0;
-       msg[0].buf = &reg;
-       msg[0].len = sizeof(reg);
-       msg[1].addr = client->addr;
-       msg[1].flags = I2C_M_RD;
-       msg[1].buf = &val;
-       msg[1].len = sizeof(val);
-
-       mutex_lock(&bq2415x_i2c_mutex);
-       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
-       mutex_unlock(&bq2415x_i2c_mutex);
-
-       if (ret < 0)
-               return ret;
-
-       return val;
-}
-
-/* read value from register, apply mask and right shift it */
-static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg,
-                                u8 mask, u8 shift)
-{
-       int ret;
-
-       if (shift > 8)
-               return -EINVAL;
-
-       ret = bq2415x_i2c_read(bq, reg);
-       if (ret < 0)
-               return ret;
-       return (ret & mask) >> shift;
-}
-
-/* read value from register and return one specified bit */
-static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit)
-{
-       if (bit > 8)
-               return -EINVAL;
-       return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit);
-}
-
-/**** i2c write functions ****/
-
-/* write value to register */
-static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val)
-{
-       struct i2c_client *client = to_i2c_client(bq->dev);
-       struct i2c_msg msg[1];
-       u8 data[2];
-       int ret;
-
-       data[0] = reg;
-       data[1] = val;
-
-       msg[0].addr = client->addr;
-       msg[0].flags = 0;
-       msg[0].buf = data;
-       msg[0].len = ARRAY_SIZE(data);
-
-       mutex_lock(&bq2415x_i2c_mutex);
-       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
-       mutex_unlock(&bq2415x_i2c_mutex);
-
-       /* i2c_transfer returns number of messages transferred */
-       if (ret < 0)
-               return ret;
-       else if (ret != 1)
-               return -EIO;
-
-       return 0;
-}
-
-/* read value from register, change it with mask left shifted and write back */
-static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val,
-                                 u8 mask, u8 shift)
-{
-       int ret;
-
-       if (shift > 8)
-               return -EINVAL;
-
-       ret = bq2415x_i2c_read(bq, reg);
-       if (ret < 0)
-               return ret;
-
-       ret &= ~mask;
-       ret |= val << shift;
-
-       return bq2415x_i2c_write(bq, reg, ret);
-}
-
-/* change only one bit in register */
-static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg,
-                                bool val, u8 bit)
-{
-       if (bit > 8)
-               return -EINVAL;
-       return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit);
-}
-
-/**** global functions ****/
-
-/* exec command function */
-static int bq2415x_exec_command(struct bq2415x_device *bq,
-                               enum bq2415x_command command)
-{
-       int ret;
-
-       switch (command) {
-       case BQ2415X_TIMER_RESET:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS,
-                               1, BQ2415X_BIT_TMR_RST);
-       case BQ2415X_OTG_STATUS:
-               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
-                               BQ2415X_BIT_OTG);
-       case BQ2415X_STAT_PIN_STATUS:
-               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
-                               BQ2415X_BIT_EN_STAT);
-       case BQ2415X_STAT_PIN_ENABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 1,
-                               BQ2415X_BIT_EN_STAT);
-       case BQ2415X_STAT_PIN_DISABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 0,
-                               BQ2415X_BIT_EN_STAT);
-       case BQ2415X_CHARGE_STATUS:
-               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
-                               BQ2415X_MASK_STAT, BQ2415X_SHIFT_STAT);
-       case BQ2415X_BOOST_STATUS:
-               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
-                               BQ2415X_BIT_BOOST);
-       case BQ2415X_FAULT_STATUS:
-               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
-                       BQ2415X_MASK_FAULT, BQ2415X_SHIFT_FAULT);
-
-       case BQ2415X_CHARGE_TERMINATION_STATUS:
-               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
-                               BQ2415X_BIT_TE);
-       case BQ2415X_CHARGE_TERMINATION_ENABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
-                               1, BQ2415X_BIT_TE);
-       case BQ2415X_CHARGE_TERMINATION_DISABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
-                               0, BQ2415X_BIT_TE);
-       case BQ2415X_CHARGER_STATUS:
-               ret = bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
-                       BQ2415X_BIT_CE);
-               if (ret < 0)
-                       return ret;
-               return ret > 0 ? 0 : 1;
-       case BQ2415X_CHARGER_ENABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
-                               0, BQ2415X_BIT_CE);
-       case BQ2415X_CHARGER_DISABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
-                               1, BQ2415X_BIT_CE);
-       case BQ2415X_HIGH_IMPEDANCE_STATUS:
-               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
-                               BQ2415X_BIT_HZ_MODE);
-       case BQ2415X_HIGH_IMPEDANCE_ENABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
-                               1, BQ2415X_BIT_HZ_MODE);
-       case BQ2415X_HIGH_IMPEDANCE_DISABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
-                               0, BQ2415X_BIT_HZ_MODE);
-       case BQ2415X_BOOST_MODE_STATUS:
-               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
-                               BQ2415X_BIT_OPA_MODE);
-       case BQ2415X_BOOST_MODE_ENABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
-                               1, BQ2415X_BIT_OPA_MODE);
-       case BQ2415X_BOOST_MODE_DISABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
-                               0, BQ2415X_BIT_OPA_MODE);
-
-       case BQ2415X_OTG_LEVEL:
-               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
-                               BQ2415X_BIT_OTG_PL);
-       case BQ2415X_OTG_ACTIVATE_HIGH:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
-                               1, BQ2415X_BIT_OTG_PL);
-       case BQ2415X_OTG_ACTIVATE_LOW:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
-                               0, BQ2415X_BIT_OTG_PL);
-       case BQ2415X_OTG_PIN_STATUS:
-               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
-                               BQ2415X_BIT_OTG_EN);
-       case BQ2415X_OTG_PIN_ENABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
-                               1, BQ2415X_BIT_OTG_EN);
-       case BQ2415X_OTG_PIN_DISABLE:
-               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
-                               0, BQ2415X_BIT_OTG_EN);
-
-       case BQ2415X_VENDER_CODE:
-               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
-                       BQ2415X_MASK_VENDER, BQ2415X_SHIFT_VENDER);
-       case BQ2415X_PART_NUMBER:
-               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
-                               BQ2415X_MASK_PN, BQ2415X_SHIFT_PN);
-       case BQ2415X_REVISION:
-               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
-                       BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION);
-       }
-       return -EINVAL;
-}
-
-/* detect chip type */
-static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq)
-{
-       struct i2c_client *client = to_i2c_client(bq->dev);
-       int ret = bq2415x_exec_command(bq, BQ2415X_PART_NUMBER);
-
-       if (ret < 0)
-               return ret;
-
-       switch (client->addr) {
-       case 0x6b:
-               switch (ret) {
-               case 0:
-                       if (bq->chip == BQ24151A)
-                               return bq->chip;
-                       return BQ24151;
-               case 1:
-                       if (bq->chip == BQ24150A ||
-                               bq->chip == BQ24152 ||
-                               bq->chip == BQ24155)
-                               return bq->chip;
-                       return BQ24150;
-               case 2:
-                       if (bq->chip == BQ24153A)
-                               return bq->chip;
-                       return BQ24153;
-               default:
-                       return BQUNKNOWN;
-               }
-               break;
-
-       case 0x6a:
-               switch (ret) {
-               case 0:
-                       if (bq->chip == BQ24156A)
-                               return bq->chip;
-                       return BQ24156;
-               case 2:
-                       if (bq->chip == BQ24157S)
-                               return bq->chip;
-                       return BQ24158;
-               default:
-                       return BQUNKNOWN;
-               }
-               break;
-       }
-
-       return BQUNKNOWN;
-}
-
-/* detect chip revision */
-static int bq2415x_detect_revision(struct bq2415x_device *bq)
-{
-       int ret = bq2415x_exec_command(bq, BQ2415X_REVISION);
-       int chip = bq2415x_detect_chip(bq);
-
-       if (ret < 0 || chip < 0)
-               return -1;
-
-       switch (chip) {
-       case BQ24150:
-       case BQ24150A:
-       case BQ24151:
-       case BQ24151A:
-       case BQ24152:
-               if (ret >= 0 && ret <= 3)
-                       return ret;
-               return -1;
-       case BQ24153:
-       case BQ24153A:
-       case BQ24156:
-       case BQ24156A:
-       case BQ24157S:
-       case BQ24158:
-               if (ret == 3)
-                       return 0;
-               else if (ret == 1)
-                       return 1;
-               return -1;
-       case BQ24155:
-               if (ret == 3)
-                       return 3;
-               return -1;
-       case BQUNKNOWN:
-               return -1;
-       }
-
-       return -1;
-}
-
-/* return chip vender code */
-static int bq2415x_get_vender_code(struct bq2415x_device *bq)
-{
-       int ret;
-
-       ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE);
-       if (ret < 0)
-               return 0;
-
-       /* convert to binary */
-       return (ret & 0x1) +
-              ((ret >> 1) & 0x1) * 10 +
-              ((ret >> 2) & 0x1) * 100;
-}
-
-/* reset all chip registers to default state */
-static void bq2415x_reset_chip(struct bq2415x_device *bq)
-{
-       bq2415x_i2c_write(bq, BQ2415X_REG_CURRENT, BQ2415X_RESET_CURRENT);
-       bq2415x_i2c_write(bq, BQ2415X_REG_VOLTAGE, BQ2415X_RESET_VOLTAGE);
-       bq2415x_i2c_write(bq, BQ2415X_REG_CONTROL, BQ2415X_RESET_CONTROL);
-       bq2415x_i2c_write(bq, BQ2415X_REG_STATUS, BQ2415X_RESET_STATUS);
-       bq->timer_error = NULL;
-}
-
-/**** properties functions ****/
-
-/* set current limit in mA */
-static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA)
-{
-       int val;
-
-       if (mA <= 100)
-               val = 0;
-       else if (mA <= 500)
-               val = 1;
-       else if (mA <= 800)
-               val = 2;
-       else
-               val = 3;
-
-       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
-                       BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
-}
-
-/* get current limit in mA */
-static int bq2415x_get_current_limit(struct bq2415x_device *bq)
-{
-       int ret;
-
-       ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
-                       BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
-       if (ret < 0)
-               return ret;
-       else if (ret == 0)
-               return 100;
-       else if (ret == 1)
-               return 500;
-       else if (ret == 2)
-               return 800;
-       else if (ret == 3)
-               return 1800;
-       return -EINVAL;
-}
-
-/* set weak battery voltage in mV */
-static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV)
-{
-       int val;
-
-       /* round to 100mV */
-       if (mV <= 3400 + 50)
-               val = 0;
-       else if (mV <= 3500 + 50)
-               val = 1;
-       else if (mV <= 3600 + 50)
-               val = 2;
-       else
-               val = 3;
-
-       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
-                       BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
-}
-
-/* get weak battery voltage in mV */
-static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq)
-{
-       int ret;
-
-       ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
-                       BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
-       if (ret < 0)
-               return ret;
-       return 100 * (34 + ret);
-}
-
-/* set battery regulation voltage in mV */
-static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq,
-                                                 int mV)
-{
-       int val = (mV/10 - 350) / 2;
-
-       /*
-        * According to datasheet, maximum battery regulation voltage is
-        * 4440mV which is b101111 = 47.
-        */
-       if (val < 0)
-               val = 0;
-       else if (val > 47)
-               return -EINVAL;
-
-       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val,
-                       BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
-}
-
-/* get battery regulation voltage in mV */
-static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq)
-{
-       int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE,
-                       BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
-
-       if (ret < 0)
-               return ret;
-       return 10 * (350 + 2*ret);
-}
-
-/* set charge current in mA (platform data must provide resistor sense) */
-static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA)
-{
-       int val;
-
-       if (bq->init_data.resistor_sense <= 0)
-               return -EINVAL;
-
-       val = (mA * bq->init_data.resistor_sense - 37400) / 6800;
-       if (val < 0)
-               val = 0;
-       else if (val > 7)
-               val = 7;
-
-       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
-                       BQ2415X_MASK_VI_CHRG | BQ2415X_MASK_RESET,
-                       BQ2415X_SHIFT_VI_CHRG);
-}
-
-/* get charge current in mA (platform data must provide resistor sense) */
-static int bq2415x_get_charge_current(struct bq2415x_device *bq)
-{
-       int ret;
-
-       if (bq->init_data.resistor_sense <= 0)
-               return -EINVAL;
-
-       ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
-                       BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG);
-       if (ret < 0)
-               return ret;
-       return (37400 + 6800*ret) / bq->init_data.resistor_sense;
-}
-
-/* set termination current in mA (platform data must provide resistor sense) */
-static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA)
-{
-       int val;
-
-       if (bq->init_data.resistor_sense <= 0)
-               return -EINVAL;
-
-       val = (mA * bq->init_data.resistor_sense - 3400) / 3400;
-       if (val < 0)
-               val = 0;
-       else if (val > 7)
-               val = 7;
-
-       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
-                       BQ2415X_MASK_VI_TERM | BQ2415X_MASK_RESET,
-                       BQ2415X_SHIFT_VI_TERM);
-}
-
-/* get termination current in mA (platform data must provide resistor sense) */
-static int bq2415x_get_termination_current(struct bq2415x_device *bq)
-{
-       int ret;
-
-       if (bq->init_data.resistor_sense <= 0)
-               return -EINVAL;
-
-       ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
-                       BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM);
-       if (ret < 0)
-               return ret;
-       return (3400 + 3400*ret) / bq->init_data.resistor_sense;
-}
-
-/* set default value of property */
-#define bq2415x_set_default_value(bq, prop) \
-       do { \
-               int ret = 0; \
-               if (bq->init_data.prop != -1) \
-                       ret = bq2415x_set_##prop(bq, bq->init_data.prop); \
-               if (ret < 0) \
-                       return ret; \
-       } while (0)
-
-/* set default values of all properties */
-static int bq2415x_set_defaults(struct bq2415x_device *bq)
-{
-       bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
-       bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
-       bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_DISABLE);
-
-       bq2415x_set_default_value(bq, current_limit);
-       bq2415x_set_default_value(bq, weak_battery_voltage);
-       bq2415x_set_default_value(bq, battery_regulation_voltage);
-
-       if (bq->init_data.resistor_sense > 0) {
-               bq2415x_set_default_value(bq, charge_current);
-               bq2415x_set_default_value(bq, termination_current);
-               bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE);
-       }
-
-       bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
-       return 0;
-}
-
-/**** charger mode functions ****/
-
-/* set charger mode */
-static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
-{
-       int ret = 0;
-       int charger = 0;
-       int boost = 0;
-
-       if (mode == BQ2415X_MODE_BOOST)
-               boost = 1;
-       else if (mode != BQ2415X_MODE_OFF)
-               charger = 1;
-
-       if (!charger)
-               ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
-
-       if (!boost)
-               ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
-
-       if (ret < 0)
-               return ret;
-
-       switch (mode) {
-       case BQ2415X_MODE_OFF:
-               dev_dbg(bq->dev, "changing mode to: Offline\n");
-               ret = bq2415x_set_current_limit(bq, 100);
-               break;
-       case BQ2415X_MODE_NONE:
-               dev_dbg(bq->dev, "changing mode to: N/A\n");
-               ret = bq2415x_set_current_limit(bq, 100);
-               break;
-       case BQ2415X_MODE_HOST_CHARGER:
-               dev_dbg(bq->dev, "changing mode to: Host/HUB charger\n");
-               ret = bq2415x_set_current_limit(bq, 500);
-               break;
-       case BQ2415X_MODE_DEDICATED_CHARGER:
-               dev_dbg(bq->dev, "changing mode to: Dedicated charger\n");
-               ret = bq2415x_set_current_limit(bq, 1800);
-               break;
-       case BQ2415X_MODE_BOOST: /* Boost mode */
-               dev_dbg(bq->dev, "changing mode to: Boost\n");
-               ret = bq2415x_set_current_limit(bq, 100);
-               break;
-       }
-
-       if (ret < 0)
-               return ret;
-
-       if (charger)
-               ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
-       else if (boost)
-               ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_ENABLE);
-
-       if (ret < 0)
-               return ret;
-
-       bq2415x_set_default_value(bq, weak_battery_voltage);
-       bq2415x_set_default_value(bq, battery_regulation_voltage);
-
-       bq->mode = mode;
-       sysfs_notify(&bq->charger->dev.kobj, NULL, "mode");
-
-       return 0;
-
-}
-
-static bool bq2415x_update_reported_mode(struct bq2415x_device *bq, int mA)
-{
-       enum bq2415x_mode mode;
-
-       if (mA == 0)
-               mode = BQ2415X_MODE_OFF;
-       else if (mA < 500)
-               mode = BQ2415X_MODE_NONE;
-       else if (mA < 1800)
-               mode = BQ2415X_MODE_HOST_CHARGER;
-       else
-               mode = BQ2415X_MODE_DEDICATED_CHARGER;
-
-       if (bq->reported_mode == mode)
-               return false;
-
-       bq->reported_mode = mode;
-       return true;
-}
-
-static int bq2415x_notifier_call(struct notifier_block *nb,
-               unsigned long val, void *v)
-{
-       struct bq2415x_device *bq =
-               container_of(nb, struct bq2415x_device, nb);
-       struct power_supply *psy = v;
-       union power_supply_propval prop;
-       int ret;
-
-       if (val != PSY_EVENT_PROP_CHANGED)
-               return NOTIFY_OK;
-
-       /* Ignore event if it was not send by notify_node/notify_device */
-       if (bq->notify_node) {
-               if (!psy->dev.parent ||
-                   psy->dev.parent->of_node != bq->notify_node)
-                       return NOTIFY_OK;
-       } else if (bq->init_data.notify_device) {
-               if (strcmp(psy->desc->name, bq->init_data.notify_device) != 0)
-                       return NOTIFY_OK;
-       }
-
-       dev_dbg(bq->dev, "notifier call was called\n");
-
-       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
-                       &prop);
-       if (ret != 0)
-               return NOTIFY_OK;
-
-       if (!bq2415x_update_reported_mode(bq, prop.intval))
-               return NOTIFY_OK;
-
-       /* if automode is not enabled do not tell about reported_mode */
-       if (bq->automode < 1)
-               return NOTIFY_OK;
-
-       schedule_delayed_work(&bq->work, 0);
-
-       return NOTIFY_OK;
-}
-
-/**** timer functions ****/
-
-/* enable/disable auto resetting chip timer */
-static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state)
-{
-       mutex_lock(&bq2415x_timer_mutex);
-
-       if (bq->autotimer == state) {
-               mutex_unlock(&bq2415x_timer_mutex);
-               return;
-       }
-
-       bq->autotimer = state;
-
-       if (state) {
-               schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
-               bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
-               bq->timer_error = NULL;
-       } else {
-               cancel_delayed_work_sync(&bq->work);
-       }
-
-       mutex_unlock(&bq2415x_timer_mutex);
-}
-
-/* called by bq2415x_timer_work on timer error */
-static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
-{
-       bq->timer_error = msg;
-       sysfs_notify(&bq->charger->dev.kobj, NULL, "timer");
-       dev_err(bq->dev, "%s\n", msg);
-       if (bq->automode > 0)
-               bq->automode = 0;
-       bq2415x_set_mode(bq, BQ2415X_MODE_OFF);
-       bq2415x_set_autotimer(bq, 0);
-}
-
-/* delayed work function for auto resetting chip timer */
-static void bq2415x_timer_work(struct work_struct *work)
-{
-       struct bq2415x_device *bq = container_of(work, struct bq2415x_device,
-                                                work.work);
-       int ret;
-       int error;
-       int boost;
-
-       if (bq->automode > 0 && (bq->reported_mode != bq->mode)) {
-               sysfs_notify(&bq->charger->dev.kobj, NULL, "reported_mode");
-               bq2415x_set_mode(bq, bq->reported_mode);
-       }
-
-       if (!bq->autotimer)
-               return;
-
-       ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
-       if (ret < 0) {
-               bq2415x_timer_error(bq, "Resetting timer failed");
-               return;
-       }
-
-       boost = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_STATUS);
-       if (boost < 0) {
-               bq2415x_timer_error(bq, "Unknown error");
-               return;
-       }
-
-       error = bq2415x_exec_command(bq, BQ2415X_FAULT_STATUS);
-       if (error < 0) {
-               bq2415x_timer_error(bq, "Unknown error");
-               return;
-       }
-
-       if (boost) {
-               switch (error) {
-               /* Non fatal errors, chip is OK */
-               case 0: /* No error */
-                       break;
-               case 6: /* Timer expired */
-                       dev_err(bq->dev, "Timer expired\n");
-                       break;
-               case 3: /* Battery voltage too low */
-                       dev_err(bq->dev, "Battery voltage to low\n");
-                       break;
-
-               /* Fatal errors, disable and reset chip */
-               case 1: /* Overvoltage protection (chip fried) */
-                       bq2415x_timer_error(bq,
-                               "Overvoltage protection (chip fried)");
-                       return;
-               case 2: /* Overload */
-                       bq2415x_timer_error(bq, "Overload");
-                       return;
-               case 4: /* Battery overvoltage protection */
-                       bq2415x_timer_error(bq,
-                               "Battery overvoltage protection");
-                       return;
-               case 5: /* Thermal shutdown (too hot) */
-                       bq2415x_timer_error(bq,
-                                       "Thermal shutdown (too hot)");
-                       return;
-               case 7: /* N/A */
-                       bq2415x_timer_error(bq, "Unknown error");
-                       return;
-               }
-       } else {
-               switch (error) {
-               /* Non fatal errors, chip is OK */
-               case 0: /* No error */
-                       break;
-               case 2: /* Sleep mode */
-                       dev_err(bq->dev, "Sleep mode\n");
-                       break;
-               case 3: /* Poor input source */
-                       dev_err(bq->dev, "Poor input source\n");
-                       break;
-               case 6: /* Timer expired */
-                       dev_err(bq->dev, "Timer expired\n");
-                       break;
-               case 7: /* No battery */
-                       dev_err(bq->dev, "No battery\n");
-                       break;
-
-               /* Fatal errors, disable and reset chip */
-               case 1: /* Overvoltage protection (chip fried) */
-                       bq2415x_timer_error(bq,
-                               "Overvoltage protection (chip fried)");
-                       return;
-               case 4: /* Battery overvoltage protection */
-                       bq2415x_timer_error(bq,
-                               "Battery overvoltage protection");
-                       return;
-               case 5: /* Thermal shutdown (too hot) */
-                       bq2415x_timer_error(bq,
-                               "Thermal shutdown (too hot)");
-                       return;
-               }
-       }
-
-       schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
-}
-
-/**** power supply interface code ****/
-
-static enum power_supply_property bq2415x_power_supply_props[] = {
-       /* TODO: maybe add more power supply properties */
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-};
-
-static int bq2415x_power_supply_get_property(struct power_supply *psy,
-                                            enum power_supply_property psp,
-                                            union power_supply_propval *val)
-{
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS);
-               if (ret < 0)
-                       return ret;
-               else if (ret == 0) /* Ready */
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               else if (ret == 1) /* Charge in progress */
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else if (ret == 2) /* Charge done */
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = bq->model;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int bq2415x_power_supply_init(struct bq2415x_device *bq)
-{
-       int ret;
-       int chip;
-       char revstr[8];
-       struct power_supply_config psy_cfg = { .drv_data = bq, };
-
-       bq->charger_desc.name = bq->name;
-       bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
-       bq->charger_desc.properties = bq2415x_power_supply_props;
-       bq->charger_desc.num_properties =
-                       ARRAY_SIZE(bq2415x_power_supply_props);
-       bq->charger_desc.get_property = bq2415x_power_supply_get_property;
-
-       ret = bq2415x_detect_chip(bq);
-       if (ret < 0)
-               chip = BQUNKNOWN;
-       else
-               chip = ret;
-
-       ret = bq2415x_detect_revision(bq);
-       if (ret < 0)
-               strcpy(revstr, "unknown");
-       else
-               sprintf(revstr, "1.%d", ret);
-
-       bq->model = kasprintf(GFP_KERNEL,
-                               "chip %s, revision %s, vender code %.3d",
-                               bq2415x_chip_name[chip], revstr,
-                               bq2415x_get_vender_code(bq));
-       if (!bq->model) {
-               dev_err(bq->dev, "failed to allocate model name\n");
-               return -ENOMEM;
-       }
-
-       bq->charger = power_supply_register(bq->dev, &bq->charger_desc,
-                                           &psy_cfg);
-       if (IS_ERR(bq->charger)) {
-               kfree(bq->model);
-               return PTR_ERR(bq->charger);
-       }
-
-       return 0;
-}
-
-static void bq2415x_power_supply_exit(struct bq2415x_device *bq)
-{
-       bq->autotimer = 0;
-       if (bq->automode > 0)
-               bq->automode = 0;
-       cancel_delayed_work_sync(&bq->work);
-       power_supply_unregister(bq->charger);
-       kfree(bq->model);
-}
-
-/**** additional sysfs entries for power supply interface ****/
-
-/* show *_status entries */
-static ssize_t bq2415x_sysfs_show_status(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       enum bq2415x_command command;
-       int ret;
-
-       if (strcmp(attr->attr.name, "otg_status") == 0)
-               command = BQ2415X_OTG_STATUS;
-       else if (strcmp(attr->attr.name, "charge_status") == 0)
-               command = BQ2415X_CHARGE_STATUS;
-       else if (strcmp(attr->attr.name, "boost_status") == 0)
-               command = BQ2415X_BOOST_STATUS;
-       else if (strcmp(attr->attr.name, "fault_status") == 0)
-               command = BQ2415X_FAULT_STATUS;
-       else
-               return -EINVAL;
-
-       ret = bq2415x_exec_command(bq, command);
-       if (ret < 0)
-               return ret;
-       return sprintf(buf, "%d\n", ret);
-}
-
-/*
- * set timer entry:
- *    auto - enable auto mode
- *    off - disable auto mode
- *    (other values) - reset chip timer
- */
-static ssize_t bq2415x_sysfs_set_timer(struct device *dev,
-                                      struct device_attribute *attr,
-                                      const char *buf,
-                                      size_t count)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       int ret = 0;
-
-       if (strncmp(buf, "auto", 4) == 0)
-               bq2415x_set_autotimer(bq, 1);
-       else if (strncmp(buf, "off", 3) == 0)
-               bq2415x_set_autotimer(bq, 0);
-       else
-               ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
-
-       if (ret < 0)
-               return ret;
-       return count;
-}
-
-/* show timer entry (auto or off) */
-static ssize_t bq2415x_sysfs_show_timer(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-
-       if (bq->timer_error)
-               return sprintf(buf, "%s\n", bq->timer_error);
-
-       if (bq->autotimer)
-               return sprintf(buf, "auto\n");
-       return sprintf(buf, "off\n");
-}
-
-/*
- * set mode entry:
- *    auto - if automode is supported, enable it and set mode to reported
- *    none - disable charger and boost mode
- *    host - charging mode for host/hub chargers (current limit 500mA)
- *    dedicated - charging mode for dedicated chargers (unlimited current limit)
- *    boost - disable charger and enable boost mode
- */
-static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
-                                     struct device_attribute *attr,
-                                     const char *buf,
-                                     size_t count)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       enum bq2415x_mode mode;
-       int ret = 0;
-
-       if (strncmp(buf, "auto", 4) == 0) {
-               if (bq->automode < 0)
-                       return -EINVAL;
-               bq->automode = 1;
-               mode = bq->reported_mode;
-       } else if (strncmp(buf, "off", 3) == 0) {
-               if (bq->automode > 0)
-                       bq->automode = 0;
-               mode = BQ2415X_MODE_OFF;
-       } else if (strncmp(buf, "none", 4) == 0) {
-               if (bq->automode > 0)
-                       bq->automode = 0;
-               mode = BQ2415X_MODE_NONE;
-       } else if (strncmp(buf, "host", 4) == 0) {
-               if (bq->automode > 0)
-                       bq->automode = 0;
-               mode = BQ2415X_MODE_HOST_CHARGER;
-       } else if (strncmp(buf, "dedicated", 9) == 0) {
-               if (bq->automode > 0)
-                       bq->automode = 0;
-               mode = BQ2415X_MODE_DEDICATED_CHARGER;
-       } else if (strncmp(buf, "boost", 5) == 0) {
-               if (bq->automode > 0)
-                       bq->automode = 0;
-               mode = BQ2415X_MODE_BOOST;
-       } else if (strncmp(buf, "reset", 5) == 0) {
-               bq2415x_reset_chip(bq);
-               bq2415x_set_defaults(bq);
-               if (bq->automode <= 0)
-                       return count;
-               bq->automode = 1;
-               mode = bq->reported_mode;
-       } else {
-               return -EINVAL;
-       }
-
-       ret = bq2415x_set_mode(bq, mode);
-       if (ret < 0)
-               return ret;
-       return count;
-}
-
-/* show mode entry (auto, none, host, dedicated or boost) */
-static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
-                                      struct device_attribute *attr,
-                                      char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       ssize_t ret = 0;
-
-       if (bq->automode > 0)
-               ret += sprintf(buf+ret, "auto (");
-
-       switch (bq->mode) {
-       case BQ2415X_MODE_OFF:
-               ret += sprintf(buf+ret, "off");
-               break;
-       case BQ2415X_MODE_NONE:
-               ret += sprintf(buf+ret, "none");
-               break;
-       case BQ2415X_MODE_HOST_CHARGER:
-               ret += sprintf(buf+ret, "host");
-               break;
-       case BQ2415X_MODE_DEDICATED_CHARGER:
-               ret += sprintf(buf+ret, "dedicated");
-               break;
-       case BQ2415X_MODE_BOOST:
-               ret += sprintf(buf+ret, "boost");
-               break;
-       }
-
-       if (bq->automode > 0)
-               ret += sprintf(buf+ret, ")");
-
-       ret += sprintf(buf+ret, "\n");
-       return ret;
-}
-
-/* show reported_mode entry (none, host, dedicated or boost) */
-static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
-                                               struct device_attribute *attr,
-                                               char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-
-       if (bq->automode < 0)
-               return -EINVAL;
-
-       switch (bq->reported_mode) {
-       case BQ2415X_MODE_OFF:
-               return sprintf(buf, "off\n");
-       case BQ2415X_MODE_NONE:
-               return sprintf(buf, "none\n");
-       case BQ2415X_MODE_HOST_CHARGER:
-               return sprintf(buf, "host\n");
-       case BQ2415X_MODE_DEDICATED_CHARGER:
-               return sprintf(buf, "dedicated\n");
-       case BQ2415X_MODE_BOOST:
-               return sprintf(buf, "boost\n");
-       }
-
-       return -EINVAL;
-}
-
-/* directly set raw value to chip register, format: 'register value' */
-static ssize_t bq2415x_sysfs_set_registers(struct device *dev,
-                                          struct device_attribute *attr,
-                                          const char *buf,
-                                          size_t count)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       ssize_t ret = 0;
-       unsigned int reg;
-       unsigned int val;
-
-       if (sscanf(buf, "%x %x", &reg, &val) != 2)
-               return -EINVAL;
-
-       if (reg > 4 || val > 255)
-               return -EINVAL;
-
-       ret = bq2415x_i2c_write(bq, reg, val);
-       if (ret < 0)
-               return ret;
-       return count;
-}
-
-/* print value of chip register, format: 'register=value' */
-static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq,
-                                      u8 reg,
-                                      char *buf)
-{
-       int ret = bq2415x_i2c_read(bq, reg);
-
-       if (ret < 0)
-               return sprintf(buf, "%#.2x=error %d\n", reg, ret);
-       return sprintf(buf, "%#.2x=%#.2x\n", reg, ret);
-}
-
-/* show all raw values of chip register, format per line: 'register=value' */
-static ssize_t bq2415x_sysfs_show_registers(struct device *dev,
-                                           struct device_attribute *attr,
-                                           char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       ssize_t ret = 0;
-
-       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret);
-       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CONTROL, buf+ret);
-       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VOLTAGE, buf+ret);
-       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VENDER, buf+ret);
-       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CURRENT, buf+ret);
-       return ret;
-}
-
-/* set current and voltage limit entries (in mA or mV) */
-static ssize_t bq2415x_sysfs_set_limit(struct device *dev,
-                                      struct device_attribute *attr,
-                                      const char *buf,
-                                      size_t count)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       long val;
-       int ret;
-
-       if (kstrtol(buf, 10, &val) < 0)
-               return -EINVAL;
-
-       if (strcmp(attr->attr.name, "current_limit") == 0)
-               ret = bq2415x_set_current_limit(bq, val);
-       else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
-               ret = bq2415x_set_weak_battery_voltage(bq, val);
-       else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
-               ret = bq2415x_set_battery_regulation_voltage(bq, val);
-       else if (strcmp(attr->attr.name, "charge_current") == 0)
-               ret = bq2415x_set_charge_current(bq, val);
-       else if (strcmp(attr->attr.name, "termination_current") == 0)
-               ret = bq2415x_set_termination_current(bq, val);
-       else
-               return -EINVAL;
-
-       if (ret < 0)
-               return ret;
-       return count;
-}
-
-/* show current and voltage limit entries (in mA or mV) */
-static ssize_t bq2415x_sysfs_show_limit(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       int ret;
-
-       if (strcmp(attr->attr.name, "current_limit") == 0)
-               ret = bq2415x_get_current_limit(bq);
-       else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
-               ret = bq2415x_get_weak_battery_voltage(bq);
-       else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
-               ret = bq2415x_get_battery_regulation_voltage(bq);
-       else if (strcmp(attr->attr.name, "charge_current") == 0)
-               ret = bq2415x_get_charge_current(bq);
-       else if (strcmp(attr->attr.name, "termination_current") == 0)
-               ret = bq2415x_get_termination_current(bq);
-       else
-               return -EINVAL;
-
-       if (ret < 0)
-               return ret;
-       return sprintf(buf, "%d\n", ret);
-}
-
-/* set *_enable entries */
-static ssize_t bq2415x_sysfs_set_enable(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf,
-                                       size_t count)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       enum bq2415x_command command;
-       long val;
-       int ret;
-
-       if (kstrtol(buf, 10, &val) < 0)
-               return -EINVAL;
-
-       if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
-               command = val ? BQ2415X_CHARGE_TERMINATION_ENABLE :
-                       BQ2415X_CHARGE_TERMINATION_DISABLE;
-       else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
-               command = val ? BQ2415X_HIGH_IMPEDANCE_ENABLE :
-                       BQ2415X_HIGH_IMPEDANCE_DISABLE;
-       else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
-               command = val ? BQ2415X_OTG_PIN_ENABLE :
-                       BQ2415X_OTG_PIN_DISABLE;
-       else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
-               command = val ? BQ2415X_STAT_PIN_ENABLE :
-                       BQ2415X_STAT_PIN_DISABLE;
-       else
-               return -EINVAL;
-
-       ret = bq2415x_exec_command(bq, command);
-       if (ret < 0)
-               return ret;
-       return count;
-}
-
-/* show *_enable entries */
-static ssize_t bq2415x_sysfs_show_enable(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
-       enum bq2415x_command command;
-       int ret;
-
-       if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
-               command = BQ2415X_CHARGE_TERMINATION_STATUS;
-       else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
-               command = BQ2415X_HIGH_IMPEDANCE_STATUS;
-       else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
-               command = BQ2415X_OTG_PIN_STATUS;
-       else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
-               command = BQ2415X_STAT_PIN_STATUS;
-       else
-               return -EINVAL;
-
-       ret = bq2415x_exec_command(bq, command);
-       if (ret < 0)
-               return ret;
-       return sprintf(buf, "%d\n", ret);
-}
-
-static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
-static DEVICE_ATTR(weak_battery_voltage, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
-static DEVICE_ATTR(battery_regulation_voltage, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
-static DEVICE_ATTR(charge_current, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
-static DEVICE_ATTR(termination_current, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
-
-static DEVICE_ATTR(charge_termination_enable, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
-static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
-static DEVICE_ATTR(otg_pin_enable, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
-static DEVICE_ATTR(stat_pin_enable, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
-
-static DEVICE_ATTR(reported_mode, S_IRUGO,
-               bq2415x_sysfs_show_reported_mode, NULL);
-static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_mode, bq2415x_sysfs_set_mode);
-static DEVICE_ATTR(timer, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_timer, bq2415x_sysfs_set_timer);
-
-static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO,
-               bq2415x_sysfs_show_registers, bq2415x_sysfs_set_registers);
-
-static DEVICE_ATTR(otg_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
-static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
-static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
-static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
-
-static struct attribute *bq2415x_sysfs_attributes[] = {
-       /*
-        * TODO: some (appropriate) of these attrs should be switched to
-        * use power supply class props.
-        */
-       &dev_attr_current_limit.attr,
-       &dev_attr_weak_battery_voltage.attr,
-       &dev_attr_battery_regulation_voltage.attr,
-       &dev_attr_charge_current.attr,
-       &dev_attr_termination_current.attr,
-
-       &dev_attr_charge_termination_enable.attr,
-       &dev_attr_high_impedance_enable.attr,
-       &dev_attr_otg_pin_enable.attr,
-       &dev_attr_stat_pin_enable.attr,
-
-       &dev_attr_reported_mode.attr,
-       &dev_attr_mode.attr,
-       &dev_attr_timer.attr,
-
-       &dev_attr_registers.attr,
-
-       &dev_attr_otg_status.attr,
-       &dev_attr_charge_status.attr,
-       &dev_attr_boost_status.attr,
-       &dev_attr_fault_status.attr,
-       NULL,
-};
-
-static const struct attribute_group bq2415x_sysfs_attr_group = {
-       .attrs = bq2415x_sysfs_attributes,
-};
-
-static int bq2415x_sysfs_init(struct bq2415x_device *bq)
-{
-       return sysfs_create_group(&bq->charger->dev.kobj,
-                       &bq2415x_sysfs_attr_group);
-}
-
-static void bq2415x_sysfs_exit(struct bq2415x_device *bq)
-{
-       sysfs_remove_group(&bq->charger->dev.kobj, &bq2415x_sysfs_attr_group);
-}
-
-/* main bq2415x probe function */
-static int bq2415x_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id)
-{
-       int ret;
-       int num;
-       char *name = NULL;
-       struct bq2415x_device *bq;
-       struct device_node *np = client->dev.of_node;
-       struct bq2415x_platform_data *pdata = client->dev.platform_data;
-       const struct acpi_device_id *acpi_id = NULL;
-       struct power_supply *notify_psy = NULL;
-       union power_supply_propval prop;
-
-       if (!np && !pdata && !ACPI_HANDLE(&client->dev)) {
-               dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n");
-               return -ENODEV;
-       }
-
-       /* Get new ID for the new device */
-       mutex_lock(&bq2415x_id_mutex);
-       num = idr_alloc(&bq2415x_id, client, 0, 0, GFP_KERNEL);
-       mutex_unlock(&bq2415x_id_mutex);
-       if (num < 0)
-               return num;
-
-       if (id) {
-               name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
-       } else if (ACPI_HANDLE(&client->dev)) {
-               acpi_id =
-                       acpi_match_device(client->dev.driver->acpi_match_table,
-                                         &client->dev);
-               name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
-       }
-       if (!name) {
-               dev_err(&client->dev, "failed to allocate device name\n");
-               ret = -ENOMEM;
-               goto error_1;
-       }
-
-       bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
-       if (!bq) {
-               ret = -ENOMEM;
-               goto error_2;
-       }
-
-       i2c_set_clientdata(client, bq);
-
-       bq->id = num;
-       bq->dev = &client->dev;
-       if (id)
-               bq->chip = id->driver_data;
-       else if (ACPI_HANDLE(bq->dev))
-               bq->chip = acpi_id->driver_data;
-       bq->name = name;
-       bq->mode = BQ2415X_MODE_OFF;
-       bq->reported_mode = BQ2415X_MODE_OFF;
-       bq->autotimer = 0;
-       bq->automode = 0;
-
-       if (np || ACPI_HANDLE(bq->dev)) {
-               ret = device_property_read_u32(bq->dev,
-                                              "ti,current-limit",
-                                              &bq->init_data.current_limit);
-               if (ret)
-                       goto error_2;
-               ret = device_property_read_u32(bq->dev,
-                                       "ti,weak-battery-voltage",
-                                       &bq->init_data.weak_battery_voltage);
-               if (ret)
-                       goto error_2;
-               ret = device_property_read_u32(bq->dev,
-                               "ti,battery-regulation-voltage",
-                               &bq->init_data.battery_regulation_voltage);
-               if (ret)
-                       goto error_2;
-               ret = device_property_read_u32(bq->dev,
-                                              "ti,charge-current",
-                                              &bq->init_data.charge_current);
-               if (ret)
-                       goto error_2;
-               ret = device_property_read_u32(bq->dev,
-                               "ti,termination-current",
-                               &bq->init_data.termination_current);
-               if (ret)
-                       goto error_2;
-               ret = device_property_read_u32(bq->dev,
-                                              "ti,resistor-sense",
-                                              &bq->init_data.resistor_sense);
-               if (ret)
-                       goto error_2;
-               if (np)
-                       bq->notify_node = of_parse_phandle(np,
-                                               "ti,usb-charger-detection", 0);
-       } else {
-               memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
-       }
-
-       bq2415x_reset_chip(bq);
-
-       ret = bq2415x_power_supply_init(bq);
-       if (ret) {
-               dev_err(bq->dev, "failed to register power supply: %d\n", ret);
-               goto error_2;
-       }
-
-       ret = bq2415x_sysfs_init(bq);
-       if (ret) {
-               dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
-               goto error_3;
-       }
-
-       ret = bq2415x_set_defaults(bq);
-       if (ret) {
-               dev_err(bq->dev, "failed to set default values: %d\n", ret);
-               goto error_4;
-       }
-
-       if (bq->notify_node || bq->init_data.notify_device) {
-               bq->nb.notifier_call = bq2415x_notifier_call;
-               ret = power_supply_reg_notifier(&bq->nb);
-               if (ret) {
-                       dev_err(bq->dev, "failed to reg notifier: %d\n", ret);
-                       goto error_4;
-               }
-
-               bq->automode = 1;
-               dev_info(bq->dev, "automode supported, waiting for events\n");
-       } else {
-               bq->automode = -1;
-               dev_info(bq->dev, "automode not supported\n");
-       }
-
-       /* Query for initial reported_mode and set it */
-       if (bq->nb.notifier_call) {
-               if (np) {
-                       notify_psy = power_supply_get_by_phandle(np,
-                                               "ti,usb-charger-detection");
-                       if (IS_ERR(notify_psy))
-                               notify_psy = NULL;
-               } else if (bq->init_data.notify_device) {
-                       notify_psy = power_supply_get_by_name(
-                                               bq->init_data.notify_device);
-               }
-       }
-       if (notify_psy) {
-               ret = power_supply_get_property(notify_psy,
-                                       POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
-               power_supply_put(notify_psy);
-
-               if (ret == 0) {
-                       bq2415x_update_reported_mode(bq, prop.intval);
-                       bq2415x_set_mode(bq, bq->reported_mode);
-               }
-       }
-
-       INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work);
-       bq2415x_set_autotimer(bq, 1);
-
-       dev_info(bq->dev, "driver registered\n");
-       return 0;
-
-error_4:
-       bq2415x_sysfs_exit(bq);
-error_3:
-       bq2415x_power_supply_exit(bq);
-error_2:
-       if (bq)
-               of_node_put(bq->notify_node);
-       kfree(name);
-error_1:
-       mutex_lock(&bq2415x_id_mutex);
-       idr_remove(&bq2415x_id, num);
-       mutex_unlock(&bq2415x_id_mutex);
-
-       return ret;
-}
-
-/* main bq2415x remove function */
-
-static int bq2415x_remove(struct i2c_client *client)
-{
-       struct bq2415x_device *bq = i2c_get_clientdata(client);
-
-       if (bq->nb.notifier_call)
-               power_supply_unreg_notifier(&bq->nb);
-
-       of_node_put(bq->notify_node);
-       bq2415x_sysfs_exit(bq);
-       bq2415x_power_supply_exit(bq);
-
-       bq2415x_reset_chip(bq);
-
-       mutex_lock(&bq2415x_id_mutex);
-       idr_remove(&bq2415x_id, bq->id);
-       mutex_unlock(&bq2415x_id_mutex);
-
-       dev_info(bq->dev, "driver unregistered\n");
-
-       kfree(bq->name);
-
-       return 0;
-}
-
-static const struct i2c_device_id bq2415x_i2c_id_table[] = {
-       { "bq2415x", BQUNKNOWN },
-       { "bq24150", BQ24150 },
-       { "bq24150a", BQ24150A },
-       { "bq24151", BQ24151 },
-       { "bq24151a", BQ24151A },
-       { "bq24152", BQ24152 },
-       { "bq24153", BQ24153 },
-       { "bq24153a", BQ24153A },
-       { "bq24155", BQ24155 },
-       { "bq24156", BQ24156 },
-       { "bq24156a", BQ24156A },
-       { "bq24157s", BQ24157S },
-       { "bq24158", BQ24158 },
-       {},
-};
-MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table);
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id bq2415x_i2c_acpi_match[] = {
-       { "BQ2415X", BQUNKNOWN },
-       { "BQ241500", BQ24150 },
-       { "BQA24150", BQ24150A },
-       { "BQ241510", BQ24151 },
-       { "BQA24151", BQ24151A },
-       { "BQ241520", BQ24152 },
-       { "BQ241530", BQ24153 },
-       { "BQA24153", BQ24153A },
-       { "BQ241550", BQ24155 },
-       { "BQ241560", BQ24156 },
-       { "BQA24156", BQ24156A },
-       { "BQS24157", BQ24157S },
-       { "BQ241580", BQ24158 },
-       {},
-};
-MODULE_DEVICE_TABLE(acpi, bq2415x_i2c_acpi_match);
-#endif
-
-#ifdef CONFIG_OF
-static const struct of_device_id bq2415x_of_match_table[] = {
-       { .compatible = "ti,bq24150" },
-       { .compatible = "ti,bq24150a" },
-       { .compatible = "ti,bq24151" },
-       { .compatible = "ti,bq24151a" },
-       { .compatible = "ti,bq24152" },
-       { .compatible = "ti,bq24153" },
-       { .compatible = "ti,bq24153a" },
-       { .compatible = "ti,bq24155" },
-       { .compatible = "ti,bq24156" },
-       { .compatible = "ti,bq24156a" },
-       { .compatible = "ti,bq24157s" },
-       { .compatible = "ti,bq24158" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, bq2415x_of_match_table);
-#endif
-
-static struct i2c_driver bq2415x_driver = {
-       .driver = {
-               .name = "bq2415x-charger",
-               .of_match_table = of_match_ptr(bq2415x_of_match_table),
-               .acpi_match_table = ACPI_PTR(bq2415x_i2c_acpi_match),
-       },
-       .probe = bq2415x_probe,
-       .remove = bq2415x_remove,
-       .id_table = bq2415x_i2c_id_table,
-};
-module_i2c_driver(bq2415x_driver);
-
-MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
-MODULE_DESCRIPTION("bq2415x charger driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c
deleted file mode 100644 (file)
index f5746b9..0000000
+++ /dev/null
@@ -1,1546 +0,0 @@
-/*
- * Driver for the TI bq24190 battery charger.
- *
- * Author: Mark A. Greer <mgreer@animalcreek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/of_irq.h>
-#include <linux/of_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/power_supply.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-
-#include <linux/power/bq24190_charger.h>
-
-
-#define        BQ24190_MANUFACTURER    "Texas Instruments"
-
-#define BQ24190_REG_ISC                0x00 /* Input Source Control */
-#define BQ24190_REG_ISC_EN_HIZ_MASK            BIT(7)
-#define BQ24190_REG_ISC_EN_HIZ_SHIFT           7
-#define BQ24190_REG_ISC_VINDPM_MASK            (BIT(6) | BIT(5) | BIT(4) | \
-                                                BIT(3))
-#define BQ24190_REG_ISC_VINDPM_SHIFT           3
-#define BQ24190_REG_ISC_IINLIM_MASK            (BIT(2) | BIT(1) | BIT(0))
-#define BQ24190_REG_ISC_IINLIM_SHIFT           0
-
-#define BQ24190_REG_POC                0x01 /* Power-On Configuration */
-#define BQ24190_REG_POC_RESET_MASK             BIT(7)
-#define BQ24190_REG_POC_RESET_SHIFT            7
-#define BQ24190_REG_POC_WDT_RESET_MASK         BIT(6)
-#define BQ24190_REG_POC_WDT_RESET_SHIFT                6
-#define BQ24190_REG_POC_CHG_CONFIG_MASK                (BIT(5) | BIT(4))
-#define BQ24190_REG_POC_CHG_CONFIG_SHIFT       4
-#define BQ24190_REG_POC_SYS_MIN_MASK           (BIT(3) | BIT(2) | BIT(1))
-#define BQ24190_REG_POC_SYS_MIN_SHIFT          1
-#define BQ24190_REG_POC_BOOST_LIM_MASK         BIT(0)
-#define BQ24190_REG_POC_BOOST_LIM_SHIFT                0
-
-#define BQ24190_REG_CCC                0x02 /* Charge Current Control */
-#define BQ24190_REG_CCC_ICHG_MASK              (BIT(7) | BIT(6) | BIT(5) | \
-                                                BIT(4) | BIT(3) | BIT(2))
-#define BQ24190_REG_CCC_ICHG_SHIFT             2
-#define BQ24190_REG_CCC_FORCE_20PCT_MASK       BIT(0)
-#define BQ24190_REG_CCC_FORCE_20PCT_SHIFT      0
-
-#define BQ24190_REG_PCTCC      0x03 /* Pre-charge/Termination Current Cntl */
-#define BQ24190_REG_PCTCC_IPRECHG_MASK         (BIT(7) | BIT(6) | BIT(5) | \
-                                                BIT(4))
-#define BQ24190_REG_PCTCC_IPRECHG_SHIFT                4
-#define BQ24190_REG_PCTCC_ITERM_MASK           (BIT(3) | BIT(2) | BIT(1) | \
-                                                BIT(0))
-#define BQ24190_REG_PCTCC_ITERM_SHIFT          0
-
-#define BQ24190_REG_CVC                0x04 /* Charge Voltage Control */
-#define BQ24190_REG_CVC_VREG_MASK              (BIT(7) | BIT(6) | BIT(5) | \
-                                                BIT(4) | BIT(3) | BIT(2))
-#define BQ24190_REG_CVC_VREG_SHIFT             2
-#define BQ24190_REG_CVC_BATLOWV_MASK           BIT(1)
-#define BQ24190_REG_CVC_BATLOWV_SHIFT          1
-#define BQ24190_REG_CVC_VRECHG_MASK            BIT(0)
-#define BQ24190_REG_CVC_VRECHG_SHIFT           0
-
-#define BQ24190_REG_CTTC       0x05 /* Charge Term/Timer Control */
-#define BQ24190_REG_CTTC_EN_TERM_MASK          BIT(7)
-#define BQ24190_REG_CTTC_EN_TERM_SHIFT         7
-#define BQ24190_REG_CTTC_TERM_STAT_MASK                BIT(6)
-#define BQ24190_REG_CTTC_TERM_STAT_SHIFT       6
-#define BQ24190_REG_CTTC_WATCHDOG_MASK         (BIT(5) | BIT(4))
-#define BQ24190_REG_CTTC_WATCHDOG_SHIFT                4
-#define BQ24190_REG_CTTC_EN_TIMER_MASK         BIT(3)
-#define BQ24190_REG_CTTC_EN_TIMER_SHIFT                3
-#define BQ24190_REG_CTTC_CHG_TIMER_MASK                (BIT(2) | BIT(1))
-#define BQ24190_REG_CTTC_CHG_TIMER_SHIFT       1
-#define BQ24190_REG_CTTC_JEITA_ISET_MASK       BIT(0)
-#define BQ24190_REG_CTTC_JEITA_ISET_SHIFT      0
-
-#define BQ24190_REG_ICTRC      0x06 /* IR Comp/Thermal Regulation Control */
-#define BQ24190_REG_ICTRC_BAT_COMP_MASK                (BIT(7) | BIT(6) | BIT(5))
-#define BQ24190_REG_ICTRC_BAT_COMP_SHIFT       5
-#define BQ24190_REG_ICTRC_VCLAMP_MASK          (BIT(4) | BIT(3) | BIT(2))
-#define BQ24190_REG_ICTRC_VCLAMP_SHIFT         2
-#define BQ24190_REG_ICTRC_TREG_MASK            (BIT(1) | BIT(0))
-#define BQ24190_REG_ICTRC_TREG_SHIFT           0
-
-#define BQ24190_REG_MOC                0x07 /* Misc. Operation Control */
-#define BQ24190_REG_MOC_DPDM_EN_MASK           BIT(7)
-#define BQ24190_REG_MOC_DPDM_EN_SHIFT          7
-#define BQ24190_REG_MOC_TMR2X_EN_MASK          BIT(6)
-#define BQ24190_REG_MOC_TMR2X_EN_SHIFT         6
-#define BQ24190_REG_MOC_BATFET_DISABLE_MASK    BIT(5)
-#define BQ24190_REG_MOC_BATFET_DISABLE_SHIFT   5
-#define BQ24190_REG_MOC_JEITA_VSET_MASK                BIT(4)
-#define BQ24190_REG_MOC_JEITA_VSET_SHIFT       4
-#define BQ24190_REG_MOC_INT_MASK_MASK          (BIT(1) | BIT(0))
-#define BQ24190_REG_MOC_INT_MASK_SHIFT         0
-
-#define BQ24190_REG_SS         0x08 /* System Status */
-#define BQ24190_REG_SS_VBUS_STAT_MASK          (BIT(7) | BIT(6))
-#define BQ24190_REG_SS_VBUS_STAT_SHIFT         6
-#define BQ24190_REG_SS_CHRG_STAT_MASK          (BIT(5) | BIT(4))
-#define BQ24190_REG_SS_CHRG_STAT_SHIFT         4
-#define BQ24190_REG_SS_DPM_STAT_MASK           BIT(3)
-#define BQ24190_REG_SS_DPM_STAT_SHIFT          3
-#define BQ24190_REG_SS_PG_STAT_MASK            BIT(2)
-#define BQ24190_REG_SS_PG_STAT_SHIFT           2
-#define BQ24190_REG_SS_THERM_STAT_MASK         BIT(1)
-#define BQ24190_REG_SS_THERM_STAT_SHIFT                1
-#define BQ24190_REG_SS_VSYS_STAT_MASK          BIT(0)
-#define BQ24190_REG_SS_VSYS_STAT_SHIFT         0
-
-#define BQ24190_REG_F          0x09 /* Fault */
-#define BQ24190_REG_F_WATCHDOG_FAULT_MASK      BIT(7)
-#define BQ24190_REG_F_WATCHDOG_FAULT_SHIFT     7
-#define BQ24190_REG_F_BOOST_FAULT_MASK         BIT(6)
-#define BQ24190_REG_F_BOOST_FAULT_SHIFT                6
-#define BQ24190_REG_F_CHRG_FAULT_MASK          (BIT(5) | BIT(4))
-#define BQ24190_REG_F_CHRG_FAULT_SHIFT         4
-#define BQ24190_REG_F_BAT_FAULT_MASK           BIT(3)
-#define BQ24190_REG_F_BAT_FAULT_SHIFT          3
-#define BQ24190_REG_F_NTC_FAULT_MASK           (BIT(2) | BIT(1) | BIT(0))
-#define BQ24190_REG_F_NTC_FAULT_SHIFT          0
-
-#define BQ24190_REG_VPRS       0x0A /* Vendor/Part/Revision Status */
-#define BQ24190_REG_VPRS_PN_MASK               (BIT(5) | BIT(4) | BIT(3))
-#define BQ24190_REG_VPRS_PN_SHIFT              3
-#define BQ24190_REG_VPRS_PN_24190                      0x4
-#define BQ24190_REG_VPRS_PN_24192                      0x5 /* Also 24193 */
-#define BQ24190_REG_VPRS_PN_24192I                     0x3
-#define BQ24190_REG_VPRS_TS_PROFILE_MASK       BIT(2)
-#define BQ24190_REG_VPRS_TS_PROFILE_SHIFT      2
-#define BQ24190_REG_VPRS_DEV_REG_MASK          (BIT(1) | BIT(0))
-#define BQ24190_REG_VPRS_DEV_REG_SHIFT         0
-
-/*
- * The FAULT register is latched by the bq24190 (except for NTC_FAULT)
- * so the first read after a fault returns the latched value and subsequent
- * reads return the current value.  In order to return the fault status
- * to the user, have the interrupt handler save the reg's value and retrieve
- * it in the appropriate health/status routine.  Each routine has its own
- * flag indicating whether it should use the value stored by the last run
- * of the interrupt handler or do an actual reg read.  That way each routine
- * can report back whatever fault may have occured.
- */
-struct bq24190_dev_info {
-       struct i2c_client               *client;
-       struct device                   *dev;
-       struct power_supply             *charger;
-       struct power_supply             *battery;
-       char                            model_name[I2C_NAME_SIZE];
-       kernel_ulong_t                  model;
-       unsigned int                    gpio_int;
-       unsigned int                    irq;
-       struct mutex                    f_reg_lock;
-       bool                            first_time;
-       bool                            charger_health_valid;
-       bool                            battery_health_valid;
-       bool                            battery_status_valid;
-       u8                              f_reg;
-       u8                              ss_reg;
-       u8                              watchdog;
-};
-
-/*
- * The tables below provide a 2-way mapping for the value that goes in
- * the register field and the real-world value that it represents.
- * The index of the array is the value that goes in the register; the
- * number at that index in the array is the real-world value that it
- * represents.
- */
-/* REG02[7:2] (ICHG) in uAh */
-static const int bq24190_ccc_ichg_values[] = {
-        512000,  576000,  640000,  704000,  768000,  832000,  896000,  960000,
-       1024000, 1088000, 1152000, 1216000, 1280000, 1344000, 1408000, 1472000,
-       1536000, 1600000, 1664000, 1728000, 1792000, 1856000, 1920000, 1984000,
-       2048000, 2112000, 2176000, 2240000, 2304000, 2368000, 2432000, 2496000,
-       2560000, 2624000, 2688000, 2752000, 2816000, 2880000, 2944000, 3008000,
-       3072000, 3136000, 3200000, 3264000, 3328000, 3392000, 3456000, 3520000,
-       3584000, 3648000, 3712000, 3776000, 3840000, 3904000, 3968000, 4032000,
-       4096000, 4160000, 4224000, 4288000, 4352000, 4416000, 4480000, 4544000
-};
-
-/* REG04[7:2] (VREG) in uV */
-static const int bq24190_cvc_vreg_values[] = {
-       3504000, 3520000, 3536000, 3552000, 3568000, 3584000, 3600000, 3616000,
-       3632000, 3648000, 3664000, 3680000, 3696000, 3712000, 3728000, 3744000,
-       3760000, 3776000, 3792000, 3808000, 3824000, 3840000, 3856000, 3872000,
-       3888000, 3904000, 3920000, 3936000, 3952000, 3968000, 3984000, 4000000,
-       4016000, 4032000, 4048000, 4064000, 4080000, 4096000, 4112000, 4128000,
-       4144000, 4160000, 4176000, 4192000, 4208000, 4224000, 4240000, 4256000,
-       4272000, 4288000, 4304000, 4320000, 4336000, 4352000, 4368000, 4384000,
-       4400000
-};
-
-/* REG06[1:0] (TREG) in tenths of degrees Celcius */
-static const int bq24190_ictrc_treg_values[] = {
-       600, 800, 1000, 1200
-};
-
-/*
- * Return the index in 'tbl' of greatest value that is less than or equal to
- * 'val'.  The index range returned is 0 to 'tbl_size' - 1.  Assumes that
- * the values in 'tbl' are sorted from smallest to largest and 'tbl_size'
- * is less than 2^8.
- */
-static u8 bq24190_find_idx(const int tbl[], int tbl_size, int v)
-{
-       int i;
-
-       for (i = 1; i < tbl_size; i++)
-               if (v < tbl[i])
-                       break;
-
-       return i - 1;
-}
-
-/* Basic driver I/O routines */
-
-static int bq24190_read(struct bq24190_dev_info *bdi, u8 reg, u8 *data)
-{
-       int ret;
-
-       ret = i2c_smbus_read_byte_data(bdi->client, reg);
-       if (ret < 0)
-               return ret;
-
-       *data = ret;
-       return 0;
-}
-
-static int bq24190_write(struct bq24190_dev_info *bdi, u8 reg, u8 data)
-{
-       return i2c_smbus_write_byte_data(bdi->client, reg, data);
-}
-
-static int bq24190_read_mask(struct bq24190_dev_info *bdi, u8 reg,
-               u8 mask, u8 shift, u8 *data)
-{
-       u8 v;
-       int ret;
-
-       ret = bq24190_read(bdi, reg, &v);
-       if (ret < 0)
-               return ret;
-
-       v &= mask;
-       v >>= shift;
-       *data = v;
-
-       return 0;
-}
-
-static int bq24190_write_mask(struct bq24190_dev_info *bdi, u8 reg,
-               u8 mask, u8 shift, u8 data)
-{
-       u8 v;
-       int ret;
-
-       ret = bq24190_read(bdi, reg, &v);
-       if (ret < 0)
-               return ret;
-
-       v &= ~mask;
-       v |= ((data << shift) & mask);
-
-       return bq24190_write(bdi, reg, v);
-}
-
-static int bq24190_get_field_val(struct bq24190_dev_info *bdi,
-               u8 reg, u8 mask, u8 shift,
-               const int tbl[], int tbl_size,
-               int *val)
-{
-       u8 v;
-       int ret;
-
-       ret = bq24190_read_mask(bdi, reg, mask, shift, &v);
-       if (ret < 0)
-               return ret;
-
-       v = (v >= tbl_size) ? (tbl_size - 1) : v;
-       *val = tbl[v];
-
-       return 0;
-}
-
-static int bq24190_set_field_val(struct bq24190_dev_info *bdi,
-               u8 reg, u8 mask, u8 shift,
-               const int tbl[], int tbl_size,
-               int val)
-{
-       u8 idx;
-
-       idx = bq24190_find_idx(tbl, tbl_size, val);
-
-       return bq24190_write_mask(bdi, reg, mask, shift, idx);
-}
-
-#ifdef CONFIG_SYSFS
-/*
- * There are a numerous options that are configurable on the bq24190
- * that go well beyond what the power_supply properties provide access to.
- * Provide sysfs access to them so they can be examined and possibly modified
- * on the fly.  They will be provided for the charger power_supply object only
- * and will be prefixed by 'f_' to make them easier to recognize.
- */
-
-#define BQ24190_SYSFS_FIELD(_name, r, f, m, store)                     \
-{                                                                      \
-       .attr   = __ATTR(f_##_name, m, bq24190_sysfs_show, store),      \
-       .reg    = BQ24190_REG_##r,                                      \
-       .mask   = BQ24190_REG_##r##_##f##_MASK,                         \
-       .shift  = BQ24190_REG_##r##_##f##_SHIFT,                        \
-}
-
-#define BQ24190_SYSFS_FIELD_RW(_name, r, f)                            \
-               BQ24190_SYSFS_FIELD(_name, r, f, S_IWUSR | S_IRUGO,     \
-                               bq24190_sysfs_store)
-
-#define BQ24190_SYSFS_FIELD_RO(_name, r, f)                            \
-               BQ24190_SYSFS_FIELD(_name, r, f, S_IRUGO, NULL)
-
-static ssize_t bq24190_sysfs_show(struct device *dev,
-               struct device_attribute *attr, char *buf);
-static ssize_t bq24190_sysfs_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count);
-
-struct bq24190_sysfs_field_info {
-       struct device_attribute attr;
-       u8      reg;
-       u8      mask;
-       u8      shift;
-};
-
-/* On i386 ptrace-abi.h defines SS that breaks the macro calls below. */
-#undef SS
-
-static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = {
-                       /*      sysfs name      reg     field in reg */
-       BQ24190_SYSFS_FIELD_RW(en_hiz,          ISC,    EN_HIZ),
-       BQ24190_SYSFS_FIELD_RW(vindpm,          ISC,    VINDPM),
-       BQ24190_SYSFS_FIELD_RW(iinlim,          ISC,    IINLIM),
-       BQ24190_SYSFS_FIELD_RW(chg_config,      POC,    CHG_CONFIG),
-       BQ24190_SYSFS_FIELD_RW(sys_min,         POC,    SYS_MIN),
-       BQ24190_SYSFS_FIELD_RW(boost_lim,       POC,    BOOST_LIM),
-       BQ24190_SYSFS_FIELD_RW(ichg,            CCC,    ICHG),
-       BQ24190_SYSFS_FIELD_RW(force_20_pct,    CCC,    FORCE_20PCT),
-       BQ24190_SYSFS_FIELD_RW(iprechg,         PCTCC,  IPRECHG),
-       BQ24190_SYSFS_FIELD_RW(iterm,           PCTCC,  ITERM),
-       BQ24190_SYSFS_FIELD_RW(vreg,            CVC,    VREG),
-       BQ24190_SYSFS_FIELD_RW(batlowv,         CVC,    BATLOWV),
-       BQ24190_SYSFS_FIELD_RW(vrechg,          CVC,    VRECHG),
-       BQ24190_SYSFS_FIELD_RW(en_term,         CTTC,   EN_TERM),
-       BQ24190_SYSFS_FIELD_RW(term_stat,       CTTC,   TERM_STAT),
-       BQ24190_SYSFS_FIELD_RO(watchdog,        CTTC,   WATCHDOG),
-       BQ24190_SYSFS_FIELD_RW(en_timer,        CTTC,   EN_TIMER),
-       BQ24190_SYSFS_FIELD_RW(chg_timer,       CTTC,   CHG_TIMER),
-       BQ24190_SYSFS_FIELD_RW(jeta_iset,       CTTC,   JEITA_ISET),
-       BQ24190_SYSFS_FIELD_RW(bat_comp,        ICTRC,  BAT_COMP),
-       BQ24190_SYSFS_FIELD_RW(vclamp,          ICTRC,  VCLAMP),
-       BQ24190_SYSFS_FIELD_RW(treg,            ICTRC,  TREG),
-       BQ24190_SYSFS_FIELD_RW(dpdm_en,         MOC,    DPDM_EN),
-       BQ24190_SYSFS_FIELD_RW(tmr2x_en,        MOC,    TMR2X_EN),
-       BQ24190_SYSFS_FIELD_RW(batfet_disable,  MOC,    BATFET_DISABLE),
-       BQ24190_SYSFS_FIELD_RW(jeita_vset,      MOC,    JEITA_VSET),
-       BQ24190_SYSFS_FIELD_RO(int_mask,        MOC,    INT_MASK),
-       BQ24190_SYSFS_FIELD_RO(vbus_stat,       SS,     VBUS_STAT),
-       BQ24190_SYSFS_FIELD_RO(chrg_stat,       SS,     CHRG_STAT),
-       BQ24190_SYSFS_FIELD_RO(dpm_stat,        SS,     DPM_STAT),
-       BQ24190_SYSFS_FIELD_RO(pg_stat,         SS,     PG_STAT),
-       BQ24190_SYSFS_FIELD_RO(therm_stat,      SS,     THERM_STAT),
-       BQ24190_SYSFS_FIELD_RO(vsys_stat,       SS,     VSYS_STAT),
-       BQ24190_SYSFS_FIELD_RO(watchdog_fault,  F,      WATCHDOG_FAULT),
-       BQ24190_SYSFS_FIELD_RO(boost_fault,     F,      BOOST_FAULT),
-       BQ24190_SYSFS_FIELD_RO(chrg_fault,      F,      CHRG_FAULT),
-       BQ24190_SYSFS_FIELD_RO(bat_fault,       F,      BAT_FAULT),
-       BQ24190_SYSFS_FIELD_RO(ntc_fault,       F,      NTC_FAULT),
-       BQ24190_SYSFS_FIELD_RO(pn,              VPRS,   PN),
-       BQ24190_SYSFS_FIELD_RO(ts_profile,      VPRS,   TS_PROFILE),
-       BQ24190_SYSFS_FIELD_RO(dev_reg,         VPRS,   DEV_REG),
-};
-
-static struct attribute *
-       bq24190_sysfs_attrs[ARRAY_SIZE(bq24190_sysfs_field_tbl) + 1];
-
-static const struct attribute_group bq24190_sysfs_attr_group = {
-       .attrs = bq24190_sysfs_attrs,
-};
-
-static void bq24190_sysfs_init_attrs(void)
-{
-       int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl);
-
-       for (i = 0; i < limit; i++)
-               bq24190_sysfs_attrs[i] = &bq24190_sysfs_field_tbl[i].attr.attr;
-
-       bq24190_sysfs_attrs[limit] = NULL; /* Has additional entry for this */
-}
-
-static struct bq24190_sysfs_field_info *bq24190_sysfs_field_lookup(
-               const char *name)
-{
-       int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl);
-
-       for (i = 0; i < limit; i++)
-               if (!strcmp(name, bq24190_sysfs_field_tbl[i].attr.attr.name))
-                       break;
-
-       if (i >= limit)
-               return NULL;
-
-       return &bq24190_sysfs_field_tbl[i];
-}
-
-static ssize_t bq24190_sysfs_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
-       struct bq24190_sysfs_field_info *info;
-       int ret;
-       u8 v;
-
-       info = bq24190_sysfs_field_lookup(attr->attr.name);
-       if (!info)
-               return -EINVAL;
-
-       ret = bq24190_read_mask(bdi, info->reg, info->mask, info->shift, &v);
-       if (ret)
-               return ret;
-
-       return scnprintf(buf, PAGE_SIZE, "%hhx\n", v);
-}
-
-static ssize_t bq24190_sysfs_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
-       struct bq24190_sysfs_field_info *info;
-       int ret;
-       u8 v;
-
-       info = bq24190_sysfs_field_lookup(attr->attr.name);
-       if (!info)
-               return -EINVAL;
-
-       ret = kstrtou8(buf, 0, &v);
-       if (ret < 0)
-               return ret;
-
-       ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v);
-       if (ret)
-               return ret;
-
-       return count;
-}
-
-static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi)
-{
-       bq24190_sysfs_init_attrs();
-
-       return sysfs_create_group(&bdi->charger->dev.kobj,
-                       &bq24190_sysfs_attr_group);
-}
-
-static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi)
-{
-       sysfs_remove_group(&bdi->charger->dev.kobj, &bq24190_sysfs_attr_group);
-}
-#else
-static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi)
-{
-       return 0;
-}
-
-static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {}
-#endif
-
-/*
- * According to the "Host Mode and default Mode" section of the
- * manual, a write to any register causes the bq24190 to switch
- * from default mode to host mode.  It will switch back to default
- * mode after a WDT timeout unless the WDT is turned off as well.
- * So, by simply turning off the WDT, we accomplish both with the
- * same write.
- */
-static int bq24190_set_mode_host(struct bq24190_dev_info *bdi)
-{
-       int ret;
-       u8 v;
-
-       ret = bq24190_read(bdi, BQ24190_REG_CTTC, &v);
-       if (ret < 0)
-               return ret;
-
-       bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >>
-                                       BQ24190_REG_CTTC_WATCHDOG_SHIFT);
-       v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK;
-
-       return bq24190_write(bdi, BQ24190_REG_CTTC, v);
-}
-
-static int bq24190_register_reset(struct bq24190_dev_info *bdi)
-{
-       int ret, limit = 100;
-       u8 v;
-
-       /* Reset the registers */
-       ret = bq24190_write_mask(bdi, BQ24190_REG_POC,
-                       BQ24190_REG_POC_RESET_MASK,
-                       BQ24190_REG_POC_RESET_SHIFT,
-                       0x1);
-       if (ret < 0)
-               return ret;
-
-       /* Reset bit will be cleared by hardware so poll until it is */
-       do {
-               ret = bq24190_read_mask(bdi, BQ24190_REG_POC,
-                               BQ24190_REG_POC_RESET_MASK,
-                               BQ24190_REG_POC_RESET_SHIFT,
-                               &v);
-               if (ret < 0)
-                       return ret;
-
-               if (!v)
-                       break;
-
-               udelay(10);
-       } while (--limit);
-
-       if (!limit)
-               return -EIO;
-
-       return 0;
-}
-
-/* Charger power supply property routines */
-
-static int bq24190_charger_get_charge_type(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       u8 v;
-       int type, ret;
-
-       ret = bq24190_read_mask(bdi, BQ24190_REG_POC,
-                       BQ24190_REG_POC_CHG_CONFIG_MASK,
-                       BQ24190_REG_POC_CHG_CONFIG_SHIFT,
-                       &v);
-       if (ret < 0)
-               return ret;
-
-       /* If POC[CHG_CONFIG] (REG01[5:4]) == 0, charge is disabled */
-       if (!v) {
-               type = POWER_SUPPLY_CHARGE_TYPE_NONE;
-       } else {
-               ret = bq24190_read_mask(bdi, BQ24190_REG_CCC,
-                               BQ24190_REG_CCC_FORCE_20PCT_MASK,
-                               BQ24190_REG_CCC_FORCE_20PCT_SHIFT,
-                               &v);
-               if (ret < 0)
-                       return ret;
-
-               type = (v) ? POWER_SUPPLY_CHARGE_TYPE_TRICKLE :
-                            POWER_SUPPLY_CHARGE_TYPE_FAST;
-       }
-
-       val->intval = type;
-
-       return 0;
-}
-
-static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi,
-               const union power_supply_propval *val)
-{
-       u8 chg_config, force_20pct, en_term;
-       int ret;
-
-       /*
-        * According to the "Termination when REG02[0] = 1" section of
-        * the bq24190 manual, the trickle charge could be less than the
-        * termination current so it recommends turning off the termination
-        * function.
-        *
-        * Note: AFAICT from the datasheet, the user will have to manually
-        * turn off the charging when in 20% mode.  If its not turned off,
-        * there could be battery damage.  So, use this mode at your own risk.
-        */
-       switch (val->intval) {
-       case POWER_SUPPLY_CHARGE_TYPE_NONE:
-               chg_config = 0x0;
-               break;
-       case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
-               chg_config = 0x1;
-               force_20pct = 0x1;
-               en_term = 0x0;
-               break;
-       case POWER_SUPPLY_CHARGE_TYPE_FAST:
-               chg_config = 0x1;
-               force_20pct = 0x0;
-               en_term = 0x1;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       if (chg_config) { /* Enabling the charger */
-               ret = bq24190_write_mask(bdi, BQ24190_REG_CCC,
-                               BQ24190_REG_CCC_FORCE_20PCT_MASK,
-                               BQ24190_REG_CCC_FORCE_20PCT_SHIFT,
-                               force_20pct);
-               if (ret < 0)
-                       return ret;
-
-               ret = bq24190_write_mask(bdi, BQ24190_REG_CTTC,
-                               BQ24190_REG_CTTC_EN_TERM_MASK,
-                               BQ24190_REG_CTTC_EN_TERM_SHIFT,
-                               en_term);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return bq24190_write_mask(bdi, BQ24190_REG_POC,
-                       BQ24190_REG_POC_CHG_CONFIG_MASK,
-                       BQ24190_REG_POC_CHG_CONFIG_SHIFT, chg_config);
-}
-
-static int bq24190_charger_get_health(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       u8 v;
-       int health, ret;
-
-       mutex_lock(&bdi->f_reg_lock);
-
-       if (bdi->charger_health_valid) {
-               v = bdi->f_reg;
-               bdi->charger_health_valid = false;
-               mutex_unlock(&bdi->f_reg_lock);
-       } else {
-               mutex_unlock(&bdi->f_reg_lock);
-
-               ret = bq24190_read(bdi, BQ24190_REG_F, &v);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {
-               /*
-                * This could be over-current or over-voltage but there's
-                * no way to tell which.  Return 'OVERVOLTAGE' since there
-                * isn't an 'OVERCURRENT' value defined that we can return
-                * even if it was over-current.
-                */
-               health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-       } else {
-               v &= BQ24190_REG_F_CHRG_FAULT_MASK;
-               v >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
-
-               switch (v) {
-               case 0x0: /* Normal */
-                       health = POWER_SUPPLY_HEALTH_GOOD;
-                       break;
-               case 0x1: /* Input Fault (VBUS OVP or VBAT<VBUS<3.8V) */
-                       /*
-                        * This could be over-voltage or under-voltage
-                        * and there's no way to tell which.  Instead
-                        * of looking foolish and returning 'OVERVOLTAGE'
-                        * when its really under-voltage, just return
-                        * 'UNSPEC_FAILURE'.
-                        */
-                       health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-                       break;
-               case 0x2: /* Thermal Shutdown */
-                       health = POWER_SUPPLY_HEALTH_OVERHEAT;
-                       break;
-               case 0x3: /* Charge Safety Timer Expiration */
-                       health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
-                       break;
-               default:
-                       health = POWER_SUPPLY_HEALTH_UNKNOWN;
-               }
-       }
-
-       val->intval = health;
-
-       return 0;
-}
-
-static int bq24190_charger_get_online(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       u8 v;
-       int ret;
-
-       ret = bq24190_read_mask(bdi, BQ24190_REG_SS,
-                       BQ24190_REG_SS_PG_STAT_MASK,
-                       BQ24190_REG_SS_PG_STAT_SHIFT, &v);
-       if (ret < 0)
-               return ret;
-
-       val->intval = v;
-       return 0;
-}
-
-static int bq24190_charger_get_current(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       u8 v;
-       int curr, ret;
-
-       ret = bq24190_get_field_val(bdi, BQ24190_REG_CCC,
-                       BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT,
-                       bq24190_ccc_ichg_values,
-                       ARRAY_SIZE(bq24190_ccc_ichg_values), &curr);
-       if (ret < 0)
-               return ret;
-
-       ret = bq24190_read_mask(bdi, BQ24190_REG_CCC,
-                       BQ24190_REG_CCC_FORCE_20PCT_MASK,
-                       BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v);
-       if (ret < 0)
-               return ret;
-
-       /* If FORCE_20PCT is enabled, then current is 20% of ICHG value */
-       if (v)
-               curr /= 5;
-
-       val->intval = curr;
-       return 0;
-}
-
-static int bq24190_charger_get_current_max(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       int idx = ARRAY_SIZE(bq24190_ccc_ichg_values) - 1;
-
-       val->intval = bq24190_ccc_ichg_values[idx];
-       return 0;
-}
-
-static int bq24190_charger_set_current(struct bq24190_dev_info *bdi,
-               const union power_supply_propval *val)
-{
-       u8 v;
-       int ret, curr = val->intval;
-
-       ret = bq24190_read_mask(bdi, BQ24190_REG_CCC,
-                       BQ24190_REG_CCC_FORCE_20PCT_MASK,
-                       BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v);
-       if (ret < 0)
-               return ret;
-
-       /* If FORCE_20PCT is enabled, have to multiply value passed in by 5 */
-       if (v)
-               curr *= 5;
-
-       return bq24190_set_field_val(bdi, BQ24190_REG_CCC,
-                       BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT,
-                       bq24190_ccc_ichg_values,
-                       ARRAY_SIZE(bq24190_ccc_ichg_values), curr);
-}
-
-static int bq24190_charger_get_voltage(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       int voltage, ret;
-
-       ret = bq24190_get_field_val(bdi, BQ24190_REG_CVC,
-                       BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT,
-                       bq24190_cvc_vreg_values,
-                       ARRAY_SIZE(bq24190_cvc_vreg_values), &voltage);
-       if (ret < 0)
-               return ret;
-
-       val->intval = voltage;
-       return 0;
-}
-
-static int bq24190_charger_get_voltage_max(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       int idx = ARRAY_SIZE(bq24190_cvc_vreg_values) - 1;
-
-       val->intval = bq24190_cvc_vreg_values[idx];
-       return 0;
-}
-
-static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi,
-               const union power_supply_propval *val)
-{
-       return bq24190_set_field_val(bdi, BQ24190_REG_CVC,
-                       BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT,
-                       bq24190_cvc_vreg_values,
-                       ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval);
-}
-
-static int bq24190_charger_get_property(struct power_supply *psy,
-               enum power_supply_property psp, union power_supply_propval *val)
-{
-       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
-       int ret;
-
-       dev_dbg(bdi->dev, "prop: %d\n", psp);
-
-       pm_runtime_get_sync(bdi->dev);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               ret = bq24190_charger_get_charge_type(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = bq24190_charger_get_health(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = bq24190_charger_get_online(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               ret = bq24190_charger_get_current(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
-               ret = bq24190_charger_get_current_max(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               ret = bq24190_charger_get_voltage(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
-               ret = bq24190_charger_get_voltage_max(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_SCOPE:
-               val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
-               ret = 0;
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = bdi->model_name;
-               ret = 0;
-               break;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = BQ24190_MANUFACTURER;
-               ret = 0;
-               break;
-       default:
-               ret = -ENODATA;
-       }
-
-       pm_runtime_put_sync(bdi->dev);
-       return ret;
-}
-
-static int bq24190_charger_set_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               const union power_supply_propval *val)
-{
-       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
-       int ret;
-
-       dev_dbg(bdi->dev, "prop: %d\n", psp);
-
-       pm_runtime_get_sync(bdi->dev);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               ret = bq24190_charger_set_charge_type(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               ret = bq24190_charger_set_current(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               ret = bq24190_charger_set_voltage(bdi, val);
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       pm_runtime_put_sync(bdi->dev);
-       return ret;
-}
-
-static int bq24190_charger_property_is_writeable(struct power_supply *psy,
-               enum power_supply_property psp)
-{
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               ret = 1;
-               break;
-       default:
-               ret = 0;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property bq24190_charger_properties[] = {
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_SCOPE,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static char *bq24190_charger_supplied_to[] = {
-       "main-battery",
-};
-
-static const struct power_supply_desc bq24190_charger_desc = {
-       .name                   = "bq24190-charger",
-       .type                   = POWER_SUPPLY_TYPE_USB,
-       .properties             = bq24190_charger_properties,
-       .num_properties         = ARRAY_SIZE(bq24190_charger_properties),
-       .get_property           = bq24190_charger_get_property,
-       .set_property           = bq24190_charger_set_property,
-       .property_is_writeable  = bq24190_charger_property_is_writeable,
-};
-
-/* Battery power supply property routines */
-
-static int bq24190_battery_get_status(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       u8 ss_reg, chrg_fault;
-       int status, ret;
-
-       mutex_lock(&bdi->f_reg_lock);
-
-       if (bdi->battery_status_valid) {
-               chrg_fault = bdi->f_reg;
-               bdi->battery_status_valid = false;
-               mutex_unlock(&bdi->f_reg_lock);
-       } else {
-               mutex_unlock(&bdi->f_reg_lock);
-
-               ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault);
-               if (ret < 0)
-                       return ret;
-       }
-
-       chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK;
-       chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
-
-       ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * The battery must be discharging when any of these are true:
-        * - there is no good power source;
-        * - there is a charge fault.
-        * Could also be discharging when in "supplement mode" but
-        * there is no way to tell when its in that mode.
-        */
-       if (!(ss_reg & BQ24190_REG_SS_PG_STAT_MASK) || chrg_fault) {
-               status = POWER_SUPPLY_STATUS_DISCHARGING;
-       } else {
-               ss_reg &= BQ24190_REG_SS_CHRG_STAT_MASK;
-               ss_reg >>= BQ24190_REG_SS_CHRG_STAT_SHIFT;
-
-               switch (ss_reg) {
-               case 0x0: /* Not Charging */
-                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-                       break;
-               case 0x1: /* Pre-charge */
-               case 0x2: /* Fast Charging */
-                       status = POWER_SUPPLY_STATUS_CHARGING;
-                       break;
-               case 0x3: /* Charge Termination Done */
-                       status = POWER_SUPPLY_STATUS_FULL;
-                       break;
-               default:
-                       ret = -EIO;
-               }
-       }
-
-       if (!ret)
-               val->intval = status;
-
-       return ret;
-}
-
-static int bq24190_battery_get_health(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       u8 v;
-       int health, ret;
-
-       mutex_lock(&bdi->f_reg_lock);
-
-       if (bdi->battery_health_valid) {
-               v = bdi->f_reg;
-               bdi->battery_health_valid = false;
-               mutex_unlock(&bdi->f_reg_lock);
-       } else {
-               mutex_unlock(&bdi->f_reg_lock);
-
-               ret = bq24190_read(bdi, BQ24190_REG_F, &v);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (v & BQ24190_REG_F_BAT_FAULT_MASK) {
-               health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-       } else {
-               v &= BQ24190_REG_F_NTC_FAULT_MASK;
-               v >>= BQ24190_REG_F_NTC_FAULT_SHIFT;
-
-               switch (v) {
-               case 0x0: /* Normal */
-                       health = POWER_SUPPLY_HEALTH_GOOD;
-                       break;
-               case 0x1: /* TS1 Cold */
-               case 0x3: /* TS2 Cold */
-               case 0x5: /* Both Cold */
-                       health = POWER_SUPPLY_HEALTH_COLD;
-                       break;
-               case 0x2: /* TS1 Hot */
-               case 0x4: /* TS2 Hot */
-               case 0x6: /* Both Hot */
-                       health = POWER_SUPPLY_HEALTH_OVERHEAT;
-                       break;
-               default:
-                       health = POWER_SUPPLY_HEALTH_UNKNOWN;
-               }
-       }
-
-       val->intval = health;
-       return 0;
-}
-
-static int bq24190_battery_get_online(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       u8 batfet_disable;
-       int ret;
-
-       ret = bq24190_read_mask(bdi, BQ24190_REG_MOC,
-                       BQ24190_REG_MOC_BATFET_DISABLE_MASK,
-                       BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, &batfet_disable);
-       if (ret < 0)
-               return ret;
-
-       val->intval = !batfet_disable;
-       return 0;
-}
-
-static int bq24190_battery_set_online(struct bq24190_dev_info *bdi,
-               const union power_supply_propval *val)
-{
-       return bq24190_write_mask(bdi, BQ24190_REG_MOC,
-                       BQ24190_REG_MOC_BATFET_DISABLE_MASK,
-                       BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, !val->intval);
-}
-
-static int bq24190_battery_get_temp_alert_max(struct bq24190_dev_info *bdi,
-               union power_supply_propval *val)
-{
-       int temp, ret;
-
-       ret = bq24190_get_field_val(bdi, BQ24190_REG_ICTRC,
-                       BQ24190_REG_ICTRC_TREG_MASK,
-                       BQ24190_REG_ICTRC_TREG_SHIFT,
-                       bq24190_ictrc_treg_values,
-                       ARRAY_SIZE(bq24190_ictrc_treg_values), &temp);
-       if (ret < 0)
-               return ret;
-
-       val->intval = temp;
-       return 0;
-}
-
-static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi,
-               const union power_supply_propval *val)
-{
-       return bq24190_set_field_val(bdi, BQ24190_REG_ICTRC,
-                       BQ24190_REG_ICTRC_TREG_MASK,
-                       BQ24190_REG_ICTRC_TREG_SHIFT,
-                       bq24190_ictrc_treg_values,
-                       ARRAY_SIZE(bq24190_ictrc_treg_values), val->intval);
-}
-
-static int bq24190_battery_get_property(struct power_supply *psy,
-               enum power_supply_property psp, union power_supply_propval *val)
-{
-       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
-       int ret;
-
-       dev_dbg(bdi->dev, "prop: %d\n", psp);
-
-       pm_runtime_get_sync(bdi->dev);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = bq24190_battery_get_status(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = bq24190_battery_get_health(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = bq24190_battery_get_online(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               /* Could be Li-on or Li-polymer but no way to tell which */
-               val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
-               ret = 0;
-               break;
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
-               ret = bq24190_battery_get_temp_alert_max(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_SCOPE:
-               val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
-               ret = 0;
-               break;
-       default:
-               ret = -ENODATA;
-       }
-
-       pm_runtime_put_sync(bdi->dev);
-       return ret;
-}
-
-static int bq24190_battery_set_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               const union power_supply_propval *val)
-{
-       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
-       int ret;
-
-       dev_dbg(bdi->dev, "prop: %d\n", psp);
-
-       pm_runtime_put_sync(bdi->dev);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = bq24190_battery_set_online(bdi, val);
-               break;
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
-               ret = bq24190_battery_set_temp_alert_max(bdi, val);
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       pm_runtime_put_sync(bdi->dev);
-       return ret;
-}
-
-static int bq24190_battery_property_is_writeable(struct power_supply *psy,
-               enum power_supply_property psp)
-{
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
-               ret = 1;
-               break;
-       default:
-               ret = 0;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property bq24190_battery_properties[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
-       POWER_SUPPLY_PROP_SCOPE,
-};
-
-static const struct power_supply_desc bq24190_battery_desc = {
-       .name                   = "bq24190-battery",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = bq24190_battery_properties,
-       .num_properties         = ARRAY_SIZE(bq24190_battery_properties),
-       .get_property           = bq24190_battery_get_property,
-       .set_property           = bq24190_battery_set_property,
-       .property_is_writeable  = bq24190_battery_property_is_writeable,
-};
-
-static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
-{
-       struct bq24190_dev_info *bdi = data;
-       bool alert_userspace = false;
-       u8 ss_reg = 0, f_reg = 0;
-       int ret;
-
-       pm_runtime_get_sync(bdi->dev);
-
-       ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg);
-       if (ret < 0) {
-               dev_err(bdi->dev, "Can't read SS reg: %d\n", ret);
-               goto out;
-       }
-
-       if (ss_reg != bdi->ss_reg) {
-               /*
-                * The device is in host mode so when PG_STAT goes from 1->0
-                * (i.e., power removed) HIZ needs to be disabled.
-                */
-               if ((bdi->ss_reg & BQ24190_REG_SS_PG_STAT_MASK) &&
-                               !(ss_reg & BQ24190_REG_SS_PG_STAT_MASK)) {
-                       ret = bq24190_write_mask(bdi, BQ24190_REG_ISC,
-                                       BQ24190_REG_ISC_EN_HIZ_MASK,
-                                       BQ24190_REG_ISC_EN_HIZ_SHIFT,
-                                       0);
-                       if (ret < 0)
-                               dev_err(bdi->dev, "Can't access ISC reg: %d\n",
-                                       ret);
-               }
-
-               bdi->ss_reg = ss_reg;
-               alert_userspace = true;
-       }
-
-       mutex_lock(&bdi->f_reg_lock);
-
-       ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
-       if (ret < 0) {
-               mutex_unlock(&bdi->f_reg_lock);
-               dev_err(bdi->dev, "Can't read F reg: %d\n", ret);
-               goto out;
-       }
-
-       if (f_reg != bdi->f_reg) {
-               bdi->f_reg = f_reg;
-               bdi->charger_health_valid = true;
-               bdi->battery_health_valid = true;
-               bdi->battery_status_valid = true;
-
-               alert_userspace = true;
-       }
-
-       mutex_unlock(&bdi->f_reg_lock);
-
-       /*
-        * Sometimes bq24190 gives a steady trickle of interrupts even
-        * though the watchdog timer is turned off and neither the STATUS
-        * nor FAULT registers have changed.  Weed out these sprurious
-        * interrupts so userspace isn't alerted for no reason.
-        * In addition, the chip always generates an interrupt after
-        * register reset so we should ignore that one (the very first
-        * interrupt received).
-        */
-       if (alert_userspace) {
-               if (!bdi->first_time) {
-                       power_supply_changed(bdi->charger);
-                       power_supply_changed(bdi->battery);
-               } else {
-                       bdi->first_time = false;
-               }
-       }
-
-out:
-       pm_runtime_put_sync(bdi->dev);
-
-       dev_dbg(bdi->dev, "ss_reg: 0x%02x, f_reg: 0x%02x\n", ss_reg, f_reg);
-
-       return IRQ_HANDLED;
-}
-
-static int bq24190_hw_init(struct bq24190_dev_info *bdi)
-{
-       u8 v;
-       int ret;
-
-       pm_runtime_get_sync(bdi->dev);
-
-       /* First check that the device really is what its supposed to be */
-       ret = bq24190_read_mask(bdi, BQ24190_REG_VPRS,
-                       BQ24190_REG_VPRS_PN_MASK,
-                       BQ24190_REG_VPRS_PN_SHIFT,
-                       &v);
-       if (ret < 0)
-               goto out;
-
-       if (v != bdi->model) {
-               ret = -ENODEV;
-               goto out;
-       }
-
-       ret = bq24190_register_reset(bdi);
-       if (ret < 0)
-               goto out;
-
-       ret = bq24190_set_mode_host(bdi);
-out:
-       pm_runtime_put_sync(bdi->dev);
-       return ret;
-}
-
-#ifdef CONFIG_OF
-static int bq24190_setup_dt(struct bq24190_dev_info *bdi)
-{
-       bdi->irq = irq_of_parse_and_map(bdi->dev->of_node, 0);
-       if (bdi->irq <= 0)
-               return -1;
-
-       return 0;
-}
-#else
-static int bq24190_setup_dt(struct bq24190_dev_info *bdi)
-{
-       return -1;
-}
-#endif
-
-static int bq24190_setup_pdata(struct bq24190_dev_info *bdi,
-               struct bq24190_platform_data *pdata)
-{
-       int ret;
-
-       if (!gpio_is_valid(pdata->gpio_int))
-               return -1;
-
-       ret = gpio_request(pdata->gpio_int, dev_name(bdi->dev));
-       if (ret < 0)
-               return -1;
-
-       ret = gpio_direction_input(pdata->gpio_int);
-       if (ret < 0)
-               goto out;
-
-       bdi->irq = gpio_to_irq(pdata->gpio_int);
-       if (!bdi->irq)
-               goto out;
-
-       bdi->gpio_int = pdata->gpio_int;
-       return 0;
-
-out:
-       gpio_free(pdata->gpio_int);
-       return -1;
-}
-
-static int bq24190_probe(struct i2c_client *client,
-               const struct i2c_device_id *id)
-{
-       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       struct device *dev = &client->dev;
-       struct bq24190_platform_data *pdata = client->dev.platform_data;
-       struct power_supply_config charger_cfg = {}, battery_cfg = {};
-       struct bq24190_dev_info *bdi;
-       int ret;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
-               return -ENODEV;
-       }
-
-       bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL);
-       if (!bdi) {
-               dev_err(dev, "Can't alloc bdi struct\n");
-               return -ENOMEM;
-       }
-
-       bdi->client = client;
-       bdi->dev = dev;
-       bdi->model = id->driver_data;
-       strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);
-       mutex_init(&bdi->f_reg_lock);
-       bdi->first_time = true;
-       bdi->charger_health_valid = false;
-       bdi->battery_health_valid = false;
-       bdi->battery_status_valid = false;
-
-       i2c_set_clientdata(client, bdi);
-
-       if (dev->of_node)
-               ret = bq24190_setup_dt(bdi);
-       else
-               ret = bq24190_setup_pdata(bdi, pdata);
-
-       if (ret) {
-               dev_err(dev, "Can't get irq info\n");
-               return -EINVAL;
-       }
-
-       ret = devm_request_threaded_irq(dev, bdi->irq, NULL,
-                       bq24190_irq_handler_thread,
-                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-                       "bq24190-charger", bdi);
-       if (ret < 0) {
-               dev_err(dev, "Can't set up irq handler\n");
-               goto out1;
-       }
-
-       pm_runtime_enable(dev);
-       pm_runtime_resume(dev);
-
-       ret = bq24190_hw_init(bdi);
-       if (ret < 0) {
-               dev_err(dev, "Hardware init failed\n");
-               goto out2;
-       }
-
-       charger_cfg.drv_data = bdi;
-       charger_cfg.supplied_to = bq24190_charger_supplied_to;
-       charger_cfg.num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to),
-       bdi->charger = power_supply_register(dev, &bq24190_charger_desc,
-                                               &charger_cfg);
-       if (IS_ERR(bdi->charger)) {
-               dev_err(dev, "Can't register charger\n");
-               ret = PTR_ERR(bdi->charger);
-               goto out2;
-       }
-
-       battery_cfg.drv_data = bdi;
-       bdi->battery = power_supply_register(dev, &bq24190_battery_desc,
-                                               &battery_cfg);
-       if (IS_ERR(bdi->battery)) {
-               dev_err(dev, "Can't register battery\n");
-               ret = PTR_ERR(bdi->battery);
-               goto out3;
-       }
-
-       ret = bq24190_sysfs_create_group(bdi);
-       if (ret) {
-               dev_err(dev, "Can't create sysfs entries\n");
-               goto out4;
-       }
-
-       return 0;
-
-out4:
-       power_supply_unregister(bdi->battery);
-out3:
-       power_supply_unregister(bdi->charger);
-out2:
-       pm_runtime_disable(dev);
-out1:
-       if (bdi->gpio_int)
-               gpio_free(bdi->gpio_int);
-
-       return ret;
-}
-
-static int bq24190_remove(struct i2c_client *client)
-{
-       struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
-
-       pm_runtime_get_sync(bdi->dev);
-       bq24190_register_reset(bdi);
-       pm_runtime_put_sync(bdi->dev);
-
-       bq24190_sysfs_remove_group(bdi);
-       power_supply_unregister(bdi->battery);
-       power_supply_unregister(bdi->charger);
-       pm_runtime_disable(bdi->dev);
-
-       if (bdi->gpio_int)
-               gpio_free(bdi->gpio_int);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int bq24190_pm_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
-
-       pm_runtime_get_sync(bdi->dev);
-       bq24190_register_reset(bdi);
-       pm_runtime_put_sync(bdi->dev);
-
-       return 0;
-}
-
-static int bq24190_pm_resume(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
-
-       bdi->charger_health_valid = false;
-       bdi->battery_health_valid = false;
-       bdi->battery_status_valid = false;
-
-       pm_runtime_get_sync(bdi->dev);
-       bq24190_register_reset(bdi);
-       pm_runtime_put_sync(bdi->dev);
-
-       /* Things may have changed while suspended so alert upper layer */
-       power_supply_changed(bdi->charger);
-       power_supply_changed(bdi->battery);
-
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(bq24190_pm_ops, bq24190_pm_suspend, bq24190_pm_resume);
-
-/*
- * Only support the bq24190 right now.  The bq24192, bq24192i, and bq24193
- * are similar but not identical so the driver needs to be extended to
- * support them.
- */
-static const struct i2c_device_id bq24190_i2c_ids[] = {
-       { "bq24190", BQ24190_REG_VPRS_PN_24190 },
-       { },
-};
-MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
-
-#ifdef CONFIG_OF
-static const struct of_device_id bq24190_of_match[] = {
-       { .compatible = "ti,bq24190", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, bq24190_of_match);
-#else
-static const struct of_device_id bq24190_of_match[] = {
-       { },
-};
-#endif
-
-static struct i2c_driver bq24190_driver = {
-       .probe          = bq24190_probe,
-       .remove         = bq24190_remove,
-       .id_table       = bq24190_i2c_ids,
-       .driver = {
-               .name           = "bq24190-charger",
-               .pm             = &bq24190_pm_ops,
-               .of_match_table = of_match_ptr(bq24190_of_match),
-       },
-};
-module_i2c_driver(bq24190_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
-MODULE_DESCRIPTION("TI BQ24190 Charger Driver");
diff --git a/drivers/power/bq24257_charger.c b/drivers/power/bq24257_charger.c
deleted file mode 100644 (file)
index 1fea2c7..0000000
+++ /dev/null
@@ -1,1196 +0,0 @@
-/*
- * TI BQ24257 charger driver
- *
- * Copyright (C) 2015 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * Datasheets:
- * http://www.ti.com/product/bq24250
- * http://www.ti.com/product/bq24251
- * http://www.ti.com/product/bq24257
- */
-
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/power_supply.h>
-#include <linux/regmap.h>
-#include <linux/types.h>
-#include <linux/gpio/consumer.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-
-#include <linux/acpi.h>
-#include <linux/of.h>
-
-#define BQ24257_REG_1                  0x00
-#define BQ24257_REG_2                  0x01
-#define BQ24257_REG_3                  0x02
-#define BQ24257_REG_4                  0x03
-#define BQ24257_REG_5                  0x04
-#define BQ24257_REG_6                  0x05
-#define BQ24257_REG_7                  0x06
-
-#define BQ24257_MANUFACTURER           "Texas Instruments"
-#define BQ24257_PG_GPIO                        "pg"
-
-#define BQ24257_ILIM_SET_DELAY         1000    /* msec */
-
-/*
- * When adding support for new devices make sure that enum bq2425x_chip and
- * bq2425x_chip_name[] always stay in sync!
- */
-enum bq2425x_chip {
-       BQ24250,
-       BQ24251,
-       BQ24257,
-};
-
-static const char *const bq2425x_chip_name[] = {
-       "bq24250",
-       "bq24251",
-       "bq24257",
-};
-
-enum bq24257_fields {
-       F_WD_FAULT, F_WD_EN, F_STAT, F_FAULT,                       /* REG 1 */
-       F_RESET, F_IILIMIT, F_EN_STAT, F_EN_TERM, F_CE, F_HZ_MODE,  /* REG 2 */
-       F_VBAT, F_USB_DET,                                          /* REG 3 */
-       F_ICHG, F_ITERM,                                            /* REG 4 */
-       F_LOOP_STATUS, F_LOW_CHG, F_DPDM_EN, F_CE_STATUS, F_VINDPM, /* REG 5 */
-       F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_EN, F_TS_STAT,           /* REG 6 */
-       F_VOVP, F_CLR_VDP, F_FORCE_BATDET, F_FORCE_PTM,             /* REG 7 */
-
-       F_MAX_FIELDS
-};
-
-/* initial field values, converted from uV/uA */
-struct bq24257_init_data {
-       u8 ichg;        /* charge current      */
-       u8 vbat;        /* regulation voltage  */
-       u8 iterm;       /* termination current */
-       u8 iilimit;     /* input current limit */
-       u8 vovp;        /* over voltage protection voltage */
-       u8 vindpm;      /* VDMP input threshold voltage */
-};
-
-struct bq24257_state {
-       u8 status;
-       u8 fault;
-       bool power_good;
-};
-
-struct bq24257_device {
-       struct i2c_client *client;
-       struct device *dev;
-       struct power_supply *charger;
-
-       enum bq2425x_chip chip;
-
-       struct regmap *rmap;
-       struct regmap_field *rmap_fields[F_MAX_FIELDS];
-
-       struct gpio_desc *pg;
-
-       struct delayed_work iilimit_setup_work;
-
-       struct bq24257_init_data init_data;
-       struct bq24257_state state;
-
-       struct mutex lock; /* protect state data */
-
-       bool iilimit_autoset_enable;
-};
-
-static bool bq24257_is_volatile_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case BQ24257_REG_2:
-       case BQ24257_REG_4:
-               return false;
-
-       default:
-               return true;
-       }
-}
-
-static const struct regmap_config bq24257_regmap_config = {
-       .reg_bits = 8,
-       .val_bits = 8,
-
-       .max_register = BQ24257_REG_7,
-       .cache_type = REGCACHE_RBTREE,
-
-       .volatile_reg = bq24257_is_volatile_reg,
-};
-
-static const struct reg_field bq24257_reg_fields[] = {
-       /* REG 1 */
-       [F_WD_FAULT]            = REG_FIELD(BQ24257_REG_1, 7, 7),
-       [F_WD_EN]               = REG_FIELD(BQ24257_REG_1, 6, 6),
-       [F_STAT]                = REG_FIELD(BQ24257_REG_1, 4, 5),
-       [F_FAULT]               = REG_FIELD(BQ24257_REG_1, 0, 3),
-       /* REG 2 */
-       [F_RESET]               = REG_FIELD(BQ24257_REG_2, 7, 7),
-       [F_IILIMIT]             = REG_FIELD(BQ24257_REG_2, 4, 6),
-       [F_EN_STAT]             = REG_FIELD(BQ24257_REG_2, 3, 3),
-       [F_EN_TERM]             = REG_FIELD(BQ24257_REG_2, 2, 2),
-       [F_CE]                  = REG_FIELD(BQ24257_REG_2, 1, 1),
-       [F_HZ_MODE]             = REG_FIELD(BQ24257_REG_2, 0, 0),
-       /* REG 3 */
-       [F_VBAT]                = REG_FIELD(BQ24257_REG_3, 2, 7),
-       [F_USB_DET]             = REG_FIELD(BQ24257_REG_3, 0, 1),
-       /* REG 4 */
-       [F_ICHG]                = REG_FIELD(BQ24257_REG_4, 3, 7),
-       [F_ITERM]               = REG_FIELD(BQ24257_REG_4, 0, 2),
-       /* REG 5 */
-       [F_LOOP_STATUS]         = REG_FIELD(BQ24257_REG_5, 6, 7),
-       [F_LOW_CHG]             = REG_FIELD(BQ24257_REG_5, 5, 5),
-       [F_DPDM_EN]             = REG_FIELD(BQ24257_REG_5, 4, 4),
-       [F_CE_STATUS]           = REG_FIELD(BQ24257_REG_5, 3, 3),
-       [F_VINDPM]              = REG_FIELD(BQ24257_REG_5, 0, 2),
-       /* REG 6 */
-       [F_X2_TMR_EN]           = REG_FIELD(BQ24257_REG_6, 7, 7),
-       [F_TMR]                 = REG_FIELD(BQ24257_REG_6, 5, 6),
-       [F_SYSOFF]              = REG_FIELD(BQ24257_REG_6, 4, 4),
-       [F_TS_EN]               = REG_FIELD(BQ24257_REG_6, 3, 3),
-       [F_TS_STAT]             = REG_FIELD(BQ24257_REG_6, 0, 2),
-       /* REG 7 */
-       [F_VOVP]                = REG_FIELD(BQ24257_REG_7, 5, 7),
-       [F_CLR_VDP]             = REG_FIELD(BQ24257_REG_7, 4, 4),
-       [F_FORCE_BATDET]        = REG_FIELD(BQ24257_REG_7, 3, 3),
-       [F_FORCE_PTM]           = REG_FIELD(BQ24257_REG_7, 2, 2)
-};
-
-static const u32 bq24257_vbat_map[] = {
-       3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000,
-       3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000,
-       3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000,
-       3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000,
-       4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000,
-       4300000, 4320000, 4340000, 4360000, 4380000, 4400000, 4420000, 4440000
-};
-
-#define BQ24257_VBAT_MAP_SIZE          ARRAY_SIZE(bq24257_vbat_map)
-
-static const u32 bq24257_ichg_map[] = {
-       500000, 550000, 600000, 650000, 700000, 750000, 800000, 850000, 900000,
-       950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000,
-       1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000,
-       1750000, 1800000, 1850000, 1900000, 1950000, 2000000
-};
-
-#define BQ24257_ICHG_MAP_SIZE          ARRAY_SIZE(bq24257_ichg_map)
-
-static const u32 bq24257_iterm_map[] = {
-       50000, 75000, 100000, 125000, 150000, 175000, 200000, 225000
-};
-
-#define BQ24257_ITERM_MAP_SIZE         ARRAY_SIZE(bq24257_iterm_map)
-
-static const u32 bq24257_iilimit_map[] = {
-       100000, 150000, 500000, 900000, 1500000, 2000000
-};
-
-#define BQ24257_IILIMIT_MAP_SIZE       ARRAY_SIZE(bq24257_iilimit_map)
-
-static const u32 bq24257_vovp_map[] = {
-       6000000, 6500000, 7000000, 8000000, 9000000, 9500000, 10000000,
-       10500000
-};
-
-#define BQ24257_VOVP_MAP_SIZE          ARRAY_SIZE(bq24257_vovp_map)
-
-static const u32 bq24257_vindpm_map[] = {
-       4200000, 4280000, 4360000, 4440000, 4520000, 4600000, 4680000,
-       4760000
-};
-
-#define BQ24257_VINDPM_MAP_SIZE                ARRAY_SIZE(bq24257_vindpm_map)
-
-static int bq24257_field_read(struct bq24257_device *bq,
-                             enum bq24257_fields field_id)
-{
-       int ret;
-       int val;
-
-       ret = regmap_field_read(bq->rmap_fields[field_id], &val);
-       if (ret < 0)
-               return ret;
-
-       return val;
-}
-
-static int bq24257_field_write(struct bq24257_device *bq,
-                              enum bq24257_fields field_id, u8 val)
-{
-       return regmap_field_write(bq->rmap_fields[field_id], val);
-}
-
-static u8 bq24257_find_idx(u32 value, const u32 *map, u8 map_size)
-{
-       u8 idx;
-
-       for (idx = 1; idx < map_size; idx++)
-               if (value < map[idx])
-                       break;
-
-       return idx - 1;
-}
-
-enum bq24257_status {
-       STATUS_READY,
-       STATUS_CHARGE_IN_PROGRESS,
-       STATUS_CHARGE_DONE,
-       STATUS_FAULT,
-};
-
-enum bq24257_fault {
-       FAULT_NORMAL,
-       FAULT_INPUT_OVP,
-       FAULT_INPUT_UVLO,
-       FAULT_SLEEP,
-       FAULT_BAT_TS,
-       FAULT_BAT_OVP,
-       FAULT_TS,
-       FAULT_TIMER,
-       FAULT_NO_BAT,
-       FAULT_ISET,
-       FAULT_INPUT_LDO_LOW,
-};
-
-static int bq24257_get_input_current_limit(struct bq24257_device *bq,
-                                          union power_supply_propval *val)
-{
-       int ret;
-
-       ret = bq24257_field_read(bq, F_IILIMIT);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * The "External ILIM" and "Production & Test" modes are not exposed
-        * through this driver and not being covered by the lookup table.
-        * Should such a mode have become active let's return an error rather
-        * than exceeding the bounds of the lookup table and returning
-        * garbage.
-        */
-       if (ret >= BQ24257_IILIMIT_MAP_SIZE)
-               return -ENODATA;
-
-       val->intval = bq24257_iilimit_map[ret];
-
-       return 0;
-}
-
-static int bq24257_set_input_current_limit(struct bq24257_device *bq,
-                                       const union power_supply_propval *val)
-{
-       /*
-        * Address the case where the user manually sets an input current limit
-        * while the charger auto-detection mechanism is is active. In this
-        * case we want to abort and go straight to the user-specified value.
-        */
-       if (bq->iilimit_autoset_enable)
-               cancel_delayed_work_sync(&bq->iilimit_setup_work);
-
-       return bq24257_field_write(bq, F_IILIMIT,
-                                  bq24257_find_idx(val->intval,
-                                                   bq24257_iilimit_map,
-                                                   BQ24257_IILIMIT_MAP_SIZE));
-}
-
-static int bq24257_power_supply_get_property(struct power_supply *psy,
-                                            enum power_supply_property psp,
-                                            union power_supply_propval *val)
-{
-       struct bq24257_device *bq = power_supply_get_drvdata(psy);
-       struct bq24257_state state;
-
-       mutex_lock(&bq->lock);
-       state = bq->state;
-       mutex_unlock(&bq->lock);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (!state.power_good)
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (state.status == STATUS_READY)
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               else if (state.status == STATUS_CHARGE_IN_PROGRESS)
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else if (state.status == STATUS_CHARGE_DONE)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-               break;
-
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = BQ24257_MANUFACTURER;
-               break;
-
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = bq2425x_chip_name[bq->chip];
-               break;
-
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = state.power_good;
-               break;
-
-       case POWER_SUPPLY_PROP_HEALTH:
-               switch (state.fault) {
-               case FAULT_NORMAL:
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-                       break;
-
-               case FAULT_INPUT_OVP:
-               case FAULT_BAT_OVP:
-                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-                       break;
-
-               case FAULT_TS:
-               case FAULT_BAT_TS:
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-                       break;
-
-               case FAULT_TIMER:
-                       val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
-                       break;
-
-               default:
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-                       break;
-               }
-
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               val->intval = bq24257_ichg_map[bq->init_data.ichg];
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
-               val->intval = bq24257_ichg_map[BQ24257_ICHG_MAP_SIZE - 1];
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               val->intval = bq24257_vbat_map[bq->init_data.vbat];
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
-               val->intval = bq24257_vbat_map[BQ24257_VBAT_MAP_SIZE - 1];
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
-               val->intval = bq24257_iterm_map[bq->init_data.iterm];
-               break;
-
-       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
-               return bq24257_get_input_current_limit(bq, val);
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int bq24257_power_supply_set_property(struct power_supply *psy,
-                                       enum power_supply_property prop,
-                                       const union power_supply_propval *val)
-{
-       struct bq24257_device *bq = power_supply_get_drvdata(psy);
-
-       switch (prop) {
-       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
-               return bq24257_set_input_current_limit(bq, val);
-       default:
-               return -EINVAL;
-       }
-}
-
-static int bq24257_power_supply_property_is_writeable(struct power_supply *psy,
-                                       enum power_supply_property psp)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
-               return true;
-       default:
-               return false;
-       }
-}
-
-static int bq24257_get_chip_state(struct bq24257_device *bq,
-                                 struct bq24257_state *state)
-{
-       int ret;
-
-       ret = bq24257_field_read(bq, F_STAT);
-       if (ret < 0)
-               return ret;
-
-       state->status = ret;
-
-       ret = bq24257_field_read(bq, F_FAULT);
-       if (ret < 0)
-               return ret;
-
-       state->fault = ret;
-
-       if (bq->pg)
-               state->power_good = !gpiod_get_value_cansleep(bq->pg);
-       else
-               /*
-                * If we have a chip without a dedicated power-good GPIO or
-                * some other explicit bit that would provide this information
-                * assume the power is good if there is no supply related
-                * fault - and not good otherwise. There is a possibility for
-                * other errors to mask that power in fact is not good but this
-                * is probably the best we can do here.
-                */
-               switch (state->fault) {
-               case FAULT_INPUT_OVP:
-               case FAULT_INPUT_UVLO:
-               case FAULT_INPUT_LDO_LOW:
-                       state->power_good = false;
-                       break;
-               default:
-                       state->power_good = true;
-               }
-
-       return 0;
-}
-
-static bool bq24257_state_changed(struct bq24257_device *bq,
-                                 struct bq24257_state *new_state)
-{
-       int ret;
-
-       mutex_lock(&bq->lock);
-       ret = (bq->state.status != new_state->status ||
-              bq->state.fault != new_state->fault ||
-              bq->state.power_good != new_state->power_good);
-       mutex_unlock(&bq->lock);
-
-       return ret;
-}
-
-enum bq24257_loop_status {
-       LOOP_STATUS_NONE,
-       LOOP_STATUS_IN_DPM,
-       LOOP_STATUS_IN_CURRENT_LIMIT,
-       LOOP_STATUS_THERMAL,
-};
-
-enum bq24257_in_ilimit {
-       IILIMIT_100,
-       IILIMIT_150,
-       IILIMIT_500,
-       IILIMIT_900,
-       IILIMIT_1500,
-       IILIMIT_2000,
-       IILIMIT_EXT,
-       IILIMIT_NONE,
-};
-
-enum bq24257_vovp {
-       VOVP_6000,
-       VOVP_6500,
-       VOVP_7000,
-       VOVP_8000,
-       VOVP_9000,
-       VOVP_9500,
-       VOVP_10000,
-       VOVP_10500
-};
-
-enum bq24257_vindpm {
-       VINDPM_4200,
-       VINDPM_4280,
-       VINDPM_4360,
-       VINDPM_4440,
-       VINDPM_4520,
-       VINDPM_4600,
-       VINDPM_4680,
-       VINDPM_4760
-};
-
-enum bq24257_port_type {
-       PORT_TYPE_DCP,          /* Dedicated Charging Port */
-       PORT_TYPE_CDP,          /* Charging Downstream Port */
-       PORT_TYPE_SDP,          /* Standard Downstream Port */
-       PORT_TYPE_NON_STANDARD,
-};
-
-enum bq24257_safety_timer {
-       SAFETY_TIMER_45,
-       SAFETY_TIMER_360,
-       SAFETY_TIMER_540,
-       SAFETY_TIMER_NONE,
-};
-
-static int bq24257_iilimit_autoset(struct bq24257_device *bq)
-{
-       int loop_status;
-       int iilimit;
-       int port_type;
-       int ret;
-       const u8 new_iilimit[] = {
-               [PORT_TYPE_DCP] = IILIMIT_2000,
-               [PORT_TYPE_CDP] = IILIMIT_2000,
-               [PORT_TYPE_SDP] = IILIMIT_500,
-               [PORT_TYPE_NON_STANDARD] = IILIMIT_500
-       };
-
-       ret = bq24257_field_read(bq, F_LOOP_STATUS);
-       if (ret < 0)
-               goto error;
-
-       loop_status = ret;
-
-       ret = bq24257_field_read(bq, F_IILIMIT);
-       if (ret < 0)
-               goto error;
-
-       iilimit = ret;
-
-       /*
-        * All USB ports should be able to handle 500mA. If not, DPM will lower
-        * the charging current to accommodate the power source. No need to set
-        * a lower IILIMIT value.
-        */
-       if (loop_status == LOOP_STATUS_IN_DPM && iilimit == IILIMIT_500)
-               return 0;
-
-       ret = bq24257_field_read(bq, F_USB_DET);
-       if (ret < 0)
-               goto error;
-
-       port_type = ret;
-
-       ret = bq24257_field_write(bq, F_IILIMIT, new_iilimit[port_type]);
-       if (ret < 0)
-               goto error;
-
-       ret = bq24257_field_write(bq, F_TMR, SAFETY_TIMER_360);
-       if (ret < 0)
-               goto error;
-
-       ret = bq24257_field_write(bq, F_CLR_VDP, 1);
-       if (ret < 0)
-               goto error;
-
-       dev_dbg(bq->dev, "port/loop = %d/%d -> iilimit = %d\n",
-               port_type, loop_status, new_iilimit[port_type]);
-
-       return 0;
-
-error:
-       dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__);
-       return ret;
-}
-
-static void bq24257_iilimit_setup_work(struct work_struct *work)
-{
-       struct bq24257_device *bq = container_of(work, struct bq24257_device,
-                                                iilimit_setup_work.work);
-
-       bq24257_iilimit_autoset(bq);
-}
-
-static void bq24257_handle_state_change(struct bq24257_device *bq,
-                                       struct bq24257_state *new_state)
-{
-       int ret;
-       struct bq24257_state old_state;
-
-       mutex_lock(&bq->lock);
-       old_state = bq->state;
-       mutex_unlock(&bq->lock);
-
-       /*
-        * Handle BQ2425x state changes observing whether the D+/D- based input
-        * current limit autoset functionality is enabled.
-        */
-       if (!new_state->power_good) {
-               dev_dbg(bq->dev, "Power removed\n");
-               if (bq->iilimit_autoset_enable) {
-                       cancel_delayed_work_sync(&bq->iilimit_setup_work);
-
-                       /* activate D+/D- port detection algorithm */
-                       ret = bq24257_field_write(bq, F_DPDM_EN, 1);
-                       if (ret < 0)
-                               goto error;
-               }
-               /*
-                * When power is removed always return to the default input
-                * current limit as configured during probe.
-                */
-               ret = bq24257_field_write(bq, F_IILIMIT, bq->init_data.iilimit);
-               if (ret < 0)
-                       goto error;
-       } else if (!old_state.power_good) {
-               dev_dbg(bq->dev, "Power inserted\n");
-
-               if (bq->iilimit_autoset_enable)
-                       /* configure input current limit */
-                       schedule_delayed_work(&bq->iilimit_setup_work,
-                                     msecs_to_jiffies(BQ24257_ILIM_SET_DELAY));
-       } else if (new_state->fault == FAULT_NO_BAT) {
-               dev_warn(bq->dev, "Battery removed\n");
-       } else if (new_state->fault == FAULT_TIMER) {
-               dev_err(bq->dev, "Safety timer expired! Battery dead?\n");
-       }
-
-       return;
-
-error:
-       dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__);
-}
-
-static irqreturn_t bq24257_irq_handler_thread(int irq, void *private)
-{
-       int ret;
-       struct bq24257_device *bq = private;
-       struct bq24257_state state;
-
-       ret = bq24257_get_chip_state(bq, &state);
-       if (ret < 0)
-               return IRQ_HANDLED;
-
-       if (!bq24257_state_changed(bq, &state))
-               return IRQ_HANDLED;
-
-       dev_dbg(bq->dev, "irq(state changed): status/fault/pg = %d/%d/%d\n",
-               state.status, state.fault, state.power_good);
-
-       bq24257_handle_state_change(bq, &state);
-
-       mutex_lock(&bq->lock);
-       bq->state = state;
-       mutex_unlock(&bq->lock);
-
-       power_supply_changed(bq->charger);
-
-       return IRQ_HANDLED;
-}
-
-static int bq24257_hw_init(struct bq24257_device *bq)
-{
-       int ret;
-       int i;
-       struct bq24257_state state;
-
-       const struct {
-               int field;
-               u32 value;
-       } init_data[] = {
-               {F_ICHG, bq->init_data.ichg},
-               {F_VBAT, bq->init_data.vbat},
-               {F_ITERM, bq->init_data.iterm},
-               {F_VOVP, bq->init_data.vovp},
-               {F_VINDPM, bq->init_data.vindpm},
-       };
-
-       /*
-        * Disable the watchdog timer to prevent the IC from going back to
-        * default settings after 50 seconds of I2C inactivity.
-        */
-       ret = bq24257_field_write(bq, F_WD_EN, 0);
-       if (ret < 0)
-               return ret;
-
-       /* configure the charge currents and voltages */
-       for (i = 0; i < ARRAY_SIZE(init_data); i++) {
-               ret = bq24257_field_write(bq, init_data[i].field,
-                                         init_data[i].value);
-               if (ret < 0)
-                       return ret;
-       }
-
-       ret = bq24257_get_chip_state(bq, &state);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&bq->lock);
-       bq->state = state;
-       mutex_unlock(&bq->lock);
-
-       if (!bq->iilimit_autoset_enable) {
-               dev_dbg(bq->dev, "manually setting iilimit = %u\n",
-                       bq->init_data.iilimit);
-
-               /* program fixed input current limit */
-               ret = bq24257_field_write(bq, F_IILIMIT,
-                                         bq->init_data.iilimit);
-               if (ret < 0)
-                       return ret;
-       } else if (!state.power_good)
-               /* activate D+/D- detection algorithm */
-               ret = bq24257_field_write(bq, F_DPDM_EN, 1);
-       else if (state.fault != FAULT_NO_BAT)
-               ret = bq24257_iilimit_autoset(bq);
-
-       return ret;
-}
-
-static enum power_supply_property bq24257_power_supply_props[] = {
-       POWER_SUPPLY_PROP_MANUFACTURER,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
-       POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
-};
-
-static char *bq24257_charger_supplied_to[] = {
-       "main-battery",
-};
-
-static const struct power_supply_desc bq24257_power_supply_desc = {
-       .name = "bq24257-charger",
-       .type = POWER_SUPPLY_TYPE_USB,
-       .properties = bq24257_power_supply_props,
-       .num_properties = ARRAY_SIZE(bq24257_power_supply_props),
-       .get_property = bq24257_power_supply_get_property,
-       .set_property = bq24257_power_supply_set_property,
-       .property_is_writeable = bq24257_power_supply_property_is_writeable,
-};
-
-static ssize_t bq24257_show_ovp_voltage(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq24257_device *bq = power_supply_get_drvdata(psy);
-
-       return scnprintf(buf, PAGE_SIZE, "%u\n",
-                        bq24257_vovp_map[bq->init_data.vovp]);
-}
-
-static ssize_t bq24257_show_in_dpm_voltage(struct device *dev,
-                                          struct device_attribute *attr,
-                                          char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq24257_device *bq = power_supply_get_drvdata(psy);
-
-       return scnprintf(buf, PAGE_SIZE, "%u\n",
-                        bq24257_vindpm_map[bq->init_data.vindpm]);
-}
-
-static ssize_t bq24257_sysfs_show_enable(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq24257_device *bq = power_supply_get_drvdata(psy);
-       int ret;
-
-       if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
-               ret = bq24257_field_read(bq, F_HZ_MODE);
-       else if (strcmp(attr->attr.name, "sysoff_enable") == 0)
-               ret = bq24257_field_read(bq, F_SYSOFF);
-       else
-               return -EINVAL;
-
-       if (ret < 0)
-               return ret;
-
-       return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
-}
-
-static ssize_t bq24257_sysfs_set_enable(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf,
-                                       size_t count)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       struct bq24257_device *bq = power_supply_get_drvdata(psy);
-       long val;
-       int ret;
-
-       if (kstrtol(buf, 10, &val) < 0)
-               return -EINVAL;
-
-       if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
-               ret = bq24257_field_write(bq, F_HZ_MODE, (bool)val);
-       else if (strcmp(attr->attr.name, "sysoff_enable") == 0)
-               ret = bq24257_field_write(bq, F_SYSOFF, (bool)val);
-       else
-               return -EINVAL;
-
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static DEVICE_ATTR(ovp_voltage, S_IRUGO, bq24257_show_ovp_voltage, NULL);
-static DEVICE_ATTR(in_dpm_voltage, S_IRUGO, bq24257_show_in_dpm_voltage, NULL);
-static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
-                  bq24257_sysfs_show_enable, bq24257_sysfs_set_enable);
-static DEVICE_ATTR(sysoff_enable, S_IWUSR | S_IRUGO,
-                  bq24257_sysfs_show_enable, bq24257_sysfs_set_enable);
-
-static struct attribute *bq24257_charger_attr[] = {
-       &dev_attr_ovp_voltage.attr,
-       &dev_attr_in_dpm_voltage.attr,
-       &dev_attr_high_impedance_enable.attr,
-       &dev_attr_sysoff_enable.attr,
-       NULL,
-};
-
-static const struct attribute_group bq24257_attr_group = {
-       .attrs = bq24257_charger_attr,
-};
-
-static int bq24257_power_supply_init(struct bq24257_device *bq)
-{
-       struct power_supply_config psy_cfg = { .drv_data = bq, };
-
-       psy_cfg.supplied_to = bq24257_charger_supplied_to;
-       psy_cfg.num_supplicants = ARRAY_SIZE(bq24257_charger_supplied_to);
-
-       bq->charger = devm_power_supply_register(bq->dev,
-                                                &bq24257_power_supply_desc,
-                                                &psy_cfg);
-
-       return PTR_ERR_OR_ZERO(bq->charger);
-}
-
-static void bq24257_pg_gpio_probe(struct bq24257_device *bq)
-{
-       bq->pg = devm_gpiod_get_optional(bq->dev, BQ24257_PG_GPIO, GPIOD_IN);
-
-       if (PTR_ERR(bq->pg) == -EPROBE_DEFER) {
-               dev_info(bq->dev, "probe retry requested for PG pin\n");
-               return;
-       } else if (IS_ERR(bq->pg)) {
-               dev_err(bq->dev, "error probing PG pin\n");
-               bq->pg = NULL;
-               return;
-       }
-
-       if (bq->pg)
-               dev_dbg(bq->dev, "probed PG pin = %d\n", desc_to_gpio(bq->pg));
-}
-
-static int bq24257_fw_probe(struct bq24257_device *bq)
-{
-       int ret;
-       u32 property;
-
-       /* Required properties */
-       ret = device_property_read_u32(bq->dev, "ti,charge-current", &property);
-       if (ret < 0)
-               return ret;
-
-       bq->init_data.ichg = bq24257_find_idx(property, bq24257_ichg_map,
-                                             BQ24257_ICHG_MAP_SIZE);
-
-       ret = device_property_read_u32(bq->dev, "ti,battery-regulation-voltage",
-                                      &property);
-       if (ret < 0)
-               return ret;
-
-       bq->init_data.vbat = bq24257_find_idx(property, bq24257_vbat_map,
-                                             BQ24257_VBAT_MAP_SIZE);
-
-       ret = device_property_read_u32(bq->dev, "ti,termination-current",
-                                      &property);
-       if (ret < 0)
-               return ret;
-
-       bq->init_data.iterm = bq24257_find_idx(property, bq24257_iterm_map,
-                                              BQ24257_ITERM_MAP_SIZE);
-
-       /* Optional properties. If not provided use reasonable default. */
-       ret = device_property_read_u32(bq->dev, "ti,current-limit",
-                                      &property);
-       if (ret < 0) {
-               bq->iilimit_autoset_enable = true;
-
-               /*
-                * Explicitly set a default value which will be needed for
-                * devices that don't support the automatic setting of the input
-                * current limit through the charger type detection mechanism.
-                */
-               bq->init_data.iilimit = IILIMIT_500;
-       } else
-               bq->init_data.iilimit =
-                               bq24257_find_idx(property,
-                                                bq24257_iilimit_map,
-                                                BQ24257_IILIMIT_MAP_SIZE);
-
-       ret = device_property_read_u32(bq->dev, "ti,ovp-voltage",
-                                      &property);
-       if (ret < 0)
-               bq->init_data.vovp = VOVP_6500;
-       else
-               bq->init_data.vovp = bq24257_find_idx(property,
-                                                     bq24257_vovp_map,
-                                                     BQ24257_VOVP_MAP_SIZE);
-
-       ret = device_property_read_u32(bq->dev, "ti,in-dpm-voltage",
-                                      &property);
-       if (ret < 0)
-               bq->init_data.vindpm = VINDPM_4360;
-       else
-               bq->init_data.vindpm =
-                               bq24257_find_idx(property,
-                                                bq24257_vindpm_map,
-                                                BQ24257_VINDPM_MAP_SIZE);
-
-       return 0;
-}
-
-static int bq24257_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id)
-{
-       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       struct device *dev = &client->dev;
-       const struct acpi_device_id *acpi_id;
-       struct bq24257_device *bq;
-       int ret;
-       int i;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
-               return -ENODEV;
-       }
-
-       bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
-       if (!bq)
-               return -ENOMEM;
-
-       bq->client = client;
-       bq->dev = dev;
-
-       if (ACPI_HANDLE(dev)) {
-               acpi_id = acpi_match_device(dev->driver->acpi_match_table,
-                                           &client->dev);
-               if (!acpi_id) {
-                       dev_err(dev, "Failed to match ACPI device\n");
-                       return -ENODEV;
-               }
-               bq->chip = (enum bq2425x_chip)acpi_id->driver_data;
-       } else {
-               bq->chip = (enum bq2425x_chip)id->driver_data;
-       }
-
-       mutex_init(&bq->lock);
-
-       bq->rmap = devm_regmap_init_i2c(client, &bq24257_regmap_config);
-       if (IS_ERR(bq->rmap)) {
-               dev_err(dev, "failed to allocate register map\n");
-               return PTR_ERR(bq->rmap);
-       }
-
-       for (i = 0; i < ARRAY_SIZE(bq24257_reg_fields); i++) {
-               const struct reg_field *reg_fields = bq24257_reg_fields;
-
-               bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
-                                                            reg_fields[i]);
-               if (IS_ERR(bq->rmap_fields[i])) {
-                       dev_err(dev, "cannot allocate regmap field\n");
-                       return PTR_ERR(bq->rmap_fields[i]);
-               }
-       }
-
-       i2c_set_clientdata(client, bq);
-
-       if (!dev->platform_data) {
-               ret = bq24257_fw_probe(bq);
-               if (ret < 0) {
-                       dev_err(dev, "Cannot read device properties.\n");
-                       return ret;
-               }
-       } else {
-               return -ENODEV;
-       }
-
-       /*
-        * The BQ24250 doesn't support the D+/D- based charger type detection
-        * used for the automatic setting of the input current limit setting so
-        * explicitly disable that feature.
-        */
-       if (bq->chip == BQ24250)
-               bq->iilimit_autoset_enable = false;
-
-       if (bq->iilimit_autoset_enable)
-               INIT_DELAYED_WORK(&bq->iilimit_setup_work,
-                                 bq24257_iilimit_setup_work);
-
-       /*
-        * The BQ24250 doesn't have a dedicated Power Good (PG) pin so let's
-        * not probe for it and instead use a SW-based approach to determine
-        * the PG state. We also use a SW-based approach for all other devices
-        * if the PG pin is either not defined or can't be probed.
-        */
-       if (bq->chip != BQ24250)
-               bq24257_pg_gpio_probe(bq);
-
-       if (PTR_ERR(bq->pg) == -EPROBE_DEFER)
-               return PTR_ERR(bq->pg);
-       else if (!bq->pg)
-               dev_info(bq->dev, "using SW-based power-good detection\n");
-
-       /* reset all registers to defaults */
-       ret = bq24257_field_write(bq, F_RESET, 1);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * Put the RESET bit back to 0, in cache. For some reason the HW always
-        * returns 1 on this bit, so this is the only way to avoid resetting the
-        * chip every time we update another field in this register.
-        */
-       ret = bq24257_field_write(bq, F_RESET, 0);
-       if (ret < 0)
-               return ret;
-
-       ret = bq24257_hw_init(bq);
-       if (ret < 0) {
-               dev_err(dev, "Cannot initialize the chip.\n");
-               return ret;
-       }
-
-       ret = devm_request_threaded_irq(dev, client->irq, NULL,
-                                       bq24257_irq_handler_thread,
-                                       IRQF_TRIGGER_FALLING |
-                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-                                       bq2425x_chip_name[bq->chip], bq);
-       if (ret) {
-               dev_err(dev, "Failed to request IRQ #%d\n", client->irq);
-               return ret;
-       }
-
-       ret = bq24257_power_supply_init(bq);
-       if (ret < 0) {
-               dev_err(dev, "Failed to register power supply\n");
-               return ret;
-       }
-
-       ret = sysfs_create_group(&bq->charger->dev.kobj, &bq24257_attr_group);
-       if (ret < 0) {
-               dev_err(dev, "Can't create sysfs entries\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int bq24257_remove(struct i2c_client *client)
-{
-       struct bq24257_device *bq = i2c_get_clientdata(client);
-
-       if (bq->iilimit_autoset_enable)
-               cancel_delayed_work_sync(&bq->iilimit_setup_work);
-
-       sysfs_remove_group(&bq->charger->dev.kobj, &bq24257_attr_group);
-
-       bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int bq24257_suspend(struct device *dev)
-{
-       struct bq24257_device *bq = dev_get_drvdata(dev);
-       int ret = 0;
-
-       if (bq->iilimit_autoset_enable)
-               cancel_delayed_work_sync(&bq->iilimit_setup_work);
-
-       /* reset all registers to default (and activate standalone mode) */
-       ret = bq24257_field_write(bq, F_RESET, 1);
-       if (ret < 0)
-               dev_err(bq->dev, "Cannot reset chip to standalone mode.\n");
-
-       return ret;
-}
-
-static int bq24257_resume(struct device *dev)
-{
-       int ret;
-       struct bq24257_device *bq = dev_get_drvdata(dev);
-
-       ret = regcache_drop_region(bq->rmap, BQ24257_REG_1, BQ24257_REG_7);
-       if (ret < 0)
-               return ret;
-
-       ret = bq24257_field_write(bq, F_RESET, 0);
-       if (ret < 0)
-               return ret;
-
-       ret = bq24257_hw_init(bq);
-       if (ret < 0) {
-               dev_err(bq->dev, "Cannot init chip after resume.\n");
-               return ret;
-       }
-
-       /* signal userspace, maybe state changed while suspended */
-       power_supply_changed(bq->charger);
-
-       return 0;
-}
-#endif
-
-static const struct dev_pm_ops bq24257_pm = {
-       SET_SYSTEM_SLEEP_PM_OPS(bq24257_suspend, bq24257_resume)
-};
-
-static const struct i2c_device_id bq24257_i2c_ids[] = {
-       { "bq24250", BQ24250 },
-       { "bq24251", BQ24251 },
-       { "bq24257", BQ24257 },
-       {},
-};
-MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids);
-
-static const struct of_device_id bq24257_of_match[] = {
-       { .compatible = "ti,bq24250", },
-       { .compatible = "ti,bq24251", },
-       { .compatible = "ti,bq24257", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, bq24257_of_match);
-
-static const struct acpi_device_id bq24257_acpi_match[] = {
-       { "BQ242500", BQ24250 },
-       { "BQ242510", BQ24251 },
-       { "BQ242570", BQ24257 },
-       {},
-};
-MODULE_DEVICE_TABLE(acpi, bq24257_acpi_match);
-
-static struct i2c_driver bq24257_driver = {
-       .driver = {
-               .name = "bq24257-charger",
-               .of_match_table = of_match_ptr(bq24257_of_match),
-               .acpi_match_table = ACPI_PTR(bq24257_acpi_match),
-               .pm = &bq24257_pm,
-       },
-       .probe = bq24257_probe,
-       .remove = bq24257_remove,
-       .id_table = bq24257_i2c_ids,
-};
-module_i2c_driver(bq24257_driver);
-
-MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
-MODULE_DESCRIPTION("bq24257 charger driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c
deleted file mode 100644 (file)
index fa454c1..0000000
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Battery charger driver for TI BQ24735
- *
- * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
- *
- * 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;
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-#include <linux/err.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-
-#include <linux/power/bq24735-charger.h>
-
-#define BQ24735_CHG_OPT                        0x12
-#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0)
-#define BQ24735_CHG_OPT_AC_PRESENT     (1 << 4)
-#define BQ24735_CHARGE_CURRENT         0x14
-#define BQ24735_CHARGE_CURRENT_MASK    0x1fc0
-#define BQ24735_CHARGE_VOLTAGE         0x15
-#define BQ24735_CHARGE_VOLTAGE_MASK    0x7ff0
-#define BQ24735_INPUT_CURRENT          0x3f
-#define BQ24735_INPUT_CURRENT_MASK     0x1f80
-#define BQ24735_MANUFACTURER_ID                0xfe
-#define BQ24735_DEVICE_ID              0xff
-
-struct bq24735 {
-       struct power_supply             *charger;
-       struct power_supply_desc        charger_desc;
-       struct i2c_client               *client;
-       struct bq24735_platform         *pdata;
-       struct mutex                    lock;
-       bool                            charging;
-};
-
-static inline struct bq24735 *to_bq24735(struct power_supply *psy)
-{
-       return power_supply_get_drvdata(psy);
-}
-
-static enum power_supply_property bq24735_charger_properties[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static int bq24735_charger_property_is_writeable(struct power_supply *psy,
-                                                enum power_supply_property psp)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               return 1;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static inline int bq24735_write_word(struct i2c_client *client, u8 reg,
-                                    u16 value)
-{
-       return i2c_smbus_write_word_data(client, reg, le16_to_cpu(value));
-}
-
-static inline int bq24735_read_word(struct i2c_client *client, u8 reg)
-{
-       s32 ret = i2c_smbus_read_word_data(client, reg);
-
-       return ret < 0 ? ret : le16_to_cpu(ret);
-}
-
-static int bq24735_update_word(struct i2c_client *client, u8 reg,
-                              u16 mask, u16 value)
-{
-       unsigned int tmp;
-       int ret;
-
-       ret = bq24735_read_word(client, reg);
-       if (ret < 0)
-               return ret;
-
-       tmp = ret & ~mask;
-       tmp |= value & mask;
-
-       return bq24735_write_word(client, reg, tmp);
-}
-
-static inline int bq24735_enable_charging(struct bq24735 *charger)
-{
-       if (charger->pdata->ext_control)
-               return 0;
-
-       return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
-                                  BQ24735_CHG_OPT_CHARGE_DISABLE,
-                                  ~BQ24735_CHG_OPT_CHARGE_DISABLE);
-}
-
-static inline int bq24735_disable_charging(struct bq24735 *charger)
-{
-       if (charger->pdata->ext_control)
-               return 0;
-
-       return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
-                                  BQ24735_CHG_OPT_CHARGE_DISABLE,
-                                  BQ24735_CHG_OPT_CHARGE_DISABLE);
-}
-
-static int bq24735_config_charger(struct bq24735 *charger)
-{
-       struct bq24735_platform *pdata = charger->pdata;
-       int ret;
-       u16 value;
-
-       if (pdata->ext_control)
-               return 0;
-
-       if (pdata->charge_current) {
-               value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK;
-
-               ret = bq24735_write_word(charger->client,
-                                        BQ24735_CHARGE_CURRENT, value);
-               if (ret < 0) {
-                       dev_err(&charger->client->dev,
-                               "Failed to write charger current : %d\n",
-                               ret);
-                       return ret;
-               }
-       }
-
-       if (pdata->charge_voltage) {
-               value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK;
-
-               ret = bq24735_write_word(charger->client,
-                                        BQ24735_CHARGE_VOLTAGE, value);
-               if (ret < 0) {
-                       dev_err(&charger->client->dev,
-                               "Failed to write charger voltage : %d\n",
-                               ret);
-                       return ret;
-               }
-       }
-
-       if (pdata->input_current) {
-               value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK;
-
-               ret = bq24735_write_word(charger->client,
-                                        BQ24735_INPUT_CURRENT, value);
-               if (ret < 0) {
-                       dev_err(&charger->client->dev,
-                               "Failed to write input current : %d\n",
-                               ret);
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-static bool bq24735_charger_is_present(struct bq24735 *charger)
-{
-       struct bq24735_platform *pdata = charger->pdata;
-       int ret;
-
-       if (pdata->status_gpio_valid) {
-               ret = gpio_get_value_cansleep(pdata->status_gpio);
-               return ret ^= pdata->status_gpio_active_low == 0;
-       } else {
-               int ac = 0;
-
-               ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
-               if (ac < 0) {
-                       dev_err(&charger->client->dev,
-                               "Failed to read charger options : %d\n",
-                               ac);
-                       return false;
-               }
-               return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
-       }
-
-       return false;
-}
-
-static int bq24735_charger_is_charging(struct bq24735 *charger)
-{
-       int ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
-
-       if (ret < 0)
-               return ret;
-
-       return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
-}
-
-static irqreturn_t bq24735_charger_isr(int irq, void *devid)
-{
-       struct power_supply *psy = devid;
-       struct bq24735 *charger = to_bq24735(psy);
-
-       mutex_lock(&charger->lock);
-
-       if (charger->charging && bq24735_charger_is_present(charger))
-               bq24735_enable_charging(charger);
-       else
-               bq24735_disable_charging(charger);
-
-       mutex_unlock(&charger->lock);
-
-       power_supply_changed(psy);
-
-       return IRQ_HANDLED;
-}
-
-static int bq24735_charger_get_property(struct power_supply *psy,
-                                       enum power_supply_property psp,
-                                       union power_supply_propval *val)
-{
-       struct bq24735 *charger = to_bq24735(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = bq24735_charger_is_present(charger) ? 1 : 0;
-               break;
-       case POWER_SUPPLY_PROP_STATUS:
-               switch (bq24735_charger_is_charging(charger)) {
-               case 1:
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-                       break;
-               case 0:
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-                       break;
-               default:
-                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-                       break;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int bq24735_charger_set_property(struct power_supply *psy,
-                                       enum power_supply_property psp,
-                                       const union power_supply_propval *val)
-{
-       struct bq24735 *charger = to_bq24735(psy);
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               switch (val->intval) {
-               case POWER_SUPPLY_STATUS_CHARGING:
-                       mutex_lock(&charger->lock);
-                       charger->charging = true;
-                       ret = bq24735_enable_charging(charger);
-                       mutex_unlock(&charger->lock);
-                       if (ret)
-                               return ret;
-                       bq24735_config_charger(charger);
-                       break;
-               case POWER_SUPPLY_STATUS_DISCHARGING:
-               case POWER_SUPPLY_STATUS_NOT_CHARGING:
-                       mutex_lock(&charger->lock);
-                       charger->charging = false;
-                       ret = bq24735_disable_charging(charger);
-                       mutex_unlock(&charger->lock);
-                       if (ret)
-                               return ret;
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               power_supply_changed(psy);
-               break;
-       default:
-               return -EPERM;
-       }
-
-       return 0;
-}
-
-static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client)
-{
-       struct bq24735_platform *pdata;
-       struct device_node *np = client->dev.of_node;
-       u32 val;
-       int ret;
-       enum of_gpio_flags flags;
-
-       pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata) {
-               dev_err(&client->dev,
-                       "Memory alloc for bq24735 pdata failed\n");
-               return NULL;
-       }
-
-       pdata->status_gpio = of_get_named_gpio_flags(np, "ti,ac-detect-gpios",
-                                                    0, &flags);
-
-       if (flags & OF_GPIO_ACTIVE_LOW)
-               pdata->status_gpio_active_low = 1;
-
-       ret = of_property_read_u32(np, "ti,charge-current", &val);
-       if (!ret)
-               pdata->charge_current = val;
-
-       ret = of_property_read_u32(np, "ti,charge-voltage", &val);
-       if (!ret)
-               pdata->charge_voltage = val;
-
-       ret = of_property_read_u32(np, "ti,input-current", &val);
-       if (!ret)
-               pdata->input_current = val;
-
-       pdata->ext_control = of_property_read_bool(np, "ti,external-control");
-
-       return pdata;
-}
-
-static int bq24735_charger_probe(struct i2c_client *client,
-                                const struct i2c_device_id *id)
-{
-       int ret;
-       struct bq24735 *charger;
-       struct power_supply_desc *supply_desc;
-       struct power_supply_config psy_cfg = {};
-       char *name;
-
-       charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);
-       if (!charger)
-               return -ENOMEM;
-
-       mutex_init(&charger->lock);
-       charger->charging = true;
-       charger->pdata = client->dev.platform_data;
-
-       if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)
-               charger->pdata = bq24735_parse_dt_data(client);
-
-       if (!charger->pdata) {
-               dev_err(&client->dev, "no platform data provided\n");
-               return -EINVAL;
-       }
-
-       name = (char *)charger->pdata->name;
-       if (!name) {
-               name = devm_kasprintf(&client->dev, GFP_KERNEL,
-                                     "bq24735@%s",
-                                     dev_name(&client->dev));
-               if (!name) {
-                       dev_err(&client->dev, "Failed to alloc device name\n");
-                       return -ENOMEM;
-               }
-       }
-
-       charger->client = client;
-
-       supply_desc = &charger->charger_desc;
-
-       supply_desc->name = name;
-       supply_desc->type = POWER_SUPPLY_TYPE_MAINS;
-       supply_desc->properties = bq24735_charger_properties;
-       supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties);
-       supply_desc->get_property = bq24735_charger_get_property;
-       supply_desc->set_property = bq24735_charger_set_property;
-       supply_desc->property_is_writeable =
-                               bq24735_charger_property_is_writeable;
-
-       psy_cfg.supplied_to = charger->pdata->supplied_to;
-       psy_cfg.num_supplicants = charger->pdata->num_supplicants;
-       psy_cfg.of_node = client->dev.of_node;
-       psy_cfg.drv_data = charger;
-
-       i2c_set_clientdata(client, charger);
-
-       if (gpio_is_valid(charger->pdata->status_gpio)) {
-               ret = devm_gpio_request(&client->dev,
-                                       charger->pdata->status_gpio,
-                                       name);
-               if (ret) {
-                       dev_err(&client->dev,
-                               "Failed GPIO request for GPIO %d: %d\n",
-                               charger->pdata->status_gpio, ret);
-               }
-
-               charger->pdata->status_gpio_valid = !ret;
-       }
-
-       if (!charger->pdata->status_gpio_valid
-           || bq24735_charger_is_present(charger)) {
-               ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
-               if (ret < 0) {
-                       dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
-                               ret);
-                       return ret;
-               } else if (ret != 0x0040) {
-                       dev_err(&client->dev,
-                               "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
-                       return -ENODEV;
-               }
-
-               ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
-               if (ret < 0) {
-                       dev_err(&client->dev, "Failed to read device id : %d\n", ret);
-                       return ret;
-               } else if (ret != 0x000B) {
-                       dev_err(&client->dev,
-                               "device id mismatch. 0x000b != 0x%04x\n", ret);
-                       return -ENODEV;
-               }
-       }
-
-       ret = bq24735_config_charger(charger);
-       if (ret < 0) {
-               dev_err(&client->dev, "failed in configuring charger");
-               return ret;
-       }
-
-       /* check for AC adapter presence */
-       if (bq24735_charger_is_present(charger)) {
-               ret = bq24735_enable_charging(charger);
-               if (ret < 0) {
-                       dev_err(&client->dev, "Failed to enable charging\n");
-                       return ret;
-               }
-       }
-
-       charger->charger = devm_power_supply_register(&client->dev, supply_desc,
-                                                     &psy_cfg);
-       if (IS_ERR(charger->charger)) {
-               ret = PTR_ERR(charger->charger);
-               dev_err(&client->dev, "Failed to register power supply: %d\n",
-                       ret);
-               return ret;
-       }
-
-       if (client->irq) {
-               ret = devm_request_threaded_irq(&client->dev, client->irq,
-                                               NULL, bq24735_charger_isr,
-                                               IRQF_TRIGGER_RISING |
-                                               IRQF_TRIGGER_FALLING |
-                                               IRQF_ONESHOT,
-                                               supply_desc->name,
-                                               charger->charger);
-               if (ret) {
-                       dev_err(&client->dev,
-                               "Unable to register IRQ %d err %d\n",
-                               client->irq, ret);
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-static const struct i2c_device_id bq24735_charger_id[] = {
-       { "bq24735-charger", 0 },
-       {}
-};
-MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);
-
-static const struct of_device_id bq24735_match_ids[] = {
-       { .compatible = "ti,bq24735", },
-       { /* end */ }
-};
-MODULE_DEVICE_TABLE(of, bq24735_match_ids);
-
-static struct i2c_driver bq24735_charger_driver = {
-       .driver = {
-               .name = "bq24735-charger",
-               .of_match_table = bq24735_match_ids,
-       },
-       .probe = bq24735_charger_probe,
-       .id_table = bq24735_charger_id,
-};
-
-module_i2c_driver(bq24735_charger_driver);
-
-MODULE_DESCRIPTION("bq24735 battery charging driver");
-MODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/bq25890_charger.c b/drivers/power/bq25890_charger.c
deleted file mode 100644 (file)
index f993a55..0000000
+++ /dev/null
@@ -1,994 +0,0 @@
-/*
- * TI BQ25890 charger driver
- *
- * Copyright (C) 2015 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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/i2c.h>
-#include <linux/power_supply.h>
-#include <linux/regmap.h>
-#include <linux/types.h>
-#include <linux/gpio/consumer.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/usb/phy.h>
-
-#include <linux/acpi.h>
-#include <linux/of.h>
-
-#define BQ25890_MANUFACTURER           "Texas Instruments"
-#define BQ25890_IRQ_PIN                        "bq25890_irq"
-
-#define BQ25890_ID                     3
-
-enum bq25890_fields {
-       F_EN_HIZ, F_EN_ILIM, F_IILIM,                                /* Reg00 */
-       F_BHOT, F_BCOLD, F_VINDPM_OFS,                               /* Reg01 */
-       F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN,
-       F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN,          /* Reg02 */
-       F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN,    /* Reg03 */
-       F_PUMPX_EN, F_ICHG,                                          /* Reg04 */
-       F_IPRECHG, F_ITERM,                                          /* Reg05 */
-       F_VREG, F_BATLOWV, F_VRECHG,                                 /* Reg06 */
-       F_TERM_EN, F_STAT_DIS, F_WD, F_TMR_EN, F_CHG_TMR,
-       F_JEITA_ISET,                                                /* Reg07 */
-       F_BATCMP, F_VCLAMP, F_TREG,                                  /* Reg08 */
-       F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET,
-       F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN,       /* Reg09 */
-       F_BOOSTV, F_BOOSTI,                                          /* Reg0A */
-       F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_VSYS_STAT, /* Reg0B */
-       F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT,
-       F_NTC_FAULT,                                                 /* Reg0C */
-       F_FORCE_VINDPM, F_VINDPM,                                    /* Reg0D */
-       F_THERM_STAT, F_BATV,                                        /* Reg0E */
-       F_SYSV,                                                      /* Reg0F */
-       F_TSPCT,                                                     /* Reg10 */
-       F_VBUS_GD, F_VBUSV,                                          /* Reg11 */
-       F_ICHGR,                                                     /* Reg12 */
-       F_VDPM_STAT, F_IDPM_STAT, F_IDPM_LIM,                        /* Reg13 */
-       F_REG_RST, F_ICO_OPTIMIZED, F_PN, F_TS_PROFILE, F_DEV_REV,   /* Reg14 */
-
-       F_MAX_FIELDS
-};
-
-/* initial field values, converted to register values */
-struct bq25890_init_data {
-       u8 ichg;        /* charge current               */
-       u8 vreg;        /* regulation voltage           */
-       u8 iterm;       /* termination current          */
-       u8 iprechg;     /* precharge current            */
-       u8 sysvmin;     /* minimum system voltage limit */
-       u8 boostv;      /* boost regulation voltage     */
-       u8 boosti;      /* boost current limit          */
-       u8 boostf;      /* boost frequency              */
-       u8 ilim_en;     /* enable ILIM pin              */
-       u8 treg;        /* thermal regulation threshold */
-};
-
-struct bq25890_state {
-       u8 online;
-       u8 chrg_status;
-       u8 chrg_fault;
-       u8 vsys_status;
-       u8 boost_fault;
-       u8 bat_fault;
-};
-
-struct bq25890_device {
-       struct i2c_client *client;
-       struct device *dev;
-       struct power_supply *charger;
-
-       struct usb_phy *usb_phy;
-       struct notifier_block usb_nb;
-       struct work_struct usb_work;
-       unsigned long usb_event;
-
-       struct regmap *rmap;
-       struct regmap_field *rmap_fields[F_MAX_FIELDS];
-
-       int chip_id;
-       struct bq25890_init_data init_data;
-       struct bq25890_state state;
-
-       struct mutex lock; /* protect state data */
-};
-
-static const struct regmap_range bq25890_readonly_reg_ranges[] = {
-       regmap_reg_range(0x0b, 0x0c),
-       regmap_reg_range(0x0e, 0x13),
-};
-
-static const struct regmap_access_table bq25890_writeable_regs = {
-       .no_ranges = bq25890_readonly_reg_ranges,
-       .n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges),
-};
-
-static const struct regmap_range bq25890_volatile_reg_ranges[] = {
-       regmap_reg_range(0x00, 0x00),
-       regmap_reg_range(0x09, 0x09),
-       regmap_reg_range(0x0b, 0x0c),
-       regmap_reg_range(0x0e, 0x14),
-};
-
-static const struct regmap_access_table bq25890_volatile_regs = {
-       .yes_ranges = bq25890_volatile_reg_ranges,
-       .n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges),
-};
-
-static const struct regmap_config bq25890_regmap_config = {
-       .reg_bits = 8,
-       .val_bits = 8,
-
-       .max_register = 0x14,
-       .cache_type = REGCACHE_RBTREE,
-
-       .wr_table = &bq25890_writeable_regs,
-       .volatile_table = &bq25890_volatile_regs,
-};
-
-static const struct reg_field bq25890_reg_fields[] = {
-       /* REG00 */
-       [F_EN_HIZ]              = REG_FIELD(0x00, 7, 7),
-       [F_EN_ILIM]             = REG_FIELD(0x00, 6, 6),
-       [F_IILIM]               = REG_FIELD(0x00, 0, 5),
-       /* REG01 */
-       [F_BHOT]                = REG_FIELD(0x01, 6, 7),
-       [F_BCOLD]               = REG_FIELD(0x01, 5, 5),
-       [F_VINDPM_OFS]          = REG_FIELD(0x01, 0, 4),
-       /* REG02 */
-       [F_CONV_START]          = REG_FIELD(0x02, 7, 7),
-       [F_CONV_RATE]           = REG_FIELD(0x02, 6, 6),
-       [F_BOOSTF]              = REG_FIELD(0x02, 5, 5),
-       [F_ICO_EN]              = REG_FIELD(0x02, 4, 4),
-       [F_HVDCP_EN]            = REG_FIELD(0x02, 3, 3),
-       [F_MAXC_EN]             = REG_FIELD(0x02, 2, 2),
-       [F_FORCE_DPM]           = REG_FIELD(0x02, 1, 1),
-       [F_AUTO_DPDM_EN]        = REG_FIELD(0x02, 0, 0),
-       /* REG03 */
-       [F_BAT_LOAD_EN]         = REG_FIELD(0x03, 7, 7),
-       [F_WD_RST]              = REG_FIELD(0x03, 6, 6),
-       [F_OTG_CFG]             = REG_FIELD(0x03, 5, 5),
-       [F_CHG_CFG]             = REG_FIELD(0x03, 4, 4),
-       [F_SYSVMIN]             = REG_FIELD(0x03, 1, 3),
-       /* REG04 */
-       [F_PUMPX_EN]            = REG_FIELD(0x04, 7, 7),
-       [F_ICHG]                = REG_FIELD(0x04, 0, 6),
-       /* REG05 */
-       [F_IPRECHG]             = REG_FIELD(0x05, 4, 7),
-       [F_ITERM]               = REG_FIELD(0x05, 0, 3),
-       /* REG06 */
-       [F_VREG]                = REG_FIELD(0x06, 2, 7),
-       [F_BATLOWV]             = REG_FIELD(0x06, 1, 1),
-       [F_VRECHG]              = REG_FIELD(0x06, 0, 0),
-       /* REG07 */
-       [F_TERM_EN]             = REG_FIELD(0x07, 7, 7),
-       [F_STAT_DIS]            = REG_FIELD(0x07, 6, 6),
-       [F_WD]                  = REG_FIELD(0x07, 4, 5),
-       [F_TMR_EN]              = REG_FIELD(0x07, 3, 3),
-       [F_CHG_TMR]             = REG_FIELD(0x07, 1, 2),
-       [F_JEITA_ISET]          = REG_FIELD(0x07, 0, 0),
-       /* REG08 */
-       [F_BATCMP]              = REG_FIELD(0x08, 6, 7),
-       [F_VCLAMP]              = REG_FIELD(0x08, 2, 4),
-       [F_TREG]                = REG_FIELD(0x08, 0, 1),
-       /* REG09 */
-       [F_FORCE_ICO]           = REG_FIELD(0x09, 7, 7),
-       [F_TMR2X_EN]            = REG_FIELD(0x09, 6, 6),
-       [F_BATFET_DIS]          = REG_FIELD(0x09, 5, 5),
-       [F_JEITA_VSET]          = REG_FIELD(0x09, 4, 4),
-       [F_BATFET_DLY]          = REG_FIELD(0x09, 3, 3),
-       [F_BATFET_RST_EN]       = REG_FIELD(0x09, 2, 2),
-       [F_PUMPX_UP]            = REG_FIELD(0x09, 1, 1),
-       [F_PUMPX_DN]            = REG_FIELD(0x09, 0, 0),
-       /* REG0A */
-       [F_BOOSTV]              = REG_FIELD(0x0A, 4, 7),
-       [F_BOOSTI]              = REG_FIELD(0x0A, 0, 2),
-       /* REG0B */
-       [F_VBUS_STAT]           = REG_FIELD(0x0B, 5, 7),
-       [F_CHG_STAT]            = REG_FIELD(0x0B, 3, 4),
-       [F_PG_STAT]             = REG_FIELD(0x0B, 2, 2),
-       [F_SDP_STAT]            = REG_FIELD(0x0B, 1, 1),
-       [F_VSYS_STAT]           = REG_FIELD(0x0B, 0, 0),
-       /* REG0C */
-       [F_WD_FAULT]            = REG_FIELD(0x0C, 7, 7),
-       [F_BOOST_FAULT]         = REG_FIELD(0x0C, 6, 6),
-       [F_CHG_FAULT]           = REG_FIELD(0x0C, 4, 5),
-       [F_BAT_FAULT]           = REG_FIELD(0x0C, 3, 3),
-       [F_NTC_FAULT]           = REG_FIELD(0x0C, 0, 2),
-       /* REG0D */
-       [F_FORCE_VINDPM]        = REG_FIELD(0x0D, 7, 7),
-       [F_VINDPM]              = REG_FIELD(0x0D, 0, 6),
-       /* REG0E */
-       [F_THERM_STAT]          = REG_FIELD(0x0E, 7, 7),
-       [F_BATV]                = REG_FIELD(0x0E, 0, 6),
-       /* REG0F */
-       [F_SYSV]                = REG_FIELD(0x0F, 0, 6),
-       /* REG10 */
-       [F_TSPCT]               = REG_FIELD(0x10, 0, 6),
-       /* REG11 */
-       [F_VBUS_GD]             = REG_FIELD(0x11, 7, 7),
-       [F_VBUSV]               = REG_FIELD(0x11, 0, 6),
-       /* REG12 */
-       [F_ICHGR]               = REG_FIELD(0x12, 0, 6),
-       /* REG13 */
-       [F_VDPM_STAT]           = REG_FIELD(0x13, 7, 7),
-       [F_IDPM_STAT]           = REG_FIELD(0x13, 6, 6),
-       [F_IDPM_LIM]            = REG_FIELD(0x13, 0, 5),
-       /* REG14 */
-       [F_REG_RST]             = REG_FIELD(0x14, 7, 7),
-       [F_ICO_OPTIMIZED]       = REG_FIELD(0x14, 6, 6),
-       [F_PN]                  = REG_FIELD(0x14, 3, 5),
-       [F_TS_PROFILE]          = REG_FIELD(0x14, 2, 2),
-       [F_DEV_REV]             = REG_FIELD(0x14, 0, 1)
-};
-
-/*
- * Most of the val -> idx conversions can be computed, given the minimum,
- * maximum and the step between values. For the rest of conversions, we use
- * lookup tables.
- */
-enum bq25890_table_ids {
-       /* range tables */
-       TBL_ICHG,
-       TBL_ITERM,
-       TBL_IPRECHG,
-       TBL_VREG,
-       TBL_BATCMP,
-       TBL_VCLAMP,
-       TBL_BOOSTV,
-       TBL_SYSVMIN,
-
-       /* lookup tables */
-       TBL_TREG,
-       TBL_BOOSTI,
-};
-
-/* Thermal Regulation Threshold lookup table, in degrees Celsius */
-static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 };
-
-#define BQ25890_TREG_TBL_SIZE          ARRAY_SIZE(bq25890_treg_tbl)
-
-/* Boost mode current limit lookup table, in uA */
-static const u32 bq25890_boosti_tbl[] = {
-       500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000
-};
-
-#define BQ25890_BOOSTI_TBL_SIZE                ARRAY_SIZE(bq25890_boosti_tbl)
-
-struct bq25890_range {
-       u32 min;
-       u32 max;
-       u32 step;
-};
-
-struct bq25890_lookup {
-       const u32 *tbl;
-       u32 size;
-};
-
-static const union {
-       struct bq25890_range  rt;
-       struct bq25890_lookup lt;
-} bq25890_tables[] = {
-       /* range tables */
-       [TBL_ICHG] =    { .rt = {0,       5056000, 64000} },     /* uA */
-       [TBL_ITERM] =   { .rt = {64000,   1024000, 64000} },     /* uA */
-       [TBL_VREG] =    { .rt = {3840000, 4608000, 16000} },     /* uV */
-       [TBL_BATCMP] =  { .rt = {0,       140,     20} },        /* mOhm */
-       [TBL_VCLAMP] =  { .rt = {0,       224000,  32000} },     /* uV */
-       [TBL_BOOSTV] =  { .rt = {4550000, 5510000, 64000} },     /* uV */
-       [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} },    /* uV */
-
-       /* lookup tables */
-       [TBL_TREG] =    { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
-       [TBL_BOOSTI] =  { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }
-};
-
-static int bq25890_field_read(struct bq25890_device *bq,
-                             enum bq25890_fields field_id)
-{
-       int ret;
-       int val;
-
-       ret = regmap_field_read(bq->rmap_fields[field_id], &val);
-       if (ret < 0)
-               return ret;
-
-       return val;
-}
-
-static int bq25890_field_write(struct bq25890_device *bq,
-                              enum bq25890_fields field_id, u8 val)
-{
-       return regmap_field_write(bq->rmap_fields[field_id], val);
-}
-
-static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id)
-{
-       u8 idx;
-
-       if (id >= TBL_TREG) {
-               const u32 *tbl = bq25890_tables[id].lt.tbl;
-               u32 tbl_size = bq25890_tables[id].lt.size;
-
-               for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++)
-                       ;
-       } else {
-               const struct bq25890_range *rtbl = &bq25890_tables[id].rt;
-               u8 rtbl_size;
-
-               rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1;
-
-               for (idx = 1;
-                    idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value);
-                    idx++)
-                       ;
-       }
-
-       return idx - 1;
-}
-
-static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id)
-{
-       const struct bq25890_range *rtbl;
-
-       /* lookup table? */
-       if (id >= TBL_TREG)
-               return bq25890_tables[id].lt.tbl[idx];
-
-       /* range table */
-       rtbl = &bq25890_tables[id].rt;
-
-       return (rtbl->min + idx * rtbl->step);
-}
-
-enum bq25890_status {
-       STATUS_NOT_CHARGING,
-       STATUS_PRE_CHARGING,
-       STATUS_FAST_CHARGING,
-       STATUS_TERMINATION_DONE,
-};
-
-enum bq25890_chrg_fault {
-       CHRG_FAULT_NORMAL,
-       CHRG_FAULT_INPUT,
-       CHRG_FAULT_THERMAL_SHUTDOWN,
-       CHRG_FAULT_TIMER_EXPIRED,
-};
-
-static int bq25890_power_supply_get_property(struct power_supply *psy,
-                                            enum power_supply_property psp,
-                                            union power_supply_propval *val)
-{
-       int ret;
-       struct bq25890_device *bq = power_supply_get_drvdata(psy);
-       struct bq25890_state state;
-
-       mutex_lock(&bq->lock);
-       state = bq->state;
-       mutex_unlock(&bq->lock);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (!state.online)
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (state.chrg_status == STATUS_NOT_CHARGING)
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               else if (state.chrg_status == STATUS_PRE_CHARGING ||
-                        state.chrg_status == STATUS_FAST_CHARGING)
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else if (state.chrg_status == STATUS_TERMINATION_DONE)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-
-               break;
-
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = BQ25890_MANUFACTURER;
-               break;
-
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = state.online;
-               break;
-
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (!state.chrg_fault && !state.bat_fault && !state.boost_fault)
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               else if (state.bat_fault)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED)
-                       val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
-               else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */
-               if (ret < 0)
-                       return ret;
-
-               /* converted_val = ADC_val * 50mA (table 10.3.19) */
-               val->intval = ret * 50000;
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
-               val->intval = bq25890_tables[TBL_ICHG].rt.max;
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               if (!state.online) {
-                       val->intval = 0;
-                       break;
-               }
-
-               ret = bq25890_field_read(bq, F_BATV); /* read measured value */
-               if (ret < 0)
-                       return ret;
-
-               /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */
-               val->intval = 2304000 + ret * 20000;
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
-               val->intval = bq25890_tables[TBL_VREG].rt.max;
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
-               val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM);
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int bq25890_get_chip_state(struct bq25890_device *bq,
-                                 struct bq25890_state *state)
-{
-       int i, ret;
-
-       struct {
-               enum bq25890_fields id;
-               u8 *data;
-       } state_fields[] = {
-               {F_CHG_STAT,    &state->chrg_status},
-               {F_PG_STAT,     &state->online},
-               {F_VSYS_STAT,   &state->vsys_status},
-               {F_BOOST_FAULT, &state->boost_fault},
-               {F_BAT_FAULT,   &state->bat_fault},
-               {F_CHG_FAULT,   &state->chrg_fault}
-       };
-
-       for (i = 0; i < ARRAY_SIZE(state_fields); i++) {
-               ret = bq25890_field_read(bq, state_fields[i].id);
-               if (ret < 0)
-                       return ret;
-
-               *state_fields[i].data = ret;
-       }
-
-       dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT=%d/%d/%d\n",
-               state->chrg_status, state->online, state->vsys_status,
-               state->chrg_fault, state->boost_fault, state->bat_fault);
-
-       return 0;
-}
-
-static bool bq25890_state_changed(struct bq25890_device *bq,
-                                 struct bq25890_state *new_state)
-{
-       struct bq25890_state old_state;
-
-       mutex_lock(&bq->lock);
-       old_state = bq->state;
-       mutex_unlock(&bq->lock);
-
-       return (old_state.chrg_status != new_state->chrg_status ||
-               old_state.chrg_fault != new_state->chrg_fault   ||
-               old_state.online != new_state->online           ||
-               old_state.bat_fault != new_state->bat_fault     ||
-               old_state.boost_fault != new_state->boost_fault ||
-               old_state.vsys_status != new_state->vsys_status);
-}
-
-static void bq25890_handle_state_change(struct bq25890_device *bq,
-                                       struct bq25890_state *new_state)
-{
-       int ret;
-       struct bq25890_state old_state;
-
-       mutex_lock(&bq->lock);
-       old_state = bq->state;
-       mutex_unlock(&bq->lock);
-
-       if (!new_state->online) {                            /* power removed */
-               /* disable ADC */
-               ret = bq25890_field_write(bq, F_CONV_START, 0);
-               if (ret < 0)
-                       goto error;
-       } else if (!old_state.online) {                     /* power inserted */
-               /* enable ADC, to have control of charge current/voltage */
-               ret = bq25890_field_write(bq, F_CONV_START, 1);
-               if (ret < 0)
-                       goto error;
-       }
-
-       return;
-
-error:
-       dev_err(bq->dev, "Error communicating with the chip.\n");
-}
-
-static irqreturn_t bq25890_irq_handler_thread(int irq, void *private)
-{
-       struct bq25890_device *bq = private;
-       int ret;
-       struct bq25890_state state;
-
-       ret = bq25890_get_chip_state(bq, &state);
-       if (ret < 0)
-               goto handled;
-
-       if (!bq25890_state_changed(bq, &state))
-               goto handled;
-
-       bq25890_handle_state_change(bq, &state);
-
-       mutex_lock(&bq->lock);
-       bq->state = state;
-       mutex_unlock(&bq->lock);
-
-       power_supply_changed(bq->charger);
-
-handled:
-       return IRQ_HANDLED;
-}
-
-static int bq25890_chip_reset(struct bq25890_device *bq)
-{
-       int ret;
-       int rst_check_counter = 10;
-
-       ret = bq25890_field_write(bq, F_REG_RST, 1);
-       if (ret < 0)
-               return ret;
-
-       do {
-               ret = bq25890_field_read(bq, F_REG_RST);
-               if (ret < 0)
-                       return ret;
-
-               usleep_range(5, 10);
-       } while (ret == 1 && --rst_check_counter);
-
-       if (!rst_check_counter)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
-static int bq25890_hw_init(struct bq25890_device *bq)
-{
-       int ret;
-       int i;
-       struct bq25890_state state;
-
-       const struct {
-               enum bq25890_fields id;
-               u32 value;
-       } init_data[] = {
-               {F_ICHG,         bq->init_data.ichg},
-               {F_VREG,         bq->init_data.vreg},
-               {F_ITERM,        bq->init_data.iterm},
-               {F_IPRECHG,      bq->init_data.iprechg},
-               {F_SYSVMIN,      bq->init_data.sysvmin},
-               {F_BOOSTV,       bq->init_data.boostv},
-               {F_BOOSTI,       bq->init_data.boosti},
-               {F_BOOSTF,       bq->init_data.boostf},
-               {F_EN_ILIM,      bq->init_data.ilim_en},
-               {F_TREG,         bq->init_data.treg}
-       };
-
-       ret = bq25890_chip_reset(bq);
-       if (ret < 0)
-               return ret;
-
-       /* disable watchdog */
-       ret = bq25890_field_write(bq, F_WD, 0);
-       if (ret < 0)
-               return ret;
-
-       /* initialize currents/voltages and other parameters */
-       for (i = 0; i < ARRAY_SIZE(init_data); i++) {
-               ret = bq25890_field_write(bq, init_data[i].id,
-                                         init_data[i].value);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* Configure ADC for continuous conversions. This does not enable it. */
-       ret = bq25890_field_write(bq, F_CONV_RATE, 1);
-       if (ret < 0)
-               return ret;
-
-       ret = bq25890_get_chip_state(bq, &state);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&bq->lock);
-       bq->state = state;
-       mutex_unlock(&bq->lock);
-
-       return 0;
-}
-
-static enum power_supply_property bq25890_power_supply_props[] = {
-       POWER_SUPPLY_PROP_MANUFACTURER,
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
-};
-
-static char *bq25890_charger_supplied_to[] = {
-       "main-battery",
-};
-
-static const struct power_supply_desc bq25890_power_supply_desc = {
-       .name = "bq25890-charger",
-       .type = POWER_SUPPLY_TYPE_USB,
-       .properties = bq25890_power_supply_props,
-       .num_properties = ARRAY_SIZE(bq25890_power_supply_props),
-       .get_property = bq25890_power_supply_get_property,
-};
-
-static int bq25890_power_supply_init(struct bq25890_device *bq)
-{
-       struct power_supply_config psy_cfg = { .drv_data = bq, };
-
-       psy_cfg.supplied_to = bq25890_charger_supplied_to;
-       psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to);
-
-       bq->charger = power_supply_register(bq->dev, &bq25890_power_supply_desc,
-                                           &psy_cfg);
-
-       return PTR_ERR_OR_ZERO(bq->charger);
-}
-
-static void bq25890_usb_work(struct work_struct *data)
-{
-       int ret;
-       struct bq25890_device *bq =
-                       container_of(data, struct bq25890_device, usb_work);
-
-       switch (bq->usb_event) {
-       case USB_EVENT_ID:
-               /* Enable boost mode */
-               ret = bq25890_field_write(bq, F_OTG_CFG, 1);
-               if (ret < 0)
-                       goto error;
-               break;
-
-       case USB_EVENT_NONE:
-               /* Disable boost mode */
-               ret = bq25890_field_write(bq, F_OTG_CFG, 0);
-               if (ret < 0)
-                       goto error;
-
-               power_supply_changed(bq->charger);
-               break;
-       }
-
-       return;
-
-error:
-       dev_err(bq->dev, "Error switching to boost/charger mode.\n");
-}
-
-static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
-                               void *priv)
-{
-       struct bq25890_device *bq =
-                       container_of(nb, struct bq25890_device, usb_nb);
-
-       bq->usb_event = val;
-       queue_work(system_power_efficient_wq, &bq->usb_work);
-
-       return NOTIFY_OK;
-}
-
-static int bq25890_irq_probe(struct bq25890_device *bq)
-{
-       struct gpio_desc *irq;
-
-       irq = devm_gpiod_get_index(bq->dev, BQ25890_IRQ_PIN, 0, GPIOD_IN);
-       if (IS_ERR(irq)) {
-               dev_err(bq->dev, "Could not probe irq pin.\n");
-               return PTR_ERR(irq);
-       }
-
-       return gpiod_to_irq(irq);
-}
-
-static int bq25890_fw_read_u32_props(struct bq25890_device *bq)
-{
-       int ret;
-       u32 property;
-       int i;
-       struct bq25890_init_data *init = &bq->init_data;
-       struct {
-               char *name;
-               bool optional;
-               enum bq25890_table_ids tbl_id;
-               u8 *conv_data; /* holds converted value from given property */
-       } props[] = {
-               /* required properties */
-               {"ti,charge-current", false, TBL_ICHG, &init->ichg},
-               {"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg},
-               {"ti,termination-current", false, TBL_ITERM, &init->iterm},
-               {"ti,precharge-current", false, TBL_ITERM, &init->iprechg},
-               {"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin},
-               {"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv},
-               {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti},
-
-               /* optional properties */
-               {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg}
-       };
-
-       /* initialize data for optional properties */
-       init->treg = 3; /* 120 degrees Celsius */
-
-       for (i = 0; i < ARRAY_SIZE(props); i++) {
-               ret = device_property_read_u32(bq->dev, props[i].name,
-                                              &property);
-               if (ret < 0) {
-                       if (props[i].optional)
-                               continue;
-
-                       return ret;
-               }
-
-               *props[i].conv_data = bq25890_find_idx(property,
-                                                      props[i].tbl_id);
-       }
-
-       return 0;
-}
-
-static int bq25890_fw_probe(struct bq25890_device *bq)
-{
-       int ret;
-       struct bq25890_init_data *init = &bq->init_data;
-
-       ret = bq25890_fw_read_u32_props(bq);
-       if (ret < 0)
-               return ret;
-
-       init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin");
-       init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq");
-
-       return 0;
-}
-
-static int bq25890_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id)
-{
-       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       struct device *dev = &client->dev;
-       struct bq25890_device *bq;
-       int ret;
-       int i;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
-               return -ENODEV;
-       }
-
-       bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
-       if (!bq)
-               return -ENOMEM;
-
-       bq->client = client;
-       bq->dev = dev;
-
-       mutex_init(&bq->lock);
-
-       bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config);
-       if (IS_ERR(bq->rmap)) {
-               dev_err(dev, "failed to allocate register map\n");
-               return PTR_ERR(bq->rmap);
-       }
-
-       for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) {
-               const struct reg_field *reg_fields = bq25890_reg_fields;
-
-               bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
-                                                            reg_fields[i]);
-               if (IS_ERR(bq->rmap_fields[i])) {
-                       dev_err(dev, "cannot allocate regmap field\n");
-                       return PTR_ERR(bq->rmap_fields[i]);
-               }
-       }
-
-       i2c_set_clientdata(client, bq);
-
-       bq->chip_id = bq25890_field_read(bq, F_PN);
-       if (bq->chip_id < 0) {
-               dev_err(dev, "Cannot read chip ID.\n");
-               return bq->chip_id;
-       }
-
-       if (bq->chip_id != BQ25890_ID) {
-               dev_err(dev, "Chip with ID=%d, not supported!\n", bq->chip_id);
-               return -ENODEV;
-       }
-
-       if (!dev->platform_data) {
-               ret = bq25890_fw_probe(bq);
-               if (ret < 0) {
-                       dev_err(dev, "Cannot read device properties.\n");
-                       return ret;
-               }
-       } else {
-               return -ENODEV;
-       }
-
-       ret = bq25890_hw_init(bq);
-       if (ret < 0) {
-               dev_err(dev, "Cannot initialize the chip.\n");
-               return ret;
-       }
-
-       if (client->irq <= 0)
-               client->irq = bq25890_irq_probe(bq);
-
-       if (client->irq < 0) {
-               dev_err(dev, "No irq resource found.\n");
-               return client->irq;
-       }
-
-       /* OTG reporting */
-       bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
-       if (!IS_ERR_OR_NULL(bq->usb_phy)) {
-               INIT_WORK(&bq->usb_work, bq25890_usb_work);
-               bq->usb_nb.notifier_call = bq25890_usb_notifier;
-               usb_register_notifier(bq->usb_phy, &bq->usb_nb);
-       }
-
-       ret = devm_request_threaded_irq(dev, client->irq, NULL,
-                                       bq25890_irq_handler_thread,
-                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                       BQ25890_IRQ_PIN, bq);
-       if (ret)
-               goto irq_fail;
-
-       ret = bq25890_power_supply_init(bq);
-       if (ret < 0) {
-               dev_err(dev, "Failed to register power supply\n");
-               goto irq_fail;
-       }
-
-       return 0;
-
-irq_fail:
-       if (!IS_ERR_OR_NULL(bq->usb_phy))
-               usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
-
-       return ret;
-}
-
-static int bq25890_remove(struct i2c_client *client)
-{
-       struct bq25890_device *bq = i2c_get_clientdata(client);
-
-       power_supply_unregister(bq->charger);
-
-       if (!IS_ERR_OR_NULL(bq->usb_phy))
-               usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
-
-       /* reset all registers to default values */
-       bq25890_chip_reset(bq);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int bq25890_suspend(struct device *dev)
-{
-       struct bq25890_device *bq = dev_get_drvdata(dev);
-
-       /*
-        * If charger is removed, while in suspend, make sure ADC is diabled
-        * since it consumes slightly more power.
-        */
-       return bq25890_field_write(bq, F_CONV_START, 0);
-}
-
-static int bq25890_resume(struct device *dev)
-{
-       int ret;
-       struct bq25890_state state;
-       struct bq25890_device *bq = dev_get_drvdata(dev);
-
-       ret = bq25890_get_chip_state(bq, &state);
-       if (ret < 0)
-               return ret;
-
-       mutex_lock(&bq->lock);
-       bq->state = state;
-       mutex_unlock(&bq->lock);
-
-       /* Re-enable ADC only if charger is plugged in. */
-       if (state.online) {
-               ret = bq25890_field_write(bq, F_CONV_START, 1);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* signal userspace, maybe state changed while suspended */
-       power_supply_changed(bq->charger);
-
-       return 0;
-}
-#endif
-
-static const struct dev_pm_ops bq25890_pm = {
-       SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume)
-};
-
-static const struct i2c_device_id bq25890_i2c_ids[] = {
-       { "bq25890", 0 },
-       {},
-};
-MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids);
-
-static const struct of_device_id bq25890_of_match[] = {
-       { .compatible = "ti,bq25890", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, bq25890_of_match);
-
-static const struct acpi_device_id bq25890_acpi_match[] = {
-       {"BQ258900", 0},
-       {},
-};
-MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match);
-
-static struct i2c_driver bq25890_driver = {
-       .driver = {
-               .name = "bq25890-charger",
-               .of_match_table = of_match_ptr(bq25890_of_match),
-               .acpi_match_table = ACPI_PTR(bq25890_acpi_match),
-               .pm = &bq25890_pm,
-       },
-       .probe = bq25890_probe,
-       .remove = bq25890_remove,
-       .id_table = bq25890_i2c_ids,
-};
-module_i2c_driver(bq25890_driver);
-
-MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
-MODULE_DESCRIPTION("bq25890 charger driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq27xxx_battery.c b/drivers/power/bq27xxx_battery.c
deleted file mode 100644 (file)
index 323d05a..0000000
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * BQ27xxx battery driver
- *
- * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
- * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
- * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de>
- * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com>
- *
- * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
- *
- * This package is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Datasheets:
- * http://www.ti.com/product/bq27000
- * http://www.ti.com/product/bq27200
- * http://www.ti.com/product/bq27010
- * http://www.ti.com/product/bq27210
- * http://www.ti.com/product/bq27500
- * http://www.ti.com/product/bq27510-g3
- * http://www.ti.com/product/bq27520-g4
- * http://www.ti.com/product/bq27530-g1
- * http://www.ti.com/product/bq27531-g1
- * http://www.ti.com/product/bq27541-g1
- * http://www.ti.com/product/bq27542-g1
- * http://www.ti.com/product/bq27546-g1
- * http://www.ti.com/product/bq27742-g1
- * http://www.ti.com/product/bq27545-g1
- * http://www.ti.com/product/bq27421-g1
- * http://www.ti.com/product/bq27425-g1
- * http://www.ti.com/product/bq27411-g1
- * http://www.ti.com/product/bq27621-g1
- */
-
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/param.h>
-#include <linux/jiffies.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-
-#include <linux/power/bq27xxx_battery.h>
-
-#define DRIVER_VERSION         "1.2.0"
-
-#define BQ27XXX_MANUFACTURER   "Texas Instruments"
-
-/* BQ27XXX Flags */
-#define BQ27XXX_FLAG_DSC       BIT(0)
-#define BQ27XXX_FLAG_SOCF      BIT(1) /* State-of-Charge threshold final */
-#define BQ27XXX_FLAG_SOC1      BIT(2) /* State-of-Charge threshold 1 */
-#define BQ27XXX_FLAG_FC                BIT(9)
-#define BQ27XXX_FLAG_OTD       BIT(14)
-#define BQ27XXX_FLAG_OTC       BIT(15)
-#define BQ27XXX_FLAG_UT                BIT(14)
-#define BQ27XXX_FLAG_OT                BIT(15)
-
-/* BQ27000 has different layout for Flags register */
-#define BQ27000_FLAG_EDVF      BIT(0) /* Final End-of-Discharge-Voltage flag */
-#define BQ27000_FLAG_EDV1      BIT(1) /* First End-of-Discharge-Voltage flag */
-#define BQ27000_FLAG_CI                BIT(4) /* Capacity Inaccurate flag */
-#define BQ27000_FLAG_FC                BIT(5)
-#define BQ27000_FLAG_CHGS      BIT(7) /* Charge state flag */
-
-#define BQ27XXX_RS                     (20) /* Resistor sense mOhm */
-#define BQ27XXX_POWER_CONSTANT         (29200) /* 29.2 ÂµV^2 * 1000 */
-#define BQ27XXX_CURRENT_CONSTANT       (3570) /* 3.57 ÂµV * 1000 */
-
-#define INVALID_REG_ADDR       0xff
-
-/*
- * bq27xxx_reg_index - Register names
- *
- * These are indexes into a device's register mapping array.
- */
-
-enum bq27xxx_reg_index {
-       BQ27XXX_REG_CTRL = 0,   /* Control */
-       BQ27XXX_REG_TEMP,       /* Temperature */
-       BQ27XXX_REG_INT_TEMP,   /* Internal Temperature */
-       BQ27XXX_REG_VOLT,       /* Voltage */
-       BQ27XXX_REG_AI,         /* Average Current */
-       BQ27XXX_REG_FLAGS,      /* Flags */
-       BQ27XXX_REG_TTE,        /* Time-to-Empty */
-       BQ27XXX_REG_TTF,        /* Time-to-Full */
-       BQ27XXX_REG_TTES,       /* Time-to-Empty Standby */
-       BQ27XXX_REG_TTECP,      /* Time-to-Empty at Constant Power */
-       BQ27XXX_REG_NAC,        /* Nominal Available Capacity */
-       BQ27XXX_REG_FCC,        /* Full Charge Capacity */
-       BQ27XXX_REG_CYCT,       /* Cycle Count */
-       BQ27XXX_REG_AE,         /* Available Energy */
-       BQ27XXX_REG_SOC,        /* State-of-Charge */
-       BQ27XXX_REG_DCAP,       /* Design Capacity */
-       BQ27XXX_REG_AP,         /* Average Power */
-       BQ27XXX_REG_MAX,        /* sentinel */
-};
-
-/* Register mappings */
-static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
-       [BQ27000] = {
-               [BQ27XXX_REG_CTRL] = 0x00,
-               [BQ27XXX_REG_TEMP] = 0x06,
-               [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_VOLT] = 0x08,
-               [BQ27XXX_REG_AI] = 0x14,
-               [BQ27XXX_REG_FLAGS] = 0x0a,
-               [BQ27XXX_REG_TTE] = 0x16,
-               [BQ27XXX_REG_TTF] = 0x18,
-               [BQ27XXX_REG_TTES] = 0x1c,
-               [BQ27XXX_REG_TTECP] = 0x26,
-               [BQ27XXX_REG_NAC] = 0x0c,
-               [BQ27XXX_REG_FCC] = 0x12,
-               [BQ27XXX_REG_CYCT] = 0x2a,
-               [BQ27XXX_REG_AE] = 0x22,
-               [BQ27XXX_REG_SOC] = 0x0b,
-               [BQ27XXX_REG_DCAP] = 0x76,
-               [BQ27XXX_REG_AP] = 0x24,
-       },
-       [BQ27010] = {
-               [BQ27XXX_REG_CTRL] = 0x00,
-               [BQ27XXX_REG_TEMP] = 0x06,
-               [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_VOLT] = 0x08,
-               [BQ27XXX_REG_AI] = 0x14,
-               [BQ27XXX_REG_FLAGS] = 0x0a,
-               [BQ27XXX_REG_TTE] = 0x16,
-               [BQ27XXX_REG_TTF] = 0x18,
-               [BQ27XXX_REG_TTES] = 0x1c,
-               [BQ27XXX_REG_TTECP] = 0x26,
-               [BQ27XXX_REG_NAC] = 0x0c,
-               [BQ27XXX_REG_FCC] = 0x12,
-               [BQ27XXX_REG_CYCT] = 0x2a,
-               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_SOC] = 0x0b,
-               [BQ27XXX_REG_DCAP] = 0x76,
-               [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
-       },
-       [BQ27500] = {
-               [BQ27XXX_REG_CTRL] = 0x00,
-               [BQ27XXX_REG_TEMP] = 0x06,
-               [BQ27XXX_REG_INT_TEMP] = 0x28,
-               [BQ27XXX_REG_VOLT] = 0x08,
-               [BQ27XXX_REG_AI] = 0x14,
-               [BQ27XXX_REG_FLAGS] = 0x0a,
-               [BQ27XXX_REG_TTE] = 0x16,
-               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTES] = 0x1a,
-               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_NAC] = 0x0c,
-               [BQ27XXX_REG_FCC] = 0x12,
-               [BQ27XXX_REG_CYCT] = 0x2a,
-               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_SOC] = 0x2c,
-               [BQ27XXX_REG_DCAP] = 0x3c,
-               [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
-       },
-       [BQ27530] = {
-               [BQ27XXX_REG_CTRL] = 0x00,
-               [BQ27XXX_REG_TEMP] = 0x06,
-               [BQ27XXX_REG_INT_TEMP] = 0x32,
-               [BQ27XXX_REG_VOLT] = 0x08,
-               [BQ27XXX_REG_AI] = 0x14,
-               [BQ27XXX_REG_FLAGS] = 0x0a,
-               [BQ27XXX_REG_TTE] = 0x16,
-               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_NAC] = 0x0c,
-               [BQ27XXX_REG_FCC] = 0x12,
-               [BQ27XXX_REG_CYCT] = 0x2a,
-               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_SOC] = 0x2c,
-               [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_AP] = 0x24,
-       },
-       [BQ27541] = {
-               [BQ27XXX_REG_CTRL] = 0x00,
-               [BQ27XXX_REG_TEMP] = 0x06,
-               [BQ27XXX_REG_INT_TEMP] = 0x28,
-               [BQ27XXX_REG_VOLT] = 0x08,
-               [BQ27XXX_REG_AI] = 0x14,
-               [BQ27XXX_REG_FLAGS] = 0x0a,
-               [BQ27XXX_REG_TTE] = 0x16,
-               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_NAC] = 0x0c,
-               [BQ27XXX_REG_FCC] = 0x12,
-               [BQ27XXX_REG_CYCT] = 0x2a,
-               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_SOC] = 0x2c,
-               [BQ27XXX_REG_DCAP] = 0x3c,
-               [BQ27XXX_REG_AP] = 0x24,
-       },
-       [BQ27545] = {
-               [BQ27XXX_REG_CTRL] = 0x00,
-               [BQ27XXX_REG_TEMP] = 0x06,
-               [BQ27XXX_REG_INT_TEMP] = 0x28,
-               [BQ27XXX_REG_VOLT] = 0x08,
-               [BQ27XXX_REG_AI] = 0x14,
-               [BQ27XXX_REG_FLAGS] = 0x0a,
-               [BQ27XXX_REG_TTE] = 0x16,
-               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_NAC] = 0x0c,
-               [BQ27XXX_REG_FCC] = 0x12,
-               [BQ27XXX_REG_CYCT] = 0x2a,
-               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_SOC] = 0x2c,
-               [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_AP] = 0x24,
-       },
-       [BQ27421] = {
-               [BQ27XXX_REG_CTRL] = 0x00,
-               [BQ27XXX_REG_TEMP] = 0x02,
-               [BQ27XXX_REG_INT_TEMP] = 0x1e,
-               [BQ27XXX_REG_VOLT] = 0x04,
-               [BQ27XXX_REG_AI] = 0x10,
-               [BQ27XXX_REG_FLAGS] = 0x06,
-               [BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_NAC] = 0x08,
-               [BQ27XXX_REG_FCC] = 0x0e,
-               [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
-               [BQ27XXX_REG_SOC] = 0x1c,
-               [BQ27XXX_REG_DCAP] = 0x3c,
-               [BQ27XXX_REG_AP] = 0x18,
-       },
-};
-
-static enum power_supply_property bq27000_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
-       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_ENERGY_NOW,
-       POWER_SUPPLY_PROP_POWER_AVG,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27010_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
-       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27500_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27530_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_POWER_AVG,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27541_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_POWER_AVG,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27545_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_POWER_AVG,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27421_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-#define BQ27XXX_PROP(_id, _prop)               \
-       [_id] = {                               \
-               .props = _prop,                 \
-               .size = ARRAY_SIZE(_prop),      \
-       }
-
-static struct {
-       enum power_supply_property *props;
-       size_t size;
-} bq27xxx_battery_props[] = {
-       BQ27XXX_PROP(BQ27000, bq27000_battery_props),
-       BQ27XXX_PROP(BQ27010, bq27010_battery_props),
-       BQ27XXX_PROP(BQ27500, bq27500_battery_props),
-       BQ27XXX_PROP(BQ27530, bq27530_battery_props),
-       BQ27XXX_PROP(BQ27541, bq27541_battery_props),
-       BQ27XXX_PROP(BQ27545, bq27545_battery_props),
-       BQ27XXX_PROP(BQ27421, bq27421_battery_props),
-};
-
-static unsigned int poll_interval = 360;
-module_param(poll_interval, uint, 0644);
-MODULE_PARM_DESC(poll_interval,
-                "battery poll interval in seconds - 0 disables polling");
-
-/*
- * Common code for BQ27xxx devices
- */
-
-static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
-                              bool single)
-{
-       /* Reports EINVAL for invalid/missing registers */
-       if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
-               return -EINVAL;
-
-       return di->bus.read(di, di->regs[reg_index], single);
-}
-
-/*
- * Return the battery State-of-Charge
- * Or < 0 if something fails.
- */
-static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di)
-{
-       int soc;
-
-       if (di->chip == BQ27000 || di->chip == BQ27010)
-               soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true);
-       else
-               soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false);
-
-       if (soc < 0)
-               dev_dbg(di->dev, "error reading State-of-Charge\n");
-
-       return soc;
-}
-
-/*
- * Return a battery charge value in ÂµAh
- * Or < 0 if something fails.
- */
-static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
-{
-       int charge;
-
-       charge = bq27xxx_read(di, reg, false);
-       if (charge < 0) {
-               dev_dbg(di->dev, "error reading charge register %02x: %d\n",
-                       reg, charge);
-               return charge;
-       }
-
-       if (di->chip == BQ27000 || di->chip == BQ27010)
-               charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
-       else
-               charge *= 1000;
-
-       return charge;
-}
-
-/*
- * Return the battery Nominal available capacity in ÂµAh
- * Or < 0 if something fails.
- */
-static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di)
-{
-       int flags;
-
-       if (di->chip == BQ27000 || di->chip == BQ27010) {
-               flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
-               if (flags >= 0 && (flags & BQ27000_FLAG_CI))
-                       return -ENODATA;
-       }
-
-       return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC);
-}
-
-/*
- * Return the battery Full Charge Capacity in ÂµAh
- * Or < 0 if something fails.
- */
-static inline int bq27xxx_battery_read_fcc(struct bq27xxx_device_info *di)
-{
-       return bq27xxx_battery_read_charge(di, BQ27XXX_REG_FCC);
-}
-
-/*
- * Return the Design Capacity in ÂµAh
- * Or < 0 if something fails.
- */
-static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di)
-{
-       int dcap;
-
-       if (di->chip == BQ27000 || di->chip == BQ27010)
-               dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true);
-       else
-               dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false);
-
-       if (dcap < 0) {
-               dev_dbg(di->dev, "error reading initial last measured discharge\n");
-               return dcap;
-       }
-
-       if (di->chip == BQ27000 || di->chip == BQ27010)
-               dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
-       else
-               dcap *= 1000;
-
-       return dcap;
-}
-
-/*
- * Return the battery Available energy in ÂµWh
- * Or < 0 if something fails.
- */
-static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di)
-{
-       int ae;
-
-       ae = bq27xxx_read(di, BQ27XXX_REG_AE, false);
-       if (ae < 0) {
-               dev_dbg(di->dev, "error reading available energy\n");
-               return ae;
-       }
-
-       if (di->chip == BQ27000 || di->chip == BQ27010)
-               ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS;
-       else
-               ae *= 1000;
-
-       return ae;
-}
-
-/*
- * Return the battery temperature in tenths of degree Kelvin
- * Or < 0 if something fails.
- */
-static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di)
-{
-       int temp;
-
-       temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false);
-       if (temp < 0) {
-               dev_err(di->dev, "error reading temperature\n");
-               return temp;
-       }
-
-       if (di->chip == BQ27000 || di->chip == BQ27010)
-               temp = 5 * temp / 2;
-
-       return temp;
-}
-
-/*
- * Return the battery Cycle count total
- * Or < 0 if something fails.
- */
-static int bq27xxx_battery_read_cyct(struct bq27xxx_device_info *di)
-{
-       int cyct;
-
-       cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false);
-       if (cyct < 0)
-               dev_err(di->dev, "error reading cycle count total\n");
-
-       return cyct;
-}
-
-/*
- * Read a time register.
- * Return < 0 if something fails.
- */
-static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg)
-{
-       int tval;
-
-       tval = bq27xxx_read(di, reg, false);
-       if (tval < 0) {
-               dev_dbg(di->dev, "error reading time register %02x: %d\n",
-                       reg, tval);
-               return tval;
-       }
-
-       if (tval == 65535)
-               return -ENODATA;
-
-       return tval * 60;
-}
-
-/*
- * Read an average power register.
- * Return < 0 if something fails.
- */
-static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)
-{
-       int tval;
-
-       tval = bq27xxx_read(di, BQ27XXX_REG_AP, false);
-       if (tval < 0) {
-               dev_err(di->dev, "error reading average power register  %02x: %d\n",
-                       BQ27XXX_REG_AP, tval);
-               return tval;
-       }
-
-       if (di->chip == BQ27000 || di->chip == BQ27010)
-               return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS;
-       else
-               return tval;
-}
-
-/*
- * Returns true if a battery over temperature condition is detected
- */
-static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
-{
-       if (di->chip == BQ27500 || di->chip == BQ27541 || di->chip == BQ27545)
-               return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
-       if (di->chip == BQ27530 || di->chip == BQ27421)
-               return flags & BQ27XXX_FLAG_OT;
-
-       return false;
-}
-
-/*
- * Returns true if a battery under temperature condition is detected
- */
-static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags)
-{
-       if (di->chip == BQ27530 || di->chip == BQ27421)
-               return flags & BQ27XXX_FLAG_UT;
-
-       return false;
-}
-
-/*
- * Returns true if a low state of charge condition is detected
- */
-static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
-{
-       if (di->chip == BQ27000 || di->chip == BQ27010)
-               return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF);
-       else
-               return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
-}
-
-/*
- * Read flag register.
- * Return < 0 if something fails.
- */
-static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
-{
-       int flags;
-
-       flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
-       if (flags < 0) {
-               dev_err(di->dev, "error reading flag register:%d\n", flags);
-               return flags;
-       }
-
-       /* Unlikely but important to return first */
-       if (unlikely(bq27xxx_battery_overtemp(di, flags)))
-               return POWER_SUPPLY_HEALTH_OVERHEAT;
-       if (unlikely(bq27xxx_battery_undertemp(di, flags)))
-               return POWER_SUPPLY_HEALTH_COLD;
-       if (unlikely(bq27xxx_battery_dead(di, flags)))
-               return POWER_SUPPLY_HEALTH_DEAD;
-
-       return POWER_SUPPLY_HEALTH_GOOD;
-}
-
-void bq27xxx_battery_update(struct bq27xxx_device_info *di)
-{
-       struct bq27xxx_reg_cache cache = {0, };
-       bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010;
-       bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010;
-
-       cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
-       if ((cache.flags & 0xff) == 0xff)
-               cache.flags = -1; /* read error */
-       if (cache.flags >= 0) {
-               cache.temperature = bq27xxx_battery_read_temperature(di);
-               if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) {
-                       dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n");
-                       cache.capacity = -ENODATA;
-                       cache.energy = -ENODATA;
-                       cache.time_to_empty = -ENODATA;
-                       cache.time_to_empty_avg = -ENODATA;
-                       cache.time_to_full = -ENODATA;
-                       cache.charge_full = -ENODATA;
-                       cache.health = -ENODATA;
-               } else {
-                       if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
-                               cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
-                       if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
-                               cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
-                       if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
-                               cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
-                       cache.charge_full = bq27xxx_battery_read_fcc(di);
-                       cache.capacity = bq27xxx_battery_read_soc(di);
-                       if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
-                               cache.energy = bq27xxx_battery_read_energy(di);
-                       cache.health = bq27xxx_battery_read_health(di);
-               }
-               if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
-                       cache.cycle_count = bq27xxx_battery_read_cyct(di);
-               if (di->regs[BQ27XXX_REG_AP] != INVALID_REG_ADDR)
-                       cache.power_avg = bq27xxx_battery_read_pwr_avg(di);
-
-               /* We only have to read charge design full once */
-               if (di->charge_design_full <= 0)
-                       di->charge_design_full = bq27xxx_battery_read_dcap(di);
-       }
-
-       if (di->cache.capacity != cache.capacity)
-               power_supply_changed(di->bat);
-
-       if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
-               di->cache = cache;
-
-       di->last_update = jiffies;
-}
-EXPORT_SYMBOL_GPL(bq27xxx_battery_update);
-
-static void bq27xxx_battery_poll(struct work_struct *work)
-{
-       struct bq27xxx_device_info *di =
-                       container_of(work, struct bq27xxx_device_info,
-                                    work.work);
-
-       bq27xxx_battery_update(di);
-
-       if (poll_interval > 0)
-               schedule_delayed_work(&di->work, poll_interval * HZ);
-}
-
-/*
- * Return the battery average current in ÂµA
- * Note that current can be negative signed as well
- * Or 0 if something fails.
- */
-static int bq27xxx_battery_current(struct bq27xxx_device_info *di,
-                                  union power_supply_propval *val)
-{
-       int curr;
-       int flags;
-
-       curr = bq27xxx_read(di, BQ27XXX_REG_AI, false);
-       if (curr < 0) {
-               dev_err(di->dev, "error reading current\n");
-               return curr;
-       }
-
-       if (di->chip == BQ27000 || di->chip == BQ27010) {
-               flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
-               if (flags & BQ27000_FLAG_CHGS) {
-                       dev_dbg(di->dev, "negative current!\n");
-                       curr = -curr;
-               }
-
-               val->intval = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
-       } else {
-               /* Other gauges return signed value */
-               val->intval = (int)((s16)curr) * 1000;
-       }
-
-       return 0;
-}
-
-static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
-                                 union power_supply_propval *val)
-{
-       int status;
-
-       if (di->chip == BQ27000 || di->chip == BQ27010) {
-               if (di->cache.flags & BQ27000_FLAG_FC)
-                       status = POWER_SUPPLY_STATUS_FULL;
-               else if (di->cache.flags & BQ27000_FLAG_CHGS)
-                       status = POWER_SUPPLY_STATUS_CHARGING;
-               else if (power_supply_am_i_supplied(di->bat))
-                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               else
-                       status = POWER_SUPPLY_STATUS_DISCHARGING;
-       } else {
-               if (di->cache.flags & BQ27XXX_FLAG_FC)
-                       status = POWER_SUPPLY_STATUS_FULL;
-               else if (di->cache.flags & BQ27XXX_FLAG_DSC)
-                       status = POWER_SUPPLY_STATUS_DISCHARGING;
-               else
-                       status = POWER_SUPPLY_STATUS_CHARGING;
-       }
-
-       val->intval = status;
-
-       return 0;
-}
-
-static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di,
-                                         union power_supply_propval *val)
-{
-       int level;
-
-       if (di->chip == BQ27000 || di->chip == BQ27010) {
-               if (di->cache.flags & BQ27000_FLAG_FC)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-               else if (di->cache.flags & BQ27000_FLAG_EDV1)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-               else if (di->cache.flags & BQ27000_FLAG_EDVF)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-               else
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-       } else {
-               if (di->cache.flags & BQ27XXX_FLAG_FC)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-               else if (di->cache.flags & BQ27XXX_FLAG_SOC1)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-               else if (di->cache.flags & BQ27XXX_FLAG_SOCF)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-               else
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-       }
-
-       val->intval = level;
-
-       return 0;
-}
-
-/*
- * Return the battery Voltage in millivolts
- * Or < 0 if something fails.
- */
-static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di,
-                                  union power_supply_propval *val)
-{
-       int volt;
-
-       volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false);
-       if (volt < 0) {
-               dev_err(di->dev, "error reading voltage\n");
-               return volt;
-       }
-
-       val->intval = volt * 1000;
-
-       return 0;
-}
-
-static int bq27xxx_simple_value(int value,
-                               union power_supply_propval *val)
-{
-       if (value < 0)
-               return value;
-
-       val->intval = value;
-
-       return 0;
-}
-
-static int bq27xxx_battery_get_property(struct power_supply *psy,
-                                       enum power_supply_property psp,
-                                       union power_supply_propval *val)
-{
-       int ret = 0;
-       struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);
-
-       mutex_lock(&di->lock);
-       if (time_is_before_jiffies(di->last_update + 5 * HZ)) {
-               cancel_delayed_work_sync(&di->work);
-               bq27xxx_battery_poll(&di->work.work);
-       }
-       mutex_unlock(&di->lock);
-
-       if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0)
-               return -ENODEV;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = bq27xxx_battery_status(di, val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = bq27xxx_battery_voltage(di, val);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = di->cache.flags < 0 ? 0 : 1;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = bq27xxx_battery_current(di, val);
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = bq27xxx_simple_value(di->cache.capacity, val);
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-               ret = bq27xxx_battery_capacity_level(di, val);
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = bq27xxx_simple_value(di->cache.temperature, val);
-               if (ret == 0)
-                       val->intval -= 2731; /* convert decidegree k to c */
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
-               ret = bq27xxx_simple_value(di->cache.time_to_empty, val);
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
-               ret = bq27xxx_simple_value(di->cache.time_to_empty_avg, val);
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
-               ret = bq27xxx_simple_value(di->cache.time_to_full, val);
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               ret = bq27xxx_simple_value(bq27xxx_battery_read_nac(di), val);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               ret = bq27xxx_simple_value(di->cache.charge_full, val);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               ret = bq27xxx_simple_value(di->charge_design_full, val);
-               break;
-       case POWER_SUPPLY_PROP_CYCLE_COUNT:
-               ret = bq27xxx_simple_value(di->cache.cycle_count, val);
-               break;
-       case POWER_SUPPLY_PROP_ENERGY_NOW:
-               ret = bq27xxx_simple_value(di->cache.energy, val);
-               break;
-       case POWER_SUPPLY_PROP_POWER_AVG:
-               ret = bq27xxx_simple_value(di->cache.power_avg, val);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = bq27xxx_simple_value(di->cache.health, val);
-               break;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = BQ27XXX_MANUFACTURER;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return ret;
-}
-
-static void bq27xxx_external_power_changed(struct power_supply *psy)
-{
-       struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);
-
-       cancel_delayed_work_sync(&di->work);
-       schedule_delayed_work(&di->work, 0);
-}
-
-int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
-{
-       struct power_supply_desc *psy_desc;
-       struct power_supply_config psy_cfg = { .drv_data = di, };
-
-       INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
-       mutex_init(&di->lock);
-       di->regs = bq27xxx_regs[di->chip];
-
-       psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL);
-       if (!psy_desc)
-               return -ENOMEM;
-
-       psy_desc->name = di->name;
-       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
-       psy_desc->properties = bq27xxx_battery_props[di->chip].props;
-       psy_desc->num_properties = bq27xxx_battery_props[di->chip].size;
-       psy_desc->get_property = bq27xxx_battery_get_property;
-       psy_desc->external_power_changed = bq27xxx_external_power_changed;
-
-       di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg);
-       if (IS_ERR(di->bat)) {
-               dev_err(di->dev, "failed to register battery\n");
-               return PTR_ERR(di->bat);
-       }
-
-       dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
-
-       bq27xxx_battery_update(di);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(bq27xxx_battery_setup);
-
-void bq27xxx_battery_teardown(struct bq27xxx_device_info *di)
-{
-       /*
-        * power_supply_unregister call bq27xxx_battery_get_property which
-        * call bq27xxx_battery_poll.
-        * Make sure that bq27xxx_battery_poll will not call
-        * schedule_delayed_work again after unregister (which cause OOPS).
-        */
-       poll_interval = 0;
-
-       cancel_delayed_work_sync(&di->work);
-
-       power_supply_unregister(di->bat);
-
-       mutex_destroy(&di->lock);
-}
-EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown);
-
-static int bq27xxx_battery_platform_read(struct bq27xxx_device_info *di, u8 reg,
-                                        bool single)
-{
-       struct device *dev = di->dev;
-       struct bq27xxx_platform_data *pdata = dev->platform_data;
-       unsigned int timeout = 3;
-       int upper, lower;
-       int temp;
-
-       if (!single) {
-               /* Make sure the value has not changed in between reading the
-                * lower and the upper part */
-               upper = pdata->read(dev, reg + 1);
-               do {
-                       temp = upper;
-                       if (upper < 0)
-                               return upper;
-
-                       lower = pdata->read(dev, reg);
-                       if (lower < 0)
-                               return lower;
-
-                       upper = pdata->read(dev, reg + 1);
-               } while (temp != upper && --timeout);
-
-               if (timeout == 0)
-                       return -EIO;
-
-               return (upper << 8) | lower;
-       }
-
-       return pdata->read(dev, reg);
-}
-
-static int bq27xxx_battery_platform_probe(struct platform_device *pdev)
-{
-       struct bq27xxx_device_info *di;
-       struct bq27xxx_platform_data *pdata = pdev->dev.platform_data;
-
-       if (!pdata) {
-               dev_err(&pdev->dev, "no platform_data supplied\n");
-               return -EINVAL;
-       }
-
-       if (!pdata->read) {
-               dev_err(&pdev->dev, "no hdq read callback supplied\n");
-               return -EINVAL;
-       }
-
-       if (!pdata->chip) {
-               dev_err(&pdev->dev, "no device supplied\n");
-               return -EINVAL;
-       }
-
-       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, di);
-
-       di->dev = &pdev->dev;
-       di->chip = pdata->chip;
-       di->name = pdata->name ?: dev_name(&pdev->dev);
-       di->bus.read = bq27xxx_battery_platform_read;
-
-       return bq27xxx_battery_setup(di);
-}
-
-static int bq27xxx_battery_platform_remove(struct platform_device *pdev)
-{
-       struct bq27xxx_device_info *di = platform_get_drvdata(pdev);
-
-       bq27xxx_battery_teardown(di);
-
-       return 0;
-}
-
-static const struct platform_device_id bq27xxx_battery_platform_id_table[] = {
-       { "bq27000-battery", },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, bq27xxx_battery_platform_id_table);
-
-#ifdef CONFIG_OF
-static const struct of_device_id bq27xxx_battery_platform_of_match_table[] = {
-       { .compatible = "ti,bq27000" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, bq27xxx_battery_platform_of_match_table);
-#endif
-
-static struct platform_driver bq27xxx_battery_platform_driver = {
-       .probe  = bq27xxx_battery_platform_probe,
-       .remove = bq27xxx_battery_platform_remove,
-       .driver = {
-               .name = "bq27000-battery",
-               .of_match_table = of_match_ptr(bq27xxx_battery_platform_of_match_table),
-       },
-       .id_table = bq27xxx_battery_platform_id_table,
-};
-module_platform_driver(bq27xxx_battery_platform_driver);
-
-MODULE_ALIAS("platform:bq27000-battery");
-
-MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
-MODULE_DESCRIPTION("BQ27xxx battery monitor driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq27xxx_battery_i2c.c b/drivers/power/bq27xxx_battery_i2c.c
deleted file mode 100644 (file)
index 85d4ea2..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * BQ27xxx battery monitor I2C driver
- *
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
- *     Andrew F. Davis <afd@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <asm/unaligned.h>
-
-#include <linux/power/bq27xxx_battery.h>
-
-static DEFINE_IDR(battery_id);
-static DEFINE_MUTEX(battery_mutex);
-
-static irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data)
-{
-       struct bq27xxx_device_info *di = data;
-
-       bq27xxx_battery_update(di);
-
-       return IRQ_HANDLED;
-}
-
-static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
-                                   bool single)
-{
-       struct i2c_client *client = to_i2c_client(di->dev);
-       struct i2c_msg msg[2];
-       unsigned char data[2];
-       int ret;
-
-       if (!client->adapter)
-               return -ENODEV;
-
-       msg[0].addr = client->addr;
-       msg[0].flags = 0;
-       msg[0].buf = &reg;
-       msg[0].len = sizeof(reg);
-       msg[1].addr = client->addr;
-       msg[1].flags = I2C_M_RD;
-       msg[1].buf = data;
-       if (single)
-               msg[1].len = 1;
-       else
-               msg[1].len = 2;
-
-       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
-       if (ret < 0)
-               return ret;
-
-       if (!single)
-               ret = get_unaligned_le16(data);
-       else
-               ret = data[0];
-
-       return ret;
-}
-
-static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
-                                    const struct i2c_device_id *id)
-{
-       struct bq27xxx_device_info *di;
-       int ret;
-       char *name;
-       int num;
-
-       /* Get new ID for the new battery device */
-       mutex_lock(&battery_mutex);
-       num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
-       mutex_unlock(&battery_mutex);
-       if (num < 0)
-               return num;
-
-       name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
-       if (!name)
-               goto err_mem;
-
-       di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
-       if (!di)
-               goto err_mem;
-
-       di->id = num;
-       di->dev = &client->dev;
-       di->chip = id->driver_data;
-       di->name = name;
-       di->bus.read = bq27xxx_battery_i2c_read;
-
-       ret = bq27xxx_battery_setup(di);
-       if (ret)
-               goto err_failed;
-
-       /* Schedule a polling after about 1 min */
-       schedule_delayed_work(&di->work, 60 * HZ);
-
-       i2c_set_clientdata(client, di);
-
-       if (client->irq) {
-               ret = devm_request_threaded_irq(&client->dev, client->irq,
-                               NULL, bq27xxx_battery_irq_handler_thread,
-                               IRQF_ONESHOT,
-                               di->name, di);
-               if (ret) {
-                       dev_err(&client->dev,
-                               "Unable to register IRQ %d error %d\n",
-                               client->irq, ret);
-                       return ret;
-               }
-       }
-
-       return 0;
-
-err_mem:
-       ret = -ENOMEM;
-
-err_failed:
-       mutex_lock(&battery_mutex);
-       idr_remove(&battery_id, num);
-       mutex_unlock(&battery_mutex);
-
-       return ret;
-}
-
-static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
-{
-       struct bq27xxx_device_info *di = i2c_get_clientdata(client);
-
-       bq27xxx_battery_teardown(di);
-
-       mutex_lock(&battery_mutex);
-       idr_remove(&battery_id, di->id);
-       mutex_unlock(&battery_mutex);
-
-       return 0;
-}
-
-static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
-       { "bq27200", BQ27000 },
-       { "bq27210", BQ27010 },
-       { "bq27500", BQ27500 },
-       { "bq27510", BQ27500 },
-       { "bq27520", BQ27500 },
-       { "bq27530", BQ27530 },
-       { "bq27531", BQ27530 },
-       { "bq27541", BQ27541 },
-       { "bq27542", BQ27541 },
-       { "bq27546", BQ27541 },
-       { "bq27742", BQ27541 },
-       { "bq27545", BQ27545 },
-       { "bq27421", BQ27421 },
-       { "bq27425", BQ27421 },
-       { "bq27441", BQ27421 },
-       { "bq27621", BQ27421 },
-       {},
-};
-MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
-
-#ifdef CONFIG_OF
-static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
-       { .compatible = "ti,bq27200" },
-       { .compatible = "ti,bq27210" },
-       { .compatible = "ti,bq27500" },
-       { .compatible = "ti,bq27510" },
-       { .compatible = "ti,bq27520" },
-       { .compatible = "ti,bq27530" },
-       { .compatible = "ti,bq27531" },
-       { .compatible = "ti,bq27541" },
-       { .compatible = "ti,bq27542" },
-       { .compatible = "ti,bq27546" },
-       { .compatible = "ti,bq27742" },
-       { .compatible = "ti,bq27545" },
-       { .compatible = "ti,bq27421" },
-       { .compatible = "ti,bq27425" },
-       { .compatible = "ti,bq27441" },
-       { .compatible = "ti,bq27621" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
-#endif
-
-static struct i2c_driver bq27xxx_battery_i2c_driver = {
-       .driver = {
-               .name = "bq27xxx-battery",
-               .of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table),
-       },
-       .probe = bq27xxx_battery_i2c_probe,
-       .remove = bq27xxx_battery_i2c_remove,
-       .id_table = bq27xxx_i2c_id_table,
-};
-module_i2c_driver(bq27xxx_battery_i2c_driver);
-
-MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
-MODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
deleted file mode 100644 (file)
index e664ca7..0000000
+++ /dev/null
@@ -1,2074 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * MyungJoo Ham <myungjoo.ham@samsung.com>
- *
- * This driver enables to monitor battery health and control charger
- * during suspend-to-mem.
- * Charger manager depends on other devices. register this later than
- * the depending devices.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-**/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/rtc.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/power/charger-manager.h>
-#include <linux/regulator/consumer.h>
-#include <linux/sysfs.h>
-#include <linux/of.h>
-#include <linux/thermal.h>
-
-/*
- * Default termperature threshold for charging.
- * Every temperature units are in tenth of centigrade.
- */
-#define CM_DEFAULT_RECHARGE_TEMP_DIFF  50
-#define CM_DEFAULT_CHARGE_TEMP_MAX     500
-
-static const char * const default_event_names[] = {
-       [CM_EVENT_UNKNOWN] = "Unknown",
-       [CM_EVENT_BATT_FULL] = "Battery Full",
-       [CM_EVENT_BATT_IN] = "Battery Inserted",
-       [CM_EVENT_BATT_OUT] = "Battery Pulled Out",
-       [CM_EVENT_BATT_OVERHEAT] = "Battery Overheat",
-       [CM_EVENT_BATT_COLD] = "Battery Cold",
-       [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
-       [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
-       [CM_EVENT_OTHERS] = "Other battery events"
-};
-
-/*
- * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
- * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
- * without any delays.
- */
-#define        CM_JIFFIES_SMALL        (2)
-
-/* If y is valid (> 0) and smaller than x, do x = y */
-#define CM_MIN_VALID(x, y)     x = (((y > 0) && ((x) > (y))) ? (y) : (x))
-
-/*
- * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking
- * rtc alarm. It should be 2 or larger
- */
-#define CM_RTC_SMALL           (2)
-
-#define UEVENT_BUF_SIZE                32
-
-static LIST_HEAD(cm_list);
-static DEFINE_MUTEX(cm_list_mtx);
-
-/* About in-suspend (suspend-again) monitoring */
-static struct alarm *cm_timer;
-
-static bool cm_suspended;
-static bool cm_timer_set;
-static unsigned long cm_suspend_duration_ms;
-
-/* About normal (not suspended) monitoring */
-static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
-static unsigned long next_polling; /* Next appointed polling time */
-static struct workqueue_struct *cm_wq; /* init at driver add */
-static struct delayed_work cm_monitor_work; /* init at driver add */
-
-/**
- * is_batt_present - See if the battery presents in place.
- * @cm: the Charger Manager representing the battery.
- */
-static bool is_batt_present(struct charger_manager *cm)
-{
-       union power_supply_propval val;
-       struct power_supply *psy;
-       bool present = false;
-       int i, ret;
-
-       switch (cm->desc->battery_present) {
-       case CM_BATTERY_PRESENT:
-               present = true;
-               break;
-       case CM_NO_BATTERY:
-               break;
-       case CM_FUEL_GAUGE:
-               psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
-               if (!psy)
-                       break;
-
-               ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,
-                               &val);
-               if (ret == 0 && val.intval)
-                       present = true;
-               power_supply_put(psy);
-               break;
-       case CM_CHARGER_STAT:
-               for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
-                       psy = power_supply_get_by_name(
-                                       cm->desc->psy_charger_stat[i]);
-                       if (!psy) {
-                               dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
-                                       cm->desc->psy_charger_stat[i]);
-                               continue;
-                       }
-
-                       ret = power_supply_get_property(psy,
-                               POWER_SUPPLY_PROP_PRESENT, &val);
-                       power_supply_put(psy);
-                       if (ret == 0 && val.intval) {
-                               present = true;
-                               break;
-                       }
-               }
-               break;
-       }
-
-       return present;
-}
-
-/**
- * is_ext_pwr_online - See if an external power source is attached to charge
- * @cm: the Charger Manager representing the battery.
- *
- * Returns true if at least one of the chargers of the battery has an external
- * power source attached to charge the battery regardless of whether it is
- * actually charging or not.
- */
-static bool is_ext_pwr_online(struct charger_manager *cm)
-{
-       union power_supply_propval val;
-       struct power_supply *psy;
-       bool online = false;
-       int i, ret;
-
-       /* If at least one of them has one, it's yes. */
-       for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
-               psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
-               if (!psy) {
-                       dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
-                                       cm->desc->psy_charger_stat[i]);
-                       continue;
-               }
-
-               ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
-                               &val);
-               power_supply_put(psy);
-               if (ret == 0 && val.intval) {
-                       online = true;
-                       break;
-               }
-       }
-
-       return online;
-}
-
-/**
- * get_batt_uV - Get the voltage level of the battery
- * @cm: the Charger Manager representing the battery.
- * @uV: the voltage level returned.
- *
- * Returns 0 if there is no error.
- * Returns a negative value on error.
- */
-static int get_batt_uV(struct charger_manager *cm, int *uV)
-{
-       union power_supply_propval val;
-       struct power_supply *fuel_gauge;
-       int ret;
-
-       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
-       if (!fuel_gauge)
-               return -ENODEV;
-
-       ret = power_supply_get_property(fuel_gauge,
-                               POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
-       power_supply_put(fuel_gauge);
-       if (ret)
-               return ret;
-
-       *uV = val.intval;
-       return 0;
-}
-
-/**
- * is_charging - Returns true if the battery is being charged.
- * @cm: the Charger Manager representing the battery.
- */
-static bool is_charging(struct charger_manager *cm)
-{
-       int i, ret;
-       bool charging = false;
-       struct power_supply *psy;
-       union power_supply_propval val;
-
-       /* If there is no battery, it cannot be charged */
-       if (!is_batt_present(cm))
-               return false;
-
-       /* If at least one of the charger is charging, return yes */
-       for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
-               /* 1. The charger sholuld not be DISABLED */
-               if (cm->emergency_stop)
-                       continue;
-               if (!cm->charger_enabled)
-                       continue;
-
-               psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
-               if (!psy) {
-                       dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
-                                       cm->desc->psy_charger_stat[i]);
-                       continue;
-               }
-
-               /* 2. The charger should be online (ext-power) */
-               ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
-                               &val);
-               if (ret) {
-                       dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
-                                cm->desc->psy_charger_stat[i]);
-                       power_supply_put(psy);
-                       continue;
-               }
-               if (val.intval == 0) {
-                       power_supply_put(psy);
-                       continue;
-               }
-
-               /*
-                * 3. The charger should not be FULL, DISCHARGING,
-                * or NOT_CHARGING.
-                */
-               ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
-                               &val);
-               power_supply_put(psy);
-               if (ret) {
-                       dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
-                                cm->desc->psy_charger_stat[i]);
-                       continue;
-               }
-               if (val.intval == POWER_SUPPLY_STATUS_FULL ||
-                               val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
-                               val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
-                       continue;
-
-               /* Then, this is charging. */
-               charging = true;
-               break;
-       }
-
-       return charging;
-}
-
-/**
- * is_full_charged - Returns true if the battery is fully charged.
- * @cm: the Charger Manager representing the battery.
- */
-static bool is_full_charged(struct charger_manager *cm)
-{
-       struct charger_desc *desc = cm->desc;
-       union power_supply_propval val;
-       struct power_supply *fuel_gauge;
-       bool is_full = false;
-       int ret = 0;
-       int uV;
-
-       /* If there is no battery, it cannot be charged */
-       if (!is_batt_present(cm))
-               return false;
-
-       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
-       if (!fuel_gauge)
-               return false;
-
-       if (desc->fullbatt_full_capacity > 0) {
-               val.intval = 0;
-
-               /* Not full if capacity of fuel gauge isn't full */
-               ret = power_supply_get_property(fuel_gauge,
-                               POWER_SUPPLY_PROP_CHARGE_FULL, &val);
-               if (!ret && val.intval > desc->fullbatt_full_capacity) {
-                       is_full = true;
-                       goto out;
-               }
-       }
-
-       /* Full, if it's over the fullbatt voltage */
-       if (desc->fullbatt_uV > 0) {
-               ret = get_batt_uV(cm, &uV);
-               if (!ret && uV >= desc->fullbatt_uV) {
-                       is_full = true;
-                       goto out;
-               }
-       }
-
-       /* Full, if the capacity is more than fullbatt_soc */
-       if (desc->fullbatt_soc > 0) {
-               val.intval = 0;
-
-               ret = power_supply_get_property(fuel_gauge,
-                               POWER_SUPPLY_PROP_CAPACITY, &val);
-               if (!ret && val.intval >= desc->fullbatt_soc) {
-                       is_full = true;
-                       goto out;
-               }
-       }
-
-out:
-       power_supply_put(fuel_gauge);
-       return is_full;
-}
-
-/**
- * is_polling_required - Return true if need to continue polling for this CM.
- * @cm: the Charger Manager representing the battery.
- */
-static bool is_polling_required(struct charger_manager *cm)
-{
-       switch (cm->desc->polling_mode) {
-       case CM_POLL_DISABLE:
-               return false;
-       case CM_POLL_ALWAYS:
-               return true;
-       case CM_POLL_EXTERNAL_POWER_ONLY:
-               return is_ext_pwr_online(cm);
-       case CM_POLL_CHARGING_ONLY:
-               return is_charging(cm);
-       default:
-               dev_warn(cm->dev, "Incorrect polling_mode (%d)\n",
-                        cm->desc->polling_mode);
-       }
-
-       return false;
-}
-
-/**
- * try_charger_enable - Enable/Disable chargers altogether
- * @cm: the Charger Manager representing the battery.
- * @enable: true: enable / false: disable
- *
- * Note that Charger Manager keeps the charger enabled regardless whether
- * the charger is charging or not (because battery is full or no external
- * power source exists) except when CM needs to disable chargers forcibly
- * bacause of emergency causes; when the battery is overheated or too cold.
- */
-static int try_charger_enable(struct charger_manager *cm, bool enable)
-{
-       int err = 0, i;
-       struct charger_desc *desc = cm->desc;
-
-       /* Ignore if it's redundent command */
-       if (enable == cm->charger_enabled)
-               return 0;
-
-       if (enable) {
-               if (cm->emergency_stop)
-                       return -EAGAIN;
-
-               /*
-                * Save start time of charging to limit
-                * maximum possible charging time.
-                */
-               cm->charging_start_time = ktime_to_ms(ktime_get());
-               cm->charging_end_time = 0;
-
-               for (i = 0 ; i < desc->num_charger_regulators ; i++) {
-                       if (desc->charger_regulators[i].externally_control)
-                               continue;
-
-                       err = regulator_enable(desc->charger_regulators[i].consumer);
-                       if (err < 0) {
-                               dev_warn(cm->dev, "Cannot enable %s regulator\n",
-                                        desc->charger_regulators[i].regulator_name);
-                       }
-               }
-       } else {
-               /*
-                * Save end time of charging to maintain fully charged state
-                * of battery after full-batt.
-                */
-               cm->charging_start_time = 0;
-               cm->charging_end_time = ktime_to_ms(ktime_get());
-
-               for (i = 0 ; i < desc->num_charger_regulators ; i++) {
-                       if (desc->charger_regulators[i].externally_control)
-                               continue;
-
-                       err = regulator_disable(desc->charger_regulators[i].consumer);
-                       if (err < 0) {
-                               dev_warn(cm->dev, "Cannot disable %s regulator\n",
-                                        desc->charger_regulators[i].regulator_name);
-                       }
-               }
-
-               /*
-                * Abnormal battery state - Stop charging forcibly,
-                * even if charger was enabled at the other places
-                */
-               for (i = 0; i < desc->num_charger_regulators; i++) {
-                       if (regulator_is_enabled(
-                                   desc->charger_regulators[i].consumer)) {
-                               regulator_force_disable(
-                                       desc->charger_regulators[i].consumer);
-                               dev_warn(cm->dev, "Disable regulator(%s) forcibly\n",
-                                        desc->charger_regulators[i].regulator_name);
-                       }
-               }
-       }
-
-       if (!err)
-               cm->charger_enabled = enable;
-
-       return err;
-}
-
-/**
- * try_charger_restart - Restart charging.
- * @cm: the Charger Manager representing the battery.
- *
- * Restart charging by turning off and on the charger.
- */
-static int try_charger_restart(struct charger_manager *cm)
-{
-       int err;
-
-       if (cm->emergency_stop)
-               return -EAGAIN;
-
-       err = try_charger_enable(cm, false);
-       if (err)
-               return err;
-
-       return try_charger_enable(cm, true);
-}
-
-/**
- * uevent_notify - Let users know something has changed.
- * @cm: the Charger Manager representing the battery.
- * @event: the event string.
- *
- * If @event is null, it implies that uevent_notify is called
- * by resume function. When called in the resume function, cm_suspended
- * should be already reset to false in order to let uevent_notify
- * notify the recent event during the suspend to users. While
- * suspended, uevent_notify does not notify users, but tracks
- * events so that uevent_notify can notify users later after resumed.
- */
-static void uevent_notify(struct charger_manager *cm, const char *event)
-{
-       static char env_str[UEVENT_BUF_SIZE + 1] = "";
-       static char env_str_save[UEVENT_BUF_SIZE + 1] = "";
-
-       if (cm_suspended) {
-               /* Nothing in suspended-event buffer */
-               if (env_str_save[0] == 0) {
-                       if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
-                               return; /* status not changed */
-                       strncpy(env_str_save, event, UEVENT_BUF_SIZE);
-                       return;
-               }
-
-               if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
-                       return; /* Duplicated. */
-               strncpy(env_str_save, event, UEVENT_BUF_SIZE);
-               return;
-       }
-
-       if (event == NULL) {
-               /* No messages pending */
-               if (!env_str_save[0])
-                       return;
-
-               strncpy(env_str, env_str_save, UEVENT_BUF_SIZE);
-               kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
-               env_str_save[0] = 0;
-
-               return;
-       }
-
-       /* status not changed */
-       if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
-               return;
-
-       /* save the status and notify the update */
-       strncpy(env_str, event, UEVENT_BUF_SIZE);
-       kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
-
-       dev_info(cm->dev, "%s\n", event);
-}
-
-/**
- * fullbatt_vchk - Check voltage drop some times after "FULL" event.
- * @work: the work_struct appointing the function
- *
- * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
- * charger_desc, Charger Manager checks voltage drop after the battery
- * "FULL" event. It checks whether the voltage has dropped more than
- * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
- */
-static void fullbatt_vchk(struct work_struct *work)
-{
-       struct delayed_work *dwork = to_delayed_work(work);
-       struct charger_manager *cm = container_of(dwork,
-                       struct charger_manager, fullbatt_vchk_work);
-       struct charger_desc *desc = cm->desc;
-       int batt_uV, err, diff;
-
-       /* remove the appointment for fullbatt_vchk */
-       cm->fullbatt_vchk_jiffies_at = 0;
-
-       if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
-               return;
-
-       err = get_batt_uV(cm, &batt_uV);
-       if (err) {
-               dev_err(cm->dev, "%s: get_batt_uV error(%d)\n", __func__, err);
-               return;
-       }
-
-       diff = desc->fullbatt_uV - batt_uV;
-       if (diff < 0)
-               return;
-
-       dev_info(cm->dev, "VBATT dropped %duV after full-batt\n", diff);
-
-       if (diff > desc->fullbatt_vchkdrop_uV) {
-               try_charger_restart(cm);
-               uevent_notify(cm, "Recharging");
-       }
-}
-
-/**
- * check_charging_duration - Monitor charging/discharging duration
- * @cm: the Charger Manager representing the battery.
- *
- * If whole charging duration exceed 'charging_max_duration_ms',
- * cm stop charging to prevent overcharge/overheat. If discharging
- * duration exceed 'discharging _max_duration_ms', charger cable is
- * attached, after full-batt, cm start charging to maintain fully
- * charged state for battery.
- */
-static int check_charging_duration(struct charger_manager *cm)
-{
-       struct charger_desc *desc = cm->desc;
-       u64 curr = ktime_to_ms(ktime_get());
-       u64 duration;
-       int ret = false;
-
-       if (!desc->charging_max_duration_ms &&
-                       !desc->discharging_max_duration_ms)
-               return ret;
-
-       if (cm->charger_enabled) {
-               duration = curr - cm->charging_start_time;
-
-               if (duration > desc->charging_max_duration_ms) {
-                       dev_info(cm->dev, "Charging duration exceed %ums\n",
-                                desc->charging_max_duration_ms);
-                       uevent_notify(cm, "Discharging");
-                       try_charger_enable(cm, false);
-                       ret = true;
-               }
-       } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) {
-               duration = curr - cm->charging_end_time;
-
-               if (duration > desc->charging_max_duration_ms &&
-                               is_ext_pwr_online(cm)) {
-                       dev_info(cm->dev, "Discharging duration exceed %ums\n",
-                                desc->discharging_max_duration_ms);
-                       uevent_notify(cm, "Recharging");
-                       try_charger_enable(cm, true);
-                       ret = true;
-               }
-       }
-
-       return ret;
-}
-
-static int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
-                                       int *temp)
-{
-       struct power_supply *fuel_gauge;
-       int ret;
-
-       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
-       if (!fuel_gauge)
-               return -ENODEV;
-
-       ret = power_supply_get_property(fuel_gauge,
-                               POWER_SUPPLY_PROP_TEMP,
-                               (union power_supply_propval *)temp);
-       power_supply_put(fuel_gauge);
-
-       return ret;
-}
-
-static int cm_get_battery_temperature(struct charger_manager *cm,
-                                       int *temp)
-{
-       int ret;
-
-       if (!cm->desc->measure_battery_temp)
-               return -ENODEV;
-
-#ifdef CONFIG_THERMAL
-       if (cm->tzd_batt) {
-               ret = thermal_zone_get_temp(cm->tzd_batt, temp);
-               if (!ret)
-                       /* Calibrate temperature unit */
-                       *temp /= 100;
-       } else
-#endif
-       {
-               /* if-else continued from CONFIG_THERMAL */
-               ret = cm_get_battery_temperature_by_psy(cm, temp);
-       }
-
-       return ret;
-}
-
-static int cm_check_thermal_status(struct charger_manager *cm)
-{
-       struct charger_desc *desc = cm->desc;
-       int temp, upper_limit, lower_limit;
-       int ret = 0;
-
-       ret = cm_get_battery_temperature(cm, &temp);
-       if (ret) {
-               /* FIXME:
-                * No information of battery temperature might
-                * occur hazadous result. We have to handle it
-                * depending on battery type.
-                */
-               dev_err(cm->dev, "Failed to get battery temperature\n");
-               return 0;
-       }
-
-       upper_limit = desc->temp_max;
-       lower_limit = desc->temp_min;
-
-       if (cm->emergency_stop) {
-               upper_limit -= desc->temp_diff;
-               lower_limit += desc->temp_diff;
-       }
-
-       if (temp > upper_limit)
-               ret = CM_EVENT_BATT_OVERHEAT;
-       else if (temp < lower_limit)
-               ret = CM_EVENT_BATT_COLD;
-
-       return ret;
-}
-
-/**
- * _cm_monitor - Monitor the temperature and return true for exceptions.
- * @cm: the Charger Manager representing the battery.
- *
- * Returns true if there is an event to notify for the battery.
- * (True if the status of "emergency_stop" changes)
- */
-static bool _cm_monitor(struct charger_manager *cm)
-{
-       int temp_alrt;
-
-       temp_alrt = cm_check_thermal_status(cm);
-
-       /* It has been stopped already */
-       if (temp_alrt && cm->emergency_stop)
-               return false;
-
-       /*
-        * Check temperature whether overheat or cold.
-        * If temperature is out of range normal state, stop charging.
-        */
-       if (temp_alrt) {
-               cm->emergency_stop = temp_alrt;
-               if (!try_charger_enable(cm, false))
-                       uevent_notify(cm, default_event_names[temp_alrt]);
-
-       /*
-        * Check whole charging duration and discharing duration
-        * after full-batt.
-        */
-       } else if (!cm->emergency_stop && check_charging_duration(cm)) {
-               dev_dbg(cm->dev,
-                       "Charging/Discharging duration is out of range\n");
-       /*
-        * Check dropped voltage of battery. If battery voltage is more
-        * dropped than fullbatt_vchkdrop_uV after fully charged state,
-        * charger-manager have to recharge battery.
-        */
-       } else if (!cm->emergency_stop && is_ext_pwr_online(cm) &&
-                       !cm->charger_enabled) {
-               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
-
-       /*
-        * Check whether fully charged state to protect overcharge
-        * if charger-manager is charging for battery.
-        */
-       } else if (!cm->emergency_stop && is_full_charged(cm) &&
-                       cm->charger_enabled) {
-               dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n");
-               uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
-
-               try_charger_enable(cm, false);
-
-               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
-       } else {
-               cm->emergency_stop = 0;
-               if (is_ext_pwr_online(cm)) {
-                       if (!try_charger_enable(cm, true))
-                               uevent_notify(cm, "CHARGING");
-               }
-       }
-
-       return true;
-}
-
-/**
- * cm_monitor - Monitor every battery.
- *
- * Returns true if there is an event to notify from any of the batteries.
- * (True if the status of "emergency_stop" changes)
- */
-static bool cm_monitor(void)
-{
-       bool stop = false;
-       struct charger_manager *cm;
-
-       mutex_lock(&cm_list_mtx);
-
-       list_for_each_entry(cm, &cm_list, entry) {
-               if (_cm_monitor(cm))
-                       stop = true;
-       }
-
-       mutex_unlock(&cm_list_mtx);
-
-       return stop;
-}
-
-/**
- * _setup_polling - Setup the next instance of polling.
- * @work: work_struct of the function _setup_polling.
- */
-static void _setup_polling(struct work_struct *work)
-{
-       unsigned long min = ULONG_MAX;
-       struct charger_manager *cm;
-       bool keep_polling = false;
-       unsigned long _next_polling;
-
-       mutex_lock(&cm_list_mtx);
-
-       list_for_each_entry(cm, &cm_list, entry) {
-               if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
-                       keep_polling = true;
-
-                       if (min > cm->desc->polling_interval_ms)
-                               min = cm->desc->polling_interval_ms;
-               }
-       }
-
-       polling_jiffy = msecs_to_jiffies(min);
-       if (polling_jiffy <= CM_JIFFIES_SMALL)
-               polling_jiffy = CM_JIFFIES_SMALL + 1;
-
-       if (!keep_polling)
-               polling_jiffy = ULONG_MAX;
-       if (polling_jiffy == ULONG_MAX)
-               goto out;
-
-       WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
-                           ". try it later. %s\n", __func__);
-
-       /*
-        * Use mod_delayed_work() iff the next polling interval should
-        * occur before the currently scheduled one.  If @cm_monitor_work
-        * isn't active, the end result is the same, so no need to worry
-        * about stale @next_polling.
-        */
-       _next_polling = jiffies + polling_jiffy;
-
-       if (time_before(_next_polling, next_polling)) {
-               mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
-               next_polling = _next_polling;
-       } else {
-               if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy))
-                       next_polling = _next_polling;
-       }
-out:
-       mutex_unlock(&cm_list_mtx);
-}
-static DECLARE_WORK(setup_polling, _setup_polling);
-
-/**
- * cm_monitor_poller - The Monitor / Poller.
- * @work: work_struct of the function cm_monitor_poller
- *
- * During non-suspended state, cm_monitor_poller is used to poll and monitor
- * the batteries.
- */
-static void cm_monitor_poller(struct work_struct *work)
-{
-       cm_monitor();
-       schedule_work(&setup_polling);
-}
-
-/**
- * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
- * @cm: the Charger Manager representing the battery.
- */
-static void fullbatt_handler(struct charger_manager *cm)
-{
-       struct charger_desc *desc = cm->desc;
-
-       if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
-               goto out;
-
-       if (cm_suspended)
-               device_set_wakeup_capable(cm->dev, true);
-
-       mod_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
-                        msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
-       cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
-                                      desc->fullbatt_vchkdrop_ms);
-
-       if (cm->fullbatt_vchk_jiffies_at == 0)
-               cm->fullbatt_vchk_jiffies_at = 1;
-
-out:
-       dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n");
-       uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
-}
-
-/**
- * battout_handler - Event handler for CM_EVENT_BATT_OUT
- * @cm: the Charger Manager representing the battery.
- */
-static void battout_handler(struct charger_manager *cm)
-{
-       if (cm_suspended)
-               device_set_wakeup_capable(cm->dev, true);
-
-       if (!is_batt_present(cm)) {
-               dev_emerg(cm->dev, "Battery Pulled Out!\n");
-               uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
-       } else {
-               uevent_notify(cm, "Battery Reinserted?");
-       }
-}
-
-/**
- * misc_event_handler - Handler for other evnets
- * @cm: the Charger Manager representing the battery.
- * @type: the Charger Manager representing the battery.
- */
-static void misc_event_handler(struct charger_manager *cm,
-                       enum cm_event_types type)
-{
-       if (cm_suspended)
-               device_set_wakeup_capable(cm->dev, true);
-
-       if (is_polling_required(cm) && cm->desc->polling_interval_ms)
-               schedule_work(&setup_polling);
-       uevent_notify(cm, default_event_names[type]);
-}
-
-static int charger_get_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               union power_supply_propval *val)
-{
-       struct charger_manager *cm = power_supply_get_drvdata(psy);
-       struct charger_desc *desc = cm->desc;
-       struct power_supply *fuel_gauge = NULL;
-       int ret = 0;
-       int uV;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (is_charging(cm))
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else if (is_ext_pwr_online(cm))
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (cm->emergency_stop > 0)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else if (cm->emergency_stop < 0)
-                       val->intval = POWER_SUPPLY_HEALTH_COLD;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               if (is_batt_present(cm))
-                       val->intval = 1;
-               else
-                       val->intval = 0;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = get_batt_uV(cm, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
-               if (!fuel_gauge) {
-                       ret = -ENODEV;
-                       break;
-               }
-               ret = power_supply_get_property(fuel_gauge,
-                               POWER_SUPPLY_PROP_CURRENT_NOW, val);
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-       case POWER_SUPPLY_PROP_TEMP_AMBIENT:
-               return cm_get_battery_temperature(cm, &val->intval);
-       case POWER_SUPPLY_PROP_CAPACITY:
-               if (!is_batt_present(cm)) {
-                       /* There is no battery. Assume 100% */
-                       val->intval = 100;
-                       break;
-               }
-
-               fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
-               if (!fuel_gauge) {
-                       ret = -ENODEV;
-                       break;
-               }
-
-               ret = power_supply_get_property(fuel_gauge,
-                                       POWER_SUPPLY_PROP_CAPACITY, val);
-               if (ret)
-                       break;
-
-               if (val->intval > 100) {
-                       val->intval = 100;
-                       break;
-               }
-               if (val->intval < 0)
-                       val->intval = 0;
-
-               /* Do not adjust SOC when charging: voltage is overrated */
-               if (is_charging(cm))
-                       break;
-
-               /*
-                * If the capacity value is inconsistent, calibrate it base on
-                * the battery voltage values and the thresholds given as desc
-                */
-               ret = get_batt_uV(cm, &uV);
-               if (ret) {
-                       /* Voltage information not available. No calibration */
-                       ret = 0;
-                       break;
-               }
-
-               if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
-                   !is_charging(cm)) {
-                       val->intval = 100;
-                       break;
-               }
-
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               if (is_ext_pwr_online(cm))
-                       val->intval = 1;
-               else
-                       val->intval = 0;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               if (is_full_charged(cm))
-                       val->intval = 1;
-               else
-                       val->intval = 0;
-               ret = 0;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               if (is_charging(cm)) {
-                       fuel_gauge = power_supply_get_by_name(
-                                       cm->desc->psy_fuel_gauge);
-                       if (!fuel_gauge) {
-                               ret = -ENODEV;
-                               break;
-                       }
-
-                       ret = power_supply_get_property(fuel_gauge,
-                                               POWER_SUPPLY_PROP_CHARGE_NOW,
-                                               val);
-                       if (ret) {
-                               val->intval = 1;
-                               ret = 0;
-                       } else {
-                               /* If CHARGE_NOW is supplied, use it */
-                               val->intval = (val->intval > 0) ?
-                                               val->intval : 1;
-                       }
-               } else {
-                       val->intval = 0;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-       if (fuel_gauge)
-               power_supply_put(fuel_gauge);
-       return ret;
-}
-
-#define NUM_CHARGER_PSY_OPTIONAL       (4)
-static enum power_supply_property default_charger_props[] = {
-       /* Guaranteed to provide */
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       /*
-        * Optional properties are:
-        * POWER_SUPPLY_PROP_CHARGE_NOW,
-        * POWER_SUPPLY_PROP_CURRENT_NOW,
-        * POWER_SUPPLY_PROP_TEMP, and
-        * POWER_SUPPLY_PROP_TEMP_AMBIENT,
-        */
-};
-
-static const struct power_supply_desc psy_default = {
-       .name = "battery",
-       .type = POWER_SUPPLY_TYPE_BATTERY,
-       .properties = default_charger_props,
-       .num_properties = ARRAY_SIZE(default_charger_props),
-       .get_property = charger_get_property,
-       .no_thermal = true,
-};
-
-/**
- * cm_setup_timer - For in-suspend monitoring setup wakeup alarm
- *                 for suspend_again.
- *
- * Returns true if the alarm is set for Charger Manager to use.
- * Returns false if
- *     cm_setup_timer fails to set an alarm,
- *     cm_setup_timer does not need to set an alarm for Charger Manager,
- *     or an alarm previously configured is to be used.
- */
-static bool cm_setup_timer(void)
-{
-       struct charger_manager *cm;
-       unsigned int wakeup_ms = UINT_MAX;
-       int timer_req = 0;
-
-       if (time_after(next_polling, jiffies))
-               CM_MIN_VALID(wakeup_ms,
-                       jiffies_to_msecs(next_polling - jiffies));
-
-       mutex_lock(&cm_list_mtx);
-       list_for_each_entry(cm, &cm_list, entry) {
-               unsigned int fbchk_ms = 0;
-
-               /* fullbatt_vchk is required. setup timer for that */
-               if (cm->fullbatt_vchk_jiffies_at) {
-                       fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
-                                                   - jiffies);
-                       if (time_is_before_eq_jiffies(
-                               cm->fullbatt_vchk_jiffies_at) ||
-                               msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
-                               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
-                               fbchk_ms = 0;
-                       }
-               }
-               CM_MIN_VALID(wakeup_ms, fbchk_ms);
-
-               /* Skip if polling is not required for this CM */
-               if (!is_polling_required(cm) && !cm->emergency_stop)
-                       continue;
-               timer_req++;
-               if (cm->desc->polling_interval_ms == 0)
-                       continue;
-               CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
-       }
-       mutex_unlock(&cm_list_mtx);
-
-       if (timer_req && cm_timer) {
-               ktime_t now, add;
-
-               /*
-                * Set alarm with the polling interval (wakeup_ms)
-                * The alarm time should be NOW + CM_RTC_SMALL or later.
-                */
-               if (wakeup_ms == UINT_MAX ||
-                       wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC)
-                       wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC;
-
-               pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms);
-
-               now = ktime_get_boottime();
-               add = ktime_set(wakeup_ms / MSEC_PER_SEC,
-                               (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
-               alarm_start(cm_timer, ktime_add(now, add));
-
-               cm_suspend_duration_ms = wakeup_ms;
-
-               return true;
-       }
-       return false;
-}
-
-/**
- * charger_extcon_work - enable/diable charger according to the state
- *                     of charger cable
- *
- * @work: work_struct of the function charger_extcon_work.
- */
-static void charger_extcon_work(struct work_struct *work)
-{
-       struct charger_cable *cable =
-                       container_of(work, struct charger_cable, wq);
-       int ret;
-
-       if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) {
-               ret = regulator_set_current_limit(cable->charger->consumer,
-                                       cable->min_uA, cable->max_uA);
-               if (ret < 0) {
-                       pr_err("Cannot set current limit of %s (%s)\n",
-                              cable->charger->regulator_name, cable->name);
-                       return;
-               }
-
-               pr_info("Set current limit of %s : %duA ~ %duA\n",
-                       cable->charger->regulator_name,
-                       cable->min_uA, cable->max_uA);
-       }
-
-       try_charger_enable(cable->cm, cable->attached);
-}
-
-/**
- * charger_extcon_notifier - receive the state of charger cable
- *                     when registered cable is attached or detached.
- *
- * @self: the notifier block of the charger_extcon_notifier.
- * @event: the cable state.
- * @ptr: the data pointer of notifier block.
- */
-static int charger_extcon_notifier(struct notifier_block *self,
-                       unsigned long event, void *ptr)
-{
-       struct charger_cable *cable =
-               container_of(self, struct charger_cable, nb);
-
-       /*
-        * The newly state of charger cable.
-        * If cable is attached, cable->attached is true.
-        */
-       cable->attached = event;
-
-       /*
-        * Setup monitoring to check battery state
-        * when charger cable is attached.
-        */
-       if (cable->attached && is_polling_required(cable->cm)) {
-               cancel_work_sync(&setup_polling);
-               schedule_work(&setup_polling);
-       }
-
-       /*
-        * Setup work for controlling charger(regulator)
-        * according to charger cable.
-        */
-       schedule_work(&cable->wq);
-
-       return NOTIFY_DONE;
-}
-
-/**
- * charger_extcon_init - register external connector to use it
- *                     as the charger cable
- *
- * @cm: the Charger Manager representing the battery.
- * @cable: the Charger cable representing the external connector.
- */
-static int charger_extcon_init(struct charger_manager *cm,
-               struct charger_cable *cable)
-{
-       int ret = 0;
-
-       /*
-        * Charger manager use Extcon framework to identify
-        * the charger cable among various external connector
-        * cable (e.g., TA, USB, MHL, Dock).
-        */
-       INIT_WORK(&cable->wq, charger_extcon_work);
-       cable->nb.notifier_call = charger_extcon_notifier;
-       ret = extcon_register_interest(&cable->extcon_dev,
-                       cable->extcon_name, cable->name, &cable->nb);
-       if (ret < 0) {
-               pr_info("Cannot register extcon_dev for %s(cable: %s)\n",
-                       cable->extcon_name, cable->name);
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-/**
- * charger_manager_register_extcon - Register extcon device to recevie state
- *                                  of charger cable.
- * @cm: the Charger Manager representing the battery.
- *
- * This function support EXTCON(External Connector) subsystem to detect the
- * state of charger cables for enabling or disabling charger(regulator) and
- * select the charger cable for charging among a number of external cable
- * according to policy of H/W board.
- */
-static int charger_manager_register_extcon(struct charger_manager *cm)
-{
-       struct charger_desc *desc = cm->desc;
-       struct charger_regulator *charger;
-       int ret = 0;
-       int i;
-       int j;
-
-       for (i = 0; i < desc->num_charger_regulators; i++) {
-               charger = &desc->charger_regulators[i];
-
-               charger->consumer = regulator_get(cm->dev,
-                                       charger->regulator_name);
-               if (IS_ERR(charger->consumer)) {
-                       dev_err(cm->dev, "Cannot find charger(%s)\n",
-                               charger->regulator_name);
-                       return PTR_ERR(charger->consumer);
-               }
-               charger->cm = cm;
-
-               for (j = 0; j < charger->num_cables; j++) {
-                       struct charger_cable *cable = &charger->cables[j];
-
-                       ret = charger_extcon_init(cm, cable);
-                       if (ret < 0) {
-                               dev_err(cm->dev, "Cannot initialize charger(%s)\n",
-                                       charger->regulator_name);
-                               goto err;
-                       }
-                       cable->charger = charger;
-                       cable->cm = cm;
-               }
-       }
-
-err:
-       return ret;
-}
-
-/* help function of sysfs node to control charger(regulator) */
-static ssize_t charger_name_show(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct charger_regulator *charger
-               = container_of(attr, struct charger_regulator, attr_name);
-
-       return sprintf(buf, "%s\n", charger->regulator_name);
-}
-
-static ssize_t charger_state_show(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct charger_regulator *charger
-               = container_of(attr, struct charger_regulator, attr_state);
-       int state = 0;
-
-       if (!charger->externally_control)
-               state = regulator_is_enabled(charger->consumer);
-
-       return sprintf(buf, "%s\n", state ? "enabled" : "disabled");
-}
-
-static ssize_t charger_externally_control_show(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct charger_regulator *charger = container_of(attr,
-                       struct charger_regulator, attr_externally_control);
-
-       return sprintf(buf, "%d\n", charger->externally_control);
-}
-
-static ssize_t charger_externally_control_store(struct device *dev,
-                               struct device_attribute *attr, const char *buf,
-                               size_t count)
-{
-       struct charger_regulator *charger
-               = container_of(attr, struct charger_regulator,
-                                       attr_externally_control);
-       struct charger_manager *cm = charger->cm;
-       struct charger_desc *desc = cm->desc;
-       int i;
-       int ret;
-       int externally_control;
-       int chargers_externally_control = 1;
-
-       ret = sscanf(buf, "%d", &externally_control);
-       if (ret == 0) {
-               ret = -EINVAL;
-               return ret;
-       }
-
-       if (!externally_control) {
-               charger->externally_control = 0;
-               return count;
-       }
-
-       for (i = 0; i < desc->num_charger_regulators; i++) {
-               if (&desc->charger_regulators[i] != charger &&
-                       !desc->charger_regulators[i].externally_control) {
-                       /*
-                        * At least, one charger is controlled by
-                        * charger-manager
-                        */
-                       chargers_externally_control = 0;
-                       break;
-               }
-       }
-
-       if (!chargers_externally_control) {
-               if (cm->charger_enabled) {
-                       try_charger_enable(charger->cm, false);
-                       charger->externally_control = externally_control;
-                       try_charger_enable(charger->cm, true);
-               } else {
-                       charger->externally_control = externally_control;
-               }
-       } else {
-               dev_warn(cm->dev,
-                        "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging\n",
-                        charger->regulator_name);
-       }
-
-       return count;
-}
-
-/**
- * charger_manager_register_sysfs - Register sysfs entry for each charger
- * @cm: the Charger Manager representing the battery.
- *
- * This function add sysfs entry for charger(regulator) to control charger from
- * user-space. If some development board use one more chargers for charging
- * but only need one charger on specific case which is dependent on user
- * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/
- * class/power_supply/battery/charger.[index]/externally_control'. For example,
- * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/
- * externally_control, this charger isn't controlled from charger-manager and
- * always stay off state of regulator.
- */
-static int charger_manager_register_sysfs(struct charger_manager *cm)
-{
-       struct charger_desc *desc = cm->desc;
-       struct charger_regulator *charger;
-       int chargers_externally_control = 1;
-       char buf[11];
-       char *str;
-       int ret = 0;
-       int i;
-
-       /* Create sysfs entry to control charger(regulator) */
-       for (i = 0; i < desc->num_charger_regulators; i++) {
-               charger = &desc->charger_regulators[i];
-
-               snprintf(buf, 10, "charger.%d", i);
-               str = devm_kzalloc(cm->dev,
-                               sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
-               if (!str) {
-                       ret = -ENOMEM;
-                       goto err;
-               }
-               strcpy(str, buf);
-
-               charger->attrs[0] = &charger->attr_name.attr;
-               charger->attrs[1] = &charger->attr_state.attr;
-               charger->attrs[2] = &charger->attr_externally_control.attr;
-               charger->attrs[3] = NULL;
-               charger->attr_g.name = str;
-               charger->attr_g.attrs = charger->attrs;
-
-               sysfs_attr_init(&charger->attr_name.attr);
-               charger->attr_name.attr.name = "name";
-               charger->attr_name.attr.mode = 0444;
-               charger->attr_name.show = charger_name_show;
-
-               sysfs_attr_init(&charger->attr_state.attr);
-               charger->attr_state.attr.name = "state";
-               charger->attr_state.attr.mode = 0444;
-               charger->attr_state.show = charger_state_show;
-
-               sysfs_attr_init(&charger->attr_externally_control.attr);
-               charger->attr_externally_control.attr.name
-                               = "externally_control";
-               charger->attr_externally_control.attr.mode = 0644;
-               charger->attr_externally_control.show
-                               = charger_externally_control_show;
-               charger->attr_externally_control.store
-                               = charger_externally_control_store;
-
-               if (!desc->charger_regulators[i].externally_control ||
-                               !chargers_externally_control)
-                       chargers_externally_control = 0;
-
-               dev_info(cm->dev, "'%s' regulator's externally_control is %d\n",
-                        charger->regulator_name, charger->externally_control);
-
-               ret = sysfs_create_group(&cm->charger_psy->dev.kobj,
-                                       &charger->attr_g);
-               if (ret < 0) {
-                       dev_err(cm->dev, "Cannot create sysfs entry of %s regulator\n",
-                               charger->regulator_name);
-                       ret = -EINVAL;
-                       goto err;
-               }
-       }
-
-       if (chargers_externally_control) {
-               dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n");
-               ret = -EINVAL;
-               goto err;
-       }
-
-err:
-       return ret;
-}
-
-static int cm_init_thermal_data(struct charger_manager *cm,
-               struct power_supply *fuel_gauge)
-{
-       struct charger_desc *desc = cm->desc;
-       union power_supply_propval val;
-       int ret;
-
-       /* Verify whether fuel gauge provides battery temperature */
-       ret = power_supply_get_property(fuel_gauge,
-                                       POWER_SUPPLY_PROP_TEMP, &val);
-
-       if (!ret) {
-               cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
-                               POWER_SUPPLY_PROP_TEMP;
-               cm->charger_psy_desc.num_properties++;
-               cm->desc->measure_battery_temp = true;
-       }
-#ifdef CONFIG_THERMAL
-       if (ret && desc->thermal_zone) {
-               cm->tzd_batt =
-                       thermal_zone_get_zone_by_name(desc->thermal_zone);
-               if (IS_ERR(cm->tzd_batt))
-                       return PTR_ERR(cm->tzd_batt);
-
-               /* Use external thermometer */
-               cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
-                               POWER_SUPPLY_PROP_TEMP_AMBIENT;
-               cm->charger_psy_desc.num_properties++;
-               cm->desc->measure_battery_temp = true;
-               ret = 0;
-       }
-#endif
-       if (cm->desc->measure_battery_temp) {
-               /* NOTICE : Default allowable minimum charge temperature is 0 */
-               if (!desc->temp_max)
-                       desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX;
-               if (!desc->temp_diff)
-                       desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF;
-       }
-
-       return ret;
-}
-
-static const struct of_device_id charger_manager_match[] = {
-       {
-               .compatible = "charger-manager",
-       },
-       {},
-};
-
-static struct charger_desc *of_cm_parse_desc(struct device *dev)
-{
-       struct charger_desc *desc;
-       struct device_node *np = dev->of_node;
-       u32 poll_mode = CM_POLL_DISABLE;
-       u32 battery_stat = CM_NO_BATTERY;
-       int num_chgs = 0;
-
-       desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
-       if (!desc)
-               return ERR_PTR(-ENOMEM);
-
-       of_property_read_string(np, "cm-name", &desc->psy_name);
-
-       of_property_read_u32(np, "cm-poll-mode", &poll_mode);
-       desc->polling_mode = poll_mode;
-
-       of_property_read_u32(np, "cm-poll-interval",
-                               &desc->polling_interval_ms);
-
-       of_property_read_u32(np, "cm-fullbatt-vchkdrop-ms",
-                                       &desc->fullbatt_vchkdrop_ms);
-       of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt",
-                                       &desc->fullbatt_vchkdrop_uV);
-       of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV);
-       of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc);
-       of_property_read_u32(np, "cm-fullbatt-capacity",
-                                       &desc->fullbatt_full_capacity);
-
-       of_property_read_u32(np, "cm-battery-stat", &battery_stat);
-       desc->battery_present = battery_stat;
-
-       /* chargers */
-       of_property_read_u32(np, "cm-num-chargers", &num_chgs);
-       if (num_chgs) {
-               /* Allocate empty bin at the tail of array */
-               desc->psy_charger_stat = devm_kzalloc(dev, sizeof(char *)
-                                               * (num_chgs + 1), GFP_KERNEL);
-               if (desc->psy_charger_stat) {
-                       int i;
-                       for (i = 0; i < num_chgs; i++)
-                               of_property_read_string_index(np, "cm-chargers",
-                                               i, &desc->psy_charger_stat[i]);
-               } else {
-                       return ERR_PTR(-ENOMEM);
-               }
-       }
-
-       of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge);
-
-       of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone);
-
-       of_property_read_u32(np, "cm-battery-cold", &desc->temp_min);
-       if (of_get_property(np, "cm-battery-cold-in-minus", NULL))
-               desc->temp_min *= -1;
-       of_property_read_u32(np, "cm-battery-hot", &desc->temp_max);
-       of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff);
-
-       of_property_read_u32(np, "cm-charging-max",
-                               &desc->charging_max_duration_ms);
-       of_property_read_u32(np, "cm-discharging-max",
-                               &desc->discharging_max_duration_ms);
-
-       /* battery charger regualtors */
-       desc->num_charger_regulators = of_get_child_count(np);
-       if (desc->num_charger_regulators) {
-               struct charger_regulator *chg_regs;
-               struct device_node *child;
-
-               chg_regs = devm_kzalloc(dev, sizeof(*chg_regs)
-                                       * desc->num_charger_regulators,
-                                       GFP_KERNEL);
-               if (!chg_regs)
-                       return ERR_PTR(-ENOMEM);
-
-               desc->charger_regulators = chg_regs;
-
-               for_each_child_of_node(np, child) {
-                       struct charger_cable *cables;
-                       struct device_node *_child;
-
-                       of_property_read_string(child, "cm-regulator-name",
-                                       &chg_regs->regulator_name);
-
-                       /* charger cables */
-                       chg_regs->num_cables = of_get_child_count(child);
-                       if (chg_regs->num_cables) {
-                               cables = devm_kzalloc(dev, sizeof(*cables)
-                                               * chg_regs->num_cables,
-                                               GFP_KERNEL);
-                               if (!cables) {
-                                       of_node_put(child);
-                                       return ERR_PTR(-ENOMEM);
-                               }
-
-                               chg_regs->cables = cables;
-
-                               for_each_child_of_node(child, _child) {
-                                       of_property_read_string(_child,
-                                       "cm-cable-name", &cables->name);
-                                       of_property_read_string(_child,
-                                       "cm-cable-extcon",
-                                       &cables->extcon_name);
-                                       of_property_read_u32(_child,
-                                       "cm-cable-min",
-                                       &cables->min_uA);
-                                       of_property_read_u32(_child,
-                                       "cm-cable-max",
-                                       &cables->max_uA);
-                                       cables++;
-                               }
-                       }
-                       chg_regs++;
-               }
-       }
-       return desc;
-}
-
-static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
-{
-       if (pdev->dev.of_node)
-               return of_cm_parse_desc(&pdev->dev);
-       return dev_get_platdata(&pdev->dev);
-}
-
-static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
-{
-       cm_timer_set = false;
-       return ALARMTIMER_NORESTART;
-}
-
-static int charger_manager_probe(struct platform_device *pdev)
-{
-       struct charger_desc *desc = cm_get_drv_data(pdev);
-       struct charger_manager *cm;
-       int ret = 0, i = 0;
-       int j = 0;
-       union power_supply_propval val;
-       struct power_supply *fuel_gauge;
-       struct power_supply_config psy_cfg = {};
-
-       if (IS_ERR(desc)) {
-               dev_err(&pdev->dev, "No platform data (desc) found\n");
-               return -ENODEV;
-       }
-
-       cm = devm_kzalloc(&pdev->dev,
-                       sizeof(struct charger_manager), GFP_KERNEL);
-       if (!cm)
-               return -ENOMEM;
-
-       /* Basic Values. Unspecified are Null or 0 */
-       cm->dev = &pdev->dev;
-       cm->desc = desc;
-       psy_cfg.drv_data = cm;
-
-       /* Initialize alarm timer */
-       if (alarmtimer_get_rtcdev()) {
-               cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
-               alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
-       }
-
-       /*
-        * The following two do not need to be errors.
-        * Users may intentionally ignore those two features.
-        */
-       if (desc->fullbatt_uV == 0) {
-               dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n");
-       }
-       if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
-               dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n");
-               desc->fullbatt_vchkdrop_ms = 0;
-               desc->fullbatt_vchkdrop_uV = 0;
-       }
-       if (desc->fullbatt_soc == 0) {
-               dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n");
-       }
-       if (desc->fullbatt_full_capacity == 0) {
-               dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n");
-       }
-
-       if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
-               dev_err(&pdev->dev, "charger_regulators undefined\n");
-               return -EINVAL;
-       }
-
-       if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
-               dev_err(&pdev->dev, "No power supply defined\n");
-               return -EINVAL;
-       }
-
-       if (!desc->psy_fuel_gauge) {
-               dev_err(&pdev->dev, "No fuel gauge power supply defined\n");
-               return -EINVAL;
-       }
-
-       /* Counting index only */
-       while (desc->psy_charger_stat[i])
-               i++;
-
-       /* Check if charger's supplies are present at probe */
-       for (i = 0; desc->psy_charger_stat[i]; i++) {
-               struct power_supply *psy;
-
-               psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
-               if (!psy) {
-                       dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
-                               desc->psy_charger_stat[i]);
-                       return -ENODEV;
-               }
-               power_supply_put(psy);
-       }
-
-       if (desc->polling_interval_ms == 0 ||
-           msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
-               dev_err(&pdev->dev, "polling_interval_ms is too small\n");
-               return -EINVAL;
-       }
-
-       if (!desc->charging_max_duration_ms ||
-                       !desc->discharging_max_duration_ms) {
-               dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n");
-               desc->charging_max_duration_ms = 0;
-               desc->discharging_max_duration_ms = 0;
-       }
-
-       platform_set_drvdata(pdev, cm);
-
-       memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default));
-
-       if (!desc->psy_name)
-               strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
-       else
-               strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
-       cm->charger_psy_desc.name = cm->psy_name_buf;
-
-       /* Allocate for psy properties because they may vary */
-       cm->charger_psy_desc.properties = devm_kzalloc(&pdev->dev,
-                               sizeof(enum power_supply_property)
-                               * (ARRAY_SIZE(default_charger_props) +
-                               NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL);
-       if (!cm->charger_psy_desc.properties)
-               return -ENOMEM;
-
-       memcpy(cm->charger_psy_desc.properties, default_charger_props,
-               sizeof(enum power_supply_property) *
-               ARRAY_SIZE(default_charger_props));
-       cm->charger_psy_desc.num_properties = psy_default.num_properties;
-
-       /* Find which optional psy-properties are available */
-       fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
-       if (!fuel_gauge) {
-               dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
-                       desc->psy_fuel_gauge);
-               return -ENODEV;
-       }
-       if (!power_supply_get_property(fuel_gauge,
-                                         POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
-               cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
-                               POWER_SUPPLY_PROP_CHARGE_NOW;
-               cm->charger_psy_desc.num_properties++;
-       }
-       if (!power_supply_get_property(fuel_gauge,
-                                         POWER_SUPPLY_PROP_CURRENT_NOW,
-                                         &val)) {
-               cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
-                               POWER_SUPPLY_PROP_CURRENT_NOW;
-               cm->charger_psy_desc.num_properties++;
-       }
-
-       ret = cm_init_thermal_data(cm, fuel_gauge);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to initialize thermal data\n");
-               cm->desc->measure_battery_temp = false;
-       }
-       power_supply_put(fuel_gauge);
-
-       INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
-
-       cm->charger_psy = power_supply_register(&pdev->dev,
-                                               &cm->charger_psy_desc,
-                                               &psy_cfg);
-       if (IS_ERR(cm->charger_psy)) {
-               dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
-                       cm->charger_psy_desc.name);
-               return PTR_ERR(cm->charger_psy);
-       }
-
-       /* Register extcon device for charger cable */
-       ret = charger_manager_register_extcon(cm);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Cannot initialize extcon device\n");
-               goto err_reg_extcon;
-       }
-
-       /* Register sysfs entry for charger(regulator) */
-       ret = charger_manager_register_sysfs(cm);
-       if (ret < 0) {
-               dev_err(&pdev->dev,
-                       "Cannot initialize sysfs entry of regulator\n");
-               goto err_reg_sysfs;
-       }
-
-       /* Add to the list */
-       mutex_lock(&cm_list_mtx);
-       list_add(&cm->entry, &cm_list);
-       mutex_unlock(&cm_list_mtx);
-
-       /*
-        * Charger-manager is capable of waking up the systme from sleep
-        * when event is happend through cm_notify_event()
-        */
-       device_init_wakeup(&pdev->dev, true);
-       device_set_wakeup_capable(&pdev->dev, false);
-
-       /*
-        * Charger-manager have to check the charging state right after
-        * tialization of charger-manager and then update current charging
-        * state.
-        */
-       cm_monitor();
-
-       schedule_work(&setup_polling);
-
-       return 0;
-
-err_reg_sysfs:
-       for (i = 0; i < desc->num_charger_regulators; i++) {
-               struct charger_regulator *charger;
-
-               charger = &desc->charger_regulators[i];
-               sysfs_remove_group(&cm->charger_psy->dev.kobj,
-                               &charger->attr_g);
-       }
-err_reg_extcon:
-       for (i = 0; i < desc->num_charger_regulators; i++) {
-               struct charger_regulator *charger;
-
-               charger = &desc->charger_regulators[i];
-               for (j = 0; j < charger->num_cables; j++) {
-                       struct charger_cable *cable = &charger->cables[j];
-                       /* Remove notifier block if only edev exists */
-                       if (cable->extcon_dev.edev)
-                               extcon_unregister_interest(&cable->extcon_dev);
-               }
-
-               regulator_put(desc->charger_regulators[i].consumer);
-       }
-
-       power_supply_unregister(cm->charger_psy);
-
-       return ret;
-}
-
-static int charger_manager_remove(struct platform_device *pdev)
-{
-       struct charger_manager *cm = platform_get_drvdata(pdev);
-       struct charger_desc *desc = cm->desc;
-       int i = 0;
-       int j = 0;
-
-       /* Remove from the list */
-       mutex_lock(&cm_list_mtx);
-       list_del(&cm->entry);
-       mutex_unlock(&cm_list_mtx);
-
-       cancel_work_sync(&setup_polling);
-       cancel_delayed_work_sync(&cm_monitor_work);
-
-       for (i = 0 ; i < desc->num_charger_regulators ; i++) {
-               struct charger_regulator *charger
-                               = &desc->charger_regulators[i];
-               for (j = 0 ; j < charger->num_cables ; j++) {
-                       struct charger_cable *cable = &charger->cables[j];
-                       extcon_unregister_interest(&cable->extcon_dev);
-               }
-       }
-
-       for (i = 0 ; i < desc->num_charger_regulators ; i++)
-               regulator_put(desc->charger_regulators[i].consumer);
-
-       power_supply_unregister(cm->charger_psy);
-
-       try_charger_enable(cm, false);
-
-       return 0;
-}
-
-static const struct platform_device_id charger_manager_id[] = {
-       { "charger-manager", 0 },
-       { },
-};
-MODULE_DEVICE_TABLE(platform, charger_manager_id);
-
-static int cm_suspend_noirq(struct device *dev)
-{
-       int ret = 0;
-
-       if (device_may_wakeup(dev)) {
-               device_set_wakeup_capable(dev, false);
-               ret = -EAGAIN;
-       }
-
-       return ret;
-}
-
-static bool cm_need_to_awake(void)
-{
-       struct charger_manager *cm;
-
-       if (cm_timer)
-               return false;
-
-       mutex_lock(&cm_list_mtx);
-       list_for_each_entry(cm, &cm_list, entry) {
-               if (is_charging(cm)) {
-                       mutex_unlock(&cm_list_mtx);
-                       return true;
-               }
-       }
-       mutex_unlock(&cm_list_mtx);
-
-       return false;
-}
-
-static int cm_suspend_prepare(struct device *dev)
-{
-       struct charger_manager *cm = dev_get_drvdata(dev);
-
-       if (cm_need_to_awake())
-               return -EBUSY;
-
-       if (!cm_suspended)
-               cm_suspended = true;
-
-       cm_timer_set = cm_setup_timer();
-
-       if (cm_timer_set) {
-               cancel_work_sync(&setup_polling);
-               cancel_delayed_work_sync(&cm_monitor_work);
-               cancel_delayed_work(&cm->fullbatt_vchk_work);
-       }
-
-       return 0;
-}
-
-static void cm_suspend_complete(struct device *dev)
-{
-       struct charger_manager *cm = dev_get_drvdata(dev);
-
-       if (cm_suspended)
-               cm_suspended = false;
-
-       if (cm_timer_set) {
-               ktime_t remain;
-
-               alarm_cancel(cm_timer);
-               cm_timer_set = false;
-               remain = alarm_expires_remaining(cm_timer);
-               cm_suspend_duration_ms -= ktime_to_ms(remain);
-               schedule_work(&setup_polling);
-       }
-
-       _cm_monitor(cm);
-
-       /* Re-enqueue delayed work (fullbatt_vchk_work) */
-       if (cm->fullbatt_vchk_jiffies_at) {
-               unsigned long delay = 0;
-               unsigned long now = jiffies + CM_JIFFIES_SMALL;
-
-               if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) {
-                       delay = (unsigned long)((long)now
-                               - (long)(cm->fullbatt_vchk_jiffies_at));
-                       delay = jiffies_to_msecs(delay);
-               } else {
-                       delay = 0;
-               }
-
-               /*
-                * Account for cm_suspend_duration_ms with assuming that
-                * timer stops in suspend.
-                */
-               if (delay > cm_suspend_duration_ms)
-                       delay -= cm_suspend_duration_ms;
-               else
-                       delay = 0;
-
-               queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
-                                  msecs_to_jiffies(delay));
-       }
-       device_set_wakeup_capable(cm->dev, false);
-}
-
-static const struct dev_pm_ops charger_manager_pm = {
-       .prepare        = cm_suspend_prepare,
-       .suspend_noirq  = cm_suspend_noirq,
-       .complete       = cm_suspend_complete,
-};
-
-static struct platform_driver charger_manager_driver = {
-       .driver = {
-               .name = "charger-manager",
-               .pm = &charger_manager_pm,
-               .of_match_table = charger_manager_match,
-       },
-       .probe = charger_manager_probe,
-       .remove = charger_manager_remove,
-       .id_table = charger_manager_id,
-};
-
-static int __init charger_manager_init(void)
-{
-       cm_wq = create_freezable_workqueue("charger_manager");
-       INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
-
-       return platform_driver_register(&charger_manager_driver);
-}
-late_initcall(charger_manager_init);
-
-static void __exit charger_manager_cleanup(void)
-{
-       destroy_workqueue(cm_wq);
-       cm_wq = NULL;
-
-       platform_driver_unregister(&charger_manager_driver);
-}
-module_exit(charger_manager_cleanup);
-
-/**
- * cm_notify_event - charger driver notify Charger Manager of charger event
- * @psy: pointer to instance of charger's power_supply
- * @type: type of charger event
- * @msg: optional message passed to uevent_notify fuction
- */
-void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
-                    char *msg)
-{
-       struct charger_manager *cm;
-       bool found_power_supply = false;
-
-       if (psy == NULL)
-               return;
-
-       mutex_lock(&cm_list_mtx);
-       list_for_each_entry(cm, &cm_list, entry) {
-               if (match_string(cm->desc->psy_charger_stat, -1,
-                                psy->desc->name) >= 0) {
-                       found_power_supply = true;
-                       break;
-               }
-       }
-       mutex_unlock(&cm_list_mtx);
-
-       if (!found_power_supply)
-               return;
-
-       switch (type) {
-       case CM_EVENT_BATT_FULL:
-               fullbatt_handler(cm);
-               break;
-       case CM_EVENT_BATT_OUT:
-               battout_handler(cm);
-               break;
-       case CM_EVENT_BATT_IN:
-       case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
-               misc_event_handler(cm, type);
-               break;
-       case CM_EVENT_UNKNOWN:
-       case CM_EVENT_OTHERS:
-               uevent_notify(cm, msg ? msg : default_event_names[type]);
-               break;
-       default:
-               dev_err(cm->dev, "%s: type not specified\n", __func__);
-               break;
-       }
-}
-EXPORT_SYMBOL_GPL(cm_notify_event);
-
-MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
-MODULE_DESCRIPTION("Charger Manager");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c
deleted file mode 100644 (file)
index 3a0bc60..0000000
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Battery and Power Management code for the Sharp SL-5x00
- *
- * Copyright (C) 2009 Thomas Kunze
- *
- * based on tosa_battery.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/power_supply.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <linux/mfd/ucb1x00.h>
-
-#include <asm/mach/sharpsl_param.h>
-#include <asm/mach-types.h>
-#include <mach/collie.h>
-
-static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
-static struct work_struct bat_work;
-static struct ucb1x00 *ucb;
-
-struct collie_bat {
-       int status;
-       struct power_supply *psy;
-       int full_chrg;
-
-       struct mutex work_lock; /* protects data */
-
-       bool (*is_present)(struct collie_bat *bat);
-       int gpio_full;
-       int gpio_charge_on;
-
-       int technology;
-
-       int gpio_bat;
-       int adc_bat;
-       int adc_bat_divider;
-       int bat_max;
-       int bat_min;
-
-       int gpio_temp;
-       int adc_temp;
-       int adc_temp_divider;
-};
-
-static struct collie_bat collie_bat_main;
-
-static unsigned long collie_read_bat(struct collie_bat *bat)
-{
-       unsigned long value = 0;
-
-       if (bat->gpio_bat < 0 || bat->adc_bat < 0)
-               return 0;
-       mutex_lock(&bat_lock);
-       gpio_set_value(bat->gpio_bat, 1);
-       msleep(5);
-       ucb1x00_adc_enable(ucb);
-       value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
-       ucb1x00_adc_disable(ucb);
-       gpio_set_value(bat->gpio_bat, 0);
-       mutex_unlock(&bat_lock);
-       value = value * 1000000 / bat->adc_bat_divider;
-
-       return value;
-}
-
-static unsigned long collie_read_temp(struct collie_bat *bat)
-{
-       unsigned long value = 0;
-       if (bat->gpio_temp < 0 || bat->adc_temp < 0)
-               return 0;
-
-       mutex_lock(&bat_lock);
-       gpio_set_value(bat->gpio_temp, 1);
-       msleep(5);
-       ucb1x00_adc_enable(ucb);
-       value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
-       ucb1x00_adc_disable(ucb);
-       gpio_set_value(bat->gpio_temp, 0);
-       mutex_unlock(&bat_lock);
-
-       value = value * 10000 / bat->adc_temp_divider;
-
-       return value;
-}
-
-static int collie_bat_get_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       int ret = 0;
-       struct collie_bat *bat = power_supply_get_drvdata(psy);
-
-       if (bat->is_present && !bat->is_present(bat)
-                       && psp != POWER_SUPPLY_PROP_PRESENT) {
-               return -ENODEV;
-       }
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = bat->status;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = bat->technology;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = collie_read_bat(bat);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               if (bat->full_chrg == -1)
-                       val->intval = bat->bat_max;
-               else
-                       val->intval = bat->full_chrg;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = bat->bat_max;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = bat->bat_min;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = collie_read_temp(bat);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = bat->is_present ? bat->is_present(bat) : 1;
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static void collie_bat_external_power_changed(struct power_supply *psy)
-{
-       schedule_work(&bat_work);
-}
-
-static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
-{
-       pr_info("collie_bat_gpio irq\n");
-       schedule_work(&bat_work);
-       return IRQ_HANDLED;
-}
-
-static void collie_bat_update(struct collie_bat *bat)
-{
-       int old;
-       struct power_supply *psy = bat->psy;
-
-       mutex_lock(&bat->work_lock);
-
-       old = bat->status;
-
-       if (bat->is_present && !bat->is_present(bat)) {
-               printk(KERN_NOTICE "%s not present\n", psy->desc->name);
-               bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
-               bat->full_chrg = -1;
-       } else if (power_supply_am_i_supplied(psy)) {
-               if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
-                       gpio_set_value(bat->gpio_charge_on, 1);
-                       mdelay(15);
-               }
-
-               if (gpio_get_value(bat->gpio_full)) {
-                       if (old == POWER_SUPPLY_STATUS_CHARGING ||
-                                       bat->full_chrg == -1)
-                               bat->full_chrg = collie_read_bat(bat);
-
-                       gpio_set_value(bat->gpio_charge_on, 0);
-                       bat->status = POWER_SUPPLY_STATUS_FULL;
-               } else {
-                       gpio_set_value(bat->gpio_charge_on, 1);
-                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
-               }
-       } else {
-               gpio_set_value(bat->gpio_charge_on, 0);
-               bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
-       }
-
-       if (old != bat->status)
-               power_supply_changed(psy);
-
-       mutex_unlock(&bat->work_lock);
-}
-
-static void collie_bat_work(struct work_struct *work)
-{
-       collie_bat_update(&collie_bat_main);
-}
-
-
-static enum power_supply_property collie_bat_main_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static enum power_supply_property collie_bat_bu_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_PRESENT,
-};
-
-static const struct power_supply_desc collie_bat_main_desc = {
-       .name           = "main-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = collie_bat_main_props,
-       .num_properties = ARRAY_SIZE(collie_bat_main_props),
-       .get_property   = collie_bat_get_property,
-       .external_power_changed = collie_bat_external_power_changed,
-       .use_for_apm    = 1,
-};
-
-static struct collie_bat collie_bat_main = {
-       .status = POWER_SUPPLY_STATUS_DISCHARGING,
-       .full_chrg = -1,
-       .psy = NULL,
-
-       .gpio_full = COLLIE_GPIO_CO,
-       .gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
-
-       .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
-
-       .gpio_bat = COLLIE_GPIO_MBAT_ON,
-       .adc_bat = UCB_ADC_INP_AD1,
-       .adc_bat_divider = 155,
-       .bat_max = 4310000,
-       .bat_min = 1551 * 1000000 / 414,
-
-       .gpio_temp = COLLIE_GPIO_TMP_ON,
-       .adc_temp = UCB_ADC_INP_AD0,
-       .adc_temp_divider = 10000,
-};
-
-static const struct power_supply_desc collie_bat_bu_desc = {
-       .name           = "backup-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = collie_bat_bu_props,
-       .num_properties = ARRAY_SIZE(collie_bat_bu_props),
-       .get_property   = collie_bat_get_property,
-       .external_power_changed = collie_bat_external_power_changed,
-};
-
-static struct collie_bat collie_bat_bu = {
-       .status = POWER_SUPPLY_STATUS_UNKNOWN,
-       .full_chrg = -1,
-       .psy = NULL,
-
-       .gpio_full = -1,
-       .gpio_charge_on = -1,
-
-       .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
-
-       .gpio_bat = COLLIE_GPIO_BBAT_ON,
-       .adc_bat = UCB_ADC_INP_AD1,
-       .adc_bat_divider = 155,
-       .bat_max = 3000000,
-       .bat_min = 1900000,
-
-       .gpio_temp = -1,
-       .adc_temp = -1,
-       .adc_temp_divider = -1,
-};
-
-static struct gpio collie_batt_gpios[] = {
-       { COLLIE_GPIO_CO,           GPIOF_IN,           "main battery full" },
-       { COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN,           "main battery low" },
-       { COLLIE_GPIO_CHARGE_ON,    GPIOF_OUT_INIT_LOW, "main charge on" },
-       { COLLIE_GPIO_MBAT_ON,      GPIOF_OUT_INIT_LOW, "main battery" },
-       { COLLIE_GPIO_TMP_ON,       GPIOF_OUT_INIT_LOW, "main battery temp" },
-       { COLLIE_GPIO_BBAT_ON,      GPIOF_OUT_INIT_LOW, "backup battery" },
-};
-
-#ifdef CONFIG_PM
-static int wakeup_enabled;
-
-static int collie_bat_suspend(struct ucb1x00_dev *dev)
-{
-       /* flush all pending status updates */
-       flush_work(&bat_work);
-
-       if (device_may_wakeup(&dev->ucb->dev) &&
-           collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
-               wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
-       else
-               wakeup_enabled = 0;
-
-       return 0;
-}
-
-static int collie_bat_resume(struct ucb1x00_dev *dev)
-{
-       if (wakeup_enabled)
-               disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
-
-       /* things may have changed while we were away */
-       schedule_work(&bat_work);
-       return 0;
-}
-#else
-#define collie_bat_suspend NULL
-#define collie_bat_resume NULL
-#endif
-
-static int collie_bat_probe(struct ucb1x00_dev *dev)
-{
-       int ret;
-       struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
-
-       if (!machine_is_collie())
-               return -ENODEV;
-
-       ucb = dev->ucb;
-
-       ret = gpio_request_array(collie_batt_gpios,
-                                ARRAY_SIZE(collie_batt_gpios));
-       if (ret)
-               return ret;
-
-       mutex_init(&collie_bat_main.work_lock);
-
-       INIT_WORK(&bat_work, collie_bat_work);
-
-       psy_main_cfg.drv_data = &collie_bat_main;
-       collie_bat_main.psy = power_supply_register(&dev->ucb->dev,
-                                                   &collie_bat_main_desc,
-                                                   &psy_main_cfg);
-       if (IS_ERR(collie_bat_main.psy)) {
-               ret = PTR_ERR(collie_bat_main.psy);
-               goto err_psy_reg_main;
-       }
-
-       psy_bu_cfg.drv_data = &collie_bat_bu;
-       collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
-                                                 &collie_bat_bu_desc,
-                                                 &psy_bu_cfg);
-       if (IS_ERR(collie_bat_bu.psy)) {
-               ret = PTR_ERR(collie_bat_bu.psy);
-               goto err_psy_reg_bu;
-       }
-
-       ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
-                               collie_bat_gpio_isr,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                               "main full", &collie_bat_main);
-       if (ret)
-               goto err_irq;
-
-       device_init_wakeup(&ucb->dev, 1);
-       schedule_work(&bat_work);
-
-       return 0;
-
-err_irq:
-       power_supply_unregister(collie_bat_bu.psy);
-err_psy_reg_bu:
-       power_supply_unregister(collie_bat_main.psy);
-err_psy_reg_main:
-
-       /* see comment in collie_bat_remove */
-       cancel_work_sync(&bat_work);
-       gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
-       return ret;
-}
-
-static void collie_bat_remove(struct ucb1x00_dev *dev)
-{
-       free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
-
-       power_supply_unregister(collie_bat_bu.psy);
-       power_supply_unregister(collie_bat_main.psy);
-
-       /*
-        * Now cancel the bat_work.  We won't get any more schedules,
-        * since all sources (isr and external_power_changed) are
-        * unregistered now.
-        */
-       cancel_work_sync(&bat_work);
-       gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
-}
-
-static struct ucb1x00_driver collie_bat_driver = {
-       .add            = collie_bat_probe,
-       .remove         = collie_bat_remove,
-       .suspend        = collie_bat_suspend,
-       .resume         = collie_bat_resume,
-};
-
-static int __init collie_bat_init(void)
-{
-       return ucb1x00_register_driver(&collie_bat_driver);
-}
-
-static void __exit collie_bat_exit(void)
-{
-       ucb1x00_unregister_driver(&collie_bat_driver);
-}
-
-module_init(collie_bat_init);
-module_exit(collie_bat_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Thomas Kunze");
-MODULE_DESCRIPTION("Collie battery driver");
diff --git a/drivers/power/da9030_battery.c b/drivers/power/da9030_battery.c
deleted file mode 100644 (file)
index 5ca0f4d..0000000
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * Battery charger driver for Dialog Semiconductor DA9030
- *
- * Copyright (C) 2008 Compulab, Ltd.
- *     Mike Rapoport <mike@compulab.co.il>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/device.h>
-#include <linux/workqueue.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/da903x.h>
-
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/notifier.h>
-
-#define DA9030_FAULT_LOG               0x0a
-#define DA9030_FAULT_LOG_OVER_TEMP     (1 << 7)
-#define DA9030_FAULT_LOG_VBAT_OVER     (1 << 4)
-
-#define DA9030_CHARGE_CONTROL          0x28
-#define DA9030_CHRG_CHARGER_ENABLE     (1 << 7)
-
-#define DA9030_ADC_MAN_CONTROL         0x30
-#define DA9030_ADC_TBATREF_ENABLE      (1 << 5)
-#define DA9030_ADC_LDO_INT_ENABLE      (1 << 4)
-
-#define DA9030_ADC_AUTO_CONTROL                0x31
-#define DA9030_ADC_TBAT_ENABLE         (1 << 5)
-#define DA9030_ADC_VBAT_IN_TXON                (1 << 4)
-#define DA9030_ADC_VCH_ENABLE          (1 << 3)
-#define DA9030_ADC_ICH_ENABLE          (1 << 2)
-#define DA9030_ADC_VBAT_ENABLE         (1 << 1)
-#define DA9030_ADC_AUTO_SLEEP_ENABLE   (1 << 0)
-
-#define DA9030_VBATMON         0x32
-#define DA9030_VBATMONTXON     0x33
-#define DA9030_TBATHIGHP       0x34
-#define DA9030_TBATHIGHN       0x35
-#define DA9030_TBATLOW         0x36
-
-#define DA9030_VBAT_RES                0x41
-#define DA9030_VBATMIN_RES     0x42
-#define DA9030_VBATMINTXON_RES 0x43
-#define DA9030_ICHMAX_RES      0x44
-#define DA9030_ICHMIN_RES      0x45
-#define DA9030_ICHAVERAGE_RES  0x46
-#define DA9030_VCHMAX_RES      0x47
-#define DA9030_VCHMIN_RES      0x48
-#define DA9030_TBAT_RES                0x49
-
-struct da9030_adc_res {
-       uint8_t vbat_res;
-       uint8_t vbatmin_res;
-       uint8_t vbatmintxon;
-       uint8_t ichmax_res;
-       uint8_t ichmin_res;
-       uint8_t ichaverage_res;
-       uint8_t vchmax_res;
-       uint8_t vchmin_res;
-       uint8_t tbat_res;
-       uint8_t adc_in4_res;
-       uint8_t adc_in5_res;
-};
-
-struct da9030_battery_thresholds {
-       int tbat_low;
-       int tbat_high;
-       int tbat_restart;
-
-       int vbat_low;
-       int vbat_crit;
-       int vbat_charge_start;
-       int vbat_charge_stop;
-       int vbat_charge_restart;
-
-       int vcharge_min;
-       int vcharge_max;
-};
-
-struct da9030_charger {
-       struct power_supply *psy;
-       struct power_supply_desc psy_desc;
-
-       struct device *master;
-
-       struct da9030_adc_res adc;
-       struct delayed_work work;
-       unsigned int interval;
-
-       struct power_supply_info *battery_info;
-
-       struct da9030_battery_thresholds thresholds;
-
-       unsigned int charge_milliamp;
-       unsigned int charge_millivolt;
-
-       /* charger status */
-       bool chdet;
-       uint8_t fault;
-       int mA;
-       int mV;
-       bool is_on;
-
-       struct notifier_block nb;
-
-       /* platform callbacks for battery low and critical events */
-       void (*battery_low)(void);
-       void (*battery_critical)(void);
-
-       struct dentry *debug_file;
-};
-
-static inline int da9030_reg_to_mV(int reg)
-{
-       return ((reg * 2650) >> 8) + 2650;
-}
-
-static inline int da9030_millivolt_to_reg(int mV)
-{
-       return ((mV - 2650) << 8) / 2650;
-}
-
-static inline int da9030_reg_to_mA(int reg)
-{
-       return ((reg * 24000) >> 8) / 15;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static int bat_debug_show(struct seq_file *s, void *data)
-{
-       struct da9030_charger *charger = s->private;
-
-       seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
-       if (charger->chdet) {
-               seq_printf(s, "iset = %dmA, vset = %dmV\n",
-                          charger->mA, charger->mV);
-       }
-
-       seq_printf(s, "vbat_res = %d (%dmV)\n",
-                  charger->adc.vbat_res,
-                  da9030_reg_to_mV(charger->adc.vbat_res));
-       seq_printf(s, "vbatmin_res = %d (%dmV)\n",
-                  charger->adc.vbatmin_res,
-                  da9030_reg_to_mV(charger->adc.vbatmin_res));
-       seq_printf(s, "vbatmintxon = %d (%dmV)\n",
-                  charger->adc.vbatmintxon,
-                  da9030_reg_to_mV(charger->adc.vbatmintxon));
-       seq_printf(s, "ichmax_res = %d (%dmA)\n",
-                  charger->adc.ichmax_res,
-                  da9030_reg_to_mV(charger->adc.ichmax_res));
-       seq_printf(s, "ichmin_res = %d (%dmA)\n",
-                  charger->adc.ichmin_res,
-                  da9030_reg_to_mA(charger->adc.ichmin_res));
-       seq_printf(s, "ichaverage_res = %d (%dmA)\n",
-                  charger->adc.ichaverage_res,
-                  da9030_reg_to_mA(charger->adc.ichaverage_res));
-       seq_printf(s, "vchmax_res = %d (%dmV)\n",
-                  charger->adc.vchmax_res,
-                  da9030_reg_to_mA(charger->adc.vchmax_res));
-       seq_printf(s, "vchmin_res = %d (%dmV)\n",
-                  charger->adc.vchmin_res,
-                  da9030_reg_to_mV(charger->adc.vchmin_res));
-
-       return 0;
-}
-
-static int debug_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, bat_debug_show, inode->i_private);
-}
-
-static const struct file_operations bat_debug_fops = {
-       .open           = debug_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
-{
-       charger->debug_file = debugfs_create_file("charger", 0666, NULL,
-                                                 charger, &bat_debug_fops);
-       return charger->debug_file;
-}
-
-static void da9030_bat_remove_debugfs(struct da9030_charger *charger)
-{
-       debugfs_remove(charger->debug_file);
-}
-#else
-static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
-{
-       return NULL;
-}
-static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)
-{
-}
-#endif
-
-static inline void da9030_read_adc(struct da9030_charger *charger,
-                                  struct da9030_adc_res *adc)
-{
-       da903x_reads(charger->master, DA9030_VBAT_RES,
-                    sizeof(*adc), (uint8_t *)adc);
-}
-
-static void da9030_charger_update_state(struct da9030_charger *charger)
-{
-       uint8_t val;
-
-       da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);
-       charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;
-       charger->mA = ((val >> 3) & 0xf) * 100;
-       charger->mV = (val & 0x7) * 50 + 4000;
-
-       da9030_read_adc(charger, &charger->adc);
-       da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);
-       charger->chdet = da903x_query_status(charger->master,
-                                                    DA9030_STATUS_CHDET);
-}
-
-static void da9030_set_charge(struct da9030_charger *charger, int on)
-{
-       uint8_t val;
-
-       if (on) {
-               val = DA9030_CHRG_CHARGER_ENABLE;
-               val |= (charger->charge_milliamp / 100) << 3;
-               val |= (charger->charge_millivolt - 4000) / 50;
-               charger->is_on = 1;
-       } else {
-               val = 0;
-               charger->is_on = 0;
-       }
-
-       da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);
-
-       power_supply_changed(charger->psy);
-}
-
-static void da9030_charger_check_state(struct da9030_charger *charger)
-{
-       da9030_charger_update_state(charger);
-
-       /* we wake or boot with external power on */
-       if (!charger->is_on) {
-               if ((charger->chdet) &&
-                   (charger->adc.vbat_res <
-                    charger->thresholds.vbat_charge_start)) {
-                       da9030_set_charge(charger, 1);
-               }
-       } else {
-               /* Charger has been pulled out */
-               if (!charger->chdet) {
-                       da9030_set_charge(charger, 0);
-                       return;
-               }
-
-               if (charger->adc.vbat_res >=
-                   charger->thresholds.vbat_charge_stop) {
-                       da9030_set_charge(charger, 0);
-                       da903x_write(charger->master, DA9030_VBATMON,
-                                      charger->thresholds.vbat_charge_restart);
-               } else if (charger->adc.vbat_res >
-                          charger->thresholds.vbat_low) {
-                       /* we are charging and passed LOW_THRESH,
-                          so upate DA9030 VBAT threshold
-                        */
-                       da903x_write(charger->master, DA9030_VBATMON,
-                                    charger->thresholds.vbat_low);
-               }
-               if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||
-                   charger->adc.vchmin_res < charger->thresholds.vcharge_min ||
-                   /* Tempreture readings are negative */
-                   charger->adc.tbat_res < charger->thresholds.tbat_high ||
-                   charger->adc.tbat_res > charger->thresholds.tbat_low) {
-                       /* disable charger */
-                       da9030_set_charge(charger, 0);
-               }
-       }
-}
-
-static void da9030_charging_monitor(struct work_struct *work)
-{
-       struct da9030_charger *charger;
-
-       charger = container_of(work, struct da9030_charger, work.work);
-
-       da9030_charger_check_state(charger);
-
-       /* reschedule for the next time */
-       schedule_delayed_work(&charger->work, charger->interval);
-}
-
-static enum power_supply_property da9030_battery_props[] = {
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-};
-
-static void da9030_battery_check_status(struct da9030_charger *charger,
-                                   union power_supply_propval *val)
-{
-       if (charger->chdet) {
-               if (charger->is_on)
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       } else {
-               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-       }
-}
-
-static void da9030_battery_check_health(struct da9030_charger *charger,
-                                   union power_supply_propval *val)
-{
-       if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)
-               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-       else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)
-               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-       else
-               val->intval = POWER_SUPPLY_HEALTH_GOOD;
-}
-
-static int da9030_battery_get_property(struct power_supply *psy,
-                                  enum power_supply_property psp,
-                                  union power_supply_propval *val)
-{
-       struct da9030_charger *charger = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               da9030_battery_check_status(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               da9030_battery_check_health(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = charger->battery_info->technology;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = charger->battery_info->voltage_max_design;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = charger->battery_info->voltage_min_design;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               val->intval =
-                       da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = charger->battery_info->name;
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static void da9030_battery_vbat_event(struct da9030_charger *charger)
-{
-       da9030_read_adc(charger, &charger->adc);
-
-       if (charger->is_on)
-               return;
-
-       if (charger->adc.vbat_res < charger->thresholds.vbat_low) {
-               /* set VBAT threshold for critical */
-               da903x_write(charger->master, DA9030_VBATMON,
-                            charger->thresholds.vbat_crit);
-               if (charger->battery_low)
-                       charger->battery_low();
-       } else if (charger->adc.vbat_res <
-                  charger->thresholds.vbat_crit) {
-               /* notify the system of battery critical */
-               if (charger->battery_critical)
-                       charger->battery_critical();
-       }
-}
-
-static int da9030_battery_event(struct notifier_block *nb, unsigned long event,
-                               void *data)
-{
-       struct da9030_charger *charger =
-               container_of(nb, struct da9030_charger, nb);
-
-       switch (event) {
-       case DA9030_EVENT_CHDET:
-               cancel_delayed_work_sync(&charger->work);
-               schedule_work(&charger->work.work);
-               break;
-       case DA9030_EVENT_VBATMON:
-               da9030_battery_vbat_event(charger);
-               break;
-       case DA9030_EVENT_CHIOVER:
-       case DA9030_EVENT_TBAT:
-               da9030_set_charge(charger, 0);
-               break;
-       }
-
-       return 0;
-}
-
-static void da9030_battery_convert_thresholds(struct da9030_charger *charger,
-                                             struct da9030_battery_info *pdata)
-{
-       charger->thresholds.tbat_low = pdata->tbat_low;
-       charger->thresholds.tbat_high = pdata->tbat_high;
-       charger->thresholds.tbat_restart  = pdata->tbat_restart;
-
-       charger->thresholds.vbat_low =
-               da9030_millivolt_to_reg(pdata->vbat_low);
-       charger->thresholds.vbat_crit =
-               da9030_millivolt_to_reg(pdata->vbat_crit);
-       charger->thresholds.vbat_charge_start =
-               da9030_millivolt_to_reg(pdata->vbat_charge_start);
-       charger->thresholds.vbat_charge_stop =
-               da9030_millivolt_to_reg(pdata->vbat_charge_stop);
-       charger->thresholds.vbat_charge_restart =
-               da9030_millivolt_to_reg(pdata->vbat_charge_restart);
-
-       charger->thresholds.vcharge_min =
-               da9030_millivolt_to_reg(pdata->vcharge_min);
-       charger->thresholds.vcharge_max =
-               da9030_millivolt_to_reg(pdata->vcharge_max);
-}
-
-static void da9030_battery_setup_psy(struct da9030_charger *charger)
-{
-       struct power_supply_desc *psy_desc = &charger->psy_desc;
-       struct power_supply_info *info = charger->battery_info;
-
-       psy_desc->name = info->name;
-       psy_desc->use_for_apm = info->use_for_apm;
-       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
-       psy_desc->get_property = da9030_battery_get_property;
-
-       psy_desc->properties = da9030_battery_props;
-       psy_desc->num_properties = ARRAY_SIZE(da9030_battery_props);
-};
-
-static int da9030_battery_charger_init(struct da9030_charger *charger)
-{
-       char v[5];
-       int ret;
-
-       v[0] = v[1] = charger->thresholds.vbat_low;
-       v[2] = charger->thresholds.tbat_high;
-       v[3] = charger->thresholds.tbat_restart;
-       v[4] = charger->thresholds.tbat_low;
-
-       ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);
-       if (ret)
-               return ret;
-
-       /*
-        * Enable reference voltage supply for ADC from the LDO_INTERNAL
-        * regulator. Must be set before ADC measurements can be made.
-        */
-       ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,
-                          DA9030_ADC_LDO_INT_ENABLE |
-                          DA9030_ADC_TBATREF_ENABLE);
-       if (ret)
-               return ret;
-
-       /* enable auto ADC measuremnts */
-       return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,
-                           DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |
-                           DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |
-                           DA9030_ADC_VBAT_ENABLE |
-                           DA9030_ADC_AUTO_SLEEP_ENABLE);
-}
-
-static int da9030_battery_probe(struct platform_device *pdev)
-{
-       struct da9030_charger *charger;
-       struct power_supply_config psy_cfg = {};
-       struct da9030_battery_info *pdata = pdev->dev.platform_data;
-       int ret;
-
-       if (pdata == NULL)
-               return -EINVAL;
-
-       if (pdata->charge_milliamp >= 1500 ||
-           pdata->charge_millivolt < 4000 ||
-           pdata->charge_millivolt > 4350)
-               return -EINVAL;
-
-       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
-       if (charger == NULL)
-               return -ENOMEM;
-
-       charger->master = pdev->dev.parent;
-
-       /* 10 seconds between monitor runs unless platform defines other
-          interval */
-       charger->interval = msecs_to_jiffies(
-               (pdata->batmon_interval ? : 10) * 1000);
-
-       charger->charge_milliamp = pdata->charge_milliamp;
-       charger->charge_millivolt = pdata->charge_millivolt;
-       charger->battery_info = pdata->battery_info;
-       charger->battery_low = pdata->battery_low;
-       charger->battery_critical = pdata->battery_critical;
-
-       da9030_battery_convert_thresholds(charger, pdata);
-
-       ret = da9030_battery_charger_init(charger);
-       if (ret)
-               goto err_charger_init;
-
-       INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
-       schedule_delayed_work(&charger->work, charger->interval);
-
-       charger->nb.notifier_call = da9030_battery_event;
-       ret = da903x_register_notifier(charger->master, &charger->nb,
-                                      DA9030_EVENT_CHDET |
-                                      DA9030_EVENT_VBATMON |
-                                      DA9030_EVENT_CHIOVER |
-                                      DA9030_EVENT_TBAT);
-       if (ret)
-               goto err_notifier;
-
-       da9030_battery_setup_psy(charger);
-       psy_cfg.drv_data = charger;
-       charger->psy = power_supply_register(&pdev->dev, &charger->psy_desc,
-                                            &psy_cfg);
-       if (IS_ERR(charger->psy)) {
-               ret = PTR_ERR(charger->psy);
-               goto err_ps_register;
-       }
-
-       charger->debug_file = da9030_bat_create_debugfs(charger);
-       platform_set_drvdata(pdev, charger);
-       return 0;
-
-err_ps_register:
-       da903x_unregister_notifier(charger->master, &charger->nb,
-                                  DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
-                                  DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
-err_notifier:
-       cancel_delayed_work(&charger->work);
-
-err_charger_init:
-       return ret;
-}
-
-static int da9030_battery_remove(struct platform_device *dev)
-{
-       struct da9030_charger *charger = platform_get_drvdata(dev);
-
-       da9030_bat_remove_debugfs(charger);
-
-       da903x_unregister_notifier(charger->master, &charger->nb,
-                                  DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
-                                  DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
-       cancel_delayed_work_sync(&charger->work);
-       da9030_set_charge(charger, 0);
-       power_supply_unregister(charger->psy);
-
-       return 0;
-}
-
-static struct platform_driver da903x_battery_driver = {
-       .driver = {
-               .name   = "da903x-battery",
-       },
-       .probe = da9030_battery_probe,
-       .remove = da9030_battery_remove,
-};
-
-module_platform_driver(da903x_battery_driver);
-
-MODULE_DESCRIPTION("DA9030 battery charger driver");
-MODULE_AUTHOR("Mike Rapoport, CompuLab");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/da9052-battery.c b/drivers/power/da9052-battery.c
deleted file mode 100644 (file)
index 830ec46..0000000
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * Batttery Driver for Dialog DA9052 PMICs
- *
- * Copyright(c) 2011 Dialog Semiconductor Ltd.
- *
- * Author: David Dajun Chen <dchen@diasemi.com>
- *
- * 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;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#include <linux/delay.h>
-#include <linux/freezer.h>
-#include <linux/fs.h>
-#include <linux/jiffies.h>
-#include <linux/module.h>
-#include <linux/timer.h>
-#include <linux/uaccess.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-
-#include <linux/mfd/da9052/da9052.h>
-#include <linux/mfd/da9052/pdata.h>
-#include <linux/mfd/da9052/reg.h>
-
-/* STATIC CONFIGURATION */
-#define DA9052_BAT_CUTOFF_VOLT         2800
-#define DA9052_BAT_TSH                 62000
-#define DA9052_BAT_LOW_CAP             4
-#define DA9052_AVG_SZ                  4
-#define DA9052_VC_TBL_SZ               68
-#define DA9052_VC_TBL_REF_SZ           3
-
-#define DA9052_ISET_USB_MASK           0x0F
-#define DA9052_CHG_USB_ILIM_MASK       0x40
-#define DA9052_CHG_LIM_COLS            16
-
-#define DA9052_MEAN(x, y)              ((x + y) / 2)
-
-enum charger_type_enum {
-       DA9052_NOCHARGER = 1,
-       DA9052_CHARGER,
-};
-
-static const u16 da9052_chg_current_lim[2][DA9052_CHG_LIM_COLS] = {
-       {70,  80,  90,  100, 110, 120, 400,  450,
-        500, 550, 600, 650, 700, 900, 1100, 1300},
-       {80,  90,  100, 110,  120,  400,  450,  500,
-        550, 600, 800, 1000, 1200, 1400, 1600, 1800},
-};
-
-static const u16 vc_tbl_ref[3] = {10, 25, 40};
-/* Lookup table for voltage vs capacity */
-static u32 const vc_tbl[3][68][2] = {
-       /* For temperature 10 degree Celsius */
-       {
-       {4082, 100}, {4036, 98},
-       {4020, 96}, {4008, 95},
-       {3997, 93}, {3983, 91},
-       {3964, 90}, {3943, 88},
-       {3926, 87}, {3912, 85},
-       {3900, 84}, {3890, 82},
-       {3881, 80}, {3873, 79},
-       {3865, 77}, {3857, 76},
-       {3848, 74}, {3839, 73},
-       {3829, 71}, {3820, 70},
-       {3811, 68}, {3802, 67},
-       {3794, 65}, {3785, 64},
-       {3778, 62}, {3770, 61},
-       {3763, 59}, {3756, 58},
-       {3750, 56}, {3744, 55},
-       {3738, 53}, {3732, 52},
-       {3727, 50}, {3722, 49},
-       {3717, 47}, {3712, 46},
-       {3708, 44}, {3703, 43},
-       {3700, 41}, {3696, 40},
-       {3693, 38}, {3691, 37},
-       {3688, 35}, {3686, 34},
-       {3683, 32}, {3681, 31},
-       {3678, 29}, {3675, 28},
-       {3672, 26}, {3669, 25},
-       {3665, 23}, {3661, 22},
-       {3656, 21}, {3651, 19},
-       {3645, 18}, {3639, 16},
-       {3631, 15}, {3622, 13},
-       {3611, 12}, {3600, 10},
-       {3587, 9}, {3572, 7},
-       {3548, 6}, {3503, 5},
-       {3420, 3}, {3268, 2},
-       {2992, 1}, {2746, 0}
-       },
-       /* For temperature 25 degree Celsius */
-       {
-       {4102, 100}, {4065, 98},
-       {4048, 96}, {4034, 95},
-       {4021, 93}, {4011, 92},
-       {4001, 90}, {3986, 88},
-       {3968, 87}, {3952, 85},
-       {3938, 84}, {3926, 82},
-       {3916, 81}, {3908, 79},
-       {3900, 77}, {3892, 76},
-       {3883, 74}, {3874, 73},
-       {3864, 71}, {3855, 70},
-       {3846, 68}, {3836, 67},
-       {3827, 65}, {3819, 64},
-       {3810, 62}, {3801, 61},
-       {3793, 59}, {3786, 58},
-       {3778, 56}, {3772, 55},
-       {3765, 53}, {3759, 52},
-       {3754, 50}, {3748, 49},
-       {3743, 47}, {3738, 46},
-       {3733, 44}, {3728, 43},
-       {3724, 41}, {3720, 40},
-       {3716, 38}, {3712, 37},
-       {3709, 35}, {3706, 34},
-       {3703, 33}, {3701, 31},
-       {3698, 30}, {3696, 28},
-       {3693, 27}, {3690, 25},
-       {3687, 24}, {3683, 22},
-       {3680, 21}, {3675, 19},
-       {3671, 18}, {3666, 17},
-       {3660, 15}, {3654, 14},
-       {3647, 12}, {3639, 11},
-       {3630, 9}, {3621, 8},
-       {3613, 6}, {3606, 5},
-       {3597, 4}, {3582, 2},
-       {3546, 1}, {2747, 0}
-       },
-       /* For temperature 40 degree Celsius */
-       {
-       {4114, 100}, {4081, 98},
-       {4065, 96}, {4050, 95},
-       {4036, 93}, {4024, 92},
-       {4013, 90}, {4002, 88},
-       {3990, 87}, {3976, 85},
-       {3962, 84}, {3950, 82},
-       {3939, 81}, {3930, 79},
-       {3921, 77}, {3912, 76},
-       {3902, 74}, {3893, 73},
-       {3883, 71}, {3874, 70},
-       {3865, 68}, {3856, 67},
-       {3847, 65}, {3838, 64},
-       {3829, 62}, {3820, 61},
-       {3812, 59}, {3803, 58},
-       {3795, 56}, {3787, 55},
-       {3780, 53}, {3773, 52},
-       {3767, 50}, {3761, 49},
-       {3756, 47}, {3751, 46},
-       {3746, 44}, {3741, 43},
-       {3736, 41}, {3732, 40},
-       {3728, 38}, {3724, 37},
-       {3720, 35}, {3716, 34},
-       {3713, 33}, {3710, 31},
-       {3707, 30}, {3704, 28},
-       {3701, 27}, {3698, 25},
-       {3695, 24}, {3691, 22},
-       {3686, 21}, {3681, 19},
-       {3676, 18}, {3671, 17},
-       {3666, 15}, {3661, 14},
-       {3655, 12}, {3648, 11},
-       {3640, 9}, {3632, 8},
-       {3622, 6}, {3616, 5},
-       {3611, 4}, {3604, 2},
-       {3594, 1}, {2747, 0}
-       }
-};
-
-struct da9052_battery {
-       struct da9052 *da9052;
-       struct power_supply *psy;
-       struct notifier_block nb;
-       int charger_type;
-       int status;
-       int health;
-};
-
-static inline int volt_reg_to_mV(int value)
-{
-       return ((value * 1000) / 512) + 2500;
-}
-
-static inline int ichg_reg_to_mA(int value)
-{
-       return (value * 3900) / 1000;
-}
-
-static int da9052_read_chgend_current(struct da9052_battery *bat,
-                                      int *current_mA)
-{
-       int ret;
-
-       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
-               return -EINVAL;
-
-       ret = da9052_reg_read(bat->da9052, DA9052_ICHG_END_REG);
-       if (ret < 0)
-               return ret;
-
-       *current_mA = ichg_reg_to_mA(ret & DA9052_ICHGEND_ICHGEND);
-
-       return 0;
-}
-
-static int da9052_read_chg_current(struct da9052_battery *bat, int *current_mA)
-{
-       int ret;
-
-       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
-               return -EINVAL;
-
-       ret = da9052_reg_read(bat->da9052, DA9052_ICHG_AV_REG);
-       if (ret < 0)
-               return ret;
-
-       *current_mA = ichg_reg_to_mA(ret & DA9052_ICHGAV_ICHGAV);
-
-       return 0;
-}
-
-static int da9052_bat_check_status(struct da9052_battery *bat, int *status)
-{
-       u8 v[2] = {0, 0};
-       u8 bat_status;
-       u8 chg_end;
-       int ret;
-       int chg_current;
-       int chg_end_current;
-       bool dcinsel;
-       bool dcindet;
-       bool vbussel;
-       bool vbusdet;
-       bool dc;
-       bool vbus;
-
-       ret = da9052_group_read(bat->da9052, DA9052_STATUS_A_REG, 2, v);
-       if (ret < 0)
-               return ret;
-
-       bat_status = v[0];
-       chg_end = v[1];
-
-       dcinsel = bat_status & DA9052_STATUSA_DCINSEL;
-       dcindet = bat_status & DA9052_STATUSA_DCINDET;
-       vbussel = bat_status & DA9052_STATUSA_VBUSSEL;
-       vbusdet = bat_status & DA9052_STATUSA_VBUSDET;
-       dc = dcinsel && dcindet;
-       vbus = vbussel && vbusdet;
-
-       /* Preference to WALL(DCIN) charger unit */
-       if (dc || vbus) {
-               bat->charger_type = DA9052_CHARGER;
-
-               /* If charging end flag is set and Charging current is greater
-                * than charging end limit then battery is charging
-               */
-               if ((chg_end & DA9052_STATUSB_CHGEND) != 0) {
-                       ret = da9052_read_chg_current(bat, &chg_current);
-                       if (ret < 0)
-                               return ret;
-                       ret = da9052_read_chgend_current(bat, &chg_end_current);
-                       if (ret < 0)
-                               return ret;
-
-                       if (chg_current >= chg_end_current)
-                               bat->status = POWER_SUPPLY_STATUS_CHARGING;
-                       else
-                               bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               } else {
-                       /* If Charging end flag is cleared then battery is
-                        * charging
-                       */
-                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
-               }
-       } else if (dcindet || vbusdet) {
-                       bat->charger_type = DA9052_CHARGER;
-                       bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       } else {
-               bat->charger_type = DA9052_NOCHARGER;
-               bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
-       }
-
-       if (status != NULL)
-               *status = bat->status;
-       return 0;
-}
-
-static int da9052_bat_read_volt(struct da9052_battery *bat, int *volt_mV)
-{
-       int volt;
-
-       volt = da9052_adc_manual_read(bat->da9052, DA9052_ADC_MAN_MUXSEL_VBAT);
-       if (volt < 0)
-               return volt;
-
-       *volt_mV = volt_reg_to_mV(volt);
-
-       return 0;
-}
-
-static int da9052_bat_check_presence(struct da9052_battery *bat, int *illegal)
-{
-       int bat_temp;
-
-       bat_temp = da9052_adc_read_temp(bat->da9052);
-       if (bat_temp < 0)
-               return bat_temp;
-
-       if (bat_temp > DA9052_BAT_TSH)
-               *illegal = 1;
-       else
-               *illegal = 0;
-
-       return 0;
-}
-
-static int da9052_bat_interpolate(int vbat_lower, int  vbat_upper,
-                                  int level_lower, int level_upper,
-                                  int bat_voltage)
-{
-       int tmp;
-
-       tmp = ((level_upper - level_lower) * 1000) / (vbat_upper - vbat_lower);
-       tmp = level_lower + (((bat_voltage - vbat_lower) * tmp) / 1000);
-
-       return tmp;
-}
-
-static unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp)
-{
-       int i;
-
-       if (adc_temp <= vc_tbl_ref[0])
-               return 0;
-
-       if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1])
-               return DA9052_VC_TBL_REF_SZ - 1;
-
-       for (i = 0; i < DA9052_VC_TBL_REF_SZ - 1; i++) {
-               if ((adc_temp > vc_tbl_ref[i]) &&
-                   (adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])))
-                               return i;
-               if ((adc_temp > DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1]))
-                    && (adc_temp <= vc_tbl_ref[i]))
-                               return i + 1;
-       }
-       /*
-        * For some reason authors of the driver didn't presume that we can
-        * end up here. It might be OK, but might be not, no one knows for
-        * sure. Go check your battery, is it on fire?
-        */
-       WARN_ON(1);
-       return 0;
-}
-
-static int da9052_bat_read_capacity(struct da9052_battery *bat, int *capacity)
-{
-       int adc_temp;
-       int bat_voltage;
-       int vbat_lower;
-       int vbat_upper;
-       int level_upper;
-       int level_lower;
-       int ret;
-       int flag;
-       int i = 0;
-       int j;
-
-       ret = da9052_bat_read_volt(bat, &bat_voltage);
-       if (ret < 0)
-               return ret;
-
-       adc_temp = da9052_adc_read_temp(bat->da9052);
-       if (adc_temp < 0)
-               return adc_temp;
-
-       i = da9052_determine_vc_tbl_index(adc_temp);
-
-       if (bat_voltage >= vc_tbl[i][0][0]) {
-               *capacity = 100;
-               return 0;
-       }
-       if (bat_voltage <= vc_tbl[i][DA9052_VC_TBL_SZ - 1][0]) {
-               *capacity = 0;
-               return 0;
-       }
-       flag = 0;
-
-       for (j = 0; j < (DA9052_VC_TBL_SZ-1); j++) {
-               if ((bat_voltage <= vc_tbl[i][j][0]) &&
-                   (bat_voltage >= vc_tbl[i][j + 1][0])) {
-                       vbat_upper = vc_tbl[i][j][0];
-                       vbat_lower = vc_tbl[i][j + 1][0];
-                       level_upper = vc_tbl[i][j][1];
-                       level_lower = vc_tbl[i][j + 1][1];
-                       flag = 1;
-                       break;
-               }
-       }
-       if (!flag)
-               return -EIO;
-
-       *capacity = da9052_bat_interpolate(vbat_lower, vbat_upper, level_lower,
-                                          level_upper, bat_voltage);
-
-       return 0;
-}
-
-static int da9052_bat_check_health(struct da9052_battery *bat, int *health)
-{
-       int ret;
-       int bat_illegal;
-       int capacity;
-
-       ret = da9052_bat_check_presence(bat, &bat_illegal);
-       if (ret < 0)
-               return ret;
-
-       if (bat_illegal) {
-               bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
-               return 0;
-       }
-
-       if (bat->health != POWER_SUPPLY_HEALTH_OVERHEAT) {
-               ret = da9052_bat_read_capacity(bat, &capacity);
-               if (ret < 0)
-                       return ret;
-               if (capacity < DA9052_BAT_LOW_CAP)
-                       bat->health = POWER_SUPPLY_HEALTH_DEAD;
-               else
-                       bat->health = POWER_SUPPLY_HEALTH_GOOD;
-       }
-
-       *health = bat->health;
-
-       return 0;
-}
-
-static irqreturn_t da9052_bat_irq(int irq, void *data)
-{
-       struct da9052_battery *bat = data;
-       int virq;
-
-       virq = regmap_irq_get_virq(bat->da9052->irq_data, irq);
-       irq -= virq;
-
-       if (irq == DA9052_IRQ_CHGEND)
-               bat->status = POWER_SUPPLY_STATUS_FULL;
-       else
-               da9052_bat_check_status(bat, NULL);
-
-       if (irq == DA9052_IRQ_CHGEND || irq == DA9052_IRQ_DCIN ||
-           irq == DA9052_IRQ_VBUS || irq == DA9052_IRQ_TBAT) {
-               power_supply_changed(bat->psy);
-       }
-
-       return IRQ_HANDLED;
-}
-
-static int da9052_USB_current_notifier(struct notifier_block *nb,
-                                       unsigned long events, void *data)
-{
-       u8 row;
-       u8 col;
-       int *current_mA = data;
-       int ret;
-       struct da9052_battery *bat = container_of(nb, struct da9052_battery,
-                                                 nb);
-
-       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
-               return -EPERM;
-
-       ret = da9052_reg_read(bat->da9052, DA9052_CHGBUCK_REG);
-       if (ret & DA9052_CHG_USB_ILIM_MASK)
-               return -EPERM;
-
-       if (bat->da9052->chip_id == DA9052)
-               row = 0;
-       else
-               row = 1;
-
-       if (*current_mA < da9052_chg_current_lim[row][0] ||
-           *current_mA > da9052_chg_current_lim[row][DA9052_CHG_LIM_COLS - 1])
-               return -EINVAL;
-
-       for (col = 0; col <= DA9052_CHG_LIM_COLS - 1 ; col++) {
-               if (*current_mA <= da9052_chg_current_lim[row][col])
-                       break;
-       }
-
-       return da9052_reg_update(bat->da9052, DA9052_ISET_REG,
-                                DA9052_ISET_USB_MASK, col);
-}
-
-static int da9052_bat_get_property(struct power_supply *psy,
-                                   enum power_supply_property psp,
-                                   union power_supply_propval *val)
-{
-       int ret;
-       int illegal;
-       struct da9052_battery *bat = power_supply_get_drvdata(psy);
-
-       ret = da9052_bat_check_presence(bat, &illegal);
-       if (ret < 0)
-               return ret;
-
-       if (illegal && psp != POWER_SUPPLY_PROP_PRESENT)
-               return -ENODEV;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = da9052_bat_check_status(bat, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval =
-                       (bat->charger_type == DA9052_NOCHARGER) ? 0 : 1;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               ret = da9052_bat_check_presence(bat, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = da9052_bat_check_health(bat, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = DA9052_BAT_CUTOFF_VOLT * 1000;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               ret = da9052_bat_read_volt(bat, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               ret = da9052_read_chg_current(bat, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = da9052_bat_read_capacity(bat, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = da9052_adc_read_temp(bat->da9052);
-               ret = val->intval;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return ret;
-}
-
-static enum power_supply_property da9052_bat_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-};
-
-static struct power_supply_desc psy_desc = {
-       .name           = "da9052-bat",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = da9052_bat_props,
-       .num_properties = ARRAY_SIZE(da9052_bat_props),
-       .get_property   = da9052_bat_get_property,
-};
-
-static char *da9052_bat_irqs[] = {
-       "BATT TEMP",
-       "DCIN DET",
-       "DCIN REM",
-       "VBUS DET",
-       "VBUS REM",
-       "CHG END",
-};
-
-static int da9052_bat_irq_bits[] = {
-       DA9052_IRQ_TBAT,
-       DA9052_IRQ_DCIN,
-       DA9052_IRQ_DCINREM,
-       DA9052_IRQ_VBUS,
-       DA9052_IRQ_VBUSREM,
-       DA9052_IRQ_CHGEND,
-};
-
-static s32 da9052_bat_probe(struct platform_device *pdev)
-{
-       struct da9052_pdata *pdata;
-       struct da9052_battery *bat;
-       struct power_supply_config psy_cfg = {};
-       int ret;
-       int i;
-
-       bat = devm_kzalloc(&pdev->dev, sizeof(struct da9052_battery),
-                               GFP_KERNEL);
-       if (!bat)
-               return -ENOMEM;
-
-       psy_cfg.drv_data = bat;
-
-       bat->da9052 = dev_get_drvdata(pdev->dev.parent);
-       bat->charger_type = DA9052_NOCHARGER;
-       bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
-       bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
-       bat->nb.notifier_call = da9052_USB_current_notifier;
-
-       pdata = bat->da9052->dev->platform_data;
-       if (pdata != NULL && pdata->use_for_apm)
-               psy_desc.use_for_apm = pdata->use_for_apm;
-       else
-               psy_desc.use_for_apm = 1;
-
-       for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) {
-               ret = da9052_request_irq(bat->da9052,
-                               da9052_bat_irq_bits[i], da9052_bat_irqs[i],
-                               da9052_bat_irq, bat);
-
-               if (ret != 0) {
-                       dev_err(bat->da9052->dev,
-                               "DA9052 failed to request %s IRQ: %d\n",
-                               da9052_bat_irqs[i], ret);
-                       goto err;
-               }
-       }
-
-       bat->psy = power_supply_register(&pdev->dev, &psy_desc, &psy_cfg);
-       if (IS_ERR(bat->psy)) {
-               ret = PTR_ERR(bat->psy);
-               goto err;
-       }
-
-       platform_set_drvdata(pdev, bat);
-       return 0;
-
-err:
-       while (--i >= 0)
-               da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
-
-       return ret;
-}
-static int da9052_bat_remove(struct platform_device *pdev)
-{
-       int i;
-       struct da9052_battery *bat = platform_get_drvdata(pdev);
-
-       for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++)
-               da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
-
-       power_supply_unregister(bat->psy);
-
-       return 0;
-}
-
-static struct platform_driver da9052_bat_driver = {
-       .probe = da9052_bat_probe,
-       .remove = da9052_bat_remove,
-       .driver = {
-               .name = "da9052-bat",
-       },
-};
-module_platform_driver(da9052_bat_driver);
-
-MODULE_DESCRIPTION("DA9052 BAT Device Driver");
-MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:da9052-bat");
diff --git a/drivers/power/da9150-charger.c b/drivers/power/da9150-charger.c
deleted file mode 100644 (file)
index 6009981..0000000
+++ /dev/null
@@ -1,694 +0,0 @@
-/*
- * DA9150 Charger Driver
- *
- * Copyright (c) 2014 Dialog Semiconductor
- *
- * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * 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;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/interrupt.h>
-#include <linux/power_supply.h>
-#include <linux/notifier.h>
-#include <linux/usb/phy.h>
-#include <linux/iio/consumer.h>
-#include <linux/mfd/da9150/core.h>
-#include <linux/mfd/da9150/registers.h>
-
-/* Private data */
-struct da9150_charger {
-       struct da9150 *da9150;
-       struct device *dev;
-
-       struct power_supply *usb;
-       struct power_supply *battery;
-       struct power_supply *supply_online;
-
-       struct usb_phy *usb_phy;
-       struct notifier_block otg_nb;
-       struct work_struct otg_work;
-       unsigned long usb_event;
-
-       struct iio_channel *ibus_chan;
-       struct iio_channel *vbus_chan;
-       struct iio_channel *tjunc_chan;
-       struct iio_channel *vbat_chan;
-};
-
-static inline int da9150_charger_supply_online(struct da9150_charger *charger,
-                                              struct power_supply *psy,
-                                              union power_supply_propval *val)
-{
-       val->intval = (psy == charger->supply_online) ? 1 : 0;
-
-       return 0;
-}
-
-/* Charger Properties */
-static int da9150_charger_vbus_voltage_now(struct da9150_charger *charger,
-                                          union power_supply_propval *val)
-{
-       int v_val, ret;
-
-       /* Read processed value - mV units */
-       ret = iio_read_channel_processed(charger->vbus_chan, &v_val);
-       if (ret < 0)
-               return ret;
-
-       /* Convert voltage to expected uV units */
-       val->intval = v_val * 1000;
-
-       return 0;
-}
-
-static int da9150_charger_ibus_current_avg(struct da9150_charger *charger,
-                                          union power_supply_propval *val)
-{
-       int i_val, ret;
-
-       /* Read processed value - mA units */
-       ret = iio_read_channel_processed(charger->ibus_chan, &i_val);
-       if (ret < 0)
-               return ret;
-
-       /* Convert current to expected uA units */
-       val->intval = i_val * 1000;
-
-       return 0;
-}
-
-static int da9150_charger_tjunc_temp(struct da9150_charger *charger,
-                                    union power_supply_propval *val)
-{
-       int t_val, ret;
-
-       /* Read processed value - 0.001 degrees C units */
-       ret = iio_read_channel_processed(charger->tjunc_chan, &t_val);
-       if (ret < 0)
-               return ret;
-
-       /* Convert temp to expect 0.1 degrees C units */
-       val->intval = t_val / 100;
-
-       return 0;
-}
-
-static enum power_supply_property da9150_charger_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static int da9150_charger_get_prop(struct power_supply *psy,
-                                  enum power_supply_property psp,
-                                  union power_supply_propval *val)
-{
-       struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent);
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = da9150_charger_supply_online(charger, psy, val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = da9150_charger_vbus_voltage_now(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               ret = da9150_charger_ibus_current_avg(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = da9150_charger_tjunc_temp(charger, val);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-/* Battery Properties */
-static int da9150_charger_battery_status(struct da9150_charger *charger,
-                                        union power_supply_propval *val)
-{
-       u8 reg;
-
-       /* Check to see if battery is discharging */
-       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);
-
-       if (((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_OFF) ||
-           ((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_WAIT)) {
-               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-
-               return 0;
-       }
-
-       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
-
-       /* Now check for other states */
-       switch (reg & DA9150_CHG_STAT_MASK) {
-       case DA9150_CHG_STAT_ACT:
-       case DA9150_CHG_STAT_PRE:
-       case DA9150_CHG_STAT_CC:
-       case DA9150_CHG_STAT_CV:
-               val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               break;
-       case DA9150_CHG_STAT_OFF:
-       case DA9150_CHG_STAT_SUSP:
-       case DA9150_CHG_STAT_TEMP:
-       case DA9150_CHG_STAT_TIME:
-       case DA9150_CHG_STAT_BAT:
-               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               break;
-       case DA9150_CHG_STAT_FULL:
-               val->intval = POWER_SUPPLY_STATUS_FULL;
-               break;
-       default:
-               val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-               break;
-       }
-
-       return 0;
-}
-
-static int da9150_charger_battery_health(struct da9150_charger *charger,
-                                        union power_supply_propval *val)
-{
-       u8 reg;
-
-       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
-
-       /* Check if temperature limit reached */
-       switch (reg & DA9150_CHG_TEMP_MASK) {
-       case DA9150_CHG_TEMP_UNDER:
-               val->intval = POWER_SUPPLY_HEALTH_COLD;
-               return 0;
-       case DA9150_CHG_TEMP_OVER:
-               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               return 0;
-       default:
-               break;
-       }
-
-       /* Check for other health states */
-       switch (reg & DA9150_CHG_STAT_MASK) {
-       case DA9150_CHG_STAT_ACT:
-       case DA9150_CHG_STAT_PRE:
-               val->intval = POWER_SUPPLY_HEALTH_DEAD;
-               break;
-       case DA9150_CHG_STAT_TIME:
-               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               break;
-       default:
-               val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       }
-
-       return 0;
-}
-
-static int da9150_charger_battery_present(struct da9150_charger *charger,
-                                         union power_supply_propval *val)
-{
-       u8 reg;
-
-       /* Check if battery present or removed */
-       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
-       if ((reg & DA9150_CHG_STAT_MASK) == DA9150_CHG_STAT_BAT)
-               val->intval = 0;
-       else
-               val->intval = 1;
-
-       return 0;
-}
-
-static int da9150_charger_battery_charge_type(struct da9150_charger *charger,
-                                             union power_supply_propval *val)
-{
-       u8 reg;
-
-       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
-
-       switch (reg & DA9150_CHG_STAT_MASK) {
-       case DA9150_CHG_STAT_CC:
-               val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
-               break;
-       case DA9150_CHG_STAT_ACT:
-       case DA9150_CHG_STAT_PRE:
-       case DA9150_CHG_STAT_CV:
-               val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-               break;
-       default:
-               val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
-               break;
-       }
-
-       return 0;
-}
-
-static int da9150_charger_battery_voltage_min(struct da9150_charger *charger,
-                                             union power_supply_propval *val)
-{
-       u8 reg;
-
-       reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_C);
-
-       /* Value starts at 2500 mV, 50 mV increments, presented in uV */
-       val->intval = ((reg & DA9150_CHG_VFAULT_MASK) * 50000) + 2500000;
-
-       return 0;
-}
-
-static int da9150_charger_battery_voltage_now(struct da9150_charger *charger,
-                                             union power_supply_propval *val)
-{
-       int v_val, ret;
-
-       /* Read processed value - mV units */
-       ret = iio_read_channel_processed(charger->vbat_chan, &v_val);
-       if (ret < 0)
-               return ret;
-
-       val->intval = v_val * 1000;
-
-       return 0;
-}
-
-static int da9150_charger_battery_current_max(struct da9150_charger *charger,
-                                             union power_supply_propval *val)
-{
-       int reg;
-
-       reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_D);
-
-       /* 25mA increments */
-       val->intval = reg * 25000;
-
-       return 0;
-}
-
-static int da9150_charger_battery_voltage_max(struct da9150_charger *charger,
-                                             union power_supply_propval *val)
-{
-       u8 reg;
-
-       reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_B);
-
-       /* Value starts at 3650 mV, 25 mV increments, presented in uV */
-       val->intval = ((reg & DA9150_CHG_VBAT_MASK) * 25000) + 3650000;
-       return 0;
-}
-
-static enum power_supply_property da9150_charger_bat_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
-};
-
-static int da9150_charger_battery_get_prop(struct power_supply *psy,
-                                          enum power_supply_property psp,
-                                          union power_supply_propval *val)
-{
-       struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent);
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = da9150_charger_battery_status(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = da9150_charger_supply_online(charger, psy, val);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = da9150_charger_battery_health(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               ret = da9150_charger_battery_present(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               ret = da9150_charger_battery_charge_type(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               ret = da9150_charger_battery_voltage_min(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = da9150_charger_battery_voltage_now(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
-               ret = da9150_charger_battery_current_max(charger, val);
-               break;
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
-               ret = da9150_charger_battery_voltage_max(charger, val);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static irqreturn_t da9150_charger_chg_irq(int irq, void *data)
-{
-       struct da9150_charger *charger = data;
-
-       power_supply_changed(charger->battery);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t da9150_charger_tjunc_irq(int irq, void *data)
-{
-       struct da9150_charger *charger = data;
-
-       /* Nothing we can really do except report this. */
-       dev_crit(charger->dev, "TJunc over temperature!!!\n");
-       power_supply_changed(charger->usb);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t da9150_charger_vfault_irq(int irq, void *data)
-{
-       struct da9150_charger *charger = data;
-
-       /* Nothing we can really do except report this. */
-       dev_crit(charger->dev, "VSYS under voltage!!!\n");
-       power_supply_changed(charger->usb);
-       power_supply_changed(charger->battery);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t da9150_charger_vbus_irq(int irq, void *data)
-{
-       struct da9150_charger *charger = data;
-       u8 reg;
-
-       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);
-
-       /* Charger plugged in or battery only */
-       switch (reg & DA9150_VBUS_STAT_MASK) {
-       case DA9150_VBUS_STAT_OFF:
-       case DA9150_VBUS_STAT_WAIT:
-               charger->supply_online = charger->battery;
-               break;
-       case DA9150_VBUS_STAT_CHG:
-               charger->supply_online = charger->usb;
-               break;
-       default:
-               dev_warn(charger->dev, "Unknown VBUS state - reg = 0x%x\n",
-                        reg);
-               charger->supply_online = NULL;
-               break;
-       }
-
-       power_supply_changed(charger->usb);
-       power_supply_changed(charger->battery);
-
-       return IRQ_HANDLED;
-}
-
-static void da9150_charger_otg_work(struct work_struct *data)
-{
-       struct da9150_charger *charger =
-               container_of(data, struct da9150_charger, otg_work);
-
-       switch (charger->usb_event) {
-       case USB_EVENT_ID:
-               /* Enable OTG Boost */
-               da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,
-                               DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_OTG);
-               break;
-       case USB_EVENT_NONE:
-               /* Revert to charge mode */
-               power_supply_changed(charger->usb);
-               power_supply_changed(charger->battery);
-               da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,
-                               DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_CHG);
-               break;
-       }
-}
-
-static int da9150_charger_otg_ncb(struct notifier_block *nb, unsigned long val,
-                                 void *priv)
-{
-       struct da9150_charger *charger =
-               container_of(nb, struct da9150_charger, otg_nb);
-
-       dev_dbg(charger->dev, "DA9150 OTG notify %lu\n", val);
-
-       charger->usb_event = val;
-       schedule_work(&charger->otg_work);
-
-       return NOTIFY_OK;
-}
-
-static int da9150_charger_register_irq(struct platform_device *pdev,
-                                      irq_handler_t handler,
-                                      const char *irq_name)
-{
-       struct device *dev = &pdev->dev;
-       struct da9150_charger *charger = platform_get_drvdata(pdev);
-       int irq, ret;
-
-       irq = platform_get_irq_byname(pdev, irq_name);
-       if (irq < 0) {
-               dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq);
-               return irq;
-       }
-
-       ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, irq_name,
-                                  charger);
-       if (ret)
-               dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
-
-       return ret;
-}
-
-static void da9150_charger_unregister_irq(struct platform_device *pdev,
-                                         const char *irq_name)
-{
-       struct device *dev = &pdev->dev;
-       struct da9150_charger *charger = platform_get_drvdata(pdev);
-       int irq;
-
-       irq = platform_get_irq_byname(pdev, irq_name);
-       if (irq < 0) {
-               dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq);
-               return;
-       }
-
-       free_irq(irq, charger);
-}
-
-static const struct power_supply_desc usb_desc = {
-       .name           = "da9150-usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .properties     = da9150_charger_props,
-       .num_properties = ARRAY_SIZE(da9150_charger_props),
-       .get_property   = da9150_charger_get_prop,
-};
-
-static const struct power_supply_desc battery_desc = {
-       .name           = "da9150-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = da9150_charger_bat_props,
-       .num_properties = ARRAY_SIZE(da9150_charger_bat_props),
-       .get_property   = da9150_charger_battery_get_prop,
-};
-
-static int da9150_charger_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct da9150 *da9150 = dev_get_drvdata(dev->parent);
-       struct da9150_charger *charger;
-       u8 reg;
-       int ret;
-
-       charger = devm_kzalloc(dev, sizeof(struct da9150_charger), GFP_KERNEL);
-       if (!charger)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, charger);
-       charger->da9150 = da9150;
-       charger->dev = dev;
-
-       /* Acquire ADC channels */
-       charger->ibus_chan = iio_channel_get(dev, "CHAN_IBUS");
-       if (IS_ERR(charger->ibus_chan)) {
-               ret = PTR_ERR(charger->ibus_chan);
-               goto ibus_chan_fail;
-       }
-
-       charger->vbus_chan = iio_channel_get(dev, "CHAN_VBUS");
-       if (IS_ERR(charger->vbus_chan)) {
-               ret = PTR_ERR(charger->vbus_chan);
-               goto vbus_chan_fail;
-       }
-
-       charger->tjunc_chan = iio_channel_get(dev, "CHAN_TJUNC");
-       if (IS_ERR(charger->tjunc_chan)) {
-               ret = PTR_ERR(charger->tjunc_chan);
-               goto tjunc_chan_fail;
-       }
-
-       charger->vbat_chan = iio_channel_get(dev, "CHAN_VBAT");
-       if (IS_ERR(charger->vbat_chan)) {
-               ret = PTR_ERR(charger->vbat_chan);
-               goto vbat_chan_fail;
-       }
-
-       /* Register power supplies */
-       charger->usb = power_supply_register(dev, &usb_desc, NULL);
-       if (IS_ERR(charger->usb)) {
-               ret = PTR_ERR(charger->usb);
-               goto usb_fail;
-       }
-
-       charger->battery = power_supply_register(dev, &battery_desc, NULL);
-       if (IS_ERR(charger->battery)) {
-               ret = PTR_ERR(charger->battery);
-               goto battery_fail;
-       }
-
-       /* Get initial online supply */
-       reg = da9150_reg_read(da9150, DA9150_STATUS_H);
-
-       switch (reg & DA9150_VBUS_STAT_MASK) {
-       case DA9150_VBUS_STAT_OFF:
-       case DA9150_VBUS_STAT_WAIT:
-               charger->supply_online = charger->battery;
-               break;
-       case DA9150_VBUS_STAT_CHG:
-               charger->supply_online = charger->usb;
-               break;
-       default:
-               dev_warn(dev, "Unknown VBUS state - reg = 0x%x\n", reg);
-               charger->supply_online = NULL;
-               break;
-       }
-
-       /* Setup OTG reporting & configuration */
-       charger->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
-       if (!IS_ERR_OR_NULL(charger->usb_phy)) {
-               INIT_WORK(&charger->otg_work, da9150_charger_otg_work);
-               charger->otg_nb.notifier_call = da9150_charger_otg_ncb;
-               usb_register_notifier(charger->usb_phy, &charger->otg_nb);
-       }
-
-       /* Register IRQs */
-       ret = da9150_charger_register_irq(pdev, da9150_charger_chg_irq,
-                                         "CHG_STATUS");
-       if (ret < 0)
-               goto chg_irq_fail;
-
-       ret = da9150_charger_register_irq(pdev, da9150_charger_tjunc_irq,
-                                         "CHG_TJUNC");
-       if (ret < 0)
-               goto tjunc_irq_fail;
-
-       ret = da9150_charger_register_irq(pdev, da9150_charger_vfault_irq,
-                                         "CHG_VFAULT");
-       if (ret < 0)
-               goto vfault_irq_fail;
-
-       ret = da9150_charger_register_irq(pdev, da9150_charger_vbus_irq,
-                                         "CHG_VBUS");
-       if (ret < 0)
-               goto vbus_irq_fail;
-
-       return 0;
-
-
-vbus_irq_fail:
-       da9150_charger_unregister_irq(pdev, "CHG_VFAULT");
-vfault_irq_fail:
-       da9150_charger_unregister_irq(pdev, "CHG_TJUNC");
-tjunc_irq_fail:
-       da9150_charger_unregister_irq(pdev, "CHG_STATUS");
-chg_irq_fail:
-       if (!IS_ERR_OR_NULL(charger->usb_phy))
-               usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);
-battery_fail:
-       power_supply_unregister(charger->usb);
-
-usb_fail:
-       iio_channel_release(charger->vbat_chan);
-
-vbat_chan_fail:
-       iio_channel_release(charger->tjunc_chan);
-
-tjunc_chan_fail:
-       iio_channel_release(charger->vbus_chan);
-
-vbus_chan_fail:
-       iio_channel_release(charger->ibus_chan);
-
-ibus_chan_fail:
-       return ret;
-}
-
-static int da9150_charger_remove(struct platform_device *pdev)
-{
-       struct da9150_charger *charger = platform_get_drvdata(pdev);
-       int irq;
-
-       /* Make sure IRQs are released before unregistering power supplies */
-       irq = platform_get_irq_byname(pdev, "CHG_VBUS");
-       free_irq(irq, charger);
-
-       irq = platform_get_irq_byname(pdev, "CHG_VFAULT");
-       free_irq(irq, charger);
-
-       irq = platform_get_irq_byname(pdev, "CHG_TJUNC");
-       free_irq(irq, charger);
-
-       irq = platform_get_irq_byname(pdev, "CHG_STATUS");
-       free_irq(irq, charger);
-
-       if (!IS_ERR_OR_NULL(charger->usb_phy))
-               usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);
-
-       power_supply_unregister(charger->battery);
-       power_supply_unregister(charger->usb);
-
-       /* Release ADC channels */
-       iio_channel_release(charger->ibus_chan);
-       iio_channel_release(charger->vbus_chan);
-       iio_channel_release(charger->tjunc_chan);
-       iio_channel_release(charger->vbat_chan);
-
-       return 0;
-}
-
-static struct platform_driver da9150_charger_driver = {
-       .driver = {
-               .name = "da9150-charger",
-       },
-       .probe = da9150_charger_probe,
-       .remove = da9150_charger_remove,
-};
-
-module_platform_driver(da9150_charger_driver);
-
-MODULE_DESCRIPTION("Charger Driver for DA9150");
-MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/da9150-fg.c b/drivers/power/da9150-fg.c
deleted file mode 100644 (file)
index 8b8ce97..0000000
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
- * DA9150 Fuel-Gauge Driver
- *
- * Copyright (c) 2015 Dialog Semiconductor
- *
- * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * 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;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/power_supply.h>
-#include <linux/list.h>
-#include <asm/div64.h>
-#include <linux/mfd/da9150/core.h>
-#include <linux/mfd/da9150/registers.h>
-
-/* Core2Wire */
-#define DA9150_QIF_READ                (0x0 << 7)
-#define DA9150_QIF_WRITE       (0x1 << 7)
-#define DA9150_QIF_CODE_MASK   0x7F
-
-#define DA9150_QIF_BYTE_SIZE   8
-#define DA9150_QIF_BYTE_MASK   0xFF
-#define DA9150_QIF_SHORT_SIZE  2
-#define DA9150_QIF_LONG_SIZE   4
-
-/* QIF Codes */
-#define DA9150_QIF_UAVG                        6
-#define DA9150_QIF_UAVG_SIZE           DA9150_QIF_LONG_SIZE
-#define DA9150_QIF_IAVG                        8
-#define DA9150_QIF_IAVG_SIZE           DA9150_QIF_LONG_SIZE
-#define DA9150_QIF_NTCAVG              12
-#define DA9150_QIF_NTCAVG_SIZE         DA9150_QIF_LONG_SIZE
-#define DA9150_QIF_SHUNT_VAL           36
-#define DA9150_QIF_SHUNT_VAL_SIZE      DA9150_QIF_SHORT_SIZE
-#define DA9150_QIF_SD_GAIN             38
-#define DA9150_QIF_SD_GAIN_SIZE                DA9150_QIF_LONG_SIZE
-#define DA9150_QIF_FCC_MAH             40
-#define DA9150_QIF_FCC_MAH_SIZE                DA9150_QIF_SHORT_SIZE
-#define DA9150_QIF_SOC_PCT             43
-#define DA9150_QIF_SOC_PCT_SIZE                DA9150_QIF_SHORT_SIZE
-#define DA9150_QIF_CHARGE_LIMIT                44
-#define DA9150_QIF_CHARGE_LIMIT_SIZE   DA9150_QIF_SHORT_SIZE
-#define DA9150_QIF_DISCHARGE_LIMIT     45
-#define DA9150_QIF_DISCHARGE_LIMIT_SIZE        DA9150_QIF_SHORT_SIZE
-#define DA9150_QIF_FW_MAIN_VER         118
-#define DA9150_QIF_FW_MAIN_VER_SIZE    DA9150_QIF_SHORT_SIZE
-#define DA9150_QIF_E_FG_STATUS         126
-#define DA9150_QIF_E_FG_STATUS_SIZE    DA9150_QIF_SHORT_SIZE
-#define DA9150_QIF_SYNC                        127
-#define DA9150_QIF_SYNC_SIZE           DA9150_QIF_SHORT_SIZE
-#define DA9150_QIF_MAX_CODES           128
-
-/* QIF Sync Timeout */
-#define DA9150_QIF_SYNC_TIMEOUT                1000
-#define DA9150_QIF_SYNC_RETRIES                10
-
-/* QIF E_FG_STATUS */
-#define DA9150_FG_IRQ_LOW_SOC_MASK     (1 << 0)
-#define DA9150_FG_IRQ_HIGH_SOC_MASK    (1 << 1)
-#define DA9150_FG_IRQ_SOC_MASK \
-       (DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK)
-
-/* Private data */
-struct da9150_fg {
-       struct da9150 *da9150;
-       struct device *dev;
-
-       struct mutex io_lock;
-
-       struct power_supply *battery;
-       struct delayed_work work;
-       u32 interval;
-
-       int warn_soc;
-       int crit_soc;
-       int soc;
-};
-
-/* Battery Properties */
-static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
-
-{
-       u8 buf[size];
-       u8 read_addr;
-       u32 res = 0;
-       int i;
-
-       /* Set QIF code (READ mode) */
-       read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ;
-
-       da9150_read_qif(fg->da9150, read_addr, size, buf);
-       for (i = 0; i < size; ++i)
-               res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE));
-
-       return res;
-}
-
-static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
-                                u32 val)
-
-{
-       u8 buf[size];
-       u8 write_addr;
-       int i;
-
-       /* Set QIF code (WRITE mode) */
-       write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE;
-
-       for (i = 0; i < size; ++i) {
-               buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) &
-                        DA9150_QIF_BYTE_MASK;
-       }
-       da9150_write_qif(fg->da9150, write_addr, size, buf);
-}
-
-/* Trigger QIF Sync to update QIF readable data */
-static void da9150_fg_read_sync_start(struct da9150_fg *fg)
-{
-       int i = 0;
-       u32 res = 0;
-
-       mutex_lock(&fg->io_lock);
-
-       /* Check if QIF sync already requested, and write to sync if not */
-       res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
-                                 DA9150_QIF_SYNC_SIZE);
-       if (res > 0)
-               da9150_fg_write_attr(fg, DA9150_QIF_SYNC,
-                                    DA9150_QIF_SYNC_SIZE, 0);
-
-       /* Wait for sync to complete */
-       res = 0;
-       while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
-               usleep_range(DA9150_QIF_SYNC_TIMEOUT,
-                            DA9150_QIF_SYNC_TIMEOUT * 2);
-               res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
-                                         DA9150_QIF_SYNC_SIZE);
-       }
-
-       /* Check if sync completed */
-       if (res == 0)
-               dev_err(fg->dev, "Failed to perform QIF read sync!\n");
-}
-
-/*
- * Should always be called after QIF sync read has been performed, and all
- * attributes required have been accessed.
- */
-static inline void da9150_fg_read_sync_end(struct da9150_fg *fg)
-{
-       mutex_unlock(&fg->io_lock);
-}
-
-/* Sync read of single QIF attribute */
-static u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size)
-{
-       u32 val;
-
-       da9150_fg_read_sync_start(fg);
-       val = da9150_fg_read_attr(fg, code, size);
-       da9150_fg_read_sync_end(fg);
-
-       return val;
-}
-
-/* Wait for QIF Sync, write QIF data and wait for ack */
-static void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size,
-                                     u32 val)
-{
-       int i = 0;
-       u32 res = 0, sync_val;
-
-       mutex_lock(&fg->io_lock);
-
-       /* Check if QIF sync already requested */
-       res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
-                                 DA9150_QIF_SYNC_SIZE);
-
-       /* Wait for an existing sync to complete */
-       while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
-               usleep_range(DA9150_QIF_SYNC_TIMEOUT,
-                            DA9150_QIF_SYNC_TIMEOUT * 2);
-               res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
-                                         DA9150_QIF_SYNC_SIZE);
-       }
-
-       if (res == 0) {
-               dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n");
-               mutex_unlock(&fg->io_lock);
-               return;
-       }
-
-       /* Write value for QIF code */
-       da9150_fg_write_attr(fg, code, size, val);
-
-       /* Wait for write acknowledgment */
-       i = 0;
-       sync_val = res;
-       while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
-               usleep_range(DA9150_QIF_SYNC_TIMEOUT,
-                            DA9150_QIF_SYNC_TIMEOUT * 2);
-               res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
-                                         DA9150_QIF_SYNC_SIZE);
-       }
-
-       mutex_unlock(&fg->io_lock);
-
-       /* Check write was actually successful */
-       if (res != (sync_val + 1))
-               dev_err(fg->dev, "Error performing QIF sync write for code %d\n",
-                       code);
-}
-
-/* Power Supply attributes */
-static int da9150_fg_capacity(struct da9150_fg *fg,
-                             union power_supply_propval *val)
-{
-       val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
-                                              DA9150_QIF_SOC_PCT_SIZE);
-
-       if (val->intval > 100)
-               val->intval = 100;
-
-       return 0;
-}
-
-static int da9150_fg_current_avg(struct da9150_fg *fg,
-                                union power_supply_propval *val)
-{
-       u32 iavg, sd_gain, shunt_val;
-       u64 div, res;
-
-       da9150_fg_read_sync_start(fg);
-       iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG,
-                                  DA9150_QIF_IAVG_SIZE);
-       shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL,
-                                       DA9150_QIF_SHUNT_VAL_SIZE);
-       sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN,
-                                     DA9150_QIF_SD_GAIN_SIZE);
-       da9150_fg_read_sync_end(fg);
-
-       div = (u64) (sd_gain * shunt_val * 65536ULL);
-       do_div(div, 1000000);
-       res = (u64) (iavg * 1000000ULL);
-       do_div(res, div);
-
-       val->intval = (int) res;
-
-       return 0;
-}
-
-static int da9150_fg_voltage_avg(struct da9150_fg *fg,
-                                union power_supply_propval *val)
-{
-       u64 res;
-
-       val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG,
-                                              DA9150_QIF_UAVG_SIZE);
-
-       res = (u64) (val->intval * 186ULL);
-       do_div(res, 10000);
-       val->intval = (int) res;
-
-       return 0;
-}
-
-static int da9150_fg_charge_full(struct da9150_fg *fg,
-                                union power_supply_propval *val)
-{
-       val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH,
-                                              DA9150_QIF_FCC_MAH_SIZE);
-
-       val->intval = val->intval * 1000;
-
-       return 0;
-}
-
-/*
- * Temperature reading from device is only valid if battery/system provides
- * valid NTC to associated pin of DA9150 chip.
- */
-static int da9150_fg_temp(struct da9150_fg *fg,
-                         union power_supply_propval *val)
-{
-       val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG,
-                                              DA9150_QIF_NTCAVG_SIZE);
-
-       val->intval = (val->intval * 10) / 1048576;
-
-       return 0;
-}
-
-static enum power_supply_property da9150_fg_props[] = {
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static int da9150_fg_get_prop(struct power_supply *psy,
-                             enum power_supply_property psp,
-                             union power_supply_propval *val)
-{
-       struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent);
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = da9150_fg_capacity(fg, val);
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               ret = da9150_fg_current_avg(fg, val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               ret = da9150_fg_voltage_avg(fg, val);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               ret = da9150_fg_charge_full(fg, val);
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = da9150_fg_temp(fg, val);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-/* Repeated SOC check */
-static bool da9150_fg_soc_changed(struct da9150_fg *fg)
-{
-       union power_supply_propval val;
-
-       da9150_fg_capacity(fg, &val);
-       if (val.intval != fg->soc) {
-               fg->soc = val.intval;
-               return true;
-       }
-
-       return false;
-}
-
-static void da9150_fg_work(struct work_struct *work)
-{
-       struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work);
-
-       /* Report if SOC has changed */
-       if (da9150_fg_soc_changed(fg))
-               power_supply_changed(fg->battery);
-
-       schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval));
-}
-
-/* SOC level event configuration */
-static void da9150_fg_soc_event_config(struct da9150_fg *fg)
-{
-       int soc;
-
-       soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
-                                      DA9150_QIF_SOC_PCT_SIZE);
-
-       if (soc > fg->warn_soc) {
-               /* If SOC > warn level, set discharge warn level event */
-               da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
-                                         DA9150_QIF_DISCHARGE_LIMIT_SIZE,
-                                         fg->warn_soc + 1);
-       } else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) {
-               /*
-                * If SOC <= warn level, set discharge crit level event,
-                * and set charge warn level event.
-                */
-               da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
-                                         DA9150_QIF_DISCHARGE_LIMIT_SIZE,
-                                         fg->crit_soc + 1);
-
-               da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
-                                         DA9150_QIF_CHARGE_LIMIT_SIZE,
-                                         fg->warn_soc);
-       } else if (soc <= fg->crit_soc) {
-               /* If SOC <= crit level, set charge crit level event */
-               da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
-                                         DA9150_QIF_CHARGE_LIMIT_SIZE,
-                                         fg->crit_soc);
-       }
-}
-
-static irqreturn_t da9150_fg_irq(int irq, void *data)
-{
-       struct da9150_fg *fg = data;
-       u32 e_fg_status;
-
-       /* Read FG IRQ status info */
-       e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS,
-                                         DA9150_QIF_E_FG_STATUS_SIZE);
-
-       /* Handle warning/critical threhold events */
-       if (e_fg_status & DA9150_FG_IRQ_SOC_MASK)
-               da9150_fg_soc_event_config(fg);
-
-       /* Clear any FG IRQs */
-       da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS,
-                            DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status);
-
-       return IRQ_HANDLED;
-}
-
-static struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev)
-{
-       struct device_node *fg_node = dev->of_node;
-       struct da9150_fg_pdata *pdata;
-
-       pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL);
-       if (!pdata)
-               return NULL;
-
-       of_property_read_u32(fg_node, "dlg,update-interval",
-                            &pdata->update_interval);
-       of_property_read_u8(fg_node, "dlg,warn-soc-level",
-                           &pdata->warn_soc_lvl);
-       of_property_read_u8(fg_node, "dlg,crit-soc-level",
-                           &pdata->crit_soc_lvl);
-
-       return pdata;
-}
-
-static const struct power_supply_desc fg_desc = {
-       .name           = "da9150-fg",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = da9150_fg_props,
-       .num_properties = ARRAY_SIZE(da9150_fg_props),
-       .get_property   = da9150_fg_get_prop,
-};
-
-static int da9150_fg_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct da9150 *da9150 = dev_get_drvdata(dev->parent);
-       struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev);
-       struct da9150_fg *fg;
-       int ver, irq, ret = 0;
-
-       fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL);
-       if (fg == NULL)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, fg);
-       fg->da9150 = da9150;
-       fg->dev = dev;
-
-       mutex_init(&fg->io_lock);
-
-       /* Enable QIF */
-       da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK,
-                       DA9150_FG_QIF_EN_MASK);
-
-       fg->battery = devm_power_supply_register(dev, &fg_desc, NULL);
-       if (IS_ERR(fg->battery)) {
-               ret = PTR_ERR(fg->battery);
-               return ret;
-       }
-
-       ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER,
-                                 DA9150_QIF_FW_MAIN_VER_SIZE);
-       dev_info(dev, "Version: 0x%x\n", ver);
-
-       /* Handle DT data if provided */
-       if (dev->of_node) {
-               fg_pdata = da9150_fg_dt_pdata(dev);
-               dev->platform_data = fg_pdata;
-       }
-
-       /* Handle any pdata provided */
-       if (fg_pdata) {
-               fg->interval = fg_pdata->update_interval;
-
-               if (fg_pdata->warn_soc_lvl > 100)
-                       dev_warn(dev, "Invalid SOC warning level provided, Ignoring");
-               else
-                       fg->warn_soc = fg_pdata->warn_soc_lvl;
-
-               if ((fg_pdata->crit_soc_lvl > 100) ||
-                   (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl))
-                       dev_warn(dev, "Invalid SOC critical level provided, Ignoring");
-               else
-                       fg->crit_soc = fg_pdata->crit_soc_lvl;
-
-
-       }
-
-       /* Configure initial SOC level events */
-       da9150_fg_soc_event_config(fg);
-
-       /*
-        * If an interval period has been provided then setup repeating
-        * work for reporting data updates.
-        */
-       if (fg->interval) {
-               INIT_DELAYED_WORK(&fg->work, da9150_fg_work);
-               schedule_delayed_work(&fg->work,
-                                     msecs_to_jiffies(fg->interval));
-       }
-
-       /* Register IRQ */
-       irq = platform_get_irq_byname(pdev, "FG");
-       if (irq < 0) {
-               dev_err(dev, "Failed to get IRQ FG: %d\n", irq);
-               ret = irq;
-               goto irq_fail;
-       }
-
-       ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,
-                                       IRQF_ONESHOT, "FG", fg);
-       if (ret) {
-               dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
-               goto irq_fail;
-       }
-
-       return 0;
-
-irq_fail:
-       if (fg->interval)
-               cancel_delayed_work(&fg->work);
-
-       return ret;
-}
-
-static int da9150_fg_remove(struct platform_device *pdev)
-{
-       struct da9150_fg *fg = platform_get_drvdata(pdev);
-
-       if (fg->interval)
-               cancel_delayed_work(&fg->work);
-
-       return 0;
-}
-
-static int da9150_fg_resume(struct platform_device *pdev)
-{
-       struct da9150_fg *fg = platform_get_drvdata(pdev);
-
-       /*
-        * Trigger SOC check to happen now so as to indicate any value change
-        * since last check before suspend.
-        */
-       if (fg->interval)
-               flush_delayed_work(&fg->work);
-
-       return 0;
-}
-
-static struct platform_driver da9150_fg_driver = {
-       .driver = {
-               .name = "da9150-fuel-gauge",
-       },
-       .probe = da9150_fg_probe,
-       .remove = da9150_fg_remove,
-       .resume = da9150_fg_resume,
-};
-
-module_platform_driver(da9150_fg_driver);
-
-MODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150");
-MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c
deleted file mode 100644 (file)
index 80f73cc..0000000
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Driver for batteries with DS2760 chips inside.
- *
- * Copyright Â© 2007 Anton Vorontsov
- *            2004-2007 Matt Reimer
- *            2004 Szabolcs Gyurko
- *
- * Use consistent with the GNU GPL is permitted,
- * provided that this copyright notice is
- * preserved in its entirety in all copies and derived works.
- *
- * Author:  Anton Vorontsov <cbou@mail.ru>
- *         February 2007
- *
- *         Matt Reimer <mreimer@vpop.net>
- *         April 2004, 2005, 2007
- *
- *         Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
- *         September 2004
- */
-
-#include <linux/module.h>
-#include <linux/param.h>
-#include <linux/jiffies.h>
-#include <linux/workqueue.h>
-#include <linux/pm.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-
-#include "../w1/w1.h"
-#include "../w1/slaves/w1_ds2760.h"
-
-struct ds2760_device_info {
-       struct device *dev;
-
-       /* DS2760 data, valid after calling ds2760_battery_read_status() */
-       unsigned long update_time;      /* jiffies when data read */
-       char raw[DS2760_DATA_SIZE];     /* raw DS2760 data */
-       int voltage_raw;                /* units of 4.88 mV */
-       int voltage_uV;                 /* units of ÂµV */
-       int current_raw;                /* units of 0.625 mA */
-       int current_uA;                 /* units of ÂµA */
-       int accum_current_raw;          /* units of 0.25 mAh */
-       int accum_current_uAh;          /* units of ÂµAh */
-       int temp_raw;                   /* units of 0.125 Â°C */
-       int temp_C;                     /* units of 0.1 Â°C */
-       int rated_capacity;             /* units of ÂµAh */
-       int rem_capacity;               /* percentage */
-       int full_active_uAh;            /* units of ÂµAh */
-       int empty_uAh;                  /* units of ÂµAh */
-       int life_sec;                   /* units of seconds */
-       int charge_status;              /* POWER_SUPPLY_STATUS_* */
-
-       int full_counter;
-       struct power_supply *bat;
-       struct power_supply_desc bat_desc;
-       struct device *w1_dev;
-       struct workqueue_struct *monitor_wqueue;
-       struct delayed_work monitor_work;
-       struct delayed_work set_charged_work;
-};
-
-static unsigned int cache_time = 1000;
-module_param(cache_time, uint, 0644);
-MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
-
-static bool pmod_enabled;
-module_param(pmod_enabled, bool, 0644);
-MODULE_PARM_DESC(pmod_enabled, "PMOD enable bit");
-
-static unsigned int rated_capacity;
-module_param(rated_capacity, uint, 0644);
-MODULE_PARM_DESC(rated_capacity, "rated battery capacity, 10*mAh or index");
-
-static unsigned int current_accum;
-module_param(current_accum, uint, 0644);
-MODULE_PARM_DESC(current_accum, "current accumulator value");
-
-/* Some batteries have their rated capacity stored a N * 10 mAh, while
- * others use an index into this table. */
-static int rated_capacities[] = {
-       0,
-       920,    /* Samsung */
-       920,    /* BYD */
-       920,    /* Lishen */
-       920,    /* NEC */
-       1440,   /* Samsung */
-       1440,   /* BYD */
-#ifdef CONFIG_MACH_H4700
-       1800,   /* HP iPAQ hx4700 3.7V 1800mAh (359113-001) */
-#else
-       1440,   /* Lishen */
-#endif
-       1440,   /* NEC */
-       2880,   /* Samsung */
-       2880,   /* BYD */
-       2880,   /* Lishen */
-       2880,   /* NEC */
-#ifdef CONFIG_MACH_H4700
-       0,
-       3600,   /* HP iPAQ hx4700 3.7V 3600mAh (359114-001) */
-#endif
-};
-
-/* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C
- * temp is in Celsius */
-static int battery_interpolate(int array[], int temp)
-{
-       int index, dt;
-
-       if (temp <= 0)
-               return array[0];
-       if (temp >= 40)
-               return array[4];
-
-       index = temp / 10;
-       dt    = temp % 10;
-
-       return array[index] + (((array[index + 1] - array[index]) * dt) / 10);
-}
-
-static int ds2760_battery_read_status(struct ds2760_device_info *di)
-{
-       int ret, i, start, count, scale[5];
-
-       if (di->update_time && time_before(jiffies, di->update_time +
-                                          msecs_to_jiffies(cache_time)))
-               return 0;
-
-       /* The first time we read the entire contents of SRAM/EEPROM,
-        * but after that we just read the interesting bits that change. */
-       if (di->update_time == 0) {
-               start = 0;
-               count = DS2760_DATA_SIZE;
-       } else {
-               start = DS2760_VOLTAGE_MSB;
-               count = DS2760_TEMP_LSB - start + 1;
-       }
-
-       ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count);
-       if (ret != count) {
-               dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n",
-                        di->w1_dev);
-               return 1;
-       }
-
-       di->update_time = jiffies;
-
-       /* DS2760 reports voltage in units of 4.88mV, but the battery class
-        * reports in units of uV, so convert by multiplying by 4880. */
-       di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) |
-                         (di->raw[DS2760_VOLTAGE_LSB] >> 5);
-       di->voltage_uV = di->voltage_raw * 4880;
-
-       /* DS2760 reports current in signed units of 0.625mA, but the battery
-        * class reports in units of ÂµA, so convert by multiplying by 625. */
-       di->current_raw =
-           (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) |
-                         (di->raw[DS2760_CURRENT_LSB] >> 3);
-       di->current_uA = di->current_raw * 625;
-
-       /* DS2760 reports accumulated current in signed units of 0.25mAh. */
-       di->accum_current_raw =
-           (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) |
-                          di->raw[DS2760_CURRENT_ACCUM_LSB];
-       di->accum_current_uAh = di->accum_current_raw * 250;
-
-       /* DS2760 reports temperature in signed units of 0.125°C, but the
-        * battery class reports in units of 1/10 Â°C, so we convert by
-        * multiplying by .125 * 10 = 1.25. */
-       di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) |
-                                    (di->raw[DS2760_TEMP_LSB] >> 5);
-       di->temp_C = di->temp_raw + (di->temp_raw / 4);
-
-       /* At least some battery monitors (e.g. HP iPAQ) store the battery's
-        * maximum rated capacity. */
-       if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities))
-               di->rated_capacity = rated_capacities[
-                       (unsigned int)di->raw[DS2760_RATED_CAPACITY]];
-       else
-               di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10;
-
-       di->rated_capacity *= 1000; /* convert to ÂµAh */
-
-       /* Calculate the full level at the present temperature. */
-       di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 |
-                             di->raw[DS2760_ACTIVE_FULL + 1];
-
-       /* If the full_active_uAh value is not given, fall back to the rated
-        * capacity. This is likely to happen when chips are not part of the
-        * battery pack and is therefore not bootstrapped. */
-       if (di->full_active_uAh == 0)
-               di->full_active_uAh = di->rated_capacity / 1000L;
-
-       scale[0] = di->full_active_uAh;
-       for (i = 1; i < 5; i++)
-               scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 1 + i];
-
-       di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10);
-       di->full_active_uAh *= 1000; /* convert to ÂµAh */
-
-       /* Calculate the empty level at the present temperature. */
-       scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4];
-       for (i = 3; i >= 0; i--)
-               scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i];
-
-       di->empty_uAh = battery_interpolate(scale, di->temp_C / 10);
-       di->empty_uAh *= 1000; /* convert to ÂµAh */
-
-       if (di->full_active_uAh == di->empty_uAh)
-               di->rem_capacity = 0;
-       else
-               /* From Maxim Application Note 131: remaining capacity =
-                * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */
-               di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) /
-                                   (di->full_active_uAh - di->empty_uAh);
-
-       if (di->rem_capacity < 0)
-               di->rem_capacity = 0;
-       if (di->rem_capacity > 100)
-               di->rem_capacity = 100;
-
-       if (di->current_uA < -100L)
-               di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * 36L)
-                                       / (di->current_uA / 100L);
-       else
-               di->life_sec = 0;
-
-       return 0;
-}
-
-static void ds2760_battery_set_current_accum(struct ds2760_device_info *di,
-                                            unsigned int acr_val)
-{
-       unsigned char acr[2];
-
-       /* acr is in units of 0.25 mAh */
-       acr_val *= 4L;
-       acr_val /= 1000;
-
-       acr[0] = acr_val >> 8;
-       acr[1] = acr_val & 0xff;
-
-       if (w1_ds2760_write(di->w1_dev, acr, DS2760_CURRENT_ACCUM_MSB, 2) < 2)
-               dev_warn(di->dev, "ACR write failed\n");
-}
-
-static void ds2760_battery_update_status(struct ds2760_device_info *di)
-{
-       int old_charge_status = di->charge_status;
-
-       ds2760_battery_read_status(di);
-
-       if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN)
-               di->full_counter = 0;
-
-       if (power_supply_am_i_supplied(di->bat)) {
-               if (di->current_uA > 10000) {
-                       di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
-                       di->full_counter = 0;
-               } else if (di->current_uA < -5000) {
-                       if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING)
-                               dev_notice(di->dev, "not enough power to "
-                                          "charge\n");
-                       di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-                       di->full_counter = 0;
-               } else if (di->current_uA < 10000 &&
-                           di->charge_status != POWER_SUPPLY_STATUS_FULL) {
-
-                       /* Don't consider the battery to be full unless
-                        * we've seen the current < 10 mA at least two
-                        * consecutive times. */
-
-                       di->full_counter++;
-
-                       if (di->full_counter < 2) {
-                               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
-                       } else {
-                               di->charge_status = POWER_SUPPLY_STATUS_FULL;
-                               ds2760_battery_set_current_accum(di,
-                                               di->full_active_uAh);
-                       }
-               }
-       } else {
-               di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
-               di->full_counter = 0;
-       }
-
-       if (di->charge_status != old_charge_status)
-               power_supply_changed(di->bat);
-}
-
-static void ds2760_battery_write_status(struct ds2760_device_info *di,
-                                       char status)
-{
-       if (status == di->raw[DS2760_STATUS_REG])
-               return;
-
-       w1_ds2760_write(di->w1_dev, &status, DS2760_STATUS_WRITE_REG, 1);
-       w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
-       w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
-}
-
-static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di,
-                                               unsigned char rated_capacity)
-{
-       if (rated_capacity == di->raw[DS2760_RATED_CAPACITY])
-               return;
-
-       w1_ds2760_write(di->w1_dev, &rated_capacity, DS2760_RATED_CAPACITY, 1);
-       w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
-       w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
-}
-
-static void ds2760_battery_write_active_full(struct ds2760_device_info *di,
-                                            int active_full)
-{
-       unsigned char tmp[2] = {
-               active_full >> 8,
-               active_full & 0xff
-       };
-
-       if (tmp[0] == di->raw[DS2760_ACTIVE_FULL] &&
-           tmp[1] == di->raw[DS2760_ACTIVE_FULL + 1])
-               return;
-
-       w1_ds2760_write(di->w1_dev, tmp, DS2760_ACTIVE_FULL, sizeof(tmp));
-       w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
-       w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
-
-       /* Write to the di->raw[] buffer directly - the DS2760_ACTIVE_FULL
-        * values won't be read back by ds2760_battery_read_status() */
-       di->raw[DS2760_ACTIVE_FULL] = tmp[0];
-       di->raw[DS2760_ACTIVE_FULL + 1] = tmp[1];
-}
-
-static void ds2760_battery_work(struct work_struct *work)
-{
-       struct ds2760_device_info *di = container_of(work,
-               struct ds2760_device_info, monitor_work.work);
-       const int interval = HZ * 60;
-
-       dev_dbg(di->dev, "%s\n", __func__);
-
-       ds2760_battery_update_status(di);
-       queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
-}
-
-static void ds2760_battery_external_power_changed(struct power_supply *psy)
-{
-       struct ds2760_device_info *di = power_supply_get_drvdata(psy);
-
-       dev_dbg(di->dev, "%s\n", __func__);
-
-       mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10);
-}
-
-
-static void ds2760_battery_set_charged_work(struct work_struct *work)
-{
-       char bias;
-       struct ds2760_device_info *di = container_of(work,
-               struct ds2760_device_info, set_charged_work.work);
-
-       dev_dbg(di->dev, "%s\n", __func__);
-
-       ds2760_battery_read_status(di);
-
-       /* When we get notified by external circuitry that the battery is
-        * considered fully charged now, we know that there is no current
-        * flow any more. However, the ds2760's internal current meter is
-        * too inaccurate to rely on - spec say something ~15% failure.
-        * Hence, we use the current offset bias register to compensate
-        * that error.
-        */
-
-       if (!power_supply_am_i_supplied(di->bat))
-               return;
-
-       bias = (signed char) di->current_raw +
-               (signed char) di->raw[DS2760_CURRENT_OFFSET_BIAS];
-
-       dev_dbg(di->dev, "%s: bias = %d\n", __func__, bias);
-
-       w1_ds2760_write(di->w1_dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1);
-       w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
-       w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
-
-       /* Write to the di->raw[] buffer directly - the CURRENT_OFFSET_BIAS
-        * value won't be read back by ds2760_battery_read_status() */
-       di->raw[DS2760_CURRENT_OFFSET_BIAS] = bias;
-}
-
-static void ds2760_battery_set_charged(struct power_supply *psy)
-{
-       struct ds2760_device_info *di = power_supply_get_drvdata(psy);
-
-       /* postpone the actual work by 20 secs. This is for debouncing GPIO
-        * signals and to let the current value settle. See AN4188. */
-       mod_delayed_work(di->monitor_wqueue, &di->set_charged_work, HZ * 20);
-}
-
-static int ds2760_battery_get_property(struct power_supply *psy,
-                                      enum power_supply_property psp,
-                                      union power_supply_propval *val)
-{
-       struct ds2760_device_info *di = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = di->charge_status;
-               return 0;
-       default:
-               break;
-       }
-
-       ds2760_battery_read_status(di);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = di->voltage_uV;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               val->intval = di->current_uA;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               val->intval = di->rated_capacity;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               val->intval = di->full_active_uAh;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_EMPTY:
-               val->intval = di->empty_uAh;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               val->intval = di->accum_current_uAh;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = di->temp_C;
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
-               val->intval = di->life_sec;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               val->intval = di->rem_capacity;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int ds2760_battery_set_property(struct power_supply *psy,
-                                      enum power_supply_property psp,
-                                      const union power_supply_propval *val)
-{
-       struct ds2760_device_info *di = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               /* the interface counts in uAh, convert the value */
-               ds2760_battery_write_active_full(di, val->intval / 1000L);
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               /* ds2760_battery_set_current_accum() does the conversion */
-               ds2760_battery_set_current_accum(di, val->intval);
-               break;
-
-       default:
-               return -EPERM;
-       }
-
-       return 0;
-}
-
-static int ds2760_battery_property_is_writeable(struct power_supply *psy,
-                                               enum power_supply_property psp)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               return 1;
-
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property ds2760_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_EMPTY,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-};
-
-static int ds2760_battery_probe(struct platform_device *pdev)
-{
-       struct power_supply_config psy_cfg = {};
-       char status;
-       int retval = 0;
-       struct ds2760_device_info *di;
-
-       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di) {
-               retval = -ENOMEM;
-               goto di_alloc_failed;
-       }
-
-       platform_set_drvdata(pdev, di);
-
-       di->dev                         = &pdev->dev;
-       di->w1_dev                      = pdev->dev.parent;
-       di->bat_desc.name               = dev_name(&pdev->dev);
-       di->bat_desc.type               = POWER_SUPPLY_TYPE_BATTERY;
-       di->bat_desc.properties         = ds2760_battery_props;
-       di->bat_desc.num_properties     = ARRAY_SIZE(ds2760_battery_props);
-       di->bat_desc.get_property       = ds2760_battery_get_property;
-       di->bat_desc.set_property       = ds2760_battery_set_property;
-       di->bat_desc.property_is_writeable =
-                                 ds2760_battery_property_is_writeable;
-       di->bat_desc.set_charged        = ds2760_battery_set_charged;
-       di->bat_desc.external_power_changed =
-                                 ds2760_battery_external_power_changed;
-
-       psy_cfg.drv_data                = di;
-
-       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
-
-       /* enable sleep mode feature */
-       ds2760_battery_read_status(di);
-       status = di->raw[DS2760_STATUS_REG];
-       if (pmod_enabled)
-               status |= DS2760_STATUS_PMOD;
-       else
-               status &= ~DS2760_STATUS_PMOD;
-
-       ds2760_battery_write_status(di, status);
-
-       /* set rated capacity from module param */
-       if (rated_capacity)
-               ds2760_battery_write_rated_capacity(di, rated_capacity);
-
-       /* set current accumulator if given as parameter.
-        * this should only be done for bootstrapping the value */
-       if (current_accum)
-               ds2760_battery_set_current_accum(di, current_accum);
-
-       di->bat = power_supply_register(&pdev->dev, &di->bat_desc, &psy_cfg);
-       if (IS_ERR(di->bat)) {
-               dev_err(di->dev, "failed to register battery\n");
-               retval = PTR_ERR(di->bat);
-               goto batt_failed;
-       }
-
-       INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work);
-       INIT_DELAYED_WORK(&di->set_charged_work,
-                         ds2760_battery_set_charged_work);
-       di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
-       if (!di->monitor_wqueue) {
-               retval = -ESRCH;
-               goto workqueue_failed;
-       }
-       queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1);
-
-       goto success;
-
-workqueue_failed:
-       power_supply_unregister(di->bat);
-batt_failed:
-di_alloc_failed:
-success:
-       return retval;
-}
-
-static int ds2760_battery_remove(struct platform_device *pdev)
-{
-       struct ds2760_device_info *di = platform_get_drvdata(pdev);
-
-       cancel_delayed_work_sync(&di->monitor_work);
-       cancel_delayed_work_sync(&di->set_charged_work);
-       destroy_workqueue(di->monitor_wqueue);
-       power_supply_unregister(di->bat);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int ds2760_battery_suspend(struct platform_device *pdev,
-                                 pm_message_t state)
-{
-       struct ds2760_device_info *di = platform_get_drvdata(pdev);
-
-       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
-
-       return 0;
-}
-
-static int ds2760_battery_resume(struct platform_device *pdev)
-{
-       struct ds2760_device_info *di = platform_get_drvdata(pdev);
-
-       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
-       power_supply_changed(di->bat);
-
-       mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ);
-
-       return 0;
-}
-
-#else
-
-#define ds2760_battery_suspend NULL
-#define ds2760_battery_resume NULL
-
-#endif /* CONFIG_PM */
-
-MODULE_ALIAS("platform:ds2760-battery");
-
-static struct platform_driver ds2760_battery_driver = {
-       .driver = {
-               .name = "ds2760-battery",
-       },
-       .probe    = ds2760_battery_probe,
-       .remove   = ds2760_battery_remove,
-       .suspend  = ds2760_battery_suspend,
-       .resume   = ds2760_battery_resume,
-};
-
-module_platform_driver(ds2760_battery_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
-             "Matt Reimer <mreimer@vpop.net>, "
-             "Anton Vorontsov <cbou@mail.ru>");
-MODULE_DESCRIPTION("ds2760 battery driver");
diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c
deleted file mode 100644 (file)
index d3743d0..0000000
+++ /dev/null
@@ -1,838 +0,0 @@
-/*
- * 1-wire client/driver for the Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC
- *
- * Copyright (C) 2010 Indesign, LLC
- *
- * Author: Clifton Barnes <cabarnes@indesign-llc.com>
- *
- * Based on ds2760_battery and ds2782_battery drivers
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/param.h>
-#include <linux/pm.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/idr.h>
-
-#include "../w1/w1.h"
-#include "../w1/slaves/w1_ds2780.h"
-
-/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
-#define DS2780_CURRENT_UNITS   1563
-/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */
-#define DS2780_CHARGE_UNITS            6250
-/* Number of bytes in user EEPROM space */
-#define DS2780_USER_EEPROM_SIZE                (DS2780_EEPROM_BLOCK0_END - \
-                                       DS2780_EEPROM_BLOCK0_START + 1)
-/* Number of bytes in parameter EEPROM space */
-#define DS2780_PARAM_EEPROM_SIZE       (DS2780_EEPROM_BLOCK1_END - \
-                                       DS2780_EEPROM_BLOCK1_START + 1)
-
-struct ds2780_device_info {
-       struct device *dev;
-       struct power_supply *bat;
-       struct power_supply_desc bat_desc;
-       struct device *w1_dev;
-};
-
-enum current_types {
-       CURRENT_NOW,
-       CURRENT_AVG,
-};
-
-static const char model[] = "DS2780";
-static const char manufacturer[] = "Maxim/Dallas";
-
-static inline struct ds2780_device_info *
-to_ds2780_device_info(struct power_supply *psy)
-{
-       return power_supply_get_drvdata(psy);
-}
-
-static inline struct power_supply *to_power_supply(struct device *dev)
-{
-       return dev_get_drvdata(dev);
-}
-
-static inline int ds2780_battery_io(struct ds2780_device_info *dev_info,
-       char *buf, int addr, size_t count, int io)
-{
-       return w1_ds2780_io(dev_info->w1_dev, buf, addr, count, io);
-}
-
-static inline int ds2780_read8(struct ds2780_device_info *dev_info, u8 *val,
-       int addr)
-{
-       return ds2780_battery_io(dev_info, val, addr, sizeof(u8), 0);
-}
-
-static int ds2780_read16(struct ds2780_device_info *dev_info, s16 *val,
-       int addr)
-{
-       int ret;
-       u8 raw[2];
-
-       ret = ds2780_battery_io(dev_info, raw, addr, sizeof(raw), 0);
-       if (ret < 0)
-               return ret;
-
-       *val = (raw[0] << 8) | raw[1];
-
-       return 0;
-}
-
-static inline int ds2780_read_block(struct ds2780_device_info *dev_info,
-       u8 *val, int addr, size_t count)
-{
-       return ds2780_battery_io(dev_info, val, addr, count, 0);
-}
-
-static inline int ds2780_write(struct ds2780_device_info *dev_info, u8 *val,
-       int addr, size_t count)
-{
-       return ds2780_battery_io(dev_info, val, addr, count, 1);
-}
-
-static inline int ds2780_store_eeprom(struct device *dev, int addr)
-{
-       return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_COPY_DATA);
-}
-
-static inline int ds2780_recall_eeprom(struct device *dev, int addr)
-{
-       return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_RECALL_DATA);
-}
-
-static int ds2780_save_eeprom(struct ds2780_device_info *dev_info, int reg)
-{
-       int ret;
-
-       ret = ds2780_store_eeprom(dev_info->w1_dev, reg);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2780_recall_eeprom(dev_info->w1_dev, reg);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-/* Set sense resistor value in mhos */
-static int ds2780_set_sense_register(struct ds2780_device_info *dev_info,
-       u8 conductance)
-{
-       int ret;
-
-       ret = ds2780_write(dev_info, &conductance,
-                               DS2780_RSNSP_REG, sizeof(u8));
-       if (ret < 0)
-               return ret;
-
-       return ds2780_save_eeprom(dev_info, DS2780_RSNSP_REG);
-}
-
-/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */
-static int ds2780_get_rsgain_register(struct ds2780_device_info *dev_info,
-       u16 *rsgain)
-{
-       return ds2780_read16(dev_info, rsgain, DS2780_RSGAIN_MSB_REG);
-}
-
-/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */
-static int ds2780_set_rsgain_register(struct ds2780_device_info *dev_info,
-       u16 rsgain)
-{
-       int ret;
-       u8 raw[] = {rsgain >> 8, rsgain & 0xFF};
-
-       ret = ds2780_write(dev_info, raw,
-                               DS2780_RSGAIN_MSB_REG, sizeof(raw));
-       if (ret < 0)
-               return ret;
-
-       return ds2780_save_eeprom(dev_info, DS2780_RSGAIN_MSB_REG);
-}
-
-static int ds2780_get_voltage(struct ds2780_device_info *dev_info,
-       int *voltage_uV)
-{
-       int ret;
-       s16 voltage_raw;
-
-       /*
-        * The voltage value is located in 10 bits across the voltage MSB
-        * and LSB registers in two's compliment form
-        * Sign bit of the voltage value is in bit 7 of the voltage MSB register
-        * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the
-        * voltage MSB register
-        * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the
-        * voltage LSB register
-        */
-       ret = ds2780_read16(dev_info, &voltage_raw,
-                               DS2780_VOLT_MSB_REG);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * DS2780 reports voltage in units of 4.88mV, but the battery class
-        * reports in units of uV, so convert by multiplying by 4880.
-        */
-       *voltage_uV = (voltage_raw / 32) * 4880;
-       return 0;
-}
-
-static int ds2780_get_temperature(struct ds2780_device_info *dev_info,
-       int *temperature)
-{
-       int ret;
-       s16 temperature_raw;
-
-       /*
-        * The temperature value is located in 10 bits across the temperature
-        * MSB and LSB registers in two's compliment form
-        * Sign bit of the temperature value is in bit 7 of the temperature
-        * MSB register
-        * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the
-        * temperature MSB register
-        * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the
-        * temperature LSB register
-        */
-       ret = ds2780_read16(dev_info, &temperature_raw,
-                               DS2780_TEMP_MSB_REG);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * Temperature is measured in units of 0.125 degrees celcius, the
-        * power_supply class measures temperature in tenths of degrees
-        * celsius. The temperature value is stored as a 10 bit number, plus
-        * sign in the upper bits of a 16 bit register.
-        */
-       *temperature = ((temperature_raw / 32) * 125) / 100;
-       return 0;
-}
-
-static int ds2780_get_current(struct ds2780_device_info *dev_info,
-       enum current_types type, int *current_uA)
-{
-       int ret, sense_res;
-       s16 current_raw;
-       u8 sense_res_raw, reg_msb;
-
-       /*
-        * The units of measurement for current are dependent on the value of
-        * the sense resistor.
-        */
-       ret = ds2780_read8(dev_info, &sense_res_raw, DS2780_RSNSP_REG);
-       if (ret < 0)
-               return ret;
-
-       if (sense_res_raw == 0) {
-               dev_err(dev_info->dev, "sense resistor value is 0\n");
-               return -EINVAL;
-       }
-       sense_res = 1000 / sense_res_raw;
-
-       if (type == CURRENT_NOW)
-               reg_msb = DS2780_CURRENT_MSB_REG;
-       else if (type == CURRENT_AVG)
-               reg_msb = DS2780_IAVG_MSB_REG;
-       else
-               return -EINVAL;
-
-       /*
-        * The current value is located in 16 bits across the current MSB
-        * and LSB registers in two's compliment form
-        * Sign bit of the current value is in bit 7 of the current MSB register
-        * Bits 14 - 8 of the current value are in bits 6 - 0 of the current
-        * MSB register
-        * Bits 7 - 0 of the current value are in bits 7 - 0 of the current
-        * LSB register
-        */
-       ret = ds2780_read16(dev_info, &current_raw, reg_msb);
-       if (ret < 0)
-               return ret;
-
-       *current_uA = current_raw * (DS2780_CURRENT_UNITS / sense_res);
-       return 0;
-}
-
-static int ds2780_get_accumulated_current(struct ds2780_device_info *dev_info,
-       int *accumulated_current)
-{
-       int ret, sense_res;
-       s16 current_raw;
-       u8 sense_res_raw;
-
-       /*
-        * The units of measurement for accumulated current are dependent on
-        * the value of the sense resistor.
-        */
-       ret = ds2780_read8(dev_info, &sense_res_raw, DS2780_RSNSP_REG);
-       if (ret < 0)
-               return ret;
-
-       if (sense_res_raw == 0) {
-               dev_err(dev_info->dev, "sense resistor value is 0\n");
-               return -ENXIO;
-       }
-       sense_res = 1000 / sense_res_raw;
-
-       /*
-        * The ACR value is located in 16 bits across the ACR MSB and
-        * LSB registers
-        * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR
-        * MSB register
-        * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR
-        * LSB register
-        */
-       ret = ds2780_read16(dev_info, &current_raw, DS2780_ACR_MSB_REG);
-       if (ret < 0)
-               return ret;
-
-       *accumulated_current = current_raw * (DS2780_CHARGE_UNITS / sense_res);
-       return 0;
-}
-
-static int ds2780_get_capacity(struct ds2780_device_info *dev_info,
-       int *capacity)
-{
-       int ret;
-       u8 raw;
-
-       ret = ds2780_read8(dev_info, &raw, DS2780_RARC_REG);
-       if (ret < 0)
-               return ret;
-
-       *capacity = raw;
-       return raw;
-}
-
-static int ds2780_get_status(struct ds2780_device_info *dev_info, int *status)
-{
-       int ret, current_uA, capacity;
-
-       ret = ds2780_get_current(dev_info, CURRENT_NOW, &current_uA);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2780_get_capacity(dev_info, &capacity);
-       if (ret < 0)
-               return ret;
-
-       if (capacity == 100)
-               *status = POWER_SUPPLY_STATUS_FULL;
-       else if (current_uA == 0)
-               *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       else if (current_uA < 0)
-               *status = POWER_SUPPLY_STATUS_DISCHARGING;
-       else
-               *status = POWER_SUPPLY_STATUS_CHARGING;
-
-       return 0;
-}
-
-static int ds2780_get_charge_now(struct ds2780_device_info *dev_info,
-       int *charge_now)
-{
-       int ret;
-       u16 charge_raw;
-
-       /*
-        * The RAAC value is located in 16 bits across the RAAC MSB and
-        * LSB registers
-        * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC
-        * MSB register
-        * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC
-        * LSB register
-        */
-       ret = ds2780_read16(dev_info, &charge_raw, DS2780_RAAC_MSB_REG);
-       if (ret < 0)
-               return ret;
-
-       *charge_now = charge_raw * 1600;
-       return 0;
-}
-
-static int ds2780_get_control_register(struct ds2780_device_info *dev_info,
-       u8 *control_reg)
-{
-       return ds2780_read8(dev_info, control_reg, DS2780_CONTROL_REG);
-}
-
-static int ds2780_set_control_register(struct ds2780_device_info *dev_info,
-       u8 control_reg)
-{
-       int ret;
-
-       ret = ds2780_write(dev_info, &control_reg,
-                               DS2780_CONTROL_REG, sizeof(u8));
-       if (ret < 0)
-               return ret;
-
-       return ds2780_save_eeprom(dev_info, DS2780_CONTROL_REG);
-}
-
-static int ds2780_battery_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       int ret = 0;
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = ds2780_get_voltage(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = ds2780_get_temperature(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = model;
-               break;
-
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = manufacturer;
-               break;
-
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = ds2780_get_current(dev_info, CURRENT_NOW, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               ret = ds2780_get_current(dev_info, CURRENT_AVG, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = ds2780_get_status(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = ds2780_get_capacity(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
-               ret = ds2780_get_accumulated_current(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               ret = ds2780_get_charge_now(dev_info, &val->intval);
-               break;
-
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property ds2780_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CHARGE_COUNTER,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-};
-
-static ssize_t ds2780_get_pmod_enabled(struct device *dev,
-       struct device_attribute *attr,
-       char *buf)
-{
-       int ret;
-       u8 control_reg;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       /* Get power mode */
-       ret = ds2780_get_control_register(dev_info, &control_reg);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%d\n",
-                !!(control_reg & DS2780_CONTROL_REG_PMOD));
-}
-
-static ssize_t ds2780_set_pmod_enabled(struct device *dev,
-       struct device_attribute *attr,
-       const char *buf,
-       size_t count)
-{
-       int ret;
-       u8 control_reg, new_setting;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       /* Set power mode */
-       ret = ds2780_get_control_register(dev_info, &control_reg);
-       if (ret < 0)
-               return ret;
-
-       ret = kstrtou8(buf, 0, &new_setting);
-       if (ret < 0)
-               return ret;
-
-       if ((new_setting != 0) && (new_setting != 1)) {
-               dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n");
-               return -EINVAL;
-       }
-
-       if (new_setting)
-               control_reg |= DS2780_CONTROL_REG_PMOD;
-       else
-               control_reg &= ~DS2780_CONTROL_REG_PMOD;
-
-       ret = ds2780_set_control_register(dev_info, control_reg);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t ds2780_get_sense_resistor_value(struct device *dev,
-       struct device_attribute *attr,
-       char *buf)
-{
-       int ret;
-       u8 sense_resistor;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       ret = ds2780_read8(dev_info, &sense_resistor, DS2780_RSNSP_REG);
-       if (ret < 0)
-               return ret;
-
-       ret = sprintf(buf, "%d\n", sense_resistor);
-       return ret;
-}
-
-static ssize_t ds2780_set_sense_resistor_value(struct device *dev,
-       struct device_attribute *attr,
-       const char *buf,
-       size_t count)
-{
-       int ret;
-       u8 new_setting;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       ret = kstrtou8(buf, 0, &new_setting);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2780_set_sense_register(dev_info, new_setting);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t ds2780_get_rsgain_setting(struct device *dev,
-       struct device_attribute *attr,
-       char *buf)
-{
-       int ret;
-       u16 rsgain;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       ret = ds2780_get_rsgain_register(dev_info, &rsgain);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%d\n", rsgain);
-}
-
-static ssize_t ds2780_set_rsgain_setting(struct device *dev,
-       struct device_attribute *attr,
-       const char *buf,
-       size_t count)
-{
-       int ret;
-       u16 new_setting;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       ret = kstrtou16(buf, 0, &new_setting);
-       if (ret < 0)
-               return ret;
-
-       /* Gain can only be from 0 to 1.999 in steps of .001 */
-       if (new_setting > 1999) {
-               dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n");
-               return -EINVAL;
-       }
-
-       ret = ds2780_set_rsgain_register(dev_info, new_setting);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t ds2780_get_pio_pin(struct device *dev,
-       struct device_attribute *attr,
-       char *buf)
-{
-       int ret;
-       u8 sfr;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       ret = ds2780_read8(dev_info, &sfr, DS2780_SFR_REG);
-       if (ret < 0)
-               return ret;
-
-       ret = sprintf(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC);
-       return ret;
-}
-
-static ssize_t ds2780_set_pio_pin(struct device *dev,
-       struct device_attribute *attr,
-       const char *buf,
-       size_t count)
-{
-       int ret;
-       u8 new_setting;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       ret = kstrtou8(buf, 0, &new_setting);
-       if (ret < 0)
-               return ret;
-
-       if ((new_setting != 0) && (new_setting != 1)) {
-               dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n");
-               return -EINVAL;
-       }
-
-       ret = ds2780_write(dev_info, &new_setting,
-                               DS2780_SFR_REG, sizeof(u8));
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
-                               struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       return ds2780_read_block(dev_info, buf,
-                               DS2780_EEPROM_BLOCK1_START + off, count);
-}
-
-static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
-                               struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-       int ret;
-
-       ret = ds2780_write(dev_info, buf,
-                               DS2780_EEPROM_BLOCK1_START + off, count);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK1_START);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static struct bin_attribute ds2780_param_eeprom_bin_attr = {
-       .attr = {
-               .name = "param_eeprom",
-               .mode = S_IRUGO | S_IWUSR,
-       },
-       .size = DS2780_PARAM_EEPROM_SIZE,
-       .read = ds2780_read_param_eeprom_bin,
-       .write = ds2780_write_param_eeprom_bin,
-};
-
-static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
-                               struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-
-       return ds2780_read_block(dev_info, buf,
-                               DS2780_EEPROM_BLOCK0_START + off, count);
-}
-
-static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
-                               struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
-       int ret;
-
-       ret = ds2780_write(dev_info, buf,
-                               DS2780_EEPROM_BLOCK0_START + off, count);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK0_START);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static struct bin_attribute ds2780_user_eeprom_bin_attr = {
-       .attr = {
-               .name = "user_eeprom",
-               .mode = S_IRUGO | S_IWUSR,
-       },
-       .size = DS2780_USER_EEPROM_SIZE,
-       .read = ds2780_read_user_eeprom_bin,
-       .write = ds2780_write_user_eeprom_bin,
-};
-
-static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2780_get_pmod_enabled,
-       ds2780_set_pmod_enabled);
-static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR,
-       ds2780_get_sense_resistor_value, ds2780_set_sense_resistor_value);
-static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2780_get_rsgain_setting,
-       ds2780_set_rsgain_setting);
-static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2780_get_pio_pin,
-       ds2780_set_pio_pin);
-
-
-static struct attribute *ds2780_attributes[] = {
-       &dev_attr_pmod_enabled.attr,
-       &dev_attr_sense_resistor_value.attr,
-       &dev_attr_rsgain_setting.attr,
-       &dev_attr_pio_pin.attr,
-       NULL
-};
-
-static const struct attribute_group ds2780_attr_group = {
-       .attrs = ds2780_attributes,
-};
-
-static int ds2780_battery_probe(struct platform_device *pdev)
-{
-       struct power_supply_config psy_cfg = {};
-       int ret = 0;
-       struct ds2780_device_info *dev_info;
-
-       dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
-       if (!dev_info) {
-               ret = -ENOMEM;
-               goto fail;
-       }
-
-       platform_set_drvdata(pdev, dev_info);
-
-       dev_info->dev                   = &pdev->dev;
-       dev_info->w1_dev                = pdev->dev.parent;
-       dev_info->bat_desc.name         = dev_name(&pdev->dev);
-       dev_info->bat_desc.type         = POWER_SUPPLY_TYPE_BATTERY;
-       dev_info->bat_desc.properties   = ds2780_battery_props;
-       dev_info->bat_desc.num_properties = ARRAY_SIZE(ds2780_battery_props);
-       dev_info->bat_desc.get_property = ds2780_battery_get_property;
-
-       psy_cfg.drv_data                = dev_info;
-
-       dev_info->bat = power_supply_register(&pdev->dev, &dev_info->bat_desc,
-                                             &psy_cfg);
-       if (IS_ERR(dev_info->bat)) {
-               dev_err(dev_info->dev, "failed to register battery\n");
-               ret = PTR_ERR(dev_info->bat);
-               goto fail;
-       }
-
-       ret = sysfs_create_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
-       if (ret) {
-               dev_err(dev_info->dev, "failed to create sysfs group\n");
-               goto fail_unregister;
-       }
-
-       ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
-                                       &ds2780_param_eeprom_bin_attr);
-       if (ret) {
-               dev_err(dev_info->dev,
-                               "failed to create param eeprom bin file");
-               goto fail_remove_group;
-       }
-
-       ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
-                                       &ds2780_user_eeprom_bin_attr);
-       if (ret) {
-               dev_err(dev_info->dev,
-                               "failed to create user eeprom bin file");
-               goto fail_remove_bin_file;
-       }
-
-       return 0;
-
-fail_remove_bin_file:
-       sysfs_remove_bin_file(&dev_info->bat->dev.kobj,
-                               &ds2780_param_eeprom_bin_attr);
-fail_remove_group:
-       sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
-fail_unregister:
-       power_supply_unregister(dev_info->bat);
-fail:
-       return ret;
-}
-
-static int ds2780_battery_remove(struct platform_device *pdev)
-{
-       struct ds2780_device_info *dev_info = platform_get_drvdata(pdev);
-
-       /*
-        * Remove attributes before unregistering power supply
-        * because 'bat' will be freed on power_supply_unregister() call.
-        */
-       sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
-
-       power_supply_unregister(dev_info->bat);
-
-       return 0;
-}
-
-static struct platform_driver ds2780_battery_driver = {
-       .driver = {
-               .name = "ds2780-battery",
-       },
-       .probe    = ds2780_battery_probe,
-       .remove   = ds2780_battery_remove,
-};
-
-module_platform_driver(ds2780_battery_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
-MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver");
-MODULE_ALIAS("platform:ds2780-battery");
diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c
deleted file mode 100644 (file)
index c368002..0000000
+++ /dev/null
@@ -1,839 +0,0 @@
-/*
- * 1-wire client/driver for the Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC
- *
- * Author: Renata Sayakhova <renata@oktetlabs.ru>
- *
- * Based on ds2780_battery drivers
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/param.h>
-#include <linux/pm.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/idr.h>
-
-#include "../w1/w1.h"
-#include "../w1/slaves/w1_ds2781.h"
-
-/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
-#define DS2781_CURRENT_UNITS   1563
-/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */
-#define DS2781_CHARGE_UNITS            6250
-/* Number of bytes in user EEPROM space */
-#define DS2781_USER_EEPROM_SIZE                (DS2781_EEPROM_BLOCK0_END - \
-                                       DS2781_EEPROM_BLOCK0_START + 1)
-/* Number of bytes in parameter EEPROM space */
-#define DS2781_PARAM_EEPROM_SIZE       (DS2781_EEPROM_BLOCK1_END - \
-                                       DS2781_EEPROM_BLOCK1_START + 1)
-
-struct ds2781_device_info {
-       struct device *dev;
-       struct power_supply *bat;
-       struct power_supply_desc bat_desc;
-       struct device *w1_dev;
-};
-
-enum current_types {
-       CURRENT_NOW,
-       CURRENT_AVG,
-};
-
-static const char model[] = "DS2781";
-static const char manufacturer[] = "Maxim/Dallas";
-
-static inline struct ds2781_device_info *
-to_ds2781_device_info(struct power_supply *psy)
-{
-       return power_supply_get_drvdata(psy);
-}
-
-static inline struct power_supply *to_power_supply(struct device *dev)
-{
-       return dev_get_drvdata(dev);
-}
-
-static inline int ds2781_battery_io(struct ds2781_device_info *dev_info,
-       char *buf, int addr, size_t count, int io)
-{
-       return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io);
-}
-
-static int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
-               int addr, size_t count)
-{
-       return ds2781_battery_io(dev_info, buf, addr, count, 0);
-}
-
-static inline int ds2781_read8(struct ds2781_device_info *dev_info, u8 *val,
-       int addr)
-{
-       return ds2781_battery_io(dev_info, val, addr, sizeof(u8), 0);
-}
-
-static int ds2781_read16(struct ds2781_device_info *dev_info, s16 *val,
-       int addr)
-{
-       int ret;
-       u8 raw[2];
-
-       ret = ds2781_battery_io(dev_info, raw, addr, sizeof(raw), 0);
-       if (ret < 0)
-               return ret;
-
-       *val = (raw[0] << 8) | raw[1];
-
-       return 0;
-}
-
-static inline int ds2781_read_block(struct ds2781_device_info *dev_info,
-       u8 *val, int addr, size_t count)
-{
-       return ds2781_battery_io(dev_info, val, addr, count, 0);
-}
-
-static inline int ds2781_write(struct ds2781_device_info *dev_info, u8 *val,
-       int addr, size_t count)
-{
-       return ds2781_battery_io(dev_info, val, addr, count, 1);
-}
-
-static inline int ds2781_store_eeprom(struct device *dev, int addr)
-{
-       return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_COPY_DATA);
-}
-
-static inline int ds2781_recall_eeprom(struct device *dev, int addr)
-{
-       return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_RECALL_DATA);
-}
-
-static int ds2781_save_eeprom(struct ds2781_device_info *dev_info, int reg)
-{
-       int ret;
-
-       ret = ds2781_store_eeprom(dev_info->w1_dev, reg);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2781_recall_eeprom(dev_info->w1_dev, reg);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-/* Set sense resistor value in mhos */
-static int ds2781_set_sense_register(struct ds2781_device_info *dev_info,
-       u8 conductance)
-{
-       int ret;
-
-       ret = ds2781_write(dev_info, &conductance,
-                               DS2781_RSNSP, sizeof(u8));
-       if (ret < 0)
-               return ret;
-
-       return ds2781_save_eeprom(dev_info, DS2781_RSNSP);
-}
-
-/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */
-static int ds2781_get_rsgain_register(struct ds2781_device_info *dev_info,
-       u16 *rsgain)
-{
-       return ds2781_read16(dev_info, rsgain, DS2781_RSGAIN_MSB);
-}
-
-/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */
-static int ds2781_set_rsgain_register(struct ds2781_device_info *dev_info,
-       u16 rsgain)
-{
-       int ret;
-       u8 raw[] = {rsgain >> 8, rsgain & 0xFF};
-
-       ret = ds2781_write(dev_info, raw,
-                               DS2781_RSGAIN_MSB, sizeof(raw));
-       if (ret < 0)
-               return ret;
-
-       return ds2781_save_eeprom(dev_info, DS2781_RSGAIN_MSB);
-}
-
-static int ds2781_get_voltage(struct ds2781_device_info *dev_info,
-       int *voltage_uV)
-{
-       int ret;
-       char val[2];
-       int voltage_raw;
-
-       ret = w1_ds2781_read(dev_info, val, DS2781_VOLT_MSB, 2 * sizeof(u8));
-       if (ret < 0)
-               return ret;
-       /*
-        * The voltage value is located in 10 bits across the voltage MSB
-        * and LSB registers in two's compliment form
-        * Sign bit of the voltage value is in bit 7 of the voltage MSB register
-        * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the
-        * voltage MSB register
-        * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the
-        * voltage LSB register
-        */
-       voltage_raw = (val[0] << 3) |
-               (val[1] >> 5);
-
-       /* DS2781 reports voltage in units of 9.76mV, but the battery class
-        * reports in units of uV, so convert by multiplying by 9760. */
-       *voltage_uV = voltage_raw * 9760;
-
-       return 0;
-}
-
-static int ds2781_get_temperature(struct ds2781_device_info *dev_info,
-       int *temp)
-{
-       int ret;
-       char val[2];
-       int temp_raw;
-
-       ret = w1_ds2781_read(dev_info, val, DS2781_TEMP_MSB, 2 * sizeof(u8));
-       if (ret < 0)
-               return ret;
-       /*
-        * The temperature value is located in 10 bits across the temperature
-        * MSB and LSB registers in two's compliment form
-        * Sign bit of the temperature value is in bit 7 of the temperature
-        * MSB register
-        * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the
-        * temperature MSB register
-        * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the
-        * temperature LSB register
-        */
-       temp_raw = ((val[0]) << 3) |
-               (val[1] >> 5);
-       *temp = temp_raw + (temp_raw / 4);
-
-       return 0;
-}
-
-static int ds2781_get_current(struct ds2781_device_info *dev_info,
-       enum current_types type, int *current_uA)
-{
-       int ret, sense_res;
-       s16 current_raw;
-       u8 sense_res_raw, reg_msb;
-
-       /*
-        * The units of measurement for current are dependent on the value of
-        * the sense resistor.
-        */
-       ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP);
-       if (ret < 0)
-               return ret;
-
-       if (sense_res_raw == 0) {
-               dev_err(dev_info->dev, "sense resistor value is 0\n");
-               return -EINVAL;
-       }
-       sense_res = 1000 / sense_res_raw;
-
-       if (type == CURRENT_NOW)
-               reg_msb = DS2781_CURRENT_MSB;
-       else if (type == CURRENT_AVG)
-               reg_msb = DS2781_IAVG_MSB;
-       else
-               return -EINVAL;
-
-       /*
-        * The current value is located in 16 bits across the current MSB
-        * and LSB registers in two's compliment form
-        * Sign bit of the current value is in bit 7 of the current MSB register
-        * Bits 14 - 8 of the current value are in bits 6 - 0 of the current
-        * MSB register
-        * Bits 7 - 0 of the current value are in bits 7 - 0 of the current
-        * LSB register
-        */
-       ret = ds2781_read16(dev_info, &current_raw, reg_msb);
-       if (ret < 0)
-               return ret;
-
-       *current_uA = current_raw * (DS2781_CURRENT_UNITS / sense_res);
-       return 0;
-}
-
-static int ds2781_get_accumulated_current(struct ds2781_device_info *dev_info,
-       int *accumulated_current)
-{
-       int ret, sense_res;
-       s16 current_raw;
-       u8 sense_res_raw;
-
-       /*
-        * The units of measurement for accumulated current are dependent on
-        * the value of the sense resistor.
-        */
-       ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP);
-       if (ret < 0)
-               return ret;
-
-       if (sense_res_raw == 0) {
-               dev_err(dev_info->dev, "sense resistor value is 0\n");
-               return -EINVAL;
-       }
-       sense_res = 1000 / sense_res_raw;
-
-       /*
-        * The ACR value is located in 16 bits across the ACR MSB and
-        * LSB registers
-        * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR
-        * MSB register
-        * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR
-        * LSB register
-        */
-       ret = ds2781_read16(dev_info, &current_raw, DS2781_ACR_MSB);
-       if (ret < 0)
-               return ret;
-
-       *accumulated_current = current_raw * (DS2781_CHARGE_UNITS / sense_res);
-       return 0;
-}
-
-static int ds2781_get_capacity(struct ds2781_device_info *dev_info,
-       int *capacity)
-{
-       int ret;
-       u8 raw;
-
-       ret = ds2781_read8(dev_info, &raw, DS2781_RARC);
-       if (ret < 0)
-               return ret;
-
-       *capacity = raw;
-       return 0;
-}
-
-static int ds2781_get_status(struct ds2781_device_info *dev_info, int *status)
-{
-       int ret, current_uA, capacity;
-
-       ret = ds2781_get_current(dev_info, CURRENT_NOW, &current_uA);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2781_get_capacity(dev_info, &capacity);
-       if (ret < 0)
-               return ret;
-
-       if (power_supply_am_i_supplied(dev_info->bat)) {
-               if (capacity == 100)
-                       *status = POWER_SUPPLY_STATUS_FULL;
-               else if (current_uA > 50000)
-                       *status = POWER_SUPPLY_STATUS_CHARGING;
-               else
-                       *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       } else {
-               *status = POWER_SUPPLY_STATUS_DISCHARGING;
-       }
-       return 0;
-}
-
-static int ds2781_get_charge_now(struct ds2781_device_info *dev_info,
-       int *charge_now)
-{
-       int ret;
-       u16 charge_raw;
-
-       /*
-        * The RAAC value is located in 16 bits across the RAAC MSB and
-        * LSB registers
-        * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC
-        * MSB register
-        * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC
-        * LSB register
-        */
-       ret = ds2781_read16(dev_info, &charge_raw, DS2781_RAAC_MSB);
-       if (ret < 0)
-               return ret;
-
-       *charge_now = charge_raw * 1600;
-       return 0;
-}
-
-static int ds2781_get_control_register(struct ds2781_device_info *dev_info,
-       u8 *control_reg)
-{
-       return ds2781_read8(dev_info, control_reg, DS2781_CONTROL);
-}
-
-static int ds2781_set_control_register(struct ds2781_device_info *dev_info,
-       u8 control_reg)
-{
-       int ret;
-
-       ret = ds2781_write(dev_info, &control_reg,
-                               DS2781_CONTROL, sizeof(u8));
-       if (ret < 0)
-               return ret;
-
-       return ds2781_save_eeprom(dev_info, DS2781_CONTROL);
-}
-
-static int ds2781_battery_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       int ret = 0;
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = ds2781_get_voltage(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = ds2781_get_temperature(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = model;
-               break;
-
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = manufacturer;
-               break;
-
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = ds2781_get_current(dev_info, CURRENT_NOW, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               ret = ds2781_get_current(dev_info, CURRENT_AVG, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = ds2781_get_status(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = ds2781_get_capacity(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
-               ret = ds2781_get_accumulated_current(dev_info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               ret = ds2781_get_charge_now(dev_info, &val->intval);
-               break;
-
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property ds2781_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CHARGE_COUNTER,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-};
-
-static ssize_t ds2781_get_pmod_enabled(struct device *dev,
-       struct device_attribute *attr,
-       char *buf)
-{
-       int ret;
-       u8 control_reg;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       /* Get power mode */
-       ret = ds2781_get_control_register(dev_info, &control_reg);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%d\n",
-                !!(control_reg & DS2781_CONTROL_PMOD));
-}
-
-static ssize_t ds2781_set_pmod_enabled(struct device *dev,
-       struct device_attribute *attr,
-       const char *buf,
-       size_t count)
-{
-       int ret;
-       u8 control_reg, new_setting;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       /* Set power mode */
-       ret = ds2781_get_control_register(dev_info, &control_reg);
-       if (ret < 0)
-               return ret;
-
-       ret = kstrtou8(buf, 0, &new_setting);
-       if (ret < 0)
-               return ret;
-
-       if ((new_setting != 0) && (new_setting != 1)) {
-               dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n");
-               return -EINVAL;
-       }
-
-       if (new_setting)
-               control_reg |= DS2781_CONTROL_PMOD;
-       else
-               control_reg &= ~DS2781_CONTROL_PMOD;
-
-       ret = ds2781_set_control_register(dev_info, control_reg);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t ds2781_get_sense_resistor_value(struct device *dev,
-       struct device_attribute *attr,
-       char *buf)
-{
-       int ret;
-       u8 sense_resistor;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       ret = ds2781_read8(dev_info, &sense_resistor, DS2781_RSNSP);
-       if (ret < 0)
-               return ret;
-
-       ret = sprintf(buf, "%d\n", sense_resistor);
-       return ret;
-}
-
-static ssize_t ds2781_set_sense_resistor_value(struct device *dev,
-       struct device_attribute *attr,
-       const char *buf,
-       size_t count)
-{
-       int ret;
-       u8 new_setting;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       ret = kstrtou8(buf, 0, &new_setting);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2781_set_sense_register(dev_info, new_setting);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t ds2781_get_rsgain_setting(struct device *dev,
-       struct device_attribute *attr,
-       char *buf)
-{
-       int ret;
-       u16 rsgain;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       ret = ds2781_get_rsgain_register(dev_info, &rsgain);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%d\n", rsgain);
-}
-
-static ssize_t ds2781_set_rsgain_setting(struct device *dev,
-       struct device_attribute *attr,
-       const char *buf,
-       size_t count)
-{
-       int ret;
-       u16 new_setting;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       ret = kstrtou16(buf, 0, &new_setting);
-       if (ret < 0)
-               return ret;
-
-       /* Gain can only be from 0 to 1.999 in steps of .001 */
-       if (new_setting > 1999) {
-               dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n");
-               return -EINVAL;
-       }
-
-       ret = ds2781_set_rsgain_register(dev_info, new_setting);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t ds2781_get_pio_pin(struct device *dev,
-       struct device_attribute *attr,
-       char *buf)
-{
-       int ret;
-       u8 sfr;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       ret = ds2781_read8(dev_info, &sfr, DS2781_SFR);
-       if (ret < 0)
-               return ret;
-
-       ret = sprintf(buf, "%d\n", sfr & DS2781_SFR_PIOSC);
-       return ret;
-}
-
-static ssize_t ds2781_set_pio_pin(struct device *dev,
-       struct device_attribute *attr,
-       const char *buf,
-       size_t count)
-{
-       int ret;
-       u8 new_setting;
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       ret = kstrtou8(buf, 0, &new_setting);
-       if (ret < 0)
-               return ret;
-
-       if ((new_setting != 0) && (new_setting != 1)) {
-               dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n");
-               return -EINVAL;
-       }
-
-       ret = ds2781_write(dev_info, &new_setting,
-                               DS2781_SFR, sizeof(u8));
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
-                               struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       return ds2781_read_block(dev_info, buf,
-                               DS2781_EEPROM_BLOCK1_START + off, count);
-}
-
-static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
-                               struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-       int ret;
-
-       ret = ds2781_write(dev_info, buf,
-                               DS2781_EEPROM_BLOCK1_START + off, count);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK1_START);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static struct bin_attribute ds2781_param_eeprom_bin_attr = {
-       .attr = {
-               .name = "param_eeprom",
-               .mode = S_IRUGO | S_IWUSR,
-       },
-       .size = DS2781_PARAM_EEPROM_SIZE,
-       .read = ds2781_read_param_eeprom_bin,
-       .write = ds2781_write_param_eeprom_bin,
-};
-
-static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
-                               struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-
-       return ds2781_read_block(dev_info, buf,
-                               DS2781_EEPROM_BLOCK0_START + off, count);
-
-}
-
-static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
-                               struct kobject *kobj,
-                               struct bin_attribute *bin_attr,
-                               char *buf, loff_t off, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = to_power_supply(dev);
-       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
-       int ret;
-
-       ret = ds2781_write(dev_info, buf,
-                               DS2781_EEPROM_BLOCK0_START + off, count);
-       if (ret < 0)
-               return ret;
-
-       ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK0_START);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static struct bin_attribute ds2781_user_eeprom_bin_attr = {
-       .attr = {
-               .name = "user_eeprom",
-               .mode = S_IRUGO | S_IWUSR,
-       },
-       .size = DS2781_USER_EEPROM_SIZE,
-       .read = ds2781_read_user_eeprom_bin,
-       .write = ds2781_write_user_eeprom_bin,
-};
-
-static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled,
-       ds2781_set_pmod_enabled);
-static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR,
-       ds2781_get_sense_resistor_value, ds2781_set_sense_resistor_value);
-static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2781_get_rsgain_setting,
-       ds2781_set_rsgain_setting);
-static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2781_get_pio_pin,
-       ds2781_set_pio_pin);
-
-
-static struct attribute *ds2781_attributes[] = {
-       &dev_attr_pmod_enabled.attr,
-       &dev_attr_sense_resistor_value.attr,
-       &dev_attr_rsgain_setting.attr,
-       &dev_attr_pio_pin.attr,
-       NULL
-};
-
-static const struct attribute_group ds2781_attr_group = {
-       .attrs = ds2781_attributes,
-};
-
-static int ds2781_battery_probe(struct platform_device *pdev)
-{
-       struct power_supply_config psy_cfg = {};
-       int ret = 0;
-       struct ds2781_device_info *dev_info;
-
-       dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
-       if (!dev_info)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, dev_info);
-
-       dev_info->dev                   = &pdev->dev;
-       dev_info->w1_dev                = pdev->dev.parent;
-       dev_info->bat_desc.name         = dev_name(&pdev->dev);
-       dev_info->bat_desc.type         = POWER_SUPPLY_TYPE_BATTERY;
-       dev_info->bat_desc.properties   = ds2781_battery_props;
-       dev_info->bat_desc.num_properties = ARRAY_SIZE(ds2781_battery_props);
-       dev_info->bat_desc.get_property = ds2781_battery_get_property;
-
-       psy_cfg.drv_data                = dev_info;
-
-       dev_info->bat = power_supply_register(&pdev->dev, &dev_info->bat_desc,
-                                               &psy_cfg);
-       if (IS_ERR(dev_info->bat)) {
-               dev_err(dev_info->dev, "failed to register battery\n");
-               ret = PTR_ERR(dev_info->bat);
-               goto fail;
-       }
-
-       ret = sysfs_create_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
-       if (ret) {
-               dev_err(dev_info->dev, "failed to create sysfs group\n");
-               goto fail_unregister;
-       }
-
-       ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
-                                       &ds2781_param_eeprom_bin_attr);
-       if (ret) {
-               dev_err(dev_info->dev,
-                               "failed to create param eeprom bin file");
-               goto fail_remove_group;
-       }
-
-       ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
-                                       &ds2781_user_eeprom_bin_attr);
-       if (ret) {
-               dev_err(dev_info->dev,
-                               "failed to create user eeprom bin file");
-               goto fail_remove_bin_file;
-       }
-
-       return 0;
-
-fail_remove_bin_file:
-       sysfs_remove_bin_file(&dev_info->bat->dev.kobj,
-                               &ds2781_param_eeprom_bin_attr);
-fail_remove_group:
-       sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
-fail_unregister:
-       power_supply_unregister(dev_info->bat);
-fail:
-       return ret;
-}
-
-static int ds2781_battery_remove(struct platform_device *pdev)
-{
-       struct ds2781_device_info *dev_info = platform_get_drvdata(pdev);
-
-       /*
-        * Remove attributes before unregistering power supply
-        * because 'bat' will be freed on power_supply_unregister() call.
-        */
-       sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
-
-       power_supply_unregister(dev_info->bat);
-
-       return 0;
-}
-
-static struct platform_driver ds2781_battery_driver = {
-       .driver = {
-               .name = "ds2781-battery",
-       },
-       .probe    = ds2781_battery_probe,
-       .remove   = ds2781_battery_remove,
-};
-module_platform_driver(ds2781_battery_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>");
-MODULE_DESCRIPTION("Maxim/Dallas DS2781 Stand-Alone Fuel Gauage IC driver");
-MODULE_ALIAS("platform:ds2781-battery");
-
diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c
deleted file mode 100644 (file)
index a1b7e05..0000000
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC
- *
- * Copyright (C) 2009 Bluewater Systems Ltd
- *
- * Author: Ryan Mallon
- *
- * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
- *
- * UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/swab.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/idr.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/ds2782_battery.h>
-
-#define DS2782_REG_RARC                0x06    /* Remaining active relative capacity */
-
-#define DS278x_REG_VOLT_MSB    0x0c
-#define DS278x_REG_TEMP_MSB    0x0a
-#define DS278x_REG_CURRENT_MSB 0x0e
-
-/* EEPROM Block */
-#define DS2782_REG_RSNSP       0x69    /* Sense resistor value */
-
-/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
-#define DS2782_CURRENT_UNITS   1563
-
-#define DS2786_REG_RARC                0x02    /* Remaining active relative capacity */
-
-#define DS2786_CURRENT_UNITS   25
-
-#define DS278x_DELAY           1000
-
-struct ds278x_info;
-
-struct ds278x_battery_ops {
-       int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
-       int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV);
-       int (*get_battery_capacity)(struct ds278x_info *info, int *capacity);
-};
-
-#define to_ds278x_info(x) power_supply_get_drvdata(x)
-
-struct ds278x_info {
-       struct i2c_client       *client;
-       struct power_supply     *battery;
-       struct power_supply_desc        battery_desc;
-       const struct ds278x_battery_ops *ops;
-       struct delayed_work     bat_work;
-       int                     id;
-       int                     rsns;
-       int                     capacity;
-       int                     status;         /* State Of Charge */
-};
-
-static DEFINE_IDR(battery_id);
-static DEFINE_MUTEX(battery_lock);
-
-static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
-{
-       int ret;
-
-       ret = i2c_smbus_read_byte_data(info->client, reg);
-       if (ret < 0) {
-               dev_err(&info->client->dev, "register read failed\n");
-               return ret;
-       }
-
-       *val = ret;
-       return 0;
-}
-
-static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
-                                   s16 *val)
-{
-       int ret;
-
-       ret = i2c_smbus_read_word_data(info->client, reg_msb);
-       if (ret < 0) {
-               dev_err(&info->client->dev, "register read failed\n");
-               return ret;
-       }
-
-       *val = swab16(ret);
-       return 0;
-}
-
-static int ds278x_get_temp(struct ds278x_info *info, int *temp)
-{
-       s16 raw;
-       int err;
-
-       /*
-        * Temperature is measured in units of 0.125 degrees celcius, the
-        * power_supply class measures temperature in tenths of degrees
-        * celsius. The temperature value is stored as a 10 bit number, plus
-        * sign in the upper bits of a 16 bit register.
-        */
-       err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
-       if (err)
-               return err;
-       *temp = ((raw / 32) * 125) / 100;
-       return 0;
-}
-
-static int ds2782_get_current(struct ds278x_info *info, int *current_uA)
-{
-       int sense_res;
-       int err;
-       u8 sense_res_raw;
-       s16 raw;
-
-       /*
-        * The units of measurement for current are dependent on the value of
-        * the sense resistor.
-        */
-       err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
-       if (err)
-               return err;
-       if (sense_res_raw == 0) {
-               dev_err(&info->client->dev, "sense resistor value is 0\n");
-               return -ENXIO;
-       }
-       sense_res = 1000 / sense_res_raw;
-
-       dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n",
-               sense_res);
-       err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
-       if (err)
-               return err;
-       *current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
-       return 0;
-}
-
-static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV)
-{
-       s16 raw;
-       int err;
-
-       /*
-        * Voltage is measured in units of 4.88mV. The voltage is stored as
-        * a 10-bit number plus sign, in the upper bits of a 16-bit register
-        */
-       err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
-       if (err)
-               return err;
-       *voltage_uV = (raw / 32) * 4800;
-       return 0;
-}
-
-static int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
-{
-       int err;
-       u8 raw;
-
-       err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
-       if (err)
-               return err;
-       *capacity = raw;
-       return 0;
-}
-
-static int ds2786_get_current(struct ds278x_info *info, int *current_uA)
-{
-       int err;
-       s16 raw;
-
-       err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
-       if (err)
-               return err;
-       *current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
-       return 0;
-}
-
-static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV)
-{
-       s16 raw;
-       int err;
-
-       /*
-        * Voltage is measured in units of 1.22mV. The voltage is stored as
-        * a 12-bit number plus sign, in the upper bits of a 16-bit register
-        */
-       err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
-       if (err)
-               return err;
-       *voltage_uV = (raw / 8) * 1220;
-       return 0;
-}
-
-static int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
-{
-       int err;
-       u8 raw;
-
-       err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
-       if (err)
-               return err;
-       /* Relative capacity is displayed with resolution 0.5 % */
-       *capacity = raw/2 ;
-       return 0;
-}
-
-static int ds278x_get_status(struct ds278x_info *info, int *status)
-{
-       int err;
-       int current_uA;
-       int capacity;
-
-       err = info->ops->get_battery_current(info, &current_uA);
-       if (err)
-               return err;
-
-       err = info->ops->get_battery_capacity(info, &capacity);
-       if (err)
-               return err;
-
-       info->capacity = capacity;
-
-       if (capacity == 100)
-               *status = POWER_SUPPLY_STATUS_FULL;
-       else if (current_uA == 0)
-               *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       else if (current_uA < 0)
-               *status = POWER_SUPPLY_STATUS_DISCHARGING;
-       else
-               *status = POWER_SUPPLY_STATUS_CHARGING;
-
-       return 0;
-}
-
-static int ds278x_battery_get_property(struct power_supply *psy,
-                                      enum power_supply_property prop,
-                                      union power_supply_propval *val)
-{
-       struct ds278x_info *info = to_ds278x_info(psy);
-       int ret;
-
-       switch (prop) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = ds278x_get_status(info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = info->ops->get_battery_capacity(info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = info->ops->get_battery_voltage(info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = info->ops->get_battery_current(info, &val->intval);
-               break;
-
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = ds278x_get_temp(info, &val->intval);
-               break;
-
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-static void ds278x_bat_update(struct ds278x_info *info)
-{
-       int old_status = info->status;
-       int old_capacity = info->capacity;
-
-       ds278x_get_status(info, &info->status);
-
-       if ((old_status != info->status) || (old_capacity != info->capacity))
-               power_supply_changed(info->battery);
-}
-
-static void ds278x_bat_work(struct work_struct *work)
-{
-       struct ds278x_info *info;
-
-       info = container_of(work, struct ds278x_info, bat_work.work);
-       ds278x_bat_update(info);
-
-       schedule_delayed_work(&info->bat_work, DS278x_DELAY);
-}
-
-static enum power_supply_property ds278x_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static void ds278x_power_supply_init(struct power_supply_desc *battery)
-{
-       battery->type                   = POWER_SUPPLY_TYPE_BATTERY;
-       battery->properties             = ds278x_battery_props;
-       battery->num_properties         = ARRAY_SIZE(ds278x_battery_props);
-       battery->get_property           = ds278x_battery_get_property;
-       battery->external_power_changed = NULL;
-}
-
-static int ds278x_battery_remove(struct i2c_client *client)
-{
-       struct ds278x_info *info = i2c_get_clientdata(client);
-
-       power_supply_unregister(info->battery);
-       kfree(info->battery_desc.name);
-
-       mutex_lock(&battery_lock);
-       idr_remove(&battery_id, info->id);
-       mutex_unlock(&battery_lock);
-
-       cancel_delayed_work(&info->bat_work);
-
-       kfree(info);
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int ds278x_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct ds278x_info *info = i2c_get_clientdata(client);
-
-       cancel_delayed_work(&info->bat_work);
-       return 0;
-}
-
-static int ds278x_resume(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct ds278x_info *info = i2c_get_clientdata(client);
-
-       schedule_delayed_work(&info->bat_work, DS278x_DELAY);
-       return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static SIMPLE_DEV_PM_OPS(ds278x_battery_pm_ops, ds278x_suspend, ds278x_resume);
-
-enum ds278x_num_id {
-       DS2782 = 0,
-       DS2786,
-};
-
-static const struct ds278x_battery_ops ds278x_ops[] = {
-       [DS2782] = {
-               .get_battery_current  = ds2782_get_current,
-               .get_battery_voltage  = ds2782_get_voltage,
-               .get_battery_capacity = ds2782_get_capacity,
-       },
-       [DS2786] = {
-               .get_battery_current  = ds2786_get_current,
-               .get_battery_voltage  = ds2786_get_voltage,
-               .get_battery_capacity = ds2786_get_capacity,
-       }
-};
-
-static int ds278x_battery_probe(struct i2c_client *client,
-                               const struct i2c_device_id *id)
-{
-       struct ds278x_platform_data *pdata = client->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       struct ds278x_info *info;
-       int ret;
-       int num;
-
-       /*
-        * ds2786 should have the sense resistor value set
-        * in the platform data
-        */
-       if (id->driver_data == DS2786 && !pdata) {
-               dev_err(&client->dev, "missing platform data for ds2786\n");
-               return -EINVAL;
-       }
-
-       /* Get an ID for this battery */
-       mutex_lock(&battery_lock);
-       ret = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
-       mutex_unlock(&battery_lock);
-       if (ret < 0)
-               goto fail_id;
-       num = ret;
-
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
-       if (!info) {
-               ret = -ENOMEM;
-               goto fail_info;
-       }
-
-       info->battery_desc.name = kasprintf(GFP_KERNEL, "%s-%d",
-                                           client->name, num);
-       if (!info->battery_desc.name) {
-               ret = -ENOMEM;
-               goto fail_name;
-       }
-
-       if (id->driver_data == DS2786)
-               info->rsns = pdata->rsns;
-
-       i2c_set_clientdata(client, info);
-       info->client = client;
-       info->id = num;
-       info->ops  = &ds278x_ops[id->driver_data];
-       ds278x_power_supply_init(&info->battery_desc);
-       psy_cfg.drv_data = info;
-
-       info->capacity = 100;
-       info->status = POWER_SUPPLY_STATUS_FULL;
-
-       INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work);
-
-       info->battery = power_supply_register(&client->dev,
-                                             &info->battery_desc, &psy_cfg);
-       if (IS_ERR(info->battery)) {
-               dev_err(&client->dev, "failed to register battery\n");
-               ret = PTR_ERR(info->battery);
-               goto fail_register;
-       } else {
-               schedule_delayed_work(&info->bat_work, DS278x_DELAY);
-       }
-
-       return 0;
-
-fail_register:
-       kfree(info->battery_desc.name);
-fail_name:
-       kfree(info);
-fail_info:
-       mutex_lock(&battery_lock);
-       idr_remove(&battery_id, num);
-       mutex_unlock(&battery_lock);
-fail_id:
-       return ret;
-}
-
-static const struct i2c_device_id ds278x_id[] = {
-       {"ds2782", DS2782},
-       {"ds2786", DS2786},
-       {},
-};
-MODULE_DEVICE_TABLE(i2c, ds278x_id);
-
-static struct i2c_driver ds278x_battery_driver = {
-       .driver         = {
-               .name   = "ds2782-battery",
-               .pm     = &ds278x_battery_pm_ops,
-       },
-       .probe          = ds278x_battery_probe,
-       .remove         = ds278x_battery_remove,
-       .id_table       = ds278x_id,
-};
-module_i2c_driver(ds278x_battery_driver);
-
-MODULE_AUTHOR("Ryan Mallon");
-MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c
deleted file mode 100644 (file)
index edb36bf..0000000
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Generic battery driver code using IIO
- * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com>
- * based on jz4740-battery.c
- * based on s3c_adc_battery.c
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file COPYING in the main directory of this archive for
- * more details.
- *
- */
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/gpio.h>
-#include <linux/err.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/iio/consumer.h>
-#include <linux/iio/types.h>
-#include <linux/power/generic-adc-battery.h>
-
-#define JITTER_DEFAULT 10 /* hope 10ms is enough */
-
-enum gab_chan_type {
-       GAB_VOLTAGE = 0,
-       GAB_CURRENT,
-       GAB_POWER,
-       GAB_MAX_CHAN_TYPE
-};
-
-/*
- * gab_chan_name suggests the standard channel names for commonly used
- * channel types.
- */
-static const char *const gab_chan_name[] = {
-       [GAB_VOLTAGE]   = "voltage",
-       [GAB_CURRENT]   = "current",
-       [GAB_POWER]             = "power",
-};
-
-struct gab {
-       struct power_supply             *psy;
-       struct power_supply_desc        psy_desc;
-       struct iio_channel      *channel[GAB_MAX_CHAN_TYPE];
-       struct gab_platform_data        *pdata;
-       struct delayed_work bat_work;
-       int     level;
-       int     status;
-       bool cable_plugged;
-};
-
-static struct gab *to_generic_bat(struct power_supply *psy)
-{
-       return power_supply_get_drvdata(psy);
-}
-
-static void gab_ext_power_changed(struct power_supply *psy)
-{
-       struct gab *adc_bat = to_generic_bat(psy);
-
-       schedule_delayed_work(&adc_bat->bat_work, msecs_to_jiffies(0));
-}
-
-static const enum power_supply_property gab_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-};
-
-/*
- * This properties are set based on the received platform data and this
- * should correspond one-to-one with enum chan_type.
- */
-static const enum power_supply_property gab_dyn_props[] = {
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_POWER_NOW,
-};
-
-static bool gab_charge_finished(struct gab *adc_bat)
-{
-       struct gab_platform_data *pdata = adc_bat->pdata;
-       bool ret = gpio_get_value(pdata->gpio_charge_finished);
-       bool inv = pdata->gpio_inverted;
-
-       if (!gpio_is_valid(pdata->gpio_charge_finished))
-               return false;
-       return ret ^ inv;
-}
-
-static int gab_get_status(struct gab *adc_bat)
-{
-       struct gab_platform_data *pdata = adc_bat->pdata;
-       struct power_supply_info *bat_info;
-
-       bat_info = &pdata->battery_info;
-       if (adc_bat->level == bat_info->charge_full_design)
-               return POWER_SUPPLY_STATUS_FULL;
-       return adc_bat->status;
-}
-
-static enum gab_chan_type gab_prop_to_chan(enum power_supply_property psp)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_POWER_NOW:
-               return GAB_POWER;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               return GAB_VOLTAGE;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               return GAB_CURRENT;
-       default:
-               WARN_ON(1);
-               break;
-       }
-       return GAB_POWER;
-}
-
-static int read_channel(struct gab *adc_bat, enum power_supply_property psp,
-               int *result)
-{
-       int ret;
-       int chan_index;
-
-       chan_index = gab_prop_to_chan(psp);
-       ret = iio_read_channel_processed(adc_bat->channel[chan_index],
-                       result);
-       if (ret < 0)
-               pr_err("read channel error\n");
-       return ret;
-}
-
-static int gab_get_property(struct power_supply *psy,
-               enum power_supply_property psp, union power_supply_propval *val)
-{
-       struct gab *adc_bat;
-       struct gab_platform_data *pdata;
-       struct power_supply_info *bat_info;
-       int result = 0;
-       int ret = 0;
-
-       adc_bat = to_generic_bat(psy);
-       if (!adc_bat) {
-               dev_err(&psy->dev, "no battery infos ?!\n");
-               return -EINVAL;
-       }
-       pdata = adc_bat->pdata;
-       bat_info = &pdata->battery_info;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = gab_get_status(adc_bat);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
-               val->intval = 0;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               val->intval = pdata->cal_charge(result);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-       case POWER_SUPPLY_PROP_POWER_NOW:
-               ret = read_channel(adc_bat, psp, &result);
-               if (ret < 0)
-                       goto err;
-               val->intval = result;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = bat_info->technology;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = bat_info->voltage_min_design;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = bat_info->voltage_max_design;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               val->intval = bat_info->charge_full_design;
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = bat_info->name;
-               break;
-       default:
-               return -EINVAL;
-       }
-err:
-       return ret;
-}
-
-static void gab_work(struct work_struct *work)
-{
-       struct gab *adc_bat;
-       struct gab_platform_data *pdata;
-       struct delayed_work *delayed_work;
-       bool is_plugged;
-       int status;
-
-       delayed_work = to_delayed_work(work);
-       adc_bat = container_of(delayed_work, struct gab, bat_work);
-       pdata = adc_bat->pdata;
-       status = adc_bat->status;
-
-       is_plugged = power_supply_am_i_supplied(adc_bat->psy);
-       adc_bat->cable_plugged = is_plugged;
-
-       if (!is_plugged)
-               adc_bat->status =  POWER_SUPPLY_STATUS_DISCHARGING;
-       else if (gab_charge_finished(adc_bat))
-               adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       else
-               adc_bat->status = POWER_SUPPLY_STATUS_CHARGING;
-
-       if (status != adc_bat->status)
-               power_supply_changed(adc_bat->psy);
-}
-
-static irqreturn_t gab_charged(int irq, void *dev_id)
-{
-       struct gab *adc_bat = dev_id;
-       struct gab_platform_data *pdata = adc_bat->pdata;
-       int delay;
-
-       delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
-       schedule_delayed_work(&adc_bat->bat_work,
-                       msecs_to_jiffies(delay));
-       return IRQ_HANDLED;
-}
-
-static int gab_probe(struct platform_device *pdev)
-{
-       struct gab *adc_bat;
-       struct power_supply_desc *psy_desc;
-       struct power_supply_config psy_cfg = {};
-       struct gab_platform_data *pdata = pdev->dev.platform_data;
-       enum power_supply_property *properties;
-       int ret = 0;
-       int chan;
-       int index = 0;
-
-       adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL);
-       if (!adc_bat) {
-               dev_err(&pdev->dev, "failed to allocate memory\n");
-               return -ENOMEM;
-       }
-
-       psy_cfg.drv_data = adc_bat;
-       psy_desc = &adc_bat->psy_desc;
-       psy_desc->name = pdata->battery_info.name;
-
-       /* bootup default values for the battery */
-       adc_bat->cable_plugged = false;
-       adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
-       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
-       psy_desc->get_property = gab_get_property;
-       psy_desc->external_power_changed = gab_ext_power_changed;
-       adc_bat->pdata = pdata;
-
-       /*
-        * copying the static properties and allocating extra memory for holding
-        * the extra configurable properties received from platform data.
-        */
-       psy_desc->properties = kcalloc(ARRAY_SIZE(gab_props) +
-                                       ARRAY_SIZE(gab_chan_name),
-                                       sizeof(*psy_desc->properties),
-                                       GFP_KERNEL);
-       if (!psy_desc->properties) {
-               ret = -ENOMEM;
-               goto first_mem_fail;
-       }
-
-       memcpy(psy_desc->properties, gab_props, sizeof(gab_props));
-       properties = (enum power_supply_property *)
-                       ((char *)psy_desc->properties + sizeof(gab_props));
-
-       /*
-        * getting channel from iio and copying the battery properties
-        * based on the channel supported by consumer device.
-        */
-       for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
-               adc_bat->channel[chan] = iio_channel_get(&pdev->dev,
-                                                        gab_chan_name[chan]);
-               if (IS_ERR(adc_bat->channel[chan])) {
-                       ret = PTR_ERR(adc_bat->channel[chan]);
-                       adc_bat->channel[chan] = NULL;
-               } else {
-                       /* copying properties for supported channels only */
-                       memcpy(properties + sizeof(*(psy_desc->properties)) * index,
-                                       &gab_dyn_props[chan],
-                                       sizeof(gab_dyn_props[chan]));
-                       index++;
-               }
-       }
-
-       /* none of the channels are supported so let's bail out */
-       if (index == 0) {
-               ret = -ENODEV;
-               goto second_mem_fail;
-       }
-
-       /*
-        * Total number of properties is equal to static properties
-        * plus the dynamic properties.Some properties may not be set
-        * as come channels may be not be supported by the device.So
-        * we need to take care of that.
-        */
-       psy_desc->num_properties = ARRAY_SIZE(gab_props) + index;
-
-       adc_bat->psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
-       if (IS_ERR(adc_bat->psy)) {
-               ret = PTR_ERR(adc_bat->psy);
-               goto err_reg_fail;
-       }
-
-       INIT_DELAYED_WORK(&adc_bat->bat_work, gab_work);
-
-       if (gpio_is_valid(pdata->gpio_charge_finished)) {
-               int irq;
-               ret = gpio_request(pdata->gpio_charge_finished, "charged");
-               if (ret)
-                       goto gpio_req_fail;
-
-               irq = gpio_to_irq(pdata->gpio_charge_finished);
-               ret = request_any_context_irq(irq, gab_charged,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                               "battery charged", adc_bat);
-               if (ret < 0)
-                       goto err_gpio;
-       }
-
-       platform_set_drvdata(pdev, adc_bat);
-
-       /* Schedule timer to check current status */
-       schedule_delayed_work(&adc_bat->bat_work,
-                       msecs_to_jiffies(0));
-       return 0;
-
-err_gpio:
-       gpio_free(pdata->gpio_charge_finished);
-gpio_req_fail:
-       power_supply_unregister(adc_bat->psy);
-err_reg_fail:
-       for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
-               if (adc_bat->channel[chan])
-                       iio_channel_release(adc_bat->channel[chan]);
-       }
-second_mem_fail:
-       kfree(psy_desc->properties);
-first_mem_fail:
-       return ret;
-}
-
-static int gab_remove(struct platform_device *pdev)
-{
-       int chan;
-       struct gab *adc_bat = platform_get_drvdata(pdev);
-       struct gab_platform_data *pdata = adc_bat->pdata;
-
-       power_supply_unregister(adc_bat->psy);
-
-       if (gpio_is_valid(pdata->gpio_charge_finished)) {
-               free_irq(gpio_to_irq(pdata->gpio_charge_finished), adc_bat);
-               gpio_free(pdata->gpio_charge_finished);
-       }
-
-       for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
-               if (adc_bat->channel[chan])
-                       iio_channel_release(adc_bat->channel[chan]);
-       }
-
-       kfree(adc_bat->psy_desc.properties);
-       cancel_delayed_work(&adc_bat->bat_work);
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int gab_suspend(struct device *dev)
-{
-       struct gab *adc_bat = dev_get_drvdata(dev);
-
-       cancel_delayed_work_sync(&adc_bat->bat_work);
-       adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
-       return 0;
-}
-
-static int gab_resume(struct device *dev)
-{
-       struct gab *adc_bat = dev_get_drvdata(dev);
-       struct gab_platform_data *pdata = adc_bat->pdata;
-       int delay;
-
-       delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
-
-       /* Schedule timer to check current status */
-       schedule_delayed_work(&adc_bat->bat_work,
-                       msecs_to_jiffies(delay));
-       return 0;
-}
-
-static const struct dev_pm_ops gab_pm_ops = {
-       .suspend        = gab_suspend,
-       .resume         = gab_resume,
-};
-
-#define GAB_PM_OPS       (&gab_pm_ops)
-#else
-#define GAB_PM_OPS       (NULL)
-#endif
-
-static struct platform_driver gab_driver = {
-       .driver         = {
-               .name   = "generic-adc-battery",
-               .pm     = GAB_PM_OPS
-       },
-       .probe          = gab_probe,
-       .remove         = gab_remove,
-};
-module_platform_driver(gab_driver);
-
-MODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>");
-MODULE_DESCRIPTION("generic battery driver using IIO");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/goldfish_battery.c b/drivers/power/goldfish_battery.c
deleted file mode 100644 (file)
index f5c525e..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Power supply driver for the goldfish emulator
- *
- * Copyright (C) 2008 Google, Inc.
- * Copyright (C) 2012 Intel, Inc.
- * Copyright (C) 2013 Intel, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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/err.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/acpi.h>
-
-struct goldfish_battery_data {
-       void __iomem *reg_base;
-       int irq;
-       spinlock_t lock;
-
-       struct power_supply *battery;
-       struct power_supply *ac;
-};
-
-#define GOLDFISH_BATTERY_READ(data, addr) \
-       (readl(data->reg_base + addr))
-#define GOLDFISH_BATTERY_WRITE(data, addr, x) \
-       (writel(x, data->reg_base + addr))
-
-/*
- * Temporary variable used between goldfish_battery_probe() and
- * goldfish_battery_open().
- */
-static struct goldfish_battery_data *battery_data;
-
-enum {
-       /* status register */
-       BATTERY_INT_STATUS          = 0x00,
-       /* set this to enable IRQ */
-       BATTERY_INT_ENABLE          = 0x04,
-
-       BATTERY_AC_ONLINE       = 0x08,
-       BATTERY_STATUS          = 0x0C,
-       BATTERY_HEALTH          = 0x10,
-       BATTERY_PRESENT         = 0x14,
-       BATTERY_CAPACITY        = 0x18,
-
-       BATTERY_STATUS_CHANGED  = 1U << 0,
-       AC_STATUS_CHANGED       = 1U << 1,
-       BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
-};
-
-
-static int goldfish_ac_get_property(struct power_supply *psy,
-                       enum power_supply_property psp,
-                       union power_supply_propval *val)
-{
-       struct goldfish_battery_data *data = power_supply_get_drvdata(psy);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static int goldfish_battery_get_property(struct power_supply *psy,
-                                enum power_supply_property psp,
-                                union power_supply_propval *val)
-{
-       struct goldfish_battery_data *data = power_supply_get_drvdata(psy);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property goldfish_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CAPACITY,
-};
-
-static enum power_supply_property goldfish_ac_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
-{
-       unsigned long irq_flags;
-       struct goldfish_battery_data *data = dev_id;
-       uint32_t status;
-
-       spin_lock_irqsave(&data->lock, irq_flags);
-
-       /* read status flags, which will clear the interrupt */
-       status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
-       status &= BATTERY_INT_MASK;
-
-       if (status & BATTERY_STATUS_CHANGED)
-               power_supply_changed(data->battery);
-       if (status & AC_STATUS_CHANGED)
-               power_supply_changed(data->ac);
-
-       spin_unlock_irqrestore(&data->lock, irq_flags);
-       return status ? IRQ_HANDLED : IRQ_NONE;
-}
-
-static const struct power_supply_desc battery_desc = {
-       .properties     = goldfish_battery_props,
-       .num_properties = ARRAY_SIZE(goldfish_battery_props),
-       .get_property   = goldfish_battery_get_property,
-       .name           = "battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-};
-
-static const struct power_supply_desc ac_desc = {
-       .properties     = goldfish_ac_props,
-       .num_properties = ARRAY_SIZE(goldfish_ac_props),
-       .get_property   = goldfish_ac_get_property,
-       .name           = "ac",
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-};
-
-static int goldfish_battery_probe(struct platform_device *pdev)
-{
-       int ret;
-       struct resource *r;
-       struct goldfish_battery_data *data;
-       struct power_supply_config psy_cfg = {};
-
-       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
-       if (data == NULL)
-               return -ENOMEM;
-
-       spin_lock_init(&data->lock);
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (r == NULL) {
-               dev_err(&pdev->dev, "platform_get_resource failed\n");
-               return -ENODEV;
-       }
-
-       data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
-       if (data->reg_base == NULL) {
-               dev_err(&pdev->dev, "unable to remap MMIO\n");
-               return -ENOMEM;
-       }
-
-       data->irq = platform_get_irq(pdev, 0);
-       if (data->irq < 0) {
-               dev_err(&pdev->dev, "platform_get_irq failed\n");
-               return -ENODEV;
-       }
-
-       ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt,
-                                               IRQF_SHARED, pdev->name, data);
-       if (ret)
-               return ret;
-
-       psy_cfg.drv_data = data;
-
-       data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
-       if (IS_ERR(data->ac))
-               return PTR_ERR(data->ac);
-
-       data->battery = power_supply_register(&pdev->dev, &battery_desc,
-                                               &psy_cfg);
-       if (IS_ERR(data->battery)) {
-               power_supply_unregister(data->ac);
-               return PTR_ERR(data->battery);
-       }
-
-       platform_set_drvdata(pdev, data);
-       battery_data = data;
-
-       GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
-       return 0;
-}
-
-static int goldfish_battery_remove(struct platform_device *pdev)
-{
-       struct goldfish_battery_data *data = platform_get_drvdata(pdev);
-
-       power_supply_unregister(data->battery);
-       power_supply_unregister(data->ac);
-       battery_data = NULL;
-       return 0;
-}
-
-static const struct of_device_id goldfish_battery_of_match[] = {
-       { .compatible = "google,goldfish-battery", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, goldfish_battery_of_match);
-
-static const struct acpi_device_id goldfish_battery_acpi_match[] = {
-       { "GFSH0001", 0 },
-       { },
-};
-MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match);
-
-static struct platform_driver goldfish_battery_device = {
-       .probe          = goldfish_battery_probe,
-       .remove         = goldfish_battery_remove,
-       .driver = {
-               .name = "goldfish-battery",
-               .of_match_table = goldfish_battery_of_match,
-               .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match),
-       }
-};
-module_platform_driver(goldfish_battery_device);
-
-MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");
diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c
deleted file mode 100644 (file)
index c5869b1..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
- *  Driver for chargers which report their online status through a GPIO pin
- *
- *  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;  either version 2 of the License, or (at your
- *  option) any later version.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-
-#include <linux/power/gpio-charger.h>
-
-struct gpio_charger {
-       const struct gpio_charger_platform_data *pdata;
-       unsigned int irq;
-       bool wakeup_enabled;
-
-       struct power_supply *charger;
-       struct power_supply_desc charger_desc;
-};
-
-static irqreturn_t gpio_charger_irq(int irq, void *devid)
-{
-       struct power_supply *charger = devid;
-
-       power_supply_changed(charger);
-
-       return IRQ_HANDLED;
-}
-
-static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy)
-{
-       return power_supply_get_drvdata(psy);
-}
-
-static int gpio_charger_get_property(struct power_supply *psy,
-               enum power_supply_property psp, union power_supply_propval *val)
-{
-       struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
-       const struct gpio_charger_platform_data *pdata = gpio_charger->pdata;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = !!gpio_get_value_cansleep(pdata->gpio);
-               val->intval ^= pdata->gpio_active_low;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property gpio_charger_properties[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static
-struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)
-{
-       struct device_node *np = dev->of_node;
-       struct gpio_charger_platform_data *pdata;
-       const char *chargetype;
-       enum of_gpio_flags flags;
-       int ret;
-
-       if (!np)
-               return ERR_PTR(-ENOENT);
-
-       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return ERR_PTR(-ENOMEM);
-
-       pdata->name = np->name;
-
-       pdata->gpio = of_get_gpio_flags(np, 0, &flags);
-       if (pdata->gpio < 0) {
-               if (pdata->gpio != -EPROBE_DEFER)
-                       dev_err(dev, "could not get charger gpio\n");
-               return ERR_PTR(pdata->gpio);
-       }
-
-       pdata->gpio_active_low = !!(flags & OF_GPIO_ACTIVE_LOW);
-
-       pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
-       ret = of_property_read_string(np, "charger-type", &chargetype);
-       if (ret >= 0) {
-               if (!strncmp("unknown", chargetype, 7))
-                       pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
-               else if (!strncmp("battery", chargetype, 7))
-                       pdata->type = POWER_SUPPLY_TYPE_BATTERY;
-               else if (!strncmp("ups", chargetype, 3))
-                       pdata->type = POWER_SUPPLY_TYPE_UPS;
-               else if (!strncmp("mains", chargetype, 5))
-                       pdata->type = POWER_SUPPLY_TYPE_MAINS;
-               else if (!strncmp("usb-sdp", chargetype, 7))
-                       pdata->type = POWER_SUPPLY_TYPE_USB;
-               else if (!strncmp("usb-dcp", chargetype, 7))
-                       pdata->type = POWER_SUPPLY_TYPE_USB_DCP;
-               else if (!strncmp("usb-cdp", chargetype, 7))
-                       pdata->type = POWER_SUPPLY_TYPE_USB_CDP;
-               else if (!strncmp("usb-aca", chargetype, 7))
-                       pdata->type = POWER_SUPPLY_TYPE_USB_ACA;
-               else
-                       dev_warn(dev, "unknown charger type %s\n", chargetype);
-       }
-
-       return pdata;
-}
-
-static int gpio_charger_probe(struct platform_device *pdev)
-{
-       const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       struct gpio_charger *gpio_charger;
-       struct power_supply_desc *charger_desc;
-       int ret;
-       int irq;
-
-       if (!pdata) {
-               pdata = gpio_charger_parse_dt(&pdev->dev);
-               if (IS_ERR(pdata)) {
-                       ret = PTR_ERR(pdata);
-                       if (ret != -EPROBE_DEFER)
-                               dev_err(&pdev->dev, "No platform data\n");
-                       return ret;
-               }
-       }
-
-       if (!gpio_is_valid(pdata->gpio)) {
-               dev_err(&pdev->dev, "Invalid gpio pin\n");
-               return -EINVAL;
-       }
-
-       gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
-                                       GFP_KERNEL);
-       if (!gpio_charger) {
-               dev_err(&pdev->dev, "Failed to alloc driver structure\n");
-               return -ENOMEM;
-       }
-
-       charger_desc = &gpio_charger->charger_desc;
-
-       charger_desc->name = pdata->name ? pdata->name : "gpio-charger";
-       charger_desc->type = pdata->type;
-       charger_desc->properties = gpio_charger_properties;
-       charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
-       charger_desc->get_property = gpio_charger_get_property;
-
-       psy_cfg.supplied_to = pdata->supplied_to;
-       psy_cfg.num_supplicants = pdata->num_supplicants;
-       psy_cfg.of_node = pdev->dev.of_node;
-       psy_cfg.drv_data = gpio_charger;
-
-       ret = gpio_request(pdata->gpio, dev_name(&pdev->dev));
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret);
-               goto err_free;
-       }
-       ret = gpio_direction_input(pdata->gpio);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret);
-               goto err_gpio_free;
-       }
-
-       gpio_charger->pdata = pdata;
-
-       gpio_charger->charger = power_supply_register(&pdev->dev,
-                                                     charger_desc, &psy_cfg);
-       if (IS_ERR(gpio_charger->charger)) {
-               ret = PTR_ERR(gpio_charger->charger);
-               dev_err(&pdev->dev, "Failed to register power supply: %d\n",
-                       ret);
-               goto err_gpio_free;
-       }
-
-       irq = gpio_to_irq(pdata->gpio);
-       if (irq > 0) {
-               ret = request_any_context_irq(irq, gpio_charger_irq,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                               dev_name(&pdev->dev), gpio_charger->charger);
-               if (ret < 0)
-                       dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret);
-               else
-                       gpio_charger->irq = irq;
-       }
-
-       platform_set_drvdata(pdev, gpio_charger);
-
-       device_init_wakeup(&pdev->dev, 1);
-
-       return 0;
-
-err_gpio_free:
-       gpio_free(pdata->gpio);
-err_free:
-       return ret;
-}
-
-static int gpio_charger_remove(struct platform_device *pdev)
-{
-       struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
-
-       if (gpio_charger->irq)
-               free_irq(gpio_charger->irq, gpio_charger->charger);
-
-       power_supply_unregister(gpio_charger->charger);
-
-       gpio_free(gpio_charger->pdata->gpio);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int gpio_charger_suspend(struct device *dev)
-{
-       struct gpio_charger *gpio_charger = dev_get_drvdata(dev);
-
-       if (device_may_wakeup(dev))
-               gpio_charger->wakeup_enabled =
-                       !enable_irq_wake(gpio_charger->irq);
-
-       return 0;
-}
-
-static int gpio_charger_resume(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
-
-       if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled)
-               disable_irq_wake(gpio_charger->irq);
-       power_supply_changed(gpio_charger->charger);
-
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops,
-               gpio_charger_suspend, gpio_charger_resume);
-
-static const struct of_device_id gpio_charger_match[] = {
-       { .compatible = "gpio-charger" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, gpio_charger_match);
-
-static struct platform_driver gpio_charger_driver = {
-       .probe = gpio_charger_probe,
-       .remove = gpio_charger_remove,
-       .driver = {
-               .name = "gpio-charger",
-               .pm = &gpio_charger_pm_ops,
-               .of_match_table = gpio_charger_match,
-       },
-};
-
-module_platform_driver(gpio_charger_driver);
-
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:gpio-charger");
diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c
deleted file mode 100644 (file)
index 9fa4acc..0000000
+++ /dev/null
@@ -1,796 +0,0 @@
-/*
- * intel_mid_battery.c - Intel MID PMIC Battery Driver
- *
- * Copyright (C) 2009 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * Author: Nithish Mahalingam <nithish.mahalingam@intel.com>
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/jiffies.h>
-#include <linux/param.h>
-#include <linux/device.h>
-#include <linux/spi/spi.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-
-#include <asm/intel_scu_ipc.h>
-
-#define DRIVER_NAME "pmic_battery"
-
-/*********************************************************************
- *             Generic defines
- *********************************************************************/
-
-static int debug;
-module_param(debug, int, 0444);
-MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages.");
-
-#define PMIC_BATT_DRV_INFO_UPDATED     1
-#define PMIC_BATT_PRESENT              1
-#define PMIC_BATT_NOT_PRESENT          0
-#define PMIC_USB_PRESENT               PMIC_BATT_PRESENT
-#define PMIC_USB_NOT_PRESENT           PMIC_BATT_NOT_PRESENT
-
-/* pmic battery register related */
-#define PMIC_BATT_CHR_SCHRGINT_ADDR    0xD2
-#define PMIC_BATT_CHR_SBATOVP_MASK     (1 << 1)
-#define PMIC_BATT_CHR_STEMP_MASK       (1 << 2)
-#define PMIC_BATT_CHR_SCOMP_MASK       (1 << 3)
-#define PMIC_BATT_CHR_SUSBDET_MASK     (1 << 4)
-#define PMIC_BATT_CHR_SBATDET_MASK     (1 << 5)
-#define PMIC_BATT_CHR_SDCLMT_MASK      (1 << 6)
-#define PMIC_BATT_CHR_SUSBOVP_MASK     (1 << 7)
-#define PMIC_BATT_CHR_EXCPT_MASK       0x86
-
-#define PMIC_BATT_ADC_ACCCHRG_MASK     (1 << 31)
-#define PMIC_BATT_ADC_ACCCHRGVAL_MASK  0x7FFFFFFF
-
-/* pmic ipc related */
-#define PMIC_BATT_CHR_IPC_FCHRG_SUBID  0x4
-#define PMIC_BATT_CHR_IPC_TCHRG_SUBID  0x6
-
-/* types of battery charging */
-enum batt_charge_type {
-       BATT_USBOTG_500MA_CHARGE,
-       BATT_USBOTG_TRICKLE_CHARGE,
-};
-
-/* valid battery events */
-enum batt_event {
-       BATT_EVENT_BATOVP_EXCPT,
-       BATT_EVENT_USBOVP_EXCPT,
-       BATT_EVENT_TEMP_EXCPT,
-       BATT_EVENT_DCLMT_EXCPT,
-       BATT_EVENT_EXCPT
-};
-
-
-/*********************************************************************
- *             Battery properties
- *********************************************************************/
-
-/*
- * pmic battery info
- */
-struct pmic_power_module_info {
-       bool is_dev_info_updated;
-       struct device *dev;
-       /* pmic battery data */
-       unsigned long update_time;              /* jiffies when data read */
-       unsigned int usb_is_present;
-       unsigned int batt_is_present;
-       unsigned int batt_health;
-       unsigned int usb_health;
-       unsigned int batt_status;
-       unsigned int batt_charge_now;           /* in mAS */
-       unsigned int batt_prev_charge_full;     /* in mAS */
-       unsigned int batt_charge_rate;          /* in units per second */
-
-       struct power_supply *usb;
-       struct power_supply *batt;
-       int irq;                                /* GPE_ID or IRQ# */
-       struct workqueue_struct *monitor_wqueue;
-       struct delayed_work monitor_battery;
-       struct work_struct handler;
-};
-
-static unsigned int delay_time = 2000; /* in ms */
-
-/*
- * pmic ac properties
- */
-static enum power_supply_property pmic_usb_props[] = {
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_HEALTH,
-};
-
-/*
- * pmic battery properties
- */
-static enum power_supply_property pmic_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-};
-
-
-/*
- * Glue functions for talking to the IPC
- */
-
-struct battery_property {
-       u32 capacity;   /* Charger capacity */
-       u8  crnt;       /* Quick charge current value*/
-       u8  volt;       /* Fine adjustment of constant charge voltage */
-       u8  prot;       /* CHRGPROT register value */
-       u8  prot2;      /* CHRGPROT1 register value */
-       u8  timer;      /* Charging timer */
-};
-
-#define IPCMSG_BATTERY         0xEF
-
-/* Battery coulomb counter accumulator commands */
-#define IPC_CMD_CC_WR            0 /* Update coulomb counter value */
-#define IPC_CMD_CC_RD            1 /* Read coulomb counter value */
-#define IPC_CMD_BATTERY_PROPERTY  2 /* Read Battery property */
-
-/**
- *     pmic_scu_ipc_battery_cc_read    -       read battery cc
- *     @value: battery coulomb counter read
- *
- *     Reads the battery couloumb counter value, returns 0 on success, or
- *     an error code
- *
- *     This function may sleep. Locking for SCU accesses is handled for
- *     the caller.
- */
-static int pmic_scu_ipc_battery_cc_read(u32 *value)
-{
-       return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD,
-                                       NULL, 0, value, 1);
-}
-
-/**
- *     pmic_scu_ipc_battery_property_get       -       fetch properties
- *     @prop: battery properties
- *
- *     Retrieve the battery properties from the power management
- *
- *     This function may sleep. Locking for SCU accesses is handled for
- *     the caller.
- */
-static int pmic_scu_ipc_battery_property_get(struct battery_property *prop)
-{
-       u32 data[3];
-       u8 *p = (u8 *)&data[1];
-       int err = intel_scu_ipc_command(IPCMSG_BATTERY,
-                               IPC_CMD_BATTERY_PROPERTY, NULL, 0, data, 3);
-
-       prop->capacity = data[0];
-       prop->crnt = *p++;
-       prop->volt = *p++;
-       prop->prot = *p++;
-       prop->prot2 = *p++;
-       prop->timer = *p++;
-
-       return err;
-}
-
-/**
- *     pmic_scu_ipc_set_charger        -       set charger
- *     @charger: charger to select
- *
- *     Switch the charging mode for the SCU
- */
-
-static int pmic_scu_ipc_set_charger(int charger)
-{
-       return intel_scu_ipc_simple_command(IPCMSG_BATTERY, charger);
-}
-
-/**
- * pmic_battery_log_event - log battery events
- * @event: battery event to be logged
- * Context: can sleep
- *
- * There are multiple battery events which may be of interest to users;
- * this battery function logs the different battery events onto the
- * kernel log messages.
- */
-static void pmic_battery_log_event(enum batt_event event)
-{
-       printk(KERN_WARNING "pmic-battery: ");
-       switch (event) {
-       case BATT_EVENT_BATOVP_EXCPT:
-               printk(KERN_CONT "battery overvoltage condition\n");
-               break;
-       case BATT_EVENT_USBOVP_EXCPT:
-               printk(KERN_CONT "usb charger overvoltage condition\n");
-               break;
-       case BATT_EVENT_TEMP_EXCPT:
-               printk(KERN_CONT "high battery temperature condition\n");
-               break;
-       case BATT_EVENT_DCLMT_EXCPT:
-               printk(KERN_CONT "over battery charge current condition\n");
-               break;
-       default:
-               printk(KERN_CONT "charger/battery exception %d\n", event);
-               break;
-       }
-}
-
-/**
- * pmic_battery_read_status - read battery status information
- * @pbi: device info structure to update the read information
- * Context: can sleep
- *
- * PMIC power source information need to be updated based on the data read
- * from the PMIC battery registers.
- *
- */
-static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
-{
-       unsigned int update_time_intrvl;
-       unsigned int chrg_val;
-       u32 ccval;
-       u8 r8;
-       struct battery_property batt_prop;
-       int batt_present = 0;
-       int usb_present = 0;
-       int batt_exception = 0;
-
-       /* make sure the last batt_status read happened delay_time before */
-       if (pbi->update_time && time_before(jiffies, pbi->update_time +
-                                               msecs_to_jiffies(delay_time)))
-               return;
-
-       update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time);
-       pbi->update_time = jiffies;
-
-       /* read coulomb counter registers and schrgint register */
-       if (pmic_scu_ipc_battery_cc_read(&ccval)) {
-               dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
-                                                               __func__);
-               return;
-       }
-
-       if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
-               dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
-                                                               __func__);
-               return;
-       }
-
-       /*
-        * set pmic_power_module_info members based on pmic register values
-        * read.
-        */
-
-       /* set batt_is_present */
-       if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
-               pbi->batt_is_present = PMIC_BATT_PRESENT;
-               batt_present = 1;
-       } else {
-               pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
-               pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
-               pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
-       }
-
-       /* set batt_health */
-       if (batt_present) {
-               if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) {
-                       pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-                       pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-                       pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT);
-                       batt_exception = 1;
-               } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) {
-                       pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
-                       pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-                       pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT);
-                       batt_exception = 1;
-               } else {
-                       pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
-                       if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
-                               /* PMIC will change charging current automatically */
-                               pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
-                       }
-               }
-       }
-
-       /* set usb_is_present */
-       if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
-               pbi->usb_is_present = PMIC_USB_PRESENT;
-               usb_present = 1;
-       } else {
-               pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
-               pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
-       }
-
-       if (usb_present) {
-               if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) {
-                       pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-                       pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT);
-               } else {
-                       pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
-               }
-       }
-
-       chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK;
-
-       /* set batt_prev_charge_full to battery capacity the first time */
-       if (!pbi->is_dev_info_updated) {
-               if (pmic_scu_ipc_battery_property_get(&batt_prop)) {
-                       dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
-                                                               __func__);
-                       return;
-               }
-               pbi->batt_prev_charge_full = batt_prop.capacity;
-       }
-
-       /* set batt_status */
-       if (batt_present && !batt_exception) {
-               if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
-                       pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
-                       pbi->batt_prev_charge_full = chrg_val;
-               } else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) {
-                       pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
-               } else {
-                       pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING;
-               }
-       }
-
-       /* set batt_charge_rate */
-       if (pbi->is_dev_info_updated && batt_present && !batt_exception) {
-               if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) {
-                       if (pbi->batt_charge_now - chrg_val) {
-                               pbi->batt_charge_rate = ((pbi->batt_charge_now -
-                                       chrg_val) * 1000 * 60) /
-                                       update_time_intrvl;
-                       }
-               } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) {
-                       if (chrg_val - pbi->batt_charge_now) {
-                               pbi->batt_charge_rate = ((chrg_val -
-                                       pbi->batt_charge_now) * 1000 * 60) /
-                                       update_time_intrvl;
-                       }
-               } else
-                       pbi->batt_charge_rate = 0;
-       } else {
-               pbi->batt_charge_rate = -1;
-       }
-
-       /* batt_charge_now */
-       if (batt_present && !batt_exception)
-               pbi->batt_charge_now = chrg_val;
-       else
-               pbi->batt_charge_now = -1;
-
-       pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED;
-}
-
-/**
- * pmic_usb_get_property - usb power source get property
- * @psy: usb power supply context
- * @psp: usb power source property
- * @val: usb power source property value
- * Context: can sleep
- *
- * PMIC usb power source property needs to be provided to power_supply
- * subsytem for it to provide the information to users.
- */
-static int pmic_usb_get_property(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy);
-
-       /* update pmic_power_module_info members */
-       pmic_battery_read_status(pbi);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = pbi->usb_is_present;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               val->intval = pbi->usb_health;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static inline unsigned long mAStouAh(unsigned long v)
-{
-       /* seconds to hours, mA to ÂµA */
-       return (v * 1000) / 3600;
-}
-
-/**
- * pmic_battery_get_property - battery power source get property
- * @psy: battery power supply context
- * @psp: battery power source property
- * @val: battery power source property value
- * Context: can sleep
- *
- * PMIC battery power source property needs to be provided to power_supply
- * subsytem for it to provide the information to users.
- */
-static int pmic_battery_get_property(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy);
-
-       /* update pmic_power_module_info members */
-       pmic_battery_read_status(pbi);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = pbi->batt_status;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               val->intval = pbi->batt_health;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = pbi->batt_is_present;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               val->intval = mAStouAh(pbi->batt_charge_now);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               val->intval = mAStouAh(pbi->batt_prev_charge_full);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/**
- * pmic_battery_monitor - monitor battery status
- * @work: work structure
- * Context: can sleep
- *
- * PMIC battery status needs to be monitored for any change
- * and information needs to be frequently updated.
- */
-static void pmic_battery_monitor(struct work_struct *work)
-{
-       struct pmic_power_module_info *pbi = container_of(work,
-                       struct pmic_power_module_info, monitor_battery.work);
-
-       /* update pmic_power_module_info members */
-       pmic_battery_read_status(pbi);
-       queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10);
-}
-
-/**
- * pmic_battery_set_charger - set battery charger
- * @pbi: device info structure
- * @chrg: charge mode to set battery charger in
- * Context: can sleep
- *
- * PMIC battery charger needs to be enabled based on the usb charge
- * capabilities connected to the platform.
- */
-static int pmic_battery_set_charger(struct pmic_power_module_info *pbi,
-                                               enum batt_charge_type chrg)
-{
-       int retval;
-
-       /* set usblmt bits and chrgcntl register bits appropriately */
-       switch (chrg) {
-       case BATT_USBOTG_500MA_CHARGE:
-               retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID);
-               break;
-       case BATT_USBOTG_TRICKLE_CHARGE:
-               retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID);
-               break;
-       default:
-               dev_warn(pbi->dev, "%s(): out of range usb charger "
-                                               "charge detected\n", __func__);
-               return -EINVAL;
-       }
-
-       if (retval) {
-               dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
-                                                               __func__);
-               return retval;
-       }
-
-       return 0;
-}
-
-/**
- * pmic_battery_interrupt_handler - pmic battery interrupt handler
- * Context: interrupt context
- *
- * PMIC battery interrupt handler which will be called with either
- * battery full condition occurs or usb otg & battery connect
- * condition occurs.
- */
-static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev)
-{
-       struct pmic_power_module_info *pbi = dev;
-
-       schedule_work(&pbi->handler);
-
-       return IRQ_HANDLED;
-}
-
-/**
- * pmic_battery_handle_intrpt - pmic battery service interrupt
- * @work: work structure
- * Context: can sleep
- *
- * PMIC battery needs to either update the battery status as full
- * if it detects battery full condition caused the interrupt or needs
- * to enable battery charger if it detects usb and battery detect
- * caused the source of interrupt.
- */
-static void pmic_battery_handle_intrpt(struct work_struct *work)
-{
-       struct pmic_power_module_info *pbi = container_of(work,
-                               struct pmic_power_module_info, handler);
-       enum batt_charge_type chrg;
-       u8 r8;
-
-       if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
-               dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
-                                                               __func__);
-               return;
-       }
-       /* find the cause of the interrupt */
-       if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
-               pbi->batt_is_present = PMIC_BATT_PRESENT;
-       } else {
-               pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
-               pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
-               pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
-               return;
-       }
-
-       if (r8 & PMIC_BATT_CHR_EXCPT_MASK) {
-               pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
-               pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
-               pmic_battery_log_event(BATT_EVENT_EXCPT);
-               return;
-       } else {
-               pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
-               pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
-       }
-
-       if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
-               u32 ccval;
-               pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
-
-               if (pmic_scu_ipc_battery_cc_read(&ccval)) {
-                       dev_warn(pbi->dev, "%s(): ipc config cmd "
-                                                       "failed\n", __func__);
-                       return;
-               }
-               pbi->batt_prev_charge_full = ccval &
-                                               PMIC_BATT_ADC_ACCCHRGVAL_MASK;
-               return;
-       }
-
-       if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
-               pbi->usb_is_present = PMIC_USB_PRESENT;
-       } else {
-               pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
-               pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
-               return;
-       }
-
-       /* setup battery charging */
-
-#if 0
-       /* check usb otg power capability and set charger accordingly */
-       retval = langwell_udc_maxpower(&power);
-       if (retval) {
-               dev_warn(pbi->dev,
-                   "%s(): usb otg power query failed with error code %d\n",
-                       __func__, retval);
-               return;
-       }
-
-       if (power >= 500)
-               chrg = BATT_USBOTG_500MA_CHARGE;
-       else
-#endif
-               chrg = BATT_USBOTG_TRICKLE_CHARGE;
-
-       /* enable battery charging */
-       if (pmic_battery_set_charger(pbi, chrg)) {
-               dev_warn(pbi->dev,
-                       "%s(): failed to set up battery charging\n", __func__);
-               return;
-       }
-
-       dev_dbg(pbi->dev,
-               "pmic-battery: %s() - setting up battery charger successful\n",
-                       __func__);
-}
-
-/*
- * Description of power supplies
- */
-static const struct power_supply_desc pmic_usb_desc = {
-       .name           = "pmic-usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .properties     = pmic_usb_props,
-       .num_properties = ARRAY_SIZE(pmic_usb_props),
-       .get_property   = pmic_usb_get_property,
-};
-
-static const struct power_supply_desc pmic_batt_desc = {
-       .name           = "pmic-batt",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = pmic_battery_props,
-       .num_properties = ARRAY_SIZE(pmic_battery_props),
-       .get_property   = pmic_battery_get_property,
-};
-
-/**
- * pmic_battery_probe - pmic battery initialize
- * @irq: pmic battery device irq
- * @dev: pmic battery device structure
- * Context: can sleep
- *
- * PMIC battery initializes its internal data structue and other
- * infrastructure components for it to work as expected.
- */
-static int probe(int irq, struct device *dev)
-{
-       int retval = 0;
-       struct pmic_power_module_info *pbi;
-       struct power_supply_config psy_cfg = {};
-
-       dev_dbg(dev, "pmic-battery: found pmic battery device\n");
-
-       pbi = kzalloc(sizeof(*pbi), GFP_KERNEL);
-       if (!pbi) {
-               dev_err(dev, "%s(): memory allocation failed\n",
-                                                               __func__);
-               return -ENOMEM;
-       }
-
-       pbi->dev = dev;
-       pbi->irq = irq;
-       dev_set_drvdata(dev, pbi);
-       psy_cfg.drv_data = pbi;
-
-       /* initialize all required framework before enabling interrupts */
-       INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt);
-       INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor);
-       pbi->monitor_wqueue =
-                       create_singlethread_workqueue(dev_name(dev));
-       if (!pbi->monitor_wqueue) {
-               dev_err(dev, "%s(): wqueue init failed\n", __func__);
-               retval = -ESRCH;
-               goto wqueue_failed;
-       }
-
-       /* register interrupt */
-       retval = request_irq(pbi->irq, pmic_battery_interrupt_handler,
-                                                       0, DRIVER_NAME, pbi);
-       if (retval) {
-               dev_err(dev, "%s(): cannot get IRQ\n", __func__);
-               goto requestirq_failed;
-       }
-
-       /* register pmic-batt with power supply subsystem */
-       pbi->batt = power_supply_register(dev, &pmic_usb_desc, &psy_cfg);
-       if (IS_ERR(pbi->batt)) {
-               dev_err(dev,
-                       "%s(): failed to register pmic battery device with power supply subsystem\n",
-                               __func__);
-               retval = PTR_ERR(pbi->batt);
-               goto power_reg_failed;
-       }
-
-       dev_dbg(dev, "pmic-battery: %s() - pmic battery device "
-               "registration with power supply subsystem successful\n",
-               __func__);
-
-       queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1);
-
-       /* register pmic-usb with power supply subsystem */
-       pbi->usb = power_supply_register(dev, &pmic_batt_desc, &psy_cfg);
-       if (IS_ERR(pbi->usb)) {
-               dev_err(dev,
-                       "%s(): failed to register pmic usb device with power supply subsystem\n",
-                               __func__);
-               retval = PTR_ERR(pbi->usb);
-               goto power_reg_failed_1;
-       }
-
-       if (debug)
-               printk(KERN_INFO "pmic-battery: %s() - pmic usb device "
-                       "registration with power supply subsystem successful\n",
-                       __func__);
-
-       return retval;
-
-power_reg_failed_1:
-       power_supply_unregister(pbi->batt);
-power_reg_failed:
-       cancel_delayed_work_sync(&pbi->monitor_battery);
-requestirq_failed:
-       destroy_workqueue(pbi->monitor_wqueue);
-wqueue_failed:
-       kfree(pbi);
-
-       return retval;
-}
-
-static int platform_pmic_battery_probe(struct platform_device *pdev)
-{
-       return probe(pdev->id, &pdev->dev);
-}
-
-/**
- * pmic_battery_remove - pmic battery finalize
- * @dev: pmic battery device structure
- * Context: can sleep
- *
- * PMIC battery finalizes its internal data structue and other
- * infrastructure components that it initialized in
- * pmic_battery_probe.
- */
-
-static int platform_pmic_battery_remove(struct platform_device *pdev)
-{
-       struct pmic_power_module_info *pbi = platform_get_drvdata(pdev);
-
-       free_irq(pbi->irq, pbi);
-       cancel_delayed_work_sync(&pbi->monitor_battery);
-       destroy_workqueue(pbi->monitor_wqueue);
-
-       power_supply_unregister(pbi->usb);
-       power_supply_unregister(pbi->batt);
-
-       cancel_work_sync(&pbi->handler);
-       kfree(pbi);
-       return 0;
-}
-
-static struct platform_driver platform_pmic_battery_driver = {
-       .driver = {
-               .name = DRIVER_NAME,
-       },
-       .probe = platform_pmic_battery_probe,
-       .remove = platform_pmic_battery_remove,
-};
-
-module_platform_driver(platform_pmic_battery_driver);
-
-MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
-MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/ipaq_micro_battery.c b/drivers/power/ipaq_micro_battery.c
deleted file mode 100644 (file)
index 35b01c7..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * h3xxx atmel micro companion support, battery subdevice
- * based on previous kernel 2.4 version
- * Author : Alessandro Gardich <gremlin@gremlin.it>
- * Author : Linus Walleij <linus.walleij@linaro.org>
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/ipaq-micro.h>
-#include <linux/power_supply.h>
-#include <linux/workqueue.h>
-
-#define BATT_PERIOD 100000 /* 100 seconds in milliseconds */
-
-#define MICRO_BATT_CHEM_ALKALINE       0x01
-#define MICRO_BATT_CHEM_NICD           0x02
-#define MICRO_BATT_CHEM_NIMH           0x03
-#define MICRO_BATT_CHEM_LION           0x04
-#define MICRO_BATT_CHEM_LIPOLY         0x05
-#define MICRO_BATT_CHEM_NOT_INSTALLED  0x06
-#define MICRO_BATT_CHEM_UNKNOWN                0xff
-
-#define MICRO_BATT_STATUS_HIGH         0x01
-#define MICRO_BATT_STATUS_LOW          0x02
-#define MICRO_BATT_STATUS_CRITICAL     0x04
-#define MICRO_BATT_STATUS_CHARGING     0x08
-#define MICRO_BATT_STATUS_CHARGEMAIN   0x10
-#define MICRO_BATT_STATUS_DEAD         0x20 /* Battery will not charge */
-#define MICRO_BATT_STATUS_NOTINSTALLED 0x20 /* For expansion pack batteries */
-#define MICRO_BATT_STATUS_FULL         0x40 /* Battery fully charged */
-#define MICRO_BATT_STATUS_NOBATTERY    0x80
-#define MICRO_BATT_STATUS_UNKNOWN      0xff
-
-struct micro_battery {
-       struct ipaq_micro *micro;
-       struct workqueue_struct *wq;
-       struct delayed_work update;
-       u8 ac;
-       u8 chemistry;
-       unsigned int voltage;
-       u16 temperature;
-       u8 flag;
-};
-
-static void micro_battery_work(struct work_struct *work)
-{
-       struct micro_battery *mb = container_of(work,
-                               struct micro_battery, update.work);
-       struct ipaq_micro_msg msg_battery = {
-               .id = MSG_BATTERY,
-       };
-       struct ipaq_micro_msg msg_sensor = {
-               .id = MSG_THERMAL_SENSOR,
-       };
-
-       /* First send battery message */
-       ipaq_micro_tx_msg_sync(mb->micro, &msg_battery);
-       if (msg_battery.rx_len < 4)
-               pr_info("ERROR");
-
-       /*
-        * Returned message format:
-        * byte 0:   0x00 = Not plugged in
-        *           0x01 = AC adapter plugged in
-        * byte 1:   chemistry
-        * byte 2:   voltage LSB
-        * byte 3:   voltage MSB
-        * byte 4:   flags
-        * byte 5-9: same for battery 2
-        */
-       mb->ac = msg_battery.rx_data[0];
-       mb->chemistry = msg_battery.rx_data[1];
-       mb->voltage = ((((unsigned short)msg_battery.rx_data[3] << 8) +
-                       msg_battery.rx_data[2]) * 5000L) * 1000 / 1024;
-       mb->flag = msg_battery.rx_data[4];
-
-       if (msg_battery.rx_len == 9)
-               pr_debug("second battery ignored\n");
-
-       /* Then read the sensor */
-       ipaq_micro_tx_msg_sync(mb->micro, &msg_sensor);
-       mb->temperature = msg_sensor.rx_data[1] << 8 | msg_sensor.rx_data[0];
-
-       queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
-}
-
-static int get_capacity(struct power_supply *b)
-{
-       struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
-
-       switch (mb->flag & 0x07) {
-       case MICRO_BATT_STATUS_HIGH:
-               return 100;
-               break;
-       case MICRO_BATT_STATUS_LOW:
-               return 50;
-               break;
-       case MICRO_BATT_STATUS_CRITICAL:
-               return 5;
-               break;
-       default:
-               break;
-       }
-       return 0;
-}
-
-static int get_status(struct power_supply *b)
-{
-       struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
-
-       if (mb->flag == MICRO_BATT_STATUS_UNKNOWN)
-               return POWER_SUPPLY_STATUS_UNKNOWN;
-
-       if (mb->flag & MICRO_BATT_STATUS_FULL)
-               return POWER_SUPPLY_STATUS_FULL;
-
-       if ((mb->flag & MICRO_BATT_STATUS_CHARGING) ||
-               (mb->flag & MICRO_BATT_STATUS_CHARGEMAIN))
-               return POWER_SUPPLY_STATUS_CHARGING;
-
-       return POWER_SUPPLY_STATUS_DISCHARGING;
-}
-
-static int micro_batt_get_property(struct power_supply *b,
-                                       enum power_supply_property psp,
-                                       union power_supply_propval *val)
-{
-       struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               switch (mb->chemistry) {
-               case MICRO_BATT_CHEM_NICD:
-                       val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd;
-                       break;
-               case MICRO_BATT_CHEM_NIMH:
-                       val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
-                       break;
-               case MICRO_BATT_CHEM_LION:
-                       val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-                       break;
-               case MICRO_BATT_CHEM_LIPOLY:
-                       val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
-                       break;
-               default:
-                       val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
-                       break;
-               };
-               break;
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = get_status(b);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = 4700000;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               val->intval = get_capacity(b);
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = mb->temperature;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = mb->voltage;
-               break;
-       default:
-               return -EINVAL;
-       };
-
-       return 0;
-}
-
-static int micro_ac_get_property(struct power_supply *b,
-                                enum power_supply_property psp,
-                                union power_supply_propval *val)
-{
-       struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = mb->ac;
-               break;
-       default:
-               return -EINVAL;
-       };
-
-       return 0;
-}
-
-static enum power_supply_property micro_batt_power_props[] = {
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-};
-
-static const struct power_supply_desc micro_batt_power_desc = {
-       .name                   = "main-battery",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = micro_batt_power_props,
-       .num_properties         = ARRAY_SIZE(micro_batt_power_props),
-       .get_property           = micro_batt_get_property,
-       .use_for_apm            = 1,
-};
-
-static enum power_supply_property micro_ac_power_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static const struct power_supply_desc micro_ac_power_desc = {
-       .name                   = "ac",
-       .type                   = POWER_SUPPLY_TYPE_MAINS,
-       .properties             = micro_ac_power_props,
-       .num_properties         = ARRAY_SIZE(micro_ac_power_props),
-       .get_property           = micro_ac_get_property,
-};
-
-static struct power_supply *micro_batt_power, *micro_ac_power;
-
-static int micro_batt_probe(struct platform_device *pdev)
-{
-       struct micro_battery *mb;
-       int ret;
-
-       mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
-       if (!mb)
-               return -ENOMEM;
-
-       mb->micro = dev_get_drvdata(pdev->dev.parent);
-       mb->wq = create_singlethread_workqueue("ipaq-battery-wq");
-       if (!mb->wq)
-               return -ENOMEM;
-
-       INIT_DELAYED_WORK(&mb->update, micro_battery_work);
-       platform_set_drvdata(pdev, mb);
-       queue_delayed_work(mb->wq, &mb->update, 1);
-
-       micro_batt_power = power_supply_register(&pdev->dev,
-                                                &micro_batt_power_desc, NULL);
-       if (IS_ERR(micro_batt_power)) {
-               ret = PTR_ERR(micro_batt_power);
-               goto batt_err;
-       }
-
-       micro_ac_power = power_supply_register(&pdev->dev,
-                                              &micro_ac_power_desc, NULL);
-       if (IS_ERR(micro_ac_power)) {
-               ret = PTR_ERR(micro_ac_power);
-               goto ac_err;
-       }
-
-       dev_info(&pdev->dev, "iPAQ micro battery driver\n");
-       return 0;
-
-ac_err:
-       power_supply_unregister(micro_batt_power);
-batt_err:
-       cancel_delayed_work_sync(&mb->update);
-       destroy_workqueue(mb->wq);
-       return ret;
-}
-
-static int micro_batt_remove(struct platform_device *pdev)
-
-{
-       struct micro_battery *mb = platform_get_drvdata(pdev);
-
-       power_supply_unregister(micro_ac_power);
-       power_supply_unregister(micro_batt_power);
-       cancel_delayed_work_sync(&mb->update);
-       destroy_workqueue(mb->wq);
-
-       return 0;
-}
-
-static int __maybe_unused micro_batt_suspend(struct device *dev)
-{
-       struct micro_battery *mb = dev_get_drvdata(dev);
-
-       cancel_delayed_work_sync(&mb->update);
-       return 0;
-}
-
-static int __maybe_unused micro_batt_resume(struct device *dev)
-{
-       struct micro_battery *mb = dev_get_drvdata(dev);
-
-       queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
-       return 0;
-}
-
-static const struct dev_pm_ops micro_batt_dev_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(micro_batt_suspend, micro_batt_resume)
-};
-
-static struct platform_driver micro_batt_device_driver = {
-       .driver         = {
-               .name   = "ipaq-micro-battery",
-               .pm     = &micro_batt_dev_pm_ops,
-       },
-       .probe          = micro_batt_probe,
-       .remove         = micro_batt_remove,
-};
-module_platform_driver(micro_batt_device_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery");
-MODULE_ALIAS("platform:battery-ipaq-micro");
diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c
deleted file mode 100644 (file)
index 4cd6899..0000000
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * ISP1704 USB Charger Detection driver
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2012 - 2013 Pali Rohár <pali.rohar@gmail.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/device.h>
-#include <linux/sysfs.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-
-#include <linux/usb/otg.h>
-#include <linux/usb/ulpi.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/power/isp1704_charger.h>
-
-/* Vendor specific Power Control register */
-#define ISP1704_PWR_CTRL               0x3d
-#define ISP1704_PWR_CTRL_SWCTRL                (1 << 0)
-#define ISP1704_PWR_CTRL_DET_COMP      (1 << 1)
-#define ISP1704_PWR_CTRL_BVALID_RISE   (1 << 2)
-#define ISP1704_PWR_CTRL_BVALID_FALL   (1 << 3)
-#define ISP1704_PWR_CTRL_DP_WKPU_EN    (1 << 4)
-#define ISP1704_PWR_CTRL_VDAT_DET      (1 << 5)
-#define ISP1704_PWR_CTRL_DPVSRC_EN     (1 << 6)
-#define ISP1704_PWR_CTRL_HWDETECT      (1 << 7)
-
-#define NXP_VENDOR_ID                  0x04cc
-
-static u16 isp170x_id[] = {
-       0x1704,
-       0x1707,
-};
-
-struct isp1704_charger {
-       struct device                   *dev;
-       struct power_supply             *psy;
-       struct power_supply_desc        psy_desc;
-       struct usb_phy                  *phy;
-       struct notifier_block           nb;
-       struct work_struct              work;
-
-       /* properties */
-       char                    model[8];
-       unsigned                present:1;
-       unsigned                online:1;
-       unsigned                current_max;
-};
-
-static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
-{
-       return usb_phy_io_read(isp->phy, reg);
-}
-
-static inline int isp1704_write(struct isp1704_charger *isp, u32 reg, u32 val)
-{
-       return usb_phy_io_write(isp->phy, val, reg);
-}
-
-/*
- * Disable/enable the power from the isp1704 if a function for it
- * has been provided with platform data.
- */
-static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
-{
-       struct isp1704_charger_data     *board = isp->dev->platform_data;
-
-       if (board && board->set_power)
-               board->set_power(on);
-       else if (board)
-               gpio_set_value(board->enable_gpio, on);
-}
-
-/*
- * Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB
- * chargers).
- *
- * REVISIT: The method is defined in Battery Charging Specification and is
- * applicable to any ULPI transceiver. Nothing isp170x specific here.
- */
-static inline int isp1704_charger_type(struct isp1704_charger *isp)
-{
-       u8 reg;
-       u8 func_ctrl;
-       u8 otg_ctrl;
-       int type = POWER_SUPPLY_TYPE_USB_DCP;
-
-       func_ctrl = isp1704_read(isp, ULPI_FUNC_CTRL);
-       otg_ctrl = isp1704_read(isp, ULPI_OTG_CTRL);
-
-       /* disable pulldowns */
-       reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN;
-       isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), reg);
-
-       /* full speed */
-       isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
-                       ULPI_FUNC_CTRL_XCVRSEL_MASK);
-       isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL),
-                       ULPI_FUNC_CTRL_FULL_SPEED);
-
-       /* Enable strong pull-up on DP (1.5K) and reset */
-       reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
-       isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), reg);
-       usleep_range(1000, 2000);
-
-       reg = isp1704_read(isp, ULPI_DEBUG);
-       if ((reg & 3) != 3)
-               type = POWER_SUPPLY_TYPE_USB_CDP;
-
-       /* recover original state */
-       isp1704_write(isp, ULPI_FUNC_CTRL, func_ctrl);
-       isp1704_write(isp, ULPI_OTG_CTRL, otg_ctrl);
-
-       return type;
-}
-
-/*
- * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger
- * is actually a dedicated charger, the following steps need to be taken.
- */
-static inline int isp1704_charger_verify(struct isp1704_charger *isp)
-{
-       int     ret = 0;
-       u8      r;
-
-       /* Reset the transceiver */
-       r = isp1704_read(isp, ULPI_FUNC_CTRL);
-       r |= ULPI_FUNC_CTRL_RESET;
-       isp1704_write(isp, ULPI_FUNC_CTRL, r);
-       usleep_range(1000, 2000);
-
-       /* Set normal mode */
-       r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
-       isp1704_write(isp, ULPI_FUNC_CTRL, r);
-
-       /* Clear the DP and DM pull-down bits */
-       r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
-       isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), r);
-
-       /* Enable strong pull-up on DP (1.5K) and reset */
-       r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
-       isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), r);
-       usleep_range(1000, 2000);
-
-       /* Read the line state */
-       if (!isp1704_read(isp, ULPI_DEBUG)) {
-               /* Disable strong pull-up on DP (1.5K) */
-               isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
-                               ULPI_FUNC_CTRL_TERMSELECT);
-               return 1;
-       }
-
-       /* Is it a charger or PS/2 connection */
-
-       /* Enable weak pull-up resistor on DP */
-       isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
-                       ISP1704_PWR_CTRL_DP_WKPU_EN);
-
-       /* Disable strong pull-up on DP (1.5K) */
-       isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
-                       ULPI_FUNC_CTRL_TERMSELECT);
-
-       /* Enable weak pull-down resistor on DM */
-       isp1704_write(isp, ULPI_SET(ULPI_OTG_CTRL),
-                       ULPI_OTG_CTRL_DM_PULLDOWN);
-
-       /* It's a charger if the line states are clear */
-       if (!(isp1704_read(isp, ULPI_DEBUG)))
-               ret = 1;
-
-       /* Disable weak pull-up resistor on DP */
-       isp1704_write(isp, ULPI_CLR(ISP1704_PWR_CTRL),
-                       ISP1704_PWR_CTRL_DP_WKPU_EN);
-
-       return ret;
-}
-
-static inline int isp1704_charger_detect(struct isp1704_charger *isp)
-{
-       unsigned long   timeout;
-       u8              pwr_ctrl;
-       int             ret = 0;
-
-       pwr_ctrl = isp1704_read(isp, ISP1704_PWR_CTRL);
-
-       /* set SW control bit in PWR_CTRL register */
-       isp1704_write(isp, ISP1704_PWR_CTRL,
-                       ISP1704_PWR_CTRL_SWCTRL);
-
-       /* enable manual charger detection */
-       isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
-                       ISP1704_PWR_CTRL_SWCTRL
-                       | ISP1704_PWR_CTRL_DPVSRC_EN);
-       usleep_range(1000, 2000);
-
-       timeout = jiffies + msecs_to_jiffies(300);
-       do {
-               /* Check if there is a charger */
-               if (isp1704_read(isp, ISP1704_PWR_CTRL)
-                               & ISP1704_PWR_CTRL_VDAT_DET) {
-                       ret = isp1704_charger_verify(isp);
-                       break;
-               }
-       } while (!time_after(jiffies, timeout) && isp->online);
-
-       /* recover original state */
-       isp1704_write(isp, ISP1704_PWR_CTRL, pwr_ctrl);
-
-       return ret;
-}
-
-static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp)
-{
-       if (isp1704_charger_detect(isp) &&
-                       isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP)
-               return true;
-       else
-               return false;
-}
-
-static void isp1704_charger_work(struct work_struct *data)
-{
-       struct isp1704_charger  *isp =
-               container_of(data, struct isp1704_charger, work);
-       static DEFINE_MUTEX(lock);
-
-       mutex_lock(&lock);
-
-       switch (isp->phy->last_event) {
-       case USB_EVENT_VBUS:
-               /* do not call wall charger detection more times */
-               if (!isp->present) {
-                       isp->online = true;
-                       isp->present = 1;
-                       isp1704_charger_set_power(isp, 1);
-
-                       /* detect wall charger */
-                       if (isp1704_charger_detect_dcp(isp)) {
-                               isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
-                               isp->current_max = 1800;
-                       } else {
-                               isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
-                               isp->current_max = 500;
-                       }
-
-                       /* enable data pullups */
-                       if (isp->phy->otg->gadget)
-                               usb_gadget_connect(isp->phy->otg->gadget);
-               }
-
-               if (isp->psy_desc.type != POWER_SUPPLY_TYPE_USB_DCP) {
-                       /*
-                        * Only 500mA here or high speed chirp
-                        * handshaking may break
-                        */
-                       if (isp->current_max > 500)
-                               isp->current_max = 500;
-
-                       if (isp->current_max > 100)
-                               isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP;
-               }
-               break;
-       case USB_EVENT_NONE:
-               isp->online = false;
-               isp->present = 0;
-               isp->current_max = 0;
-               isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
-
-               /*
-                * Disable data pullups. We need to prevent the controller from
-                * enumerating.
-                *
-                * FIXME: This is here to allow charger detection with Host/HUB
-                * chargers. The pullups may be enabled elsewhere, so this can
-                * not be the final solution.
-                */
-               if (isp->phy->otg->gadget)
-                       usb_gadget_disconnect(isp->phy->otg->gadget);
-
-               isp1704_charger_set_power(isp, 0);
-               break;
-       default:
-               goto out;
-       }
-
-       power_supply_changed(isp->psy);
-out:
-       mutex_unlock(&lock);
-}
-
-static int isp1704_notifier_call(struct notifier_block *nb,
-               unsigned long val, void *v)
-{
-       struct isp1704_charger *isp =
-               container_of(nb, struct isp1704_charger, nb);
-
-       schedule_work(&isp->work);
-
-       return NOTIFY_OK;
-}
-
-static int isp1704_charger_get_property(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct isp1704_charger *isp = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = isp->present;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = isp->online;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_MAX:
-               val->intval = isp->current_max;
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = isp->model;
-               break;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = "NXP";
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static enum power_supply_property power_props[] = {
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_CURRENT_MAX,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
-{
-       int vendor;
-       int product;
-       int i;
-       int ret = -ENODEV;
-
-       /* Test ULPI interface */
-       ret = isp1704_write(isp, ULPI_SCRATCH, 0xaa);
-       if (ret < 0)
-               return ret;
-
-       ret = isp1704_read(isp, ULPI_SCRATCH);
-       if (ret < 0)
-               return ret;
-
-       if (ret != 0xaa)
-               return -ENODEV;
-
-       /* Verify the product and vendor id matches */
-       vendor = isp1704_read(isp, ULPI_VENDOR_ID_LOW);
-       vendor |= isp1704_read(isp, ULPI_VENDOR_ID_HIGH) << 8;
-       if (vendor != NXP_VENDOR_ID)
-               return -ENODEV;
-
-       product = isp1704_read(isp, ULPI_PRODUCT_ID_LOW);
-       product |= isp1704_read(isp, ULPI_PRODUCT_ID_HIGH) << 8;
-
-       for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
-               if (product == isp170x_id[i]) {
-                       sprintf(isp->model, "isp%x", product);
-                       return product;
-               }
-       }
-
-       dev_err(isp->dev, "product id %x not matching known ids", product);
-
-       return -ENODEV;
-}
-
-static int isp1704_charger_probe(struct platform_device *pdev)
-{
-       struct isp1704_charger  *isp;
-       int                     ret = -ENODEV;
-       struct power_supply_config psy_cfg = {};
-
-       struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev);
-       struct device_node *np = pdev->dev.of_node;
-
-       if (np) {
-               int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0);
-
-               if (gpio < 0) {
-                       dev_err(&pdev->dev, "missing DT GPIO nxp,enable-gpio\n");
-                       return gpio;
-               }
-
-               pdata = devm_kzalloc(&pdev->dev,
-                       sizeof(struct isp1704_charger_data), GFP_KERNEL);
-               pdata->enable_gpio = gpio;
-
-               dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio);
-
-               ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
-                                       GPIOF_OUT_INIT_HIGH, "isp1704_reset");
-               if (ret) {
-                       dev_err(&pdev->dev, "gpio request failed\n");
-                       goto fail0;
-               }
-       }
-
-       if (!pdata) {
-               dev_err(&pdev->dev, "missing platform data!\n");
-               return -ENODEV;
-       }
-
-
-       isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
-       if (!isp)
-               return -ENOMEM;
-
-       if (np)
-               isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
-       else
-               isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
-
-       if (IS_ERR(isp->phy)) {
-               ret = PTR_ERR(isp->phy);
-               dev_err(&pdev->dev, "usb_get_phy failed\n");
-               goto fail0;
-       }
-
-       isp->dev = &pdev->dev;
-       platform_set_drvdata(pdev, isp);
-
-       isp1704_charger_set_power(isp, 1);
-
-       ret = isp1704_test_ulpi(isp);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "isp1704_test_ulpi failed\n");
-               goto fail1;
-       }
-
-       isp->psy_desc.name              = "isp1704";
-       isp->psy_desc.type              = POWER_SUPPLY_TYPE_USB;
-       isp->psy_desc.properties        = power_props;
-       isp->psy_desc.num_properties    = ARRAY_SIZE(power_props);
-       isp->psy_desc.get_property      = isp1704_charger_get_property;
-
-       psy_cfg.drv_data                = isp;
-
-       isp->psy = power_supply_register(isp->dev, &isp->psy_desc, &psy_cfg);
-       if (IS_ERR(isp->psy)) {
-               ret = PTR_ERR(isp->psy);
-               dev_err(&pdev->dev, "power_supply_register failed\n");
-               goto fail1;
-       }
-
-       /*
-        * REVISIT: using work in order to allow the usb notifications to be
-        * made atomically in the future.
-        */
-       INIT_WORK(&isp->work, isp1704_charger_work);
-
-       isp->nb.notifier_call = isp1704_notifier_call;
-
-       ret = usb_register_notifier(isp->phy, &isp->nb);
-       if (ret) {
-               dev_err(&pdev->dev, "usb_register_notifier failed\n");
-               goto fail2;
-       }
-
-       dev_info(isp->dev, "registered with product id %s\n", isp->model);
-
-       /*
-        * Taking over the D+ pullup.
-        *
-        * FIXME: The device will be disconnected if it was already
-        * enumerated. The charger driver should be always loaded before any
-        * gadget is loaded.
-        */
-       if (isp->phy->otg->gadget)
-               usb_gadget_disconnect(isp->phy->otg->gadget);
-
-       if (isp->phy->last_event == USB_EVENT_NONE)
-               isp1704_charger_set_power(isp, 0);
-
-       /* Detect charger if VBUS is valid (the cable was already plugged). */
-       if (isp->phy->last_event == USB_EVENT_VBUS &&
-                       !isp->phy->otg->default_a)
-               schedule_work(&isp->work);
-
-       return 0;
-fail2:
-       power_supply_unregister(isp->psy);
-fail1:
-       isp1704_charger_set_power(isp, 0);
-fail0:
-       dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
-
-       return ret;
-}
-
-static int isp1704_charger_remove(struct platform_device *pdev)
-{
-       struct isp1704_charger *isp = platform_get_drvdata(pdev);
-
-       usb_unregister_notifier(isp->phy, &isp->nb);
-       power_supply_unregister(isp->psy);
-       isp1704_charger_set_power(isp, 0);
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id omap_isp1704_of_match[] = {
-       { .compatible = "nxp,isp1704", },
-       { .compatible = "nxp,isp1707", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, omap_isp1704_of_match);
-#endif
-
-static struct platform_driver isp1704_charger_driver = {
-       .driver = {
-               .name = "isp1704_charger",
-               .of_match_table = of_match_ptr(omap_isp1704_of_match),
-       },
-       .probe = isp1704_charger_probe,
-       .remove = isp1704_charger_remove,
-};
-
-module_platform_driver(isp1704_charger_driver);
-
-MODULE_ALIAS("platform:isp1704_charger");
-MODULE_AUTHOR("Nokia Corporation");
-MODULE_DESCRIPTION("ISP170x USB Charger driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c
deleted file mode 100644 (file)
index 88f04f4..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Battery measurement code for Ingenic JZ SOC.
- *
- * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com>
- * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
- *
- * based on tosa_battery.c
- *
- * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
-*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/gpio.h>
-#include <linux/mfd/core.h>
-#include <linux/power_supply.h>
-
-#include <linux/power/jz4740-battery.h>
-#include <linux/jz4740-adc.h>
-
-struct jz_battery {
-       struct jz_battery_platform_data *pdata;
-       struct platform_device *pdev;
-
-       void __iomem *base;
-
-       int irq;
-       int charge_irq;
-
-       const struct mfd_cell *cell;
-
-       int status;
-       long voltage;
-
-       struct completion read_completion;
-
-       struct power_supply *battery;
-       struct power_supply_desc battery_desc;
-       struct delayed_work work;
-
-       struct mutex lock;
-};
-
-static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy)
-{
-       return power_supply_get_drvdata(psy);
-}
-
-static irqreturn_t jz_battery_irq_handler(int irq, void *devid)
-{
-       struct jz_battery *battery = devid;
-
-       complete(&battery->read_completion);
-       return IRQ_HANDLED;
-}
-
-static long jz_battery_read_voltage(struct jz_battery *battery)
-{
-       long t;
-       unsigned long val;
-       long voltage;
-
-       mutex_lock(&battery->lock);
-
-       reinit_completion(&battery->read_completion);
-
-       enable_irq(battery->irq);
-       battery->cell->enable(battery->pdev);
-
-       t = wait_for_completion_interruptible_timeout(&battery->read_completion,
-               HZ);
-
-       if (t > 0) {
-               val = readw(battery->base) & 0xfff;
-
-               if (battery->pdata->info.voltage_max_design <= 2500000)
-                       val = (val * 78125UL) >> 7UL;
-               else
-                       val = ((val * 924375UL) >> 9UL) + 33000;
-               voltage = (long)val;
-       } else {
-               voltage = t ? t : -ETIMEDOUT;
-       }
-
-       battery->cell->disable(battery->pdev);
-       disable_irq(battery->irq);
-
-       mutex_unlock(&battery->lock);
-
-       return voltage;
-}
-
-static int jz_battery_get_capacity(struct power_supply *psy)
-{
-       struct jz_battery *jz_battery = psy_to_jz_battery(psy);
-       struct power_supply_info *info = &jz_battery->pdata->info;
-       long voltage;
-       int ret;
-       int voltage_span;
-
-       voltage = jz_battery_read_voltage(jz_battery);
-
-       if (voltage < 0)
-               return voltage;
-
-       voltage_span = info->voltage_max_design - info->voltage_min_design;
-       ret = ((voltage - info->voltage_min_design) * 100) / voltage_span;
-
-       if (ret > 100)
-               ret = 100;
-       else if (ret < 0)
-               ret = 0;
-
-       return ret;
-}
-
-static int jz_battery_get_property(struct power_supply *psy,
-       enum power_supply_property psp, union power_supply_propval *val)
-{
-       struct jz_battery *jz_battery = psy_to_jz_battery(psy);
-       struct power_supply_info *info = &jz_battery->pdata->info;
-       long voltage;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = jz_battery->status;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = jz_battery->pdata->info.technology;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               voltage = jz_battery_read_voltage(jz_battery);
-               if (voltage < info->voltage_min_design)
-                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               val->intval = jz_battery_get_capacity(psy);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = jz_battery_read_voltage(jz_battery);
-               if (val->intval < 0)
-                       return val->intval;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = info->voltage_max_design;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = info->voltage_min_design;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = 1;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static void jz_battery_external_power_changed(struct power_supply *psy)
-{
-       struct jz_battery *jz_battery = psy_to_jz_battery(psy);
-
-       mod_delayed_work(system_wq, &jz_battery->work, 0);
-}
-
-static irqreturn_t jz_battery_charge_irq(int irq, void *data)
-{
-       struct jz_battery *jz_battery = data;
-
-       mod_delayed_work(system_wq, &jz_battery->work, 0);
-
-       return IRQ_HANDLED;
-}
-
-static void jz_battery_update(struct jz_battery *jz_battery)
-{
-       int status;
-       long voltage;
-       bool has_changed = false;
-       int is_charging;
-
-       if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
-               is_charging = gpio_get_value(jz_battery->pdata->gpio_charge);
-               is_charging ^= jz_battery->pdata->gpio_charge_active_low;
-               if (is_charging)
-                       status = POWER_SUPPLY_STATUS_CHARGING;
-               else
-                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-
-               if (status != jz_battery->status) {
-                       jz_battery->status = status;
-                       has_changed = true;
-               }
-       }
-
-       voltage = jz_battery_read_voltage(jz_battery);
-       if (voltage >= 0 && abs(voltage - jz_battery->voltage) > 50000) {
-               jz_battery->voltage = voltage;
-               has_changed = true;
-       }
-
-       if (has_changed)
-               power_supply_changed(jz_battery->battery);
-}
-
-static enum power_supply_property jz_battery_properties[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_PRESENT,
-};
-
-static void jz_battery_work(struct work_struct *work)
-{
-       /* Too small interval will increase system workload */
-       const int interval = HZ * 30;
-       struct jz_battery *jz_battery = container_of(work, struct jz_battery,
-                                           work.work);
-
-       jz_battery_update(jz_battery);
-       schedule_delayed_work(&jz_battery->work, interval);
-}
-
-static int jz_battery_probe(struct platform_device *pdev)
-{
-       int ret = 0;
-       struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data;
-       struct power_supply_config psy_cfg = {};
-       struct jz_battery *jz_battery;
-       struct power_supply_desc *battery_desc;
-       struct resource *mem;
-
-       if (!pdata) {
-               dev_err(&pdev->dev, "No platform_data supplied\n");
-               return -ENXIO;
-       }
-
-       jz_battery = devm_kzalloc(&pdev->dev, sizeof(*jz_battery), GFP_KERNEL);
-       if (!jz_battery) {
-               dev_err(&pdev->dev, "Failed to allocate driver structure\n");
-               return -ENOMEM;
-       }
-
-       jz_battery->cell = mfd_get_cell(pdev);
-
-       jz_battery->irq = platform_get_irq(pdev, 0);
-       if (jz_battery->irq < 0) {
-               dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
-               return jz_battery->irq;
-       }
-
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       jz_battery->base = devm_ioremap_resource(&pdev->dev, mem);
-       if (IS_ERR(jz_battery->base))
-               return PTR_ERR(jz_battery->base);
-
-       battery_desc = &jz_battery->battery_desc;
-       battery_desc->name = pdata->info.name;
-       battery_desc->type = POWER_SUPPLY_TYPE_BATTERY;
-       battery_desc->properties        = jz_battery_properties;
-       battery_desc->num_properties    = ARRAY_SIZE(jz_battery_properties);
-       battery_desc->get_property      = jz_battery_get_property;
-       battery_desc->external_power_changed =
-                                       jz_battery_external_power_changed;
-       battery_desc->use_for_apm       = 1;
-
-       psy_cfg.drv_data = jz_battery;
-
-       jz_battery->pdata = pdata;
-       jz_battery->pdev = pdev;
-
-       init_completion(&jz_battery->read_completion);
-       mutex_init(&jz_battery->lock);
-
-       INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work);
-
-       ret = request_irq(jz_battery->irq, jz_battery_irq_handler, 0, pdev->name,
-                       jz_battery);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to request irq %d\n", ret);
-               return ret;
-       }
-       disable_irq(jz_battery->irq);
-
-       if (gpio_is_valid(pdata->gpio_charge)) {
-               ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev));
-               if (ret) {
-                       dev_err(&pdev->dev, "charger state gpio request failed.\n");
-                       goto err_free_irq;
-               }
-               ret = gpio_direction_input(pdata->gpio_charge);
-               if (ret) {
-                       dev_err(&pdev->dev, "charger state gpio set direction failed.\n");
-                       goto err_free_gpio;
-               }
-
-               jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge);
-
-               if (jz_battery->charge_irq >= 0) {
-                       ret = request_irq(jz_battery->charge_irq,
-                                   jz_battery_charge_irq,
-                                   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                                   dev_name(&pdev->dev), jz_battery);
-                       if (ret) {
-                               dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret);
-                               goto err_free_gpio;
-                       }
-               }
-       } else {
-               jz_battery->charge_irq = -1;
-       }
-
-       if (jz_battery->pdata->info.voltage_max_design <= 2500000)
-               jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB,
-                       JZ_ADC_CONFIG_BAT_MB);
-       else
-               jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB, 0);
-
-       jz_battery->battery = power_supply_register(&pdev->dev, battery_desc,
-                                                       &psy_cfg);
-       if (IS_ERR(jz_battery->battery)) {
-               dev_err(&pdev->dev, "power supply battery register failed.\n");
-               ret = PTR_ERR(jz_battery->battery);
-               goto err_free_charge_irq;
-       }
-
-       platform_set_drvdata(pdev, jz_battery);
-       schedule_delayed_work(&jz_battery->work, 0);
-
-       return 0;
-
-err_free_charge_irq:
-       if (jz_battery->charge_irq >= 0)
-               free_irq(jz_battery->charge_irq, jz_battery);
-err_free_gpio:
-       if (gpio_is_valid(pdata->gpio_charge))
-               gpio_free(jz_battery->pdata->gpio_charge);
-err_free_irq:
-       free_irq(jz_battery->irq, jz_battery);
-       return ret;
-}
-
-static int jz_battery_remove(struct platform_device *pdev)
-{
-       struct jz_battery *jz_battery = platform_get_drvdata(pdev);
-
-       cancel_delayed_work_sync(&jz_battery->work);
-
-       if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
-               if (jz_battery->charge_irq >= 0)
-                       free_irq(jz_battery->charge_irq, jz_battery);
-               gpio_free(jz_battery->pdata->gpio_charge);
-       }
-
-       power_supply_unregister(jz_battery->battery);
-
-       free_irq(jz_battery->irq, jz_battery);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int jz_battery_suspend(struct device *dev)
-{
-       struct jz_battery *jz_battery = dev_get_drvdata(dev);
-
-       cancel_delayed_work_sync(&jz_battery->work);
-       jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN;
-
-       return 0;
-}
-
-static int jz_battery_resume(struct device *dev)
-{
-       struct jz_battery *jz_battery = dev_get_drvdata(dev);
-
-       schedule_delayed_work(&jz_battery->work, 0);
-
-       return 0;
-}
-
-static const struct dev_pm_ops jz_battery_pm_ops = {
-       .suspend        = jz_battery_suspend,
-       .resume         = jz_battery_resume,
-};
-
-#define JZ_BATTERY_PM_OPS (&jz_battery_pm_ops)
-#else
-#define JZ_BATTERY_PM_OPS NULL
-#endif
-
-static struct platform_driver jz_battery_driver = {
-       .probe          = jz_battery_probe,
-       .remove         = jz_battery_remove,
-       .driver = {
-               .name = "jz4740-battery",
-               .pm = JZ_BATTERY_PM_OPS,
-       },
-};
-
-module_platform_driver(jz_battery_driver);
-
-MODULE_ALIAS("platform:jz4740-battery");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
-MODULE_DESCRIPTION("JZ4740 SoC battery driver");
diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c
deleted file mode 100644 (file)
index 042fb3d..0000000
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * Driver for LP8727 Micro/Mini USB IC with integrated charger
- *
- *                     Copyright (C) 2011 Texas Instruments
- *                     Copyright (C) 2011 National Semiconductor
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/power_supply.h>
-#include <linux/platform_data/lp8727.h>
-#include <linux/of.h>
-
-#define LP8788_NUM_INTREGS     2
-#define DEFAULT_DEBOUNCE_MSEC  270
-
-/* Registers */
-#define LP8727_CTRL1           0x1
-#define LP8727_CTRL2           0x2
-#define LP8727_SWCTRL          0x3
-#define LP8727_INT1            0x4
-#define LP8727_INT2            0x5
-#define LP8727_STATUS1         0x6
-#define LP8727_STATUS2         0x7
-#define LP8727_CHGCTRL2                0x9
-
-/* CTRL1 register */
-#define LP8727_CP_EN           BIT(0)
-#define LP8727_ADC_EN          BIT(1)
-#define LP8727_ID200_EN                BIT(4)
-
-/* CTRL2 register */
-#define LP8727_CHGDET_EN       BIT(1)
-#define LP8727_INT_EN          BIT(6)
-
-/* SWCTRL register */
-#define LP8727_SW_DM1_DM       (0x0 << 0)
-#define LP8727_SW_DM1_HiZ      (0x7 << 0)
-#define LP8727_SW_DP2_DP       (0x0 << 3)
-#define LP8727_SW_DP2_HiZ      (0x7 << 3)
-
-/* INT1 register */
-#define LP8727_IDNO            (0xF << 0)
-#define LP8727_VBUS            BIT(4)
-
-/* STATUS1 register */
-#define LP8727_CHGSTAT         (3 << 4)
-#define LP8727_CHPORT          BIT(6)
-#define LP8727_DCPORT          BIT(7)
-#define LP8727_STAT_EOC                0x30
-
-/* STATUS2 register */
-#define LP8727_TEMP_STAT       (3 << 5)
-#define LP8727_TEMP_SHIFT      5
-
-/* CHGCTRL2 register */
-#define LP8727_ICHG_SHIFT      4
-
-enum lp8727_dev_id {
-       LP8727_ID_NONE,
-       LP8727_ID_TA,
-       LP8727_ID_DEDICATED_CHG,
-       LP8727_ID_USB_CHG,
-       LP8727_ID_USB_DS,
-       LP8727_ID_MAX,
-};
-
-enum lp8727_die_temp {
-       LP8788_TEMP_75C,
-       LP8788_TEMP_95C,
-       LP8788_TEMP_115C,
-       LP8788_TEMP_135C,
-};
-
-struct lp8727_psy {
-       struct power_supply *ac;
-       struct power_supply *usb;
-       struct power_supply *batt;
-};
-
-struct lp8727_chg {
-       struct device *dev;
-       struct i2c_client *client;
-       struct mutex xfer_lock;
-       struct lp8727_psy *psy;
-       struct lp8727_platform_data *pdata;
-
-       /* Charger Data */
-       enum lp8727_dev_id devid;
-       struct lp8727_chg_param *chg_param;
-
-       /* Interrupt Handling */
-       int irq;
-       struct delayed_work work;
-       unsigned long debounce_jiffies;
-};
-
-static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
-{
-       s32 ret;
-
-       mutex_lock(&pchg->xfer_lock);
-       ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data);
-       mutex_unlock(&pchg->xfer_lock);
-
-       return (ret != len) ? -EIO : 0;
-}
-
-static inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data)
-{
-       return lp8727_read_bytes(pchg, reg, data, 1);
-}
-
-static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data)
-{
-       int ret;
-
-       mutex_lock(&pchg->xfer_lock);
-       ret = i2c_smbus_write_byte_data(pchg->client, reg, data);
-       mutex_unlock(&pchg->xfer_lock);
-
-       return ret;
-}
-
-static bool lp8727_is_charger_attached(const char *name, int id)
-{
-       if (!strcmp(name, "ac"))
-               return id == LP8727_ID_TA || id == LP8727_ID_DEDICATED_CHG;
-       else if (!strcmp(name, "usb"))
-               return id == LP8727_ID_USB_CHG;
-
-       return id >= LP8727_ID_TA && id <= LP8727_ID_USB_CHG;
-}
-
-static int lp8727_init_device(struct lp8727_chg *pchg)
-{
-       u8 val;
-       int ret;
-       u8 intstat[LP8788_NUM_INTREGS];
-
-       /* clear interrupts */
-       ret = lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS);
-       if (ret)
-               return ret;
-
-       val = LP8727_ID200_EN | LP8727_ADC_EN | LP8727_CP_EN;
-       ret = lp8727_write_byte(pchg, LP8727_CTRL1, val);
-       if (ret)
-               return ret;
-
-       val = LP8727_INT_EN | LP8727_CHGDET_EN;
-       return lp8727_write_byte(pchg, LP8727_CTRL2, val);
-}
-
-static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
-{
-       u8 val;
-
-       lp8727_read_byte(pchg, LP8727_STATUS1, &val);
-       return val & LP8727_DCPORT;
-}
-
-static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
-{
-       u8 val;
-
-       lp8727_read_byte(pchg, LP8727_STATUS1, &val);
-       return val & LP8727_CHPORT;
-}
-
-static inline void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
-{
-       lp8727_write_byte(pchg, LP8727_SWCTRL, sw);
-}
-
-static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
-{
-       struct lp8727_platform_data *pdata = pchg->pdata;
-       u8 devid = LP8727_ID_NONE;
-       u8 swctrl = LP8727_SW_DM1_HiZ | LP8727_SW_DP2_HiZ;
-
-       switch (id) {
-       case 0x5:
-               devid = LP8727_ID_TA;
-               pchg->chg_param = pdata ? pdata->ac : NULL;
-               break;
-       case 0xB:
-               if (lp8727_is_dedicated_charger(pchg)) {
-                       pchg->chg_param = pdata ? pdata->ac : NULL;
-                       devid = LP8727_ID_DEDICATED_CHG;
-               } else if (lp8727_is_usb_charger(pchg)) {
-                       pchg->chg_param = pdata ? pdata->usb : NULL;
-                       devid = LP8727_ID_USB_CHG;
-                       swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;
-               } else if (vbusin) {
-                       devid = LP8727_ID_USB_DS;
-                       swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;
-               }
-               break;
-       default:
-               devid = LP8727_ID_NONE;
-               pchg->chg_param = NULL;
-               break;
-       }
-
-       pchg->devid = devid;
-       lp8727_ctrl_switch(pchg, swctrl);
-}
-
-static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
-{
-       u8 val;
-
-       lp8727_read_byte(pchg, LP8727_CTRL2, &val);
-       val |= LP8727_CHGDET_EN;
-       lp8727_write_byte(pchg, LP8727_CTRL2, val);
-}
-
-static void lp8727_delayed_func(struct work_struct *_work)
-{
-       struct lp8727_chg *pchg = container_of(_work, struct lp8727_chg,
-                                               work.work);
-       u8 intstat[LP8788_NUM_INTREGS];
-       u8 idno;
-       u8 vbus;
-
-       if (lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS)) {
-               dev_err(pchg->dev, "can not read INT registers\n");
-               return;
-       }
-
-       idno = intstat[0] & LP8727_IDNO;
-       vbus = intstat[0] & LP8727_VBUS;
-
-       lp8727_id_detection(pchg, idno, vbus);
-       lp8727_enable_chgdet(pchg);
-
-       power_supply_changed(pchg->psy->ac);
-       power_supply_changed(pchg->psy->usb);
-       power_supply_changed(pchg->psy->batt);
-}
-
-static irqreturn_t lp8727_isr_func(int irq, void *ptr)
-{
-       struct lp8727_chg *pchg = ptr;
-
-       schedule_delayed_work(&pchg->work, pchg->debounce_jiffies);
-       return IRQ_HANDLED;
-}
-
-static int lp8727_setup_irq(struct lp8727_chg *pchg)
-{
-       int ret;
-       int irq = pchg->client->irq;
-       unsigned delay_msec = pchg->pdata ? pchg->pdata->debounce_msec :
-                                               DEFAULT_DEBOUNCE_MSEC;
-
-       INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
-
-       if (irq <= 0) {
-               dev_warn(pchg->dev, "invalid irq number: %d\n", irq);
-               return 0;
-       }
-
-       ret = request_threaded_irq(irq, NULL, lp8727_isr_func,
-                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                               "lp8727_irq", pchg);
-
-       if (ret)
-               return ret;
-
-       pchg->irq = irq;
-       pchg->debounce_jiffies = msecs_to_jiffies(delay_msec);
-
-       return 0;
-}
-
-static void lp8727_release_irq(struct lp8727_chg *pchg)
-{
-       cancel_delayed_work_sync(&pchg->work);
-
-       if (pchg->irq)
-               free_irq(pchg->irq, pchg);
-}
-
-static enum power_supply_property lp8727_charger_prop[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static enum power_supply_property lp8727_battery_prop[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static char *battery_supplied_to[] = {
-       "main_batt",
-};
-
-static int lp8727_charger_get_property(struct power_supply *psy,
-                                      enum power_supply_property psp,
-                                      union power_supply_propval *val)
-{
-       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);
-
-       if (psp != POWER_SUPPLY_PROP_ONLINE)
-               return -EINVAL;
-
-       val->intval = lp8727_is_charger_attached(psy->desc->name, pchg->devid);
-
-       return 0;
-}
-
-static bool lp8727_is_high_temperature(enum lp8727_die_temp temp)
-{
-       switch (temp) {
-       case LP8788_TEMP_95C:
-       case LP8788_TEMP_115C:
-       case LP8788_TEMP_135C:
-               return true;
-       default:
-               return false;
-       }
-}
-
-static int lp8727_battery_get_property(struct power_supply *psy,
-                                      enum power_supply_property psp,
-                                      union power_supply_propval *val)
-{
-       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);
-       struct lp8727_platform_data *pdata = pchg->pdata;
-       enum lp8727_die_temp temp;
-       u8 read;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid)) {
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-                       return 0;
-               }
-
-               lp8727_read_byte(pchg, LP8727_STATUS1, &read);
-
-               val->intval = (read & LP8727_CHGSTAT) == LP8727_STAT_EOC ?
-                               POWER_SUPPLY_STATUS_FULL :
-                               POWER_SUPPLY_STATUS_CHARGING;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               lp8727_read_byte(pchg, LP8727_STATUS2, &read);
-               temp = (read & LP8727_TEMP_STAT) >> LP8727_TEMP_SHIFT;
-
-               val->intval = lp8727_is_high_temperature(temp) ?
-                       POWER_SUPPLY_HEALTH_OVERHEAT :
-                       POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               if (!pdata)
-                       return -EINVAL;
-
-               if (pdata->get_batt_present)
-                       val->intval = pdata->get_batt_present();
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (!pdata)
-                       return -EINVAL;
-
-               if (pdata->get_batt_level)
-                       val->intval = pdata->get_batt_level();
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               if (!pdata)
-                       return -EINVAL;
-
-               if (pdata->get_batt_capacity)
-                       val->intval = pdata->get_batt_capacity();
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               if (!pdata)
-                       return -EINVAL;
-
-               if (pdata->get_batt_temp)
-                       val->intval = pdata->get_batt_temp();
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static void lp8727_charger_changed(struct power_supply *psy)
-{
-       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);
-       u8 eoc_level;
-       u8 ichg;
-       u8 val;
-
-       /* skip if no charger exists */
-       if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid))
-               return;
-
-       /* update charging parameters */
-       if (pchg->chg_param) {
-               eoc_level = pchg->chg_param->eoc_level;
-               ichg = pchg->chg_param->ichg;
-               val = (ichg << LP8727_ICHG_SHIFT) | eoc_level;
-               lp8727_write_byte(pchg, LP8727_CHGCTRL2, val);
-       }
-}
-
-static const struct power_supply_desc lp8727_ac_desc = {
-       .name                   = "ac",
-       .type                   = POWER_SUPPLY_TYPE_MAINS,
-       .properties             = lp8727_charger_prop,
-       .num_properties         = ARRAY_SIZE(lp8727_charger_prop),
-       .get_property           = lp8727_charger_get_property,
-};
-
-static const struct power_supply_desc lp8727_usb_desc = {
-       .name                   = "usb",
-       .type                   = POWER_SUPPLY_TYPE_USB,
-       .properties             = lp8727_charger_prop,
-       .num_properties         = ARRAY_SIZE(lp8727_charger_prop),
-       .get_property           = lp8727_charger_get_property,
-};
-
-static const struct power_supply_desc lp8727_batt_desc = {
-       .name                   = "main_batt",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = lp8727_battery_prop,
-       .num_properties         = ARRAY_SIZE(lp8727_battery_prop),
-       .get_property           = lp8727_battery_get_property,
-       .external_power_changed = lp8727_charger_changed,
-};
-
-static int lp8727_register_psy(struct lp8727_chg *pchg)
-{
-       struct power_supply_config psy_cfg = {}; /* Only for ac and usb */
-       struct lp8727_psy *psy;
-
-       psy = devm_kzalloc(pchg->dev, sizeof(*psy), GFP_KERNEL);
-       if (!psy)
-               return -ENOMEM;
-
-       pchg->psy = psy;
-
-       psy_cfg.supplied_to = battery_supplied_to;
-       psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
-
-       psy->ac = power_supply_register(pchg->dev, &lp8727_ac_desc, &psy_cfg);
-       if (IS_ERR(psy->ac))
-               goto err_psy_ac;
-
-       psy->usb = power_supply_register(pchg->dev, &lp8727_usb_desc,
-                                        &psy_cfg);
-       if (IS_ERR(psy->usb))
-               goto err_psy_usb;
-
-       psy->batt = power_supply_register(pchg->dev, &lp8727_batt_desc, NULL);
-       if (IS_ERR(psy->batt))
-               goto err_psy_batt;
-
-       return 0;
-
-err_psy_batt:
-       power_supply_unregister(psy->usb);
-err_psy_usb:
-       power_supply_unregister(psy->ac);
-err_psy_ac:
-       return -EPERM;
-}
-
-static void lp8727_unregister_psy(struct lp8727_chg *pchg)
-{
-       struct lp8727_psy *psy = pchg->psy;
-
-       if (!psy)
-               return;
-
-       power_supply_unregister(psy->ac);
-       power_supply_unregister(psy->usb);
-       power_supply_unregister(psy->batt);
-}
-
-#ifdef CONFIG_OF
-static struct lp8727_chg_param
-*lp8727_parse_charge_pdata(struct device *dev, struct device_node *np)
-{
-       struct lp8727_chg_param *param;
-
-       param = devm_kzalloc(dev, sizeof(*param), GFP_KERNEL);
-       if (!param)
-               goto out;
-
-       of_property_read_u8(np, "eoc-level", (u8 *)&param->eoc_level);
-       of_property_read_u8(np, "charging-current", (u8 *)&param->ichg);
-out:
-       return param;
-}
-
-static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)
-{
-       struct device_node *np = dev->of_node;
-       struct device_node *child;
-       struct lp8727_platform_data *pdata;
-       const char *type;
-
-       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return ERR_PTR(-ENOMEM);
-
-       of_property_read_u32(np, "debounce-ms", &pdata->debounce_msec);
-
-       /* If charging parameter is not defined, just skip parsing the dt */
-       if (of_get_child_count(np) == 0)
-               return pdata;
-
-       for_each_child_of_node(np, child) {
-               of_property_read_string(child, "charger-type", &type);
-
-               if (!strcmp(type, "ac"))
-                       pdata->ac = lp8727_parse_charge_pdata(dev, child);
-
-               if (!strcmp(type, "usb"))
-                       pdata->usb = lp8727_parse_charge_pdata(dev, child);
-       }
-
-       return pdata;
-}
-#else
-static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)
-{
-       return NULL;
-}
-#endif
-
-static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
-{
-       struct lp8727_chg *pchg;
-       struct lp8727_platform_data *pdata;
-       int ret;
-
-       if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
-               return -EIO;
-
-       if (cl->dev.of_node) {
-               pdata = lp8727_parse_dt(&cl->dev);
-               if (IS_ERR(pdata))
-                       return PTR_ERR(pdata);
-       } else {
-               pdata = dev_get_platdata(&cl->dev);
-       }
-
-       pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL);
-       if (!pchg)
-               return -ENOMEM;
-
-       pchg->client = cl;
-       pchg->dev = &cl->dev;
-       pchg->pdata = pdata;
-       i2c_set_clientdata(cl, pchg);
-
-       mutex_init(&pchg->xfer_lock);
-
-       ret = lp8727_init_device(pchg);
-       if (ret) {
-               dev_err(pchg->dev, "i2c communication err: %d", ret);
-               return ret;
-       }
-
-       ret = lp8727_register_psy(pchg);
-       if (ret) {
-               dev_err(pchg->dev, "power supplies register err: %d", ret);
-               return ret;
-       }
-
-       ret = lp8727_setup_irq(pchg);
-       if (ret) {
-               dev_err(pchg->dev, "irq handler err: %d", ret);
-               lp8727_unregister_psy(pchg);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int lp8727_remove(struct i2c_client *cl)
-{
-       struct lp8727_chg *pchg = i2c_get_clientdata(cl);
-
-       lp8727_release_irq(pchg);
-       lp8727_unregister_psy(pchg);
-       return 0;
-}
-
-static const struct of_device_id lp8727_dt_ids[] = {
-       { .compatible = "ti,lp8727", },
-       { }
-};
-MODULE_DEVICE_TABLE(of, lp8727_dt_ids);
-
-static const struct i2c_device_id lp8727_ids[] = {
-       {"lp8727", 0},
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, lp8727_ids);
-
-static struct i2c_driver lp8727_driver = {
-       .driver = {
-                  .name = "lp8727",
-                  .of_match_table = of_match_ptr(lp8727_dt_ids),
-                  },
-       .probe = lp8727_probe,
-       .remove = lp8727_remove,
-       .id_table = lp8727_ids,
-};
-module_i2c_driver(lp8727_driver);
-
-MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver");
-MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/lp8788-charger.c b/drivers/power/lp8788-charger.c
deleted file mode 100644 (file)
index 7321b72..0000000
+++ /dev/null
@@ -1,764 +0,0 @@
-/*
- * TI LP8788 MFD - battery charger driver
- *
- * Copyright 2012 Texas Instruments
- *
- * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/err.h>
-#include <linux/iio/consumer.h>
-#include <linux/interrupt.h>
-#include <linux/irqdomain.h>
-#include <linux/mfd/lp8788.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-
-/* register address */
-#define LP8788_CHG_STATUS              0x07
-#define LP8788_CHG_IDCIN               0x13
-#define LP8788_CHG_IBATT               0x14
-#define LP8788_CHG_VTERM               0x15
-#define LP8788_CHG_EOC                 0x16
-
-/* mask/shift bits */
-#define LP8788_CHG_INPUT_STATE_M       0x03    /* Addr 07h */
-#define LP8788_CHG_STATE_M             0x3C
-#define LP8788_CHG_STATE_S             2
-#define LP8788_NO_BATT_M               BIT(6)
-#define LP8788_BAD_BATT_M              BIT(7)
-#define LP8788_CHG_IBATT_M             0x1F    /* Addr 14h */
-#define LP8788_CHG_VTERM_M             0x0F    /* Addr 15h */
-#define LP8788_CHG_EOC_LEVEL_M         0x30    /* Addr 16h */
-#define LP8788_CHG_EOC_LEVEL_S         4
-#define LP8788_CHG_EOC_TIME_M          0x0E
-#define LP8788_CHG_EOC_TIME_S          1
-#define LP8788_CHG_EOC_MODE_M          BIT(0)
-
-#define LP8788_CHARGER_NAME            "charger"
-#define LP8788_BATTERY_NAME            "main_batt"
-
-#define LP8788_CHG_START               0x11
-#define LP8788_CHG_END                 0x1C
-
-#define LP8788_ISEL_MAX                        23
-#define LP8788_ISEL_STEP               50
-#define LP8788_VTERM_MIN               4100
-#define LP8788_VTERM_STEP              25
-#define LP8788_MAX_BATT_CAPACITY       100
-#define LP8788_MAX_CHG_IRQS            11
-
-enum lp8788_charging_state {
-       LP8788_OFF,
-       LP8788_WARM_UP,
-       LP8788_LOW_INPUT = 0x3,
-       LP8788_PRECHARGE,
-       LP8788_CC,
-       LP8788_CV,
-       LP8788_MAINTENANCE,
-       LP8788_BATTERY_FAULT,
-       LP8788_SYSTEM_SUPPORT = 0xC,
-       LP8788_HIGH_CURRENT = 0xF,
-       LP8788_MAX_CHG_STATE,
-};
-
-enum lp8788_charger_adc_sel {
-       LP8788_VBATT,
-       LP8788_BATT_TEMP,
-       LP8788_NUM_CHG_ADC,
-};
-
-enum lp8788_charger_input_state {
-       LP8788_SYSTEM_SUPPLY = 1,
-       LP8788_FULL_FUNCTION,
-};
-
-/*
- * struct lp8788_chg_irq
- * @which        : lp8788 interrupt id
- * @virq         : Linux IRQ number from irq_domain
- */
-struct lp8788_chg_irq {
-       enum lp8788_int_id which;
-       int virq;
-};
-
-/*
- * struct lp8788_charger
- * @lp           : used for accessing the registers of mfd lp8788 device
- * @charger      : power supply driver for the battery charger
- * @battery      : power supply driver for the battery
- * @charger_work : work queue for charger input interrupts
- * @chan         : iio channels for getting adc values
- *                 eg) battery voltage, capacity and temperature
- * @irqs         : charger dedicated interrupts
- * @num_irqs     : total numbers of charger interrupts
- * @pdata        : charger platform specific data
- */
-struct lp8788_charger {
-       struct lp8788 *lp;
-       struct power_supply *charger;
-       struct power_supply *battery;
-       struct work_struct charger_work;
-       struct iio_channel *chan[LP8788_NUM_CHG_ADC];
-       struct lp8788_chg_irq irqs[LP8788_MAX_CHG_IRQS];
-       int num_irqs;
-       struct lp8788_charger_platform_data *pdata;
-};
-
-static char *battery_supplied_to[] = {
-       LP8788_BATTERY_NAME,
-};
-
-static enum power_supply_property lp8788_charger_prop[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_CURRENT_MAX,
-};
-
-static enum power_supply_property lp8788_battery_prop[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static bool lp8788_is_charger_detected(struct lp8788_charger *pchg)
-{
-       u8 data;
-
-       lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
-       data &= LP8788_CHG_INPUT_STATE_M;
-
-       return data == LP8788_SYSTEM_SUPPLY || data == LP8788_FULL_FUNCTION;
-}
-
-static int lp8788_charger_get_property(struct power_supply *psy,
-                                       enum power_supply_property psp,
-                                       union power_supply_propval *val)
-{
-       struct lp8788_charger *pchg = dev_get_drvdata(psy->dev.parent);
-       u8 read;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = lp8788_is_charger_detected(pchg);
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_MAX:
-               lp8788_read_byte(pchg->lp, LP8788_CHG_IDCIN, &read);
-               val->intval = LP8788_ISEL_STEP *
-                               (min_t(int, read, LP8788_ISEL_MAX) + 1);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int lp8788_get_battery_status(struct lp8788_charger *pchg,
-                               union power_supply_propval *val)
-{
-       enum lp8788_charging_state state;
-       u8 data;
-       int ret;
-
-       ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
-       if (ret)
-               return ret;
-
-       state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
-       switch (state) {
-       case LP8788_OFF:
-               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               break;
-       case LP8788_PRECHARGE:
-       case LP8788_CC:
-       case LP8788_CV:
-       case LP8788_HIGH_CURRENT:
-               val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               break;
-       case LP8788_MAINTENANCE:
-               val->intval = POWER_SUPPLY_STATUS_FULL;
-               break;
-       default:
-               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               break;
-       }
-
-       return 0;
-}
-
-static int lp8788_get_battery_health(struct lp8788_charger *pchg,
-                               union power_supply_propval *val)
-{
-       u8 data;
-       int ret;
-
-       ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
-       if (ret)
-               return ret;
-
-       if (data & LP8788_NO_BATT_M)
-               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-       else if (data & LP8788_BAD_BATT_M)
-               val->intval = POWER_SUPPLY_HEALTH_DEAD;
-       else
-               val->intval = POWER_SUPPLY_HEALTH_GOOD;
-
-       return 0;
-}
-
-static int lp8788_get_battery_present(struct lp8788_charger *pchg,
-                               union power_supply_propval *val)
-{
-       u8 data;
-       int ret;
-
-       ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
-       if (ret)
-               return ret;
-
-       val->intval = !(data & LP8788_NO_BATT_M);
-       return 0;
-}
-
-static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, int *result)
-{
-       struct iio_channel *channel = pchg->chan[LP8788_VBATT];
-
-       if (!channel)
-               return -EINVAL;
-
-       return iio_read_channel_processed(channel, result);
-}
-
-static int lp8788_get_battery_voltage(struct lp8788_charger *pchg,
-                               union power_supply_propval *val)
-{
-       return lp8788_get_vbatt_adc(pchg, &val->intval);
-}
-
-static int lp8788_get_battery_capacity(struct lp8788_charger *pchg,
-                               union power_supply_propval *val)
-{
-       struct lp8788 *lp = pchg->lp;
-       struct lp8788_charger_platform_data *pdata = pchg->pdata;
-       unsigned int max_vbatt;
-       int vbatt;
-       enum lp8788_charging_state state;
-       u8 data;
-       int ret;
-
-       if (!pdata)
-               return -EINVAL;
-
-       max_vbatt = pdata->max_vbatt_mv;
-       if (max_vbatt == 0)
-               return -EINVAL;
-
-       ret = lp8788_read_byte(lp, LP8788_CHG_STATUS, &data);
-       if (ret)
-               return ret;
-
-       state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
-
-       if (state == LP8788_MAINTENANCE) {
-               val->intval = LP8788_MAX_BATT_CAPACITY;
-       } else {
-               ret = lp8788_get_vbatt_adc(pchg, &vbatt);
-               if (ret)
-                       return ret;
-
-               val->intval = (vbatt * LP8788_MAX_BATT_CAPACITY) / max_vbatt;
-               val->intval = min(val->intval, LP8788_MAX_BATT_CAPACITY);
-       }
-
-       return 0;
-}
-
-static int lp8788_get_battery_temperature(struct lp8788_charger *pchg,
-                               union power_supply_propval *val)
-{
-       struct iio_channel *channel = pchg->chan[LP8788_BATT_TEMP];
-       int result;
-       int ret;
-
-       if (!channel)
-               return -EINVAL;
-
-       ret = iio_read_channel_processed(channel, &result);
-       if (ret < 0)
-               return -EINVAL;
-
-       /* unit: 0.1 'C */
-       val->intval = result * 10;
-
-       return 0;
-}
-
-static int lp8788_get_battery_charging_current(struct lp8788_charger *pchg,
-                               union power_supply_propval *val)
-{
-       u8 read;
-
-       lp8788_read_byte(pchg->lp, LP8788_CHG_IBATT, &read);
-       read &= LP8788_CHG_IBATT_M;
-       val->intval = LP8788_ISEL_STEP *
-                       (min_t(int, read, LP8788_ISEL_MAX) + 1);
-
-       return 0;
-}
-
-static int lp8788_get_charging_termination_voltage(struct lp8788_charger *pchg,
-                               union power_supply_propval *val)
-{
-       u8 read;
-
-       lp8788_read_byte(pchg->lp, LP8788_CHG_VTERM, &read);
-       read &= LP8788_CHG_VTERM_M;
-       val->intval = LP8788_VTERM_MIN + LP8788_VTERM_STEP * read;
-
-       return 0;
-}
-
-static int lp8788_battery_get_property(struct power_supply *psy,
-                                       enum power_supply_property psp,
-                                       union power_supply_propval *val)
-{
-       struct lp8788_charger *pchg = dev_get_drvdata(psy->dev.parent);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               return lp8788_get_battery_status(pchg, val);
-       case POWER_SUPPLY_PROP_HEALTH:
-               return lp8788_get_battery_health(pchg, val);
-       case POWER_SUPPLY_PROP_PRESENT:
-               return lp8788_get_battery_present(pchg, val);
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               return lp8788_get_battery_voltage(pchg, val);
-       case POWER_SUPPLY_PROP_CAPACITY:
-               return lp8788_get_battery_capacity(pchg, val);
-       case POWER_SUPPLY_PROP_TEMP:
-               return lp8788_get_battery_temperature(pchg, val);
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               return lp8788_get_battery_charging_current(pchg, val);
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
-               return lp8788_get_charging_termination_voltage(pchg, val);
-       default:
-               return -EINVAL;
-       }
-}
-
-static inline bool lp8788_is_valid_charger_register(u8 addr)
-{
-       return addr >= LP8788_CHG_START && addr <= LP8788_CHG_END;
-}
-
-static int lp8788_update_charger_params(struct platform_device *pdev,
-                                       struct lp8788_charger *pchg)
-{
-       struct lp8788 *lp = pchg->lp;
-       struct lp8788_charger_platform_data *pdata = pchg->pdata;
-       struct lp8788_chg_param *param;
-       int i;
-       int ret;
-
-       if (!pdata || !pdata->chg_params) {
-               dev_info(&pdev->dev, "skip updating charger parameters\n");
-               return 0;
-       }
-
-       /* settting charging parameters */
-       for (i = 0; i < pdata->num_chg_params; i++) {
-               param = pdata->chg_params + i;
-
-               if (!param)
-                       continue;
-
-               if (lp8788_is_valid_charger_register(param->addr)) {
-                       ret = lp8788_write_byte(lp, param->addr, param->val);
-                       if (ret)
-                               return ret;
-               }
-       }
-
-       return 0;
-}
-
-static const struct power_supply_desc lp8788_psy_charger_desc = {
-       .name           = LP8788_CHARGER_NAME,
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-       .properties     = lp8788_charger_prop,
-       .num_properties = ARRAY_SIZE(lp8788_charger_prop),
-       .get_property   = lp8788_charger_get_property,
-};
-
-static const struct power_supply_desc lp8788_psy_battery_desc = {
-       .name           = LP8788_BATTERY_NAME,
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = lp8788_battery_prop,
-       .num_properties = ARRAY_SIZE(lp8788_battery_prop),
-       .get_property   = lp8788_battery_get_property,
-};
-
-static int lp8788_psy_register(struct platform_device *pdev,
-                               struct lp8788_charger *pchg)
-{
-       struct power_supply_config charger_cfg = {};
-
-       charger_cfg.supplied_to = battery_supplied_to;
-       charger_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
-
-       pchg->charger = power_supply_register(&pdev->dev,
-                                             &lp8788_psy_charger_desc,
-                                             &charger_cfg);
-       if (IS_ERR(pchg->charger))
-               return -EPERM;
-
-       pchg->battery = power_supply_register(&pdev->dev,
-                                             &lp8788_psy_battery_desc, NULL);
-       if (IS_ERR(pchg->battery)) {
-               power_supply_unregister(pchg->charger);
-               return -EPERM;
-       }
-
-       return 0;
-}
-
-static void lp8788_psy_unregister(struct lp8788_charger *pchg)
-{
-       power_supply_unregister(pchg->battery);
-       power_supply_unregister(pchg->charger);
-}
-
-static void lp8788_charger_event(struct work_struct *work)
-{
-       struct lp8788_charger *pchg =
-               container_of(work, struct lp8788_charger, charger_work);
-       struct lp8788_charger_platform_data *pdata = pchg->pdata;
-       enum lp8788_charger_event event = lp8788_is_charger_detected(pchg);
-
-       pdata->charger_event(pchg->lp, event);
-}
-
-static bool lp8788_find_irq_id(struct lp8788_charger *pchg, int virq, int *id)
-{
-       bool found = false;
-       int i;
-
-       for (i = 0; i < pchg->num_irqs; i++) {
-               if (pchg->irqs[i].virq == virq) {
-                       *id = pchg->irqs[i].which;
-                       found = true;
-                       break;
-               }
-       }
-
-       return found;
-}
-
-static irqreturn_t lp8788_charger_irq_thread(int virq, void *ptr)
-{
-       struct lp8788_charger *pchg = ptr;
-       struct lp8788_charger_platform_data *pdata = pchg->pdata;
-       int id = -1;
-
-       if (!lp8788_find_irq_id(pchg, virq, &id))
-               return IRQ_NONE;
-
-       switch (id) {
-       case LP8788_INT_CHG_INPUT_STATE:
-       case LP8788_INT_CHG_STATE:
-       case LP8788_INT_EOC:
-       case LP8788_INT_BATT_LOW:
-       case LP8788_INT_NO_BATT:
-               power_supply_changed(pchg->charger);
-               power_supply_changed(pchg->battery);
-               break;
-       default:
-               break;
-       }
-
-       /* report charger dectection event if used */
-       if (!pdata)
-               goto irq_handled;
-
-       if (pdata->charger_event && id == LP8788_INT_CHG_INPUT_STATE)
-               schedule_work(&pchg->charger_work);
-
-irq_handled:
-       return IRQ_HANDLED;
-}
-
-static int lp8788_set_irqs(struct platform_device *pdev,
-                       struct lp8788_charger *pchg, const char *name)
-{
-       struct resource *r;
-       struct irq_domain *irqdm = pchg->lp->irqdm;
-       int irq_start;
-       int irq_end;
-       int virq;
-       int nr_irq;
-       int i;
-       int ret;
-
-       /* no error even if no irq resource */
-       r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, name);
-       if (!r)
-               return 0;
-
-       irq_start = r->start;
-       irq_end = r->end;
-
-       for (i = irq_start; i <= irq_end; i++) {
-               nr_irq = pchg->num_irqs;
-
-               virq = irq_create_mapping(irqdm, i);
-               pchg->irqs[nr_irq].virq = virq;
-               pchg->irqs[nr_irq].which = i;
-               pchg->num_irqs++;
-
-               ret = request_threaded_irq(virq, NULL,
-                                       lp8788_charger_irq_thread,
-                                       0, name, pchg);
-               if (ret)
-                       break;
-       }
-
-       if (i <= irq_end)
-               goto err_free_irq;
-
-       return 0;
-
-err_free_irq:
-       for (i = 0; i < pchg->num_irqs; i++)
-               free_irq(pchg->irqs[i].virq, pchg);
-       return ret;
-}
-
-static int lp8788_irq_register(struct platform_device *pdev,
-                               struct lp8788_charger *pchg)
-{
-       const char *name[] = {
-               LP8788_CHG_IRQ, LP8788_PRSW_IRQ, LP8788_BATT_IRQ
-       };
-       int i;
-       int ret;
-
-       INIT_WORK(&pchg->charger_work, lp8788_charger_event);
-       pchg->num_irqs = 0;
-
-       for (i = 0; i < ARRAY_SIZE(name); i++) {
-               ret = lp8788_set_irqs(pdev, pchg, name[i]);
-               if (ret) {
-                       dev_warn(&pdev->dev, "irq setup failed: %s\n", name[i]);
-                       return ret;
-               }
-       }
-
-       if (pchg->num_irqs > LP8788_MAX_CHG_IRQS) {
-               dev_err(&pdev->dev, "invalid total number of irqs: %d\n",
-                       pchg->num_irqs);
-               return -EINVAL;
-       }
-
-
-       return 0;
-}
-
-static void lp8788_irq_unregister(struct platform_device *pdev,
-                                 struct lp8788_charger *pchg)
-{
-       int i;
-       int irq;
-
-       for (i = 0; i < pchg->num_irqs; i++) {
-               irq = pchg->irqs[i].virq;
-               if (!irq)
-                       continue;
-
-               free_irq(irq, pchg);
-       }
-}
-
-static void lp8788_setup_adc_channel(struct device *dev,
-                               struct lp8788_charger *pchg)
-{
-       struct lp8788_charger_platform_data *pdata = pchg->pdata;
-       struct iio_channel *chan;
-
-       if (!pdata)
-               return;
-
-       /* ADC channel for battery voltage */
-       chan = iio_channel_get(dev, pdata->adc_vbatt);
-       pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
-
-       /* ADC channel for battery temperature */
-       chan = iio_channel_get(dev, pdata->adc_batt_temp);
-       pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
-}
-
-static void lp8788_release_adc_channel(struct lp8788_charger *pchg)
-{
-       int i;
-
-       for (i = 0; i < LP8788_NUM_CHG_ADC; i++) {
-               if (!pchg->chan[i])
-                       continue;
-
-               iio_channel_release(pchg->chan[i]);
-               pchg->chan[i] = NULL;
-       }
-}
-
-static ssize_t lp8788_show_charger_status(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct lp8788_charger *pchg = dev_get_drvdata(dev);
-       enum lp8788_charging_state state;
-       char *desc[LP8788_MAX_CHG_STATE] = {
-               [LP8788_OFF] = "CHARGER OFF",
-               [LP8788_WARM_UP] = "WARM UP",
-               [LP8788_LOW_INPUT] = "LOW INPUT STATE",
-               [LP8788_PRECHARGE] = "CHARGING - PRECHARGE",
-               [LP8788_CC] = "CHARGING - CC",
-               [LP8788_CV] = "CHARGING - CV",
-               [LP8788_MAINTENANCE] = "NO CHARGING - MAINTENANCE",
-               [LP8788_BATTERY_FAULT] = "BATTERY FAULT",
-               [LP8788_SYSTEM_SUPPORT] = "SYSTEM SUPPORT",
-               [LP8788_HIGH_CURRENT] = "HIGH CURRENT",
-       };
-       u8 data;
-
-       lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
-       state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
-
-       return scnprintf(buf, PAGE_SIZE, "%s\n", desc[state]);
-}
-
-static ssize_t lp8788_show_eoc_time(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct lp8788_charger *pchg = dev_get_drvdata(dev);
-       char *stime[] = { "400ms", "5min", "10min", "15min",
-                       "20min", "25min", "30min" "No timeout" };
-       u8 val;
-
-       lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
-       val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S;
-
-       return scnprintf(buf, PAGE_SIZE, "End Of Charge Time: %s\n",
-                       stime[val]);
-}
-
-static ssize_t lp8788_show_eoc_level(struct device *dev,
-                               struct device_attribute *attr, char *buf)
-{
-       struct lp8788_charger *pchg = dev_get_drvdata(dev);
-       char *abs_level[] = { "25mA", "49mA", "75mA", "98mA" };
-       char *relative_level[] = { "5%", "10%", "15%", "20%" };
-       char *level;
-       u8 val;
-       u8 mode;
-
-       lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
-
-       mode = val & LP8788_CHG_EOC_MODE_M;
-       val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S;
-       level = mode ? abs_level[val] : relative_level[val];
-
-       return scnprintf(buf, PAGE_SIZE, "End Of Charge Level: %s\n", level);
-}
-
-static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL);
-static DEVICE_ATTR(eoc_time, S_IRUSR, lp8788_show_eoc_time, NULL);
-static DEVICE_ATTR(eoc_level, S_IRUSR, lp8788_show_eoc_level, NULL);
-
-static struct attribute *lp8788_charger_attr[] = {
-       &dev_attr_charger_status.attr,
-       &dev_attr_eoc_time.attr,
-       &dev_attr_eoc_level.attr,
-       NULL,
-};
-
-static const struct attribute_group lp8788_attr_group = {
-       .attrs = lp8788_charger_attr,
-};
-
-static int lp8788_charger_probe(struct platform_device *pdev)
-{
-       struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
-       struct lp8788_charger *pchg;
-       struct device *dev = &pdev->dev;
-       int ret;
-
-       pchg = devm_kzalloc(dev, sizeof(struct lp8788_charger), GFP_KERNEL);
-       if (!pchg)
-               return -ENOMEM;
-
-       pchg->lp = lp;
-       pchg->pdata = lp->pdata ? lp->pdata->chg_pdata : NULL;
-       platform_set_drvdata(pdev, pchg);
-
-       ret = lp8788_update_charger_params(pdev, pchg);
-       if (ret)
-               return ret;
-
-       lp8788_setup_adc_channel(&pdev->dev, pchg);
-
-       ret = lp8788_psy_register(pdev, pchg);
-       if (ret)
-               return ret;
-
-       ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group);
-       if (ret) {
-               lp8788_psy_unregister(pchg);
-               return ret;
-       }
-
-       ret = lp8788_irq_register(pdev, pchg);
-       if (ret)
-               dev_warn(dev, "failed to register charger irq: %d\n", ret);
-
-       return 0;
-}
-
-static int lp8788_charger_remove(struct platform_device *pdev)
-{
-       struct lp8788_charger *pchg = platform_get_drvdata(pdev);
-
-       flush_work(&pchg->charger_work);
-       lp8788_irq_unregister(pdev, pchg);
-       sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group);
-       lp8788_psy_unregister(pchg);
-       lp8788_release_adc_channel(pchg);
-
-       return 0;
-}
-
-static struct platform_driver lp8788_charger_driver = {
-       .probe = lp8788_charger_probe,
-       .remove = lp8788_charger_remove,
-       .driver = {
-               .name = LP8788_DEV_CHARGER,
-       },
-};
-module_platform_driver(lp8788_charger_driver);
-
-MODULE_DESCRIPTION("TI LP8788 Charger Driver");
-MODULE_AUTHOR("Milo Kim");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:lp8788-charger");
diff --git a/drivers/power/ltc2941-battery-gauge.c b/drivers/power/ltc2941-battery-gauge.c
deleted file mode 100644 (file)
index 4adf2ba..0000000
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * I2C client/driver for the Linear Technology LTC2941 and LTC2943
- * Battery Gas Gauge IC
- *
- * Copyright (C) 2014 Topic Embedded Systems
- *
- * Author: Auryn Verwegen
- * Author: Mike Looijmans
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/swab.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-
-#define I16_MSB(x)                     ((x >> 8) & 0xFF)
-#define I16_LSB(x)                     (x & 0xFF)
-
-#define LTC294X_WORK_DELAY             10      /* Update delay in seconds */
-
-#define LTC294X_MAX_VALUE              0xFFFF
-#define LTC294X_MID_SUPPLY             0x7FFF
-
-#define LTC2941_MAX_PRESCALER_EXP      7
-#define LTC2943_MAX_PRESCALER_EXP      6
-
-enum ltc294x_reg {
-       LTC294X_REG_STATUS              = 0x00,
-       LTC294X_REG_CONTROL             = 0x01,
-       LTC294X_REG_ACC_CHARGE_MSB      = 0x02,
-       LTC294X_REG_ACC_CHARGE_LSB      = 0x03,
-       LTC294X_REG_THRESH_HIGH_MSB     = 0x04,
-       LTC294X_REG_THRESH_HIGH_LSB     = 0x05,
-       LTC294X_REG_THRESH_LOW_MSB      = 0x06,
-       LTC294X_REG_THRESH_LOW_LSB      = 0x07,
-       LTC294X_REG_VOLTAGE_MSB = 0x08,
-       LTC294X_REG_VOLTAGE_LSB = 0x09,
-       LTC294X_REG_CURRENT_MSB = 0x0E,
-       LTC294X_REG_CURRENT_LSB = 0x0F,
-       LTC294X_REG_TEMPERATURE_MSB     = 0x14,
-       LTC294X_REG_TEMPERATURE_LSB     = 0x15,
-};
-
-#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6))
-#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)
-#define LTC294X_REG_CONTROL_PRESCALER_MASK     (BIT(5) | BIT(4) | BIT(3))
-#define LTC294X_REG_CONTROL_SHUTDOWN_MASK      (BIT(0))
-#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \
-       ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)
-#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED       0
-
-#define LTC2941_NUM_REGS       0x08
-#define LTC2943_NUM_REGS       0x18
-
-struct ltc294x_info {
-       struct i2c_client *client;      /* I2C Client pointer */
-       struct power_supply *supply;    /* Supply pointer */
-       struct power_supply_desc supply_desc;   /* Supply description */
-       struct delayed_work work;       /* Work scheduler */
-       int num_regs;   /* Number of registers (chip type) */
-       int charge;     /* Last charge register content */
-       int r_sense;    /* mOhm */
-       int Qlsb;       /* nAh */
-};
-
-static inline int convert_bin_to_uAh(
-       const struct ltc294x_info *info, int Q)
-{
-       return ((Q * (info->Qlsb / 10))) / 100;
-}
-
-static inline int convert_uAh_to_bin(
-       const struct ltc294x_info *info, int uAh)
-{
-       int Q;
-
-       Q = (uAh * 100) / (info->Qlsb/10);
-       return (Q < LTC294X_MAX_VALUE) ? Q : LTC294X_MAX_VALUE;
-}
-
-static int ltc294x_read_regs(struct i2c_client *client,
-       enum ltc294x_reg reg, u8 *buf, int num_regs)
-{
-       int ret;
-       struct i2c_msg msgs[2] = { };
-       u8 reg_start = reg;
-
-       msgs[0].addr    = client->addr;
-       msgs[0].len     = 1;
-       msgs[0].buf     = &reg_start;
-
-       msgs[1].addr    = client->addr;
-       msgs[1].len     = num_regs;
-       msgs[1].buf     = buf;
-       msgs[1].flags   = I2C_M_RD;
-
-       ret = i2c_transfer(client->adapter, &msgs[0], 2);
-       if (ret < 0) {
-               dev_err(&client->dev, "ltc2941 read_reg failed!\n");
-               return ret;
-       }
-
-       dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",
-               __func__, reg, num_regs, *buf);
-
-       return 0;
-}
-
-static int ltc294x_write_regs(struct i2c_client *client,
-       enum ltc294x_reg reg, const u8 *buf, int num_regs)
-{
-       int ret;
-       u8 reg_start = reg;
-
-       ret = i2c_smbus_write_i2c_block_data(client, reg_start, num_regs, buf);
-       if (ret < 0) {
-               dev_err(&client->dev, "ltc2941 write_reg failed!\n");
-               return ret;
-       }
-
-       dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",
-               __func__, reg, num_regs, *buf);
-
-       return 0;
-}
-
-static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)
-{
-       int ret;
-       u8 value;
-       u8 control;
-
-       /* Read status and control registers */
-       ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1);
-       if (ret < 0) {
-               dev_err(&info->client->dev,
-                       "Could not read registers from device\n");
-               goto error_exit;
-       }
-
-       control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) |
-                               LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED;
-       /* Put the 2943 into "monitor" mode, so it measures every 10 sec */
-       if (info->num_regs == LTC2943_NUM_REGS)
-               control |= LTC2943_REG_CONTROL_MODE_SCAN;
-
-       if (value != control) {
-               ret = ltc294x_write_regs(info->client,
-                       LTC294X_REG_CONTROL, &control, 1);
-               if (ret < 0) {
-                       dev_err(&info->client->dev,
-                               "Could not write register\n");
-                       goto error_exit;
-               }
-       }
-
-       return 0;
-
-error_exit:
-       return ret;
-}
-
-static int ltc294x_read_charge_register(const struct ltc294x_info *info)
-{
-       int ret;
-       u8 datar[2];
-
-       ret = ltc294x_read_regs(info->client,
-               LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2);
-       if (ret < 0)
-               return ret;
-       return (datar[0] << 8) + datar[1];
-}
-
-static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val)
-{
-       int value = ltc294x_read_charge_register(info);
-
-       if (value < 0)
-               return value;
-       /* When r_sense < 0, this counts up when the battery discharges */
-       if (info->Qlsb < 0)
-               value -= 0xFFFF;
-       *val = convert_bin_to_uAh(info, value);
-       return 0;
-}
-
-static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val)
-{
-       int ret;
-       u8 dataw[2];
-       u8 ctrl_reg;
-       s32 value;
-
-       value = convert_uAh_to_bin(info, val);
-       /* Direction depends on how sense+/- were connected */
-       if (info->Qlsb < 0)
-               value += 0xFFFF;
-       if ((value < 0) || (value > 0xFFFF)) /* input validation */
-               return -EINVAL;
-
-       /* Read control register */
-       ret = ltc294x_read_regs(info->client,
-               LTC294X_REG_CONTROL, &ctrl_reg, 1);
-       if (ret < 0)
-               return ret;
-       /* Disable analog section */
-       ctrl_reg |= LTC294X_REG_CONTROL_SHUTDOWN_MASK;
-       ret = ltc294x_write_regs(info->client,
-               LTC294X_REG_CONTROL, &ctrl_reg, 1);
-       if (ret < 0)
-               return ret;
-       /* Set new charge value */
-       dataw[0] = I16_MSB(value);
-       dataw[1] = I16_LSB(value);
-       ret = ltc294x_write_regs(info->client,
-               LTC294X_REG_ACC_CHARGE_MSB, &dataw[0], 2);
-       if (ret < 0)
-               goto error_exit;
-       /* Enable analog section */
-error_exit:
-       ctrl_reg &= ~LTC294X_REG_CONTROL_SHUTDOWN_MASK;
-       ret = ltc294x_write_regs(info->client,
-               LTC294X_REG_CONTROL, &ctrl_reg, 1);
-
-       return ret < 0 ? ret : 0;
-}
-
-static int ltc294x_get_charge_counter(
-       const struct ltc294x_info *info, int *val)
-{
-       int value = ltc294x_read_charge_register(info);
-
-       if (value < 0)
-               return value;
-       value -= LTC294X_MID_SUPPLY;
-       *val = convert_bin_to_uAh(info, value);
-       return 0;
-}
-
-static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val)
-{
-       int ret;
-       u8 datar[2];
-       u32 value;
-
-       ret = ltc294x_read_regs(info->client,
-               LTC294X_REG_VOLTAGE_MSB, &datar[0], 2);
-       value = (datar[0] << 8) | datar[1];
-       *val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */
-       return ret;
-}
-
-static int ltc294x_get_current(const struct ltc294x_info *info, int *val)
-{
-       int ret;
-       u8 datar[2];
-       s32 value;
-
-       ret = ltc294x_read_regs(info->client,
-               LTC294X_REG_CURRENT_MSB, &datar[0], 2);
-       value = (datar[0] << 8) | datar[1];
-       value -= 0x7FFF;
-       /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm,
-        * the formula below keeps everything in s32 range while preserving
-        * enough digits */
-       *val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */
-       return ret;
-}
-
-static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
-{
-       int ret;
-       u8 datar[2];
-       u32 value;
-
-       ret = ltc294x_read_regs(info->client,
-               LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2);
-       value = (datar[0] << 8) | datar[1];
-       /* Full-scale is 510 Kelvin, convert to centidegrees  */
-       *val = (((51000 * value) / 0xFFFF) - 27215);
-       return ret;
-}
-
-static int ltc294x_get_property(struct power_supply *psy,
-                               enum power_supply_property prop,
-                               union power_supply_propval *val)
-{
-       struct ltc294x_info *info = power_supply_get_drvdata(psy);
-
-       switch (prop) {
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               return ltc294x_get_charge_now(info, &val->intval);
-       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
-               return ltc294x_get_charge_counter(info, &val->intval);
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               return ltc294x_get_voltage(info, &val->intval);
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               return ltc294x_get_current(info, &val->intval);
-       case POWER_SUPPLY_PROP_TEMP:
-               return ltc294x_get_temperature(info, &val->intval);
-       default:
-               return -EINVAL;
-       }
-}
-
-static int ltc294x_set_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       const union power_supply_propval *val)
-{
-       struct ltc294x_info *info = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               return ltc294x_set_charge_now(info, val->intval);
-       default:
-               return -EPERM;
-       }
-}
-
-static int ltc294x_property_is_writeable(
-       struct power_supply *psy, enum power_supply_property psp)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               return 1;
-       default:
-               return 0;
-       }
-}
-
-static void ltc294x_update(struct ltc294x_info *info)
-{
-       int charge = ltc294x_read_charge_register(info);
-
-       if (charge != info->charge) {
-               info->charge = charge;
-               power_supply_changed(info->supply);
-       }
-}
-
-static void ltc294x_work(struct work_struct *work)
-{
-       struct ltc294x_info *info;
-
-       info = container_of(work, struct ltc294x_info, work.work);
-       ltc294x_update(info);
-       schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
-}
-
-static enum power_supply_property ltc294x_properties[] = {
-       POWER_SUPPLY_PROP_CHARGE_COUNTER,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-};
-
-static int ltc294x_i2c_remove(struct i2c_client *client)
-{
-       struct ltc294x_info *info = i2c_get_clientdata(client);
-
-       cancel_delayed_work(&info->work);
-       power_supply_unregister(info->supply);
-       return 0;
-}
-
-static int ltc294x_i2c_probe(struct i2c_client *client,
-       const struct i2c_device_id *id)
-{
-       struct power_supply_config psy_cfg = {};
-       struct ltc294x_info *info;
-       int ret;
-       u32 prescaler_exp;
-       s32 r_sense;
-       struct device_node *np;
-
-       info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
-       if (info == NULL)
-               return -ENOMEM;
-
-       i2c_set_clientdata(client, info);
-
-       np = of_node_get(client->dev.of_node);
-
-       info->num_regs = id->driver_data;
-       info->supply_desc.name = np->name;
-
-       /* r_sense can be negative, when sense+ is connected to the battery
-        * instead of the sense-. This results in reversed measurements. */
-       ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);
-       if (ret < 0) {
-               dev_err(&client->dev,
-                       "Could not find lltc,resistor-sense in devicetree\n");
-               return ret;
-       }
-       info->r_sense = r_sense;
-
-       ret = of_property_read_u32(np, "lltc,prescaler-exponent",
-               &prescaler_exp);
-       if (ret < 0) {
-               dev_warn(&client->dev,
-                       "lltc,prescaler-exponent not in devicetree\n");
-               prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
-       }
-
-       if (info->num_regs == LTC2943_NUM_REGS) {
-               if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)
-                       prescaler_exp = LTC2943_MAX_PRESCALER_EXP;
-               info->Qlsb = ((340 * 50000) / r_sense) /
-                               (4096 / (1 << (2*prescaler_exp)));
-       } else {
-               if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP)
-                       prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
-               info->Qlsb = ((85 * 50000) / r_sense) /
-                               (128 / (1 << prescaler_exp));
-       }
-
-       info->client = client;
-       info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY;
-       info->supply_desc.properties = ltc294x_properties;
-       if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
-               info->supply_desc.num_properties =
-                       ARRAY_SIZE(ltc294x_properties);
-       else if (info->num_regs >= LTC294X_REG_CURRENT_LSB)
-               info->supply_desc.num_properties =
-                       ARRAY_SIZE(ltc294x_properties) - 1;
-       else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB)
-               info->supply_desc.num_properties =
-                       ARRAY_SIZE(ltc294x_properties) - 2;
-       else
-               info->supply_desc.num_properties =
-                       ARRAY_SIZE(ltc294x_properties) - 3;
-       info->supply_desc.get_property = ltc294x_get_property;
-       info->supply_desc.set_property = ltc294x_set_property;
-       info->supply_desc.property_is_writeable = ltc294x_property_is_writeable;
-       info->supply_desc.external_power_changed        = NULL;
-
-       psy_cfg.drv_data = info;
-
-       INIT_DELAYED_WORK(&info->work, ltc294x_work);
-
-       ret = ltc294x_reset(info, prescaler_exp);
-       if (ret < 0) {
-               dev_err(&client->dev, "Communication with chip failed\n");
-               return ret;
-       }
-
-       info->supply = power_supply_register(&client->dev, &info->supply_desc,
-                                            &psy_cfg);
-       if (IS_ERR(info->supply)) {
-               dev_err(&client->dev, "failed to register ltc2941\n");
-               return PTR_ERR(info->supply);
-       } else {
-               schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int ltc294x_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct ltc294x_info *info = i2c_get_clientdata(client);
-
-       cancel_delayed_work(&info->work);
-       return 0;
-}
-
-static int ltc294x_resume(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct ltc294x_info *info = i2c_get_clientdata(client);
-
-       schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
-       return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume);
-#define LTC294X_PM_OPS (&ltc294x_pm_ops)
-
-#else
-#define LTC294X_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
-
-
-static const struct i2c_device_id ltc294x_i2c_id[] = {
-       {"ltc2941", LTC2941_NUM_REGS},
-       {"ltc2943", LTC2943_NUM_REGS},
-       { },
-};
-MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);
-
-static struct i2c_driver ltc294x_driver = {
-       .driver = {
-               .name   = "LTC2941",
-               .pm     = LTC294X_PM_OPS,
-       },
-       .probe          = ltc294x_i2c_probe,
-       .remove         = ltc294x_i2c_remove,
-       .id_table       = ltc294x_i2c_id,
-};
-module_i2c_driver(ltc294x_driver);
-
-MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems");
-MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products");
-MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/max14577_charger.c b/drivers/power/max14577_charger.c
deleted file mode 100644 (file)
index a36bcaf..0000000
+++ /dev/null
@@ -1,648 +0,0 @@
-/*
- * max14577_charger.c - Battery charger driver for the Maxim 14577/77836
- *
- * Copyright (C) 2013,2014 Samsung Electronics
- * Krzysztof Kozlowski <k.kozlowski@samsung.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/max14577-private.h>
-#include <linux/mfd/max14577.h>
-
-struct max14577_charger {
-       struct device           *dev;
-       struct max14577         *max14577;
-       struct power_supply     *charger;
-
-       struct max14577_charger_platform_data   *pdata;
-};
-
-/*
- * Helper function for mapping values of STATUS2/CHGTYP register on max14577
- * and max77836 chipsets to enum maxim_muic_charger_type.
- */
-static enum max14577_muic_charger_type maxim_get_charger_type(
-               enum maxim_device_type dev_type, u8 val) {
-       switch (val) {
-       case MAX14577_CHARGER_TYPE_NONE:
-       case MAX14577_CHARGER_TYPE_USB:
-       case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
-       case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
-       case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
-       case MAX14577_CHARGER_TYPE_SPECIAL_1A:
-               return val;
-       case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
-       case MAX14577_CHARGER_TYPE_RESERVED:
-               if (dev_type == MAXIM_DEVICE_TYPE_MAX77836)
-                       val |= 0x8;
-               return val;
-       default:
-               WARN_ONCE(1, "max14577: Unsupported chgtyp register value 0x%02x", val);
-               return val;
-       }
-}
-
-static int max14577_get_charger_state(struct max14577_charger *chg, int *val)
-{
-       struct regmap *rmap = chg->max14577->regmap;
-       int ret;
-       u8 reg_data;
-
-       /*
-        * Charging occurs only if:
-        *  - CHGCTRL2/MBCHOSTEN == 1
-        *  - STATUS2/CGMBC == 1
-        *
-        * TODO:
-        *  - handle FULL after Top-off timer (EOC register may be off
-        *    and the charger won't be charging although MBCHOSTEN is on)
-        *  - handle properly dead-battery charging (respect timer)
-        *  - handle timers (fast-charge and prequal) /MBCCHGERR/
-        */
-       ret = max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL2, &reg_data);
-       if (ret < 0)
-               goto out;
-
-       if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0) {
-               *val = POWER_SUPPLY_STATUS_DISCHARGING;
-               goto out;
-       }
-
-       ret = max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, &reg_data);
-       if (ret < 0)
-               goto out;
-
-       if (reg_data & STATUS3_CGMBC_MASK) {
-               /* Charger or USB-cable is connected */
-               if (reg_data & STATUS3_EOC_MASK)
-                       *val = POWER_SUPPLY_STATUS_FULL;
-               else
-                       *val = POWER_SUPPLY_STATUS_CHARGING;
-               goto out;
-       }
-
-       *val = POWER_SUPPLY_STATUS_DISCHARGING;
-
-out:
-       return ret;
-}
-
-/*
- * Supported charge types:
- *  - POWER_SUPPLY_CHARGE_TYPE_NONE
- *  - POWER_SUPPLY_CHARGE_TYPE_FAST
- */
-static int max14577_get_charge_type(struct max14577_charger *chg, int *val)
-{
-       int ret, charging;
-
-       /*
-        * TODO: CHARGE_TYPE_TRICKLE (VCHGR_RC or EOC)?
-        * As spec says:
-        * [after reaching EOC interrupt]
-        * "When the battery is fully charged, the 30-minute (typ)
-        *  top-off timer starts. The device continues to trickle
-        *  charge the battery until the top-off timer runs out."
-        */
-       ret = max14577_get_charger_state(chg, &charging);
-       if (ret < 0)
-               return ret;
-
-       if (charging == POWER_SUPPLY_STATUS_CHARGING)
-               *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
-       else
-               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
-
-       return 0;
-}
-
-static int max14577_get_online(struct max14577_charger *chg, int *val)
-{
-       struct regmap *rmap = chg->max14577->regmap;
-       u8 reg_data;
-       int ret;
-       enum max14577_muic_charger_type chg_type;
-
-       ret = max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, &reg_data);
-       if (ret < 0)
-               return ret;
-
-       reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
-       chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
-       switch (chg_type) {
-       case MAX14577_CHARGER_TYPE_USB:
-       case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
-       case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
-       case MAX14577_CHARGER_TYPE_SPECIAL_1A:
-       case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
-       case MAX77836_CHARGER_TYPE_SPECIAL_BIAS:
-               *val = 1;
-               break;
-       case MAX14577_CHARGER_TYPE_NONE:
-       case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
-       case MAX14577_CHARGER_TYPE_RESERVED:
-       case MAX77836_CHARGER_TYPE_RESERVED:
-       default:
-               *val = 0;
-       }
-
-       return 0;
-}
-
-/*
- * Supported health statuses:
- *  - POWER_SUPPLY_HEALTH_DEAD
- *  - POWER_SUPPLY_HEALTH_OVERVOLTAGE
- *  - POWER_SUPPLY_HEALTH_GOOD
- */
-static int max14577_get_battery_health(struct max14577_charger *chg, int *val)
-{
-       struct regmap *rmap = chg->max14577->regmap;
-       int ret;
-       u8 reg_data;
-       enum max14577_muic_charger_type chg_type;
-
-       ret = max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, &reg_data);
-       if (ret < 0)
-               goto out;
-
-       reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
-       chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
-       if (chg_type == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
-               *val = POWER_SUPPLY_HEALTH_DEAD;
-               goto out;
-       }
-
-       ret = max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, &reg_data);
-       if (ret < 0)
-               goto out;
-
-       if (reg_data & STATUS3_OVP_MASK) {
-               *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               goto out;
-       }
-
-       /* Not dead, not overvoltage */
-       *val = POWER_SUPPLY_HEALTH_GOOD;
-
-out:
-       return ret;
-}
-
-/*
- * Always returns 1.
- * The max14577 chip doesn't report any status of battery presence.
- * Lets assume that it will always be used with some battery.
- */
-static int max14577_get_present(struct max14577_charger *chg, int *val)
-{
-       *val = 1;
-
-       return 0;
-}
-
-static int max14577_set_fast_charge_timer(struct max14577_charger *chg,
-               unsigned long hours)
-{
-       u8 reg_data;
-
-       switch (hours) {
-       case 5 ... 7:
-               reg_data = hours - 3;
-               break;
-       case 0:
-               /* Disable */
-               reg_data = 0x7;
-               break;
-       default:
-               dev_err(chg->dev, "Wrong value for Fast-Charge Timer: %lu\n",
-                               hours);
-               return -EINVAL;
-       }
-       reg_data <<= CHGCTRL1_TCHW_SHIFT;
-
-       return max14577_update_reg(chg->max14577->regmap,
-                       MAX14577_REG_CHGCTRL1, CHGCTRL1_TCHW_MASK, reg_data);
-}
-
-static int max14577_init_constant_voltage(struct max14577_charger *chg,
-               unsigned int uvolt)
-{
-       u8 reg_data;
-
-       if (uvolt < MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN ||
-                       uvolt > MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
-               return -EINVAL;
-
-       if (uvolt == 4200000)
-               reg_data = 0x0;
-       else if (uvolt == MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
-               reg_data = 0x1f;
-       else if (uvolt <= 4280000) {
-               unsigned int val = uvolt;
-
-               val -= MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN;
-               val /= MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP;
-               if (uvolt <= 4180000)
-                       reg_data = 0x1 + val;
-               else
-                       reg_data = val; /* Fix for gap between 4.18V and 4.22V */
-       } else
-               return -EINVAL;
-
-       reg_data <<= CHGCTRL3_MBCCVWRC_SHIFT;
-
-       return max14577_write_reg(chg->max14577->regmap,
-                       MAX14577_CHG_REG_CHG_CTRL3, reg_data);
-}
-
-static int max14577_init_eoc(struct max14577_charger *chg,
-               unsigned int uamp)
-{
-       unsigned int current_bits = 0xf;
-       u8 reg_data;
-
-       switch (chg->max14577->dev_type) {
-       case MAXIM_DEVICE_TYPE_MAX77836:
-               if (uamp < 5000)
-                       return -EINVAL; /* Requested current is too low */
-
-               if (uamp >= 7500 && uamp < 10000)
-                       current_bits = 0x0;
-               else if (uamp <= 50000) {
-                       /* <5000, 7499> and <10000, 50000> */
-                       current_bits = uamp / 5000;
-               } else {
-                       uamp = min(uamp, 100000U) - 50000U;
-                       current_bits = 0xa + uamp / 10000;
-               }
-               break;
-
-       case MAXIM_DEVICE_TYPE_MAX14577:
-       default:
-               if (uamp < MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN)
-                       return -EINVAL; /* Requested current is too low */
-
-               uamp = min(uamp, MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
-               uamp -= MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN;
-               current_bits = uamp / MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP;
-               break;
-       }
-
-       reg_data = current_bits << CHGCTRL5_EOCS_SHIFT;
-
-       return max14577_update_reg(chg->max14577->regmap,
-                       MAX14577_CHG_REG_CHG_CTRL5, CHGCTRL5_EOCS_MASK,
-                       reg_data);
-}
-
-static int max14577_init_fast_charge(struct max14577_charger *chg,
-               unsigned int uamp)
-{
-       u8 reg_data;
-       int ret;
-       const struct maxim_charger_current *limits =
-               &maxim_charger_currents[chg->max14577->dev_type];
-
-       ret = maxim_charger_calc_reg_current(limits, uamp, uamp, &reg_data);
-       if (ret) {
-               dev_err(chg->dev, "Wrong value for fast charge: %u\n", uamp);
-               return ret;
-       }
-
-       return max14577_update_reg(chg->max14577->regmap,
-                       MAX14577_CHG_REG_CHG_CTRL4,
-                       CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
-                       reg_data);
-}
-
-/*
- * Sets charger registers to proper and safe default values.
- * Some of these values are equal to defaults in MAX14577E
- * data sheet but there are minor differences.
- */
-static int max14577_charger_reg_init(struct max14577_charger *chg)
-{
-       struct regmap *rmap = chg->max14577->regmap;
-       u8 reg_data;
-       int ret;
-
-       /*
-        * Charger-Type Manual Detection, default off (set CHGTYPMAN to 0)
-        * Charger-Detection Enable, default on (set CHGDETEN to 1)
-        * Combined mask of CHGDETEN and CHGTYPMAN will zero the CHGTYPMAN bit
-        */
-       reg_data = 0x1 << CDETCTRL1_CHGDETEN_SHIFT;
-       max14577_update_reg(rmap, MAX14577_REG_CDETCTRL1,
-                       CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK,
-                       reg_data);
-
-       /*
-        * Wall-Adapter Rapid Charge, default on
-        * Battery-Charger, default on
-        */
-       reg_data = 0x1 << CHGCTRL2_VCHGR_RC_SHIFT;
-       reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT;
-       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data);
-
-       /* Auto Charging Stop, default off */
-       reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT;
-       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data);
-
-       ret = max14577_init_constant_voltage(chg, chg->pdata->constant_uvolt);
-       if (ret)
-               return ret;
-
-       ret = max14577_init_eoc(chg, chg->pdata->eoc_uamp);
-       if (ret)
-               return ret;
-
-       ret = max14577_init_fast_charge(chg, chg->pdata->fast_charge_uamp);
-       if (ret)
-               return ret;
-
-       ret = max14577_set_fast_charge_timer(chg,
-                       MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT);
-       if (ret)
-               return ret;
-
-       /* Initialize Overvoltage-Protection Threshold */
-       switch (chg->pdata->ovp_uvolt) {
-       case 7500000:
-               reg_data = 0x0;
-               break;
-       case 6000000:
-       case 6500000:
-       case 7000000:
-               reg_data = 0x1 + (chg->pdata->ovp_uvolt - 6000000) / 500000;
-               break;
-       default:
-               dev_err(chg->dev, "Wrong value for OVP: %u\n",
-                               chg->pdata->ovp_uvolt);
-               return -EINVAL;
-       }
-       reg_data <<= CHGCTRL7_OTPCGHCVS_SHIFT;
-       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data);
-
-       return 0;
-}
-
-/* Support property from charger */
-static enum power_supply_property max14577_charger_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static const char * const model_names[] = {
-       [MAXIM_DEVICE_TYPE_UNKNOWN]     = "MAX14577-like",
-       [MAXIM_DEVICE_TYPE_MAX14577]    = "MAX14577",
-       [MAXIM_DEVICE_TYPE_MAX77836]    = "MAX77836",
-};
-static const char *manufacturer = "Maxim Integrated";
-
-static int max14577_charger_get_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       struct max14577_charger *chg = power_supply_get_drvdata(psy);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = max14577_get_charger_state(chg, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               ret = max14577_get_charge_type(chg, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = max14577_get_battery_health(chg, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               ret = max14577_get_present(chg, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = max14577_get_online(chg, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               BUILD_BUG_ON(ARRAY_SIZE(model_names) != MAXIM_DEVICE_TYPE_NUM);
-               val->strval = model_names[chg->max14577->dev_type];
-               break;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = manufacturer;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return ret;
-}
-
-static const struct power_supply_desc max14577_charger_desc = {
-       .name = "max14577-charger",
-       .type = POWER_SUPPLY_TYPE_BATTERY,
-       .properties = max14577_charger_props,
-       .num_properties = ARRAY_SIZE(max14577_charger_props),
-       .get_property = max14577_charger_get_property,
-};
-
-#ifdef CONFIG_OF
-static struct max14577_charger_platform_data *max14577_charger_dt_init(
-               struct platform_device *pdev)
-{
-       struct max14577_charger_platform_data *pdata;
-       struct device_node *np = pdev->dev.of_node;
-       int ret;
-
-       if (!np) {
-               dev_err(&pdev->dev, "No charger OF node\n");
-               return ERR_PTR(-EINVAL);
-       }
-
-       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return ERR_PTR(-ENOMEM);
-
-       ret = of_property_read_u32(np, "maxim,constant-uvolt",
-                       &pdata->constant_uvolt);
-       if (ret) {
-               dev_err(&pdev->dev, "Cannot parse maxim,constant-uvolt field from DT\n");
-               return ERR_PTR(ret);
-       }
-
-       ret = of_property_read_u32(np, "maxim,fast-charge-uamp",
-                       &pdata->fast_charge_uamp);
-       if (ret) {
-               dev_err(&pdev->dev, "Cannot parse maxim,fast-charge-uamp field from DT\n");
-               return ERR_PTR(ret);
-       }
-
-       ret = of_property_read_u32(np, "maxim,eoc-uamp", &pdata->eoc_uamp);
-       if (ret) {
-               dev_err(&pdev->dev, "Cannot parse maxim,eoc-uamp field from DT\n");
-               return ERR_PTR(ret);
-       }
-
-       ret = of_property_read_u32(np, "maxim,ovp-uvolt", &pdata->ovp_uvolt);
-       if (ret) {
-               dev_err(&pdev->dev, "Cannot parse maxim,ovp-uvolt field from DT\n");
-               return ERR_PTR(ret);
-       }
-
-       return pdata;
-}
-#else /* CONFIG_OF */
-static struct max14577_charger_platform_data *max14577_charger_dt_init(
-               struct platform_device *pdev)
-{
-       return NULL;
-}
-#endif /* CONFIG_OF */
-
-static ssize_t show_fast_charge_timer(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct max14577_charger *chg = dev_get_drvdata(dev);
-       u8 reg_data;
-       int ret;
-       unsigned int val;
-
-       ret = max14577_read_reg(chg->max14577->regmap, MAX14577_REG_CHGCTRL1,
-                       &reg_data);
-       if (ret)
-               return ret;
-
-       reg_data &= CHGCTRL1_TCHW_MASK;
-       reg_data >>= CHGCTRL1_TCHW_SHIFT;
-       switch (reg_data) {
-       case 0x2 ... 0x4:
-               val = reg_data + 3;
-               break;
-       case 0x7:
-               val = 0;
-               break;
-       default:
-               val = 5;
-               break;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
-}
-
-static ssize_t store_fast_charge_timer(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct max14577_charger *chg = dev_get_drvdata(dev);
-       unsigned long val;
-       int ret;
-
-       ret = kstrtoul(buf, 10, &val);
-       if (ret)
-               return ret;
-
-       ret = max14577_set_fast_charge_timer(chg, val);
-       if (ret)
-               return ret;
-
-       return count;
-}
-
-static DEVICE_ATTR(fast_charge_timer, S_IRUGO | S_IWUSR,
-               show_fast_charge_timer, store_fast_charge_timer);
-
-static int max14577_charger_probe(struct platform_device *pdev)
-{
-       struct max14577_charger *chg;
-       struct power_supply_config psy_cfg = {};
-       struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent);
-       int ret;
-
-       chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
-       if (!chg)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, chg);
-       chg->dev = &pdev->dev;
-       chg->max14577 = max14577;
-
-       chg->pdata = max14577_charger_dt_init(pdev);
-       if (IS_ERR_OR_NULL(chg->pdata))
-               return PTR_ERR(chg->pdata);
-
-       ret = max14577_charger_reg_init(chg);
-       if (ret)
-               return ret;
-
-       ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
-       if (ret) {
-               dev_err(&pdev->dev, "failed: create sysfs entry\n");
-               return ret;
-       }
-
-       psy_cfg.drv_data = chg;
-       chg->charger = power_supply_register(&pdev->dev, &max14577_charger_desc,
-                                               &psy_cfg);
-       if (IS_ERR(chg->charger)) {
-               dev_err(&pdev->dev, "failed: power supply register\n");
-               ret = PTR_ERR(chg->charger);
-               goto err;
-       }
-
-       /* Check for valid values for charger */
-       BUILD_BUG_ON(MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN +
-                       MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP * 0xf !=
-                       MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
-       return 0;
-
-err:
-       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
-
-       return ret;
-}
-
-static int max14577_charger_remove(struct platform_device *pdev)
-{
-       struct max14577_charger *chg = platform_get_drvdata(pdev);
-
-       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
-       power_supply_unregister(chg->charger);
-
-       return 0;
-}
-
-static const struct platform_device_id max14577_charger_id[] = {
-       { "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, },
-       { "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, },
-       { }
-};
-MODULE_DEVICE_TABLE(platform, max14577_charger_id);
-
-static struct platform_driver max14577_charger_driver = {
-       .driver = {
-               .name   = "max14577-charger",
-       },
-       .probe          = max14577_charger_probe,
-       .remove         = max14577_charger_remove,
-       .id_table       = max14577_charger_id,
-};
-module_platform_driver(max14577_charger_driver);
-
-MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
-MODULE_DESCRIPTION("Maxim 14577/77836 charger driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c
deleted file mode 100644 (file)
index 8689c80..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- *  max17040_battery.c
- *  fuel-gauge systems for lithium-ion (Li+) batteries
- *
- *  Copyright (C) 2009 Samsung Electronics
- *  Minkyu Kang <mk7.kang@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/power_supply.h>
-#include <linux/max17040_battery.h>
-#include <linux/slab.h>
-
-#define MAX17040_VCELL_MSB     0x02
-#define MAX17040_VCELL_LSB     0x03
-#define MAX17040_SOC_MSB       0x04
-#define MAX17040_SOC_LSB       0x05
-#define MAX17040_MODE_MSB      0x06
-#define MAX17040_MODE_LSB      0x07
-#define MAX17040_VER_MSB       0x08
-#define MAX17040_VER_LSB       0x09
-#define MAX17040_RCOMP_MSB     0x0C
-#define MAX17040_RCOMP_LSB     0x0D
-#define MAX17040_CMD_MSB       0xFE
-#define MAX17040_CMD_LSB       0xFF
-
-#define MAX17040_DELAY         1000
-#define MAX17040_BATTERY_FULL  95
-
-struct max17040_chip {
-       struct i2c_client               *client;
-       struct delayed_work             work;
-       struct power_supply             *battery;
-       struct max17040_platform_data   *pdata;
-
-       /* State Of Connect */
-       int online;
-       /* battery voltage */
-       int vcell;
-       /* battery capacity */
-       int soc;
-       /* State Of Charge */
-       int status;
-};
-
-static int max17040_get_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       struct max17040_chip *chip = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = chip->status;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = chip->online;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = chip->vcell;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               val->intval = chip->soc;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int max17040_write_reg(struct i2c_client *client, int reg, u8 value)
-{
-       int ret;
-
-       ret = i2c_smbus_write_byte_data(client, reg, value);
-
-       if (ret < 0)
-               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-
-       return ret;
-}
-
-static int max17040_read_reg(struct i2c_client *client, int reg)
-{
-       int ret;
-
-       ret = i2c_smbus_read_byte_data(client, reg);
-
-       if (ret < 0)
-               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
-
-       return ret;
-}
-
-static void max17040_reset(struct i2c_client *client)
-{
-       max17040_write_reg(client, MAX17040_CMD_MSB, 0x54);
-       max17040_write_reg(client, MAX17040_CMD_LSB, 0x00);
-}
-
-static void max17040_get_vcell(struct i2c_client *client)
-{
-       struct max17040_chip *chip = i2c_get_clientdata(client);
-       u8 msb;
-       u8 lsb;
-
-       msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
-       lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
-
-       chip->vcell = (msb << 4) + (lsb >> 4);
-}
-
-static void max17040_get_soc(struct i2c_client *client)
-{
-       struct max17040_chip *chip = i2c_get_clientdata(client);
-       u8 msb;
-       u8 lsb;
-
-       msb = max17040_read_reg(client, MAX17040_SOC_MSB);
-       lsb = max17040_read_reg(client, MAX17040_SOC_LSB);
-
-       chip->soc = msb;
-}
-
-static void max17040_get_version(struct i2c_client *client)
-{
-       u8 msb;
-       u8 lsb;
-
-       msb = max17040_read_reg(client, MAX17040_VER_MSB);
-       lsb = max17040_read_reg(client, MAX17040_VER_LSB);
-
-       dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver %d%d\n", msb, lsb);
-}
-
-static void max17040_get_online(struct i2c_client *client)
-{
-       struct max17040_chip *chip = i2c_get_clientdata(client);
-
-       if (chip->pdata && chip->pdata->battery_online)
-               chip->online = chip->pdata->battery_online();
-       else
-               chip->online = 1;
-}
-
-static void max17040_get_status(struct i2c_client *client)
-{
-       struct max17040_chip *chip = i2c_get_clientdata(client);
-
-       if (!chip->pdata || !chip->pdata->charger_online
-                       || !chip->pdata->charger_enable) {
-               chip->status = POWER_SUPPLY_STATUS_UNKNOWN;
-               return;
-       }
-
-       if (chip->pdata->charger_online()) {
-               if (chip->pdata->charger_enable())
-                       chip->status = POWER_SUPPLY_STATUS_CHARGING;
-               else
-                       chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       } else {
-               chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
-       }
-
-       if (chip->soc > MAX17040_BATTERY_FULL)
-               chip->status = POWER_SUPPLY_STATUS_FULL;
-}
-
-static void max17040_work(struct work_struct *work)
-{
-       struct max17040_chip *chip;
-
-       chip = container_of(work, struct max17040_chip, work.work);
-
-       max17040_get_vcell(chip->client);
-       max17040_get_soc(chip->client);
-       max17040_get_online(chip->client);
-       max17040_get_status(chip->client);
-
-       queue_delayed_work(system_power_efficient_wq, &chip->work,
-                          MAX17040_DELAY);
-}
-
-static enum power_supply_property max17040_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-};
-
-static const struct power_supply_desc max17040_battery_desc = {
-       .name           = "battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property   = max17040_get_property,
-       .properties     = max17040_battery_props,
-       .num_properties = ARRAY_SIZE(max17040_battery_props),
-};
-
-static int max17040_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
-{
-       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       struct power_supply_config psy_cfg = {};
-       struct max17040_chip *chip;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
-               return -EIO;
-
-       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
-       if (!chip)
-               return -ENOMEM;
-
-       chip->client = client;
-       chip->pdata = client->dev.platform_data;
-
-       i2c_set_clientdata(client, chip);
-       psy_cfg.drv_data = chip;
-
-       chip->battery = power_supply_register(&client->dev,
-                               &max17040_battery_desc, &psy_cfg);
-       if (IS_ERR(chip->battery)) {
-               dev_err(&client->dev, "failed: power supply register\n");
-               return PTR_ERR(chip->battery);
-       }
-
-       max17040_reset(client);
-       max17040_get_version(client);
-
-       INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
-       queue_delayed_work(system_power_efficient_wq, &chip->work,
-                          MAX17040_DELAY);
-
-       return 0;
-}
-
-static int max17040_remove(struct i2c_client *client)
-{
-       struct max17040_chip *chip = i2c_get_clientdata(client);
-
-       power_supply_unregister(chip->battery);
-       cancel_delayed_work(&chip->work);
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int max17040_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct max17040_chip *chip = i2c_get_clientdata(client);
-
-       cancel_delayed_work(&chip->work);
-       return 0;
-}
-
-static int max17040_resume(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct max17040_chip *chip = i2c_get_clientdata(client);
-
-       queue_delayed_work(system_power_efficient_wq, &chip->work,
-                          MAX17040_DELAY);
-       return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
-#define MAX17040_PM_OPS (&max17040_pm_ops)
-
-#else
-
-#define MAX17040_PM_OPS NULL
-
-#endif /* CONFIG_PM_SLEEP */
-
-static const struct i2c_device_id max17040_id[] = {
-       { "max17040" },
-       { "max77836-battery" },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, max17040_id);
-
-static struct i2c_driver max17040_i2c_driver = {
-       .driver = {
-               .name   = "max17040",
-               .pm     = MAX17040_PM_OPS,
-       },
-       .probe          = max17040_probe,
-       .remove         = max17040_remove,
-       .id_table       = max17040_id,
-};
-module_i2c_driver(max17040_i2c_driver);
-
-MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
-MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
deleted file mode 100644 (file)
index 9c65f13..0000000
+++ /dev/null
@@ -1,1016 +0,0 @@
-/*
- * Fuel gauge driver for Maxim 17042 / 8966 / 8997
- *  Note that Maxim 8966 and 8997 are mfd and this is its subdevice.
- *
- * Copyright (C) 2011 Samsung Electronics
- * MyungJoo Ham <myungjoo.ham@samsung.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * This driver is based on max17040_battery.c
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/pm.h>
-#include <linux/mod_devicetable.h>
-#include <linux/power_supply.h>
-#include <linux/power/max17042_battery.h>
-#include <linux/of.h>
-#include <linux/regmap.h>
-
-/* Status register bits */
-#define STATUS_POR_BIT         (1 << 1)
-#define STATUS_BST_BIT         (1 << 3)
-#define STATUS_VMN_BIT         (1 << 8)
-#define STATUS_TMN_BIT         (1 << 9)
-#define STATUS_SMN_BIT         (1 << 10)
-#define STATUS_BI_BIT          (1 << 11)
-#define STATUS_VMX_BIT         (1 << 12)
-#define STATUS_TMX_BIT         (1 << 13)
-#define STATUS_SMX_BIT         (1 << 14)
-#define STATUS_BR_BIT          (1 << 15)
-
-/* Interrupt mask bits */
-#define CONFIG_ALRT_BIT_ENBL   (1 << 2)
-#define STATUS_INTR_SOCMIN_BIT (1 << 10)
-#define STATUS_INTR_SOCMAX_BIT (1 << 14)
-
-#define VFSOC0_LOCK            0x0000
-#define VFSOC0_UNLOCK          0x0080
-#define MODEL_UNLOCK1  0X0059
-#define MODEL_UNLOCK2  0X00C4
-#define MODEL_LOCK1            0X0000
-#define MODEL_LOCK2            0X0000
-
-#define dQ_ACC_DIV     0x4
-#define dP_ACC_100     0x1900
-#define dP_ACC_200     0x3200
-
-#define MAX17042_VMAX_TOLERANCE                50 /* 50 mV */
-
-struct max17042_chip {
-       struct i2c_client *client;
-       struct regmap *regmap;
-       struct power_supply *battery;
-       enum max170xx_chip_type chip_type;
-       struct max17042_platform_data *pdata;
-       struct work_struct work;
-       int    init_complete;
-};
-
-static enum power_supply_property max17042_battery_props[] = {
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_VOLTAGE_OCV,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_COUNTER,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
-       POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
-       POWER_SUPPLY_PROP_TEMP_MIN,
-       POWER_SUPPLY_PROP_TEMP_MAX,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-};
-
-static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
-{
-       int ret;
-       u32 data;
-       struct regmap *map = chip->regmap;
-
-       ret = regmap_read(map, MAX17042_TEMP, &data);
-       if (ret < 0)
-               return ret;
-
-       *temp = data;
-       /* The value is signed. */
-       if (*temp & 0x8000) {
-               *temp = (0x7fff & ~*temp) + 1;
-               *temp *= -1;
-       }
-
-       /* The value is converted into deci-centigrade scale */
-       /* Units of LSB = 1 / 256 degree Celsius */
-       *temp = *temp * 10 / 256;
-       return 0;
-}
-
-static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
-{
-       int temp, vavg, vbatt, ret;
-       u32 val;
-
-       ret = regmap_read(chip->regmap, MAX17042_AvgVCELL, &val);
-       if (ret < 0)
-               goto health_error;
-
-       /* bits [0-3] unused */
-       vavg = val * 625 / 8;
-       /* Convert to millivolts */
-       vavg /= 1000;
-
-       ret = regmap_read(chip->regmap, MAX17042_VCELL, &val);
-       if (ret < 0)
-               goto health_error;
-
-       /* bits [0-3] unused */
-       vbatt = val * 625 / 8;
-       /* Convert to millivolts */
-       vbatt /= 1000;
-
-       if (vavg < chip->pdata->vmin) {
-               *health = POWER_SUPPLY_HEALTH_DEAD;
-               goto out;
-       }
-
-       if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) {
-               *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               goto out;
-       }
-
-       ret = max17042_get_temperature(chip, &temp);
-       if (ret < 0)
-               goto health_error;
-
-       if (temp <= chip->pdata->temp_min) {
-               *health = POWER_SUPPLY_HEALTH_COLD;
-               goto out;
-       }
-
-       if (temp >= chip->pdata->temp_max) {
-               *health = POWER_SUPPLY_HEALTH_OVERHEAT;
-               goto out;
-       }
-
-       *health = POWER_SUPPLY_HEALTH_GOOD;
-
-out:
-       return 0;
-
-health_error:
-       return ret;
-}
-
-static int max17042_get_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       struct max17042_chip *chip = power_supply_get_drvdata(psy);
-       struct regmap *map = chip->regmap;
-       int ret;
-       u32 data;
-
-       if (!chip->init_complete)
-               return -EAGAIN;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-               ret = regmap_read(map, MAX17042_STATUS, &data);
-               if (ret < 0)
-                       return ret;
-
-               if (data & MAX17042_STATUS_BattAbsent)
-                       val->intval = 0;
-               else
-                       val->intval = 1;
-               break;
-       case POWER_SUPPLY_PROP_CYCLE_COUNT:
-               ret = regmap_read(map, MAX17042_Cycles, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               ret = regmap_read(map, MAX17042_MinMaxVolt, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data >> 8;
-               val->intval *= 20000; /* Units of LSB = 20mV */
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
-                       ret = regmap_read(map, MAX17042_V_empty, &data);
-               else
-                       ret = regmap_read(map, MAX17047_V_empty, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data >> 7;
-               val->intval *= 10000; /* Units of LSB = 10mV */
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = regmap_read(map, MAX17042_VCELL, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data * 625 / 8;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               ret = regmap_read(map, MAX17042_AvgVCELL, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data * 625 / 8;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
-               ret = regmap_read(map, MAX17042_OCVInternal, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data * 625 / 8;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = regmap_read(map, MAX17042_RepSOC, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data >> 8;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               ret = regmap_read(map, MAX17042_FullCAP, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data * 1000 / 2;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
-               ret = regmap_read(map, MAX17042_QH, &data);
-               if (ret < 0)
-                       return ret;
-
-               val->intval = data * 1000 / 2;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = max17042_get_temperature(chip, &val->intval);
-               if (ret < 0)
-                       return ret;
-               break;
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
-               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
-               if (ret < 0)
-                       return ret;
-               /* LSB is Alert Minimum. In deci-centigrade */
-               val->intval = (data & 0xff) * 10;
-               break;
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
-               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
-               if (ret < 0)
-                       return ret;
-               /* MSB is Alert Maximum. In deci-centigrade */
-               val->intval = (data >> 8) * 10;
-               break;
-       case POWER_SUPPLY_PROP_TEMP_MIN:
-               val->intval = chip->pdata->temp_min;
-               break;
-       case POWER_SUPPLY_PROP_TEMP_MAX:
-               val->intval = chip->pdata->temp_max;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = max17042_get_battery_health(chip, &val->intval);
-               if (ret < 0)
-                       return ret;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               if (chip->pdata->enable_current_sense) {
-                       ret = regmap_read(map, MAX17042_Current, &data);
-                       if (ret < 0)
-                               return ret;
-
-                       val->intval = data;
-                       if (val->intval & 0x8000) {
-                               /* Negative */
-                               val->intval = ~val->intval & 0x7fff;
-                               val->intval++;
-                               val->intval *= -1;
-                       }
-                       val->intval *= 1562500 / chip->pdata->r_sns;
-               } else {
-                       return -EINVAL;
-               }
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               if (chip->pdata->enable_current_sense) {
-                       ret = regmap_read(map, MAX17042_AvgCurrent, &data);
-                       if (ret < 0)
-                               return ret;
-
-                       val->intval = data;
-                       if (val->intval & 0x8000) {
-                               /* Negative */
-                               val->intval = ~val->intval & 0x7fff;
-                               val->intval++;
-                               val->intval *= -1;
-                       }
-                       val->intval *= 1562500 / chip->pdata->r_sns;
-               } else {
-                       return -EINVAL;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int max17042_set_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           const union power_supply_propval *val)
-{
-       struct max17042_chip *chip = power_supply_get_drvdata(psy);
-       struct regmap *map = chip->regmap;
-       int ret = 0;
-       u32 data;
-       int8_t temp;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
-               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
-               if (ret < 0)
-                       return ret;
-
-               /* Input in deci-centigrade, convert to centigrade */
-               temp = val->intval / 10;
-               /* force min < max */
-               if (temp >= (int8_t)(data >> 8))
-                       temp = (int8_t)(data >> 8) - 1;
-               /* Write both MAX and MIN ALERT */
-               data = (data & 0xff00) + temp;
-               ret = regmap_write(map, MAX17042_TALRT_Th, data);
-               break;
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
-               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
-               if (ret < 0)
-                       return ret;
-
-               /* Input in Deci-Centigrade, convert to centigrade */
-               temp = val->intval / 10;
-               /* force max > min */
-               if (temp <= (int8_t)(data & 0xff))
-                       temp = (int8_t)(data & 0xff) + 1;
-               /* Write both MAX and MIN ALERT */
-               data = (data & 0xff) + (temp << 8);
-               ret = regmap_write(map, MAX17042_TALRT_Th, data);
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-static int max17042_property_is_writeable(struct power_supply *psy,
-               enum power_supply_property psp)
-{
-       int ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
-       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
-               ret = 1;
-               break;
-       default:
-               ret = 0;
-       }
-
-       return ret;
-}
-
-static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
-{
-       int retries = 8;
-       int ret;
-       u32 read_value;
-
-       do {
-               ret = regmap_write(map, reg, value);
-               regmap_read(map, reg, &read_value);
-               if (read_value != value) {
-                       ret = -EIO;
-                       retries--;
-               }
-       } while (retries && read_value != value);
-
-       if (ret < 0)
-               pr_err("%s: err %d\n", __func__, ret);
-
-       return ret;
-}
-
-static inline void max17042_override_por(struct regmap *map,
-                                        u8 reg, u16 value)
-{
-       if (value)
-               regmap_write(map, reg, value);
-}
-
-static inline void max10742_unlock_model(struct max17042_chip *chip)
-{
-       struct regmap *map = chip->regmap;
-
-       regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
-       regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
-}
-
-static inline void max10742_lock_model(struct max17042_chip *chip)
-{
-       struct regmap *map = chip->regmap;
-
-       regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1);
-       regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2);
-}
-
-static inline void max17042_write_model_data(struct max17042_chip *chip,
-                                       u8 addr, int size)
-{
-       struct regmap *map = chip->regmap;
-       int i;
-
-       for (i = 0; i < size; i++)
-               regmap_write(map, addr + i,
-                       chip->pdata->config_data->cell_char_tbl[i]);
-}
-
-static inline void max17042_read_model_data(struct max17042_chip *chip,
-                                       u8 addr, u32 *data, int size)
-{
-       struct regmap *map = chip->regmap;
-       int i;
-
-       for (i = 0; i < size; i++)
-               regmap_read(map, addr + i, &data[i]);
-}
-
-static inline int max17042_model_data_compare(struct max17042_chip *chip,
-                                       u16 *data1, u16 *data2, int size)
-{
-       int i;
-
-       if (memcmp(data1, data2, size)) {
-               dev_err(&chip->client->dev, "%s compare failed\n", __func__);
-               for (i = 0; i < size; i++)
-                       dev_info(&chip->client->dev, "0x%x, 0x%x",
-                               data1[i], data2[i]);
-               dev_info(&chip->client->dev, "\n");
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int max17042_init_model(struct max17042_chip *chip)
-{
-       int ret;
-       int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
-       u32 *temp_data;
-
-       temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
-       if (!temp_data)
-               return -ENOMEM;
-
-       max10742_unlock_model(chip);
-       max17042_write_model_data(chip, MAX17042_MODELChrTbl,
-                               table_size);
-       max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
-                               table_size);
-
-       ret = max17042_model_data_compare(
-               chip,
-               chip->pdata->config_data->cell_char_tbl,
-               (u16 *)temp_data,
-               table_size);
-
-       max10742_lock_model(chip);
-       kfree(temp_data);
-
-       return ret;
-}
-
-static int max17042_verify_model_lock(struct max17042_chip *chip)
-{
-       int i;
-       int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
-       u32 *temp_data;
-       int ret = 0;
-
-       temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
-       if (!temp_data)
-               return -ENOMEM;
-
-       max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
-                               table_size);
-       for (i = 0; i < table_size; i++)
-               if (temp_data[i])
-                       ret = -EINVAL;
-
-       kfree(temp_data);
-       return ret;
-}
-
-static void max17042_write_config_regs(struct max17042_chip *chip)
-{
-       struct max17042_config_data *config = chip->pdata->config_data;
-       struct regmap *map = chip->regmap;
-
-       regmap_write(map, MAX17042_CONFIG, config->config);
-       regmap_write(map, MAX17042_LearnCFG, config->learn_cfg);
-       regmap_write(map, MAX17042_FilterCFG,
-                       config->filter_cfg);
-       regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg);
-       if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 ||
-                       chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)
-               regmap_write(map, MAX17047_FullSOCThr,
-                                               config->full_soc_thresh);
-}
-
-static void  max17042_write_custom_regs(struct max17042_chip *chip)
-{
-       struct max17042_config_data *config = chip->pdata->config_data;
-       struct regmap *map = chip->regmap;
-
-       max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0);
-       max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0);
-       max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term);
-       if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) {
-               regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco);
-               max17042_write_verify_reg(map, MAX17042_K_empty0,
-                                       config->kempty0);
-       } else {
-               max17042_write_verify_reg(map, MAX17047_QRTbl00,
-                                               config->qrtbl00);
-               max17042_write_verify_reg(map, MAX17047_QRTbl10,
-                                               config->qrtbl10);
-               max17042_write_verify_reg(map, MAX17047_QRTbl20,
-                                               config->qrtbl20);
-               max17042_write_verify_reg(map, MAX17047_QRTbl30,
-                                               config->qrtbl30);
-       }
-}
-
-static void max17042_update_capacity_regs(struct max17042_chip *chip)
-{
-       struct max17042_config_data *config = chip->pdata->config_data;
-       struct regmap *map = chip->regmap;
-
-       max17042_write_verify_reg(map, MAX17042_FullCAP,
-                               config->fullcap);
-       regmap_write(map, MAX17042_DesignCap, config->design_cap);
-       max17042_write_verify_reg(map, MAX17042_FullCAPNom,
-                               config->fullcapnom);
-}
-
-static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
-{
-       unsigned int vfSoc;
-       struct regmap *map = chip->regmap;
-
-       regmap_read(map, MAX17042_VFSOC, &vfSoc);
-       regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
-       max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc);
-       regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
-}
-
-static void max17042_load_new_capacity_params(struct max17042_chip *chip)
-{
-       u32 full_cap0, rep_cap, dq_acc, vfSoc;
-       u32 rem_cap;
-
-       struct max17042_config_data *config = chip->pdata->config_data;
-       struct regmap *map = chip->regmap;
-
-       regmap_read(map, MAX17042_FullCAP0, &full_cap0);
-       regmap_read(map, MAX17042_VFSOC, &vfSoc);
-
-       /* fg_vfSoc needs to shifted by 8 bits to get the
-        * perc in 1% accuracy, to get the right rem_cap multiply
-        * full_cap0, fg_vfSoc and devide by 100
-        */
-       rem_cap = ((vfSoc >> 8) * full_cap0) / 100;
-       max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap);
-
-       rep_cap = rem_cap;
-       max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap);
-
-       /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */
-       dq_acc = config->fullcap / dQ_ACC_DIV;
-       max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc);
-       max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200);
-
-       max17042_write_verify_reg(map, MAX17042_FullCAP,
-                       config->fullcap);
-       regmap_write(map, MAX17042_DesignCap,
-                       config->design_cap);
-       max17042_write_verify_reg(map, MAX17042_FullCAPNom,
-                       config->fullcapnom);
-       /* Update SOC register with new SOC */
-       regmap_write(map, MAX17042_RepSOC, vfSoc);
-}
-
-/*
- * Block write all the override values coming from platform data.
- * This function MUST be called before the POR initialization proceedure
- * specified by maxim.
- */
-static inline void max17042_override_por_values(struct max17042_chip *chip)
-{
-       struct regmap *map = chip->regmap;
-       struct max17042_config_data *config = chip->pdata->config_data;
-
-       max17042_override_por(map, MAX17042_TGAIN, config->tgain);
-       max17042_override_por(map, MAx17042_TOFF, config->toff);
-       max17042_override_por(map, MAX17042_CGAIN, config->cgain);
-       max17042_override_por(map, MAX17042_COFF, config->coff);
-
-       max17042_override_por(map, MAX17042_VALRT_Th, config->valrt_thresh);
-       max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh);
-       max17042_override_por(map, MAX17042_SALRT_Th,
-                                               config->soc_alrt_thresh);
-       max17042_override_por(map, MAX17042_CONFIG, config->config);
-       max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer);
-
-       max17042_override_por(map, MAX17042_DesignCap, config->design_cap);
-       max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term);
-
-       max17042_override_por(map, MAX17042_AtRate, config->at_rate);
-       max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg);
-       max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg);
-       max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg);
-       max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg);
-       max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
-
-       max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
-       max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
-       if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
-               max17042_override_por(map, MAX17042_SOC_empty,
-                                               config->socempty);
-       max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
-       max17042_override_por(map, MAX17042_dQacc, config->dqacc);
-       max17042_override_por(map, MAX17042_dPacc, config->dpacc);
-
-       if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
-               max17042_override_por(map, MAX17042_V_empty, config->vempty);
-       else
-               max17042_override_por(map, MAX17047_V_empty, config->vempty);
-       max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
-       max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
-       max17042_override_por(map, MAX17042_FCTC, config->fctc);
-       max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
-       max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
-       if (chip->chip_type) {
-               max17042_override_por(map, MAX17042_EmptyTempCo,
-                                               config->empty_tempco);
-               max17042_override_por(map, MAX17042_K_empty0,
-                                               config->kempty0);
-       }
-}
-
-static int max17042_init_chip(struct max17042_chip *chip)
-{
-       struct regmap *map = chip->regmap;
-       int ret;
-
-       max17042_override_por_values(chip);
-       /* After Power up, the MAX17042 requires 500mS in order
-        * to perform signal debouncing and initial SOC reporting
-        */
-       msleep(500);
-
-       /* Initialize configaration */
-       max17042_write_config_regs(chip);
-
-       /* write cell characterization data */
-       ret = max17042_init_model(chip);
-       if (ret) {
-               dev_err(&chip->client->dev, "%s init failed\n",
-                       __func__);
-               return -EIO;
-       }
-
-       ret = max17042_verify_model_lock(chip);
-       if (ret) {
-               dev_err(&chip->client->dev, "%s lock verify failed\n",
-                       __func__);
-               return -EIO;
-       }
-       /* write custom parameters */
-       max17042_write_custom_regs(chip);
-
-       /* update capacity params */
-       max17042_update_capacity_regs(chip);
-
-       /* delay must be atleast 350mS to allow VFSOC
-        * to be calculated from the new configuration
-        */
-       msleep(350);
-
-       /* reset vfsoc0 reg */
-       max17042_reset_vfsoc0_reg(chip);
-
-       /* load new capacity params */
-       max17042_load_new_capacity_params(chip);
-
-       /* Init complete, Clear the POR bit */
-       regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0);
-       return 0;
-}
-
-static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
-{
-       struct regmap *map = chip->regmap;
-       u32 soc, soc_tr;
-
-       /* program interrupt thesholds such that we should
-        * get interrupt for every 'off' perc change in the soc
-        */
-       regmap_read(map, MAX17042_RepSOC, &soc);
-       soc >>= 8;
-       soc_tr = (soc + off) << 8;
-       soc_tr |= (soc - off);
-       regmap_write(map, MAX17042_SALRT_Th, soc_tr);
-}
-
-static irqreturn_t max17042_thread_handler(int id, void *dev)
-{
-       struct max17042_chip *chip = dev;
-       u32 val;
-
-       regmap_read(chip->regmap, MAX17042_STATUS, &val);
-       if ((val & STATUS_INTR_SOCMIN_BIT) ||
-               (val & STATUS_INTR_SOCMAX_BIT)) {
-               dev_info(&chip->client->dev, "SOC threshold INTR\n");
-               max17042_set_soc_threshold(chip, 1);
-       }
-
-       power_supply_changed(chip->battery);
-       return IRQ_HANDLED;
-}
-
-static void max17042_init_worker(struct work_struct *work)
-{
-       struct max17042_chip *chip = container_of(work,
-                               struct max17042_chip, work);
-       int ret;
-
-       /* Initialize registers according to values from the platform data */
-       if (chip->pdata->enable_por_init && chip->pdata->config_data) {
-               ret = max17042_init_chip(chip);
-               if (ret)
-                       return;
-       }
-
-       chip->init_complete = 1;
-}
-
-#ifdef CONFIG_OF
-static struct max17042_platform_data *
-max17042_get_pdata(struct device *dev)
-{
-       struct device_node *np = dev->of_node;
-       u32 prop;
-       struct max17042_platform_data *pdata;
-
-       if (!np)
-               return dev->platform_data;
-
-       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return NULL;
-
-       /*
-        * Require current sense resistor value to be specified for
-        * current-sense functionality to be enabled at all.
-        */
-       if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) {
-               pdata->r_sns = prop;
-               pdata->enable_current_sense = true;
-       }
-
-       if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min))
-               pdata->temp_min = INT_MIN;
-       if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max))
-               pdata->temp_max = INT_MAX;
-       if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin))
-               pdata->vmin = INT_MIN;
-       if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
-               pdata->vmax = INT_MAX;
-
-       return pdata;
-}
-#else
-static struct max17042_platform_data *
-max17042_get_pdata(struct device *dev)
-{
-       return dev->platform_data;
-}
-#endif
-
-static const struct regmap_config max17042_regmap_config = {
-       .reg_bits = 8,
-       .val_bits = 16,
-       .val_format_endian = REGMAP_ENDIAN_NATIVE,
-};
-
-static const struct power_supply_desc max17042_psy_desc = {
-       .name           = "max170xx_battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property   = max17042_get_property,
-       .set_property   = max17042_set_property,
-       .property_is_writeable  = max17042_property_is_writeable,
-       .properties     = max17042_battery_props,
-       .num_properties = ARRAY_SIZE(max17042_battery_props),
-};
-
-static const struct power_supply_desc max17042_no_current_sense_psy_desc = {
-       .name           = "max170xx_battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property   = max17042_get_property,
-       .set_property   = max17042_set_property,
-       .property_is_writeable  = max17042_property_is_writeable,
-       .properties     = max17042_battery_props,
-       .num_properties = ARRAY_SIZE(max17042_battery_props) - 2,
-};
-
-static int max17042_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
-{
-       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
-       struct power_supply_config psy_cfg = {};
-       struct max17042_chip *chip;
-       int ret;
-       int i;
-       u32 val;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
-               return -EIO;
-
-       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
-       if (!chip)
-               return -ENOMEM;
-
-       chip->client = client;
-       chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config);
-       if (IS_ERR(chip->regmap)) {
-               dev_err(&client->dev, "Failed to initialize regmap\n");
-               return -EINVAL;
-       }
-
-       chip->pdata = max17042_get_pdata(&client->dev);
-       if (!chip->pdata) {
-               dev_err(&client->dev, "no platform data provided\n");
-               return -EINVAL;
-       }
-
-       i2c_set_clientdata(client, chip);
-       chip->chip_type = id->driver_data;
-       psy_cfg.drv_data = chip;
-
-       /* When current is not measured,
-        * CURRENT_NOW and CURRENT_AVG properties should be invisible. */
-       if (!chip->pdata->enable_current_sense)
-               max17042_desc = &max17042_no_current_sense_psy_desc;
-
-       if (chip->pdata->r_sns == 0)
-               chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
-
-       if (chip->pdata->init_data)
-               for (i = 0; i < chip->pdata->num_init_data; i++)
-                       regmap_write(chip->regmap,
-                                       chip->pdata->init_data[i].addr,
-                                       chip->pdata->init_data[i].data);
-
-       if (!chip->pdata->enable_current_sense) {
-               regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
-               regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
-               regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
-       }
-
-       chip->battery = devm_power_supply_register(&client->dev, max17042_desc,
-                                                  &psy_cfg);
-       if (IS_ERR(chip->battery)) {
-               dev_err(&client->dev, "failed: power supply register\n");
-               return PTR_ERR(chip->battery);
-       }
-
-       if (client->irq) {
-               ret = devm_request_threaded_irq(&client->dev, client->irq,
-                                               NULL,
-                                               max17042_thread_handler,
-                                               IRQF_TRIGGER_FALLING |
-                                               IRQF_ONESHOT,
-                                               chip->battery->desc->name,
-                                               chip);
-               if (!ret) {
-                       regmap_update_bits(chip->regmap, MAX17042_CONFIG,
-                                       CONFIG_ALRT_BIT_ENBL,
-                                       CONFIG_ALRT_BIT_ENBL);
-                       max17042_set_soc_threshold(chip, 1);
-               } else {
-                       client->irq = 0;
-                       dev_err(&client->dev, "%s(): cannot get IRQ\n",
-                               __func__);
-               }
-       }
-
-       regmap_read(chip->regmap, MAX17042_STATUS, &val);
-       if (val & STATUS_POR_BIT) {
-               INIT_WORK(&chip->work, max17042_init_worker);
-               schedule_work(&chip->work);
-       } else {
-               chip->init_complete = 1;
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int max17042_suspend(struct device *dev)
-{
-       struct max17042_chip *chip = dev_get_drvdata(dev);
-
-       /*
-        * disable the irq and enable irq_wake
-        * capability to the interrupt line.
-        */
-       if (chip->client->irq) {
-               disable_irq(chip->client->irq);
-               enable_irq_wake(chip->client->irq);
-       }
-
-       return 0;
-}
-
-static int max17042_resume(struct device *dev)
-{
-       struct max17042_chip *chip = dev_get_drvdata(dev);
-
-       if (chip->client->irq) {
-               disable_irq_wake(chip->client->irq);
-               enable_irq(chip->client->irq);
-               /* re-program the SOC thresholds to 1% change */
-               max17042_set_soc_threshold(chip, 1);
-       }
-
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
-                       max17042_resume);
-
-#ifdef CONFIG_OF
-static const struct of_device_id max17042_dt_match[] = {
-       { .compatible = "maxim,max17042" },
-       { .compatible = "maxim,max17047" },
-       { .compatible = "maxim,max17050" },
-       { },
-};
-MODULE_DEVICE_TABLE(of, max17042_dt_match);
-#endif
-
-static const struct i2c_device_id max17042_id[] = {
-       { "max17042", MAXIM_DEVICE_TYPE_MAX17042 },
-       { "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
-       { "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, max17042_id);
-
-static struct i2c_driver max17042_i2c_driver = {
-       .driver = {
-               .name   = "max17042",
-               .of_match_table = of_match_ptr(max17042_dt_match),
-               .pm     = &max17042_pm_ops,
-       },
-       .probe          = max17042_probe,
-       .id_table       = max17042_id,
-};
-module_i2c_driver(max17042_i2c_driver);
-
-MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
-MODULE_DESCRIPTION("MAX17042 Fuel Gauge");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/max77693_charger.c b/drivers/power/max77693_charger.c
deleted file mode 100644 (file)
index 060cab5..0000000
+++ /dev/null
@@ -1,771 +0,0 @@
-/*
- * max77693_charger.c - Battery charger driver for the Maxim 77693
- *
- * Copyright (C) 2014 Samsung Electronics
- * Krzysztof Kozlowski <k.kozlowski@samsung.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/regmap.h>
-#include <linux/mfd/max77693.h>
-#include <linux/mfd/max77693-common.h>
-#include <linux/mfd/max77693-private.h>
-
-#define MAX77693_CHARGER_NAME                          "max77693-charger"
-static const char *max77693_charger_model              = "MAX77693";
-static const char *max77693_charger_manufacturer       = "Maxim Integrated";
-
-struct max77693_charger {
-       struct device           *dev;
-       struct max77693_dev     *max77693;
-       struct power_supply     *charger;
-
-       u32 constant_volt;
-       u32 min_system_volt;
-       u32 thermal_regulation_temp;
-       u32 batttery_overcurrent;
-       u32 charge_input_threshold_volt;
-};
-
-static int max77693_get_charger_state(struct regmap *regmap, int *val)
-{
-       int ret;
-       unsigned int data;
-
-       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
-       if (ret < 0)
-               return ret;
-
-       data &= CHG_DETAILS_01_CHG_MASK;
-       data >>= CHG_DETAILS_01_CHG_SHIFT;
-
-       switch (data) {
-       case MAX77693_CHARGING_PREQUALIFICATION:
-       case MAX77693_CHARGING_FAST_CONST_CURRENT:
-       case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
-       case MAX77693_CHARGING_TOP_OFF:
-       /* In high temp the charging current is reduced, but still charging */
-       case MAX77693_CHARGING_HIGH_TEMP:
-               *val = POWER_SUPPLY_STATUS_CHARGING;
-               break;
-       case MAX77693_CHARGING_DONE:
-               *val = POWER_SUPPLY_STATUS_FULL;
-               break;
-       case MAX77693_CHARGING_TIMER_EXPIRED:
-       case MAX77693_CHARGING_THERMISTOR_SUSPEND:
-               *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               break;
-       case MAX77693_CHARGING_OFF:
-       case MAX77693_CHARGING_OVER_TEMP:
-       case MAX77693_CHARGING_WATCHDOG_EXPIRED:
-               *val = POWER_SUPPLY_STATUS_DISCHARGING;
-               break;
-       case MAX77693_CHARGING_RESERVED:
-       default:
-               *val = POWER_SUPPLY_STATUS_UNKNOWN;
-       }
-
-       return 0;
-}
-
-static int max77693_get_charge_type(struct regmap *regmap, int *val)
-{
-       int ret;
-       unsigned int data;
-
-       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
-       if (ret < 0)
-               return ret;
-
-       data &= CHG_DETAILS_01_CHG_MASK;
-       data >>= CHG_DETAILS_01_CHG_SHIFT;
-
-       switch (data) {
-       case MAX77693_CHARGING_PREQUALIFICATION:
-       /*
-        * Top-off: trickle or fast? In top-off the current varies between
-        * 100 and 250 mA. It is higher than prequalification current.
-        */
-       case MAX77693_CHARGING_TOP_OFF:
-               *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-               break;
-       case MAX77693_CHARGING_FAST_CONST_CURRENT:
-       case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
-       /* In high temp the charging current is reduced, but still charging */
-       case MAX77693_CHARGING_HIGH_TEMP:
-               *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
-               break;
-       case MAX77693_CHARGING_DONE:
-       case MAX77693_CHARGING_TIMER_EXPIRED:
-       case MAX77693_CHARGING_THERMISTOR_SUSPEND:
-       case MAX77693_CHARGING_OFF:
-       case MAX77693_CHARGING_OVER_TEMP:
-       case MAX77693_CHARGING_WATCHDOG_EXPIRED:
-               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
-               break;
-       case MAX77693_CHARGING_RESERVED:
-       default:
-               *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
-       }
-
-       return 0;
-}
-
-/*
- * Supported health statuses:
- *  - POWER_SUPPLY_HEALTH_DEAD
- *  - POWER_SUPPLY_HEALTH_GOOD
- *  - POWER_SUPPLY_HEALTH_OVERVOLTAGE
- *  - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE
- *  - POWER_SUPPLY_HEALTH_UNKNOWN
- *  - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
- */
-static int max77693_get_battery_health(struct regmap *regmap, int *val)
-{
-       int ret;
-       unsigned int data;
-
-       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
-       if (ret < 0)
-               return ret;
-
-       data &= CHG_DETAILS_01_BAT_MASK;
-       data >>= CHG_DETAILS_01_BAT_SHIFT;
-
-       switch (data) {
-       case MAX77693_BATTERY_NOBAT:
-               *val = POWER_SUPPLY_HEALTH_DEAD;
-               break;
-       case MAX77693_BATTERY_PREQUALIFICATION:
-       case MAX77693_BATTERY_GOOD:
-       case MAX77693_BATTERY_LOWVOLTAGE:
-               *val = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       case MAX77693_BATTERY_TIMER_EXPIRED:
-               /*
-                * Took longer to charge than expected, charging suspended.
-                * Damaged battery?
-                */
-               *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
-               break;
-       case MAX77693_BATTERY_OVERVOLTAGE:
-               *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               break;
-       case MAX77693_BATTERY_OVERCURRENT:
-               *val = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               break;
-       case MAX77693_BATTERY_RESERVED:
-       default:
-               *val = POWER_SUPPLY_HEALTH_UNKNOWN;
-               break;
-       }
-
-       return 0;
-}
-
-static int max77693_get_present(struct regmap *regmap, int *val)
-{
-       unsigned int data;
-       int ret;
-
-       /*
-        * Read CHG_INT_OK register. High DETBAT bit here should be
-        * equal to value 0x0 in CHG_DETAILS_01/BAT field.
-        */
-       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
-       if (ret < 0)
-               return ret;
-
-       *val = (data & CHG_INT_OK_DETBAT_MASK) ? 0 : 1;
-
-       return 0;
-}
-
-static int max77693_get_online(struct regmap *regmap, int *val)
-{
-       unsigned int data;
-       int ret;
-
-       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
-       if (ret < 0)
-               return ret;
-
-       *val = (data & CHG_INT_OK_CHGIN_MASK) ? 1 : 0;
-
-       return 0;
-}
-
-static enum power_supply_property max77693_charger_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static int max77693_charger_get_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       struct max77693_charger *chg = power_supply_get_drvdata(psy);
-       struct regmap *regmap = chg->max77693->regmap;
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = max77693_get_charger_state(regmap, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               ret = max77693_get_charge_type(regmap, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = max77693_get_battery_health(regmap, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               ret = max77693_get_present(regmap, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = max77693_get_online(regmap, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = max77693_charger_model;
-               break;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = max77693_charger_manufacturer;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return ret;
-}
-
-static const struct power_supply_desc max77693_charger_desc = {
-       .name           = MAX77693_CHARGER_NAME,
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = max77693_charger_props,
-       .num_properties = ARRAY_SIZE(max77693_charger_props),
-       .get_property   = max77693_charger_get_property,
-};
-
-static ssize_t device_attr_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count,
-               int (*fn)(struct max77693_charger *, unsigned long))
-{
-       struct max77693_charger *chg = dev_get_drvdata(dev);
-       unsigned long val;
-       int ret;
-
-       ret = kstrtoul(buf, 10, &val);
-       if (ret)
-               return ret;
-
-       ret = fn(chg, val);
-       if (ret)
-               return ret;
-
-       return count;
-}
-
-static ssize_t fast_charge_timer_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct max77693_charger *chg = dev_get_drvdata(dev);
-       unsigned int data, val;
-       int ret;
-
-       ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01,
-                       &data);
-       if (ret < 0)
-               return ret;
-
-       data &= CHG_CNFG_01_FCHGTIME_MASK;
-       data >>= CHG_CNFG_01_FCHGTIME_SHIFT;
-       switch (data) {
-       case 0x1 ... 0x7:
-               /* Starting from 4 hours, step by 2 hours */
-               val = 4 + (data - 1) * 2;
-               break;
-       case 0x0:
-       default:
-               val = 0;
-               break;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
-}
-
-static int max77693_set_fast_charge_timer(struct max77693_charger *chg,
-               unsigned long hours)
-{
-       unsigned int data;
-
-       /*
-        * 0x00 - disable
-        * 0x01 - 4h
-        * 0x02 - 6h
-        * ...
-        * 0x07 - 16h
-        * Round down odd values.
-        */
-       switch (hours) {
-       case 4 ... 16:
-               data = (hours - 4) / 2 + 1;
-               break;
-       case 0:
-               /* Disable */
-               data = 0;
-               break;
-       default:
-               return -EINVAL;
-       }
-       data <<= CHG_CNFG_01_FCHGTIME_SHIFT;
-
-       return regmap_update_bits(chg->max77693->regmap,
-                       MAX77693_CHG_REG_CHG_CNFG_01,
-                       CHG_CNFG_01_FCHGTIME_MASK, data);
-}
-
-static ssize_t fast_charge_timer_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       return device_attr_store(dev, attr, buf, count,
-                       max77693_set_fast_charge_timer);
-}
-
-static ssize_t top_off_threshold_current_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct max77693_charger *chg = dev_get_drvdata(dev);
-       unsigned int data, val;
-       int ret;
-
-       ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
-                       &data);
-       if (ret < 0)
-               return ret;
-
-       data &= CHG_CNFG_03_TOITH_MASK;
-       data >>= CHG_CNFG_03_TOITH_SHIFT;
-
-       if (data <= 0x04)
-               val = 100000 + data * 25000;
-       else
-               val = data * 50000;
-
-       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
-}
-
-static int max77693_set_top_off_threshold_current(struct max77693_charger *chg,
-               unsigned long uamp)
-{
-       unsigned int data;
-
-       if (uamp < 100000 || uamp > 350000)
-               return -EINVAL;
-
-       if (uamp <= 200000)
-               data = (uamp - 100000) / 25000;
-       else
-               /* (200000, 350000> */
-               data = uamp / 50000;
-
-       data <<= CHG_CNFG_03_TOITH_SHIFT;
-
-       return regmap_update_bits(chg->max77693->regmap,
-                       MAX77693_CHG_REG_CHG_CNFG_03,
-                       CHG_CNFG_03_TOITH_MASK, data);
-}
-
-static ssize_t top_off_threshold_current_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       return device_attr_store(dev, attr, buf, count,
-                       max77693_set_top_off_threshold_current);
-}
-
-static ssize_t top_off_timer_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct max77693_charger *chg = dev_get_drvdata(dev);
-       unsigned int data, val;
-       int ret;
-
-       ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
-                       &data);
-       if (ret < 0)
-               return ret;
-
-       data &= CHG_CNFG_03_TOTIME_MASK;
-       data >>= CHG_CNFG_03_TOTIME_SHIFT;
-
-       val = data * 10;
-
-       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
-}
-
-static int max77693_set_top_off_timer(struct max77693_charger *chg,
-               unsigned long minutes)
-{
-       unsigned int data;
-
-       if (minutes > 70)
-               return -EINVAL;
-
-       data = minutes / 10;
-       data <<= CHG_CNFG_03_TOTIME_SHIFT;
-
-       return regmap_update_bits(chg->max77693->regmap,
-                       MAX77693_CHG_REG_CHG_CNFG_03,
-                       CHG_CNFG_03_TOTIME_MASK, data);
-}
-
-static ssize_t top_off_timer_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       return device_attr_store(dev, attr, buf, count,
-                       max77693_set_top_off_timer);
-}
-
-static DEVICE_ATTR_RW(fast_charge_timer);
-static DEVICE_ATTR_RW(top_off_threshold_current);
-static DEVICE_ATTR_RW(top_off_timer);
-
-static int max77693_set_constant_volt(struct max77693_charger *chg,
-               unsigned int uvolt)
-{
-       unsigned int data;
-
-       /*
-        * 0x00 - 3.650 V
-        * 0x01 - 3.675 V
-        * ...
-        * 0x1b - 4.325 V
-        * 0x1c - 4.340 V
-        * 0x1d - 4.350 V
-        * 0x1e - 4.375 V
-        * 0x1f - 4.400 V
-        */
-       if (uvolt >= 3650000 && uvolt < 4340000)
-               data = (uvolt - 3650000) / 25000;
-       else if (uvolt >= 4340000 && uvolt < 4350000)
-               data = 0x1c;
-       else if (uvolt >= 4350000 && uvolt <= 4400000)
-               data = 0x1d + (uvolt - 4350000) / 25000;
-       else {
-               dev_err(chg->dev, "Wrong value for charging constant voltage\n");
-               return -EINVAL;
-       }
-
-       data <<= CHG_CNFG_04_CHGCVPRM_SHIFT;
-
-       dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt,
-                       data);
-
-       return regmap_update_bits(chg->max77693->regmap,
-                       MAX77693_CHG_REG_CHG_CNFG_04,
-                       CHG_CNFG_04_CHGCVPRM_MASK, data);
-}
-
-static int max77693_set_min_system_volt(struct max77693_charger *chg,
-               unsigned int uvolt)
-{
-       unsigned int data;
-
-       if (uvolt < 3000000 || uvolt > 3700000) {
-               dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n");
-               return -EINVAL;
-       }
-
-       data = (uvolt - 3000000) / 100000;
-
-       data <<= CHG_CNFG_04_MINVSYS_SHIFT;
-
-       dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n",
-                       uvolt, data);
-
-       return regmap_update_bits(chg->max77693->regmap,
-                       MAX77693_CHG_REG_CHG_CNFG_04,
-                       CHG_CNFG_04_MINVSYS_MASK, data);
-}
-
-static int max77693_set_thermal_regulation_temp(struct max77693_charger *chg,
-               unsigned int cels)
-{
-       unsigned int data;
-
-       switch (cels) {
-       case 70:
-       case 85:
-       case 100:
-       case 115:
-               data = (cels - 70) / 15;
-               break;
-       default:
-               dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n");
-               return -EINVAL;
-       }
-
-       data <<= CHG_CNFG_07_REGTEMP_SHIFT;
-
-       dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n",
-                       cels, data);
-
-       return regmap_update_bits(chg->max77693->regmap,
-                       MAX77693_CHG_REG_CHG_CNFG_07,
-                       CHG_CNFG_07_REGTEMP_MASK, data);
-}
-
-static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
-               unsigned int uamp)
-{
-       unsigned int data;
-
-       if (uamp && (uamp < 2000000 || uamp > 3500000)) {
-               dev_err(chg->dev, "Wrong value for battery overcurrent\n");
-               return -EINVAL;
-       }
-
-       if (uamp)
-               data = ((uamp - 2000000) / 250000) + 1;
-       else
-               data = 0; /* disable */
-
-       data <<= CHG_CNFG_12_B2SOVRC_SHIFT;
-
-       dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data);
-
-       return regmap_update_bits(chg->max77693->regmap,
-                       MAX77693_CHG_REG_CHG_CNFG_12,
-                       CHG_CNFG_12_B2SOVRC_MASK, data);
-}
-
-static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg,
-               unsigned int uvolt)
-{
-       unsigned int data;
-
-       switch (uvolt) {
-       case 4300000:
-               data = 0x0;
-               break;
-       case 4700000:
-       case 4800000:
-       case 4900000:
-               data = (uvolt - 4700000) / 100000;
-       default:
-               dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n");
-               return -EINVAL;
-       }
-
-       data <<= CHG_CNFG_12_VCHGINREG_SHIFT;
-
-       dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n",
-                       uvolt, data);
-
-       return regmap_update_bits(chg->max77693->regmap,
-                       MAX77693_CHG_REG_CHG_CNFG_12,
-                       CHG_CNFG_12_VCHGINREG_MASK, data);
-}
-
-/*
- * Sets charger registers to proper and safe default values.
- */
-static int max77693_reg_init(struct max77693_charger *chg)
-{
-       int ret;
-       unsigned int data;
-
-       /* Unlock charger register protection */
-       data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT);
-       ret = regmap_update_bits(chg->max77693->regmap,
-                               MAX77693_CHG_REG_CHG_CNFG_06,
-                               CHG_CNFG_06_CHGPROT_MASK, data);
-       if (ret) {
-               dev_err(chg->dev, "Error unlocking registers: %d\n", ret);
-               return ret;
-       }
-
-       ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER);
-       if (ret)
-               return ret;
-
-       ret = max77693_set_top_off_threshold_current(chg,
-                       DEFAULT_TOP_OFF_THRESHOLD_CURRENT);
-       if (ret)
-               return ret;
-
-       ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER);
-       if (ret)
-               return ret;
-
-       ret = max77693_set_constant_volt(chg, chg->constant_volt);
-       if (ret)
-               return ret;
-
-       ret = max77693_set_min_system_volt(chg, chg->min_system_volt);
-       if (ret)
-               return ret;
-
-       ret = max77693_set_thermal_regulation_temp(chg,
-                       chg->thermal_regulation_temp);
-       if (ret)
-               return ret;
-
-       ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent);
-       if (ret)
-               return ret;
-
-       return max77693_set_charge_input_threshold_volt(chg,
-                       chg->charge_input_threshold_volt);
-}
-
-#ifdef CONFIG_OF
-static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
-{
-       struct device_node *np = dev->of_node;
-
-       if (!np) {
-               dev_err(dev, "no charger OF node\n");
-               return -EINVAL;
-       }
-
-       if (of_property_read_u32(np, "maxim,constant-microvolt",
-                       &chg->constant_volt))
-               chg->constant_volt = DEFAULT_CONSTANT_VOLT;
-
-       if (of_property_read_u32(np, "maxim,min-system-microvolt",
-                       &chg->min_system_volt))
-               chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT;
-
-       if (of_property_read_u32(np, "maxim,thermal-regulation-celsius",
-                       &chg->thermal_regulation_temp))
-               chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP;
-
-       if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp",
-                       &chg->batttery_overcurrent))
-               chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT;
-
-       if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt",
-                       &chg->charge_input_threshold_volt))
-               chg->charge_input_threshold_volt =
-                       DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT;
-
-       return 0;
-}
-#else /* CONFIG_OF */
-static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
-{
-       return 0;
-}
-#endif /* CONFIG_OF */
-
-static int max77693_charger_probe(struct platform_device *pdev)
-{
-       struct max77693_charger *chg;
-       struct power_supply_config psy_cfg = {};
-       struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
-       int ret;
-
-       chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
-       if (!chg)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, chg);
-       chg->dev = &pdev->dev;
-       chg->max77693 = max77693;
-
-       ret = max77693_dt_init(&pdev->dev, chg);
-       if (ret)
-               return ret;
-
-       ret = max77693_reg_init(chg);
-       if (ret)
-               return ret;
-
-       psy_cfg.drv_data = chg;
-
-       ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
-       if (ret) {
-               dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n");
-               goto err;
-       }
-
-       ret = device_create_file(&pdev->dev,
-                       &dev_attr_top_off_threshold_current);
-       if (ret) {
-               dev_err(&pdev->dev, "failed: create top off current sysfs entry\n");
-               goto err;
-       }
-
-       ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer);
-       if (ret) {
-               dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n");
-               goto err;
-       }
-
-       chg->charger = power_supply_register(&pdev->dev,
-                                               &max77693_charger_desc,
-                                               &psy_cfg);
-       if (IS_ERR(chg->charger)) {
-               dev_err(&pdev->dev, "failed: power supply register\n");
-               ret = PTR_ERR(chg->charger);
-               goto err;
-       }
-
-       return 0;
-
-err:
-       device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
-       device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
-       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
-
-       return ret;
-}
-
-static int max77693_charger_remove(struct platform_device *pdev)
-{
-       struct max77693_charger *chg = platform_get_drvdata(pdev);
-
-       device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
-       device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
-       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
-
-       power_supply_unregister(chg->charger);
-
-       return 0;
-}
-
-static const struct platform_device_id max77693_charger_id[] = {
-       { "max77693-charger", 0, },
-       { }
-};
-MODULE_DEVICE_TABLE(platform, max77693_charger_id);
-
-static struct platform_driver max77693_charger_driver = {
-       .driver = {
-               .name   = "max77693-charger",
-       },
-       .probe          = max77693_charger_probe,
-       .remove         = max77693_charger_remove,
-       .id_table       = max77693_charger_id,
-};
-module_platform_driver(max77693_charger_driver);
-
-MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
-MODULE_DESCRIPTION("Maxim 77693 charger driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c
deleted file mode 100644 (file)
index fdc73d6..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver
- *
- * Copyright (C) 2011 Samsung Electronics
- * MyungJoo Ham <myungjoo.ham@samsung.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/slab.h>
-#include <linux/power_supply.h>
-#include <linux/platform_device.h>
-#include <linux/power/max8903_charger.h>
-
-struct max8903_data {
-       struct max8903_pdata *pdata;
-       struct device *dev;
-       struct power_supply *psy;
-       struct power_supply_desc psy_desc;
-       bool fault;
-       bool usb_in;
-       bool ta_in;
-};
-
-static enum power_supply_property max8903_charger_props[] = {
-       POWER_SUPPLY_PROP_STATUS, /* Charger status output */
-       POWER_SUPPLY_PROP_ONLINE, /* External power source */
-       POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
-};
-
-static int max8903_get_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               union power_supply_propval *val)
-{
-       struct max8903_data *data = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-               if (gpio_is_valid(data->pdata->chg)) {
-                       if (gpio_get_value(data->pdata->chg) == 0)
-                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
-                       else if (data->usb_in || data->ta_in)
-                               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-                       else
-                               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               }
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = 0;
-               if (data->usb_in || data->ta_in)
-                       val->intval = 1;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               if (data->fault)
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static irqreturn_t max8903_dcin(int irq, void *_data)
-{
-       struct max8903_data *data = _data;
-       struct max8903_pdata *pdata = data->pdata;
-       bool ta_in;
-       enum power_supply_type old_type;
-
-       ta_in = gpio_get_value(pdata->dok) ? false : true;
-
-       if (ta_in == data->ta_in)
-               return IRQ_HANDLED;
-
-       data->ta_in = ta_in;
-
-       /* Set Current-Limit-Mode 1:DC 0:USB */
-       if (gpio_is_valid(pdata->dcm))
-               gpio_set_value(pdata->dcm, ta_in ? 1 : 0);
-
-       /* Charger Enable / Disable (cen is negated) */
-       if (gpio_is_valid(pdata->cen))
-               gpio_set_value(pdata->cen, ta_in ? 0 :
-                               (data->usb_in ? 0 : 1));
-
-       dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
-                       "Connected" : "Disconnected");
-
-       old_type = data->psy_desc.type;
-
-       if (data->ta_in)
-               data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
-       else if (data->usb_in)
-               data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
-       else
-               data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
-
-       if (old_type != data->psy_desc.type)
-               power_supply_changed(data->psy);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t max8903_usbin(int irq, void *_data)
-{
-       struct max8903_data *data = _data;
-       struct max8903_pdata *pdata = data->pdata;
-       bool usb_in;
-       enum power_supply_type old_type;
-
-       usb_in = gpio_get_value(pdata->uok) ? false : true;
-
-       if (usb_in == data->usb_in)
-               return IRQ_HANDLED;
-
-       data->usb_in = usb_in;
-
-       /* Do not touch Current-Limit-Mode */
-
-       /* Charger Enable / Disable (cen is negated) */
-       if (gpio_is_valid(pdata->cen))
-               gpio_set_value(pdata->cen, usb_in ? 0 :
-                               (data->ta_in ? 0 : 1));
-
-       dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
-                       "Connected" : "Disconnected");
-
-       old_type = data->psy_desc.type;
-
-       if (data->ta_in)
-               data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
-       else if (data->usb_in)
-               data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
-       else
-               data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
-
-       if (old_type != data->psy_desc.type)
-               power_supply_changed(data->psy);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t max8903_fault(int irq, void *_data)
-{
-       struct max8903_data *data = _data;
-       struct max8903_pdata *pdata = data->pdata;
-       bool fault;
-
-       fault = gpio_get_value(pdata->flt) ? false : true;
-
-       if (fault == data->fault)
-               return IRQ_HANDLED;
-
-       data->fault = fault;
-
-       if (fault)
-               dev_err(data->dev, "Charger suffers a fault and stops.\n");
-       else
-               dev_err(data->dev, "Charger recovered from a fault.\n");
-
-       return IRQ_HANDLED;
-}
-
-static struct max8903_pdata *max8903_parse_dt_data(struct device *dev)
-{
-       struct device_node *np = dev->of_node;
-       struct max8903_pdata *pdata = NULL;
-
-       if (!np)
-               return NULL;
-
-       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return NULL;
-
-       pdata->dc_valid = false;
-       pdata->usb_valid = false;
-
-       pdata->cen = of_get_named_gpio(np, "cen-gpios", 0);
-       if (!gpio_is_valid(pdata->cen))
-               pdata->cen = -EINVAL;
-
-       pdata->chg = of_get_named_gpio(np, "chg-gpios", 0);
-       if (!gpio_is_valid(pdata->chg))
-               pdata->chg = -EINVAL;
-
-       pdata->flt = of_get_named_gpio(np, "flt-gpios", 0);
-       if (!gpio_is_valid(pdata->flt))
-               pdata->flt = -EINVAL;
-
-       pdata->usus = of_get_named_gpio(np, "usus-gpios", 0);
-       if (!gpio_is_valid(pdata->usus))
-               pdata->usus = -EINVAL;
-
-       pdata->dcm = of_get_named_gpio(np, "dcm-gpios", 0);
-       if (!gpio_is_valid(pdata->dcm))
-               pdata->dcm = -EINVAL;
-
-       pdata->dok = of_get_named_gpio(np, "dok-gpios", 0);
-       if (!gpio_is_valid(pdata->dok))
-               pdata->dok = -EINVAL;
-       else
-               pdata->dc_valid = true;
-
-       pdata->uok = of_get_named_gpio(np, "uok-gpios", 0);
-       if (!gpio_is_valid(pdata->uok))
-               pdata->uok = -EINVAL;
-       else
-               pdata->usb_valid = true;
-
-       return pdata;
-}
-
-static int max8903_setup_gpios(struct platform_device *pdev)
-{
-       struct max8903_data *data = platform_get_drvdata(pdev);
-       struct device *dev = &pdev->dev;
-       struct max8903_pdata *pdata = pdev->dev.platform_data;
-       int ret = 0;
-       int gpio;
-       int ta_in = 0;
-       int usb_in = 0;
-
-       if (pdata->dc_valid) {
-               if (gpio_is_valid(pdata->dok)) {
-                       ret = devm_gpio_request(dev, pdata->dok,
-                                               data->psy_desc.name);
-                       if (ret) {
-                               dev_err(dev,
-                                       "Failed GPIO request for dok: %d err %d\n",
-                                       pdata->dok, ret);
-                               return ret;
-                       }
-
-                       gpio = pdata->dok; /* PULL_UPed Interrupt */
-                       ta_in = gpio_get_value(gpio) ? 0 : 1;
-               } else {
-                       dev_err(dev, "When DC is wired, DOK should be wired as well.\n");
-                       return -EINVAL;
-               }
-       }
-
-       if (gpio_is_valid(pdata->dcm)) {
-               ret = devm_gpio_request(dev, pdata->dcm, data->psy_desc.name);
-               if (ret) {
-                       dev_err(dev,
-                               "Failed GPIO request for dcm: %d err %d\n",
-                               pdata->dcm, ret);
-                       return ret;
-               }
-
-               gpio = pdata->dcm; /* Output */
-               gpio_set_value(gpio, ta_in);
-       }
-
-       if (pdata->usb_valid) {
-               if (gpio_is_valid(pdata->uok)) {
-                       ret = devm_gpio_request(dev, pdata->uok,
-                                               data->psy_desc.name);
-                       if (ret) {
-                               dev_err(dev,
-                                       "Failed GPIO request for uok: %d err %d\n",
-                                       pdata->uok, ret);
-                               return ret;
-                       }
-
-                       gpio = pdata->uok;
-                       usb_in = gpio_get_value(gpio) ? 0 : 1;
-               } else {
-                       dev_err(dev, "When USB is wired, UOK should be wired."
-                                       "as well.\n");
-                       return -EINVAL;
-               }
-       }
-
-       if (gpio_is_valid(pdata->cen)) {
-               ret = devm_gpio_request(dev, pdata->cen, data->psy_desc.name);
-               if (ret) {
-                       dev_err(dev,
-                               "Failed GPIO request for cen: %d err %d\n",
-                               pdata->cen, ret);
-                       return ret;
-               }
-
-               gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
-       }
-
-       if (gpio_is_valid(pdata->chg)) {
-               ret = devm_gpio_request(dev, pdata->chg, data->psy_desc.name);
-               if (ret) {
-                       dev_err(dev,
-                               "Failed GPIO request for chg: %d err %d\n",
-                               pdata->chg, ret);
-                       return ret;
-               }
-       }
-
-       if (gpio_is_valid(pdata->flt)) {
-               ret = devm_gpio_request(dev, pdata->flt, data->psy_desc.name);
-               if (ret) {
-                       dev_err(dev,
-                               "Failed GPIO request for flt: %d err %d\n",
-                               pdata->flt, ret);
-                       return ret;
-               }
-       }
-
-       if (gpio_is_valid(pdata->usus)) {
-               ret = devm_gpio_request(dev, pdata->usus, data->psy_desc.name);
-               if (ret) {
-                       dev_err(dev,
-                               "Failed GPIO request for usus: %d err %d\n",
-                               pdata->usus, ret);
-                       return ret;
-               }
-       }
-
-       data->fault = false;
-       data->ta_in = ta_in;
-       data->usb_in = usb_in;
-
-       return 0;
-}
-
-static int max8903_probe(struct platform_device *pdev)
-{
-       struct max8903_data *data;
-       struct device *dev = &pdev->dev;
-       struct max8903_pdata *pdata = pdev->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       int ret = 0;
-
-       data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       if (IS_ENABLED(CONFIG_OF) && !pdata && dev->of_node)
-               pdata = max8903_parse_dt_data(dev);
-
-       if (!pdata) {
-               dev_err(dev, "No platform data.\n");
-               return -EINVAL;
-       }
-
-       pdev->dev.platform_data = pdata;
-       data->pdata = pdata;
-       data->dev = dev;
-       platform_set_drvdata(pdev, data);
-
-       if (pdata->dc_valid == false && pdata->usb_valid == false) {
-               dev_err(dev, "No valid power sources.\n");
-               return -EINVAL;
-       }
-
-       ret = max8903_setup_gpios(pdev);
-       if (ret)
-               return ret;
-
-       data->psy_desc.name = "max8903_charger";
-       data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
-                       ((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
-                        POWER_SUPPLY_TYPE_BATTERY);
-       data->psy_desc.get_property = max8903_get_property;
-       data->psy_desc.properties = max8903_charger_props;
-       data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
-
-       psy_cfg.of_node = dev->of_node;
-       psy_cfg.drv_data = data;
-
-       data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
-       if (IS_ERR(data->psy)) {
-               dev_err(dev, "failed: power supply register.\n");
-               return PTR_ERR(data->psy);
-       }
-
-       if (pdata->dc_valid) {
-               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok),
-                                       NULL, max8903_dcin,
-                                       IRQF_TRIGGER_FALLING |
-                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-                                       "MAX8903 DC IN", data);
-               if (ret) {
-                       dev_err(dev, "Cannot request irq %d for DC (%d)\n",
-                                       gpio_to_irq(pdata->dok), ret);
-                       return ret;
-               }
-       }
-
-       if (pdata->usb_valid) {
-               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok),
-                                       NULL, max8903_usbin,
-                                       IRQF_TRIGGER_FALLING |
-                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-                                       "MAX8903 USB IN", data);
-               if (ret) {
-                       dev_err(dev, "Cannot request irq %d for USB (%d)\n",
-                                       gpio_to_irq(pdata->uok), ret);
-                       return ret;
-               }
-       }
-
-       if (gpio_is_valid(pdata->flt)) {
-               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
-                                       NULL, max8903_fault,
-                                       IRQF_TRIGGER_FALLING |
-                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-                                       "MAX8903 Fault", data);
-               if (ret) {
-                       dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
-                                       gpio_to_irq(pdata->flt), ret);
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-
-static const struct of_device_id max8903_match_ids[] = {
-       { .compatible = "maxim,max8903", },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, max8903_match_ids);
-
-static struct platform_driver max8903_driver = {
-       .probe  = max8903_probe,
-       .driver = {
-               .name   = "max8903-charger",
-               .of_match_table = max8903_match_ids
-       },
-};
-
-module_platform_driver(max8903_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("MAX8903 Charger Driver");
-MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
-MODULE_ALIAS("platform:max8903-charger");
diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c
deleted file mode 100644 (file)
index 3b94620..0000000
+++ /dev/null
@@ -1,596 +0,0 @@
-/*
- * Battery driver for Maxim MAX8925
- *
- * Copyright (c) 2009-2010 Marvell International Ltd.
- *     Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/max8925.h>
-
-/* registers in GPM */
-#define MAX8925_OUT5VEN                        0x54
-#define MAX8925_OUT3VEN                        0x58
-#define MAX8925_CHG_CNTL1              0x7c
-
-/* bits definition */
-#define MAX8925_CHG_STAT_VSYSLOW       (1 << 0)
-#define MAX8925_CHG_STAT_MODE_MASK     (3 << 2)
-#define MAX8925_CHG_STAT_EN_MASK       (1 << 4)
-#define MAX8925_CHG_MBDET              (1 << 1)
-#define MAX8925_CHG_AC_RANGE_MASK      (3 << 6)
-
-/* registers in ADC */
-#define MAX8925_ADC_RES_CNFG1          0x06
-#define MAX8925_ADC_AVG_CNFG1          0x07
-#define MAX8925_ADC_ACQ_CNFG1          0x08
-#define MAX8925_ADC_ACQ_CNFG2          0x09
-/* 2 bytes registers in below. MSB is 1st, LSB is 2nd. */
-#define MAX8925_ADC_AUX2               0x62
-#define MAX8925_ADC_VCHG               0x64
-#define MAX8925_ADC_VBBATT             0x66
-#define MAX8925_ADC_VMBATT             0x68
-#define MAX8925_ADC_ISNS               0x6a
-#define MAX8925_ADC_THM                        0x6c
-#define MAX8925_ADC_TDIE               0x6e
-#define MAX8925_CMD_AUX2               0xc8
-#define MAX8925_CMD_VCHG               0xd0
-#define MAX8925_CMD_VBBATT             0xd8
-#define MAX8925_CMD_VMBATT             0xe0
-#define MAX8925_CMD_ISNS               0xe8
-#define MAX8925_CMD_THM                        0xf0
-#define MAX8925_CMD_TDIE               0xf8
-
-enum {
-       MEASURE_AUX2,
-       MEASURE_VCHG,
-       MEASURE_VBBATT,
-       MEASURE_VMBATT,
-       MEASURE_ISNS,
-       MEASURE_THM,
-       MEASURE_TDIE,
-       MEASURE_MAX,
-};
-
-struct max8925_power_info {
-       struct max8925_chip     *chip;
-       struct i2c_client       *gpm;
-       struct i2c_client       *adc;
-
-       struct power_supply     *ac;
-       struct power_supply     *usb;
-       struct power_supply     *battery;
-       int                     irq_base;
-       unsigned                ac_online:1;
-       unsigned                usb_online:1;
-       unsigned                bat_online:1;
-       unsigned                chg_mode:2;
-       unsigned                batt_detect:1;  /* detecing MB by ID pin */
-       unsigned                topoff_threshold:2;
-       unsigned                fast_charge:3;
-       unsigned                no_temp_support:1;
-       unsigned                no_insert_detect:1;
-
-       int (*set_charger) (int);
-};
-
-static int __set_charger(struct max8925_power_info *info, int enable)
-{
-       struct max8925_chip *chip = info->chip;
-       if (enable) {
-               /* enable charger in platform */
-               if (info->set_charger)
-                       info->set_charger(1);
-               /* enable charger */
-               max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 0);
-       } else {
-               /* disable charge */
-               max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
-               if (info->set_charger)
-                       info->set_charger(0);
-       }
-       dev_dbg(chip->dev, "%s\n", (enable) ? "Enable charger"
-               : "Disable charger");
-       return 0;
-}
-
-static irqreturn_t max8925_charger_handler(int irq, void *data)
-{
-       struct max8925_power_info *info = (struct max8925_power_info *)data;
-       struct max8925_chip *chip = info->chip;
-
-       switch (irq - chip->irq_base) {
-       case MAX8925_IRQ_VCHG_DC_R:
-               info->ac_online = 1;
-               __set_charger(info, 1);
-               dev_dbg(chip->dev, "Adapter inserted\n");
-               break;
-       case MAX8925_IRQ_VCHG_DC_F:
-               info->ac_online = 0;
-               __set_charger(info, 0);
-               dev_dbg(chip->dev, "Adapter removed\n");
-               break;
-       case MAX8925_IRQ_VCHG_THM_OK_F:
-               /* Battery is not ready yet */
-               dev_dbg(chip->dev, "Battery temperature is out of range\n");
-       case MAX8925_IRQ_VCHG_DC_OVP:
-               dev_dbg(chip->dev, "Error detection\n");
-               __set_charger(info, 0);
-               break;
-       case MAX8925_IRQ_VCHG_THM_OK_R:
-               /* Battery is ready now */
-               dev_dbg(chip->dev, "Battery temperature is in range\n");
-               break;
-       case MAX8925_IRQ_VCHG_SYSLOW_R:
-               /* VSYS is low */
-               dev_info(chip->dev, "Sys power is too low\n");
-               break;
-       case MAX8925_IRQ_VCHG_SYSLOW_F:
-               dev_dbg(chip->dev, "Sys power is above low threshold\n");
-               break;
-       case MAX8925_IRQ_VCHG_DONE:
-               __set_charger(info, 0);
-               dev_dbg(chip->dev, "Charging is done\n");
-               break;
-       case MAX8925_IRQ_VCHG_TOPOFF:
-               dev_dbg(chip->dev, "Charging in top-off mode\n");
-               break;
-       case MAX8925_IRQ_VCHG_TMR_FAULT:
-               __set_charger(info, 0);
-               dev_dbg(chip->dev, "Safe timer is expired\n");
-               break;
-       case MAX8925_IRQ_VCHG_RST:
-               __set_charger(info, 0);
-               dev_dbg(chip->dev, "Charger is reset\n");
-               break;
-       }
-       return IRQ_HANDLED;
-}
-
-static int start_measure(struct max8925_power_info *info, int type)
-{
-       unsigned char buf[2] = {0, 0};
-       int meas_cmd;
-       int meas_reg = 0, ret;
-
-       switch (type) {
-       case MEASURE_VCHG:
-               meas_cmd = MAX8925_CMD_VCHG;
-               meas_reg = MAX8925_ADC_VCHG;
-               break;
-       case MEASURE_VBBATT:
-               meas_cmd = MAX8925_CMD_VBBATT;
-               meas_reg = MAX8925_ADC_VBBATT;
-               break;
-       case MEASURE_VMBATT:
-               meas_cmd = MAX8925_CMD_VMBATT;
-               meas_reg = MAX8925_ADC_VMBATT;
-               break;
-       case MEASURE_ISNS:
-               meas_cmd = MAX8925_CMD_ISNS;
-               meas_reg = MAX8925_ADC_ISNS;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       max8925_reg_write(info->adc, meas_cmd, 0);
-       max8925_bulk_read(info->adc, meas_reg, 2, buf);
-       ret = ((buf[0]<<8) | buf[1]) >> 4;
-
-       return ret;
-}
-
-static int max8925_ac_get_prop(struct power_supply *psy,
-                              enum power_supply_property psp,
-                              union power_supply_propval *val)
-{
-       struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = info->ac_online;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (info->ac_online) {
-                       ret = start_measure(info, MEASURE_VCHG);
-                       if (ret >= 0) {
-                               val->intval = ret * 2000;       /* unit is uV */
-                               goto out;
-                       }
-               }
-               ret = -ENODATA;
-               break;
-       default:
-               ret = -ENODEV;
-               break;
-       }
-out:
-       return ret;
-}
-
-static enum power_supply_property max8925_ac_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-};
-
-static int max8925_usb_get_prop(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = info->usb_online;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (info->usb_online) {
-                       ret = start_measure(info, MEASURE_VCHG);
-                       if (ret >= 0) {
-                               val->intval = ret * 2000;       /* unit is uV */
-                               goto out;
-                       }
-               }
-               ret = -ENODATA;
-               break;
-       default:
-               ret = -ENODEV;
-               break;
-       }
-out:
-       return ret;
-}
-
-static enum power_supply_property max8925_usb_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-};
-
-static int max8925_bat_get_prop(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = info->bat_online;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (info->bat_online) {
-                       ret = start_measure(info, MEASURE_VMBATT);
-                       if (ret >= 0) {
-                               val->intval = ret * 2000;       /* unit is uV */
-                               ret = 0;
-                               break;
-                       }
-               }
-               ret = -ENODATA;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               if (info->bat_online) {
-                       ret = start_measure(info, MEASURE_ISNS);
-                       if (ret >= 0) {
-                               /* assume r_sns is 0.02 */
-                               ret = ((ret * 6250) - 3125) /* uA */;
-                               val->intval = 0;
-                               if (ret > 0)
-                                       val->intval = ret; /* unit is mA */
-                               ret = 0;
-                               break;
-                       }
-               }
-               ret = -ENODATA;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               if (!info->bat_online) {
-                       ret = -ENODATA;
-                       break;
-               }
-               ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
-               ret = (ret & MAX8925_CHG_STAT_MODE_MASK) >> 2;
-               switch (ret) {
-               case 1:
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
-                       break;
-               case 0:
-               case 2:
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-                       break;
-               case 3:
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
-                       break;
-               }
-               ret = 0;
-               break;
-       case POWER_SUPPLY_PROP_STATUS:
-               if (!info->bat_online) {
-                       ret = -ENODATA;
-                       break;
-               }
-               ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
-               if (info->usb_online || info->ac_online) {
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-                       if (ret & MAX8925_CHG_STAT_EN_MASK)
-                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               } else
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               ret = 0;
-               break;
-       default:
-               ret = -ENODEV;
-               break;
-       }
-       return ret;
-}
-
-static enum power_supply_property max8925_battery_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_STATUS,
-};
-
-static const struct power_supply_desc ac_desc = {
-       .name           = "max8925-ac",
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-       .properties     = max8925_ac_props,
-       .num_properties = ARRAY_SIZE(max8925_ac_props),
-       .get_property   = max8925_ac_get_prop,
-};
-
-static const struct power_supply_desc usb_desc = {
-       .name           = "max8925-usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .properties     = max8925_usb_props,
-       .num_properties = ARRAY_SIZE(max8925_usb_props),
-       .get_property   = max8925_usb_get_prop,
-};
-
-static const struct power_supply_desc battery_desc = {
-       .name           = "max8925-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = max8925_battery_props,
-       .num_properties = ARRAY_SIZE(max8925_battery_props),
-       .get_property   = max8925_bat_get_prop,
-};
-
-#define REQUEST_IRQ(_irq, _name)                                       \
-do {                                                                   \
-       ret = request_threaded_irq(chip->irq_base + _irq, NULL,         \
-                                   max8925_charger_handler,            \
-                                   IRQF_ONESHOT, _name, info);         \
-       if (ret)                                                        \
-               dev_err(chip->dev, "Failed to request IRQ #%d: %d\n",   \
-                       _irq, ret);                                     \
-} while (0)
-
-static int max8925_init_charger(struct max8925_chip *chip,
-                                         struct max8925_power_info *info)
-{
-       int ret;
-
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
-       if (!info->no_insert_detect) {
-               REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
-               REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
-       }
-       if (!info->no_temp_support) {
-               REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
-               REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
-       }
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");
-
-       info->usb_online = 0;
-       info->bat_online = 0;
-
-       /* check for power - can miss interrupt at boot time */
-       if (start_measure(info, MEASURE_VCHG) * 2000 > 500000)
-               info->ac_online = 1;
-       else
-               info->ac_online = 0;
-
-       ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
-       if (ret >= 0) {
-               /*
-                * If battery detection is enabled, ID pin of battery is
-                * connected to MBDET pin of MAX8925. It could be used to
-                * detect battery presence.
-                * Otherwise, we have to assume that battery is always on.
-                */
-               if (info->batt_detect)
-                       info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1;
-               else
-                       info->bat_online = 1;
-               if (ret & MAX8925_CHG_AC_RANGE_MASK)
-                       info->ac_online = 1;
-               else
-                       info->ac_online = 0;
-       }
-       /* disable charge */
-       max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
-       /* set charging current in charge topoff mode */
-       max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5,
-                        info->topoff_threshold << 5);
-       /* set charing current in fast charge mode */
-       max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge);
-
-       return 0;
-}
-
-static int max8925_deinit_charger(struct max8925_power_info *info)
-{
-       struct max8925_chip *chip = info->chip;
-       int irq;
-
-       irq = chip->irq_base + MAX8925_IRQ_VCHG_DC_OVP;
-       for (; irq <= chip->irq_base + MAX8925_IRQ_VCHG_TMR_FAULT; irq++)
-               free_irq(irq, info);
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static struct max8925_power_pdata *
-max8925_power_dt_init(struct platform_device *pdev)
-{
-       struct device_node *nproot = pdev->dev.parent->of_node;
-       struct device_node *np;
-       int batt_detect;
-       int topoff_threshold;
-       int fast_charge;
-       int no_temp_support;
-       int no_insert_detect;
-       struct max8925_power_pdata *pdata;
-
-       if (!nproot)
-               return pdev->dev.platform_data;
-
-       np = of_get_child_by_name(nproot, "charger");
-       if (!np) {
-               dev_err(&pdev->dev, "failed to find charger node\n");
-               return NULL;
-       }
-
-       pdata = devm_kzalloc(&pdev->dev,
-                       sizeof(struct max8925_power_pdata),
-                       GFP_KERNEL);
-       if (!pdata)
-               goto ret;
-
-       of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
-       of_property_read_u32(np, "batt-detect", &batt_detect);
-       of_property_read_u32(np, "fast-charge", &fast_charge);
-       of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
-       of_property_read_u32(np, "no-temp-support", &no_temp_support);
-
-       pdata->batt_detect = batt_detect;
-       pdata->fast_charge = fast_charge;
-       pdata->topoff_threshold = topoff_threshold;
-       pdata->no_insert_detect = no_insert_detect;
-       pdata->no_temp_support = no_temp_support;
-
-ret:
-       of_node_put(np);
-       return pdata;
-}
-#else
-static struct max8925_power_pdata *
-max8925_power_dt_init(struct platform_device *pdev)
-{
-       return pdev->dev.platform_data;
-}
-#endif
-
-static int max8925_power_probe(struct platform_device *pdev)
-{
-       struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
-       struct power_supply_config psy_cfg = {}; /* Only for ac and usb */
-       struct max8925_power_pdata *pdata = NULL;
-       struct max8925_power_info *info;
-       int ret;
-
-       pdata = max8925_power_dt_init(pdev);
-       if (!pdata) {
-               dev_err(&pdev->dev, "platform data isn't assigned to "
-                       "power supply\n");
-               return -EINVAL;
-       }
-
-       info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
-                               GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-       info->chip = chip;
-       info->gpm = chip->i2c;
-       info->adc = chip->adc;
-       platform_set_drvdata(pdev, info);
-
-       psy_cfg.supplied_to = pdata->supplied_to;
-       psy_cfg.num_supplicants = pdata->num_supplicants;
-
-       info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
-       if (IS_ERR(info->ac)) {
-               ret = PTR_ERR(info->ac);
-               goto out;
-       }
-       info->ac->dev.parent = &pdev->dev;
-
-       info->usb = power_supply_register(&pdev->dev, &usb_desc, &psy_cfg);
-       if (IS_ERR(info->usb)) {
-               ret = PTR_ERR(info->usb);
-               goto out_unregister_ac;
-       }
-       info->usb->dev.parent = &pdev->dev;
-
-       info->battery = power_supply_register(&pdev->dev, &battery_desc, NULL);
-       if (IS_ERR(info->battery)) {
-               ret = PTR_ERR(info->battery);
-               goto out_unregister_usb;
-       }
-       info->battery->dev.parent = &pdev->dev;
-
-       info->batt_detect = pdata->batt_detect;
-       info->topoff_threshold = pdata->topoff_threshold;
-       info->fast_charge = pdata->fast_charge;
-       info->set_charger = pdata->set_charger;
-       info->no_temp_support = pdata->no_temp_support;
-       info->no_insert_detect = pdata->no_insert_detect;
-
-       max8925_init_charger(chip, info);
-       return 0;
-out_unregister_usb:
-       power_supply_unregister(info->usb);
-out_unregister_ac:
-       power_supply_unregister(info->ac);
-out:
-       return ret;
-}
-
-static int max8925_power_remove(struct platform_device *pdev)
-{
-       struct max8925_power_info *info = platform_get_drvdata(pdev);
-
-       if (info) {
-               power_supply_unregister(info->ac);
-               power_supply_unregister(info->usb);
-               power_supply_unregister(info->battery);
-               max8925_deinit_charger(info);
-       }
-       return 0;
-}
-
-static struct platform_driver max8925_power_driver = {
-       .probe  = max8925_power_probe,
-       .remove = max8925_power_remove,
-       .driver = {
-               .name   = "max8925-power",
-       },
-};
-
-module_platform_driver(max8925_power_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Power supply driver for MAX8925");
-MODULE_ALIAS("platform:max8925-power");
diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c
deleted file mode 100644 (file)
index 0b2eab5..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * max8997_charger.c - Power supply consumer driver for the Maxim 8997/8966
- *
- *  Copyright (C) 2011 Samsung Electronics
- *  MyungJoo Ham <myungjoo.ham@samsung.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/max8997.h>
-#include <linux/mfd/max8997-private.h>
-
-struct charger_data {
-       struct device *dev;
-       struct max8997_dev *iodev;
-       struct power_supply *battery;
-};
-
-static enum power_supply_property max8997_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS, /* "FULL" or "NOT FULL" only. */
-       POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */
-       POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */
-};
-
-/* Note that the charger control is done by a current regulator "CHARGER" */
-static int max8997_battery_get_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               union power_supply_propval *val)
-{
-       struct charger_data *charger = power_supply_get_drvdata(psy);
-       struct i2c_client *i2c = charger->iodev->i2c;
-       int ret;
-       u8 reg;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = 0;
-               ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
-               if (ret)
-                       return ret;
-               if ((reg & (1 << 0)) == 0x1)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = 0;
-               ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
-               if (ret)
-                       return ret;
-               if ((reg & (1 << 2)) == 0x0)
-                       val->intval = 1;
-
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = 0;
-               ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
-               if (ret)
-                       return ret;
-               /* DCINOK */
-               if (reg & (1 << 1))
-                       val->intval = 1;
-
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static const struct power_supply_desc max8997_battery_desc = {
-       .name           = "max8997_pmic",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property   = max8997_battery_get_property,
-       .properties     = max8997_battery_props,
-       .num_properties = ARRAY_SIZE(max8997_battery_props),
-};
-
-static int max8997_battery_probe(struct platform_device *pdev)
-{
-       int ret = 0;
-       struct charger_data *charger;
-       struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
-       struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev);
-       struct power_supply_config psy_cfg = {};
-
-       if (!pdata)
-               return -EINVAL;
-
-       if (pdata->eoc_mA) {
-               int val = (pdata->eoc_mA - 50) / 10;
-               if (val < 0)
-                       val = 0;
-               if (val > 0xf)
-                       val = 0xf;
-
-               ret = max8997_update_reg(iodev->i2c,
-                               MAX8997_REG_MBCCTRL5, val, 0xf);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "Cannot use i2c bus.\n");
-                       return ret;
-               }
-       }
-
-       switch (pdata->timeout) {
-       case 5:
-               ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
-                               0x2 << 4, 0x7 << 4);
-               break;
-       case 6:
-               ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
-                               0x3 << 4, 0x7 << 4);
-               break;
-       case 7:
-               ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
-                               0x4 << 4, 0x7 << 4);
-               break;
-       case 0:
-               ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
-                               0x7 << 4, 0x7 << 4);
-               break;
-       default:
-               dev_err(&pdev->dev, "incorrect timeout value (%d)\n",
-                               pdata->timeout);
-               return -EINVAL;
-       }
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Cannot use i2c bus.\n");
-               return ret;
-       }
-
-       charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),
-                               GFP_KERNEL);
-       if (charger == NULL) {
-               dev_err(&pdev->dev, "Cannot allocate memory.\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(pdev, charger);
-
-
-       charger->dev = &pdev->dev;
-       charger->iodev = iodev;
-
-       psy_cfg.drv_data = charger;
-
-       charger->battery = power_supply_register(&pdev->dev,
-                                                &max8997_battery_desc,
-                                                &psy_cfg);
-       if (IS_ERR(charger->battery)) {
-               dev_err(&pdev->dev, "failed: power supply register\n");
-               return PTR_ERR(charger->battery);
-       }
-
-       return 0;
-}
-
-static int max8997_battery_remove(struct platform_device *pdev)
-{
-       struct charger_data *charger = platform_get_drvdata(pdev);
-
-       power_supply_unregister(charger->battery);
-       return 0;
-}
-
-static const struct platform_device_id max8997_battery_id[] = {
-       { "max8997-battery", 0 },
-       { }
-};
-
-static struct platform_driver max8997_battery_driver = {
-       .driver = {
-               .name = "max8997-battery",
-       },
-       .probe = max8997_battery_probe,
-       .remove = max8997_battery_remove,
-       .id_table = max8997_battery_id,
-};
-
-static int __init max8997_battery_init(void)
-{
-       return platform_driver_register(&max8997_battery_driver);
-}
-subsys_initcall(max8997_battery_init);
-
-static void __exit max8997_battery_cleanup(void)
-{
-       platform_driver_unregister(&max8997_battery_driver);
-}
-module_exit(max8997_battery_cleanup);
-
-MODULE_DESCRIPTION("MAXIM 8997/8966 battery control driver");
-MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c
deleted file mode 100644 (file)
index b64cf0f..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * max8998_charger.c - Power supply consumer driver for the Maxim 8998/LP3974
- *
- *  Copyright (C) 2009-2010 Samsung Electronics
- *  MyungJoo Ham <myungjoo.ham@samsung.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/max8998.h>
-#include <linux/mfd/max8998-private.h>
-
-struct max8998_battery_data {
-       struct device *dev;
-       struct max8998_dev *iodev;
-       struct power_supply *battery;
-};
-
-static enum power_supply_property max8998_battery_props[] = {
-       POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */
-       POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */
-};
-
-/* Note that the charger control is done by a current regulator "CHARGER" */
-static int max8998_battery_get_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               union power_supply_propval *val)
-{
-       struct max8998_battery_data *max8998 = power_supply_get_drvdata(psy);
-       struct i2c_client *i2c = max8998->iodev->i2c;
-       int ret;
-       u8 reg;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-               ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, &reg);
-               if (ret)
-                       return ret;
-               if (reg & (1 << 4))
-                       val->intval = 0;
-               else
-                       val->intval = 1;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, &reg);
-               if (ret)
-                       return ret;
-               if (reg & (1 << 3))
-                       val->intval = 0;
-               else
-                       val->intval = 1;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static const struct power_supply_desc max8998_battery_desc = {
-       .name           = "max8998_pmic",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property   = max8998_battery_get_property,
-       .properties     = max8998_battery_props,
-       .num_properties = ARRAY_SIZE(max8998_battery_props),
-};
-
-static int max8998_battery_probe(struct platform_device *pdev)
-{
-       struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent);
-       struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev);
-       struct power_supply_config psy_cfg = {};
-       struct max8998_battery_data *max8998;
-       struct i2c_client *i2c;
-       int ret = 0;
-
-       if (!pdata) {
-               dev_err(pdev->dev.parent, "No platform init data supplied\n");
-               return -ENODEV;
-       }
-
-       max8998 = devm_kzalloc(&pdev->dev, sizeof(struct max8998_battery_data),
-                               GFP_KERNEL);
-       if (!max8998)
-               return -ENOMEM;
-
-       max8998->dev = &pdev->dev;
-       max8998->iodev = iodev;
-       platform_set_drvdata(pdev, max8998);
-       i2c = max8998->iodev->i2c;
-
-       /* Setup "End of Charge" */
-       /* If EOC value equals 0,
-        * remain value set from bootloader or default value */
-       if (pdata->eoc >= 10 && pdata->eoc <= 45) {
-               max8998_update_reg(i2c, MAX8998_REG_CHGR1,
-                               (pdata->eoc / 5 - 2) << 5, 0x7 << 5);
-       } else if (pdata->eoc == 0) {
-               dev_dbg(max8998->dev,
-                       "EOC value not set: leave it unchanged.\n");
-       } else {
-               dev_err(max8998->dev, "Invalid EOC value\n");
-               return -EINVAL;
-       }
-
-       /* Setup Charge Restart Level */
-       switch (pdata->restart) {
-       case 100:
-               max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x1 << 3, 0x3 << 3);
-               break;
-       case 150:
-               max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x0 << 3, 0x3 << 3);
-               break;
-       case 200:
-               max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x2 << 3, 0x3 << 3);
-               break;
-       case -1:
-               max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x3 << 3, 0x3 << 3);
-               break;
-       case 0:
-               dev_dbg(max8998->dev,
-                       "Restart Level not set: leave it unchanged.\n");
-               break;
-       default:
-               dev_err(max8998->dev, "Invalid Restart Level\n");
-               return -EINVAL;
-       }
-
-       /* Setup Charge Full Timeout */
-       switch (pdata->timeout) {
-       case 5:
-               max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x0 << 4, 0x3 << 4);
-               break;
-       case 6:
-               max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x1 << 4, 0x3 << 4);
-               break;
-       case 7:
-               max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x2 << 4, 0x3 << 4);
-               break;
-       case -1:
-               max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x3 << 4, 0x3 << 4);
-               break;
-       case 0:
-               dev_dbg(max8998->dev,
-                       "Full Timeout not set: leave it unchanged.\n");
-               break;
-       default:
-               dev_err(max8998->dev, "Invalid Full Timeout value\n");
-               return -EINVAL;
-       }
-
-       psy_cfg.drv_data = max8998;
-
-       max8998->battery = devm_power_supply_register(max8998->dev,
-                                                     &max8998_battery_desc,
-                                                     &psy_cfg);
-       if (IS_ERR(max8998->battery)) {
-               ret = PTR_ERR(max8998->battery);
-               dev_err(max8998->dev, "failed: power supply register: %d\n",
-                       ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static const struct platform_device_id max8998_battery_id[] = {
-       { "max8998-battery", TYPE_MAX8998 },
-       { }
-};
-
-static struct platform_driver max8998_battery_driver = {
-       .driver = {
-               .name = "max8998-battery",
-       },
-       .probe = max8998_battery_probe,
-       .id_table = max8998_battery_id,
-};
-
-module_platform_driver(max8998_battery_driver);
-
-MODULE_DESCRIPTION("MAXIM 8998 battery control driver");
-MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:max8998-battery");
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c
deleted file mode 100644 (file)
index 9e29b13..0000000
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
- * Battery driver for One Laptop Per Child board.
- *
- *     Copyright Â© 2006-2010  David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/err.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/jiffies.h>
-#include <linux/sched.h>
-#include <linux/olpc-ec.h>
-#include <asm/olpc.h>
-
-
-#define EC_BAT_VOLTAGE 0x10    /* uint16_t,    *9.76/32,    mV   */
-#define EC_BAT_CURRENT 0x11    /* int16_t,     *15.625/120, mA   */
-#define EC_BAT_ACR     0x12    /* int16_t,     *6250/15,    ÂµAh  */
-#define EC_BAT_TEMP    0x13    /* uint16_t,    *100/256,   Â°C  */
-#define EC_AMB_TEMP    0x14    /* uint16_t,    *100/256,   Â°C  */
-#define EC_BAT_STATUS  0x15    /* uint8_t,     bitmask */
-#define EC_BAT_SOC     0x16    /* uint8_t,     percentage */
-#define EC_BAT_SERIAL  0x17    /* uint8_t[6] */
-#define EC_BAT_EEPROM  0x18    /* uint8_t adr as input, uint8_t output */
-#define EC_BAT_ERRCODE 0x1f    /* uint8_t,     bitmask */
-
-#define BAT_STAT_PRESENT       0x01
-#define BAT_STAT_FULL          0x02
-#define BAT_STAT_LOW           0x04
-#define BAT_STAT_DESTROY       0x08
-#define BAT_STAT_AC            0x10
-#define BAT_STAT_CHARGING      0x20
-#define BAT_STAT_DISCHARGING   0x40
-#define BAT_STAT_TRICKLE       0x80
-
-#define BAT_ERR_INFOFAIL       0x02
-#define BAT_ERR_OVERVOLTAGE    0x04
-#define BAT_ERR_OVERTEMP       0x05
-#define BAT_ERR_GAUGESTOP      0x06
-#define BAT_ERR_OUT_OF_CONTROL 0x07
-#define BAT_ERR_ID_FAIL                0x09
-#define BAT_ERR_ACR_FAIL       0x10
-
-#define BAT_ADDR_MFR_TYPE      0x5F
-
-/*********************************************************************
- *             Power
- *********************************************************************/
-
-static int olpc_ac_get_prop(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       int ret = 0;
-       uint8_t status;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
-               if (ret)
-                       return ret;
-
-               val->intval = !!(status & BAT_STAT_AC);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static enum power_supply_property olpc_ac_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static const struct power_supply_desc olpc_ac_desc = {
-       .name = "olpc-ac",
-       .type = POWER_SUPPLY_TYPE_MAINS,
-       .properties = olpc_ac_props,
-       .num_properties = ARRAY_SIZE(olpc_ac_props),
-       .get_property = olpc_ac_get_prop,
-};
-
-static struct power_supply *olpc_ac;
-
-static char bat_serial[17]; /* Ick */
-
-static int olpc_bat_get_status(union power_supply_propval *val, uint8_t ec_byte)
-{
-       if (olpc_platform_info.ecver > 0x44) {
-               if (ec_byte & (BAT_STAT_CHARGING | BAT_STAT_TRICKLE))
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else if (ec_byte & BAT_STAT_DISCHARGING)
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (ec_byte & BAT_STAT_FULL)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else /* er,... */
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       } else {
-               /* Older EC didn't report charge/discharge bits */
-               if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (ec_byte & BAT_STAT_FULL)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else /* Not _necessarily_ true but EC doesn't tell all yet */
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-       }
-
-       return 0;
-}
-
-static int olpc_bat_get_health(union power_supply_propval *val)
-{
-       uint8_t ec_byte;
-       int ret;
-
-       ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
-       if (ret)
-               return ret;
-
-       switch (ec_byte) {
-       case 0:
-               val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-
-       case BAT_ERR_OVERTEMP:
-               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               break;
-
-       case BAT_ERR_OVERVOLTAGE:
-               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               break;
-
-       case BAT_ERR_INFOFAIL:
-       case BAT_ERR_OUT_OF_CONTROL:
-       case BAT_ERR_ID_FAIL:
-       case BAT_ERR_ACR_FAIL:
-               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               break;
-
-       default:
-               /* Eep. We don't know this failure code */
-               ret = -EIO;
-       }
-
-       return ret;
-}
-
-static int olpc_bat_get_mfr(union power_supply_propval *val)
-{
-       uint8_t ec_byte;
-       int ret;
-
-       ec_byte = BAT_ADDR_MFR_TYPE;
-       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
-       if (ret)
-               return ret;
-
-       switch (ec_byte >> 4) {
-       case 1:
-               val->strval = "Gold Peak";
-               break;
-       case 2:
-               val->strval = "BYD";
-               break;
-       default:
-               val->strval = "Unknown";
-               break;
-       }
-
-       return ret;
-}
-
-static int olpc_bat_get_tech(union power_supply_propval *val)
-{
-       uint8_t ec_byte;
-       int ret;
-
-       ec_byte = BAT_ADDR_MFR_TYPE;
-       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
-       if (ret)
-               return ret;
-
-       switch (ec_byte & 0xf) {
-       case 1:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
-               break;
-       case 2:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe;
-               break;
-       default:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
-               break;
-       }
-
-       return ret;
-}
-
-static int olpc_bat_get_charge_full_design(union power_supply_propval *val)
-{
-       uint8_t ec_byte;
-       union power_supply_propval tech;
-       int ret, mfr;
-
-       ret = olpc_bat_get_tech(&tech);
-       if (ret)
-               return ret;
-
-       ec_byte = BAT_ADDR_MFR_TYPE;
-       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
-       if (ret)
-               return ret;
-
-       mfr = ec_byte >> 4;
-
-       switch (tech.intval) {
-       case POWER_SUPPLY_TECHNOLOGY_NiMH:
-               switch (mfr) {
-               case 1: /* Gold Peak */
-                       val->intval = 3000000*.8;
-                       break;
-               default:
-                       return -EIO;
-               }
-               break;
-
-       case POWER_SUPPLY_TECHNOLOGY_LiFe:
-               switch (mfr) {
-               case 1: /* Gold Peak, fall through */
-               case 2: /* BYD */
-                       val->intval = 2800000;
-                       break;
-               default:
-                       return -EIO;
-               }
-               break;
-
-       default:
-               return -EIO;
-       }
-
-       return ret;
-}
-
-static int olpc_bat_get_charge_now(union power_supply_propval *val)
-{
-       uint8_t soc;
-       union power_supply_propval full;
-       int ret;
-
-       ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &soc, 1);
-       if (ret)
-               return ret;
-
-       ret = olpc_bat_get_charge_full_design(&full);
-       if (ret)
-               return ret;
-
-       val->intval = soc * (full.intval / 100);
-       return 0;
-}
-
-static int olpc_bat_get_voltage_max_design(union power_supply_propval *val)
-{
-       uint8_t ec_byte;
-       union power_supply_propval tech;
-       int mfr;
-       int ret;
-
-       ret = olpc_bat_get_tech(&tech);
-       if (ret)
-               return ret;
-
-       ec_byte = BAT_ADDR_MFR_TYPE;
-       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
-       if (ret)
-               return ret;
-
-       mfr = ec_byte >> 4;
-
-       switch (tech.intval) {
-       case POWER_SUPPLY_TECHNOLOGY_NiMH:
-               switch (mfr) {
-               case 1: /* Gold Peak */
-                       val->intval = 6000000;
-                       break;
-               default:
-                       return -EIO;
-               }
-               break;
-
-       case POWER_SUPPLY_TECHNOLOGY_LiFe:
-               switch (mfr) {
-               case 1: /* Gold Peak */
-                       val->intval = 6400000;
-                       break;
-               case 2: /* BYD */
-                       val->intval = 6500000;
-                       break;
-               default:
-                       return -EIO;
-               }
-               break;
-
-       default:
-               return -EIO;
-       }
-
-       return ret;
-}
-
-/*********************************************************************
- *             Battery properties
- *********************************************************************/
-static int olpc_bat_get_property(struct power_supply *psy,
-                                enum power_supply_property psp,
-                                union power_supply_propval *val)
-{
-       int ret = 0;
-       __be16 ec_word;
-       uint8_t ec_byte;
-       __be64 ser_buf;
-
-       ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1);
-       if (ret)
-               return ret;
-
-       /* Theoretically there's a race here -- the battery could be
-          removed immediately after we check whether it's present, and
-          then we query for some other property of the now-absent battery.
-          It doesn't matter though -- the EC will return the last-known
-          information, and it's as if we just ran that _little_ bit faster
-          and managed to read it out before the battery went away. */
-       if (!(ec_byte & (BAT_STAT_PRESENT | BAT_STAT_TRICKLE)) &&
-                       psp != POWER_SUPPLY_PROP_PRESENT)
-               return -ENODEV;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = olpc_bat_get_status(val, ec_byte);
-               if (ret)
-                       return ret;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               if (ec_byte & BAT_STAT_TRICKLE)
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-               else if (ec_byte & BAT_STAT_CHARGING)
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
-               else
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = !!(ec_byte & (BAT_STAT_PRESENT |
-                                           BAT_STAT_TRICKLE));
-               break;
-
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (ec_byte & BAT_STAT_DESTROY)
-                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
-               else {
-                       ret = olpc_bat_get_health(val);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               ret = olpc_bat_get_mfr(val);
-               if (ret)
-                       return ret;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               ret = olpc_bat_get_tech(val);
-               if (ret)
-                       return ret;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2);
-               if (ret)
-                       return ret;
-
-               val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2);
-               if (ret)
-                       return ret;
-
-               val->intval = (s16)be16_to_cpu(ec_word) * 15625L / 120;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1);
-               if (ret)
-                       return ret;
-               val->intval = ec_byte;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-               if (ec_byte & BAT_STAT_FULL)
-                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-               else if (ec_byte & BAT_STAT_LOW)
-                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-               else
-                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               ret = olpc_bat_get_charge_full_design(val);
-               if (ret)
-                       return ret;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               ret = olpc_bat_get_charge_now(val);
-               if (ret)
-                       return ret;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2);
-               if (ret)
-                       return ret;
-
-               val->intval = (s16)be16_to_cpu(ec_word) * 100 / 256;
-               break;
-       case POWER_SUPPLY_PROP_TEMP_AMBIENT:
-               ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
-               if (ret)
-                       return ret;
-
-               val->intval = (int)be16_to_cpu(ec_word) * 100 / 256;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
-               ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2);
-               if (ret)
-                       return ret;
-
-               val->intval = (s16)be16_to_cpu(ec_word) * 6250 / 15;
-               break;
-       case POWER_SUPPLY_PROP_SERIAL_NUMBER:
-               ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8);
-               if (ret)
-                       return ret;
-
-               sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf));
-               val->strval = bat_serial;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               ret = olpc_bat_get_voltage_max_design(val);
-               if (ret)
-                       return ret;
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property olpc_xo1_bat_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TEMP_AMBIENT,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-       POWER_SUPPLY_PROP_SERIAL_NUMBER,
-       POWER_SUPPLY_PROP_CHARGE_COUNTER,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-};
-
-/* XO-1.5 does not have ambient temperature property */
-static enum power_supply_property olpc_xo15_bat_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-       POWER_SUPPLY_PROP_SERIAL_NUMBER,
-       POWER_SUPPLY_PROP_CHARGE_COUNTER,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-};
-
-/* EEPROM reading goes completely around the power_supply API, sadly */
-
-#define EEPROM_START   0x20
-#define EEPROM_END     0x80
-#define EEPROM_SIZE    (EEPROM_END - EEPROM_START)
-
-static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
-               struct bin_attribute *attr, char *buf, loff_t off, size_t count)
-{
-       uint8_t ec_byte;
-       int ret;
-       int i;
-
-       for (i = 0; i < count; i++) {
-               ec_byte = EEPROM_START + off + i;
-               ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1);
-               if (ret) {
-                       pr_err("olpc-battery: "
-                              "EC_BAT_EEPROM cmd @ 0x%x failed - %d!\n",
-                              ec_byte, ret);
-                       return -EIO;
-               }
-       }
-
-       return count;
-}
-
-static struct bin_attribute olpc_bat_eeprom = {
-       .attr = {
-               .name = "eeprom",
-               .mode = S_IRUGO,
-       },
-       .size = EEPROM_SIZE,
-       .read = olpc_bat_eeprom_read,
-};
-
-/* Allow userspace to see the specific error value pulled from the EC */
-
-static ssize_t olpc_bat_error_read(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       uint8_t ec_byte;
-       ssize_t ret;
-
-       ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%d\n", ec_byte);
-}
-
-static struct device_attribute olpc_bat_error = {
-       .attr = {
-               .name = "error",
-               .mode = S_IRUGO,
-       },
-       .show = olpc_bat_error_read,
-};
-
-/*********************************************************************
- *             Initialisation
- *********************************************************************/
-
-static struct power_supply_desc olpc_bat_desc = {
-       .name = "olpc-battery",
-       .get_property = olpc_bat_get_property,
-       .use_for_apm = 1,
-};
-
-static struct power_supply *olpc_bat;
-
-static int olpc_battery_suspend(struct platform_device *pdev,
-                               pm_message_t state)
-{
-       if (device_may_wakeup(&olpc_ac->dev))
-               olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR);
-       else
-               olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR);
-
-       if (device_may_wakeup(&olpc_bat->dev))
-               olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
-                                  | EC_SCI_SRC_BATERR);
-       else
-               olpc_ec_wakeup_clear(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
-                                    | EC_SCI_SRC_BATERR);
-
-       return 0;
-}
-
-static int olpc_battery_probe(struct platform_device *pdev)
-{
-       int ret;
-       uint8_t status;
-
-       /*
-        * We've seen a number of EC protocol changes; this driver requires
-        * the latest EC protocol, supported by 0x44 and above.
-        */
-       if (olpc_platform_info.ecver < 0x44) {
-               printk(KERN_NOTICE "OLPC EC version 0x%02x too old for "
-                       "battery driver.\n", olpc_platform_info.ecver);
-               return -ENXIO;
-       }
-
-       ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
-       if (ret)
-               return ret;
-
-       /* Ignore the status. It doesn't actually matter */
-
-       olpc_ac = power_supply_register(&pdev->dev, &olpc_ac_desc, NULL);
-       if (IS_ERR(olpc_ac))
-               return PTR_ERR(olpc_ac);
-
-       if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */
-               olpc_bat_desc.properties = olpc_xo15_bat_props;
-               olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo15_bat_props);
-       } else { /* XO-1 */
-               olpc_bat_desc.properties = olpc_xo1_bat_props;
-               olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo1_bat_props);
-       }
-
-       olpc_bat = power_supply_register(&pdev->dev, &olpc_bat_desc, NULL);
-       if (IS_ERR(olpc_bat)) {
-               ret = PTR_ERR(olpc_bat);
-               goto battery_failed;
-       }
-
-       ret = device_create_bin_file(&olpc_bat->dev, &olpc_bat_eeprom);
-       if (ret)
-               goto eeprom_failed;
-
-       ret = device_create_file(&olpc_bat->dev, &olpc_bat_error);
-       if (ret)
-               goto error_failed;
-
-       if (olpc_ec_wakeup_available()) {
-               device_set_wakeup_capable(&olpc_ac->dev, true);
-               device_set_wakeup_capable(&olpc_bat->dev, true);
-       }
-
-       return 0;
-
-error_failed:
-       device_remove_bin_file(&olpc_bat->dev, &olpc_bat_eeprom);
-eeprom_failed:
-       power_supply_unregister(olpc_bat);
-battery_failed:
-       power_supply_unregister(olpc_ac);
-       return ret;
-}
-
-static int olpc_battery_remove(struct platform_device *pdev)
-{
-       device_remove_file(&olpc_bat->dev, &olpc_bat_error);
-       device_remove_bin_file(&olpc_bat->dev, &olpc_bat_eeprom);
-       power_supply_unregister(olpc_bat);
-       power_supply_unregister(olpc_ac);
-       return 0;
-}
-
-static const struct of_device_id olpc_battery_ids[] = {
-       { .compatible = "olpc,xo1-battery" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, olpc_battery_ids);
-
-static struct platform_driver olpc_battery_driver = {
-       .driver = {
-               .name = "olpc-battery",
-               .of_match_table = olpc_battery_ids,
-       },
-       .probe = olpc_battery_probe,
-       .remove = olpc_battery_remove,
-       .suspend = olpc_battery_suspend,
-};
-
-module_platform_driver(olpc_battery_driver);
-
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine");
diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c
deleted file mode 100644 (file)
index d05597b..0000000
+++ /dev/null
@@ -1,488 +0,0 @@
-/* NXP PCF50633 Main Battery Charger Driver
- *
- * (C) 2006-2008 by Openmoko, Inc.
- * Author: Balaji Rao <balajirrao@openmoko.org>
- * All rights reserved.
- *
- * Broken down from monstrous PCF50633 driver mainly by
- * Harald Welte, Andy Green and Werner Almesberger
- *
- *  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;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/device.h>
-#include <linux/sysfs.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-
-#include <linux/mfd/pcf50633/core.h>
-#include <linux/mfd/pcf50633/mbc.h>
-
-struct pcf50633_mbc {
-       struct pcf50633 *pcf;
-
-       int adapter_online;
-       int usb_online;
-
-       struct power_supply *usb;
-       struct power_supply *adapter;
-       struct power_supply *ac;
-};
-
-int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
-{
-       struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
-       int ret = 0;
-       u8 bits;
-       int charging_start = 1;
-       u8 mbcs2, chgmod;
-       unsigned int mbcc5;
-
-       if (ma >= 1000) {
-               bits = PCF50633_MBCC7_USB_1000mA;
-               ma = 1000;
-       } else if (ma >= 500) {
-               bits = PCF50633_MBCC7_USB_500mA;
-               ma = 500;
-       } else if (ma >= 100) {
-               bits = PCF50633_MBCC7_USB_100mA;
-               ma = 100;
-       } else {
-               bits = PCF50633_MBCC7_USB_SUSPEND;
-               charging_start = 0;
-               ma = 0;
-       }
-
-       ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
-                                       PCF50633_MBCC7_USB_MASK, bits);
-       if (ret)
-               dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
-       else
-               dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
-
-       /*
-        * We limit the charging current to be the USB current limit.
-        * The reason is that on pcf50633, when it enters PMU Standby mode,
-        * which it does when the device goes "off", the USB current limit
-        * reverts to the variant default.  In at least one common case, that
-        * default is 500mA.  By setting the charging current to be the same
-        * as the USB limit we set here before PMU standby, we enforce it only
-        * using the correct amount of current even when the USB current limit
-        * gets reset to the wrong thing
-        */
-
-       if (mbc->pcf->pdata->charger_reference_current_ma) {
-               mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
-               if (mbcc5 > 255)
-                       mbcc5 = 255;
-               pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
-       }
-
-       mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
-       chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
-
-       /* If chgmod == BATFULL, setting chgena has no effect.
-        * Datasheet says we need to set resume instead but when autoresume is
-        * used resume doesn't work. Clear and set chgena instead.
-        */
-       if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
-               pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
-                               PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
-       else {
-               pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
-                               PCF50633_MBCC1_CHGENA);
-               pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
-                               PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
-       }
-
-       power_supply_changed(mbc->usb);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
-
-int pcf50633_mbc_get_status(struct pcf50633 *pcf)
-{
-       struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
-       int status = 0;
-       u8 chgmod;
-
-       if (!mbc)
-               return 0;
-
-       chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
-               & PCF50633_MBCS2_MBC_MASK;
-
-       if (mbc->usb_online)
-               status |= PCF50633_MBC_USB_ONLINE;
-       if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
-           chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
-           chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
-           chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
-               status |= PCF50633_MBC_USB_ACTIVE;
-       if (mbc->adapter_online)
-               status |= PCF50633_MBC_ADAPTER_ONLINE;
-       if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
-           chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
-           chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
-           chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
-               status |= PCF50633_MBC_ADAPTER_ACTIVE;
-
-       return status;
-}
-EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
-
-int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
-{
-       struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
-
-       if (!mbc)
-               return 0;
-
-       return mbc->usb_online;
-}
-EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
-
-static ssize_t
-show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-
-       u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
-       u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
-
-       return sprintf(buf, "%d\n", chgmod);
-}
-static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
-
-static ssize_t
-show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-       u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
-                                               PCF50633_MBCC7_USB_MASK;
-       unsigned int ma;
-
-       if (usblim == PCF50633_MBCC7_USB_1000mA)
-               ma = 1000;
-       else if (usblim == PCF50633_MBCC7_USB_500mA)
-               ma = 500;
-       else if (usblim == PCF50633_MBCC7_USB_100mA)
-               ma = 100;
-       else
-               ma = 0;
-
-       return sprintf(buf, "%u\n", ma);
-}
-
-static ssize_t set_usblim(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-       unsigned long ma;
-       int ret;
-
-       ret = kstrtoul(buf, 10, &ma);
-       if (ret)
-               return ret;
-
-       pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
-
-       return count;
-}
-
-static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
-
-static ssize_t
-show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-       u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
-       unsigned int ma;
-
-       if (!mbc->pcf->pdata->charger_reference_current_ma)
-               return -ENODEV;
-
-       ma = (mbc->pcf->pdata->charger_reference_current_ma *  mbcc5) >> 8;
-
-       return sprintf(buf, "%u\n", ma);
-}
-
-static ssize_t set_chglim(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
-       unsigned long ma;
-       unsigned int mbcc5;
-       int ret;
-
-       if (!mbc->pcf->pdata->charger_reference_current_ma)
-               return -ENODEV;
-
-       ret = kstrtoul(buf, 10, &ma);
-       if (ret)
-               return ret;
-
-       mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
-       if (mbcc5 > 255)
-               mbcc5 = 255;
-       pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
-
-       return count;
-}
-
-/*
- * This attribute allows to change MBC charging limit on the fly
- * independently of usb current limit. It also gets set automatically every
- * time usb current limit is changed.
- */
-static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
-
-static struct attribute *pcf50633_mbc_sysfs_entries[] = {
-       &dev_attr_chgmode.attr,
-       &dev_attr_usb_curlim.attr,
-       &dev_attr_chg_curlim.attr,
-       NULL,
-};
-
-static struct attribute_group mbc_attr_group = {
-       .name   = NULL,                 /* put in device directory */
-       .attrs  = pcf50633_mbc_sysfs_entries,
-};
-
-static void
-pcf50633_mbc_irq_handler(int irq, void *data)
-{
-       struct pcf50633_mbc *mbc = data;
-
-       /* USB */
-       if (irq == PCF50633_IRQ_USBINS) {
-               mbc->usb_online = 1;
-       } else if (irq == PCF50633_IRQ_USBREM) {
-               mbc->usb_online = 0;
-               pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
-       }
-
-       /* Adapter */
-       if (irq == PCF50633_IRQ_ADPINS)
-               mbc->adapter_online = 1;
-       else if (irq == PCF50633_IRQ_ADPREM)
-               mbc->adapter_online = 0;
-
-       power_supply_changed(mbc->ac);
-       power_supply_changed(mbc->usb);
-       power_supply_changed(mbc->adapter);
-
-       if (mbc->pcf->pdata->mbc_event_callback)
-               mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
-}
-
-static int adapter_get_property(struct power_supply *psy,
-                       enum power_supply_property psp,
-                       union power_supply_propval *val)
-{
-       struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval =  mbc->adapter_online;
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static int usb_get_property(struct power_supply *psy,
-                       enum power_supply_property psp,
-                       union power_supply_propval *val)
-{
-       struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
-       int ret = 0;
-       u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
-                                               PCF50633_MBCC7_USB_MASK;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = mbc->usb_online &&
-                               (usblim <= PCF50633_MBCC7_USB_500mA);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static int ac_get_property(struct power_supply *psy,
-                       enum power_supply_property psp,
-                       union power_supply_propval *val)
-{
-       struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
-       int ret = 0;
-       u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
-                                               PCF50633_MBCC7_USB_MASK;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = mbc->usb_online &&
-                               (usblim == PCF50633_MBCC7_USB_1000mA);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static enum power_supply_property power_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static const u8 mbc_irq_handlers[] = {
-       PCF50633_IRQ_ADPINS,
-       PCF50633_IRQ_ADPREM,
-       PCF50633_IRQ_USBINS,
-       PCF50633_IRQ_USBREM,
-       PCF50633_IRQ_BATFULL,
-       PCF50633_IRQ_CHGHALT,
-       PCF50633_IRQ_THLIMON,
-       PCF50633_IRQ_THLIMOFF,
-       PCF50633_IRQ_USBLIMON,
-       PCF50633_IRQ_USBLIMOFF,
-       PCF50633_IRQ_LOWSYS,
-       PCF50633_IRQ_LOWBAT,
-};
-
-static const struct power_supply_desc pcf50633_mbc_adapter_desc = {
-       .name           = "adapter",
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-       .properties     = power_props,
-       .num_properties = ARRAY_SIZE(power_props),
-       .get_property   = &adapter_get_property,
-};
-
-static const struct power_supply_desc pcf50633_mbc_usb_desc = {
-       .name           = "usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .properties     = power_props,
-       .num_properties = ARRAY_SIZE(power_props),
-       .get_property   = usb_get_property,
-};
-
-static const struct power_supply_desc pcf50633_mbc_ac_desc = {
-       .name           = "ac",
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-       .properties     = power_props,
-       .num_properties = ARRAY_SIZE(power_props),
-       .get_property   = ac_get_property,
-};
-
-static int pcf50633_mbc_probe(struct platform_device *pdev)
-{
-       struct power_supply_config psy_cfg = {};
-       struct pcf50633_mbc *mbc;
-       int ret;
-       int i;
-       u8 mbcs1;
-
-       mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
-       if (!mbc)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, mbc);
-       mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
-
-       /* Set up IRQ handlers */
-       for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
-               pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
-                                       pcf50633_mbc_irq_handler, mbc);
-
-       psy_cfg.supplied_to             = mbc->pcf->pdata->batteries;
-       psy_cfg.num_supplicants         = mbc->pcf->pdata->num_batteries;
-       psy_cfg.drv_data                = mbc;
-
-       /* Create power supplies */
-       mbc->adapter = power_supply_register(&pdev->dev,
-                                            &pcf50633_mbc_adapter_desc,
-                                            &psy_cfg);
-       if (IS_ERR(mbc->adapter)) {
-               dev_err(mbc->pcf->dev, "failed to register adapter\n");
-               ret = PTR_ERR(mbc->adapter);
-               return ret;
-       }
-
-       mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
-                                        &psy_cfg);
-       if (IS_ERR(mbc->usb)) {
-               dev_err(mbc->pcf->dev, "failed to register usb\n");
-               power_supply_unregister(mbc->adapter);
-               ret = PTR_ERR(mbc->usb);
-               return ret;
-       }
-
-       mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc,
-                                       &psy_cfg);
-       if (IS_ERR(mbc->ac)) {
-               dev_err(mbc->pcf->dev, "failed to register ac\n");
-               power_supply_unregister(mbc->adapter);
-               power_supply_unregister(mbc->usb);
-               ret = PTR_ERR(mbc->ac);
-               return ret;
-       }
-
-       ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
-       if (ret)
-               dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
-
-       mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
-       if (mbcs1 & PCF50633_MBCS1_USBPRES)
-               pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
-       if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
-               pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
-
-       return 0;
-}
-
-static int pcf50633_mbc_remove(struct platform_device *pdev)
-{
-       struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
-       int i;
-
-       /* Remove IRQ handlers */
-       for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
-               pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
-
-       sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group);
-       power_supply_unregister(mbc->usb);
-       power_supply_unregister(mbc->adapter);
-       power_supply_unregister(mbc->ac);
-
-       return 0;
-}
-
-static struct platform_driver pcf50633_mbc_driver = {
-       .driver = {
-               .name = "pcf50633-mbc",
-       },
-       .probe = pcf50633_mbc_probe,
-       .remove = pcf50633_mbc_remove,
-};
-
-module_platform_driver(pcf50633_mbc_driver);
-
-MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
-MODULE_DESCRIPTION("PCF50633 mbc driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:pcf50633-mbc");
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
deleted file mode 100644 (file)
index dfe1ee8..0000000
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * Common power driver for PDAs and phones with one or two external
- * power supplies (AC/USB) connected to main and backup batteries,
- * and optional builtin charger.
- *
- * Copyright Â© 2007 Anton Vorontsov <cbou@mail.ru>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/notifier.h>
-#include <linux/power_supply.h>
-#include <linux/pda_power.h>
-#include <linux/regulator/consumer.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
-#include <linux/usb/otg.h>
-
-static inline unsigned int get_irq_flags(struct resource *res)
-{
-       return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK);
-}
-
-static struct device *dev;
-static struct pda_power_pdata *pdata;
-static struct resource *ac_irq, *usb_irq;
-static struct timer_list charger_timer;
-static struct timer_list supply_timer;
-static struct timer_list polling_timer;
-static int polling;
-static struct power_supply *pda_psy_ac, *pda_psy_usb;
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-static struct usb_phy *transceiver;
-static struct notifier_block otg_nb;
-#endif
-
-static struct regulator *ac_draw;
-
-enum {
-       PDA_PSY_OFFLINE = 0,
-       PDA_PSY_ONLINE = 1,
-       PDA_PSY_TO_CHANGE,
-};
-static int new_ac_status = -1;
-static int new_usb_status = -1;
-static int ac_status = -1;
-static int usb_status = -1;
-
-static int pda_power_get_property(struct power_supply *psy,
-                                 enum power_supply_property psp,
-                                 union power_supply_propval *val)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               if (psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
-                       val->intval = pdata->is_ac_online ?
-                                     pdata->is_ac_online() : 0;
-               else
-                       val->intval = pdata->is_usb_online ?
-                                     pdata->is_usb_online() : 0;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static enum power_supply_property pda_power_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static char *pda_power_supplied_to[] = {
-       "main-battery",
-       "backup-battery",
-};
-
-static const struct power_supply_desc pda_psy_ac_desc = {
-       .name = "ac",
-       .type = POWER_SUPPLY_TYPE_MAINS,
-       .properties = pda_power_props,
-       .num_properties = ARRAY_SIZE(pda_power_props),
-       .get_property = pda_power_get_property,
-};
-
-static const struct power_supply_desc pda_psy_usb_desc = {
-       .name = "usb",
-       .type = POWER_SUPPLY_TYPE_USB,
-       .properties = pda_power_props,
-       .num_properties = ARRAY_SIZE(pda_power_props),
-       .get_property = pda_power_get_property,
-};
-
-static void update_status(void)
-{
-       if (pdata->is_ac_online)
-               new_ac_status = !!pdata->is_ac_online();
-
-       if (pdata->is_usb_online)
-               new_usb_status = !!pdata->is_usb_online();
-}
-
-static void update_charger(void)
-{
-       static int regulator_enabled;
-       int max_uA = pdata->ac_max_uA;
-
-       if (pdata->set_charge) {
-               if (new_ac_status > 0) {
-                       dev_dbg(dev, "charger on (AC)\n");
-                       pdata->set_charge(PDA_POWER_CHARGE_AC);
-               } else if (new_usb_status > 0) {
-                       dev_dbg(dev, "charger on (USB)\n");
-                       pdata->set_charge(PDA_POWER_CHARGE_USB);
-               } else {
-                       dev_dbg(dev, "charger off\n");
-                       pdata->set_charge(0);
-               }
-       } else if (ac_draw) {
-               if (new_ac_status > 0) {
-                       regulator_set_current_limit(ac_draw, max_uA, max_uA);
-                       if (!regulator_enabled) {
-                               dev_dbg(dev, "charger on (AC)\n");
-                               WARN_ON(regulator_enable(ac_draw));
-                               regulator_enabled = 1;
-                       }
-               } else {
-                       if (regulator_enabled) {
-                               dev_dbg(dev, "charger off\n");
-                               WARN_ON(regulator_disable(ac_draw));
-                               regulator_enabled = 0;
-                       }
-               }
-       }
-}
-
-static void supply_timer_func(unsigned long unused)
-{
-       if (ac_status == PDA_PSY_TO_CHANGE) {
-               ac_status = new_ac_status;
-               power_supply_changed(pda_psy_ac);
-       }
-
-       if (usb_status == PDA_PSY_TO_CHANGE) {
-               usb_status = new_usb_status;
-               power_supply_changed(pda_psy_usb);
-       }
-}
-
-static void psy_changed(void)
-{
-       update_charger();
-
-       /*
-        * Okay, charger set. Now wait a bit before notifying supplicants,
-        * charge power should stabilize.
-        */
-       mod_timer(&supply_timer,
-                 jiffies + msecs_to_jiffies(pdata->wait_for_charger));
-}
-
-static void charger_timer_func(unsigned long unused)
-{
-       update_status();
-       psy_changed();
-}
-
-static irqreturn_t power_changed_isr(int irq, void *power_supply)
-{
-       if (power_supply == pda_psy_ac)
-               ac_status = PDA_PSY_TO_CHANGE;
-       else if (power_supply == pda_psy_usb)
-               usb_status = PDA_PSY_TO_CHANGE;
-       else
-               return IRQ_NONE;
-
-       /*
-        * Wait a bit before reading ac/usb line status and setting charger,
-        * because ac/usb status readings may lag from irq.
-        */
-       mod_timer(&charger_timer,
-                 jiffies + msecs_to_jiffies(pdata->wait_for_status));
-
-       return IRQ_HANDLED;
-}
-
-static void polling_timer_func(unsigned long unused)
-{
-       int changed = 0;
-
-       dev_dbg(dev, "polling...\n");
-
-       update_status();
-
-       if (!ac_irq && new_ac_status != ac_status) {
-               ac_status = PDA_PSY_TO_CHANGE;
-               changed = 1;
-       }
-
-       if (!usb_irq && new_usb_status != usb_status) {
-               usb_status = PDA_PSY_TO_CHANGE;
-               changed = 1;
-       }
-
-       if (changed)
-               psy_changed();
-
-       mod_timer(&polling_timer,
-                 jiffies + msecs_to_jiffies(pdata->polling_interval));
-}
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-static int otg_is_usb_online(void)
-{
-       return (transceiver->last_event == USB_EVENT_VBUS ||
-               transceiver->last_event == USB_EVENT_ENUMERATED);
-}
-
-static int otg_is_ac_online(void)
-{
-       return (transceiver->last_event == USB_EVENT_CHARGER);
-}
-
-static int otg_handle_notification(struct notifier_block *nb,
-               unsigned long event, void *unused)
-{
-       switch (event) {
-       case USB_EVENT_CHARGER:
-               ac_status = PDA_PSY_TO_CHANGE;
-               break;
-       case USB_EVENT_VBUS:
-       case USB_EVENT_ENUMERATED:
-               usb_status = PDA_PSY_TO_CHANGE;
-               break;
-       case USB_EVENT_NONE:
-               ac_status = PDA_PSY_TO_CHANGE;
-               usb_status = PDA_PSY_TO_CHANGE;
-               break;
-       default:
-               return NOTIFY_OK;
-       }
-
-       /*
-        * Wait a bit before reading ac/usb line status and setting charger,
-        * because ac/usb status readings may lag from irq.
-        */
-       mod_timer(&charger_timer,
-                 jiffies + msecs_to_jiffies(pdata->wait_for_status));
-
-       return NOTIFY_OK;
-}
-#endif
-
-static int pda_power_probe(struct platform_device *pdev)
-{
-       struct power_supply_config psy_cfg = {};
-       int ret = 0;
-
-       dev = &pdev->dev;
-
-       if (pdev->id != -1) {
-               dev_err(dev, "it's meaningless to register several "
-                       "pda_powers; use id = -1\n");
-               ret = -EINVAL;
-               goto wrongid;
-       }
-
-       pdata = pdev->dev.platform_data;
-
-       if (pdata->init) {
-               ret = pdata->init(dev);
-               if (ret < 0)
-                       goto init_failed;
-       }
-
-       ac_draw = regulator_get(dev, "ac_draw");
-       if (IS_ERR(ac_draw)) {
-               dev_dbg(dev, "couldn't get ac_draw regulator\n");
-               ac_draw = NULL;
-       }
-
-       update_status();
-       update_charger();
-
-       if (!pdata->wait_for_status)
-               pdata->wait_for_status = 500;
-
-       if (!pdata->wait_for_charger)
-               pdata->wait_for_charger = 500;
-
-       if (!pdata->polling_interval)
-               pdata->polling_interval = 2000;
-
-       if (!pdata->ac_max_uA)
-               pdata->ac_max_uA = 500000;
-
-       setup_timer(&charger_timer, charger_timer_func, 0);
-       setup_timer(&supply_timer, supply_timer_func, 0);
-
-       ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
-       usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
-
-       if (pdata->supplied_to) {
-               psy_cfg.supplied_to = pdata->supplied_to;
-               psy_cfg.num_supplicants = pdata->num_supplicants;
-       } else {
-               psy_cfg.supplied_to = pda_power_supplied_to;
-               psy_cfg.num_supplicants = ARRAY_SIZE(pda_power_supplied_to);
-       }
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-       transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
-       if (!IS_ERR_OR_NULL(transceiver)) {
-               if (!pdata->is_usb_online)
-                       pdata->is_usb_online = otg_is_usb_online;
-               if (!pdata->is_ac_online)
-                       pdata->is_ac_online = otg_is_ac_online;
-       }
-#endif
-
-       if (pdata->is_ac_online) {
-               pda_psy_ac = power_supply_register(&pdev->dev,
-                                                  &pda_psy_ac_desc, &psy_cfg);
-               if (IS_ERR(pda_psy_ac)) {
-                       dev_err(dev, "failed to register %s power supply\n",
-                               pda_psy_ac_desc.name);
-                       ret = PTR_ERR(pda_psy_ac);
-                       goto ac_supply_failed;
-               }
-
-               if (ac_irq) {
-                       ret = request_irq(ac_irq->start, power_changed_isr,
-                                         get_irq_flags(ac_irq), ac_irq->name,
-                                         pda_psy_ac);
-                       if (ret) {
-                               dev_err(dev, "request ac irq failed\n");
-                               goto ac_irq_failed;
-                       }
-               } else {
-                       polling = 1;
-               }
-       }
-
-       if (pdata->is_usb_online) {
-               pda_psy_usb = power_supply_register(&pdev->dev,
-                                                   &pda_psy_usb_desc,
-                                                   &psy_cfg);
-               if (IS_ERR(pda_psy_usb)) {
-                       dev_err(dev, "failed to register %s power supply\n",
-                               pda_psy_usb_desc.name);
-                       ret = PTR_ERR(pda_psy_usb);
-                       goto usb_supply_failed;
-               }
-
-               if (usb_irq) {
-                       ret = request_irq(usb_irq->start, power_changed_isr,
-                                         get_irq_flags(usb_irq),
-                                         usb_irq->name, pda_psy_usb);
-                       if (ret) {
-                               dev_err(dev, "request usb irq failed\n");
-                               goto usb_irq_failed;
-                       }
-               } else {
-                       polling = 1;
-               }
-       }
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-       if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) {
-               otg_nb.notifier_call = otg_handle_notification;
-               ret = usb_register_notifier(transceiver, &otg_nb);
-               if (ret) {
-                       dev_err(dev, "failure to register otg notifier\n");
-                       goto otg_reg_notifier_failed;
-               }
-               polling = 0;
-       }
-#endif
-
-       if (polling) {
-               dev_dbg(dev, "will poll for status\n");
-               setup_timer(&polling_timer, polling_timer_func, 0);
-               mod_timer(&polling_timer,
-                         jiffies + msecs_to_jiffies(pdata->polling_interval));
-       }
-
-       if (ac_irq || usb_irq)
-               device_init_wakeup(&pdev->dev, 1);
-
-       return 0;
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-otg_reg_notifier_failed:
-       if (pdata->is_usb_online && usb_irq)
-               free_irq(usb_irq->start, pda_psy_usb);
-#endif
-usb_irq_failed:
-       if (pdata->is_usb_online)
-               power_supply_unregister(pda_psy_usb);
-usb_supply_failed:
-       if (pdata->is_ac_online && ac_irq)
-               free_irq(ac_irq->start, pda_psy_ac);
-#if IS_ENABLED(CONFIG_USB_PHY)
-       if (!IS_ERR_OR_NULL(transceiver))
-               usb_put_phy(transceiver);
-#endif
-ac_irq_failed:
-       if (pdata->is_ac_online)
-               power_supply_unregister(pda_psy_ac);
-ac_supply_failed:
-       if (ac_draw) {
-               regulator_put(ac_draw);
-               ac_draw = NULL;
-       }
-       if (pdata->exit)
-               pdata->exit(dev);
-init_failed:
-wrongid:
-       return ret;
-}
-
-static int pda_power_remove(struct platform_device *pdev)
-{
-       if (pdata->is_usb_online && usb_irq)
-               free_irq(usb_irq->start, pda_psy_usb);
-       if (pdata->is_ac_online && ac_irq)
-               free_irq(ac_irq->start, pda_psy_ac);
-
-       if (polling)
-               del_timer_sync(&polling_timer);
-       del_timer_sync(&charger_timer);
-       del_timer_sync(&supply_timer);
-
-       if (pdata->is_usb_online)
-               power_supply_unregister(pda_psy_usb);
-       if (pdata->is_ac_online)
-               power_supply_unregister(pda_psy_ac);
-#if IS_ENABLED(CONFIG_USB_PHY)
-       if (!IS_ERR_OR_NULL(transceiver))
-               usb_put_phy(transceiver);
-#endif
-       if (ac_draw) {
-               regulator_put(ac_draw);
-               ac_draw = NULL;
-       }
-       if (pdata->exit)
-               pdata->exit(dev);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int ac_wakeup_enabled;
-static int usb_wakeup_enabled;
-
-static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
-{
-       if (pdata->suspend) {
-               int ret = pdata->suspend(state);
-
-               if (ret)
-                       return ret;
-       }
-
-       if (device_may_wakeup(&pdev->dev)) {
-               if (ac_irq)
-                       ac_wakeup_enabled = !enable_irq_wake(ac_irq->start);
-               if (usb_irq)
-                       usb_wakeup_enabled = !enable_irq_wake(usb_irq->start);
-       }
-
-       return 0;
-}
-
-static int pda_power_resume(struct platform_device *pdev)
-{
-       if (device_may_wakeup(&pdev->dev)) {
-               if (usb_irq && usb_wakeup_enabled)
-                       disable_irq_wake(usb_irq->start);
-               if (ac_irq && ac_wakeup_enabled)
-                       disable_irq_wake(ac_irq->start);
-       }
-
-       if (pdata->resume)
-               return pdata->resume();
-
-       return 0;
-}
-#else
-#define pda_power_suspend NULL
-#define pda_power_resume NULL
-#endif /* CONFIG_PM */
-
-static struct platform_driver pda_power_pdrv = {
-       .driver = {
-               .name = "pda-power",
-       },
-       .probe = pda_power_probe,
-       .remove = pda_power_remove,
-       .suspend = pda_power_suspend,
-       .resume = pda_power_resume,
-};
-
-module_platform_driver(pda_power_pdrv);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
-MODULE_ALIAS("platform:pda-power");
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
deleted file mode 100644 (file)
index fb62ed3..0000000
+++ /dev/null
@@ -1,1257 +0,0 @@
-/*
- * Copyright 2012 ST Ericsson.
- *
- * Power supply driver for ST Ericsson pm2xxx_charger charger
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/regulator/consumer.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/workqueue.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
-#include <linux/mfd/abx500/ux500_chargalg.h>
-#include <linux/pm2301_charger.h>
-#include <linux/gpio.h>
-#include <linux/pm_runtime.h>
-#include <linux/pm.h>
-
-#include "pm2301_charger.h"
-
-#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
-               struct pm2xxx_charger, ac_chg)
-#define SLEEP_MIN              50
-#define SLEEP_MAX              100
-#define PM2XXX_AUTOSUSPEND_DELAY 500
-
-static int pm2xxx_interrupt_registers[] = {
-       PM2XXX_REG_INT1,
-       PM2XXX_REG_INT2,
-       PM2XXX_REG_INT3,
-       PM2XXX_REG_INT4,
-       PM2XXX_REG_INT5,
-       PM2XXX_REG_INT6,
-};
-
-static enum power_supply_property pm2xxx_charger_ac_props[] = {
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-};
-
-static int pm2xxx_charger_voltage_map[] = {
-       3500,
-       3525,
-       3550,
-       3575,
-       3600,
-       3625,
-       3650,
-       3675,
-       3700,
-       3725,
-       3750,
-       3775,
-       3800,
-       3825,
-       3850,
-       3875,
-       3900,
-       3925,
-       3950,
-       3975,
-       4000,
-       4025,
-       4050,
-       4075,
-       4100,
-       4125,
-       4150,
-       4175,
-       4200,
-       4225,
-       4250,
-       4275,
-       4300,
-};
-
-static int pm2xxx_charger_current_map[] = {
-       200,
-       200,
-       400,
-       600,
-       800,
-       1000,
-       1200,
-       1400,
-       1600,
-       1800,
-       2000,
-       2200,
-       2400,
-       2600,
-       2800,
-       3000,
-};
-
-static const struct i2c_device_id pm2xxx_ident[] = {
-       { "pm2301", 0 },
-       { }
-};
-
-static void set_lpn_pin(struct pm2xxx_charger *pm2)
-{
-       if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) {
-               gpio_set_value(pm2->lpn_pin, 1);
-               usleep_range(SLEEP_MIN, SLEEP_MAX);
-       }
-}
-
-static void clear_lpn_pin(struct pm2xxx_charger *pm2)
-{
-       if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin))
-               gpio_set_value(pm2->lpn_pin, 0);
-}
-
-static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
-{
-       int ret;
-
-       /* wake up the device */
-       pm_runtime_get_sync(pm2->dev);
-
-       ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
-                               1, val);
-       if (ret < 0)
-               dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
-       else
-               ret = 0;
-
-       pm_runtime_put_sync(pm2->dev);
-
-       return ret;
-}
-
-static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
-{
-       int ret;
-
-       /* wake up the device */
-       pm_runtime_get_sync(pm2->dev);
-
-       ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
-                               1, &val);
-       if (ret < 0)
-               dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
-       else
-               ret = 0;
-
-       pm_runtime_put_sync(pm2->dev);
-
-       return ret;
-}
-
-static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2)
-{
-       int ret;
-
-       /* Enable charging */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
-                       (PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA));
-
-       return ret;
-}
-
-static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2)
-{
-       int ret;
-
-       /* Disable SW EOC ctrl */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, PM2XXX_SWCTRL_HW);
-       if (ret < 0) {
-               dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
-               return ret;
-       }
-
-       /* Disable charging */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
-                       (PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS));
-       if (ret < 0) {
-               dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
-{
-       queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
-
-       return 0;
-}
-
-
-static int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
-{
-       queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
-
-       return 0;
-}
-
-static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val)
-{
-       dev_err(pm2->dev, "Overvoltage detected\n");
-       pm2->flags.ovv = true;
-       power_supply_changed(pm2->ac_chg.psy);
-
-       /* Schedule a new HW failure check */
-       queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0);
-
-       return 0;
-}
-
-static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
-{
-       dev_dbg(pm2->dev , "20 minutes watchdog expired\n");
-
-       pm2->ac.wd_expired = true;
-       power_supply_changed(pm2->ac_chg.psy);
-
-       return 0;
-}
-
-static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val)
-{
-       int ret;
-
-       switch (val) {
-       case PM2XXX_INT1_ITVBATLOWR:
-               dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n");
-               /* Enable SW EOC ctrl */
-               ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
-                                                       PM2XXX_SWCTRL_SW);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
-                       return ret;
-               }
-               break;
-
-       case PM2XXX_INT1_ITVBATLOWF:
-               dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n");
-               /* Disable SW EOC ctrl */
-               ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
-                                                       PM2XXX_SWCTRL_HW);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
-                       return ret;
-               }
-               break;
-
-       default:
-               dev_err(pm2->dev, "Unknown VBAT level\n");
-       }
-
-       return 0;
-}
-
-static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val)
-{
-       dev_dbg(pm2->dev, "battery disconnected\n");
-
-       return 0;
-}
-
-static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
-{
-       int ret;
-
-       ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val);
-
-       if (ret < 0) {
-               dev_err(pm2->dev, "Charger detection failed\n");
-               goto out;
-       }
-
-       *val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG);
-
-out:
-       return ret;
-}
-
-static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val)
-{
-
-       int ret;
-       u8 read_val;
-
-       /*
-        * Since we can't be sure that the events are received
-        * synchronously, we have the check if the main charger is
-        * connected by reading the interrupt source register.
-        */
-       ret = pm2xxx_charger_detection(pm2, &read_val);
-
-       if ((ret == 0) && read_val) {
-               pm2->ac.charger_connected = 1;
-               pm2->ac_conn = true;
-               queue_work(pm2->charger_wq, &pm2->ac_work);
-       }
-
-
-       return ret;
-}
-
-static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2,
-                                                               int val)
-{
-       pm2->ac.charger_connected = 0;
-       queue_work(pm2->charger_wq, &pm2->ac_work);
-
-       return 0;
-}
-
-static int pm2_int_reg0(void *pm2_data, int val)
-{
-       struct pm2xxx_charger *pm2 = pm2_data;
-       int ret = 0;
-
-       if (val & PM2XXX_INT1_ITVBATLOWR) {
-               ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
-                                               PM2XXX_INT1_ITVBATLOWR);
-               if (ret < 0)
-                       goto out;
-       }
-
-       if (val & PM2XXX_INT1_ITVBATLOWF) {
-               ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
-                                               PM2XXX_INT1_ITVBATLOWF);
-               if (ret < 0)
-                       goto out;
-       }
-
-       if (val & PM2XXX_INT1_ITVBATDISCONNECT) {
-               ret = pm2xxx_charger_bat_disc_mngt(pm2,
-                               PM2XXX_INT1_ITVBATDISCONNECT);
-               if (ret < 0)
-                       goto out;
-       }
-out:
-       return ret;
-}
-
-static int pm2_int_reg1(void *pm2_data, int val)
-{
-       struct pm2xxx_charger *pm2 = pm2_data;
-       int ret = 0;
-
-       if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
-               dev_dbg(pm2->dev , "Main charger plugged\n");
-               ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val &
-                       (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG));
-       }
-
-       if (val &
-               (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) {
-               dev_dbg(pm2->dev , "Main charger unplugged\n");
-               ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val &
-                                               (PM2XXX_INT2_ITVPWR1UNPLUG |
-                                               PM2XXX_INT2_ITVPWR2UNPLUG));
-       }
-
-       return ret;
-}
-
-static int pm2_int_reg2(void *pm2_data, int val)
-{
-       struct pm2xxx_charger *pm2 = pm2_data;
-       int ret = 0;
-
-       if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD)
-               ret = pm2xxx_charger_wd_exp_mngt(pm2, val);
-
-       if (val & (PM2XXX_INT3_ITCHPRECHARGEWD |
-                               PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) {
-               dev_dbg(pm2->dev,
-                       "Watchdog occurred for precharge, CC and CV charge\n");
-       }
-
-       return ret;
-}
-
-static int pm2_int_reg3(void *pm2_data, int val)
-{
-       struct pm2xxx_charger *pm2 = pm2_data;
-       int ret = 0;
-
-       if (val & (PM2XXX_INT4_ITCHARGINGON)) {
-               dev_dbg(pm2->dev ,
-                       "chargind operation has started\n");
-       }
-
-       if (val & (PM2XXX_INT4_ITVRESUME)) {
-               dev_dbg(pm2->dev,
-                       "battery discharged down to VResume threshold\n");
-       }
-
-       if (val & (PM2XXX_INT4_ITBATTFULL)) {
-               dev_dbg(pm2->dev , "battery fully detected\n");
-       }
-
-       if (val & (PM2XXX_INT4_ITCVPHASE)) {
-               dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n");
-       }
-
-       if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
-               pm2->failure_case = VPWR_OVV;
-               ret = pm2xxx_charger_ovv_mngt(pm2, val &
-                       (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV));
-               dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n");
-       }
-
-       if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD |
-                               PM2XXX_INT4_S_ITBATTEMPHOT)) {
-               ret = pm2xxx_charger_batt_therm_mngt(pm2, val &
-                       (PM2XXX_INT4_S_ITBATTEMPCOLD |
-                       PM2XXX_INT4_S_ITBATTEMPHOT));
-               dev_dbg(pm2->dev, "BTEMP is too Low/High\n");
-       }
-
-       return ret;
-}
-
-static int pm2_int_reg4(void *pm2_data, int val)
-{
-       struct pm2xxx_charger *pm2 = pm2_data;
-       int ret = 0;
-
-       if (val & PM2XXX_INT5_ITVSYSTEMOVV) {
-               pm2->failure_case = VSYSTEM_OVV;
-               ret = pm2xxx_charger_ovv_mngt(pm2, val &
-                                               PM2XXX_INT5_ITVSYSTEMOVV);
-               dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n");
-       }
-
-       if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL |
-                               PM2XXX_INT5_ITTHERMALWARNINGRISE |
-                               PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
-                               PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) {
-               dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n");
-               ret = pm2xxx_charger_die_therm_mngt(pm2, val &
-                       (PM2XXX_INT5_ITTHERMALWARNINGFALL |
-                       PM2XXX_INT5_ITTHERMALWARNINGRISE |
-                       PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
-                       PM2XXX_INT5_ITTHERMALSHUTDOWNRISE));
-       }
-
-       return ret;
-}
-
-static int pm2_int_reg5(void *pm2_data, int val)
-{
-       struct pm2xxx_charger *pm2 = pm2_data;
-       int ret = 0;
-
-       if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
-               dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
-       }
-
-       if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE |
-                       PM2XXX_INT6_ITVPWR1VALIDRISE |
-                       PM2XXX_INT6_ITVPWR2VALIDFALL |
-                       PM2XXX_INT6_ITVPWR1VALIDFALL)) {
-               dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n");
-       }
-
-       return ret;
-}
-
-static irqreturn_t  pm2xxx_irq_int(int irq, void *data)
-{
-       struct pm2xxx_charger *pm2 = data;
-       struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
-       int i;
-
-       /* wake up the device */
-       pm_runtime_get_sync(pm2->dev);
-
-       do {
-               for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
-                       pm2xxx_reg_read(pm2,
-                               pm2xxx_interrupt_registers[i],
-                               &(interrupt->reg[i]));
-
-                       if (interrupt->reg[i] > 0)
-                               interrupt->handler[i](pm2, interrupt->reg[i]);
-               }
-       } while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0);
-
-       pm_runtime_mark_last_busy(pm2->dev);
-       pm_runtime_put_autosuspend(pm2->dev);
-
-       return IRQ_HANDLED;
-}
-
-static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2)
-{
-       int ret = 0;
-       u8 val;
-
-       if (pm2->ac.charger_connected && pm2->ac.charger_online) {
-
-               ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
-                       goto out;
-               }
-
-               if (val & PM2XXX_INT4_S_ITCVPHASE)
-                       ret = PM2XXX_CONST_VOLT;
-               else
-                       ret = PM2XXX_CONST_CURR;
-       }
-out:
-       return ret;
-}
-
-static int pm2xxx_current_to_regval(int curr)
-{
-       int i;
-
-       if (curr < pm2xxx_charger_current_map[0])
-               return 0;
-
-       for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) {
-               if (curr < pm2xxx_charger_current_map[i])
-                       return (i - 1);
-       }
-
-       i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1;
-       if (curr == pm2xxx_charger_current_map[i])
-               return i;
-       else
-               return -EINVAL;
-}
-
-static int pm2xxx_voltage_to_regval(int curr)
-{
-       int i;
-
-       if (curr < pm2xxx_charger_voltage_map[0])
-               return 0;
-
-       for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) {
-               if (curr < pm2xxx_charger_voltage_map[i])
-                       return i - 1;
-       }
-
-       i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1;
-       if (curr == pm2xxx_charger_voltage_map[i])
-               return i;
-       else
-               return -EINVAL;
-}
-
-static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger,
-               int ich_out)
-{
-       int ret;
-       int curr_index;
-       struct pm2xxx_charger *pm2;
-       u8 val;
-
-       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
-               pm2 = to_pm2xxx_charger_ac_device_info(charger);
-       else
-               return -ENXIO;
-
-       curr_index = pm2xxx_current_to_regval(ich_out);
-       if (curr_index < 0) {
-               dev_err(pm2->dev,
-                       "Charger current too high, charging not started\n");
-               return -ENXIO;
-       }
-
-       ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
-       if (ret >= 0) {
-               val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK;
-               val |= curr_index;
-               ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val);
-               if (ret < 0) {
-                       dev_err(pm2->dev,
-                               "%s write failed\n", __func__);
-               }
-       }
-       else
-               dev_err(pm2->dev, "%s read failed\n", __func__);
-
-       return ret;
-}
-
-static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       struct pm2xxx_charger *pm2;
-
-       pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy));
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (pm2->flags.mainextchnotok)
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               else if (pm2->ac.wd_expired)
-                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
-               else if (pm2->flags.main_thermal_prot)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else if (pm2->flags.ovv)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = pm2->ac.charger_online;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = pm2->ac.charger_connected;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2);
-               val->intval = pm2->ac.cv_active;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
-{
-       int ret = 0;
-
-       /* enable CC and CV watchdog */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3,
-               (PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN));
-       if( ret < 0)
-               return ret;
-
-       /* enable precharge watchdog */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4,
-                                       PM2XXX_CH_WD_PRECH_PHASE_60MIN);
-
-       /* Disable auto timeout */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG5,
-                                       PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN);
-
-       /*
-     * EOC current level = 100mA
-        * Precharge current level = 100mA
-        * CC current level = 1000mA
-        */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6,
-               (PM2XXX_DIR_CH_CC_CURRENT_1000MA |
-               PM2XXX_CH_PRECH_CURRENT_100MA |
-               PM2XXX_CH_EOC_CURRENT_100MA));
-
-       /*
-     * recharge threshold = 3.8V
-        * Precharge to CC threshold = 2.9V
-        */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG7,
-               (PM2XXX_CH_PRECH_VOL_2_9 | PM2XXX_CH_VRESUME_VOL_3_8));
-
-       /* float voltage charger level = 4.2V */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8,
-               PM2XXX_CH_VOLT_4_2);
-
-       /* Voltage drop between VBAT and VSYS in HW charging = 300mV */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG9,
-               (PM2XXX_CH_150MV_DROP_300MV | PM2XXX_CHARCHING_INFO_DIS |
-               PM2XXX_CH_CC_REDUCED_CURRENT_IDENT |
-               PM2XXX_CH_CC_MODEDROP_DIS));
-
-       /* Input charger level of over voltage = 10V */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR2,
-                                       PM2XXX_VPWR2_OVV_10);
-       ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR1,
-                                       PM2XXX_VPWR1_OVV_10);
-
-       /* Input charger drop */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR2,
-               (PM2XXX_VPWR2_HW_OPT_DIS | PM2XXX_VPWR2_VALID_DIS |
-               PM2XXX_VPWR2_DROP_DIS));
-       ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR1,
-               (PM2XXX_VPWR1_HW_OPT_DIS | PM2XXX_VPWR1_VALID_DIS |
-               PM2XXX_VPWR1_DROP_DIS));
-
-       /* Disable battery low monitoring */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG,
-               PM2XXX_VBAT_LOW_MONITORING_ENA);
-
-       return ret;
-}
-
-static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
-       int enable, int vset, int iset)
-{
-       int ret;
-       int volt_index;
-       int curr_index;
-       u8 val;
-
-       struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger);
-
-       if (enable) {
-               if (!pm2->ac.charger_connected) {
-                       dev_dbg(pm2->dev, "AC charger not connected\n");
-                       return -ENXIO;
-               }
-
-               dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset);
-               if (!pm2->vddadc_en_ac) {
-                       ret = regulator_enable(pm2->regu);
-                       if (ret)
-                               dev_warn(pm2->dev,
-                                       "Failed to enable vddadc regulator\n");
-                       else
-                               pm2->vddadc_en_ac = true;
-               }
-
-               ret = pm2xxx_charging_init(pm2);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "%s charging init failed\n",
-                                       __func__);
-                       goto error_occured;
-               }
-
-               volt_index = pm2xxx_voltage_to_regval(vset);
-               curr_index = pm2xxx_current_to_regval(iset);
-
-               if (volt_index < 0 || curr_index < 0) {
-                       dev_err(pm2->dev,
-                               "Charger voltage or current too high, "
-                               "charging not started\n");
-                       return -ENXIO;
-               }
-
-               ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
-                       goto error_occured;
-               }
-               val &= ~PM2XXX_CH_VOLT_MASK;
-               val |= volt_index;
-               ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
-                       goto error_occured;
-               }
-
-               ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
-                       goto error_occured;
-               }
-               val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK;
-               val |= curr_index;
-               ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
-                       goto error_occured;
-               }
-
-               if (!pm2->bat->enable_overshoot) {
-                       ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val);
-                       if (ret < 0) {
-                               dev_err(pm2->dev, "%s pm2xxx read failed\n",
-                                                               __func__);
-                               goto error_occured;
-                       }
-                       val |= PM2XXX_ANTI_OVERSHOOT_EN;
-                       ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, val);
-                       if (ret < 0) {
-                               dev_err(pm2->dev, "%s pm2xxx write failed\n",
-                                                               __func__);
-                               goto error_occured;
-                       }
-               }
-
-               ret = pm2xxx_charging_enable_mngt(pm2);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "Failed to enable"
-                                               "pm2xxx ac charger\n");
-                       goto error_occured;
-               }
-
-               pm2->ac.charger_online = 1;
-       } else {
-               pm2->ac.charger_online = 0;
-               pm2->ac.wd_expired = false;
-
-               /* Disable regulator if enabled */
-               if (pm2->vddadc_en_ac) {
-                       regulator_disable(pm2->regu);
-                       pm2->vddadc_en_ac = false;
-               }
-
-               ret = pm2xxx_charging_disable_mngt(pm2);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "failed to disable"
-                                               "pm2xxx ac charger\n");
-                       goto error_occured;
-               }
-
-               dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n");
-       }
-       power_supply_changed(pm2->ac_chg.psy);
-
-error_occured:
-       return ret;
-}
-
-static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger)
-{
-       int ret;
-       struct pm2xxx_charger *pm2;
-
-       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
-               pm2 = to_pm2xxx_charger_ac_device_info(charger);
-       else
-               return -ENXIO;
-
-       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER);
-       if (ret)
-               dev_err(pm2->dev, "Failed to kick WD!\n");
-
-       return ret;
-}
-
-static void pm2xxx_charger_ac_work(struct work_struct *work)
-{
-       struct pm2xxx_charger *pm2 = container_of(work,
-               struct pm2xxx_charger, ac_work);
-
-
-       power_supply_changed(pm2->ac_chg.psy);
-       sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present");
-};
-
-static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work)
-{
-       u8 reg_value;
-
-       struct pm2xxx_charger *pm2 = container_of(work,
-               struct pm2xxx_charger, check_hw_failure_work.work);
-
-       if (pm2->flags.ovv) {
-               pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &reg_value);
-
-               if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV |
-                                       PM2XXX_INT4_S_ITVPWR2OVV))) {
-                       pm2->flags.ovv = false;
-                       power_supply_changed(pm2->ac_chg.psy);
-               }
-       }
-
-       /* If we still have a failure, schedule a new check */
-       if (pm2->flags.ovv) {
-               queue_delayed_work(pm2->charger_wq,
-                       &pm2->check_hw_failure_work, round_jiffies(HZ));
-       }
-}
-
-static void pm2xxx_charger_check_main_thermal_prot_work(
-       struct work_struct *work)
-{
-       int ret;
-       u8 val;
-
-       struct pm2xxx_charger *pm2 = container_of(work, struct pm2xxx_charger,
-                                       check_main_thermal_prot_work);
-
-       /* Check if die temp warning is still active */
-       ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT5, &val);
-       if (ret < 0) {
-               dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
-               return;
-       }
-       if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGRISE
-                       | PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE))
-               pm2->flags.main_thermal_prot = true;
-       else if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGFALL
-                               | PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL))
-               pm2->flags.main_thermal_prot = false;
-
-       power_supply_changed(pm2->ac_chg.psy);
-}
-
-static struct pm2xxx_interrupts pm2xxx_int = {
-       .handler[0] = pm2_int_reg0,
-       .handler[1] = pm2_int_reg1,
-       .handler[2] = pm2_int_reg2,
-       .handler[3] = pm2_int_reg3,
-       .handler[4] = pm2_int_reg4,
-       .handler[5] = pm2_int_reg5,
-};
-
-static struct pm2xxx_irq pm2xxx_charger_irq[] = {
-       {"PM2XXX_IRQ_INT", pm2xxx_irq_int},
-};
-
-static int __maybe_unused pm2xxx_wall_charger_resume(struct device *dev)
-{
-       struct i2c_client *i2c_client = to_i2c_client(dev);
-       struct pm2xxx_charger *pm2;
-
-       pm2 =  (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
-       set_lpn_pin(pm2);
-
-       /* If we still have a HW failure, schedule a new check */
-       if (pm2->flags.ovv)
-               queue_delayed_work(pm2->charger_wq,
-                               &pm2->check_hw_failure_work, 0);
-
-       return 0;
-}
-
-static int __maybe_unused pm2xxx_wall_charger_suspend(struct device *dev)
-{
-       struct i2c_client *i2c_client = to_i2c_client(dev);
-       struct pm2xxx_charger *pm2;
-
-       pm2 =  (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
-       clear_lpn_pin(pm2);
-
-       /* Cancel any pending HW failure check */
-       if (delayed_work_pending(&pm2->check_hw_failure_work))
-               cancel_delayed_work(&pm2->check_hw_failure_work);
-
-       flush_work(&pm2->ac_work);
-       flush_work(&pm2->check_main_thermal_prot_work);
-
-       return 0;
-}
-
-static int __maybe_unused pm2xxx_runtime_suspend(struct device *dev)
-{
-       struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
-       struct pm2xxx_charger *pm2;
-
-       pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
-       clear_lpn_pin(pm2);
-
-       return 0;
-}
-
-static int __maybe_unused pm2xxx_runtime_resume(struct device *dev)
-{
-       struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
-       struct pm2xxx_charger *pm2;
-
-       pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
-
-       if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0)
-               set_lpn_pin(pm2);
-
-       return 0;
-}
-
-static const struct dev_pm_ops pm2xxx_pm_ops __maybe_unused = {
-       SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend,
-               pm2xxx_wall_charger_resume)
-       SET_RUNTIME_PM_OPS(pm2xxx_runtime_suspend, pm2xxx_runtime_resume, NULL)
-};
-
-static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
-               const struct i2c_device_id *id)
-{
-       struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       struct pm2xxx_charger *pm2;
-       int ret = 0;
-       u8 val;
-       int i;
-
-       if (!pl_data) {
-               dev_err(&i2c_client->dev, "No platform data supplied\n");
-               return -EINVAL;
-       }
-
-       pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL);
-       if (!pm2) {
-               dev_err(&i2c_client->dev, "pm2xxx_charger allocation failed\n");
-               return -ENOMEM;
-       }
-
-       /* get parent data */
-       pm2->dev = &i2c_client->dev;
-
-       pm2->pm2_int = &pm2xxx_int;
-
-       /* get charger spcific platform data */
-       if (!pl_data->wall_charger) {
-               dev_err(pm2->dev, "no charger platform data supplied\n");
-               ret = -EINVAL;
-               goto free_device_info;
-       }
-
-       pm2->pdata = pl_data->wall_charger;
-
-       /* get battery specific platform data */
-       if (!pl_data->battery) {
-               dev_err(pm2->dev, "no battery platform data supplied\n");
-               ret = -EINVAL;
-               goto free_device_info;
-       }
-
-       pm2->bat = pl_data->battery;
-
-       if (!i2c_check_functionality(i2c_client->adapter,
-                       I2C_FUNC_SMBUS_BYTE_DATA |
-                       I2C_FUNC_SMBUS_READ_WORD_DATA)) {
-               ret = -ENODEV;
-               dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n");
-               goto free_device_info;
-       }
-
-       pm2->config.pm2xxx_i2c = i2c_client;
-       pm2->config.pm2xxx_id = (struct i2c_device_id *) id;
-       i2c_set_clientdata(i2c_client, pm2);
-
-       /* AC supply */
-       /* power_supply base class */
-       pm2->ac_chg_desc.name = pm2->pdata->label;
-       pm2->ac_chg_desc.type = POWER_SUPPLY_TYPE_MAINS;
-       pm2->ac_chg_desc.properties = pm2xxx_charger_ac_props;
-       pm2->ac_chg_desc.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props);
-       pm2->ac_chg_desc.get_property = pm2xxx_charger_ac_get_property;
-
-       psy_cfg.supplied_to = pm2->pdata->supplied_to;
-       psy_cfg.num_supplicants = pm2->pdata->num_supplicants;
-       /* pm2xxx_charger sub-class */
-       pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en;
-       pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick;
-       pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current;
-       pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[
-               ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1];
-       pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[
-               ARRAY_SIZE(pm2xxx_charger_current_map) - 1];
-       pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL;
-       pm2->ac_chg.enabled = true;
-       pm2->ac_chg.external = true;
-
-       /* Create a work queue for the charger */
-       pm2->charger_wq = create_singlethread_workqueue("pm2xxx_charger_wq");
-       if (pm2->charger_wq == NULL) {
-               ret = -ENOMEM;
-               dev_err(pm2->dev, "failed to create work queue\n");
-               goto free_device_info;
-       }
-
-       /* Init work for charger detection */
-       INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work);
-
-       /* Init work for checking HW status */
-       INIT_WORK(&pm2->check_main_thermal_prot_work,
-               pm2xxx_charger_check_main_thermal_prot_work);
-
-       /* Init work for HW failure check */
-       INIT_DEFERRABLE_WORK(&pm2->check_hw_failure_work,
-               pm2xxx_charger_check_hw_failure_work);
-
-       /*
-        * VDD ADC supply needs to be enabled from this driver when there
-        * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
-        * interrupts during charging
-        */
-       pm2->regu = regulator_get(pm2->dev, "vddadc");
-       if (IS_ERR(pm2->regu)) {
-               ret = PTR_ERR(pm2->regu);
-               dev_err(pm2->dev, "failed to get vddadc regulator\n");
-               goto free_charger_wq;
-       }
-
-       /* Register AC charger class */
-       pm2->ac_chg.psy = power_supply_register(pm2->dev, &pm2->ac_chg_desc,
-                                               &psy_cfg);
-       if (IS_ERR(pm2->ac_chg.psy)) {
-               dev_err(pm2->dev, "failed to register AC charger\n");
-               ret = PTR_ERR(pm2->ac_chg.psy);
-               goto free_regulator;
-       }
-
-       /* Register interrupts */
-       ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number),
-                               NULL,
-                               pm2xxx_charger_irq[0].isr,
-                               pm2->pdata->irq_type,
-                               pm2xxx_charger_irq[0].name, pm2);
-
-       if (ret != 0) {
-               dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n",
-               pm2xxx_charger_irq[0].name,
-                       gpio_to_irq(pm2->pdata->gpio_irq_number), ret);
-               goto unregister_pm2xxx_charger;
-       }
-
-       ret = pm_runtime_set_active(pm2->dev);
-       if (ret)
-               dev_err(pm2->dev, "set active Error\n");
-
-       pm_runtime_enable(pm2->dev);
-       pm_runtime_set_autosuspend_delay(pm2->dev, PM2XXX_AUTOSUSPEND_DELAY);
-       pm_runtime_use_autosuspend(pm2->dev);
-       pm_runtime_resume(pm2->dev);
-
-       /* pm interrupt can wake up system */
-       ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
-       if (ret) {
-               dev_err(pm2->dev, "failed to set irq wake\n");
-               goto unregister_pm2xxx_interrupt;
-       }
-
-       mutex_init(&pm2->lock);
-
-       if (gpio_is_valid(pm2->pdata->lpn_gpio)) {
-               /* get lpn GPIO from platform data */
-               pm2->lpn_pin = pm2->pdata->lpn_gpio;
-
-               /*
-                * Charger detection mechanism requires pulling up the LPN pin
-                * while i2c communication if Charger is not connected
-                * LPN pin of PM2301 is GPIO60 of AB9540
-                */
-               ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio");
-
-               if (ret < 0) {
-                       dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n");
-                       goto disable_pm2_irq_wake;
-               }
-               ret = gpio_direction_output(pm2->lpn_pin, 0);
-               if (ret < 0) {
-                       dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n");
-                       goto free_gpio;
-               }
-               set_lpn_pin(pm2);
-       }
-
-       /* read  interrupt registers */
-       for (i = 0; i < PM2XXX_NUM_INT_REG; i++)
-               pm2xxx_reg_read(pm2,
-                       pm2xxx_interrupt_registers[i],
-                       &val);
-
-       ret = pm2xxx_charger_detection(pm2, &val);
-
-       if ((ret == 0) && val) {
-               pm2->ac.charger_connected = 1;
-               ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
-                                            AB8500_MAIN_CH_DET);
-               pm2->ac_conn = true;
-               power_supply_changed(pm2->ac_chg.psy);
-               sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present");
-       }
-
-       return 0;
-
-free_gpio:
-       if (gpio_is_valid(pm2->lpn_pin))
-               gpio_free(pm2->lpn_pin);
-disable_pm2_irq_wake:
-       disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
-unregister_pm2xxx_interrupt:
-       /* disable interrupt */
-       free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
-unregister_pm2xxx_charger:
-       /* unregister power supply */
-       power_supply_unregister(pm2->ac_chg.psy);
-free_regulator:
-       /* disable the regulator */
-       regulator_put(pm2->regu);
-free_charger_wq:
-       destroy_workqueue(pm2->charger_wq);
-free_device_info:
-       kfree(pm2);
-
-       return ret;
-}
-
-static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
-{
-       struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client);
-
-       /* Disable pm_runtime */
-       pm_runtime_disable(pm2->dev);
-       /* Disable AC charging */
-       pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0);
-
-       /* Disable wake by pm interrupt */
-       disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
-
-       /* Disable interrupts */
-       free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
-
-       /* Delete the work queue */
-       destroy_workqueue(pm2->charger_wq);
-
-       flush_scheduled_work();
-
-       /* disable the regulator */
-       regulator_put(pm2->regu);
-
-       power_supply_unregister(pm2->ac_chg.psy);
-
-       if (gpio_is_valid(pm2->lpn_pin))
-               gpio_free(pm2->lpn_pin);
-
-       kfree(pm2);
-
-       return 0;
-}
-
-static const struct i2c_device_id pm2xxx_id[] = {
-       { "pm2301", 0 },
-       { }
-};
-
-MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
-
-static struct i2c_driver pm2xxx_charger_driver = {
-       .probe = pm2xxx_wall_charger_probe,
-       .remove = pm2xxx_wall_charger_remove,
-       .driver = {
-               .name = "pm2xxx-wall_charger",
-               .pm = IS_ENABLED(CONFIG_PM) ? &pm2xxx_pm_ops : NULL,
-       },
-       .id_table = pm2xxx_id,
-};
-
-static int __init pm2xxx_charger_init(void)
-{
-       return i2c_add_driver(&pm2xxx_charger_driver);
-}
-
-static void __exit pm2xxx_charger_exit(void)
-{
-       i2c_del_driver(&pm2xxx_charger_driver);
-}
-
-device_initcall_sync(pm2xxx_charger_init);
-module_exit(pm2xxx_charger_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
-MODULE_DESCRIPTION("PM2xxx charger management driver");
diff --git a/drivers/power/pm2301_charger.h b/drivers/power/pm2301_charger.h
deleted file mode 100644 (file)
index 24181cf..0000000
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * PM2301 power supply interface
- *
- * License terms:  GNU General Public License (GPL), version 2
- */
-
-#ifndef PM2301_CHARGER_H
-#define PM2301_CHARGER_H
-
-/* Watchdog timeout constant */
-#define WD_TIMER                       0x30 /* 4min */
-#define WD_KICK_INTERVAL               (30 * HZ)
-
-#define PM2XXX_NUM_INT_REG             0x6
-
-/* Constant voltage/current */
-#define PM2XXX_CONST_CURR              0x0
-#define PM2XXX_CONST_VOLT              0x1
-
-/* Lowest charger voltage is 3.39V -> 0x4E */
-#define LOW_VOLT_REG                   0x4E
-
-#define PM2XXX_BATT_CTRL_REG1          0x00
-#define PM2XXX_BATT_CTRL_REG2          0x01
-#define PM2XXX_BATT_CTRL_REG3          0x02
-#define PM2XXX_BATT_CTRL_REG4          0x03
-#define PM2XXX_BATT_CTRL_REG5          0x04
-#define PM2XXX_BATT_CTRL_REG6          0x05
-#define PM2XXX_BATT_CTRL_REG7          0x06
-#define PM2XXX_BATT_CTRL_REG8          0x07
-#define PM2XXX_NTC_CTRL_REG1           0x08
-#define PM2XXX_NTC_CTRL_REG2           0x09
-#define PM2XXX_BATT_CTRL_REG9          0x0A
-#define PM2XXX_BATT_STAT_REG1          0x0B
-#define PM2XXX_INP_VOLT_VPWR2          0x11
-#define PM2XXX_INP_DROP_VPWR2          0x13
-#define PM2XXX_INP_VOLT_VPWR1          0x15
-#define PM2XXX_INP_DROP_VPWR1          0x17
-#define PM2XXX_INP_MODE_VPWR           0x18
-#define PM2XXX_BATT_WD_KICK            0x70
-#define PM2XXX_DEV_VER_STAT            0x0C
-#define PM2XXX_THERM_WARN_CTRL_REG     0x20
-#define PM2XXX_BATT_DISC_REG           0x21
-#define PM2XXX_BATT_LOW_LEV_COMP_REG   0x22
-#define PM2XXX_BATT_LOW_LEV_VAL_REG    0x23
-#define PM2XXX_I2C_PAD_CTRL_REG                0x24
-#define PM2XXX_SW_CTRL_REG             0x26
-#define PM2XXX_LED_CTRL_REG            0x28
-
-#define PM2XXX_REG_INT1                        0x40
-#define PM2XXX_MASK_REG_INT1           0x50
-#define PM2XXX_SRCE_REG_INT1           0x60
-#define PM2XXX_REG_INT2                        0x41
-#define PM2XXX_MASK_REG_INT2           0x51
-#define PM2XXX_SRCE_REG_INT2           0x61
-#define PM2XXX_REG_INT3                        0x42
-#define PM2XXX_MASK_REG_INT3           0x52
-#define PM2XXX_SRCE_REG_INT3           0x62
-#define PM2XXX_REG_INT4                        0x43
-#define PM2XXX_MASK_REG_INT4           0x53
-#define PM2XXX_SRCE_REG_INT4           0x63
-#define PM2XXX_REG_INT5                        0x44
-#define PM2XXX_MASK_REG_INT5           0x54
-#define PM2XXX_SRCE_REG_INT5           0x64
-#define PM2XXX_REG_INT6                        0x45
-#define PM2XXX_MASK_REG_INT6           0x55
-#define PM2XXX_SRCE_REG_INT6           0x65
-
-#define VPWR_OVV                       0x0
-#define VSYSTEM_OVV                    0x1
-
-/* control Reg 1 */
-#define PM2XXX_CH_RESUME_EN            0x1
-#define PM2XXX_CH_RESUME_DIS           0x0
-
-/* control Reg 2 */
-#define PM2XXX_CH_AUTO_RESUME_EN       0X2
-#define PM2XXX_CH_AUTO_RESUME_DIS      0X0
-#define PM2XXX_CHARGER_ENA             0x4
-#define PM2XXX_CHARGER_DIS             0x0
-
-/* control Reg 3 */
-#define PM2XXX_CH_WD_CC_PHASE_OFF      0x0
-#define PM2XXX_CH_WD_CC_PHASE_5MIN     0x1
-#define PM2XXX_CH_WD_CC_PHASE_10MIN    0x2
-#define PM2XXX_CH_WD_CC_PHASE_30MIN    0x3
-#define PM2XXX_CH_WD_CC_PHASE_60MIN    0x4
-#define PM2XXX_CH_WD_CC_PHASE_120MIN   0x5
-#define PM2XXX_CH_WD_CC_PHASE_240MIN   0x6
-#define PM2XXX_CH_WD_CC_PHASE_360MIN   0x7
-
-#define PM2XXX_CH_WD_CV_PHASE_OFF      (0x0<<3)
-#define PM2XXX_CH_WD_CV_PHASE_5MIN     (0x1<<3)
-#define PM2XXX_CH_WD_CV_PHASE_10MIN    (0x2<<3)
-#define PM2XXX_CH_WD_CV_PHASE_30MIN    (0x3<<3)
-#define PM2XXX_CH_WD_CV_PHASE_60MIN    (0x4<<3)
-#define PM2XXX_CH_WD_CV_PHASE_120MIN   (0x5<<3)
-#define PM2XXX_CH_WD_CV_PHASE_240MIN   (0x6<<3)
-#define PM2XXX_CH_WD_CV_PHASE_360MIN   (0x7<<3)
-
-/* control Reg 4 */
-#define PM2XXX_CH_WD_PRECH_PHASE_OFF   0x0
-#define PM2XXX_CH_WD_PRECH_PHASE_1MIN  0x1
-#define PM2XXX_CH_WD_PRECH_PHASE_5MIN  0x2
-#define PM2XXX_CH_WD_PRECH_PHASE_10MIN 0x3
-#define PM2XXX_CH_WD_PRECH_PHASE_30MIN 0x4
-#define PM2XXX_CH_WD_PRECH_PHASE_60MIN 0x5
-#define PM2XXX_CH_WD_PRECH_PHASE_120MIN        0x6
-#define PM2XXX_CH_WD_PRECH_PHASE_240MIN        0x7
-
-/* control Reg 5 */
-#define PM2XXX_CH_WD_AUTO_TIMEOUT_NONE 0x0
-#define PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN        0x1
-
-/* control Reg 6 */
-#define PM2XXX_DIR_CH_CC_CURRENT_MASK  0x0F
-#define PM2XXX_DIR_CH_CC_CURRENT_200MA 0x0
-#define PM2XXX_DIR_CH_CC_CURRENT_400MA 0x2
-#define PM2XXX_DIR_CH_CC_CURRENT_600MA 0x3
-#define PM2XXX_DIR_CH_CC_CURRENT_800MA 0x4
-#define PM2XXX_DIR_CH_CC_CURRENT_1000MA        0x5
-#define PM2XXX_DIR_CH_CC_CURRENT_1200MA        0x6
-#define PM2XXX_DIR_CH_CC_CURRENT_1400MA        0x7
-#define PM2XXX_DIR_CH_CC_CURRENT_1600MA        0x8
-#define PM2XXX_DIR_CH_CC_CURRENT_1800MA        0x9
-#define PM2XXX_DIR_CH_CC_CURRENT_2000MA        0xA
-#define PM2XXX_DIR_CH_CC_CURRENT_2200MA        0xB
-#define PM2XXX_DIR_CH_CC_CURRENT_2400MA        0xC
-#define PM2XXX_DIR_CH_CC_CURRENT_2600MA        0xD
-#define PM2XXX_DIR_CH_CC_CURRENT_2800MA        0xE
-#define PM2XXX_DIR_CH_CC_CURRENT_3000MA        0xF
-
-#define PM2XXX_CH_PRECH_CURRENT_MASK   0x30
-#define PM2XXX_CH_PRECH_CURRENT_25MA   (0x0<<4)
-#define PM2XXX_CH_PRECH_CURRENT_50MA   (0x1<<4)
-#define PM2XXX_CH_PRECH_CURRENT_75MA   (0x2<<4)
-#define PM2XXX_CH_PRECH_CURRENT_100MA  (0x3<<4)
-
-#define PM2XXX_CH_EOC_CURRENT_MASK     0xC0
-#define PM2XXX_CH_EOC_CURRENT_100MA    (0x0<<6)
-#define PM2XXX_CH_EOC_CURRENT_150MA    (0x1<<6)
-#define PM2XXX_CH_EOC_CURRENT_300MA    (0x2<<6)
-#define PM2XXX_CH_EOC_CURRENT_400MA    (0x3<<6)
-
-/* control Reg 7 */
-#define PM2XXX_CH_PRECH_VOL_2_5                0x0
-#define PM2XXX_CH_PRECH_VOL_2_7                0x1
-#define PM2XXX_CH_PRECH_VOL_2_9                0x2
-#define PM2XXX_CH_PRECH_VOL_3_1                0x3
-
-#define PM2XXX_CH_VRESUME_VOL_3_2      (0x0<<2)
-#define PM2XXX_CH_VRESUME_VOL_3_4      (0x1<<2)
-#define PM2XXX_CH_VRESUME_VOL_3_6      (0x2<<2)
-#define PM2XXX_CH_VRESUME_VOL_3_8      (0x3<<2)
-
-/* control Reg 8 */
-#define PM2XXX_CH_VOLT_MASK            0x3F
-#define PM2XXX_CH_VOLT_3_5             0x0
-#define PM2XXX_CH_VOLT_3_5225          0x1
-#define PM2XXX_CH_VOLT_3_6             0x4
-#define PM2XXX_CH_VOLT_3_7             0x8
-#define PM2XXX_CH_VOLT_4_0             0x14
-#define PM2XXX_CH_VOLT_4_175           0x1B
-#define PM2XXX_CH_VOLT_4_2             0x1C
-#define PM2XXX_CH_VOLT_4_275           0x1F
-#define PM2XXX_CH_VOLT_4_3             0x20
-
-/*NTC control register 1*/
-#define PM2XXX_BTEMP_HIGH_TH_45                0x0
-#define PM2XXX_BTEMP_HIGH_TH_50                0x1
-#define PM2XXX_BTEMP_HIGH_TH_55                0x2
-#define PM2XXX_BTEMP_HIGH_TH_60                0x3
-#define PM2XXX_BTEMP_HIGH_TH_65                0x4
-
-#define PM2XXX_BTEMP_LOW_TH_N5         (0x0<<3)
-#define PM2XXX_BTEMP_LOW_TH_0          (0x1<<3)
-#define PM2XXX_BTEMP_LOW_TH_5          (0x2<<3)
-#define PM2XXX_BTEMP_LOW_TH_10         (0x3<<3)
-
-/*NTC control register 2*/
-#define PM2XXX_NTC_BETA_COEFF_3477     0x0
-#define PM2XXX_NTC_BETA_COEFF_3964     0x1
-
-#define PM2XXX_NTC_RES_10K             (0x0<<2)
-#define PM2XXX_NTC_RES_47K             (0x1<<2)
-#define PM2XXX_NTC_RES_100K            (0x2<<2)
-#define PM2XXX_NTC_RES_NO_NTC          (0x3<<2)
-
-/* control Reg 9 */
-#define PM2XXX_CH_CC_MODEDROP_EN       1
-#define PM2XXX_CH_CC_MODEDROP_DIS      0
-
-#define PM2XXX_CH_CC_REDUCED_CURRENT_100MA     (0x0<<1)
-#define PM2XXX_CH_CC_REDUCED_CURRENT_200MA     (0x1<<1)
-#define PM2XXX_CH_CC_REDUCED_CURRENT_400MA     (0x2<<1)
-#define PM2XXX_CH_CC_REDUCED_CURRENT_IDENT     (0x3<<1)
-
-#define PM2XXX_CHARCHING_INFO_DIS      (0<<3)
-#define PM2XXX_CHARCHING_INFO_EN       (1<<3)
-
-#define PM2XXX_CH_150MV_DROP_300MV     (0<<4)
-#define PM2XXX_CH_150MV_DROP_150MV     (1<<4)
-
-
-/* charger status register */
-#define PM2XXX_CHG_STATUS_OFF          0x0
-#define PM2XXX_CHG_STATUS_ON           0x1
-#define PM2XXX_CHG_STATUS_FULL         0x2
-#define PM2XXX_CHG_STATUS_ERR          0x3
-#define PM2XXX_CHG_STATUS_WAIT         0x4
-#define PM2XXX_CHG_STATUS_NOBAT                0x5
-
-/* Input charger voltage VPWR2 */
-#define PM2XXX_VPWR2_OVV_6_0           0x0
-#define PM2XXX_VPWR2_OVV_6_3           0x1
-#define PM2XXX_VPWR2_OVV_10            0x2
-#define PM2XXX_VPWR2_OVV_NONE          0x3
-
-/* Input charger drop VPWR2 */
-#define PM2XXX_VPWR2_HW_OPT_EN         (0x1<<4)
-#define PM2XXX_VPWR2_HW_OPT_DIS                (0x0<<4)
-
-#define PM2XXX_VPWR2_VALID_EN          (0x1<<3)
-#define PM2XXX_VPWR2_VALID_DIS         (0x0<<3)
-
-#define PM2XXX_VPWR2_DROP_EN           (0x1<<2)
-#define PM2XXX_VPWR2_DROP_DIS          (0x0<<2)
-
-/* Input charger voltage VPWR1 */
-#define PM2XXX_VPWR1_OVV_6_0           0x0
-#define PM2XXX_VPWR1_OVV_6_3           0x1
-#define PM2XXX_VPWR1_OVV_10            0x2
-#define PM2XXX_VPWR1_OVV_NONE          0x3
-
-/* Input charger drop VPWR1 */
-#define PM2XXX_VPWR1_HW_OPT_EN         (0x1<<4)
-#define PM2XXX_VPWR1_HW_OPT_DIS                (0x0<<4)
-
-#define PM2XXX_VPWR1_VALID_EN          (0x1<<3)
-#define PM2XXX_VPWR1_VALID_DIS         (0x0<<3)
-
-#define PM2XXX_VPWR1_DROP_EN           (0x1<<2)
-#define PM2XXX_VPWR1_DROP_DIS          (0x0<<2)
-
-/* Battery low level comparator control register */
-#define PM2XXX_VBAT_LOW_MONITORING_DIS 0x0
-#define PM2XXX_VBAT_LOW_MONITORING_ENA 0x1
-
-/* Battery low level value control register */
-#define PM2XXX_VBAT_LOW_LEVEL_2_3      0x0
-#define PM2XXX_VBAT_LOW_LEVEL_2_4      0x1
-#define PM2XXX_VBAT_LOW_LEVEL_2_5      0x2
-#define PM2XXX_VBAT_LOW_LEVEL_2_6      0x3
-#define PM2XXX_VBAT_LOW_LEVEL_2_7      0x4
-#define PM2XXX_VBAT_LOW_LEVEL_2_8      0x5
-#define PM2XXX_VBAT_LOW_LEVEL_2_9      0x6
-#define PM2XXX_VBAT_LOW_LEVEL_3_0      0x7
-#define PM2XXX_VBAT_LOW_LEVEL_3_1      0x8
-#define PM2XXX_VBAT_LOW_LEVEL_3_2      0x9
-#define PM2XXX_VBAT_LOW_LEVEL_3_3      0xA
-#define PM2XXX_VBAT_LOW_LEVEL_3_4      0xB
-#define PM2XXX_VBAT_LOW_LEVEL_3_5      0xC
-#define PM2XXX_VBAT_LOW_LEVEL_3_6      0xD
-#define PM2XXX_VBAT_LOW_LEVEL_3_7      0xE
-#define PM2XXX_VBAT_LOW_LEVEL_3_8      0xF
-#define PM2XXX_VBAT_LOW_LEVEL_3_9      0x10
-#define PM2XXX_VBAT_LOW_LEVEL_4_0      0x11
-#define PM2XXX_VBAT_LOW_LEVEL_4_1      0x12
-#define PM2XXX_VBAT_LOW_LEVEL_4_2      0x13
-
-/* SW CTRL */
-#define PM2XXX_SWCTRL_HW               0x0
-#define PM2XXX_SWCTRL_SW               0x1
-
-
-/* LED Driver Control */
-#define PM2XXX_LED_CURRENT_MASK                0x0C
-#define PM2XXX_LED_CURRENT_2_5MA       (0X0<<2)
-#define PM2XXX_LED_CURRENT_1MA         (0X1<<2)
-#define PM2XXX_LED_CURRENT_5MA         (0X2<<2)
-#define PM2XXX_LED_CURRENT_10MA                (0X3<<2)
-
-#define PM2XXX_LED_SELECT_MASK         0x02
-#define PM2XXX_LED_SELECT_EN           (0X0<<1)
-#define PM2XXX_LED_SELECT_DIS          (0X1<<1)
-
-#define PM2XXX_ANTI_OVERSHOOT_MASK     0x01
-#define PM2XXX_ANTI_OVERSHOOT_DIS      0X0
-#define PM2XXX_ANTI_OVERSHOOT_EN       0X1
-
-enum pm2xxx_reg_int1 {
-       PM2XXX_INT1_ITVBATDISCONNECT    = 0x02,
-       PM2XXX_INT1_ITVBATLOWR          = 0x04,
-       PM2XXX_INT1_ITVBATLOWF          = 0x08,
-};
-
-enum pm2xxx_mask_reg_int1 {
-       PM2XXX_INT1_M_ITVBATDISCONNECT  = 0x02,
-       PM2XXX_INT1_M_ITVBATLOWR        = 0x04,
-       PM2XXX_INT1_M_ITVBATLOWF        = 0x08,
-};
-
-enum pm2xxx_source_reg_int1 {
-       PM2XXX_INT1_S_ITVBATDISCONNECT  = 0x02,
-       PM2XXX_INT1_S_ITVBATLOWR        = 0x04,
-       PM2XXX_INT1_S_ITVBATLOWF        = 0x08,
-};
-
-enum pm2xxx_reg_int2 {
-       PM2XXX_INT2_ITVPWR2PLUG         = 0x01,
-       PM2XXX_INT2_ITVPWR2UNPLUG       = 0x02,
-       PM2XXX_INT2_ITVPWR1PLUG         = 0x04,
-       PM2XXX_INT2_ITVPWR1UNPLUG       = 0x08,
-};
-
-enum pm2xxx_mask_reg_int2 {
-       PM2XXX_INT2_M_ITVPWR2PLUG       = 0x01,
-       PM2XXX_INT2_M_ITVPWR2UNPLUG     = 0x02,
-       PM2XXX_INT2_M_ITVPWR1PLUG       = 0x04,
-       PM2XXX_INT2_M_ITVPWR1UNPLUG     = 0x08,
-};
-
-enum pm2xxx_source_reg_int2 {
-       PM2XXX_INT2_S_ITVPWR2PLUG       = 0x03,
-       PM2XXX_INT2_S_ITVPWR1PLUG       = 0x0c,
-};
-
-enum pm2xxx_reg_int3 {
-       PM2XXX_INT3_ITCHPRECHARGEWD     = 0x01,
-       PM2XXX_INT3_ITCHCCWD            = 0x02,
-       PM2XXX_INT3_ITCHCVWD            = 0x04,
-       PM2XXX_INT3_ITAUTOTIMEOUTWD     = 0x08,
-};
-
-enum pm2xxx_mask_reg_int3 {
-       PM2XXX_INT3_M_ITCHPRECHARGEWD   = 0x01,
-       PM2XXX_INT3_M_ITCHCCWD          = 0x02,
-       PM2XXX_INT3_M_ITCHCVWD          = 0x04,
-       PM2XXX_INT3_M_ITAUTOTIMEOUTWD   = 0x08,
-};
-
-enum pm2xxx_source_reg_int3 {
-       PM2XXX_INT3_S_ITCHPRECHARGEWD   = 0x01,
-       PM2XXX_INT3_S_ITCHCCWD          = 0x02,
-       PM2XXX_INT3_S_ITCHCVWD          = 0x04,
-       PM2XXX_INT3_S_ITAUTOTIMEOUTWD   = 0x08,
-};
-
-enum pm2xxx_reg_int4 {
-       PM2XXX_INT4_ITBATTEMPCOLD       = 0x01,
-       PM2XXX_INT4_ITBATTEMPHOT        = 0x02,
-       PM2XXX_INT4_ITVPWR2OVV          = 0x04,
-       PM2XXX_INT4_ITVPWR1OVV          = 0x08,
-       PM2XXX_INT4_ITCHARGINGON        = 0x10,
-       PM2XXX_INT4_ITVRESUME           = 0x20,
-       PM2XXX_INT4_ITBATTFULL          = 0x40,
-       PM2XXX_INT4_ITCVPHASE           = 0x80,
-};
-
-enum pm2xxx_mask_reg_int4 {
-       PM2XXX_INT4_M_ITBATTEMPCOLD     = 0x01,
-       PM2XXX_INT4_M_ITBATTEMPHOT      = 0x02,
-       PM2XXX_INT4_M_ITVPWR2OVV        = 0x04,
-       PM2XXX_INT4_M_ITVPWR1OVV        = 0x08,
-       PM2XXX_INT4_M_ITCHARGINGON      = 0x10,
-       PM2XXX_INT4_M_ITVRESUME         = 0x20,
-       PM2XXX_INT4_M_ITBATTFULL        = 0x40,
-       PM2XXX_INT4_M_ITCVPHASE         = 0x80,
-};
-
-enum pm2xxx_source_reg_int4 {
-       PM2XXX_INT4_S_ITBATTEMPCOLD     = 0x01,
-       PM2XXX_INT4_S_ITBATTEMPHOT      = 0x02,
-       PM2XXX_INT4_S_ITVPWR2OVV        = 0x04,
-       PM2XXX_INT4_S_ITVPWR1OVV        = 0x08,
-       PM2XXX_INT4_S_ITCHARGINGON      = 0x10,
-       PM2XXX_INT4_S_ITVRESUME         = 0x20,
-       PM2XXX_INT4_S_ITBATTFULL        = 0x40,
-       PM2XXX_INT4_S_ITCVPHASE         = 0x80,
-};
-
-enum pm2xxx_reg_int5 {
-       PM2XXX_INT5_ITTHERMALSHUTDOWNRISE       = 0x01,
-       PM2XXX_INT5_ITTHERMALSHUTDOWNFALL       = 0x02,
-       PM2XXX_INT5_ITTHERMALWARNINGRISE        = 0x04,
-       PM2XXX_INT5_ITTHERMALWARNINGFALL        = 0x08,
-       PM2XXX_INT5_ITVSYSTEMOVV                = 0x10,
-};
-
-enum pm2xxx_mask_reg_int5 {
-       PM2XXX_INT5_M_ITTHERMALSHUTDOWNRISE     = 0x01,
-       PM2XXX_INT5_M_ITTHERMALSHUTDOWNFALL     = 0x02,
-       PM2XXX_INT5_M_ITTHERMALWARNINGRISE      = 0x04,
-       PM2XXX_INT5_M_ITTHERMALWARNINGFALL      = 0x08,
-       PM2XXX_INT5_M_ITVSYSTEMOVV              = 0x10,
-};
-
-enum pm2xxx_source_reg_int5 {
-       PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE     = 0x01,
-       PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL     = 0x02,
-       PM2XXX_INT5_S_ITTHERMALWARNINGRISE      = 0x04,
-       PM2XXX_INT5_S_ITTHERMALWARNINGFALL      = 0x08,
-       PM2XXX_INT5_S_ITVSYSTEMOVV              = 0x10,
-};
-
-enum pm2xxx_reg_int6 {
-       PM2XXX_INT6_ITVPWR2DROP         = 0x01,
-       PM2XXX_INT6_ITVPWR1DROP         = 0x02,
-       PM2XXX_INT6_ITVPWR2VALIDRISE    = 0x04,
-       PM2XXX_INT6_ITVPWR2VALIDFALL    = 0x08,
-       PM2XXX_INT6_ITVPWR1VALIDRISE    = 0x10,
-       PM2XXX_INT6_ITVPWR1VALIDFALL    = 0x20,
-};
-
-enum pm2xxx_mask_reg_int6 {
-       PM2XXX_INT6_M_ITVPWR2DROP       = 0x01,
-       PM2XXX_INT6_M_ITVPWR1DROP       = 0x02,
-       PM2XXX_INT6_M_ITVPWR2VALIDRISE  = 0x04,
-       PM2XXX_INT6_M_ITVPWR2VALIDFALL  = 0x08,
-       PM2XXX_INT6_M_ITVPWR1VALIDRISE  = 0x10,
-       PM2XXX_INT6_M_ITVPWR1VALIDFALL  = 0x20,
-};
-
-enum pm2xxx_source_reg_int6 {
-       PM2XXX_INT6_S_ITVPWR2DROP       = 0x01,
-       PM2XXX_INT6_S_ITVPWR1DROP       = 0x02,
-       PM2XXX_INT6_S_ITVPWR2VALIDRISE  = 0x04,
-       PM2XXX_INT6_S_ITVPWR2VALIDFALL  = 0x08,
-       PM2XXX_INT6_S_ITVPWR1VALIDRISE  = 0x10,
-       PM2XXX_INT6_S_ITVPWR1VALIDFALL  = 0x20,
-};
-
-struct pm2xxx_charger_info {
-       int charger_connected;
-       int charger_online;
-       int cv_active;
-       bool wd_expired;
-};
-
-struct pm2xxx_charger_event_flags {
-       bool mainextchnotok;
-       bool main_thermal_prot;
-       bool ovv;
-       bool chgwdexp;
-};
-
-struct pm2xxx_interrupts {
-       u8 reg[PM2XXX_NUM_INT_REG];
-       int (*handler[PM2XXX_NUM_INT_REG])(void *, int);
-};
-
-struct pm2xxx_config {
-       struct i2c_client *pm2xxx_i2c;
-       struct i2c_device_id *pm2xxx_id;
-};
-
-struct pm2xxx_irq {
-       char *name;
-       irqreturn_t (*isr)(int irq, void *data);
-};
-
-struct pm2xxx_charger {
-       struct device *dev;
-       u8 chip_id;
-       bool vddadc_en_ac;
-       struct pm2xxx_config config;
-       bool ac_conn;
-       unsigned int gpio_irq;
-       int vbat;
-       int old_vbat;
-       int failure_case;
-       int failure_input_ovv;
-       unsigned int lpn_pin;
-       struct pm2xxx_interrupts *pm2_int;
-       struct regulator *regu;
-       struct pm2xxx_bm_data *bat;
-       struct mutex lock;
-       struct ab8500 *parent;
-       struct pm2xxx_charger_info ac;
-       struct pm2xxx_charger_platform_data *pdata;
-       struct workqueue_struct *charger_wq;
-       struct delayed_work check_vbat_work;
-       struct work_struct ac_work;
-       struct work_struct check_main_thermal_prot_work;
-       struct delayed_work check_hw_failure_work;
-       struct ux500_charger ac_chg;
-       struct power_supply_desc ac_chg_desc;
-       struct pm2xxx_charger_event_flags flags;
-};
-
-#endif /* PM2301_CHARGER_H */
diff --git a/drivers/power/pmu_battery.c b/drivers/power/pmu_battery.c
deleted file mode 100644 (file)
index 9c8d525..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Battery class driver for Apple PMU
- *
- *     Copyright Â© 2006  David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/power_supply.h>
-#include <linux/adb.h>
-#include <linux/pmu.h>
-#include <linux/slab.h>
-
-static struct pmu_battery_dev {
-       struct power_supply *bat;
-       struct power_supply_desc bat_desc;
-       struct pmu_battery_info *pbi;
-       char name[16];
-       int propval;
-} *pbats[PMU_MAX_BATTERIES];
-
-#define to_pmu_battery_dev(x) power_supply_get_drvdata(x)
-
-/*********************************************************************
- *             Power
- *********************************************************************/
-
-static int pmu_get_ac_prop(struct power_supply *psy,
-                          enum power_supply_property psp,
-                          union power_supply_propval *val)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) ||
-                             (pmu_battery_count == 0);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property pmu_ac_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static const struct power_supply_desc pmu_ac_desc = {
-       .name = "pmu-ac",
-       .type = POWER_SUPPLY_TYPE_MAINS,
-       .properties = pmu_ac_props,
-       .num_properties = ARRAY_SIZE(pmu_ac_props),
-       .get_property = pmu_get_ac_prop,
-};
-
-static struct power_supply *pmu_ac;
-
-/*********************************************************************
- *             Battery properties
- *********************************************************************/
-
-static char *pmu_batt_types[] = {
-       "Smart", "Comet", "Hooper", "Unknown"
-};
-
-static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi)
-{
-       switch (pbi->flags & PMU_BATT_TYPE_MASK) {
-       case PMU_BATT_TYPE_SMART:
-               return pmu_batt_types[0];
-       case PMU_BATT_TYPE_COMET:
-               return pmu_batt_types[1];
-       case PMU_BATT_TYPE_HOOPER:
-               return pmu_batt_types[2];
-       default: break;
-       }
-       return pmu_batt_types[3];
-}
-
-static int pmu_bat_get_property(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy);
-       struct pmu_battery_info *pbi = pbat->pbi;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (pbi->flags & PMU_BATT_CHARGING)
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else if (pmu_power_flags & PMU_PWR_AC_PRESENT)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = !!(pbi->flags & PMU_BATT_PRESENT);
-               break;
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = pmu_bat_get_model_name(pbi);
-               break;
-       case POWER_SUPPLY_PROP_ENERGY_AVG:
-               val->intval = pbi->charge     * 1000; /* mWh -> ÂµWh */
-               break;
-       case POWER_SUPPLY_PROP_ENERGY_FULL:
-               val->intval = pbi->max_charge * 1000; /* mWh -> ÂµWh */
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_AVG:
-               val->intval = pbi->amperage   * 1000; /* mA -> ÂµA */
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               val->intval = pbi->voltage    * 1000; /* mV -> ÂµV */
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
-               val->intval = pbi->time_remaining;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property pmu_bat_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_ENERGY_AVG,
-       POWER_SUPPLY_PROP_ENERGY_FULL,
-       POWER_SUPPLY_PROP_CURRENT_AVG,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
-};
-
-/*********************************************************************
- *             Initialisation
- *********************************************************************/
-
-static struct platform_device *bat_pdev;
-
-static int __init pmu_bat_init(void)
-{
-       int ret = 0;
-       int i;
-
-       bat_pdev = platform_device_register_simple("pmu-battery",
-                                                  0, NULL, 0);
-       if (IS_ERR(bat_pdev)) {
-               ret = PTR_ERR(bat_pdev);
-               goto pdev_register_failed;
-       }
-
-       pmu_ac = power_supply_register(&bat_pdev->dev, &pmu_ac_desc, NULL);
-       if (IS_ERR(pmu_ac)) {
-               ret = PTR_ERR(pmu_ac);
-               goto ac_register_failed;
-       }
-
-       for (i = 0; i < pmu_battery_count; i++) {
-               struct power_supply_config psy_cfg = {};
-               struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat),
-                                                      GFP_KERNEL);
-               if (!pbat)
-                       break;
-
-               sprintf(pbat->name, "PMU_battery_%d", i);
-               pbat->bat_desc.name = pbat->name;
-               pbat->bat_desc.properties = pmu_bat_props;
-               pbat->bat_desc.num_properties = ARRAY_SIZE(pmu_bat_props);
-               pbat->bat_desc.get_property = pmu_bat_get_property;
-               pbat->pbi = &pmu_batteries[i];
-               psy_cfg.drv_data = pbat;
-
-               pbat->bat = power_supply_register(&bat_pdev->dev,
-                                                 &pbat->bat_desc,
-                                                 &psy_cfg);
-               if (IS_ERR(pbat->bat)) {
-                       ret = PTR_ERR(pbat->bat);
-                       kfree(pbat);
-                       goto battery_register_failed;
-               }
-               pbats[i] = pbat;
-       }
-
-       goto success;
-
-battery_register_failed:
-       while (i--) {
-               if (!pbats[i])
-                       continue;
-               power_supply_unregister(pbats[i]->bat);
-               kfree(pbats[i]);
-       }
-       power_supply_unregister(pmu_ac);
-ac_register_failed:
-       platform_device_unregister(bat_pdev);
-pdev_register_failed:
-success:
-       return ret;
-}
-
-static void __exit pmu_bat_exit(void)
-{
-       int i;
-
-       for (i = 0; i < PMU_MAX_BATTERIES; i++) {
-               if (!pbats[i])
-                       continue;
-               power_supply_unregister(pbats[i]->bat);
-               kfree(pbats[i]);
-       }
-       power_supply_unregister(pmu_ac);
-       platform_device_unregister(bat_pdev);
-}
-
-module_init(pmu_bat_init);
-module_exit(pmu_bat_exit);
-
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("PMU battery driver");
diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h
deleted file mode 100644 (file)
index cc439fd..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *  Functions private to power supply class
- *
- *  Copyright Â© 2007  Anton Vorontsov <cbou@mail.ru>
- *  Copyright Â© 2004  Szabolcs Gyurko
- *  Copyright Â© 2003  Ian Molton <spyro@f2s.com>
- *
- *  Modified: 2004, Oct     Szabolcs Gyurko
- *
- *  You may use this code as per GPL version 2
- */
-
-struct device;
-struct device_type;
-struct power_supply;
-
-#ifdef CONFIG_SYSFS
-
-extern void power_supply_init_attrs(struct device_type *dev_type);
-extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env);
-
-#else
-
-static inline void power_supply_init_attrs(struct device_type *dev_type) {}
-#define power_supply_uevent NULL
-
-#endif /* CONFIG_SYSFS */
-
-#ifdef CONFIG_LEDS_TRIGGERS
-
-extern void power_supply_update_leds(struct power_supply *psy);
-extern int power_supply_create_triggers(struct power_supply *psy);
-extern void power_supply_remove_triggers(struct power_supply *psy);
-
-#else
-
-static inline void power_supply_update_leds(struct power_supply *psy) {}
-static inline int power_supply_create_triggers(struct power_supply *psy)
-{ return 0; }
-static inline void power_supply_remove_triggers(struct power_supply *psy) {}
-
-#endif /* CONFIG_LEDS_TRIGGERS */
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
deleted file mode 100644 (file)
index a74d8ca..0000000
+++ /dev/null
@@ -1,989 +0,0 @@
-/*
- *  Universal power supply monitor class
- *
- *  Copyright Â© 2007  Anton Vorontsov <cbou@mail.ru>
- *  Copyright Â© 2004  Szabolcs Gyurko
- *  Copyright Â© 2003  Ian Molton <spyro@f2s.com>
- *
- *  Modified: 2004, Oct     Szabolcs Gyurko
- *
- *  You may use this code as per GPL version 2
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/notifier.h>
-#include <linux/err.h>
-#include <linux/power_supply.h>
-#include <linux/thermal.h>
-#include "power_supply.h"
-
-/* exported for the APM Power driver, APM emulation */
-struct class *power_supply_class;
-EXPORT_SYMBOL_GPL(power_supply_class);
-
-ATOMIC_NOTIFIER_HEAD(power_supply_notifier);
-EXPORT_SYMBOL_GPL(power_supply_notifier);
-
-static struct device_type power_supply_dev_type;
-
-#define POWER_SUPPLY_DEFERRED_REGISTER_TIME    msecs_to_jiffies(10)
-
-static bool __power_supply_is_supplied_by(struct power_supply *supplier,
-                                        struct power_supply *supply)
-{
-       int i;
-
-       if (!supply->supplied_from && !supplier->supplied_to)
-               return false;
-
-       /* Support both supplied_to and supplied_from modes */
-       if (supply->supplied_from) {
-               if (!supplier->desc->name)
-                       return false;
-               for (i = 0; i < supply->num_supplies; i++)
-                       if (!strcmp(supplier->desc->name, supply->supplied_from[i]))
-                               return true;
-       } else {
-               if (!supply->desc->name)
-                       return false;
-               for (i = 0; i < supplier->num_supplicants; i++)
-                       if (!strcmp(supplier->supplied_to[i], supply->desc->name))
-                               return true;
-       }
-
-       return false;
-}
-
-static int __power_supply_changed_work(struct device *dev, void *data)
-{
-       struct power_supply *psy = data;
-       struct power_supply *pst = dev_get_drvdata(dev);
-
-       if (__power_supply_is_supplied_by(psy, pst)) {
-               if (pst->desc->external_power_changed)
-                       pst->desc->external_power_changed(pst);
-       }
-
-       return 0;
-}
-
-static void power_supply_changed_work(struct work_struct *work)
-{
-       unsigned long flags;
-       struct power_supply *psy = container_of(work, struct power_supply,
-                                               changed_work);
-
-       dev_dbg(&psy->dev, "%s\n", __func__);
-
-       spin_lock_irqsave(&psy->changed_lock, flags);
-       /*
-        * Check 'changed' here to avoid issues due to race between
-        * power_supply_changed() and this routine. In worst case
-        * power_supply_changed() can be called again just before we take above
-        * lock. During the first call of this routine we will mark 'changed' as
-        * false and it will stay false for the next call as well.
-        */
-       if (likely(psy->changed)) {
-               psy->changed = false;
-               spin_unlock_irqrestore(&psy->changed_lock, flags);
-               class_for_each_device(power_supply_class, NULL, psy,
-                                     __power_supply_changed_work);
-               power_supply_update_leds(psy);
-               atomic_notifier_call_chain(&power_supply_notifier,
-                               PSY_EVENT_PROP_CHANGED, psy);
-               kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE);
-               spin_lock_irqsave(&psy->changed_lock, flags);
-       }
-
-       /*
-        * Hold the wakeup_source until all events are processed.
-        * power_supply_changed() might have called again and have set 'changed'
-        * to true.
-        */
-       if (likely(!psy->changed))
-               pm_relax(&psy->dev);
-       spin_unlock_irqrestore(&psy->changed_lock, flags);
-}
-
-void power_supply_changed(struct power_supply *psy)
-{
-       unsigned long flags;
-
-       dev_dbg(&psy->dev, "%s\n", __func__);
-
-       spin_lock_irqsave(&psy->changed_lock, flags);
-       psy->changed = true;
-       pm_stay_awake(&psy->dev);
-       spin_unlock_irqrestore(&psy->changed_lock, flags);
-       schedule_work(&psy->changed_work);
-}
-EXPORT_SYMBOL_GPL(power_supply_changed);
-
-/*
- * Notify that power supply was registered after parent finished the probing.
- *
- * Often power supply is registered from driver's probe function. However
- * calling power_supply_changed() directly from power_supply_register()
- * would lead to execution of get_property() function provided by the driver
- * too early - before the probe ends.
- *
- * Avoid that by waiting on parent's mutex.
- */
-static void power_supply_deferred_register_work(struct work_struct *work)
-{
-       struct power_supply *psy = container_of(work, struct power_supply,
-                                               deferred_register_work.work);
-
-       if (psy->dev.parent)
-               mutex_lock(&psy->dev.parent->mutex);
-
-       power_supply_changed(psy);
-
-       if (psy->dev.parent)
-               mutex_unlock(&psy->dev.parent->mutex);
-}
-
-#ifdef CONFIG_OF
-#include <linux/of.h>
-
-static int __power_supply_populate_supplied_from(struct device *dev,
-                                                void *data)
-{
-       struct power_supply *psy = data;
-       struct power_supply *epsy = dev_get_drvdata(dev);
-       struct device_node *np;
-       int i = 0;
-
-       do {
-               np = of_parse_phandle(psy->of_node, "power-supplies", i++);
-               if (!np)
-                       break;
-
-               if (np == epsy->of_node) {
-                       dev_info(&psy->dev, "%s: Found supply : %s\n",
-                               psy->desc->name, epsy->desc->name);
-                       psy->supplied_from[i-1] = (char *)epsy->desc->name;
-                       psy->num_supplies++;
-                       of_node_put(np);
-                       break;
-               }
-               of_node_put(np);
-       } while (np);
-
-       return 0;
-}
-
-static int power_supply_populate_supplied_from(struct power_supply *psy)
-{
-       int error;
-
-       error = class_for_each_device(power_supply_class, NULL, psy,
-                                     __power_supply_populate_supplied_from);
-
-       dev_dbg(&psy->dev, "%s %d\n", __func__, error);
-
-       return error;
-}
-
-static int  __power_supply_find_supply_from_node(struct device *dev,
-                                                void *data)
-{
-       struct device_node *np = data;
-       struct power_supply *epsy = dev_get_drvdata(dev);
-
-       /* returning non-zero breaks out of class_for_each_device loop */
-       if (epsy->of_node == np)
-               return 1;
-
-       return 0;
-}
-
-static int power_supply_find_supply_from_node(struct device_node *supply_node)
-{
-       int error;
-
-       /*
-        * class_for_each_device() either returns its own errors or values
-        * returned by __power_supply_find_supply_from_node().
-        *
-        * __power_supply_find_supply_from_node() will return 0 (no match)
-        * or 1 (match).
-        *
-        * We return 0 if class_for_each_device() returned 1, -EPROBE_DEFER if
-        * it returned 0, or error as returned by it.
-        */
-       error = class_for_each_device(power_supply_class, NULL, supply_node,
-                                      __power_supply_find_supply_from_node);
-
-       return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER;
-}
-
-static int power_supply_check_supplies(struct power_supply *psy)
-{
-       struct device_node *np;
-       int cnt = 0;
-
-       /* If there is already a list honor it */
-       if (psy->supplied_from && psy->num_supplies > 0)
-               return 0;
-
-       /* No device node found, nothing to do */
-       if (!psy->of_node)
-               return 0;
-
-       do {
-               int ret;
-
-               np = of_parse_phandle(psy->of_node, "power-supplies", cnt++);
-               if (!np)
-                       break;
-
-               ret = power_supply_find_supply_from_node(np);
-               of_node_put(np);
-
-               if (ret) {
-                       dev_dbg(&psy->dev, "Failed to find supply!\n");
-                       return ret;
-               }
-       } while (np);
-
-       /* Missing valid "power-supplies" entries */
-       if (cnt == 1)
-               return 0;
-
-       /* All supplies found, allocate char ** array for filling */
-       psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from),
-                                         GFP_KERNEL);
-       if (!psy->supplied_from) {
-               dev_err(&psy->dev, "Couldn't allocate memory for supply list\n");
-               return -ENOMEM;
-       }
-
-       *psy->supplied_from = devm_kzalloc(&psy->dev,
-                                          sizeof(char *) * (cnt - 1),
-                                          GFP_KERNEL);
-       if (!*psy->supplied_from) {
-               dev_err(&psy->dev, "Couldn't allocate memory for supply list\n");
-               return -ENOMEM;
-       }
-
-       return power_supply_populate_supplied_from(psy);
-}
-#else
-static inline int power_supply_check_supplies(struct power_supply *psy)
-{
-       return 0;
-}
-#endif
-
-static int __power_supply_am_i_supplied(struct device *dev, void *data)
-{
-       union power_supply_propval ret = {0,};
-       struct power_supply *psy = data;
-       struct power_supply *epsy = dev_get_drvdata(dev);
-
-       if (__power_supply_is_supplied_by(epsy, psy))
-               if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE,
-                                       &ret))
-                       return ret.intval;
-
-       return 0;
-}
-
-int power_supply_am_i_supplied(struct power_supply *psy)
-{
-       int error;
-
-       error = class_for_each_device(power_supply_class, NULL, psy,
-                                     __power_supply_am_i_supplied);
-
-       dev_dbg(&psy->dev, "%s %d\n", __func__, error);
-
-       return error;
-}
-EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
-
-static int __power_supply_is_system_supplied(struct device *dev, void *data)
-{
-       union power_supply_propval ret = {0,};
-       struct power_supply *psy = dev_get_drvdata(dev);
-       unsigned int *count = data;
-
-       (*count)++;
-       if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY)
-               if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE,
-                                       &ret))
-                       return ret.intval;
-
-       return 0;
-}
-
-int power_supply_is_system_supplied(void)
-{
-       int error;
-       unsigned int count = 0;
-
-       error = class_for_each_device(power_supply_class, NULL, &count,
-                                     __power_supply_is_system_supplied);
-
-       /*
-        * If no power class device was found at all, most probably we are
-        * running on a desktop system, so assume we are on mains power.
-        */
-       if (count == 0)
-               return 1;
-
-       return error;
-}
-EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
-
-int power_supply_set_battery_charged(struct power_supply *psy)
-{
-       if (atomic_read(&psy->use_cnt) >= 0 &&
-                       psy->desc->type == POWER_SUPPLY_TYPE_BATTERY &&
-                       psy->desc->set_charged) {
-               psy->desc->set_charged(psy);
-               return 0;
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(power_supply_set_battery_charged);
-
-static int power_supply_match_device_by_name(struct device *dev, const void *data)
-{
-       const char *name = data;
-       struct power_supply *psy = dev_get_drvdata(dev);
-
-       return strcmp(psy->desc->name, name) == 0;
-}
-
-/**
- * power_supply_get_by_name() - Search for a power supply and returns its ref
- * @name: Power supply name to fetch
- *
- * If power supply was found, it increases reference count for the
- * internal power supply's device. The user should power_supply_put()
- * after usage.
- *
- * Return: On success returns a reference to a power supply with
- * matching name equals to @name, a NULL otherwise.
- */
-struct power_supply *power_supply_get_by_name(const char *name)
-{
-       struct power_supply *psy = NULL;
-       struct device *dev = class_find_device(power_supply_class, NULL, name,
-                                       power_supply_match_device_by_name);
-
-       if (dev) {
-               psy = dev_get_drvdata(dev);
-               atomic_inc(&psy->use_cnt);
-       }
-
-       return psy;
-}
-EXPORT_SYMBOL_GPL(power_supply_get_by_name);
-
-/**
- * power_supply_put() - Drop reference obtained with power_supply_get_by_name
- * @psy: Reference to put
- *
- * The reference to power supply should be put before unregistering
- * the power supply.
- */
-void power_supply_put(struct power_supply *psy)
-{
-       might_sleep();
-
-       atomic_dec(&psy->use_cnt);
-       put_device(&psy->dev);
-}
-EXPORT_SYMBOL_GPL(power_supply_put);
-
-#ifdef CONFIG_OF
-static int power_supply_match_device_node(struct device *dev, const void *data)
-{
-       return dev->parent && dev->parent->of_node == data;
-}
-
-/**
- * power_supply_get_by_phandle() - Search for a power supply and returns its ref
- * @np: Pointer to device node holding phandle property
- * @phandle_name: Name of property holding a power supply name
- *
- * If power supply was found, it increases reference count for the
- * internal power supply's device. The user should power_supply_put()
- * after usage.
- *
- * Return: On success returns a reference to a power supply with
- * matching name equals to value under @property, NULL or ERR_PTR otherwise.
- */
-struct power_supply *power_supply_get_by_phandle(struct device_node *np,
-                                                       const char *property)
-{
-       struct device_node *power_supply_np;
-       struct power_supply *psy = NULL;
-       struct device *dev;
-
-       power_supply_np = of_parse_phandle(np, property, 0);
-       if (!power_supply_np)
-               return ERR_PTR(-ENODEV);
-
-       dev = class_find_device(power_supply_class, NULL, power_supply_np,
-                                               power_supply_match_device_node);
-
-       of_node_put(power_supply_np);
-
-       if (dev) {
-               psy = dev_get_drvdata(dev);
-               atomic_inc(&psy->use_cnt);
-       }
-
-       return psy;
-}
-EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
-
-static void devm_power_supply_put(struct device *dev, void *res)
-{
-       struct power_supply **psy = res;
-
-       power_supply_put(*psy);
-}
-
-/**
- * devm_power_supply_get_by_phandle() - Resource managed version of
- *  power_supply_get_by_phandle()
- * @dev: Pointer to device holding phandle property
- * @phandle_name: Name of property holding a power supply phandle
- *
- * Return: On success returns a reference to a power supply with
- * matching name equals to value under @property, NULL or ERR_PTR otherwise.
- */
-struct power_supply *devm_power_supply_get_by_phandle(struct device *dev,
-                                                     const char *property)
-{
-       struct power_supply **ptr, *psy;
-
-       if (!dev->of_node)
-               return ERR_PTR(-ENODEV);
-
-       ptr = devres_alloc(devm_power_supply_put, sizeof(*ptr), GFP_KERNEL);
-       if (!ptr)
-               return ERR_PTR(-ENOMEM);
-
-       psy = power_supply_get_by_phandle(dev->of_node, property);
-       if (IS_ERR_OR_NULL(psy)) {
-               devres_free(ptr);
-       } else {
-               *ptr = psy;
-               devres_add(dev, ptr);
-       }
-       return psy;
-}
-EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
-#endif /* CONFIG_OF */
-
-int power_supply_get_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       if (atomic_read(&psy->use_cnt) <= 0) {
-               if (!psy->initialized)
-                       return -EAGAIN;
-               return -ENODEV;
-       }
-
-       return psy->desc->get_property(psy, psp, val);
-}
-EXPORT_SYMBOL_GPL(power_supply_get_property);
-
-int power_supply_set_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           const union power_supply_propval *val)
-{
-       if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property)
-               return -ENODEV;
-
-       return psy->desc->set_property(psy, psp, val);
-}
-EXPORT_SYMBOL_GPL(power_supply_set_property);
-
-int power_supply_property_is_writeable(struct power_supply *psy,
-                                       enum power_supply_property psp)
-{
-       if (atomic_read(&psy->use_cnt) <= 0 ||
-                       !psy->desc->property_is_writeable)
-               return -ENODEV;
-
-       return psy->desc->property_is_writeable(psy, psp);
-}
-EXPORT_SYMBOL_GPL(power_supply_property_is_writeable);
-
-void power_supply_external_power_changed(struct power_supply *psy)
-{
-       if (atomic_read(&psy->use_cnt) <= 0 ||
-                       !psy->desc->external_power_changed)
-               return;
-
-       psy->desc->external_power_changed(psy);
-}
-EXPORT_SYMBOL_GPL(power_supply_external_power_changed);
-
-int power_supply_powers(struct power_supply *psy, struct device *dev)
-{
-       return sysfs_create_link(&psy->dev.kobj, &dev->kobj, "powers");
-}
-EXPORT_SYMBOL_GPL(power_supply_powers);
-
-static void power_supply_dev_release(struct device *dev)
-{
-       struct power_supply *psy = container_of(dev, struct power_supply, dev);
-       pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
-       kfree(psy);
-}
-
-int power_supply_reg_notifier(struct notifier_block *nb)
-{
-       return atomic_notifier_chain_register(&power_supply_notifier, nb);
-}
-EXPORT_SYMBOL_GPL(power_supply_reg_notifier);
-
-void power_supply_unreg_notifier(struct notifier_block *nb)
-{
-       atomic_notifier_chain_unregister(&power_supply_notifier, nb);
-}
-EXPORT_SYMBOL_GPL(power_supply_unreg_notifier);
-
-#ifdef CONFIG_THERMAL
-static int power_supply_read_temp(struct thermal_zone_device *tzd,
-               int *temp)
-{
-       struct power_supply *psy;
-       union power_supply_propval val;
-       int ret;
-
-       WARN_ON(tzd == NULL);
-       psy = tzd->devdata;
-       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &val);
-       if (ret)
-               return ret;
-
-       /* Convert tenths of degree Celsius to milli degree Celsius. */
-       *temp = val.intval * 100;
-
-       return ret;
-}
-
-static struct thermal_zone_device_ops psy_tzd_ops = {
-       .get_temp = power_supply_read_temp,
-};
-
-static int psy_register_thermal(struct power_supply *psy)
-{
-       int i;
-
-       if (psy->desc->no_thermal)
-               return 0;
-
-       /* Register battery zone device psy reports temperature */
-       for (i = 0; i < psy->desc->num_properties; i++) {
-               if (psy->desc->properties[i] == POWER_SUPPLY_PROP_TEMP) {
-                       psy->tzd = thermal_zone_device_register(psy->desc->name,
-                                       0, 0, psy, &psy_tzd_ops, NULL, 0, 0);
-                       return PTR_ERR_OR_ZERO(psy->tzd);
-               }
-       }
-       return 0;
-}
-
-static void psy_unregister_thermal(struct power_supply *psy)
-{
-       if (IS_ERR_OR_NULL(psy->tzd))
-               return;
-       thermal_zone_device_unregister(psy->tzd);
-}
-
-/* thermal cooling device callbacks */
-static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd,
-                                       unsigned long *state)
-{
-       struct power_supply *psy;
-       union power_supply_propval val;
-       int ret;
-
-       psy = tcd->devdata;
-       ret = power_supply_get_property(psy,
-                       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
-       if (ret)
-               return ret;
-
-       *state = val.intval;
-
-       return ret;
-}
-
-static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd,
-                                       unsigned long *state)
-{
-       struct power_supply *psy;
-       union power_supply_propval val;
-       int ret;
-
-       psy = tcd->devdata;
-       ret = power_supply_get_property(psy,
-                       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
-       if (ret)
-               return ret;
-
-       *state = val.intval;
-
-       return ret;
-}
-
-static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
-                                       unsigned long state)
-{
-       struct power_supply *psy;
-       union power_supply_propval val;
-       int ret;
-
-       psy = tcd->devdata;
-       val.intval = state;
-       ret = psy->desc->set_property(psy,
-               POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
-
-       return ret;
-}
-
-static struct thermal_cooling_device_ops psy_tcd_ops = {
-       .get_max_state = ps_get_max_charge_cntl_limit,
-       .get_cur_state = ps_get_cur_chrage_cntl_limit,
-       .set_cur_state = ps_set_cur_charge_cntl_limit,
-};
-
-static int psy_register_cooler(struct power_supply *psy)
-{
-       int i;
-
-       /* Register for cooling device if psy can control charging */
-       for (i = 0; i < psy->desc->num_properties; i++) {
-               if (psy->desc->properties[i] ==
-                               POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) {
-                       psy->tcd = thermal_cooling_device_register(
-                                                       (char *)psy->desc->name,
-                                                       psy, &psy_tcd_ops);
-                       return PTR_ERR_OR_ZERO(psy->tcd);
-               }
-       }
-       return 0;
-}
-
-static void psy_unregister_cooler(struct power_supply *psy)
-{
-       if (IS_ERR_OR_NULL(psy->tcd))
-               return;
-       thermal_cooling_device_unregister(psy->tcd);
-}
-#else
-static int psy_register_thermal(struct power_supply *psy)
-{
-       return 0;
-}
-
-static void psy_unregister_thermal(struct power_supply *psy)
-{
-}
-
-static int psy_register_cooler(struct power_supply *psy)
-{
-       return 0;
-}
-
-static void psy_unregister_cooler(struct power_supply *psy)
-{
-}
-#endif
-
-static struct power_supply *__must_check
-__power_supply_register(struct device *parent,
-                                  const struct power_supply_desc *desc,
-                                  const struct power_supply_config *cfg,
-                                  bool ws)
-{
-       struct device *dev;
-       struct power_supply *psy;
-       int rc;
-
-       if (!parent)
-               pr_warn("%s: Expected proper parent device for '%s'\n",
-                       __func__, desc->name);
-
-       psy = kzalloc(sizeof(*psy), GFP_KERNEL);
-       if (!psy)
-               return ERR_PTR(-ENOMEM);
-
-       dev = &psy->dev;
-
-       device_initialize(dev);
-
-       dev->class = power_supply_class;
-       dev->type = &power_supply_dev_type;
-       dev->parent = parent;
-       dev->release = power_supply_dev_release;
-       dev_set_drvdata(dev, psy);
-       psy->desc = desc;
-       if (cfg) {
-               psy->drv_data = cfg->drv_data;
-               psy->of_node = cfg->of_node;
-               psy->supplied_to = cfg->supplied_to;
-               psy->num_supplicants = cfg->num_supplicants;
-       }
-
-       rc = dev_set_name(dev, "%s", desc->name);
-       if (rc)
-               goto dev_set_name_failed;
-
-       INIT_WORK(&psy->changed_work, power_supply_changed_work);
-       INIT_DELAYED_WORK(&psy->deferred_register_work,
-                         power_supply_deferred_register_work);
-
-       rc = power_supply_check_supplies(psy);
-       if (rc) {
-               dev_info(dev, "Not all required supplies found, defer probe\n");
-               goto check_supplies_failed;
-       }
-
-       spin_lock_init(&psy->changed_lock);
-       rc = device_init_wakeup(dev, ws);
-       if (rc)
-               goto wakeup_init_failed;
-
-       rc = device_add(dev);
-       if (rc)
-               goto device_add_failed;
-
-       rc = psy_register_thermal(psy);
-       if (rc)
-               goto register_thermal_failed;
-
-       rc = psy_register_cooler(psy);
-       if (rc)
-               goto register_cooler_failed;
-
-       rc = power_supply_create_triggers(psy);
-       if (rc)
-               goto create_triggers_failed;
-
-       /*
-        * Update use_cnt after any uevents (most notably from device_add()).
-        * We are here still during driver's probe but
-        * the power_supply_uevent() calls back driver's get_property
-        * method so:
-        * 1. Driver did not assigned the returned struct power_supply,
-        * 2. Driver could not finish initialization (anything in its probe
-        *    after calling power_supply_register()).
-        */
-       atomic_inc(&psy->use_cnt);
-       psy->initialized = true;
-
-       queue_delayed_work(system_power_efficient_wq,
-                          &psy->deferred_register_work,
-                          POWER_SUPPLY_DEFERRED_REGISTER_TIME);
-
-       return psy;
-
-create_triggers_failed:
-       psy_unregister_cooler(psy);
-register_cooler_failed:
-       psy_unregister_thermal(psy);
-register_thermal_failed:
-       device_del(dev);
-device_add_failed:
-wakeup_init_failed:
-check_supplies_failed:
-dev_set_name_failed:
-       put_device(dev);
-       return ERR_PTR(rc);
-}
-
-/**
- * power_supply_register() - Register new power supply
- * @parent:    Device to be a parent of power supply's device, usually
- *             the device which probe function calls this
- * @desc:      Description of power supply, must be valid through whole
- *             lifetime of this power supply
- * @cfg:       Run-time specific configuration accessed during registering,
- *             may be NULL
- *
- * Return: A pointer to newly allocated power_supply on success
- * or ERR_PTR otherwise.
- * Use power_supply_unregister() on returned power_supply pointer to release
- * resources.
- */
-struct power_supply *__must_check power_supply_register(struct device *parent,
-               const struct power_supply_desc *desc,
-               const struct power_supply_config *cfg)
-{
-       return __power_supply_register(parent, desc, cfg, true);
-}
-EXPORT_SYMBOL_GPL(power_supply_register);
-
-/**
- * power_supply_register_no_ws() - Register new non-waking-source power supply
- * @parent:    Device to be a parent of power supply's device, usually
- *             the device which probe function calls this
- * @desc:      Description of power supply, must be valid through whole
- *             lifetime of this power supply
- * @cfg:       Run-time specific configuration accessed during registering,
- *             may be NULL
- *
- * Return: A pointer to newly allocated power_supply on success
- * or ERR_PTR otherwise.
- * Use power_supply_unregister() on returned power_supply pointer to release
- * resources.
- */
-struct power_supply *__must_check
-power_supply_register_no_ws(struct device *parent,
-               const struct power_supply_desc *desc,
-               const struct power_supply_config *cfg)
-{
-       return __power_supply_register(parent, desc, cfg, false);
-}
-EXPORT_SYMBOL_GPL(power_supply_register_no_ws);
-
-static void devm_power_supply_release(struct device *dev, void *res)
-{
-       struct power_supply **psy = res;
-
-       power_supply_unregister(*psy);
-}
-
-/**
- * devm_power_supply_register() - Register managed power supply
- * @parent:    Device to be a parent of power supply's device, usually
- *             the device which probe function calls this
- * @desc:      Description of power supply, must be valid through whole
- *             lifetime of this power supply
- * @cfg:       Run-time specific configuration accessed during registering,
- *             may be NULL
- *
- * Return: A pointer to newly allocated power_supply on success
- * or ERR_PTR otherwise.
- * The returned power_supply pointer will be automatically unregistered
- * on driver detach.
- */
-struct power_supply *__must_check
-devm_power_supply_register(struct device *parent,
-               const struct power_supply_desc *desc,
-               const struct power_supply_config *cfg)
-{
-       struct power_supply **ptr, *psy;
-
-       ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL);
-
-       if (!ptr)
-               return ERR_PTR(-ENOMEM);
-       psy = __power_supply_register(parent, desc, cfg, true);
-       if (IS_ERR(psy)) {
-               devres_free(ptr);
-       } else {
-               *ptr = psy;
-               devres_add(parent, ptr);
-       }
-       return psy;
-}
-EXPORT_SYMBOL_GPL(devm_power_supply_register);
-
-/**
- * devm_power_supply_register_no_ws() - Register managed non-waking-source power supply
- * @parent:    Device to be a parent of power supply's device, usually
- *             the device which probe function calls this
- * @desc:      Description of power supply, must be valid through whole
- *             lifetime of this power supply
- * @cfg:       Run-time specific configuration accessed during registering,
- *             may be NULL
- *
- * Return: A pointer to newly allocated power_supply on success
- * or ERR_PTR otherwise.
- * The returned power_supply pointer will be automatically unregistered
- * on driver detach.
- */
-struct power_supply *__must_check
-devm_power_supply_register_no_ws(struct device *parent,
-               const struct power_supply_desc *desc,
-               const struct power_supply_config *cfg)
-{
-       struct power_supply **ptr, *psy;
-
-       ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL);
-
-       if (!ptr)
-               return ERR_PTR(-ENOMEM);
-       psy = __power_supply_register(parent, desc, cfg, false);
-       if (IS_ERR(psy)) {
-               devres_free(ptr);
-       } else {
-               *ptr = psy;
-               devres_add(parent, ptr);
-       }
-       return psy;
-}
-EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws);
-
-/**
- * power_supply_unregister() - Remove this power supply from system
- * @psy:       Pointer to power supply to unregister
- *
- * Remove this power supply from the system. The resources of power supply
- * will be freed here or on last power_supply_put() call.
- */
-void power_supply_unregister(struct power_supply *psy)
-{
-       WARN_ON(atomic_dec_return(&psy->use_cnt));
-       cancel_work_sync(&psy->changed_work);
-       cancel_delayed_work_sync(&psy->deferred_register_work);
-       sysfs_remove_link(&psy->dev.kobj, "powers");
-       power_supply_remove_triggers(psy);
-       psy_unregister_cooler(psy);
-       psy_unregister_thermal(psy);
-       device_init_wakeup(&psy->dev, false);
-       device_unregister(&psy->dev);
-}
-EXPORT_SYMBOL_GPL(power_supply_unregister);
-
-void *power_supply_get_drvdata(struct power_supply *psy)
-{
-       return psy->drv_data;
-}
-EXPORT_SYMBOL_GPL(power_supply_get_drvdata);
-
-static int __init power_supply_class_init(void)
-{
-       power_supply_class = class_create(THIS_MODULE, "power_supply");
-
-       if (IS_ERR(power_supply_class))
-               return PTR_ERR(power_supply_class);
-
-       power_supply_class->dev_uevent = power_supply_uevent;
-       power_supply_init_attrs(&power_supply_dev_type);
-
-       return 0;
-}
-
-static void __exit power_supply_class_exit(void)
-{
-       class_destroy(power_supply_class);
-}
-
-subsys_initcall(power_supply_class_init);
-module_exit(power_supply_class_exit);
-
-MODULE_DESCRIPTION("Universal power supply monitor class");
-MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
-             "Szabolcs Gyurko, "
-             "Anton Vorontsov <cbou@mail.ru>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c
deleted file mode 100644 (file)
index 2277ad9..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- *  LEDs triggers for power supply class
- *
- *  Copyright Â© 2007  Anton Vorontsov <cbou@mail.ru>
- *  Copyright Â© 2004  Szabolcs Gyurko
- *  Copyright Â© 2003  Ian Molton <spyro@f2s.com>
- *
- *  Modified: 2004, Oct     Szabolcs Gyurko
- *
- *  You may use this code as per GPL version 2
- */
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-
-#include "power_supply.h"
-
-/* Battery specific LEDs triggers. */
-
-static void power_supply_update_bat_leds(struct power_supply *psy)
-{
-       union power_supply_propval status;
-       unsigned long delay_on = 0;
-       unsigned long delay_off = 0;
-
-       if (power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
-               return;
-
-       dev_dbg(&psy->dev, "%s %d\n", __func__, status.intval);
-
-       switch (status.intval) {
-       case POWER_SUPPLY_STATUS_FULL:
-               led_trigger_event(psy->charging_full_trig, LED_FULL);
-               led_trigger_event(psy->charging_trig, LED_OFF);
-               led_trigger_event(psy->full_trig, LED_FULL);
-               led_trigger_event(psy->charging_blink_full_solid_trig,
-                       LED_FULL);
-               break;
-       case POWER_SUPPLY_STATUS_CHARGING:
-               led_trigger_event(psy->charging_full_trig, LED_FULL);
-               led_trigger_event(psy->charging_trig, LED_FULL);
-               led_trigger_event(psy->full_trig, LED_OFF);
-               led_trigger_blink(psy->charging_blink_full_solid_trig,
-                       &delay_on, &delay_off);
-               break;
-       default:
-               led_trigger_event(psy->charging_full_trig, LED_OFF);
-               led_trigger_event(psy->charging_trig, LED_OFF);
-               led_trigger_event(psy->full_trig, LED_OFF);
-               led_trigger_event(psy->charging_blink_full_solid_trig,
-                       LED_OFF);
-               break;
-       }
-}
-
-static int power_supply_create_bat_triggers(struct power_supply *psy)
-{
-       psy->charging_full_trig_name = kasprintf(GFP_KERNEL,
-                                       "%s-charging-or-full", psy->desc->name);
-       if (!psy->charging_full_trig_name)
-               goto charging_full_failed;
-
-       psy->charging_trig_name = kasprintf(GFP_KERNEL,
-                                       "%s-charging", psy->desc->name);
-       if (!psy->charging_trig_name)
-               goto charging_failed;
-
-       psy->full_trig_name = kasprintf(GFP_KERNEL, "%s-full", psy->desc->name);
-       if (!psy->full_trig_name)
-               goto full_failed;
-
-       psy->charging_blink_full_solid_trig_name = kasprintf(GFP_KERNEL,
-               "%s-charging-blink-full-solid", psy->desc->name);
-       if (!psy->charging_blink_full_solid_trig_name)
-               goto charging_blink_full_solid_failed;
-
-       led_trigger_register_simple(psy->charging_full_trig_name,
-                                   &psy->charging_full_trig);
-       led_trigger_register_simple(psy->charging_trig_name,
-                                   &psy->charging_trig);
-       led_trigger_register_simple(psy->full_trig_name,
-                                   &psy->full_trig);
-       led_trigger_register_simple(psy->charging_blink_full_solid_trig_name,
-                                   &psy->charging_blink_full_solid_trig);
-
-       return 0;
-
-charging_blink_full_solid_failed:
-       kfree(psy->full_trig_name);
-full_failed:
-       kfree(psy->charging_trig_name);
-charging_failed:
-       kfree(psy->charging_full_trig_name);
-charging_full_failed:
-       return -ENOMEM;
-}
-
-static void power_supply_remove_bat_triggers(struct power_supply *psy)
-{
-       led_trigger_unregister_simple(psy->charging_full_trig);
-       led_trigger_unregister_simple(psy->charging_trig);
-       led_trigger_unregister_simple(psy->full_trig);
-       led_trigger_unregister_simple(psy->charging_blink_full_solid_trig);
-       kfree(psy->charging_blink_full_solid_trig_name);
-       kfree(psy->full_trig_name);
-       kfree(psy->charging_trig_name);
-       kfree(psy->charging_full_trig_name);
-}
-
-/* Generated power specific LEDs triggers. */
-
-static void power_supply_update_gen_leds(struct power_supply *psy)
-{
-       union power_supply_propval online;
-
-       if (power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
-               return;
-
-       dev_dbg(&psy->dev, "%s %d\n", __func__, online.intval);
-
-       if (online.intval)
-               led_trigger_event(psy->online_trig, LED_FULL);
-       else
-               led_trigger_event(psy->online_trig, LED_OFF);
-}
-
-static int power_supply_create_gen_triggers(struct power_supply *psy)
-{
-       psy->online_trig_name = kasprintf(GFP_KERNEL, "%s-online",
-                                         psy->desc->name);
-       if (!psy->online_trig_name)
-               return -ENOMEM;
-
-       led_trigger_register_simple(psy->online_trig_name, &psy->online_trig);
-
-       return 0;
-}
-
-static void power_supply_remove_gen_triggers(struct power_supply *psy)
-{
-       led_trigger_unregister_simple(psy->online_trig);
-       kfree(psy->online_trig_name);
-}
-
-/* Choice what triggers to create&update. */
-
-void power_supply_update_leds(struct power_supply *psy)
-{
-       if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
-               power_supply_update_bat_leds(psy);
-       else
-               power_supply_update_gen_leds(psy);
-}
-
-int power_supply_create_triggers(struct power_supply *psy)
-{
-       if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
-               return power_supply_create_bat_triggers(psy);
-       return power_supply_create_gen_triggers(psy);
-}
-
-void power_supply_remove_triggers(struct power_supply *psy)
-{
-       if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
-               power_supply_remove_bat_triggers(psy);
-       else
-               power_supply_remove_gen_triggers(psy);
-}
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
deleted file mode 100644 (file)
index bcde8d1..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- *  Sysfs interface for the universal power supply monitor class
- *
- *  Copyright Â© 2007  David Woodhouse <dwmw2@infradead.org>
- *  Copyright Â© 2007  Anton Vorontsov <cbou@mail.ru>
- *  Copyright Â© 2004  Szabolcs Gyurko
- *  Copyright Â© 2003  Ian Molton <spyro@f2s.com>
- *
- *  Modified: 2004, Oct     Szabolcs Gyurko
- *
- *  You may use this code as per GPL version 2
- */
-
-#include <linux/ctype.h>
-#include <linux/device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-
-#include "power_supply.h"
-
-/*
- * This is because the name "current" breaks the device attr macro.
- * The "current" word resolves to "(get_current())" so instead of
- * "current" "(get_current())" appears in the sysfs.
- *
- * The source of this definition is the device.h which calls __ATTR
- * macro in sysfs.h which calls the __stringify macro.
- *
- * Only modification that the name is not tried to be resolved
- * (as a macro let's say).
- */
-
-#define POWER_SUPPLY_ATTR(_name)                                       \
-{                                                                      \
-       .attr = { .name = #_name },                                     \
-       .show = power_supply_show_property,                             \
-       .store = power_supply_store_property,                           \
-}
-
-static struct device_attribute power_supply_attrs[];
-
-static ssize_t power_supply_show_property(struct device *dev,
-                                         struct device_attribute *attr,
-                                         char *buf) {
-       static char *type_text[] = {
-               "Unknown", "Battery", "UPS", "Mains", "USB",
-               "USB_DCP", "USB_CDP", "USB_ACA", "USB_C",
-               "USB_PD", "USB_PD_DRP"
-       };
-       static char *status_text[] = {
-               "Unknown", "Charging", "Discharging", "Not charging", "Full"
-       };
-       static char *charge_type[] = {
-               "Unknown", "N/A", "Trickle", "Fast"
-       };
-       static char *health_text[] = {
-               "Unknown", "Good", "Overheat", "Dead", "Over voltage",
-               "Unspecified failure", "Cold", "Watchdog timer expire",
-               "Safety timer expire"
-       };
-       static char *technology_text[] = {
-               "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
-               "LiMn"
-       };
-       static char *capacity_level_text[] = {
-               "Unknown", "Critical", "Low", "Normal", "High", "Full"
-       };
-       static char *scope_text[] = {
-               "Unknown", "System", "Device"
-       };
-       ssize_t ret = 0;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       const ptrdiff_t off = attr - power_supply_attrs;
-       union power_supply_propval value;
-
-       if (off == POWER_SUPPLY_PROP_TYPE) {
-               value.intval = psy->desc->type;
-       } else {
-               ret = power_supply_get_property(psy, off, &value);
-
-               if (ret < 0) {
-                       if (ret == -ENODATA)
-                               dev_dbg(dev, "driver has no data for `%s' property\n",
-                                       attr->attr.name);
-                       else if (ret != -ENODEV && ret != -EAGAIN)
-                               dev_err(dev, "driver failed to report `%s' property: %zd\n",
-                                       attr->attr.name, ret);
-                       return ret;
-               }
-       }
-
-       if (off == POWER_SUPPLY_PROP_STATUS)
-               return sprintf(buf, "%s\n", status_text[value.intval]);
-       else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
-               return sprintf(buf, "%s\n", charge_type[value.intval]);
-       else if (off == POWER_SUPPLY_PROP_HEALTH)
-               return sprintf(buf, "%s\n", health_text[value.intval]);
-       else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
-               return sprintf(buf, "%s\n", technology_text[value.intval]);
-       else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
-               return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
-       else if (off == POWER_SUPPLY_PROP_TYPE)
-               return sprintf(buf, "%s\n", type_text[value.intval]);
-       else if (off == POWER_SUPPLY_PROP_SCOPE)
-               return sprintf(buf, "%s\n", scope_text[value.intval]);
-       else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
-               return sprintf(buf, "%s\n", value.strval);
-
-       return sprintf(buf, "%d\n", value.intval);
-}
-
-static ssize_t power_supply_store_property(struct device *dev,
-                                          struct device_attribute *attr,
-                                          const char *buf, size_t count) {
-       ssize_t ret;
-       struct power_supply *psy = dev_get_drvdata(dev);
-       const ptrdiff_t off = attr - power_supply_attrs;
-       union power_supply_propval value;
-       long long_val;
-
-       /* TODO: support other types than int */
-       ret = kstrtol(buf, 10, &long_val);
-       if (ret < 0)
-               return ret;
-
-       value.intval = long_val;
-
-       ret = power_supply_set_property(psy, off, &value);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-/* Must be in the same order as POWER_SUPPLY_PROP_* */
-static struct device_attribute power_supply_attrs[] = {
-       /* Properties of type `int' */
-       POWER_SUPPLY_ATTR(status),
-       POWER_SUPPLY_ATTR(charge_type),
-       POWER_SUPPLY_ATTR(health),
-       POWER_SUPPLY_ATTR(present),
-       POWER_SUPPLY_ATTR(online),
-       POWER_SUPPLY_ATTR(authentic),
-       POWER_SUPPLY_ATTR(technology),
-       POWER_SUPPLY_ATTR(cycle_count),
-       POWER_SUPPLY_ATTR(voltage_max),
-       POWER_SUPPLY_ATTR(voltage_min),
-       POWER_SUPPLY_ATTR(voltage_max_design),
-       POWER_SUPPLY_ATTR(voltage_min_design),
-       POWER_SUPPLY_ATTR(voltage_now),
-       POWER_SUPPLY_ATTR(voltage_avg),
-       POWER_SUPPLY_ATTR(voltage_ocv),
-       POWER_SUPPLY_ATTR(voltage_boot),
-       POWER_SUPPLY_ATTR(current_max),
-       POWER_SUPPLY_ATTR(current_now),
-       POWER_SUPPLY_ATTR(current_avg),
-       POWER_SUPPLY_ATTR(current_boot),
-       POWER_SUPPLY_ATTR(power_now),
-       POWER_SUPPLY_ATTR(power_avg),
-       POWER_SUPPLY_ATTR(charge_full_design),
-       POWER_SUPPLY_ATTR(charge_empty_design),
-       POWER_SUPPLY_ATTR(charge_full),
-       POWER_SUPPLY_ATTR(charge_empty),
-       POWER_SUPPLY_ATTR(charge_now),
-       POWER_SUPPLY_ATTR(charge_avg),
-       POWER_SUPPLY_ATTR(charge_counter),
-       POWER_SUPPLY_ATTR(constant_charge_current),
-       POWER_SUPPLY_ATTR(constant_charge_current_max),
-       POWER_SUPPLY_ATTR(constant_charge_voltage),
-       POWER_SUPPLY_ATTR(constant_charge_voltage_max),
-       POWER_SUPPLY_ATTR(charge_control_limit),
-       POWER_SUPPLY_ATTR(charge_control_limit_max),
-       POWER_SUPPLY_ATTR(input_current_limit),
-       POWER_SUPPLY_ATTR(energy_full_design),
-       POWER_SUPPLY_ATTR(energy_empty_design),
-       POWER_SUPPLY_ATTR(energy_full),
-       POWER_SUPPLY_ATTR(energy_empty),
-       POWER_SUPPLY_ATTR(energy_now),
-       POWER_SUPPLY_ATTR(energy_avg),
-       POWER_SUPPLY_ATTR(capacity),
-       POWER_SUPPLY_ATTR(capacity_alert_min),
-       POWER_SUPPLY_ATTR(capacity_alert_max),
-       POWER_SUPPLY_ATTR(capacity_level),
-       POWER_SUPPLY_ATTR(temp),
-       POWER_SUPPLY_ATTR(temp_max),
-       POWER_SUPPLY_ATTR(temp_min),
-       POWER_SUPPLY_ATTR(temp_alert_min),
-       POWER_SUPPLY_ATTR(temp_alert_max),
-       POWER_SUPPLY_ATTR(temp_ambient),
-       POWER_SUPPLY_ATTR(temp_ambient_alert_min),
-       POWER_SUPPLY_ATTR(temp_ambient_alert_max),
-       POWER_SUPPLY_ATTR(time_to_empty_now),
-       POWER_SUPPLY_ATTR(time_to_empty_avg),
-       POWER_SUPPLY_ATTR(time_to_full_now),
-       POWER_SUPPLY_ATTR(time_to_full_avg),
-       POWER_SUPPLY_ATTR(type),
-       POWER_SUPPLY_ATTR(scope),
-       POWER_SUPPLY_ATTR(charge_term_current),
-       POWER_SUPPLY_ATTR(calibrate),
-       /* Properties of type `const char *' */
-       POWER_SUPPLY_ATTR(model_name),
-       POWER_SUPPLY_ATTR(manufacturer),
-       POWER_SUPPLY_ATTR(serial_number),
-};
-
-static struct attribute *
-__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
-
-static umode_t power_supply_attr_is_visible(struct kobject *kobj,
-                                          struct attribute *attr,
-                                          int attrno)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct power_supply *psy = dev_get_drvdata(dev);
-       umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
-       int i;
-
-       if (attrno == POWER_SUPPLY_PROP_TYPE)
-               return mode;
-
-       for (i = 0; i < psy->desc->num_properties; i++) {
-               int property = psy->desc->properties[i];
-
-               if (property == attrno) {
-                       if (psy->desc->property_is_writeable &&
-                           psy->desc->property_is_writeable(psy, property) > 0)
-                               mode |= S_IWUSR;
-
-                       return mode;
-               }
-       }
-
-       return 0;
-}
-
-static struct attribute_group power_supply_attr_group = {
-       .attrs = __power_supply_attrs,
-       .is_visible = power_supply_attr_is_visible,
-};
-
-static const struct attribute_group *power_supply_attr_groups[] = {
-       &power_supply_attr_group,
-       NULL,
-};
-
-void power_supply_init_attrs(struct device_type *dev_type)
-{
-       int i;
-
-       dev_type->groups = power_supply_attr_groups;
-
-       for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
-               __power_supply_attrs[i] = &power_supply_attrs[i].attr;
-}
-
-static char *kstruprdup(const char *str, gfp_t gfp)
-{
-       char *ret, *ustr;
-
-       ustr = ret = kmalloc(strlen(str) + 1, gfp);
-
-       if (!ret)
-               return NULL;
-
-       while (*str)
-               *ustr++ = toupper(*str++);
-
-       *ustr = 0;
-
-       return ret;
-}
-
-int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
-       struct power_supply *psy = dev_get_drvdata(dev);
-       int ret = 0, j;
-       char *prop_buf;
-       char *attrname;
-
-       dev_dbg(dev, "uevent\n");
-
-       if (!psy || !psy->desc) {
-               dev_dbg(dev, "No power supply yet\n");
-               return ret;
-       }
-
-       dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->desc->name);
-
-       ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
-       if (ret)
-               return ret;
-
-       prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
-       if (!prop_buf)
-               return -ENOMEM;
-
-       for (j = 0; j < psy->desc->num_properties; j++) {
-               struct device_attribute *attr;
-               char *line;
-
-               attr = &power_supply_attrs[psy->desc->properties[j]];
-
-               ret = power_supply_show_property(dev, attr, prop_buf);
-               if (ret == -ENODEV || ret == -ENODATA) {
-                       /* When a battery is absent, we expect -ENODEV. Don't abort;
-                          send the uevent with at least the the PRESENT=0 property */
-                       ret = 0;
-                       continue;
-               }
-
-               if (ret < 0)
-                       goto out;
-
-               line = strchr(prop_buf, '\n');
-               if (line)
-                       *line = 0;
-
-               attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
-               if (!attrname) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-
-               dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
-
-               ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
-               kfree(attrname);
-               if (ret)
-                       goto out;
-       }
-
-out:
-       free_page((unsigned long)prop_buf);
-
-       return ret;
-}
diff --git a/drivers/power/qcom_smbb.c b/drivers/power/qcom_smbb.c
deleted file mode 100644 (file)
index b5896ba..0000000
+++ /dev/null
@@ -1,972 +0,0 @@
-/* Copyright (c) 2014, Sony Mobile Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- *
- * This driver is for the multi-block Switch-Mode Battery Charger and Boost
- * (SMBB) hardware, found in Qualcomm PM8941 PMICs.  The charger is an
- * integrated, single-cell lithium-ion battery charger.
- *
- * Sub-components:
- *  - Charger core
- *  - Buck
- *  - DC charge-path
- *  - USB charge-path
- *  - Battery interface
- *  - Boost (not implemented)
- *  - Misc
- *  - HF-Buck
- */
-
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/regmap.h>
-#include <linux/slab.h>
-#include <linux/extcon.h>
-
-#define SMBB_CHG_VMAX          0x040
-#define SMBB_CHG_VSAFE         0x041
-#define SMBB_CHG_CFG           0x043
-#define SMBB_CHG_IMAX          0x044
-#define SMBB_CHG_ISAFE         0x045
-#define SMBB_CHG_VIN_MIN       0x047
-#define SMBB_CHG_CTRL          0x049
-#define CTRL_EN                        BIT(7)
-#define SMBB_CHG_VBAT_WEAK     0x052
-#define SMBB_CHG_IBAT_TERM_CHG 0x05b
-#define IBAT_TERM_CHG_IEOC     BIT(7)
-#define IBAT_TERM_CHG_IEOC_BMS BIT(7)
-#define IBAT_TERM_CHG_IEOC_CHG 0
-#define SMBB_CHG_VBAT_DET      0x05d
-#define SMBB_CHG_TCHG_MAX_EN   0x060
-#define TCHG_MAX_EN            BIT(7)
-#define SMBB_CHG_WDOG_TIME     0x062
-#define SMBB_CHG_WDOG_EN       0x065
-#define WDOG_EN                        BIT(7)
-
-#define SMBB_BUCK_REG_MODE     0x174
-#define BUCK_REG_MODE          BIT(0)
-#define BUCK_REG_MODE_VBAT     BIT(0)
-#define BUCK_REG_MODE_VSYS     0
-
-#define SMBB_BAT_PRES_STATUS   0x208
-#define PRES_STATUS_BAT_PRES   BIT(7)
-#define SMBB_BAT_TEMP_STATUS   0x209
-#define TEMP_STATUS_OK         BIT(7)
-#define TEMP_STATUS_HOT                BIT(6)
-#define SMBB_BAT_BTC_CTRL      0x249
-#define BTC_CTRL_COMP_EN       BIT(7)
-#define BTC_CTRL_COLD_EXT      BIT(1)
-#define BTC_CTRL_HOT_EXT_N     BIT(0)
-
-#define SMBB_USB_IMAX          0x344
-#define SMBB_USB_ENUM_TIMER_STOP 0x34e
-#define ENUM_TIMER_STOP                BIT(0)
-#define SMBB_USB_SEC_ACCESS    0x3d0
-#define SEC_ACCESS_MAGIC       0xa5
-#define SMBB_USB_REV_BST       0x3ed
-#define REV_BST_CHG_GONE       BIT(7)
-
-#define SMBB_DC_IMAX           0x444
-
-#define SMBB_MISC_REV2         0x601
-#define SMBB_MISC_BOOT_DONE    0x642
-#define BOOT_DONE              BIT(7)
-
-#define STATUS_USBIN_VALID     BIT(0) /* USB connection is valid */
-#define STATUS_DCIN_VALID      BIT(1) /* DC connection is valid */
-#define STATUS_BAT_HOT         BIT(2) /* Battery temp 1=Hot, 0=Cold */
-#define STATUS_BAT_OK          BIT(3) /* Battery temp OK */
-#define STATUS_BAT_PRESENT     BIT(4) /* Battery is present */
-#define STATUS_CHG_DONE                BIT(5) /* Charge cycle is complete */
-#define STATUS_CHG_TRKL                BIT(6) /* Trickle charging */
-#define STATUS_CHG_FAST                BIT(7) /* Fast charging */
-#define STATUS_CHG_GONE                BIT(8) /* No charger is connected */
-
-enum smbb_attr {
-       ATTR_BAT_ISAFE,
-       ATTR_BAT_IMAX,
-       ATTR_USBIN_IMAX,
-       ATTR_DCIN_IMAX,
-       ATTR_BAT_VSAFE,
-       ATTR_BAT_VMAX,
-       ATTR_BAT_VMIN,
-       ATTR_CHG_VDET,
-       ATTR_VIN_MIN,
-       _ATTR_CNT,
-};
-
-struct smbb_charger {
-       unsigned int revision;
-       unsigned int addr;
-       struct device *dev;
-       struct extcon_dev *edev;
-
-       bool dc_disabled;
-       bool jeita_ext_temp;
-       unsigned long status;
-       struct mutex statlock;
-
-       unsigned int attr[_ATTR_CNT];
-
-       struct power_supply *usb_psy;
-       struct power_supply *dc_psy;
-       struct power_supply *bat_psy;
-       struct regmap *regmap;
-};
-
-static const unsigned int smbb_usb_extcon_cable[] = {
-       EXTCON_USB,
-       EXTCON_NONE,
-};
-
-static int smbb_vbat_weak_fn(unsigned int index)
-{
-       return 2100000 + index * 100000;
-}
-
-static int smbb_vin_fn(unsigned int index)
-{
-       if (index > 42)
-               return 5600000 + (index - 43) * 200000;
-       return 3400000 + index * 50000;
-}
-
-static int smbb_vmax_fn(unsigned int index)
-{
-       return 3240000 + index * 10000;
-}
-
-static int smbb_vbat_det_fn(unsigned int index)
-{
-       return 3240000 + index * 20000;
-}
-
-static int smbb_imax_fn(unsigned int index)
-{
-       if (index < 2)
-               return 100000 + index * 50000;
-       return index * 100000;
-}
-
-static int smbb_bat_imax_fn(unsigned int index)
-{
-       return index * 50000;
-}
-
-static unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int))
-{
-       unsigned int widx;
-       unsigned int sel;
-
-       for (widx = sel = 0; (*fn)(widx) <= val; ++widx)
-               sel = widx;
-
-       return sel;
-}
-
-static const struct smbb_charger_attr {
-       const char *name;
-       unsigned int reg;
-       unsigned int safe_reg;
-       unsigned int max;
-       unsigned int min;
-       unsigned int fail_ok;
-       int (*hw_fn)(unsigned int);
-} smbb_charger_attrs[] = {
-       [ATTR_BAT_ISAFE] = {
-               .name = "qcom,fast-charge-safe-current",
-               .reg = SMBB_CHG_ISAFE,
-               .max = 3000000,
-               .min = 200000,
-               .hw_fn = smbb_bat_imax_fn,
-               .fail_ok = 1,
-       },
-       [ATTR_BAT_IMAX] = {
-               .name = "qcom,fast-charge-current-limit",
-               .reg = SMBB_CHG_IMAX,
-               .safe_reg = SMBB_CHG_ISAFE,
-               .max = 3000000,
-               .min = 200000,
-               .hw_fn = smbb_bat_imax_fn,
-       },
-       [ATTR_DCIN_IMAX] = {
-               .name = "qcom,dc-current-limit",
-               .reg = SMBB_DC_IMAX,
-               .max = 2500000,
-               .min = 100000,
-               .hw_fn = smbb_imax_fn,
-       },
-       [ATTR_BAT_VSAFE] = {
-               .name = "qcom,fast-charge-safe-voltage",
-               .reg = SMBB_CHG_VSAFE,
-               .max = 5000000,
-               .min = 3240000,
-               .hw_fn = smbb_vmax_fn,
-               .fail_ok = 1,
-       },
-       [ATTR_BAT_VMAX] = {
-               .name = "qcom,fast-charge-high-threshold-voltage",
-               .reg = SMBB_CHG_VMAX,
-               .safe_reg = SMBB_CHG_VSAFE,
-               .max = 5000000,
-               .min = 3240000,
-               .hw_fn = smbb_vmax_fn,
-       },
-       [ATTR_BAT_VMIN] = {
-               .name = "qcom,fast-charge-low-threshold-voltage",
-               .reg = SMBB_CHG_VBAT_WEAK,
-               .max = 3600000,
-               .min = 2100000,
-               .hw_fn = smbb_vbat_weak_fn,
-       },
-       [ATTR_CHG_VDET] = {
-               .name = "qcom,auto-recharge-threshold-voltage",
-               .reg = SMBB_CHG_VBAT_DET,
-               .max = 5000000,
-               .min = 3240000,
-               .hw_fn = smbb_vbat_det_fn,
-       },
-       [ATTR_VIN_MIN] = {
-               .name = "qcom,minimum-input-voltage",
-               .reg = SMBB_CHG_VIN_MIN,
-               .max = 9600000,
-               .min = 4200000,
-               .hw_fn = smbb_vin_fn,
-       },
-       [ATTR_USBIN_IMAX] = {
-               .name = "usb-charge-current-limit",
-               .reg = SMBB_USB_IMAX,
-               .max = 2500000,
-               .min = 100000,
-               .hw_fn = smbb_imax_fn,
-       },
-};
-
-static int smbb_charger_attr_write(struct smbb_charger *chg,
-               enum smbb_attr which, unsigned int val)
-{
-       const struct smbb_charger_attr *prop;
-       unsigned int wval;
-       unsigned int out;
-       int rc;
-
-       prop = &smbb_charger_attrs[which];
-
-       if (val > prop->max || val < prop->min) {
-               dev_err(chg->dev, "value out of range for %s [%u:%u]\n",
-                       prop->name, prop->min, prop->max);
-               return -EINVAL;
-       }
-
-       if (prop->safe_reg) {
-               rc = regmap_read(chg->regmap,
-                               chg->addr + prop->safe_reg, &wval);
-               if (rc) {
-                       dev_err(chg->dev,
-                               "unable to read safe value for '%s'\n",
-                               prop->name);
-                       return rc;
-               }
-
-               wval = prop->hw_fn(wval);
-
-               if (val > wval) {
-                       dev_warn(chg->dev,
-                               "%s above safe value, clamping at %u\n",
-                               prop->name, wval);
-                       val = wval;
-               }
-       }
-
-       wval = smbb_hw_lookup(val, prop->hw_fn);
-
-       rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval);
-       if (rc) {
-               dev_err(chg->dev, "unable to update %s", prop->name);
-               return rc;
-       }
-       out = prop->hw_fn(wval);
-       if (out != val) {
-               dev_warn(chg->dev,
-                       "%s inaccurate, rounded to %u\n",
-                       prop->name, out);
-       }
-
-       dev_dbg(chg->dev, "%s <= %d\n", prop->name, out);
-
-       chg->attr[which] = out;
-
-       return 0;
-}
-
-static int smbb_charger_attr_read(struct smbb_charger *chg,
-               enum smbb_attr which)
-{
-       const struct smbb_charger_attr *prop;
-       unsigned int val;
-       int rc;
-
-       prop = &smbb_charger_attrs[which];
-
-       rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val);
-       if (rc) {
-               dev_err(chg->dev, "failed to read %s\n", prop->name);
-               return rc;
-       }
-       val = prop->hw_fn(val);
-       dev_dbg(chg->dev, "%s => %d\n", prop->name, val);
-
-       chg->attr[which] = val;
-
-       return 0;
-}
-
-static int smbb_charger_attr_parse(struct smbb_charger *chg,
-               enum smbb_attr which)
-{
-       const struct smbb_charger_attr *prop;
-       unsigned int val;
-       int rc;
-
-       prop = &smbb_charger_attrs[which];
-
-       rc = of_property_read_u32(chg->dev->of_node, prop->name, &val);
-       if (rc == 0) {
-               rc = smbb_charger_attr_write(chg, which, val);
-               if (!rc || !prop->fail_ok)
-                       return rc;
-       }
-       return smbb_charger_attr_read(chg, which);
-}
-
-static void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag)
-{
-       bool state;
-       int ret;
-
-       ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state);
-       if (ret < 0) {
-               dev_err(chg->dev, "failed to read irq line\n");
-               return;
-       }
-
-       mutex_lock(&chg->statlock);
-       if (state)
-               chg->status |= flag;
-       else
-               chg->status &= ~flag;
-       mutex_unlock(&chg->statlock);
-
-       dev_dbg(chg->dev, "status = %03lx\n", chg->status);
-}
-
-static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)
-{
-       struct smbb_charger *chg = _data;
-
-       smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
-       extcon_set_cable_state_(chg->edev, EXTCON_USB,
-                               chg->status & STATUS_USBIN_VALID);
-       power_supply_changed(chg->usb_psy);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t smbb_dc_valid_handler(int irq, void *_data)
-{
-       struct smbb_charger *chg = _data;
-
-       smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID);
-       if (!chg->dc_disabled)
-               power_supply_changed(chg->dc_psy);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t smbb_bat_temp_handler(int irq, void *_data)
-{
-       struct smbb_charger *chg = _data;
-       unsigned int val;
-       int rc;
-
-       rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val);
-       if (rc)
-               return IRQ_HANDLED;
-
-       mutex_lock(&chg->statlock);
-       if (val & TEMP_STATUS_OK) {
-               chg->status |= STATUS_BAT_OK;
-       } else {
-               chg->status &= ~STATUS_BAT_OK;
-               if (val & TEMP_STATUS_HOT)
-                       chg->status |= STATUS_BAT_HOT;
-       }
-       mutex_unlock(&chg->statlock);
-
-       power_supply_changed(chg->bat_psy);
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t smbb_bat_present_handler(int irq, void *_data)
-{
-       struct smbb_charger *chg = _data;
-
-       smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT);
-       power_supply_changed(chg->bat_psy);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t smbb_chg_done_handler(int irq, void *_data)
-{
-       struct smbb_charger *chg = _data;
-
-       smbb_set_line_flag(chg, irq, STATUS_CHG_DONE);
-       power_supply_changed(chg->bat_psy);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t smbb_chg_gone_handler(int irq, void *_data)
-{
-       struct smbb_charger *chg = _data;
-
-       smbb_set_line_flag(chg, irq, STATUS_CHG_GONE);
-       power_supply_changed(chg->bat_psy);
-       power_supply_changed(chg->usb_psy);
-       if (!chg->dc_disabled)
-               power_supply_changed(chg->dc_psy);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t smbb_chg_fast_handler(int irq, void *_data)
-{
-       struct smbb_charger *chg = _data;
-
-       smbb_set_line_flag(chg, irq, STATUS_CHG_FAST);
-       power_supply_changed(chg->bat_psy);
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t smbb_chg_trkl_handler(int irq, void *_data)
-{
-       struct smbb_charger *chg = _data;
-
-       smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL);
-       power_supply_changed(chg->bat_psy);
-
-       return IRQ_HANDLED;
-}
-
-static const struct smbb_irq {
-       const char *name;
-       irqreturn_t (*handler)(int, void *);
-} smbb_charger_irqs[] = {
-       { "chg-done", smbb_chg_done_handler },
-       { "chg-fast", smbb_chg_fast_handler },
-       { "chg-trkl", smbb_chg_trkl_handler },
-       { "bat-temp-ok", smbb_bat_temp_handler },
-       { "bat-present", smbb_bat_present_handler },
-       { "chg-gone", smbb_chg_gone_handler },
-       { "usb-valid", smbb_usb_valid_handler },
-       { "dc-valid", smbb_dc_valid_handler },
-};
-
-static int smbb_usbin_get_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               union power_supply_propval *val)
-{
-       struct smbb_charger *chg = power_supply_get_drvdata(psy);
-       int rc = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               mutex_lock(&chg->statlock);
-               val->intval = !(chg->status & STATUS_CHG_GONE) &&
-                               (chg->status & STATUS_USBIN_VALID);
-               mutex_unlock(&chg->statlock);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
-               val->intval = chg->attr[ATTR_USBIN_IMAX];
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
-               val->intval = 2500000;
-               break;
-       default:
-               rc = -EINVAL;
-               break;
-       }
-
-       return rc;
-}
-
-static int smbb_usbin_set_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               const union power_supply_propval *val)
-{
-       struct smbb_charger *chg = power_supply_get_drvdata(psy);
-       int rc;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
-               rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX,
-                               val->intval);
-               break;
-       default:
-               rc = -EINVAL;
-               break;
-       }
-
-       return rc;
-}
-
-static int smbb_dcin_get_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               union power_supply_propval *val)
-{
-       struct smbb_charger *chg = power_supply_get_drvdata(psy);
-       int rc = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               mutex_lock(&chg->statlock);
-               val->intval = !(chg->status & STATUS_CHG_GONE) &&
-                               (chg->status & STATUS_DCIN_VALID);
-               mutex_unlock(&chg->statlock);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
-               val->intval = chg->attr[ATTR_DCIN_IMAX];
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
-               val->intval = 2500000;
-               break;
-       default:
-               rc = -EINVAL;
-               break;
-       }
-
-       return rc;
-}
-
-static int smbb_dcin_set_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               const union power_supply_propval *val)
-{
-       struct smbb_charger *chg = power_supply_get_drvdata(psy);
-       int rc;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
-               rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX,
-                               val->intval);
-               break;
-       default:
-               rc = -EINVAL;
-               break;
-       }
-
-       return rc;
-}
-
-static int smbb_charger_writable_property(struct power_supply *psy,
-               enum power_supply_property psp)
-{
-       return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
-}
-
-static int smbb_battery_get_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               union power_supply_propval *val)
-{
-       struct smbb_charger *chg = power_supply_get_drvdata(psy);
-       unsigned long status;
-       int rc = 0;
-
-       mutex_lock(&chg->statlock);
-       status = chg->status;
-       mutex_unlock(&chg->statlock);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (status & STATUS_CHG_GONE)
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID)))
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (status & STATUS_CHG_DONE)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else if (!(status & STATUS_BAT_OK))
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL))
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else /* everything is ok for charging, but we are not... */
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               if (status & STATUS_BAT_OK)
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-               else if (status & STATUS_BAT_HOT)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_COLD;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               if (status & STATUS_CHG_FAST)
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
-               else if (status & STATUS_CHG_TRKL)
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-               else
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = !!(status & STATUS_BAT_PRESENT);
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_MAX:
-               val->intval = chg->attr[ATTR_BAT_IMAX];
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               val->intval = chg->attr[ATTR_BAT_VMAX];
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               /* this charger is a single-cell lithium-ion battery charger
-               * only.  If you hook up some other technology, there will be
-               * fireworks.
-               */
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = 3000000; /* single-cell li-ion low end */
-               break;
-       default:
-               rc = -EINVAL;
-               break;
-       }
-
-       return rc;
-}
-
-static int smbb_battery_set_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               const union power_supply_propval *val)
-{
-       struct smbb_charger *chg = power_supply_get_drvdata(psy);
-       int rc;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CURRENT_MAX:
-               rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval);
-               break;
-       default:
-               rc = -EINVAL;
-               break;
-       }
-
-       return rc;
-}
-
-static int smbb_battery_writable_property(struct power_supply *psy,
-               enum power_supply_property psp)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_CURRENT_MAX:
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               return 1;
-       default:
-               return 0;
-       }
-}
-
-static enum power_supply_property smbb_charger_properties[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
-       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
-};
-
-static enum power_supply_property smbb_battery_properties[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_CURRENT_MAX,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-};
-
-static const struct reg_off_mask_default {
-       unsigned int offset;
-       unsigned int mask;
-       unsigned int value;
-       unsigned int rev_mask;
-} smbb_charger_setup[] = {
-       /* The bootloader is supposed to set this... make sure anyway. */
-       { SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE },
-
-       /* Disable software timer */
-       { SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 },
-
-       /* Clear and disable watchdog */
-       { SMBB_CHG_WDOG_TIME, 0xff, 160 },
-       { SMBB_CHG_WDOG_EN, WDOG_EN, 0 },
-
-       /* Use charger based EoC detection */
-       { SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG },
-
-       /* Disable GSM PA load adjustment.
-       * The PA signal is incorrectly connected on v2.
-       */
-       { SMBB_CHG_CFG, 0xff, 0x00, BIT(3) },
-
-       /* Use VBAT (not VSYS) to compensate for IR drop during fast charging */
-       { SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT },
-
-       /* Enable battery temperature comparators */
-       { SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN },
-
-       /* Stop USB enumeration timer */
-       { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
-
-#if 0 /* FIXME supposedly only to disable hardware ARB termination */
-       { SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC },
-       { SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE },
-#endif
-
-       /* Stop USB enumeration timer, again */
-       { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
-
-       /* Enable charging */
-       { SMBB_CHG_CTRL, CTRL_EN, CTRL_EN },
-};
-
-static char *smbb_bif[] = { "smbb-bif" };
-
-static const struct power_supply_desc bat_psy_desc = {
-       .name = "smbb-bif",
-       .type = POWER_SUPPLY_TYPE_BATTERY,
-       .properties = smbb_battery_properties,
-       .num_properties = ARRAY_SIZE(smbb_battery_properties),
-       .get_property = smbb_battery_get_property,
-       .set_property = smbb_battery_set_property,
-       .property_is_writeable = smbb_battery_writable_property,
-};
-
-static const struct power_supply_desc usb_psy_desc = {
-       .name = "smbb-usbin",
-       .type = POWER_SUPPLY_TYPE_USB,
-       .properties = smbb_charger_properties,
-       .num_properties = ARRAY_SIZE(smbb_charger_properties),
-       .get_property = smbb_usbin_get_property,
-       .set_property = smbb_usbin_set_property,
-       .property_is_writeable = smbb_charger_writable_property,
-};
-
-static const struct power_supply_desc dc_psy_desc = {
-       .name = "smbb-dcin",
-       .type = POWER_SUPPLY_TYPE_MAINS,
-       .properties = smbb_charger_properties,
-       .num_properties = ARRAY_SIZE(smbb_charger_properties),
-       .get_property = smbb_dcin_get_property,
-       .set_property = smbb_dcin_set_property,
-       .property_is_writeable = smbb_charger_writable_property,
-};
-
-static int smbb_charger_probe(struct platform_device *pdev)
-{
-       struct power_supply_config bat_cfg = {};
-       struct power_supply_config usb_cfg = {};
-       struct power_supply_config dc_cfg = {};
-       struct smbb_charger *chg;
-       int rc, i;
-
-       chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
-       if (!chg)
-               return -ENOMEM;
-
-       chg->dev = &pdev->dev;
-       mutex_init(&chg->statlock);
-
-       chg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
-       if (!chg->regmap) {
-               dev_err(&pdev->dev, "failed to locate regmap\n");
-               return -ENODEV;
-       }
-
-       rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr);
-       if (rc) {
-               dev_err(&pdev->dev, "missing or invalid 'reg' property\n");
-               return rc;
-       }
-
-       rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision);
-       if (rc) {
-               dev_err(&pdev->dev, "unable to read revision\n");
-               return rc;
-       }
-
-       chg->revision += 1;
-       if (chg->revision != 2 && chg->revision != 3) {
-               dev_err(&pdev->dev, "v1 hardware not supported\n");
-               return -ENODEV;
-       }
-       dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision);
-
-       chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc");
-
-       for (i = 0; i < _ATTR_CNT; ++i) {
-               rc = smbb_charger_attr_parse(chg, i);
-               if (rc) {
-                       dev_err(&pdev->dev, "failed to parse/apply settings\n");
-                       return rc;
-               }
-       }
-
-       bat_cfg.drv_data = chg;
-       bat_cfg.of_node = pdev->dev.of_node;
-       chg->bat_psy = devm_power_supply_register(&pdev->dev,
-                                                 &bat_psy_desc,
-                                                 &bat_cfg);
-       if (IS_ERR(chg->bat_psy)) {
-               dev_err(&pdev->dev, "failed to register battery\n");
-               return PTR_ERR(chg->bat_psy);
-       }
-
-       usb_cfg.drv_data = chg;
-       usb_cfg.supplied_to = smbb_bif;
-       usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
-       chg->usb_psy = devm_power_supply_register(&pdev->dev,
-                                                 &usb_psy_desc,
-                                                 &usb_cfg);
-       if (IS_ERR(chg->usb_psy)) {
-               dev_err(&pdev->dev, "failed to register USB power supply\n");
-               return PTR_ERR(chg->usb_psy);
-       }
-
-       chg->edev = devm_extcon_dev_allocate(&pdev->dev, smbb_usb_extcon_cable);
-       if (IS_ERR(chg->edev)) {
-               dev_err(&pdev->dev, "failed to allocate extcon device\n");
-               return -ENOMEM;
-       }
-
-       rc = devm_extcon_dev_register(&pdev->dev, chg->edev);
-       if (rc < 0) {
-               dev_err(&pdev->dev, "failed to register extcon device\n");
-               return rc;
-       }
-
-       if (!chg->dc_disabled) {
-               dc_cfg.drv_data = chg;
-               dc_cfg.supplied_to = smbb_bif;
-               dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
-               chg->dc_psy = devm_power_supply_register(&pdev->dev,
-                                                        &dc_psy_desc,
-                                                        &dc_cfg);
-               if (IS_ERR(chg->dc_psy)) {
-                       dev_err(&pdev->dev, "failed to register DC power supply\n");
-                       return PTR_ERR(chg->dc_psy);
-               }
-       }
-
-       for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) {
-               int irq;
-
-               irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
-               if (irq < 0) {
-                       dev_err(&pdev->dev, "failed to get irq '%s'\n",
-                               smbb_charger_irqs[i].name);
-                       return irq;
-               }
-
-               smbb_charger_irqs[i].handler(irq, chg);
-
-               rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-                               smbb_charger_irqs[i].handler, IRQF_ONESHOT,
-                               smbb_charger_irqs[i].name, chg);
-               if (rc) {
-                       dev_err(&pdev->dev, "failed to request irq '%s'\n",
-                               smbb_charger_irqs[i].name);
-                       return rc;
-               }
-       }
-
-       chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
-                       "qcom,jeita-extended-temp-range");
-
-       /* Set temperature range to [35%:70%] or [25%:80%] accordingly */
-       rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL,
-                       BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N,
-                       chg->jeita_ext_temp ?
-                               BTC_CTRL_COLD_EXT :
-                               BTC_CTRL_HOT_EXT_N);
-       if (rc) {
-               dev_err(&pdev->dev,
-                       "unable to set %s temperature range\n",
-                       chg->jeita_ext_temp ? "JEITA extended" : "normal");
-               return rc;
-       }
-
-       for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) {
-               const struct reg_off_mask_default *r = &smbb_charger_setup[i];
-
-               if (r->rev_mask & BIT(chg->revision))
-                       continue;
-
-               rc = regmap_update_bits(chg->regmap, chg->addr + r->offset,
-                               r->mask, r->value);
-               if (rc) {
-                       dev_err(&pdev->dev,
-                               "unable to initializing charging, bailing\n");
-                       return rc;
-               }
-       }
-
-       platform_set_drvdata(pdev, chg);
-
-       return 0;
-}
-
-static int smbb_charger_remove(struct platform_device *pdev)
-{
-       struct smbb_charger *chg;
-
-       chg = platform_get_drvdata(pdev);
-
-       regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0);
-
-       return 0;
-}
-
-static const struct of_device_id smbb_charger_id_table[] = {
-       { .compatible = "qcom,pm8941-charger" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, smbb_charger_id_table);
-
-static struct platform_driver smbb_charger_driver = {
-       .probe    = smbb_charger_probe,
-       .remove  = smbb_charger_remove,
-       .driver  = {
-               .name   = "qcom-smbb",
-               .of_match_table = smbb_charger_id_table,
-       },
-};
-module_platform_driver(smbb_charger_driver);
-
-MODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/rt5033_battery.c b/drivers/power/rt5033_battery.c
deleted file mode 100644 (file)
index bcdd830..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Fuel gauge driver for Richtek RT5033
- *
- * Copyright (C) 2014 Samsung Electronics, Co., Ltd.
- * Author: Beomho Seo <beomho.seo@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published bythe Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/rt5033-private.h>
-#include <linux/mfd/rt5033.h>
-
-static int rt5033_battery_get_capacity(struct i2c_client *client)
-{
-       struct rt5033_battery *battery = i2c_get_clientdata(client);
-       u32 msb;
-
-       regmap_read(battery->regmap, RT5033_FUEL_REG_SOC_H, &msb);
-
-       return msb;
-}
-
-static int rt5033_battery_get_present(struct i2c_client *client)
-{
-       struct rt5033_battery *battery = i2c_get_clientdata(client);
-       u32 val;
-
-       regmap_read(battery->regmap, RT5033_FUEL_REG_CONFIG_L, &val);
-
-       return (val & RT5033_FUEL_BAT_PRESENT) ? true : false;
-}
-
-static int rt5033_battery_get_watt_prop(struct i2c_client *client,
-               enum power_supply_property psp)
-{
-       struct rt5033_battery *battery = i2c_get_clientdata(client);
-       unsigned int regh, regl;
-       int ret;
-       u32 msb, lsb;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               regh = RT5033_FUEL_REG_VBAT_H;
-               regl = RT5033_FUEL_REG_VBAT_L;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               regh = RT5033_FUEL_REG_AVG_VOLT_H;
-               regl = RT5033_FUEL_REG_AVG_VOLT_L;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
-               regh = RT5033_FUEL_REG_OCV_H;
-               regl = RT5033_FUEL_REG_OCV_L;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       regmap_read(battery->regmap, regh, &msb);
-       regmap_read(battery->regmap, regl, &lsb);
-
-       ret = ((msb << 4) + (lsb >> 4)) * 1250 / 1000;
-
-       return ret;
-}
-
-static int rt5033_battery_get_property(struct power_supply *psy,
-               enum power_supply_property psp,
-               union power_supply_propval *val)
-{
-       struct rt5033_battery *battery = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
-               val->intval = rt5033_battery_get_watt_prop(battery->client,
-                                                                       psp);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = rt5033_battery_get_present(battery->client);
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               val->intval = rt5033_battery_get_capacity(battery->client);
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static enum power_supply_property rt5033_battery_props[] = {
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_AVG,
-       POWER_SUPPLY_PROP_VOLTAGE_OCV,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_CAPACITY,
-};
-
-static const struct regmap_config rt5033_battery_regmap_config = {
-       .reg_bits       = 8,
-       .val_bits       = 8,
-       .max_register   = RT5033_FUEL_REG_END,
-};
-
-static const struct power_supply_desc rt5033_battery_desc = {
-       .name           = "rt5033-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property   = rt5033_battery_get_property,
-       .properties     = rt5033_battery_props,
-       .num_properties = ARRAY_SIZE(rt5033_battery_props),
-};
-
-static int rt5033_battery_probe(struct i2c_client *client,
-               const struct i2c_device_id *id)
-{
-       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       struct power_supply_config psy_cfg = {};
-       struct rt5033_battery *battery;
-       u32 ret;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
-               return -EIO;
-
-       battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL);
-       if (!battery)
-               return -EINVAL;
-
-       battery->client = client;
-       battery->regmap = devm_regmap_init_i2c(client,
-                       &rt5033_battery_regmap_config);
-       if (IS_ERR(battery->regmap)) {
-               dev_err(&client->dev, "Failed to initialize regmap\n");
-               return -EINVAL;
-       }
-
-       i2c_set_clientdata(client, battery);
-       psy_cfg.drv_data = battery;
-
-       battery->psy = power_supply_register(&client->dev,
-                                            &rt5033_battery_desc, &psy_cfg);
-       if (IS_ERR(battery->psy)) {
-               dev_err(&client->dev, "Failed to register power supply\n");
-               ret = PTR_ERR(battery->psy);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int rt5033_battery_remove(struct i2c_client *client)
-{
-       struct rt5033_battery *battery = i2c_get_clientdata(client);
-
-       power_supply_unregister(battery->psy);
-
-       return 0;
-}
-
-static const struct i2c_device_id rt5033_battery_id[] = {
-       { "rt5033-battery", },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, rt5033_battery_id);
-
-static struct i2c_driver rt5033_battery_driver = {
-       .driver = {
-               .name = "rt5033-battery",
-       },
-       .probe = rt5033_battery_probe,
-       .remove = rt5033_battery_remove,
-       .id_table = rt5033_battery_id,
-};
-module_i2c_driver(rt5033_battery_driver);
-
-MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver");
-MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/rt9455_charger.c b/drivers/power/rt9455_charger.c
deleted file mode 100644 (file)
index cfdbde9..0000000
+++ /dev/null
@@ -1,1763 +0,0 @@
-/*
- * Driver for Richtek RT9455WSC battery charger.
- *
- * Copyright (C) 2015 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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/interrupt.h>
-#include <linux/delay.h>
-#include <linux/of_irq.h>
-#include <linux/of_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/power_supply.h>
-#include <linux/i2c.h>
-#include <linux/acpi.h>
-#include <linux/usb/phy.h>
-#include <linux/regmap.h>
-
-#define RT9455_MANUFACTURER                    "Richtek"
-#define RT9455_MODEL_NAME                      "RT9455"
-#define RT9455_DRIVER_NAME                     "rt9455-charger"
-
-#define RT9455_IRQ_NAME                                "interrupt"
-
-#define RT9455_PWR_RDY_DELAY                   1 /* 1 second */
-#define RT9455_MAX_CHARGING_TIME               21600 /* 6 hrs */
-#define RT9455_BATT_PRESENCE_DELAY             60 /* 60 seconds */
-
-#define RT9455_CHARGE_MODE                     0x00
-#define RT9455_BOOST_MODE                      0x01
-
-#define RT9455_FAULT                           0x03
-
-#define RT9455_IAICR_100MA                     0x00
-#define RT9455_IAICR_500MA                     0x01
-#define RT9455_IAICR_NO_LIMIT                  0x03
-
-#define RT9455_CHARGE_DISABLE                  0x00
-#define RT9455_CHARGE_ENABLE                   0x01
-
-#define RT9455_PWR_FAULT                       0x00
-#define RT9455_PWR_GOOD                                0x01
-
-#define RT9455_REG_CTRL1                       0x00 /* CTRL1 reg address */
-#define RT9455_REG_CTRL2                       0x01 /* CTRL2 reg address */
-#define RT9455_REG_CTRL3                       0x02 /* CTRL3 reg address */
-#define RT9455_REG_DEV_ID                      0x03 /* DEV_ID reg address */
-#define RT9455_REG_CTRL4                       0x04 /* CTRL4 reg address */
-#define RT9455_REG_CTRL5                       0x05 /* CTRL5 reg address */
-#define RT9455_REG_CTRL6                       0x06 /* CTRL6 reg address */
-#define RT9455_REG_CTRL7                       0x07 /* CTRL7 reg address */
-#define RT9455_REG_IRQ1                                0x08 /* IRQ1 reg address */
-#define RT9455_REG_IRQ2                                0x09 /* IRQ2 reg address */
-#define RT9455_REG_IRQ3                                0x0A /* IRQ3 reg address */
-#define RT9455_REG_MASK1                       0x0B /* MASK1 reg address */
-#define RT9455_REG_MASK2                       0x0C /* MASK2 reg address */
-#define RT9455_REG_MASK3                       0x0D /* MASK3 reg address */
-
-enum rt9455_fields {
-       F_STAT, F_BOOST, F_PWR_RDY, F_OTG_PIN_POLARITY, /* CTRL1 reg fields */
-
-       F_IAICR, F_TE_SHDN_EN, F_HIGHER_OCP, F_TE, F_IAICR_INT, F_HIZ,
-       F_OPA_MODE, /* CTRL2 reg fields */
-
-       F_VOREG, F_OTG_PL, F_OTG_EN, /* CTRL3 reg fields */
-
-       F_VENDOR_ID, F_CHIP_REV, /* DEV_ID reg fields */
-
-       F_RST, /* CTRL4 reg fields */
-
-       F_TMR_EN, F_MIVR, F_IPREC, F_IEOC_PERCENTAGE, /* CTRL5 reg fields*/
-
-       F_IAICR_SEL, F_ICHRG, F_VPREC, /* CTRL6 reg fields */
-
-       F_BATD_EN, F_CHG_EN, F_VMREG, /* CTRL7 reg fields */
-
-       F_TSDI, F_VINOVPI, F_BATAB, /* IRQ1 reg fields */
-
-       F_CHRVPI, F_CHBATOVI, F_CHTERMI, F_CHRCHGI, F_CH32MI, F_CHTREGI,
-       F_CHMIVRI, /* IRQ2 reg fields */
-
-       F_BSTBUSOVI, F_BSTOLI, F_BSTLOWVI, F_BST32SI, /* IRQ3 reg fields */
-
-       F_TSDM, F_VINOVPIM, F_BATABM, /* MASK1 reg fields */
-
-       F_CHRVPIM, F_CHBATOVIM, F_CHTERMIM, F_CHRCHGIM, F_CH32MIM, F_CHTREGIM,
-       F_CHMIVRIM, /* MASK2 reg fields */
-
-       F_BSTVINOVIM, F_BSTOLIM, F_BSTLOWVIM, F_BST32SIM, /* MASK3 reg fields */
-
-       F_MAX_FIELDS
-};
-
-static const struct reg_field rt9455_reg_fields[] = {
-       [F_STAT]                = REG_FIELD(RT9455_REG_CTRL1, 4, 5),
-       [F_BOOST]               = REG_FIELD(RT9455_REG_CTRL1, 3, 3),
-       [F_PWR_RDY]             = REG_FIELD(RT9455_REG_CTRL1, 2, 2),
-       [F_OTG_PIN_POLARITY]    = REG_FIELD(RT9455_REG_CTRL1, 1, 1),
-
-       [F_IAICR]               = REG_FIELD(RT9455_REG_CTRL2, 6, 7),
-       [F_TE_SHDN_EN]          = REG_FIELD(RT9455_REG_CTRL2, 5, 5),
-       [F_HIGHER_OCP]          = REG_FIELD(RT9455_REG_CTRL2, 4, 4),
-       [F_TE]                  = REG_FIELD(RT9455_REG_CTRL2, 3, 3),
-       [F_IAICR_INT]           = REG_FIELD(RT9455_REG_CTRL2, 2, 2),
-       [F_HIZ]                 = REG_FIELD(RT9455_REG_CTRL2, 1, 1),
-       [F_OPA_MODE]            = REG_FIELD(RT9455_REG_CTRL2, 0, 0),
-
-       [F_VOREG]               = REG_FIELD(RT9455_REG_CTRL3, 2, 7),
-       [F_OTG_PL]              = REG_FIELD(RT9455_REG_CTRL3, 1, 1),
-       [F_OTG_EN]              = REG_FIELD(RT9455_REG_CTRL3, 0, 0),
-
-       [F_VENDOR_ID]           = REG_FIELD(RT9455_REG_DEV_ID, 4, 7),
-       [F_CHIP_REV]            = REG_FIELD(RT9455_REG_DEV_ID, 0, 3),
-
-       [F_RST]                 = REG_FIELD(RT9455_REG_CTRL4, 7, 7),
-
-       [F_TMR_EN]              = REG_FIELD(RT9455_REG_CTRL5, 7, 7),
-       [F_MIVR]                = REG_FIELD(RT9455_REG_CTRL5, 4, 5),
-       [F_IPREC]               = REG_FIELD(RT9455_REG_CTRL5, 2, 3),
-       [F_IEOC_PERCENTAGE]     = REG_FIELD(RT9455_REG_CTRL5, 0, 1),
-
-       [F_IAICR_SEL]           = REG_FIELD(RT9455_REG_CTRL6, 7, 7),
-       [F_ICHRG]               = REG_FIELD(RT9455_REG_CTRL6, 4, 6),
-       [F_VPREC]               = REG_FIELD(RT9455_REG_CTRL6, 0, 2),
-
-       [F_BATD_EN]             = REG_FIELD(RT9455_REG_CTRL7, 6, 6),
-       [F_CHG_EN]              = REG_FIELD(RT9455_REG_CTRL7, 4, 4),
-       [F_VMREG]               = REG_FIELD(RT9455_REG_CTRL7, 0, 3),
-
-       [F_TSDI]                = REG_FIELD(RT9455_REG_IRQ1, 7, 7),
-       [F_VINOVPI]             = REG_FIELD(RT9455_REG_IRQ1, 6, 6),
-       [F_BATAB]               = REG_FIELD(RT9455_REG_IRQ1, 0, 0),
-
-       [F_CHRVPI]              = REG_FIELD(RT9455_REG_IRQ2, 7, 7),
-       [F_CHBATOVI]            = REG_FIELD(RT9455_REG_IRQ2, 5, 5),
-       [F_CHTERMI]             = REG_FIELD(RT9455_REG_IRQ2, 4, 4),
-       [F_CHRCHGI]             = REG_FIELD(RT9455_REG_IRQ2, 3, 3),
-       [F_CH32MI]              = REG_FIELD(RT9455_REG_IRQ2, 2, 2),
-       [F_CHTREGI]             = REG_FIELD(RT9455_REG_IRQ2, 1, 1),
-       [F_CHMIVRI]             = REG_FIELD(RT9455_REG_IRQ2, 0, 0),
-
-       [F_BSTBUSOVI]           = REG_FIELD(RT9455_REG_IRQ3, 7, 7),
-       [F_BSTOLI]              = REG_FIELD(RT9455_REG_IRQ3, 6, 6),
-       [F_BSTLOWVI]            = REG_FIELD(RT9455_REG_IRQ3, 5, 5),
-       [F_BST32SI]             = REG_FIELD(RT9455_REG_IRQ3, 3, 3),
-
-       [F_TSDM]                = REG_FIELD(RT9455_REG_MASK1, 7, 7),
-       [F_VINOVPIM]            = REG_FIELD(RT9455_REG_MASK1, 6, 6),
-       [F_BATABM]              = REG_FIELD(RT9455_REG_MASK1, 0, 0),
-
-       [F_CHRVPIM]             = REG_FIELD(RT9455_REG_MASK2, 7, 7),
-       [F_CHBATOVIM]           = REG_FIELD(RT9455_REG_MASK2, 5, 5),
-       [F_CHTERMIM]            = REG_FIELD(RT9455_REG_MASK2, 4, 4),
-       [F_CHRCHGIM]            = REG_FIELD(RT9455_REG_MASK2, 3, 3),
-       [F_CH32MIM]             = REG_FIELD(RT9455_REG_MASK2, 2, 2),
-       [F_CHTREGIM]            = REG_FIELD(RT9455_REG_MASK2, 1, 1),
-       [F_CHMIVRIM]            = REG_FIELD(RT9455_REG_MASK2, 0, 0),
-
-       [F_BSTVINOVIM]          = REG_FIELD(RT9455_REG_MASK3, 7, 7),
-       [F_BSTOLIM]             = REG_FIELD(RT9455_REG_MASK3, 6, 6),
-       [F_BSTLOWVIM]           = REG_FIELD(RT9455_REG_MASK3, 5, 5),
-       [F_BST32SIM]            = REG_FIELD(RT9455_REG_MASK3, 3, 3),
-};
-
-#define GET_MASK(fid)  (BIT(rt9455_reg_fields[fid].msb + 1) - \
-                        BIT(rt9455_reg_fields[fid].lsb))
-
-/*
- * Each array initialised below shows the possible real-world values for a
- * group of bits belonging to RT9455 registers. The arrays are sorted in
- * ascending order. The index of each real-world value represents the value
- * that is encoded in the group of bits belonging to RT9455 registers.
- */
-/* REG06[6:4] (ICHRG) in uAh */
-static const int rt9455_ichrg_values[] = {
-        500000,  650000,  800000,  950000, 1100000, 1250000, 1400000, 1550000
-};
-
-/*
- * When the charger is in charge mode, REG02[7:2] represent battery regulation
- * voltage.
- */
-/* REG02[7:2] (VOREG) in uV */
-static const int rt9455_voreg_values[] = {
-       3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000,
-       3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000,
-       3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000,
-       3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000,
-       4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000,
-       4300000, 4330000, 4350000, 4370000, 4390000, 4410000, 4430000, 4450000,
-       4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000,
-       4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000
-};
-
-/*
- * When the charger is in boost mode, REG02[7:2] represent boost output
- * voltage.
- */
-/* REG02[7:2] (Boost output voltage) in uV */
-static const int rt9455_boost_voltage_values[] = {
-       4425000, 4450000, 4475000, 4500000, 4525000, 4550000, 4575000, 4600000,
-       4625000, 4650000, 4675000, 4700000, 4725000, 4750000, 4775000, 4800000,
-       4825000, 4850000, 4875000, 4900000, 4925000, 4950000, 4975000, 5000000,
-       5025000, 5050000, 5075000, 5100000, 5125000, 5150000, 5175000, 5200000,
-       5225000, 5250000, 5275000, 5300000, 5325000, 5350000, 5375000, 5400000,
-       5425000, 5450000, 5475000, 5500000, 5525000, 5550000, 5575000, 5600000,
-       5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000,
-       5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000,
-};
-
-/* REG07[3:0] (VMREG) in uV */
-static const int rt9455_vmreg_values[] = {
-       4200000, 4220000, 4240000, 4260000, 4280000, 4300000, 4320000, 4340000,
-       4360000, 4380000, 4400000, 4430000, 4450000, 4450000, 4450000, 4450000
-};
-
-/* REG05[5:4] (IEOC_PERCENTAGE) */
-static const int rt9455_ieoc_percentage_values[] = {
-       10, 30, 20, 30
-};
-
-/* REG05[1:0] (MIVR) in uV */
-static const int rt9455_mivr_values[] = {
-       4000000, 4250000, 4500000, 5000000
-};
-
-/* REG05[1:0] (IAICR) in uA */
-static const int rt9455_iaicr_values[] = {
-       100000, 500000, 1000000, 2000000
-};
-
-struct rt9455_info {
-       struct i2c_client               *client;
-       struct regmap                   *regmap;
-       struct regmap_field             *regmap_fields[F_MAX_FIELDS];
-       struct power_supply             *charger;
-#if IS_ENABLED(CONFIG_USB_PHY)
-       struct usb_phy                  *usb_phy;
-       struct notifier_block           nb;
-#endif
-       struct delayed_work             pwr_rdy_work;
-       struct delayed_work             max_charging_time_work;
-       struct delayed_work             batt_presence_work;
-       u32                             voreg;
-       u32                             boost_voltage;
-};
-
-/*
- * Iterate through each element of the 'tbl' array until an element whose value
- * is greater than v is found. Return the index of the respective element,
- * or the index of the last element in the array, if no such element is found.
- */
-static unsigned int rt9455_find_idx(const int tbl[], int tbl_size, int v)
-{
-       int i;
-
-       /*
-        * No need to iterate until the last index in the table because
-        * if no element greater than v is found in the table,
-        * or if only the last element is greater than v,
-        * function returns the index of the last element.
-        */
-       for (i = 0; i < tbl_size - 1; i++)
-               if (v <= tbl[i])
-                       return i;
-
-       return (tbl_size - 1);
-}
-
-static int rt9455_get_field_val(struct rt9455_info *info,
-                               enum rt9455_fields field,
-                               const int tbl[], int tbl_size, int *val)
-{
-       unsigned int v;
-       int ret;
-
-       ret = regmap_field_read(info->regmap_fields[field], &v);
-       if (ret)
-               return ret;
-
-       v = (v >= tbl_size) ? (tbl_size - 1) : v;
-       *val = tbl[v];
-
-       return 0;
-}
-
-static int rt9455_set_field_val(struct rt9455_info *info,
-                               enum rt9455_fields field,
-                               const int tbl[], int tbl_size, int val)
-{
-       unsigned int idx = rt9455_find_idx(tbl, tbl_size, val);
-
-       return regmap_field_write(info->regmap_fields[field], idx);
-}
-
-static int rt9455_register_reset(struct rt9455_info *info)
-{
-       struct device *dev = &info->client->dev;
-       unsigned int v;
-       int ret, limit = 100;
-
-       ret = regmap_field_write(info->regmap_fields[F_RST], 0x01);
-       if (ret) {
-               dev_err(dev, "Failed to set RST bit\n");
-               return ret;
-       }
-
-       /*
-        * To make sure that reset operation has finished, loop until RST bit
-        * is set to 0.
-        */
-       do {
-               ret = regmap_field_read(info->regmap_fields[F_RST], &v);
-               if (ret) {
-                       dev_err(dev, "Failed to read RST bit\n");
-                       return ret;
-               }
-
-               if (!v)
-                       break;
-
-               usleep_range(10, 100);
-       } while (--limit);
-
-       if (!limit)
-               return -EIO;
-
-       return 0;
-}
-
-/* Charger power supply property routines */
-static enum power_supply_property rt9455_charger_properties[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_SCOPE,
-       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static char *rt9455_charger_supplied_to[] = {
-       "main-battery",
-};
-
-static int rt9455_charger_get_status(struct rt9455_info *info,
-                                    union power_supply_propval *val)
-{
-       unsigned int v, pwr_rdy;
-       int ret;
-
-       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY],
-                               &pwr_rdy);
-       if (ret) {
-               dev_err(&info->client->dev, "Failed to read PWR_RDY bit\n");
-               return ret;
-       }
-
-       /*
-        * If PWR_RDY bit is unset, the battery is discharging. Otherwise,
-        * STAT bits value must be checked.
-        */
-       if (!pwr_rdy) {
-               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               return 0;
-       }
-
-       ret = regmap_field_read(info->regmap_fields[F_STAT], &v);
-       if (ret) {
-               dev_err(&info->client->dev, "Failed to read STAT bits\n");
-               return ret;
-       }
-
-       switch (v) {
-       case 0:
-               /*
-                * If PWR_RDY bit is set, but STAT bits value is 0, the charger
-                * may be in one of the following cases:
-                * 1. CHG_EN bit is 0.
-                * 2. CHG_EN bit is 1 but the battery is not connected.
-                * In any of these cases, POWER_SUPPLY_STATUS_NOT_CHARGING is
-                * returned.
-                */
-               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               return 0;
-       case 1:
-               val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               return 0;
-       case 2:
-               val->intval = POWER_SUPPLY_STATUS_FULL;
-               return 0;
-       default:
-               val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-               return 0;
-       }
-}
-
-static int rt9455_charger_get_health(struct rt9455_info *info,
-                                    union power_supply_propval *val)
-{
-       struct device *dev = &info->client->dev;
-       unsigned int v;
-       int ret;
-
-       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-
-       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &v);
-       if (ret) {
-               dev_err(dev, "Failed to read IRQ1 register\n");
-               return ret;
-       }
-
-       if (v & GET_MASK(F_TSDI)) {
-               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               return 0;
-       }
-       if (v & GET_MASK(F_VINOVPI)) {
-               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               return 0;
-       }
-       if (v & GET_MASK(F_BATAB)) {
-               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               return 0;
-       }
-
-       ret = regmap_read(info->regmap, RT9455_REG_IRQ2, &v);
-       if (ret) {
-               dev_err(dev, "Failed to read IRQ2 register\n");
-               return ret;
-       }
-
-       if (v & GET_MASK(F_CHBATOVI)) {
-               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               return 0;
-       }
-       if (v & GET_MASK(F_CH32MI)) {
-               val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
-               return 0;
-       }
-
-       ret = regmap_read(info->regmap, RT9455_REG_IRQ3, &v);
-       if (ret) {
-               dev_err(dev, "Failed to read IRQ3 register\n");
-               return ret;
-       }
-
-       if (v & GET_MASK(F_BSTBUSOVI)) {
-               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               return 0;
-       }
-       if (v & GET_MASK(F_BSTOLI)) {
-               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               return 0;
-       }
-       if (v & GET_MASK(F_BSTLOWVI)) {
-               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               return 0;
-       }
-       if (v & GET_MASK(F_BST32SI)) {
-               val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
-               return 0;
-       }
-
-       ret = regmap_field_read(info->regmap_fields[F_STAT], &v);
-       if (ret) {
-               dev_err(dev, "Failed to read STAT bits\n");
-               return ret;
-       }
-
-       if (v == RT9455_FAULT) {
-               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               return 0;
-       }
-
-       return 0;
-}
-
-static int rt9455_charger_get_battery_presence(struct rt9455_info *info,
-                                              union power_supply_propval *val)
-{
-       unsigned int v;
-       int ret;
-
-       ret = regmap_field_read(info->regmap_fields[F_BATAB], &v);
-       if (ret) {
-               dev_err(&info->client->dev, "Failed to read BATAB bit\n");
-               return ret;
-       }
-
-       /*
-        * Since BATAB is 1 when battery is NOT present and 0 otherwise,
-        * !BATAB is returned.
-        */
-       val->intval = !v;
-
-       return 0;
-}
-
-static int rt9455_charger_get_online(struct rt9455_info *info,
-                                    union power_supply_propval *val)
-{
-       unsigned int v;
-       int ret;
-
-       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY], &v);
-       if (ret) {
-               dev_err(&info->client->dev, "Failed to read PWR_RDY bit\n");
-               return ret;
-       }
-
-       val->intval = (int)v;
-
-       return 0;
-}
-
-static int rt9455_charger_get_current(struct rt9455_info *info,
-                                     union power_supply_propval *val)
-{
-       int curr;
-       int ret;
-
-       ret = rt9455_get_field_val(info, F_ICHRG,
-                                  rt9455_ichrg_values,
-                                  ARRAY_SIZE(rt9455_ichrg_values),
-                                  &curr);
-       if (ret) {
-               dev_err(&info->client->dev, "Failed to read ICHRG value\n");
-               return ret;
-       }
-
-       val->intval = curr;
-
-       return 0;
-}
-
-static int rt9455_charger_get_current_max(struct rt9455_info *info,
-                                         union power_supply_propval *val)
-{
-       int idx = ARRAY_SIZE(rt9455_ichrg_values) - 1;
-
-       val->intval = rt9455_ichrg_values[idx];
-
-       return 0;
-}
-
-static int rt9455_charger_get_voltage(struct rt9455_info *info,
-                                     union power_supply_propval *val)
-{
-       int voltage;
-       int ret;
-
-       ret = rt9455_get_field_val(info, F_VOREG,
-                                  rt9455_voreg_values,
-                                  ARRAY_SIZE(rt9455_voreg_values),
-                                  &voltage);
-       if (ret) {
-               dev_err(&info->client->dev, "Failed to read VOREG value\n");
-               return ret;
-       }
-
-       val->intval = voltage;
-
-       return 0;
-}
-
-static int rt9455_charger_get_voltage_max(struct rt9455_info *info,
-                                         union power_supply_propval *val)
-{
-       int idx = ARRAY_SIZE(rt9455_vmreg_values) - 1;
-
-       val->intval = rt9455_vmreg_values[idx];
-
-       return 0;
-}
-
-static int rt9455_charger_get_term_current(struct rt9455_info *info,
-                                          union power_supply_propval *val)
-{
-       struct device *dev = &info->client->dev;
-       int ichrg, ieoc_percentage, ret;
-
-       ret = rt9455_get_field_val(info, F_ICHRG,
-                                  rt9455_ichrg_values,
-                                  ARRAY_SIZE(rt9455_ichrg_values),
-                                  &ichrg);
-       if (ret) {
-               dev_err(dev, "Failed to read ICHRG value\n");
-               return ret;
-       }
-
-       ret = rt9455_get_field_val(info, F_IEOC_PERCENTAGE,
-                                  rt9455_ieoc_percentage_values,
-                                  ARRAY_SIZE(rt9455_ieoc_percentage_values),
-                                  &ieoc_percentage);
-       if (ret) {
-               dev_err(dev, "Failed to read IEOC value\n");
-               return ret;
-       }
-
-       val->intval = ichrg * ieoc_percentage / 100;
-
-       return 0;
-}
-
-static int rt9455_charger_get_property(struct power_supply *psy,
-                                      enum power_supply_property psp,
-                                      union power_supply_propval *val)
-{
-       struct rt9455_info *info = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               return rt9455_charger_get_status(info, val);
-       case POWER_SUPPLY_PROP_HEALTH:
-               return rt9455_charger_get_health(info, val);
-       case POWER_SUPPLY_PROP_PRESENT:
-               return rt9455_charger_get_battery_presence(info, val);
-       case POWER_SUPPLY_PROP_ONLINE:
-               return rt9455_charger_get_online(info, val);
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               return rt9455_charger_get_current(info, val);
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
-               return rt9455_charger_get_current_max(info, val);
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               return rt9455_charger_get_voltage(info, val);
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
-               return rt9455_charger_get_voltage_max(info, val);
-       case POWER_SUPPLY_PROP_SCOPE:
-               val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
-               return 0;
-       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
-               return rt9455_charger_get_term_current(info, val);
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = RT9455_MODEL_NAME;
-               return 0;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = RT9455_MANUFACTURER;
-               return 0;
-       default:
-               return -ENODATA;
-       }
-}
-
-static int rt9455_hw_init(struct rt9455_info *info, u32 ichrg,
-                         u32 ieoc_percentage,
-                         u32 mivr, u32 iaicr)
-{
-       struct device *dev = &info->client->dev;
-       int idx, ret;
-
-       ret = rt9455_register_reset(info);
-       if (ret) {
-               dev_err(dev, "Power On Reset failed\n");
-               return ret;
-       }
-
-       /* Set TE bit in order to enable end of charge detection */
-       ret = regmap_field_write(info->regmap_fields[F_TE], 1);
-       if (ret) {
-               dev_err(dev, "Failed to set TE bit\n");
-               return ret;
-       }
-
-       /* Set TE_SHDN_EN bit in order to enable end of charge detection */
-       ret = regmap_field_write(info->regmap_fields[F_TE_SHDN_EN], 1);
-       if (ret) {
-               dev_err(dev, "Failed to set TE_SHDN_EN bit\n");
-               return ret;
-       }
-
-       /*
-        * Set BATD_EN bit in order to enable battery detection
-        * when charging is done
-        */
-       ret = regmap_field_write(info->regmap_fields[F_BATD_EN], 1);
-       if (ret) {
-               dev_err(dev, "Failed to set BATD_EN bit\n");
-               return ret;
-       }
-
-       /*
-        * Disable Safety Timer. In charge mode, this timer terminates charging
-        * if no read or write via I2C is done within 32 minutes. This timer
-        * avoids overcharging the baterry when the OS is not loaded and the
-        * charger is connected to a power source.
-        * In boost mode, this timer triggers BST32SI interrupt if no read or
-        * write via I2C is done within 32 seconds.
-        * When the OS is loaded and the charger driver is inserted, it is used
-        * delayed_work, named max_charging_time_work, to avoid overcharging
-        * the battery.
-        */
-       ret = regmap_field_write(info->regmap_fields[F_TMR_EN], 0x00);
-       if (ret) {
-               dev_err(dev, "Failed to disable Safety Timer\n");
-               return ret;
-       }
-
-       /* Set ICHRG to value retrieved from device-specific data */
-       ret = rt9455_set_field_val(info, F_ICHRG,
-                                  rt9455_ichrg_values,
-                                  ARRAY_SIZE(rt9455_ichrg_values), ichrg);
-       if (ret) {
-               dev_err(dev, "Failed to set ICHRG value\n");
-               return ret;
-       }
-
-       /* Set IEOC Percentage to value retrieved from device-specific data */
-       ret = rt9455_set_field_val(info, F_IEOC_PERCENTAGE,
-                                  rt9455_ieoc_percentage_values,
-                                  ARRAY_SIZE(rt9455_ieoc_percentage_values),
-                                  ieoc_percentage);
-       if (ret) {
-               dev_err(dev, "Failed to set IEOC Percentage value\n");
-               return ret;
-       }
-
-       /* Set VOREG to value retrieved from device-specific data */
-       ret = rt9455_set_field_val(info, F_VOREG,
-                                  rt9455_voreg_values,
-                                  ARRAY_SIZE(rt9455_voreg_values),
-                                  info->voreg);
-       if (ret) {
-               dev_err(dev, "Failed to set VOREG value\n");
-               return ret;
-       }
-
-       /* Set VMREG value to maximum (4.45V). */
-       idx = ARRAY_SIZE(rt9455_vmreg_values) - 1;
-       ret = rt9455_set_field_val(info, F_VMREG,
-                                  rt9455_vmreg_values,
-                                  ARRAY_SIZE(rt9455_vmreg_values),
-                                  rt9455_vmreg_values[idx]);
-       if (ret) {
-               dev_err(dev, "Failed to set VMREG value\n");
-               return ret;
-       }
-
-       /*
-        * Set MIVR to value retrieved from device-specific data.
-        * If no value is specified, default value for MIVR is 4.5V.
-        */
-       if (mivr == -1)
-               mivr = 4500000;
-
-       ret = rt9455_set_field_val(info, F_MIVR,
-                                  rt9455_mivr_values,
-                                  ARRAY_SIZE(rt9455_mivr_values), mivr);
-       if (ret) {
-               dev_err(dev, "Failed to set MIVR value\n");
-               return ret;
-       }
-
-       /*
-        * Set IAICR to value retrieved from device-specific data.
-        * If no value is specified, default value for IAICR is 500 mA.
-        */
-       if (iaicr == -1)
-               iaicr = 500000;
-
-       ret = rt9455_set_field_val(info, F_IAICR,
-                                  rt9455_iaicr_values,
-                                  ARRAY_SIZE(rt9455_iaicr_values), iaicr);
-       if (ret) {
-               dev_err(dev, "Failed to set IAICR value\n");
-               return ret;
-       }
-
-       /*
-        * Set IAICR_INT bit so that IAICR value is determined by IAICR bits
-        * and not by OTG pin.
-        */
-       ret = regmap_field_write(info->regmap_fields[F_IAICR_INT], 0x01);
-       if (ret) {
-               dev_err(dev, "Failed to set IAICR_INT bit\n");
-               return ret;
-       }
-
-       /*
-        * Disable CHMIVRI interrupt. Because the driver sets MIVR value,
-        * CHMIVRI is triggered, but there is no action to be taken by the
-        * driver when CHMIVRI is triggered.
-        */
-       ret = regmap_field_write(info->regmap_fields[F_CHMIVRIM], 0x01);
-       if (ret) {
-               dev_err(dev, "Failed to mask CHMIVRI interrupt\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-/*
- * Before setting the charger into boost mode, boost output voltage is
- * set. This is needed because boost output voltage may differ from battery
- * regulation voltage. F_VOREG bits represent either battery regulation voltage
- * or boost output voltage, depending on the mode the charger is. Both battery
- * regulation voltage and boost output voltage are read from DT/ACPI during
- * probe.
- */
-static int rt9455_set_boost_voltage_before_boost_mode(struct rt9455_info *info)
-{
-       struct device *dev = &info->client->dev;
-       int ret;
-
-       ret = rt9455_set_field_val(info, F_VOREG,
-                                  rt9455_boost_voltage_values,
-                                  ARRAY_SIZE(rt9455_boost_voltage_values),
-                                  info->boost_voltage);
-       if (ret) {
-               dev_err(dev, "Failed to set boost output voltage value\n");
-               return ret;
-       }
-
-       return 0;
-}
-#endif
-
-/*
- * Before setting the charger into charge mode, battery regulation voltage is
- * set. This is needed because boost output voltage may differ from battery
- * regulation voltage. F_VOREG bits represent either battery regulation voltage
- * or boost output voltage, depending on the mode the charger is. Both battery
- * regulation voltage and boost output voltage are read from DT/ACPI during
- * probe.
- */
-static int rt9455_set_voreg_before_charge_mode(struct rt9455_info *info)
-{
-       struct device *dev = &info->client->dev;
-       int ret;
-
-       ret = rt9455_set_field_val(info, F_VOREG,
-                                  rt9455_voreg_values,
-                                  ARRAY_SIZE(rt9455_voreg_values),
-                                  info->voreg);
-       if (ret) {
-               dev_err(dev, "Failed to set VOREG value\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int rt9455_irq_handler_check_irq1_register(struct rt9455_info *info,
-                                                 bool *_is_battery_absent,
-                                                 bool *_alert_userspace)
-{
-       unsigned int irq1, mask1, mask2;
-       struct device *dev = &info->client->dev;
-       bool is_battery_absent = false;
-       bool alert_userspace = false;
-       int ret;
-
-       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &irq1);
-       if (ret) {
-               dev_err(dev, "Failed to read IRQ1 register\n");
-               return ret;
-       }
-
-       ret = regmap_read(info->regmap, RT9455_REG_MASK1, &mask1);
-       if (ret) {
-               dev_err(dev, "Failed to read MASK1 register\n");
-               return ret;
-       }
-
-       if (irq1 & GET_MASK(F_TSDI)) {
-               dev_err(dev, "Thermal shutdown fault occurred\n");
-               alert_userspace = true;
-       }
-
-       if (irq1 & GET_MASK(F_VINOVPI)) {
-               dev_err(dev, "Overvoltage input occurred\n");
-               alert_userspace = true;
-       }
-
-       if (irq1 & GET_MASK(F_BATAB)) {
-               dev_err(dev, "Battery absence occurred\n");
-               is_battery_absent = true;
-               alert_userspace = true;
-
-               if ((mask1 & GET_MASK(F_BATABM)) == 0) {
-                       ret = regmap_field_write(info->regmap_fields[F_BATABM],
-                                                0x01);
-                       if (ret) {
-                               dev_err(dev, "Failed to mask BATAB interrupt\n");
-                               return ret;
-                       }
-               }
-
-               ret = regmap_read(info->regmap, RT9455_REG_MASK2, &mask2);
-               if (ret) {
-                       dev_err(dev, "Failed to read MASK2 register\n");
-                       return ret;
-               }
-
-               if (mask2 & GET_MASK(F_CHTERMIM)) {
-                       ret = regmap_field_write(
-                               info->regmap_fields[F_CHTERMIM], 0x00);
-                       if (ret) {
-                               dev_err(dev, "Failed to unmask CHTERMI interrupt\n");
-                               return ret;
-                       }
-               }
-
-               if (mask2 & GET_MASK(F_CHRCHGIM)) {
-                       ret = regmap_field_write(
-                               info->regmap_fields[F_CHRCHGIM], 0x00);
-                       if (ret) {
-                               dev_err(dev, "Failed to unmask CHRCHGI interrupt\n");
-                               return ret;
-                       }
-               }
-
-               /*
-                * When the battery is absent, max_charging_time_work is
-                * cancelled, since no charging is done.
-                */
-               cancel_delayed_work_sync(&info->max_charging_time_work);
-               /*
-                * Since no interrupt is triggered when the battery is
-                * reconnected, max_charging_time_work is not rescheduled.
-                * Therefore, batt_presence_work is scheduled to check whether
-                * the battery is still absent or not.
-                */
-               queue_delayed_work(system_power_efficient_wq,
-                                  &info->batt_presence_work,
-                                  RT9455_BATT_PRESENCE_DELAY * HZ);
-       }
-
-       *_is_battery_absent = is_battery_absent;
-
-       if (alert_userspace)
-               *_alert_userspace = alert_userspace;
-
-       return 0;
-}
-
-static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
-                                                 bool is_battery_absent,
-                                                 bool *_alert_userspace)
-{
-       unsigned int irq2, mask2;
-       struct device *dev = &info->client->dev;
-       bool alert_userspace = false;
-       int ret;
-
-       ret = regmap_read(info->regmap, RT9455_REG_IRQ2, &irq2);
-       if (ret) {
-               dev_err(dev, "Failed to read IRQ2 register\n");
-               return ret;
-       }
-
-       ret = regmap_read(info->regmap, RT9455_REG_MASK2, &mask2);
-       if (ret) {
-               dev_err(dev, "Failed to read MASK2 register\n");
-               return ret;
-       }
-
-       if (irq2 & GET_MASK(F_CHRVPI)) {
-               dev_dbg(dev, "Charger fault occurred\n");
-               /*
-                * CHRVPI bit is set in 2 cases:
-                * 1. when the power source is connected to the charger.
-                * 2. when the power source is disconnected from the charger.
-                * To identify the case, PWR_RDY bit is checked. Because
-                * PWR_RDY bit is set / cleared after CHRVPI interrupt is
-                * triggered, it is used delayed_work to later read PWR_RDY bit.
-                * Also, do not set to true alert_userspace, because there is no
-                * need to notify userspace when CHRVPI interrupt has occurred.
-                * Userspace will be notified after PWR_RDY bit is read.
-                */
-               queue_delayed_work(system_power_efficient_wq,
-                                  &info->pwr_rdy_work,
-                                  RT9455_PWR_RDY_DELAY * HZ);
-       }
-       if (irq2 & GET_MASK(F_CHBATOVI)) {
-               dev_err(dev, "Battery OVP occurred\n");
-               alert_userspace = true;
-       }
-       if (irq2 & GET_MASK(F_CHTERMI)) {
-               dev_dbg(dev, "Charge terminated\n");
-               if (!is_battery_absent) {
-                       if ((mask2 & GET_MASK(F_CHTERMIM)) == 0) {
-                               ret = regmap_field_write(
-                                       info->regmap_fields[F_CHTERMIM], 0x01);
-                               if (ret) {
-                                       dev_err(dev, "Failed to mask CHTERMI interrupt\n");
-                                       return ret;
-                               }
-                               /*
-                                * Update MASK2 value, since CHTERMIM bit is
-                                * set.
-                                */
-                               mask2 = mask2 | GET_MASK(F_CHTERMIM);
-                       }
-                       cancel_delayed_work_sync(&info->max_charging_time_work);
-                       alert_userspace = true;
-               }
-       }
-       if (irq2 & GET_MASK(F_CHRCHGI)) {
-               dev_dbg(dev, "Recharge request\n");
-               ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
-                                        RT9455_CHARGE_ENABLE);
-               if (ret) {
-                       dev_err(dev, "Failed to enable charging\n");
-                       return ret;
-               }
-               if (mask2 & GET_MASK(F_CHTERMIM)) {
-                       ret = regmap_field_write(
-                               info->regmap_fields[F_CHTERMIM], 0x00);
-                       if (ret) {
-                               dev_err(dev, "Failed to unmask CHTERMI interrupt\n");
-                               return ret;
-                       }
-                       /* Update MASK2 value, since CHTERMIM bit is cleared. */
-                       mask2 = mask2 & ~GET_MASK(F_CHTERMIM);
-               }
-               if (!is_battery_absent) {
-                       /*
-                        * No need to check whether the charger is connected to
-                        * power source when CHRCHGI is received, since CHRCHGI
-                        * is not triggered if the charger is not connected to
-                        * the power source.
-                        */
-                       queue_delayed_work(system_power_efficient_wq,
-                                          &info->max_charging_time_work,
-                                          RT9455_MAX_CHARGING_TIME * HZ);
-                       alert_userspace = true;
-               }
-       }
-       if (irq2 & GET_MASK(F_CH32MI)) {
-               dev_err(dev, "Charger fault. 32 mins timeout occurred\n");
-               alert_userspace = true;
-       }
-       if (irq2 & GET_MASK(F_CHTREGI)) {
-               dev_warn(dev,
-                        "Charger warning. Thermal regulation loop active\n");
-               alert_userspace = true;
-       }
-       if (irq2 & GET_MASK(F_CHMIVRI)) {
-               dev_dbg(dev,
-                       "Charger warning. Input voltage MIVR loop active\n");
-       }
-
-       if (alert_userspace)
-               *_alert_userspace = alert_userspace;
-
-       return 0;
-}
-
-static int rt9455_irq_handler_check_irq3_register(struct rt9455_info *info,
-                                                 bool *_alert_userspace)
-{
-       unsigned int irq3, mask3;
-       struct device *dev = &info->client->dev;
-       bool alert_userspace = false;
-       int ret;
-
-       ret = regmap_read(info->regmap, RT9455_REG_IRQ3, &irq3);
-       if (ret) {
-               dev_err(dev, "Failed to read IRQ3 register\n");
-               return ret;
-       }
-
-       ret = regmap_read(info->regmap, RT9455_REG_MASK3, &mask3);
-       if (ret) {
-               dev_err(dev, "Failed to read MASK3 register\n");
-               return ret;
-       }
-
-       if (irq3 & GET_MASK(F_BSTBUSOVI)) {
-               dev_err(dev, "Boost fault. Overvoltage input occurred\n");
-               alert_userspace = true;
-       }
-       if (irq3 & GET_MASK(F_BSTOLI)) {
-               dev_err(dev, "Boost fault. Overload\n");
-               alert_userspace = true;
-       }
-       if (irq3 & GET_MASK(F_BSTLOWVI)) {
-               dev_err(dev, "Boost fault. Battery voltage too low\n");
-               alert_userspace = true;
-       }
-       if (irq3 & GET_MASK(F_BST32SI)) {
-               dev_err(dev, "Boost fault. 32 seconds timeout occurred.\n");
-               alert_userspace = true;
-       }
-
-       if (alert_userspace) {
-               dev_info(dev, "Boost fault occurred, therefore the charger goes into charge mode\n");
-               ret = rt9455_set_voreg_before_charge_mode(info);
-               if (ret) {
-                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
-                       return ret;
-               }
-               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
-                                        RT9455_CHARGE_MODE);
-               if (ret) {
-                       dev_err(dev, "Failed to set charger in charge mode\n");
-                       return ret;
-               }
-               *_alert_userspace = alert_userspace;
-       }
-
-       return 0;
-}
-
-static irqreturn_t rt9455_irq_handler_thread(int irq, void *data)
-{
-       struct rt9455_info *info = data;
-       struct device *dev;
-       bool alert_userspace = false;
-       bool is_battery_absent = false;
-       unsigned int status;
-       int ret;
-
-       if (!info)
-               return IRQ_NONE;
-
-       dev = &info->client->dev;
-
-       if (irq != info->client->irq) {
-               dev_err(dev, "Interrupt is not for RT9455 charger\n");
-               return IRQ_NONE;
-       }
-
-       ret = regmap_field_read(info->regmap_fields[F_STAT], &status);
-       if (ret) {
-               dev_err(dev, "Failed to read STAT bits\n");
-               return IRQ_HANDLED;
-       }
-       dev_dbg(dev, "Charger status is %d\n", status);
-
-       /*
-        * Each function that processes an IRQ register receives as output
-        * parameter alert_userspace pointer. alert_userspace is set to true
-        * in such a function only if an interrupt has occurred in the
-        * respective interrupt register. This way, it is avoided the following
-        * case: interrupt occurs only in IRQ1 register,
-        * rt9455_irq_handler_check_irq1_register() function sets to true
-        * alert_userspace, but rt9455_irq_handler_check_irq2_register()
-        * and rt9455_irq_handler_check_irq3_register() functions set to false
-        * alert_userspace and power_supply_changed() is never called.
-        */
-       ret = rt9455_irq_handler_check_irq1_register(info, &is_battery_absent,
-                                                    &alert_userspace);
-       if (ret) {
-               dev_err(dev, "Failed to handle IRQ1 register\n");
-               return IRQ_HANDLED;
-       }
-
-       ret = rt9455_irq_handler_check_irq2_register(info, is_battery_absent,
-                                                    &alert_userspace);
-       if (ret) {
-               dev_err(dev, "Failed to handle IRQ2 register\n");
-               return IRQ_HANDLED;
-       }
-
-       ret = rt9455_irq_handler_check_irq3_register(info, &alert_userspace);
-       if (ret) {
-               dev_err(dev, "Failed to handle IRQ3 register\n");
-               return IRQ_HANDLED;
-       }
-
-       if (alert_userspace) {
-               /*
-                * Sometimes, an interrupt occurs while rt9455_probe() function
-                * is executing and power_supply_register() is not yet called.
-                * Do not call power_supply_changed() in this case.
-                */
-               if (info->charger)
-                       power_supply_changed(info->charger);
-       }
-
-       return IRQ_HANDLED;
-}
-
-static int rt9455_discover_charger(struct rt9455_info *info, u32 *ichrg,
-                                  u32 *ieoc_percentage,
-                                  u32 *mivr, u32 *iaicr)
-{
-       struct device *dev = &info->client->dev;
-       int ret;
-
-       if (!dev->of_node && !ACPI_HANDLE(dev)) {
-               dev_err(dev, "No support for either device tree or ACPI\n");
-               return -EINVAL;
-       }
-       /*
-        * ICHRG, IEOC_PERCENTAGE, VOREG and boost output voltage are mandatory
-        * parameters.
-        */
-       ret = device_property_read_u32(dev, "richtek,output-charge-current",
-                                      ichrg);
-       if (ret) {
-               dev_err(dev, "Error: missing \"output-charge-current\" property\n");
-               return ret;
-       }
-
-       ret = device_property_read_u32(dev, "richtek,end-of-charge-percentage",
-                                      ieoc_percentage);
-       if (ret) {
-               dev_err(dev, "Error: missing \"end-of-charge-percentage\" property\n");
-               return ret;
-       }
-
-       ret = device_property_read_u32(dev,
-                                      "richtek,battery-regulation-voltage",
-                                      &info->voreg);
-       if (ret) {
-               dev_err(dev, "Error: missing \"battery-regulation-voltage\" property\n");
-               return ret;
-       }
-
-       ret = device_property_read_u32(dev, "richtek,boost-output-voltage",
-                                      &info->boost_voltage);
-       if (ret) {
-               dev_err(dev, "Error: missing \"boost-output-voltage\" property\n");
-               return ret;
-       }
-
-       /*
-        * MIVR and IAICR are optional parameters. Do not return error if one of
-        * them is not present in ACPI table or device tree specification.
-        */
-       device_property_read_u32(dev, "richtek,min-input-voltage-regulation",
-                                mivr);
-       device_property_read_u32(dev, "richtek,avg-input-current-regulation",
-                                iaicr);
-
-       return 0;
-}
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-static int rt9455_usb_event_none(struct rt9455_info *info,
-                                u8 opa_mode, u8 iaicr)
-{
-       struct device *dev = &info->client->dev;
-       int ret;
-
-       if (opa_mode == RT9455_BOOST_MODE) {
-               ret = rt9455_set_voreg_before_charge_mode(info);
-               if (ret) {
-                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
-                       return ret;
-               }
-               /*
-                * If the charger is in boost mode, and it has received
-                * USB_EVENT_NONE, this means the consumer device powered by the
-                * charger is not connected anymore.
-                * In this case, the charger goes into charge mode.
-                */
-               dev_dbg(dev, "USB_EVENT_NONE received, therefore the charger goes into charge mode\n");
-               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
-                                        RT9455_CHARGE_MODE);
-               if (ret) {
-                       dev_err(dev, "Failed to set charger in charge mode\n");
-                       return NOTIFY_DONE;
-               }
-       }
-
-       dev_dbg(dev, "USB_EVENT_NONE received, therefore IAICR is set to its minimum value\n");
-       if (iaicr != RT9455_IAICR_100MA) {
-               ret = regmap_field_write(info->regmap_fields[F_IAICR],
-                                        RT9455_IAICR_100MA);
-               if (ret) {
-                       dev_err(dev, "Failed to set IAICR value\n");
-                       return NOTIFY_DONE;
-               }
-       }
-
-       return NOTIFY_OK;
-}
-
-static int rt9455_usb_event_vbus(struct rt9455_info *info,
-                                u8 opa_mode, u8 iaicr)
-{
-       struct device *dev = &info->client->dev;
-       int ret;
-
-       if (opa_mode == RT9455_BOOST_MODE) {
-               ret = rt9455_set_voreg_before_charge_mode(info);
-               if (ret) {
-                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
-                       return ret;
-               }
-               /*
-                * If the charger is in boost mode, and it has received
-                * USB_EVENT_VBUS, this means the consumer device powered by the
-                * charger is not connected anymore.
-                * In this case, the charger goes into charge mode.
-                */
-               dev_dbg(dev, "USB_EVENT_VBUS received, therefore the charger goes into charge mode\n");
-               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
-                                        RT9455_CHARGE_MODE);
-               if (ret) {
-                       dev_err(dev, "Failed to set charger in charge mode\n");
-                       return NOTIFY_DONE;
-               }
-       }
-
-       dev_dbg(dev, "USB_EVENT_VBUS received, therefore IAICR is set to 500 mA\n");
-       if (iaicr != RT9455_IAICR_500MA) {
-               ret = regmap_field_write(info->regmap_fields[F_IAICR],
-                                        RT9455_IAICR_500MA);
-               if (ret) {
-                       dev_err(dev, "Failed to set IAICR value\n");
-                       return NOTIFY_DONE;
-               }
-       }
-
-       return NOTIFY_OK;
-}
-
-static int rt9455_usb_event_id(struct rt9455_info *info,
-                              u8 opa_mode, u8 iaicr)
-{
-       struct device *dev = &info->client->dev;
-       int ret;
-
-       if (opa_mode == RT9455_CHARGE_MODE) {
-               ret = rt9455_set_boost_voltage_before_boost_mode(info);
-               if (ret) {
-                       dev_err(dev, "Failed to set boost output voltage before entering boost mode\n");
-                       return ret;
-               }
-               /*
-                * If the charger is in charge mode, and it has received
-                * USB_EVENT_ID, this means a consumer device is connected and
-                * it should be powered by the charger.
-                * In this case, the charger goes into boost mode.
-                */
-               dev_dbg(dev, "USB_EVENT_ID received, therefore the charger goes into boost mode\n");
-               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
-                                        RT9455_BOOST_MODE);
-               if (ret) {
-                       dev_err(dev, "Failed to set charger in boost mode\n");
-                       return NOTIFY_DONE;
-               }
-       }
-
-       dev_dbg(dev, "USB_EVENT_ID received, therefore IAICR is set to its minimum value\n");
-       if (iaicr != RT9455_IAICR_100MA) {
-               ret = regmap_field_write(info->regmap_fields[F_IAICR],
-                                        RT9455_IAICR_100MA);
-               if (ret) {
-                       dev_err(dev, "Failed to set IAICR value\n");
-                       return NOTIFY_DONE;
-               }
-       }
-
-       return NOTIFY_OK;
-}
-
-static int rt9455_usb_event_charger(struct rt9455_info *info,
-                                   u8 opa_mode, u8 iaicr)
-{
-       struct device *dev = &info->client->dev;
-       int ret;
-
-       if (opa_mode == RT9455_BOOST_MODE) {
-               ret = rt9455_set_voreg_before_charge_mode(info);
-               if (ret) {
-                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
-                       return ret;
-               }
-               /*
-                * If the charger is in boost mode, and it has received
-                * USB_EVENT_CHARGER, this means the consumer device powered by
-                * the charger is not connected anymore.
-                * In this case, the charger goes into charge mode.
-                */
-               dev_dbg(dev, "USB_EVENT_CHARGER received, therefore the charger goes into charge mode\n");
-               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
-                                        RT9455_CHARGE_MODE);
-               if (ret) {
-                       dev_err(dev, "Failed to set charger in charge mode\n");
-                       return NOTIFY_DONE;
-               }
-       }
-
-       dev_dbg(dev, "USB_EVENT_CHARGER received, therefore IAICR is set to no current limit\n");
-       if (iaicr != RT9455_IAICR_NO_LIMIT) {
-               ret = regmap_field_write(info->regmap_fields[F_IAICR],
-                                        RT9455_IAICR_NO_LIMIT);
-               if (ret) {
-                       dev_err(dev, "Failed to set IAICR value\n");
-                       return NOTIFY_DONE;
-               }
-       }
-
-       return NOTIFY_OK;
-}
-
-static int rt9455_usb_event(struct notifier_block *nb,
-                           unsigned long event, void *power)
-{
-       struct rt9455_info *info = container_of(nb, struct rt9455_info, nb);
-       struct device *dev = &info->client->dev;
-       unsigned int opa_mode, iaicr;
-       int ret;
-
-       /*
-        * Determine whether the charger is in charge mode
-        * or in boost mode.
-        */
-       ret = regmap_field_read(info->regmap_fields[F_OPA_MODE],
-                               &opa_mode);
-       if (ret) {
-               dev_err(dev, "Failed to read OPA_MODE value\n");
-               return NOTIFY_DONE;
-       }
-
-       ret = regmap_field_read(info->regmap_fields[F_IAICR],
-                               &iaicr);
-       if (ret) {
-               dev_err(dev, "Failed to read IAICR value\n");
-               return NOTIFY_DONE;
-       }
-
-       dev_dbg(dev, "Received USB event %lu\n", event);
-       switch (event) {
-       case USB_EVENT_NONE:
-               return rt9455_usb_event_none(info, opa_mode, iaicr);
-       case USB_EVENT_VBUS:
-               return rt9455_usb_event_vbus(info, opa_mode, iaicr);
-       case USB_EVENT_ID:
-               return rt9455_usb_event_id(info, opa_mode, iaicr);
-       case USB_EVENT_CHARGER:
-               return rt9455_usb_event_charger(info, opa_mode, iaicr);
-       default:
-               dev_err(dev, "Unknown USB event\n");
-       }
-       return NOTIFY_DONE;
-}
-#endif
-
-static void rt9455_pwr_rdy_work_callback(struct work_struct *work)
-{
-       struct rt9455_info *info = container_of(work, struct rt9455_info,
-                                               pwr_rdy_work.work);
-       struct device *dev = &info->client->dev;
-       unsigned int pwr_rdy;
-       int ret;
-
-       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY], &pwr_rdy);
-       if (ret) {
-               dev_err(dev, "Failed to read PWR_RDY bit\n");
-               return;
-       }
-       switch (pwr_rdy) {
-       case RT9455_PWR_FAULT:
-               dev_dbg(dev, "Charger disconnected from power source\n");
-               cancel_delayed_work_sync(&info->max_charging_time_work);
-               break;
-       case RT9455_PWR_GOOD:
-               dev_dbg(dev, "Charger connected to power source\n");
-               ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
-                                        RT9455_CHARGE_ENABLE);
-               if (ret) {
-                       dev_err(dev, "Failed to enable charging\n");
-                       return;
-               }
-               queue_delayed_work(system_power_efficient_wq,
-                                  &info->max_charging_time_work,
-                                  RT9455_MAX_CHARGING_TIME * HZ);
-               break;
-       }
-       /*
-        * Notify userspace that the charger has been either connected to or
-        * disconnected from the power source.
-        */
-       power_supply_changed(info->charger);
-}
-
-static void rt9455_max_charging_time_work_callback(struct work_struct *work)
-{
-       struct rt9455_info *info = container_of(work, struct rt9455_info,
-                                               max_charging_time_work.work);
-       struct device *dev = &info->client->dev;
-       int ret;
-
-       dev_err(dev, "Battery has been charging for at least 6 hours and is not yet fully charged. Battery is dead, therefore charging is disabled.\n");
-       ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
-                                RT9455_CHARGE_DISABLE);
-       if (ret)
-               dev_err(dev, "Failed to disable charging\n");
-}
-
-static void rt9455_batt_presence_work_callback(struct work_struct *work)
-{
-       struct rt9455_info *info = container_of(work, struct rt9455_info,
-                                               batt_presence_work.work);
-       struct device *dev = &info->client->dev;
-       unsigned int irq1, mask1;
-       int ret;
-
-       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &irq1);
-       if (ret) {
-               dev_err(dev, "Failed to read IRQ1 register\n");
-               return;
-       }
-
-       /*
-        * If the battery is still absent, batt_presence_work is rescheduled.
-        * Otherwise, max_charging_time is scheduled.
-        */
-       if (irq1 & GET_MASK(F_BATAB)) {
-               queue_delayed_work(system_power_efficient_wq,
-                                  &info->batt_presence_work,
-                                  RT9455_BATT_PRESENCE_DELAY * HZ);
-       } else {
-               queue_delayed_work(system_power_efficient_wq,
-                                  &info->max_charging_time_work,
-                                  RT9455_MAX_CHARGING_TIME * HZ);
-
-               ret = regmap_read(info->regmap, RT9455_REG_MASK1, &mask1);
-               if (ret) {
-                       dev_err(dev, "Failed to read MASK1 register\n");
-                       return;
-               }
-
-               if (mask1 & GET_MASK(F_BATABM)) {
-                       ret = regmap_field_write(info->regmap_fields[F_BATABM],
-                                                0x00);
-                       if (ret)
-                               dev_err(dev, "Failed to unmask BATAB interrupt\n");
-               }
-               /*
-                * Notify userspace that the battery is now connected to the
-                * charger.
-                */
-               power_supply_changed(info->charger);
-       }
-}
-
-static const struct power_supply_desc rt9455_charger_desc = {
-       .name                   = RT9455_DRIVER_NAME,
-       .type                   = POWER_SUPPLY_TYPE_USB,
-       .properties             = rt9455_charger_properties,
-       .num_properties         = ARRAY_SIZE(rt9455_charger_properties),
-       .get_property           = rt9455_charger_get_property,
-};
-
-static bool rt9455_is_writeable_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case RT9455_REG_DEV_ID:
-       case RT9455_REG_IRQ1:
-       case RT9455_REG_IRQ2:
-       case RT9455_REG_IRQ3:
-               return false;
-       default:
-               return true;
-       }
-}
-
-static bool rt9455_is_volatile_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case RT9455_REG_DEV_ID:
-       case RT9455_REG_CTRL5:
-       case RT9455_REG_CTRL6:
-               return false;
-       default:
-               return true;
-       }
-}
-
-static const struct regmap_config rt9455_regmap_config = {
-       .reg_bits       = 8,
-       .val_bits       = 8,
-       .writeable_reg  = rt9455_is_writeable_reg,
-       .volatile_reg   = rt9455_is_volatile_reg,
-       .max_register   = RT9455_REG_MASK3,
-       .cache_type     = REGCACHE_RBTREE,
-};
-
-static int rt9455_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
-{
-       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
-       struct device *dev = &client->dev;
-       struct rt9455_info *info;
-       struct power_supply_config rt9455_charger_config = {};
-       /*
-        * Mandatory device-specific data values. Also, VOREG and boost output
-        * voltage are mandatory values, but they are stored in rt9455_info
-        * structure.
-        */
-       u32 ichrg, ieoc_percentage;
-       /* Optional device-specific data values. */
-       u32 mivr = -1, iaicr = -1;
-       int i, ret;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
-               return -ENODEV;
-       }
-       info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-
-       info->client = client;
-       i2c_set_clientdata(client, info);
-
-       info->regmap = devm_regmap_init_i2c(client,
-                                           &rt9455_regmap_config);
-       if (IS_ERR(info->regmap)) {
-               dev_err(dev, "Failed to initialize register map\n");
-               return -EINVAL;
-       }
-
-       for (i = 0; i < F_MAX_FIELDS; i++) {
-               info->regmap_fields[i] =
-                       devm_regmap_field_alloc(dev, info->regmap,
-                                               rt9455_reg_fields[i]);
-               if (IS_ERR(info->regmap_fields[i])) {
-                       dev_err(dev,
-                               "Failed to allocate regmap field = %d\n", i);
-                       return PTR_ERR(info->regmap_fields[i]);
-               }
-       }
-
-       ret = rt9455_discover_charger(info, &ichrg, &ieoc_percentage,
-                                     &mivr, &iaicr);
-       if (ret) {
-               dev_err(dev, "Failed to discover charger\n");
-               return ret;
-       }
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-       info->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
-       if (IS_ERR(info->usb_phy)) {
-               dev_err(dev, "Failed to get USB transceiver\n");
-       } else {
-               info->nb.notifier_call = rt9455_usb_event;
-               ret = usb_register_notifier(info->usb_phy, &info->nb);
-               if (ret) {
-                       dev_err(dev, "Failed to register USB notifier\n");
-                       /*
-                        * If usb_register_notifier() fails, set notifier_call
-                        * to NULL, to avoid calling usb_unregister_notifier().
-                        */
-                       info->nb.notifier_call = NULL;
-               }
-       }
-#endif
-
-       INIT_DEFERRABLE_WORK(&info->pwr_rdy_work, rt9455_pwr_rdy_work_callback);
-       INIT_DEFERRABLE_WORK(&info->max_charging_time_work,
-                            rt9455_max_charging_time_work_callback);
-       INIT_DEFERRABLE_WORK(&info->batt_presence_work,
-                            rt9455_batt_presence_work_callback);
-
-       rt9455_charger_config.of_node           = dev->of_node;
-       rt9455_charger_config.drv_data          = info;
-       rt9455_charger_config.supplied_to       = rt9455_charger_supplied_to;
-       rt9455_charger_config.num_supplicants   =
-                                       ARRAY_SIZE(rt9455_charger_supplied_to);
-       ret = devm_request_threaded_irq(dev, client->irq, NULL,
-                                       rt9455_irq_handler_thread,
-                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-                                       RT9455_DRIVER_NAME, info);
-       if (ret) {
-               dev_err(dev, "Failed to register IRQ handler\n");
-               goto put_usb_notifier;
-       }
-
-       ret = rt9455_hw_init(info, ichrg, ieoc_percentage, mivr, iaicr);
-       if (ret) {
-               dev_err(dev, "Failed to set charger to its default values\n");
-               goto put_usb_notifier;
-       }
-
-       info->charger = devm_power_supply_register(dev, &rt9455_charger_desc,
-                                                  &rt9455_charger_config);
-       if (IS_ERR(info->charger)) {
-               dev_err(dev, "Failed to register charger\n");
-               ret = PTR_ERR(info->charger);
-               goto put_usb_notifier;
-       }
-
-       return 0;
-
-put_usb_notifier:
-#if IS_ENABLED(CONFIG_USB_PHY)
-       if (info->nb.notifier_call)  {
-               usb_unregister_notifier(info->usb_phy, &info->nb);
-               info->nb.notifier_call = NULL;
-       }
-#endif
-       return ret;
-}
-
-static int rt9455_remove(struct i2c_client *client)
-{
-       int ret;
-       struct rt9455_info *info = i2c_get_clientdata(client);
-
-       ret = rt9455_register_reset(info);
-       if (ret)
-               dev_err(&info->client->dev, "Failed to set charger to its default values\n");
-
-#if IS_ENABLED(CONFIG_USB_PHY)
-       if (info->nb.notifier_call)
-               usb_unregister_notifier(info->usb_phy, &info->nb);
-#endif
-
-       cancel_delayed_work_sync(&info->pwr_rdy_work);
-       cancel_delayed_work_sync(&info->max_charging_time_work);
-       cancel_delayed_work_sync(&info->batt_presence_work);
-
-       return ret;
-}
-
-static const struct i2c_device_id rt9455_i2c_id_table[] = {
-       { RT9455_DRIVER_NAME, 0 },
-       { },
-};
-MODULE_DEVICE_TABLE(i2c, rt9455_i2c_id_table);
-
-static const struct of_device_id rt9455_of_match[] = {
-       { .compatible = "richtek,rt9455", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, rt9455_of_match);
-
-static const struct acpi_device_id rt9455_i2c_acpi_match[] = {
-       { "RT945500", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(acpi, rt9455_i2c_acpi_match);
-
-static struct i2c_driver rt9455_driver = {
-       .probe          = rt9455_probe,
-       .remove         = rt9455_remove,
-       .id_table       = rt9455_i2c_id_table,
-       .driver = {
-               .name           = RT9455_DRIVER_NAME,
-               .of_match_table = of_match_ptr(rt9455_of_match),
-               .acpi_match_table = ACPI_PTR(rt9455_i2c_acpi_match),
-       },
-};
-module_i2c_driver(rt9455_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Anda-Maria Nicolae <anda-maria.nicolae@intel.com>");
-MODULE_DESCRIPTION("Richtek RT9455 Charger Driver");
diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c
deleted file mode 100644 (file)
index af9383d..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Nokia RX-51 battery driver
- *
- * Copyright (C) 2012  Pali Rohár <pali.rohar@gmail.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <linux/module.h>
-#include <linux/param.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/i2c/twl4030-madc.h>
-#include <linux/iio/consumer.h>
-#include <linux/of.h>
-
-struct rx51_device_info {
-       struct device *dev;
-       struct power_supply *bat;
-       struct power_supply_desc bat_desc;
-       struct iio_channel *channel_temp;
-       struct iio_channel *channel_bsi;
-       struct iio_channel *channel_vbat;
-};
-
-/*
- * Read ADCIN channel value, code copied from maemo kernel
- */
-static int rx51_battery_read_adc(struct iio_channel *channel)
-{
-       int val, err;
-       err = iio_read_channel_average_raw(channel, &val);
-       if (err < 0)
-               return err;
-       return val;
-}
-
-/*
- * Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage
- * This conversion formula was extracted from maemo program bsi-read
- */
-static int rx51_battery_read_voltage(struct rx51_device_info *di)
-{
-       int voltage = rx51_battery_read_adc(di->channel_vbat);
-
-       if (voltage < 0) {
-               dev_err(di->dev, "Could not read ADC: %d\n", voltage);
-               return voltage;
-       }
-
-       return 1000 * (10000 * voltage / 1705);
-}
-
-/*
- * Temperature look-up tables
- * TEMP = (1/(t1 + 1/298) - 273.15)
- * Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255))
- * Formula is based on experimental data, RX-51 CAL data, maemo program bme
- * and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671
- */
-
-/*
- * Table1 (temperature for first 25 RAW values)
- * Usage: TEMP = rx51_temp_table1[RAW]
- *   RAW is between 1 and 24
- *   TEMP is between 201 C and 55 C
- */
-static u8 rx51_temp_table1[] = {
-       255, 201, 159, 138, 124, 114, 106,  99,  94,  89,  85,  82,  78,  75,
-        73,  70,  68,  66,  64,  62,  61,  59,  57,  56,  55
-};
-
-/*
- * Table2 (lowest RAW value for temperature)
- * Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first]
- *   TEMP is between 53 C and -32 C
- *   RAW is between 25 and 993
- */
-#define rx51_temp_table2_first 53
-static u16 rx51_temp_table2[] = {
-        25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  39,
-        40,  41,  43,  44,  46,  48,  49,  51,  53,  55,  57,  59,  61,  64,
-        66,  69,  71,  74,  77,  80,  83,  86,  90,  94,  97, 101, 106, 110,
-       115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211,
-       221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415,
-       437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885,
-       937, 993, 1024
-};
-
-/*
- * Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius
- * Use Temperature look-up tables for conversation
- */
-static int rx51_battery_read_temperature(struct rx51_device_info *di)
-{
-       int min = 0;
-       int max = ARRAY_SIZE(rx51_temp_table2) - 1;
-       int raw = rx51_battery_read_adc(di->channel_temp);
-
-       if (raw < 0)
-               dev_err(di->dev, "Could not read ADC: %d\n", raw);
-
-       /* Zero and negative values are undefined */
-       if (raw <= 0)
-               return INT_MAX;
-
-       /* ADC channels are 10 bit, higher value are undefined */
-       if (raw >= (1 << 10))
-               return INT_MIN;
-
-       /* First check for temperature in first direct table */
-       if (raw < ARRAY_SIZE(rx51_temp_table1))
-               return rx51_temp_table1[raw] * 10;
-
-       /* Binary search RAW value in second inverse table */
-       while (max - min > 1) {
-               int mid = (max + min) / 2;
-               if (rx51_temp_table2[mid] <= raw)
-                       min = mid;
-               else if (rx51_temp_table2[mid] > raw)
-                       max = mid;
-               if (rx51_temp_table2[mid] == raw)
-                       break;
-       }
-
-       return (rx51_temp_table2_first - min) * 10;
-}
-
-/*
- * Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah
- * This conversion formula was extracted from maemo program bsi-read
- */
-static int rx51_battery_read_capacity(struct rx51_device_info *di)
-{
-       int capacity = rx51_battery_read_adc(di->channel_bsi);
-
-       if (capacity < 0) {
-               dev_err(di->dev, "Could not read ADC: %d\n", capacity);
-               return capacity;
-       }
-
-       return 1280 * (1200 * capacity)/(1024 - capacity);
-}
-
-/*
- * Return power_supply property
- */
-static int rx51_battery_get_property(struct power_supply *psy,
-                                       enum power_supply_property psp,
-                                       union power_supply_propval *val)
-{
-       struct rx51_device_info *di = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = 4200000;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = rx51_battery_read_voltage(di) ? 1 : 0;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = rx51_battery_read_voltage(di);
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = rx51_battery_read_temperature(di);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               val->intval = rx51_battery_read_capacity(di);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       if (val->intval == INT_MAX || val->intval == INT_MIN)
-               return -EINVAL;
-
-       return 0;
-}
-
-static enum power_supply_property rx51_battery_props[] = {
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-};
-
-static int rx51_battery_probe(struct platform_device *pdev)
-{
-       struct power_supply_config psy_cfg = {};
-       struct rx51_device_info *di;
-       int ret;
-
-       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, di);
-
-       di->dev = &pdev->dev;
-       di->bat_desc.name = "rx51-battery";
-       di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
-       di->bat_desc.properties = rx51_battery_props;
-       di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props);
-       di->bat_desc.get_property = rx51_battery_get_property;
-
-       psy_cfg.drv_data = di;
-
-       di->channel_temp = iio_channel_get(di->dev, "temp");
-       if (IS_ERR(di->channel_temp)) {
-               ret = PTR_ERR(di->channel_temp);
-               goto error;
-       }
-
-       di->channel_bsi  = iio_channel_get(di->dev, "bsi");
-       if (IS_ERR(di->channel_bsi)) {
-               ret = PTR_ERR(di->channel_bsi);
-               goto error_channel_temp;
-       }
-
-       di->channel_vbat = iio_channel_get(di->dev, "vbat");
-       if (IS_ERR(di->channel_vbat)) {
-               ret = PTR_ERR(di->channel_vbat);
-               goto error_channel_bsi;
-       }
-
-       di->bat = power_supply_register(di->dev, &di->bat_desc, &psy_cfg);
-       if (IS_ERR(di->bat)) {
-               ret = PTR_ERR(di->bat);
-               goto error_channel_vbat;
-       }
-
-       return 0;
-
-error_channel_vbat:
-       iio_channel_release(di->channel_vbat);
-error_channel_bsi:
-       iio_channel_release(di->channel_bsi);
-error_channel_temp:
-       iio_channel_release(di->channel_temp);
-error:
-
-       return ret;
-}
-
-static int rx51_battery_remove(struct platform_device *pdev)
-{
-       struct rx51_device_info *di = platform_get_drvdata(pdev);
-
-       power_supply_unregister(di->bat);
-
-       iio_channel_release(di->channel_vbat);
-       iio_channel_release(di->channel_bsi);
-       iio_channel_release(di->channel_temp);
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id n900_battery_of_match[] = {
-       {.compatible = "nokia,n900-battery", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, n900_battery_of_match);
-#endif
-
-static struct platform_driver rx51_battery_driver = {
-       .probe = rx51_battery_probe,
-       .remove = rx51_battery_remove,
-       .driver = {
-               .name = "rx51-battery",
-               .of_match_table = of_match_ptr(n900_battery_of_match),
-       },
-};
-module_platform_driver(rx51_battery_driver);
-
-MODULE_ALIAS("platform:rx51-battery");
-MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
-MODULE_DESCRIPTION("Nokia RX-51 battery driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c
deleted file mode 100644 (file)
index 0ffe5cd..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- *     iPAQ h1930/h1940/rx1950 battery controller driver
- *     Copyright (c) Vasily Khoruzhick
- *     Based on h1940_battery.c by Arnaud Patard
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file COPYING in the main directory of this archive for
- * more details.
- *
- */
-
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/leds.h>
-#include <linux/gpio.h>
-#include <linux/err.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
-#include <linux/s3c_adc_battery.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include <plat/adc.h>
-
-#define BAT_POLL_INTERVAL              10000 /* ms */
-#define JITTER_DELAY                   500 /* ms */
-
-struct s3c_adc_bat {
-       struct power_supply             *psy;
-       struct s3c_adc_client           *client;
-       struct s3c_adc_bat_pdata        *pdata;
-       int                             volt_value;
-       int                             cur_value;
-       unsigned int                    timestamp;
-       int                             level;
-       int                             status;
-       int                             cable_plugged:1;
-};
-
-static struct delayed_work bat_work;
-
-static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
-{
-       schedule_delayed_work(&bat_work,
-               msecs_to_jiffies(JITTER_DELAY));
-}
-
-static int gather_samples(struct s3c_adc_client *client, int num, int channel)
-{
-       int value, i;
-
-       /* default to 1 if nothing is set */
-       if (num < 1)
-               num = 1;
-
-       value = 0;
-       for (i = 0; i < num; i++)
-               value += s3c_adc_read(client, channel);
-       value /= num;
-
-       return value;
-}
-
-static enum power_supply_property s3c_adc_backup_bat_props[] = {
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-};
-
-static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct s3c_adc_bat *bat = power_supply_get_drvdata(psy);
-
-       if (!bat) {
-               dev_err(&psy->dev, "%s: no battery infos ?!\n", __func__);
-               return -EINVAL;
-       }
-
-       if (bat->volt_value < 0 ||
-               jiffies_to_msecs(jiffies - bat->timestamp) >
-                       BAT_POLL_INTERVAL) {
-               bat->volt_value = gather_samples(bat->client,
-                       bat->pdata->backup_volt_samples,
-                       bat->pdata->backup_volt_channel);
-               bat->volt_value *= bat->pdata->backup_volt_mult;
-               bat->timestamp = jiffies;
-       }
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = bat->volt_value;
-               return 0;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
-               val->intval = bat->pdata->backup_volt_min;
-               return 0;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = bat->pdata->backup_volt_max;
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-static const struct power_supply_desc backup_bat_desc = {
-       .name           = "backup-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = s3c_adc_backup_bat_props,
-       .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props),
-       .get_property   = s3c_adc_backup_bat_get_property,
-       .use_for_apm    = 1,
-};
-
-static struct s3c_adc_bat backup_bat;
-
-static enum power_supply_property s3c_adc_main_bat_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-};
-
-static int calc_full_volt(int volt_val, int cur_val, int impedance)
-{
-       return volt_val + cur_val * impedance / 1000;
-}
-
-static int charge_finished(struct s3c_adc_bat *bat)
-{
-       return bat->pdata->gpio_inverted ?
-               !gpio_get_value(bat->pdata->gpio_charge_finished) :
-               gpio_get_value(bat->pdata->gpio_charge_finished);
-}
-
-static int s3c_adc_bat_get_property(struct power_supply *psy,
-                                   enum power_supply_property psp,
-                                   union power_supply_propval *val)
-{
-       struct s3c_adc_bat *bat = power_supply_get_drvdata(psy);
-
-       int new_level;
-       int full_volt;
-       const struct s3c_adc_bat_thresh *lut;
-       unsigned int lut_size;
-
-       if (!bat) {
-               dev_err(&psy->dev, "no battery infos ?!\n");
-               return -EINVAL;
-       }
-
-       lut = bat->pdata->lut_noac;
-       lut_size = bat->pdata->lut_noac_cnt;
-
-       if (bat->volt_value < 0 || bat->cur_value < 0 ||
-               jiffies_to_msecs(jiffies - bat->timestamp) >
-                       BAT_POLL_INTERVAL) {
-               bat->volt_value = gather_samples(bat->client,
-                       bat->pdata->volt_samples,
-                       bat->pdata->volt_channel) * bat->pdata->volt_mult;
-               bat->cur_value = gather_samples(bat->client,
-                       bat->pdata->current_samples,
-                       bat->pdata->current_channel) * bat->pdata->current_mult;
-               bat->timestamp = jiffies;
-       }
-
-       if (bat->cable_plugged &&
-               ((bat->pdata->gpio_charge_finished < 0) ||
-               !charge_finished(bat))) {
-               lut = bat->pdata->lut_acin;
-               lut_size = bat->pdata->lut_acin_cnt;
-       }
-
-       new_level = 100000;
-       full_volt = calc_full_volt((bat->volt_value / 1000),
-               (bat->cur_value / 1000), bat->pdata->internal_impedance);
-
-       if (full_volt < calc_full_volt(lut->volt, lut->cur,
-               bat->pdata->internal_impedance)) {
-               lut_size--;
-               while (lut_size--) {
-                       int lut_volt1;
-                       int lut_volt2;
-
-                       lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur,
-                               bat->pdata->internal_impedance);
-                       lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur,
-                               bat->pdata->internal_impedance);
-                       if (full_volt < lut_volt1 && full_volt >= lut_volt2) {
-                               new_level = (lut[1].level +
-                                       (lut[0].level - lut[1].level) *
-                                       (full_volt - lut_volt2) /
-                                       (lut_volt1 - lut_volt2)) * 1000;
-                               break;
-                       }
-                       new_level = lut[1].level * 1000;
-                       lut++;
-               }
-       }
-
-       bat->level = new_level;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (bat->pdata->gpio_charge_finished < 0)
-                       val->intval = bat->level == 100000 ?
-                               POWER_SUPPLY_STATUS_FULL : bat->status;
-               else
-                       val->intval = bat->status;
-               return 0;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               val->intval = 100000;
-               return 0;
-       case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
-               val->intval = 0;
-               return 0;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               val->intval = bat->level;
-               return 0;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = bat->volt_value;
-               return 0;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               val->intval = bat->cur_value;
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-static const struct power_supply_desc main_bat_desc = {
-       .name                   = "main-battery",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = s3c_adc_main_bat_props,
-       .num_properties         = ARRAY_SIZE(s3c_adc_main_bat_props),
-       .get_property           = s3c_adc_bat_get_property,
-       .external_power_changed = s3c_adc_bat_ext_power_changed,
-       .use_for_apm            = 1,
-};
-
-static struct s3c_adc_bat main_bat;
-
-static void s3c_adc_bat_work(struct work_struct *work)
-{
-       struct s3c_adc_bat *bat = &main_bat;
-       int is_charged;
-       int is_plugged;
-       static int was_plugged;
-
-       is_plugged = power_supply_am_i_supplied(bat->psy);
-       bat->cable_plugged = is_plugged;
-       if (is_plugged != was_plugged) {
-               was_plugged = is_plugged;
-               if (is_plugged) {
-                       if (bat->pdata->enable_charger)
-                               bat->pdata->enable_charger();
-                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
-               } else {
-                       if (bat->pdata->disable_charger)
-                               bat->pdata->disable_charger();
-                       bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
-               }
-       } else {
-               if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) {
-                       is_charged = charge_finished(&main_bat);
-                       if (is_charged) {
-                               if (bat->pdata->disable_charger)
-                                       bat->pdata->disable_charger();
-                               bat->status = POWER_SUPPLY_STATUS_FULL;
-                       } else {
-                               if (bat->pdata->enable_charger)
-                                       bat->pdata->enable_charger();
-                               bat->status = POWER_SUPPLY_STATUS_CHARGING;
-                       }
-               }
-       }
-
-       power_supply_changed(bat->psy);
-}
-
-static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
-{
-       schedule_delayed_work(&bat_work,
-               msecs_to_jiffies(JITTER_DELAY));
-       return IRQ_HANDLED;
-}
-
-static int s3c_adc_bat_probe(struct platform_device *pdev)
-{
-       struct s3c_adc_client   *client;
-       struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
-       int ret;
-
-       client = s3c_adc_register(pdev, NULL, NULL, 0);
-       if (IS_ERR(client)) {
-               dev_err(&pdev->dev, "cannot register adc\n");
-               return PTR_ERR(client);
-       }
-
-       platform_set_drvdata(pdev, client);
-
-       main_bat.client = client;
-       main_bat.pdata = pdata;
-       main_bat.volt_value = -1;
-       main_bat.cur_value = -1;
-       main_bat.cable_plugged = 0;
-       main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING;
-
-       main_bat.psy = power_supply_register(&pdev->dev, &main_bat_desc, NULL);
-       if (IS_ERR(main_bat.psy)) {
-               ret = PTR_ERR(main_bat.psy);
-               goto err_reg_main;
-       }
-       if (pdata->backup_volt_mult) {
-               const struct power_supply_config psy_cfg
-                                               = { .drv_data = &backup_bat, };
-
-               backup_bat.client = client;
-               backup_bat.pdata = pdev->dev.platform_data;
-               backup_bat.volt_value = -1;
-               backup_bat.psy = power_supply_register(&pdev->dev,
-                                                      &backup_bat_desc,
-                                                      &psy_cfg);
-               if (IS_ERR(backup_bat.psy)) {
-                       ret = PTR_ERR(backup_bat.psy);
-                       goto err_reg_backup;
-               }
-       }
-
-       INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
-
-       if (pdata->gpio_charge_finished >= 0) {
-               ret = gpio_request(pdata->gpio_charge_finished, "charged");
-               if (ret)
-                       goto err_gpio;
-
-               ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished),
-                               s3c_adc_bat_charged,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                               "battery charged", NULL);
-               if (ret)
-                       goto err_irq;
-       }
-
-       if (pdata->init) {
-               ret = pdata->init();
-               if (ret)
-                       goto err_platform;
-       }
-
-       dev_info(&pdev->dev, "successfully loaded\n");
-       device_init_wakeup(&pdev->dev, 1);
-
-       /* Schedule timer to check current status */
-       schedule_delayed_work(&bat_work,
-               msecs_to_jiffies(JITTER_DELAY));
-
-       return 0;
-
-err_platform:
-       if (pdata->gpio_charge_finished >= 0)
-               free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
-err_irq:
-       if (pdata->gpio_charge_finished >= 0)
-               gpio_free(pdata->gpio_charge_finished);
-err_gpio:
-       if (pdata->backup_volt_mult)
-               power_supply_unregister(backup_bat.psy);
-err_reg_backup:
-       power_supply_unregister(main_bat.psy);
-err_reg_main:
-       return ret;
-}
-
-static int s3c_adc_bat_remove(struct platform_device *pdev)
-{
-       struct s3c_adc_client *client = platform_get_drvdata(pdev);
-       struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
-
-       power_supply_unregister(main_bat.psy);
-       if (pdata->backup_volt_mult)
-               power_supply_unregister(backup_bat.psy);
-
-       s3c_adc_release(client);
-
-       if (pdata->gpio_charge_finished >= 0) {
-               free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
-               gpio_free(pdata->gpio_charge_finished);
-       }
-
-       cancel_delayed_work(&bat_work);
-
-       if (pdata->exit)
-               pdata->exit();
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int s3c_adc_bat_suspend(struct platform_device *pdev,
-       pm_message_t state)
-{
-       struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
-
-       if (pdata->gpio_charge_finished >= 0) {
-               if (device_may_wakeup(&pdev->dev))
-                       enable_irq_wake(
-                               gpio_to_irq(pdata->gpio_charge_finished));
-               else {
-                       disable_irq(gpio_to_irq(pdata->gpio_charge_finished));
-                       main_bat.pdata->disable_charger();
-               }
-       }
-
-       return 0;
-}
-
-static int s3c_adc_bat_resume(struct platform_device *pdev)
-{
-       struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
-
-       if (pdata->gpio_charge_finished >= 0) {
-               if (device_may_wakeup(&pdev->dev))
-                       disable_irq_wake(
-                               gpio_to_irq(pdata->gpio_charge_finished));
-               else
-                       enable_irq(gpio_to_irq(pdata->gpio_charge_finished));
-       }
-
-       /* Schedule timer to check current status */
-       schedule_delayed_work(&bat_work,
-               msecs_to_jiffies(JITTER_DELAY));
-
-       return 0;
-}
-#else
-#define s3c_adc_bat_suspend NULL
-#define s3c_adc_bat_resume NULL
-#endif
-
-static struct platform_driver s3c_adc_bat_driver = {
-       .driver         = {
-               .name   = "s3c-adc-battery",
-       },
-       .probe          = s3c_adc_bat_probe,
-       .remove         = s3c_adc_bat_remove,
-       .suspend        = s3c_adc_bat_suspend,
-       .resume         = s3c_adc_bat_resume,
-};
-
-module_platform_driver(s3c_adc_bat_driver);
-
-MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
-MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controller driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c
deleted file mode 100644 (file)
index 768b9fc..0000000
+++ /dev/null
@@ -1,998 +0,0 @@
-/*
- * Gas Gauge driver for SBS Compliant Batteries
- *
- * Copyright (c) 2010, NVIDIA 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/power_supply.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <linux/of.h>
-#include <linux/stat.h>
-
-#include <linux/power/sbs-battery.h>
-
-enum {
-       REG_MANUFACTURER_DATA,
-       REG_TEMPERATURE,
-       REG_VOLTAGE,
-       REG_CURRENT,
-       REG_CAPACITY,
-       REG_TIME_TO_EMPTY,
-       REG_TIME_TO_FULL,
-       REG_STATUS,
-       REG_CYCLE_COUNT,
-       REG_SERIAL_NUMBER,
-       REG_REMAINING_CAPACITY,
-       REG_REMAINING_CAPACITY_CHARGE,
-       REG_FULL_CHARGE_CAPACITY,
-       REG_FULL_CHARGE_CAPACITY_CHARGE,
-       REG_DESIGN_CAPACITY,
-       REG_DESIGN_CAPACITY_CHARGE,
-       REG_DESIGN_VOLTAGE_MIN,
-       REG_DESIGN_VOLTAGE_MAX,
-       REG_MANUFACTURER,
-       REG_MODEL_NAME,
-};
-
-/* Battery Mode defines */
-#define BATTERY_MODE_OFFSET            0x03
-#define BATTERY_MODE_MASK              0x8000
-enum sbs_battery_mode {
-       BATTERY_MODE_AMPS,
-       BATTERY_MODE_WATTS
-};
-
-/* manufacturer access defines */
-#define MANUFACTURER_ACCESS_STATUS     0x0006
-#define MANUFACTURER_ACCESS_SLEEP      0x0011
-
-/* battery status value bits */
-#define BATTERY_DISCHARGING            0x40
-#define BATTERY_FULL_CHARGED           0x20
-#define BATTERY_FULL_DISCHARGED                0x10
-
-/* min_value and max_value are only valid for numerical data */
-#define SBS_DATA(_psp, _addr, _min_value, _max_value) { \
-       .psp = _psp, \
-       .addr = _addr, \
-       .min_value = _min_value, \
-       .max_value = _max_value, \
-}
-
-static const struct chip_data {
-       enum power_supply_property psp;
-       u8 addr;
-       int min_value;
-       int max_value;
-} sbs_data[] = {
-       [REG_MANUFACTURER_DATA] =
-               SBS_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
-       [REG_TEMPERATURE] =
-               SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
-       [REG_VOLTAGE] =
-               SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
-       [REG_CURRENT] =
-               SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
-       [REG_CAPACITY] =
-               SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0D, 0, 100),
-       [REG_REMAINING_CAPACITY] =
-               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
-       [REG_REMAINING_CAPACITY_CHARGE] =
-               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535),
-       [REG_FULL_CHARGE_CAPACITY] =
-               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
-       [REG_FULL_CHARGE_CAPACITY_CHARGE] =
-               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535),
-       [REG_TIME_TO_EMPTY] =
-               SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535),
-       [REG_TIME_TO_FULL] =
-               SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535),
-       [REG_STATUS] =
-               SBS_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
-       [REG_CYCLE_COUNT] =
-               SBS_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535),
-       [REG_DESIGN_CAPACITY] =
-               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535),
-       [REG_DESIGN_CAPACITY_CHARGE] =
-               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, 65535),
-       [REG_DESIGN_VOLTAGE_MIN] =
-               SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 0x19, 0, 65535),
-       [REG_DESIGN_VOLTAGE_MAX] =
-               SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535),
-       [REG_SERIAL_NUMBER] =
-               SBS_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
-       /* Properties of type `const char *' */
-       [REG_MANUFACTURER] =
-               SBS_DATA(POWER_SUPPLY_PROP_MANUFACTURER, 0x20, 0, 65535),
-       [REG_MODEL_NAME] =
-               SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535)
-};
-
-static enum power_supply_property sbs_properties[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
-       POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
-       POWER_SUPPLY_PROP_SERIAL_NUMBER,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_ENERGY_NOW,
-       POWER_SUPPLY_PROP_ENERGY_FULL,
-       POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       /* Properties of type `const char *' */
-       POWER_SUPPLY_PROP_MANUFACTURER,
-       POWER_SUPPLY_PROP_MODEL_NAME
-};
-
-struct sbs_info {
-       struct i2c_client               *client;
-       struct power_supply             *power_supply;
-       struct sbs_platform_data        *pdata;
-       bool                            is_present;
-       bool                            gpio_detect;
-       bool                            enable_detection;
-       int                             irq;
-       int                             last_state;
-       int                             poll_time;
-       struct delayed_work             work;
-       int                             ignore_changes;
-};
-
-static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
-static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
-static bool force_load;
-
-static int sbs_read_word_data(struct i2c_client *client, u8 address)
-{
-       struct sbs_info *chip = i2c_get_clientdata(client);
-       s32 ret = 0;
-       int retries = 1;
-
-       if (chip->pdata)
-               retries = max(chip->pdata->i2c_retry_count + 1, 1);
-
-       while (retries > 0) {
-               ret = i2c_smbus_read_word_data(client, address);
-               if (ret >= 0)
-                       break;
-               retries--;
-       }
-
-       if (ret < 0) {
-               dev_dbg(&client->dev,
-                       "%s: i2c read at address 0x%x failed\n",
-                       __func__, address);
-               return ret;
-       }
-
-       return le16_to_cpu(ret);
-}
-
-static int sbs_read_string_data(struct i2c_client *client, u8 address,
-                               char *values)
-{
-       struct sbs_info *chip = i2c_get_clientdata(client);
-       s32 ret = 0, block_length = 0;
-       int retries_length = 1, retries_block = 1;
-       u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
-
-       if (chip->pdata) {
-               retries_length = max(chip->pdata->i2c_retry_count + 1, 1);
-               retries_block = max(chip->pdata->i2c_retry_count + 1, 1);
-       }
-
-       /* Adapter needs to support these two functions */
-       if (!i2c_check_functionality(client->adapter,
-                                    I2C_FUNC_SMBUS_BYTE_DATA |
-                                    I2C_FUNC_SMBUS_I2C_BLOCK)){
-               return -ENODEV;
-       }
-
-       /* Get the length of block data */
-       while (retries_length > 0) {
-               ret = i2c_smbus_read_byte_data(client, address);
-               if (ret >= 0)
-                       break;
-               retries_length--;
-       }
-
-       if (ret < 0) {
-               dev_dbg(&client->dev,
-                       "%s: i2c read at address 0x%x failed\n",
-                       __func__, address);
-               return ret;
-       }
-
-       /* block_length does not include NULL terminator */
-       block_length = ret;
-       if (block_length > I2C_SMBUS_BLOCK_MAX) {
-               dev_err(&client->dev,
-                       "%s: Returned block_length is longer than 0x%x\n",
-                       __func__, I2C_SMBUS_BLOCK_MAX);
-               return -EINVAL;
-       }
-
-       /* Get the block data */
-       while (retries_block > 0) {
-               ret = i2c_smbus_read_i2c_block_data(
-                               client, address,
-                               block_length + 1, block_buffer);
-               if (ret >= 0)
-                       break;
-               retries_block--;
-       }
-
-       if (ret < 0) {
-               dev_dbg(&client->dev,
-                       "%s: i2c read at address 0x%x failed\n",
-                       __func__, address);
-               return ret;
-       }
-
-       /* block_buffer[0] == block_length */
-       memcpy(values, block_buffer + 1, block_length);
-       values[block_length] = '\0';
-
-       return le16_to_cpu(ret);
-}
-
-static int sbs_write_word_data(struct i2c_client *client, u8 address,
-       u16 value)
-{
-       struct sbs_info *chip = i2c_get_clientdata(client);
-       s32 ret = 0;
-       int retries = 1;
-
-       if (chip->pdata)
-               retries = max(chip->pdata->i2c_retry_count + 1, 1);
-
-       while (retries > 0) {
-               ret = i2c_smbus_write_word_data(client, address,
-                       le16_to_cpu(value));
-               if (ret >= 0)
-                       break;
-               retries--;
-       }
-
-       if (ret < 0) {
-               dev_dbg(&client->dev,
-                       "%s: i2c write to address 0x%x failed\n",
-                       __func__, address);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int sbs_get_battery_presence_and_health(
-       struct i2c_client *client, enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       s32 ret;
-       struct sbs_info *chip = i2c_get_clientdata(client);
-
-       if (psp == POWER_SUPPLY_PROP_PRESENT &&
-               chip->gpio_detect) {
-               ret = gpio_get_value(chip->pdata->battery_detect);
-               if (ret == chip->pdata->battery_detect_present)
-                       val->intval = 1;
-               else
-                       val->intval = 0;
-               chip->is_present = val->intval;
-               return ret;
-       }
-
-       /* Write to ManufacturerAccess with
-        * ManufacturerAccess command and then
-        * read the status */
-       ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
-                                       MANUFACTURER_ACCESS_STATUS);
-       if (ret < 0) {
-               if (psp == POWER_SUPPLY_PROP_PRESENT)
-                       val->intval = 0; /* battery removed */
-               return ret;
-       }
-
-       ret = sbs_read_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr);
-       if (ret < 0)
-               return ret;
-
-       if (ret < sbs_data[REG_MANUFACTURER_DATA].min_value ||
-           ret > sbs_data[REG_MANUFACTURER_DATA].max_value) {
-               val->intval = 0;
-               return 0;
-       }
-
-       /* Mask the upper nibble of 2nd byte and
-        * lower byte of response then
-        * shift the result by 8 to get status*/
-       ret &= 0x0F00;
-       ret >>= 8;
-       if (psp == POWER_SUPPLY_PROP_PRESENT) {
-               if (ret == 0x0F)
-                       /* battery removed */
-                       val->intval = 0;
-               else
-                       val->intval = 1;
-       } else if (psp == POWER_SUPPLY_PROP_HEALTH) {
-               if (ret == 0x09)
-                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               else if (ret == 0x0B)
-                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else if (ret == 0x0C)
-                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
-               else
-                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
-       }
-
-       return 0;
-}
-
-static int sbs_get_battery_property(struct i2c_client *client,
-       int reg_offset, enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       struct sbs_info *chip = i2c_get_clientdata(client);
-       s32 ret;
-
-       ret = sbs_read_word_data(client, sbs_data[reg_offset].addr);
-       if (ret < 0)
-               return ret;
-
-       /* returned values are 16 bit */
-       if (sbs_data[reg_offset].min_value < 0)
-               ret = (s16)ret;
-
-       if (ret >= sbs_data[reg_offset].min_value &&
-           ret <= sbs_data[reg_offset].max_value) {
-               val->intval = ret;
-               if (psp != POWER_SUPPLY_PROP_STATUS)
-                       return 0;
-
-               if (ret & BATTERY_FULL_CHARGED)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else if (ret & BATTERY_DISCHARGING)
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-
-               if (chip->poll_time == 0)
-                       chip->last_state = val->intval;
-               else if (chip->last_state != val->intval) {
-                       cancel_delayed_work_sync(&chip->work);
-                       power_supply_changed(chip->power_supply);
-                       chip->poll_time = 0;
-               }
-       } else {
-               if (psp == POWER_SUPPLY_PROP_STATUS)
-                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-               else
-                       val->intval = 0;
-       }
-
-       return 0;
-}
-
-static int sbs_get_battery_string_property(struct i2c_client *client,
-       int reg_offset, enum power_supply_property psp, char *val)
-{
-       s32 ret;
-
-       ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val);
-
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static void  sbs_unit_adjustment(struct i2c_client *client,
-       enum power_supply_property psp, union power_supply_propval *val)
-{
-#define BASE_UNIT_CONVERSION           1000
-#define BATTERY_MODE_CAP_MULT_WATT     (10 * BASE_UNIT_CONVERSION)
-#define TIME_UNIT_CONVERSION           60
-#define TEMP_KELVIN_TO_CELSIUS         2731
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ENERGY_NOW:
-       case POWER_SUPPLY_PROP_ENERGY_FULL:
-       case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
-               /* sbs provides energy in units of 10mWh.
-                * Convert to ÂµWh
-                */
-               val->intval *= BATTERY_MODE_CAP_MULT_WATT;
-               break;
-
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               val->intval *= BASE_UNIT_CONVERSION;
-               break;
-
-       case POWER_SUPPLY_PROP_TEMP:
-               /* sbs provides battery temperature in 0.1K
-                * so convert it to 0.1°C
-                */
-               val->intval -= TEMP_KELVIN_TO_CELSIUS;
-               break;
-
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
-       case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
-               /* sbs provides time to empty and time to full in minutes.
-                * Convert to seconds
-                */
-               val->intval *= TIME_UNIT_CONVERSION;
-               break;
-
-       default:
-               dev_dbg(&client->dev,
-                       "%s: no need for unit conversion %d\n", __func__, psp);
-       }
-}
-
-static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client,
-       enum sbs_battery_mode mode)
-{
-       int ret, original_val;
-
-       original_val = sbs_read_word_data(client, BATTERY_MODE_OFFSET);
-       if (original_val < 0)
-               return original_val;
-
-       if ((original_val & BATTERY_MODE_MASK) == mode)
-               return mode;
-
-       if (mode == BATTERY_MODE_AMPS)
-               ret = original_val & ~BATTERY_MODE_MASK;
-       else
-               ret = original_val | BATTERY_MODE_MASK;
-
-       ret = sbs_write_word_data(client, BATTERY_MODE_OFFSET, ret);
-       if (ret < 0)
-               return ret;
-
-       return original_val & BATTERY_MODE_MASK;
-}
-
-static int sbs_get_battery_capacity(struct i2c_client *client,
-       int reg_offset, enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       s32 ret;
-       enum sbs_battery_mode mode = BATTERY_MODE_WATTS;
-
-       if (power_supply_is_amp_property(psp))
-               mode = BATTERY_MODE_AMPS;
-
-       mode = sbs_set_battery_mode(client, mode);
-       if (mode < 0)
-               return mode;
-
-       ret = sbs_read_word_data(client, sbs_data[reg_offset].addr);
-       if (ret < 0)
-               return ret;
-
-       if (psp == POWER_SUPPLY_PROP_CAPACITY) {
-               /* sbs spec says that this can be >100 %
-               * even if max value is 100 % */
-               val->intval = min(ret, 100);
-       } else
-               val->intval = ret;
-
-       ret = sbs_set_battery_mode(client, mode);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static char sbs_serial[5];
-static int sbs_get_battery_serial_number(struct i2c_client *client,
-       union power_supply_propval *val)
-{
-       int ret;
-
-       ret = sbs_read_word_data(client, sbs_data[REG_SERIAL_NUMBER].addr);
-       if (ret < 0)
-               return ret;
-
-       ret = sprintf(sbs_serial, "%04x", ret);
-       val->strval = sbs_serial;
-
-       return 0;
-}
-
-static int sbs_get_property_index(struct i2c_client *client,
-       enum power_supply_property psp)
-{
-       int count;
-       for (count = 0; count < ARRAY_SIZE(sbs_data); count++)
-               if (psp == sbs_data[count].psp)
-                       return count;
-
-       dev_warn(&client->dev,
-               "%s: Invalid Property - %d\n", __func__, psp);
-
-       return -EINVAL;
-}
-
-static int sbs_get_property(struct power_supply *psy,
-       enum power_supply_property psp,
-       union power_supply_propval *val)
-{
-       int ret = 0;
-       struct sbs_info *chip = power_supply_get_drvdata(psy);
-       struct i2c_client *client = chip->client;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_PRESENT:
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = sbs_get_battery_presence_and_health(client, psp, val);
-               if (psp == POWER_SUPPLY_PROP_PRESENT)
-                       return 0;
-               break;
-
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               goto done; /* don't trigger power_supply_changed()! */
-
-       case POWER_SUPPLY_PROP_ENERGY_NOW:
-       case POWER_SUPPLY_PROP_ENERGY_FULL:
-       case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = sbs_get_property_index(client, psp);
-               if (ret < 0)
-                       break;
-
-               ret = sbs_get_battery_capacity(client, ret, psp, val);
-               break;
-
-       case POWER_SUPPLY_PROP_SERIAL_NUMBER:
-               ret = sbs_get_battery_serial_number(client, val);
-               break;
-
-       case POWER_SUPPLY_PROP_STATUS:
-       case POWER_SUPPLY_PROP_CYCLE_COUNT:
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-       case POWER_SUPPLY_PROP_TEMP:
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
-       case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               ret = sbs_get_property_index(client, psp);
-               if (ret < 0)
-                       break;
-
-               ret = sbs_get_battery_property(client, ret, psp, val);
-               break;
-
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               ret = sbs_get_property_index(client, psp);
-               if (ret < 0)
-                       break;
-
-               ret = sbs_get_battery_string_property(client, ret, psp,
-                                                     model_name);
-               val->strval = model_name;
-               break;
-
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               ret = sbs_get_property_index(client, psp);
-               if (ret < 0)
-                       break;
-
-               ret = sbs_get_battery_string_property(client, ret, psp,
-                                                     manufacturer);
-               val->strval = manufacturer;
-               break;
-
-       default:
-               dev_err(&client->dev,
-                       "%s: INVALID property\n", __func__);
-               return -EINVAL;
-       }
-
-       if (!chip->enable_detection)
-               goto done;
-
-       if (!chip->gpio_detect &&
-               chip->is_present != (ret >= 0)) {
-               chip->is_present = (ret >= 0);
-               power_supply_changed(chip->power_supply);
-       }
-
-done:
-       if (!ret) {
-               /* Convert units to match requirements for power supply class */
-               sbs_unit_adjustment(client, psp, val);
-       }
-
-       dev_dbg(&client->dev,
-               "%s: property = %d, value = %x\n", __func__, psp, val->intval);
-
-       if (ret && chip->is_present)
-               return ret;
-
-       /* battery not present, so return NODATA for properties */
-       if (ret)
-               return -ENODATA;
-
-       return 0;
-}
-
-static irqreturn_t sbs_irq(int irq, void *devid)
-{
-       struct power_supply *battery = devid;
-
-       power_supply_changed(battery);
-
-       return IRQ_HANDLED;
-}
-
-static void sbs_external_power_changed(struct power_supply *psy)
-{
-       struct sbs_info *chip = power_supply_get_drvdata(psy);
-
-       if (chip->ignore_changes > 0) {
-               chip->ignore_changes--;
-               return;
-       }
-
-       /* cancel outstanding work */
-       cancel_delayed_work_sync(&chip->work);
-
-       schedule_delayed_work(&chip->work, HZ);
-       chip->poll_time = chip->pdata->poll_retry_count;
-}
-
-static void sbs_delayed_work(struct work_struct *work)
-{
-       struct sbs_info *chip;
-       s32 ret;
-
-       chip = container_of(work, struct sbs_info, work.work);
-
-       ret = sbs_read_word_data(chip->client, sbs_data[REG_STATUS].addr);
-       /* if the read failed, give up on this work */
-       if (ret < 0) {
-               chip->poll_time = 0;
-               return;
-       }
-
-       if (ret & BATTERY_FULL_CHARGED)
-               ret = POWER_SUPPLY_STATUS_FULL;
-       else if (ret & BATTERY_DISCHARGING)
-               ret = POWER_SUPPLY_STATUS_DISCHARGING;
-       else
-               ret = POWER_SUPPLY_STATUS_CHARGING;
-
-       if (chip->last_state != ret) {
-               chip->poll_time = 0;
-               power_supply_changed(chip->power_supply);
-               return;
-       }
-       if (chip->poll_time > 0) {
-               schedule_delayed_work(&chip->work, HZ);
-               chip->poll_time--;
-               return;
-       }
-}
-
-#if defined(CONFIG_OF)
-
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-
-static const struct of_device_id sbs_dt_ids[] = {
-       { .compatible = "sbs,sbs-battery" },
-       { .compatible = "ti,bq20z75" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, sbs_dt_ids);
-
-static struct sbs_platform_data *sbs_of_populate_pdata(
-               struct i2c_client *client)
-{
-       struct device_node *of_node = client->dev.of_node;
-       struct sbs_platform_data *pdata = client->dev.platform_data;
-       enum of_gpio_flags gpio_flags;
-       int rc;
-       u32 prop;
-
-       /* verify this driver matches this device */
-       if (!of_node)
-               return NULL;
-
-       /* if platform data is set, honor it */
-       if (pdata)
-               return pdata;
-
-       /* first make sure at least one property is set, otherwise
-        * it won't change behavior from running without pdata.
-        */
-       if (!of_get_property(of_node, "sbs,i2c-retry-count", NULL) &&
-               !of_get_property(of_node, "sbs,poll-retry-count", NULL) &&
-               !of_get_property(of_node, "sbs,battery-detect-gpios", NULL))
-               goto of_out;
-
-       pdata = devm_kzalloc(&client->dev, sizeof(struct sbs_platform_data),
-                               GFP_KERNEL);
-       if (!pdata)
-               goto of_out;
-
-       rc = of_property_read_u32(of_node, "sbs,i2c-retry-count", &prop);
-       if (!rc)
-               pdata->i2c_retry_count = prop;
-
-       rc = of_property_read_u32(of_node, "sbs,poll-retry-count", &prop);
-       if (!rc)
-               pdata->poll_retry_count = prop;
-
-       if (!of_get_property(of_node, "sbs,battery-detect-gpios", NULL)) {
-               pdata->battery_detect = -1;
-               goto of_out;
-       }
-
-       pdata->battery_detect = of_get_named_gpio_flags(of_node,
-                       "sbs,battery-detect-gpios", 0, &gpio_flags);
-
-       if (gpio_flags & OF_GPIO_ACTIVE_LOW)
-               pdata->battery_detect_present = 0;
-       else
-               pdata->battery_detect_present = 1;
-
-of_out:
-       return pdata;
-}
-#else
-static struct sbs_platform_data *sbs_of_populate_pdata(
-       struct i2c_client *client)
-{
-       return client->dev.platform_data;
-}
-#endif
-
-static const struct power_supply_desc sbs_default_desc = {
-       .type = POWER_SUPPLY_TYPE_BATTERY,
-       .properties = sbs_properties,
-       .num_properties = ARRAY_SIZE(sbs_properties),
-       .get_property = sbs_get_property,
-       .external_power_changed = sbs_external_power_changed,
-};
-
-static int sbs_probe(struct i2c_client *client,
-       const struct i2c_device_id *id)
-{
-       struct sbs_info *chip;
-       struct power_supply_desc *sbs_desc;
-       struct sbs_platform_data *pdata = client->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       int rc;
-       int irq;
-
-       sbs_desc = devm_kmemdup(&client->dev, &sbs_default_desc,
-                       sizeof(*sbs_desc), GFP_KERNEL);
-       if (!sbs_desc)
-               return -ENOMEM;
-
-       sbs_desc->name = devm_kasprintf(&client->dev, GFP_KERNEL, "sbs-%s",
-                       dev_name(&client->dev));
-       if (!sbs_desc->name)
-               return -ENOMEM;
-
-       chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL);
-       if (!chip)
-               return -ENOMEM;
-
-       chip->client = client;
-       chip->enable_detection = false;
-       chip->gpio_detect = false;
-       psy_cfg.of_node = client->dev.of_node;
-       psy_cfg.drv_data = chip;
-       /* ignore first notification of external change, it is generated
-        * from the power_supply_register call back
-        */
-       chip->ignore_changes = 1;
-       chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
-
-       pdata = sbs_of_populate_pdata(client);
-
-       if (pdata) {
-               chip->gpio_detect = gpio_is_valid(pdata->battery_detect);
-               chip->pdata = pdata;
-       }
-
-       i2c_set_clientdata(client, chip);
-
-       if (!chip->gpio_detect)
-               goto skip_gpio;
-
-       rc = gpio_request(pdata->battery_detect, dev_name(&client->dev));
-       if (rc) {
-               dev_warn(&client->dev, "Failed to request gpio: %d\n", rc);
-               chip->gpio_detect = false;
-               goto skip_gpio;
-       }
-
-       rc = gpio_direction_input(pdata->battery_detect);
-       if (rc) {
-               dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc);
-               gpio_free(pdata->battery_detect);
-               chip->gpio_detect = false;
-               goto skip_gpio;
-       }
-
-       irq = gpio_to_irq(pdata->battery_detect);
-       if (irq <= 0) {
-               dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq);
-               gpio_free(pdata->battery_detect);
-               chip->gpio_detect = false;
-               goto skip_gpio;
-       }
-
-       rc = request_irq(irq, sbs_irq,
-               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-               dev_name(&client->dev), chip->power_supply);
-       if (rc) {
-               dev_warn(&client->dev, "Failed to request irq: %d\n", rc);
-               gpio_free(pdata->battery_detect);
-               chip->gpio_detect = false;
-               goto skip_gpio;
-       }
-
-       chip->irq = irq;
-
-skip_gpio:
-       /*
-        * Before we register, we might need to make sure we can actually talk
-        * to the battery.
-        */
-       if (!force_load) {
-               rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr);
-
-               if (rc < 0) {
-                       dev_err(&client->dev, "%s: Failed to get device status\n",
-                               __func__);
-                       goto exit_psupply;
-               }
-       }
-
-       chip->power_supply = power_supply_register(&client->dev, sbs_desc,
-                                                  &psy_cfg);
-       if (IS_ERR(chip->power_supply)) {
-               dev_err(&client->dev,
-                       "%s: Failed to register power supply\n", __func__);
-               rc = PTR_ERR(chip->power_supply);
-               goto exit_psupply;
-       }
-
-       dev_info(&client->dev,
-               "%s: battery gas gauge device registered\n", client->name);
-
-       INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
-
-       chip->enable_detection = true;
-
-       return 0;
-
-exit_psupply:
-       if (chip->irq)
-               free_irq(chip->irq, chip->power_supply);
-       if (chip->gpio_detect)
-               gpio_free(pdata->battery_detect);
-
-       kfree(chip);
-
-       return rc;
-}
-
-static int sbs_remove(struct i2c_client *client)
-{
-       struct sbs_info *chip = i2c_get_clientdata(client);
-
-       if (chip->irq)
-               free_irq(chip->irq, chip->power_supply);
-       if (chip->gpio_detect)
-               gpio_free(chip->pdata->battery_detect);
-
-       power_supply_unregister(chip->power_supply);
-
-       cancel_delayed_work_sync(&chip->work);
-
-       kfree(chip);
-       chip = NULL;
-
-       return 0;
-}
-
-#if defined CONFIG_PM_SLEEP
-
-static int sbs_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct sbs_info *chip = i2c_get_clientdata(client);
-       s32 ret;
-
-       if (chip->poll_time > 0)
-               cancel_delayed_work_sync(&chip->work);
-
-       /* write to manufacturer access with sleep command */
-       ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
-               MANUFACTURER_ACCESS_SLEEP);
-       if (chip->is_present && ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL);
-#define SBS_PM_OPS (&sbs_pm_ops)
-
-#else
-#define SBS_PM_OPS NULL
-#endif
-
-static const struct i2c_device_id sbs_id[] = {
-       { "bq20z75", 0 },
-       { "sbs-battery", 1 },
-       {}
-};
-MODULE_DEVICE_TABLE(i2c, sbs_id);
-
-static struct i2c_driver sbs_battery_driver = {
-       .probe          = sbs_probe,
-       .remove         = sbs_remove,
-       .id_table       = sbs_id,
-       .driver = {
-               .name   = "sbs-battery",
-               .of_match_table = of_match_ptr(sbs_dt_ids),
-               .pm     = SBS_PM_OPS,
-       },
-};
-module_i2c_driver(sbs_battery_driver);
-
-MODULE_DESCRIPTION("SBS battery monitor driver");
-MODULE_LICENSE("GPL");
-
-module_param(force_load, bool, S_IRUSR | S_IRGRP | S_IROTH);
-MODULE_PARM_DESC(force_load,
-                "Attempt to load the driver even if no battery is connected");
diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c
deleted file mode 100644 (file)
index 072c518..0000000
+++ /dev/null
@@ -1,1334 +0,0 @@
-/*
- * Summit Microelectronics SMB347 Battery Charger Driver
- *
- * Copyright (C) 2011, Intel Corporation
- *
- * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com>
- *          Mika Westerberg <mika.westerberg@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/err.h>
-#include <linux/gpio.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <linux/power_supply.h>
-#include <linux/power/smb347-charger.h>
-#include <linux/regmap.h>
-
-/*
- * Configuration registers. These are mirrored to volatile RAM and can be
- * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be
- * reloaded from non-volatile registers after POR.
- */
-#define CFG_CHARGE_CURRENT                     0x00
-#define CFG_CHARGE_CURRENT_FCC_MASK            0xe0
-#define CFG_CHARGE_CURRENT_FCC_SHIFT           5
-#define CFG_CHARGE_CURRENT_PCC_MASK            0x18
-#define CFG_CHARGE_CURRENT_PCC_SHIFT           3
-#define CFG_CHARGE_CURRENT_TC_MASK             0x07
-#define CFG_CURRENT_LIMIT                      0x01
-#define CFG_CURRENT_LIMIT_DC_MASK              0xf0
-#define CFG_CURRENT_LIMIT_DC_SHIFT             4
-#define CFG_CURRENT_LIMIT_USB_MASK             0x0f
-#define CFG_FLOAT_VOLTAGE                      0x03
-#define CFG_FLOAT_VOLTAGE_FLOAT_MASK           0x3f
-#define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK       0xc0
-#define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT      6
-#define CFG_STAT                               0x05
-#define CFG_STAT_DISABLED                      BIT(5)
-#define CFG_STAT_ACTIVE_HIGH                   BIT(7)
-#define CFG_PIN                                        0x06
-#define CFG_PIN_EN_CTRL_MASK                   0x60
-#define CFG_PIN_EN_CTRL_ACTIVE_HIGH            0x40
-#define CFG_PIN_EN_CTRL_ACTIVE_LOW             0x60
-#define CFG_PIN_EN_APSD_IRQ                    BIT(1)
-#define CFG_PIN_EN_CHARGER_ERROR               BIT(2)
-#define CFG_THERM                              0x07
-#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK   0x03
-#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT  0
-#define CFG_THERM_SOFT_COLD_COMPENSATION_MASK  0x0c
-#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
-#define CFG_THERM_MONITOR_DISABLED             BIT(4)
-#define CFG_SYSOK                              0x08
-#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED  BIT(2)
-#define CFG_OTHER                              0x09
-#define CFG_OTHER_RID_MASK                     0xc0
-#define CFG_OTHER_RID_ENABLED_AUTO_OTG         0xc0
-#define CFG_OTG                                        0x0a
-#define CFG_OTG_TEMP_THRESHOLD_MASK            0x30
-#define CFG_OTG_TEMP_THRESHOLD_SHIFT           4
-#define CFG_OTG_CC_COMPENSATION_MASK           0xc0
-#define CFG_OTG_CC_COMPENSATION_SHIFT          6
-#define CFG_TEMP_LIMIT                         0x0b
-#define CFG_TEMP_LIMIT_SOFT_HOT_MASK           0x03
-#define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT          0
-#define CFG_TEMP_LIMIT_SOFT_COLD_MASK          0x0c
-#define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT         2
-#define CFG_TEMP_LIMIT_HARD_HOT_MASK           0x30
-#define CFG_TEMP_LIMIT_HARD_HOT_SHIFT          4
-#define CFG_TEMP_LIMIT_HARD_COLD_MASK          0xc0
-#define CFG_TEMP_LIMIT_HARD_COLD_SHIFT         6
-#define CFG_FAULT_IRQ                          0x0c
-#define CFG_FAULT_IRQ_DCIN_UV                  BIT(2)
-#define CFG_STATUS_IRQ                         0x0d
-#define CFG_STATUS_IRQ_TERMINATION_OR_TAPER    BIT(4)
-#define CFG_STATUS_IRQ_CHARGE_TIMEOUT          BIT(7)
-#define CFG_ADDRESS                            0x0e
-
-/* Command registers */
-#define CMD_A                                  0x30
-#define CMD_A_CHG_ENABLED                      BIT(1)
-#define CMD_A_SUSPEND_ENABLED                  BIT(2)
-#define CMD_A_ALLOW_WRITE                      BIT(7)
-#define CMD_B                                  0x31
-#define CMD_C                                  0x33
-
-/* Interrupt Status registers */
-#define IRQSTAT_A                              0x35
-#define IRQSTAT_C                              0x37
-#define IRQSTAT_C_TERMINATION_STAT             BIT(0)
-#define IRQSTAT_C_TERMINATION_IRQ              BIT(1)
-#define IRQSTAT_C_TAPER_IRQ                    BIT(3)
-#define IRQSTAT_D                              0x38
-#define IRQSTAT_D_CHARGE_TIMEOUT_STAT          BIT(2)
-#define IRQSTAT_D_CHARGE_TIMEOUT_IRQ           BIT(3)
-#define IRQSTAT_E                              0x39
-#define IRQSTAT_E_USBIN_UV_STAT                        BIT(0)
-#define IRQSTAT_E_USBIN_UV_IRQ                 BIT(1)
-#define IRQSTAT_E_DCIN_UV_STAT                 BIT(4)
-#define IRQSTAT_E_DCIN_UV_IRQ                  BIT(5)
-#define IRQSTAT_F                              0x3a
-
-/* Status registers */
-#define STAT_A                                 0x3b
-#define STAT_A_FLOAT_VOLTAGE_MASK              0x3f
-#define STAT_B                                 0x3c
-#define STAT_C                                 0x3d
-#define STAT_C_CHG_ENABLED                     BIT(0)
-#define STAT_C_HOLDOFF_STAT                    BIT(3)
-#define STAT_C_CHG_MASK                                0x06
-#define STAT_C_CHG_SHIFT                       1
-#define STAT_C_CHG_TERM                                BIT(5)
-#define STAT_C_CHARGER_ERROR                   BIT(6)
-#define STAT_E                                 0x3f
-
-#define SMB347_MAX_REGISTER                    0x3f
-
-/**
- * struct smb347_charger - smb347 charger instance
- * @lock: protects concurrent access to online variables
- * @dev: pointer to device
- * @regmap: pointer to driver regmap
- * @mains: power_supply instance for AC/DC power
- * @usb: power_supply instance for USB power
- * @battery: power_supply instance for battery
- * @mains_online: is AC/DC input connected
- * @usb_online: is USB input connected
- * @charging_enabled: is charging enabled
- * @pdata: pointer to platform data
- */
-struct smb347_charger {
-       struct mutex            lock;
-       struct device           *dev;
-       struct regmap           *regmap;
-       struct power_supply     *mains;
-       struct power_supply     *usb;
-       struct power_supply     *battery;
-       bool                    mains_online;
-       bool                    usb_online;
-       bool                    charging_enabled;
-       const struct smb347_charger_platform_data *pdata;
-};
-
-/* Fast charge current in uA */
-static const unsigned int fcc_tbl[] = {
-       700000,
-       900000,
-       1200000,
-       1500000,
-       1800000,
-       2000000,
-       2200000,
-       2500000,
-};
-
-/* Pre-charge current in uA */
-static const unsigned int pcc_tbl[] = {
-       100000,
-       150000,
-       200000,
-       250000,
-};
-
-/* Termination current in uA */
-static const unsigned int tc_tbl[] = {
-       37500,
-       50000,
-       100000,
-       150000,
-       200000,
-       250000,
-       500000,
-       600000,
-};
-
-/* Input current limit in uA */
-static const unsigned int icl_tbl[] = {
-       300000,
-       500000,
-       700000,
-       900000,
-       1200000,
-       1500000,
-       1800000,
-       2000000,
-       2200000,
-       2500000,
-};
-
-/* Charge current compensation in uA */
-static const unsigned int ccc_tbl[] = {
-       250000,
-       700000,
-       900000,
-       1200000,
-};
-
-/* Convert register value to current using lookup table */
-static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
-{
-       if (val >= size)
-               return -EINVAL;
-       return tbl[val];
-}
-
-/* Convert current to register value using lookup table */
-static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
-{
-       size_t i;
-
-       for (i = 0; i < size; i++)
-               if (val < tbl[i])
-                       break;
-       return i > 0 ? i - 1 : -EINVAL;
-}
-
-/**
- * smb347_update_ps_status - refreshes the power source status
- * @smb: pointer to smb347 charger instance
- *
- * Function checks whether any power source is connected to the charger and
- * updates internal state accordingly. If there is a change to previous state
- * function returns %1, otherwise %0 and negative errno in case of errror.
- */
-static int smb347_update_ps_status(struct smb347_charger *smb)
-{
-       bool usb = false;
-       bool dc = false;
-       unsigned int val;
-       int ret;
-
-       ret = regmap_read(smb->regmap, IRQSTAT_E, &val);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * Dc and usb are set depending on whether they are enabled in
-        * platform data _and_ whether corresponding undervoltage is set.
-        */
-       if (smb->pdata->use_mains)
-               dc = !(val & IRQSTAT_E_DCIN_UV_STAT);
-       if (smb->pdata->use_usb)
-               usb = !(val & IRQSTAT_E_USBIN_UV_STAT);
-
-       mutex_lock(&smb->lock);
-       ret = smb->mains_online != dc || smb->usb_online != usb;
-       smb->mains_online = dc;
-       smb->usb_online = usb;
-       mutex_unlock(&smb->lock);
-
-       return ret;
-}
-
-/*
- * smb347_is_ps_online - returns whether input power source is connected
- * @smb: pointer to smb347 charger instance
- *
- * Returns %true if input power source is connected. Note that this is
- * dependent on what platform has configured for usable power sources. For
- * example if USB is disabled, this will return %false even if the USB cable
- * is connected.
- */
-static bool smb347_is_ps_online(struct smb347_charger *smb)
-{
-       bool ret;
-
-       mutex_lock(&smb->lock);
-       ret = smb->usb_online || smb->mains_online;
-       mutex_unlock(&smb->lock);
-
-       return ret;
-}
-
-/**
- * smb347_charging_status - returns status of charging
- * @smb: pointer to smb347 charger instance
- *
- * Function returns charging status. %0 means no charging is in progress,
- * %1 means pre-charging, %2 fast-charging and %3 taper-charging.
- */
-static int smb347_charging_status(struct smb347_charger *smb)
-{
-       unsigned int val;
-       int ret;
-
-       if (!smb347_is_ps_online(smb))
-               return 0;
-
-       ret = regmap_read(smb->regmap, STAT_C, &val);
-       if (ret < 0)
-               return 0;
-
-       return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
-}
-
-static int smb347_charging_set(struct smb347_charger *smb, bool enable)
-{
-       int ret = 0;
-
-       if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) {
-               dev_dbg(smb->dev, "charging enable/disable in SW disabled\n");
-               return 0;
-       }
-
-       mutex_lock(&smb->lock);
-       if (smb->charging_enabled != enable) {
-               ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
-                                        enable ? CMD_A_CHG_ENABLED : 0);
-               if (!ret)
-                       smb->charging_enabled = enable;
-       }
-       mutex_unlock(&smb->lock);
-       return ret;
-}
-
-static inline int smb347_charging_enable(struct smb347_charger *smb)
-{
-       return smb347_charging_set(smb, true);
-}
-
-static inline int smb347_charging_disable(struct smb347_charger *smb)
-{
-       return smb347_charging_set(smb, false);
-}
-
-static int smb347_start_stop_charging(struct smb347_charger *smb)
-{
-       int ret;
-
-       /*
-        * Depending on whether valid power source is connected or not, we
-        * disable or enable the charging. We do it manually because it
-        * depends on how the platform has configured the valid inputs.
-        */
-       if (smb347_is_ps_online(smb)) {
-               ret = smb347_charging_enable(smb);
-               if (ret < 0)
-                       dev_err(smb->dev, "failed to enable charging\n");
-       } else {
-               ret = smb347_charging_disable(smb);
-               if (ret < 0)
-                       dev_err(smb->dev, "failed to disable charging\n");
-       }
-
-       return ret;
-}
-
-static int smb347_set_charge_current(struct smb347_charger *smb)
-{
-       int ret;
-
-       if (smb->pdata->max_charge_current) {
-               ret = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl),
-                                   smb->pdata->max_charge_current);
-               if (ret < 0)
-                       return ret;
-
-               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
-                                        CFG_CHARGE_CURRENT_FCC_MASK,
-                                        ret << CFG_CHARGE_CURRENT_FCC_SHIFT);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (smb->pdata->pre_charge_current) {
-               ret = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl),
-                                   smb->pdata->pre_charge_current);
-               if (ret < 0)
-                       return ret;
-
-               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
-                                        CFG_CHARGE_CURRENT_PCC_MASK,
-                                        ret << CFG_CHARGE_CURRENT_PCC_SHIFT);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (smb->pdata->termination_current) {
-               ret = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl),
-                                   smb->pdata->termination_current);
-               if (ret < 0)
-                       return ret;
-
-               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
-                                        CFG_CHARGE_CURRENT_TC_MASK, ret);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static int smb347_set_current_limits(struct smb347_charger *smb)
-{
-       int ret;
-
-       if (smb->pdata->mains_current_limit) {
-               ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
-                                   smb->pdata->mains_current_limit);
-               if (ret < 0)
-                       return ret;
-
-               ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
-                                        CFG_CURRENT_LIMIT_DC_MASK,
-                                        ret << CFG_CURRENT_LIMIT_DC_SHIFT);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (smb->pdata->usb_hc_current_limit) {
-               ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
-                                   smb->pdata->usb_hc_current_limit);
-               if (ret < 0)
-                       return ret;
-
-               ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
-                                        CFG_CURRENT_LIMIT_USB_MASK, ret);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static int smb347_set_voltage_limits(struct smb347_charger *smb)
-{
-       int ret;
-
-       if (smb->pdata->pre_to_fast_voltage) {
-               ret = smb->pdata->pre_to_fast_voltage;
-
-               /* uV */
-               ret = clamp_val(ret, 2400000, 3000000) - 2400000;
-               ret /= 200000;
-
-               ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
-                               CFG_FLOAT_VOLTAGE_THRESHOLD_MASK,
-                               ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (smb->pdata->max_charge_voltage) {
-               ret = smb->pdata->max_charge_voltage;
-
-               /* uV */
-               ret = clamp_val(ret, 3500000, 4500000) - 3500000;
-               ret /= 20000;
-
-               ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
-                                        CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static int smb347_set_temp_limits(struct smb347_charger *smb)
-{
-       bool enable_therm_monitor = false;
-       int ret = 0;
-       int val;
-
-       if (smb->pdata->chip_temp_threshold) {
-               val = smb->pdata->chip_temp_threshold;
-
-               /* degree C */
-               val = clamp_val(val, 100, 130) - 100;
-               val /= 10;
-
-               ret = regmap_update_bits(smb->regmap, CFG_OTG,
-                                        CFG_OTG_TEMP_THRESHOLD_MASK,
-                                        val << CFG_OTG_TEMP_THRESHOLD_SHIFT);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
-               val = smb->pdata->soft_cold_temp_limit;
-
-               val = clamp_val(val, 0, 15);
-               val /= 5;
-               /* this goes from higher to lower so invert the value */
-               val = ~val & 0x3;
-
-               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
-                                        CFG_TEMP_LIMIT_SOFT_COLD_MASK,
-                                        val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT);
-               if (ret < 0)
-                       return ret;
-
-               enable_therm_monitor = true;
-       }
-
-       if (smb->pdata->soft_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) {
-               val = smb->pdata->soft_hot_temp_limit;
-
-               val = clamp_val(val, 40, 55) - 40;
-               val /= 5;
-
-               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
-                                        CFG_TEMP_LIMIT_SOFT_HOT_MASK,
-                                        val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT);
-               if (ret < 0)
-                       return ret;
-
-               enable_therm_monitor = true;
-       }
-
-       if (smb->pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
-               val = smb->pdata->hard_cold_temp_limit;
-
-               val = clamp_val(val, -5, 10) + 5;
-               val /= 5;
-               /* this goes from higher to lower so invert the value */
-               val = ~val & 0x3;
-
-               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
-                                        CFG_TEMP_LIMIT_HARD_COLD_MASK,
-                                        val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT);
-               if (ret < 0)
-                       return ret;
-
-               enable_therm_monitor = true;
-       }
-
-       if (smb->pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) {
-               val = smb->pdata->hard_hot_temp_limit;
-
-               val = clamp_val(val, 50, 65) - 50;
-               val /= 5;
-
-               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
-                                        CFG_TEMP_LIMIT_HARD_HOT_MASK,
-                                        val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT);
-               if (ret < 0)
-                       return ret;
-
-               enable_therm_monitor = true;
-       }
-
-       /*
-        * If any of the temperature limits are set, we also enable the
-        * thermistor monitoring.
-        *
-        * When soft limits are hit, the device will start to compensate
-        * current and/or voltage depending on the configuration.
-        *
-        * When hard limit is hit, the device will suspend charging
-        * depending on the configuration.
-        */
-       if (enable_therm_monitor) {
-               ret = regmap_update_bits(smb->regmap, CFG_THERM,
-                                        CFG_THERM_MONITOR_DISABLED, 0);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (smb->pdata->suspend_on_hard_temp_limit) {
-               ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
-                                CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (smb->pdata->soft_temp_limit_compensation !=
-           SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) {
-               val = smb->pdata->soft_temp_limit_compensation & 0x3;
-
-               ret = regmap_update_bits(smb->regmap, CFG_THERM,
-                                CFG_THERM_SOFT_HOT_COMPENSATION_MASK,
-                                val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT);
-               if (ret < 0)
-                       return ret;
-
-               ret = regmap_update_bits(smb->regmap, CFG_THERM,
-                                CFG_THERM_SOFT_COLD_COMPENSATION_MASK,
-                                val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (smb->pdata->charge_current_compensation) {
-               val = current_to_hw(ccc_tbl, ARRAY_SIZE(ccc_tbl),
-                                   smb->pdata->charge_current_compensation);
-               if (val < 0)
-                       return val;
-
-               ret = regmap_update_bits(smb->regmap, CFG_OTG,
-                               CFG_OTG_CC_COMPENSATION_MASK,
-                               (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return ret;
-}
-
-/*
- * smb347_set_writable - enables/disables writing to non-volatile registers
- * @smb: pointer to smb347 charger instance
- *
- * You can enable/disable writing to the non-volatile configuration
- * registers by calling this function.
- *
- * Returns %0 on success and negative errno in case of failure.
- */
-static int smb347_set_writable(struct smb347_charger *smb, bool writable)
-{
-       return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
-                                 writable ? CMD_A_ALLOW_WRITE : 0);
-}
-
-static int smb347_hw_init(struct smb347_charger *smb)
-{
-       unsigned int val;
-       int ret;
-
-       ret = smb347_set_writable(smb, true);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * Program the platform specific configuration values to the device
-        * first.
-        */
-       ret = smb347_set_charge_current(smb);
-       if (ret < 0)
-               goto fail;
-
-       ret = smb347_set_current_limits(smb);
-       if (ret < 0)
-               goto fail;
-
-       ret = smb347_set_voltage_limits(smb);
-       if (ret < 0)
-               goto fail;
-
-       ret = smb347_set_temp_limits(smb);
-       if (ret < 0)
-               goto fail;
-
-       /* If USB charging is disabled we put the USB in suspend mode */
-       if (!smb->pdata->use_usb) {
-               ret = regmap_update_bits(smb->regmap, CMD_A,
-                                        CMD_A_SUSPEND_ENABLED,
-                                        CMD_A_SUSPEND_ENABLED);
-               if (ret < 0)
-                       goto fail;
-       }
-
-       /*
-        * If configured by platform data, we enable hardware Auto-OTG
-        * support for driving VBUS. Otherwise we disable it.
-        */
-       ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK,
-               smb->pdata->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0);
-       if (ret < 0)
-               goto fail;
-
-       /*
-        * Make the charging functionality controllable by a write to the
-        * command register unless pin control is specified in the platform
-        * data.
-        */
-       switch (smb->pdata->enable_control) {
-       case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW:
-               val = CFG_PIN_EN_CTRL_ACTIVE_LOW;
-               break;
-       case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH:
-               val = CFG_PIN_EN_CTRL_ACTIVE_HIGH;
-               break;
-       default:
-               val = 0;
-               break;
-       }
-
-       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK,
-                                val);
-       if (ret < 0)
-               goto fail;
-
-       /* Disable Automatic Power Source Detection (APSD) interrupt. */
-       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0);
-       if (ret < 0)
-               goto fail;
-
-       ret = smb347_update_ps_status(smb);
-       if (ret < 0)
-               goto fail;
-
-       ret = smb347_start_stop_charging(smb);
-
-fail:
-       smb347_set_writable(smb, false);
-       return ret;
-}
-
-static irqreturn_t smb347_interrupt(int irq, void *data)
-{
-       struct smb347_charger *smb = data;
-       unsigned int stat_c, irqstat_c, irqstat_d, irqstat_e;
-       bool handled = false;
-       int ret;
-
-       ret = regmap_read(smb->regmap, STAT_C, &stat_c);
-       if (ret < 0) {
-               dev_warn(smb->dev, "reading STAT_C failed\n");
-               return IRQ_NONE;
-       }
-
-       ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c);
-       if (ret < 0) {
-               dev_warn(smb->dev, "reading IRQSTAT_C failed\n");
-               return IRQ_NONE;
-       }
-
-       ret = regmap_read(smb->regmap, IRQSTAT_D, &irqstat_d);
-       if (ret < 0) {
-               dev_warn(smb->dev, "reading IRQSTAT_D failed\n");
-               return IRQ_NONE;
-       }
-
-       ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e);
-       if (ret < 0) {
-               dev_warn(smb->dev, "reading IRQSTAT_E failed\n");
-               return IRQ_NONE;
-       }
-
-       /*
-        * If we get charger error we report the error back to user.
-        * If the error is recovered charging will resume again.
-        */
-       if (stat_c & STAT_C_CHARGER_ERROR) {
-               dev_err(smb->dev, "charging stopped due to charger error\n");
-               power_supply_changed(smb->battery);
-               handled = true;
-       }
-
-       /*
-        * If we reached the termination current the battery is charged and
-        * we can update the status now. Charging is automatically
-        * disabled by the hardware.
-        */
-       if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) {
-               if (irqstat_c & IRQSTAT_C_TERMINATION_STAT)
-                       power_supply_changed(smb->battery);
-               dev_dbg(smb->dev, "going to HW maintenance mode\n");
-               handled = true;
-       }
-
-       /*
-        * If we got a charger timeout INT that means the charge
-        * full is not detected with in charge timeout value.
-        */
-       if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ) {
-               dev_dbg(smb->dev, "total Charge Timeout INT received\n");
-
-               if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT)
-                       dev_warn(smb->dev, "charging stopped due to timeout\n");
-               power_supply_changed(smb->battery);
-               handled = true;
-       }
-
-       /*
-        * If we got an under voltage interrupt it means that AC/USB input
-        * was connected or disconnected.
-        */
-       if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) {
-               if (smb347_update_ps_status(smb) > 0) {
-                       smb347_start_stop_charging(smb);
-                       if (smb->pdata->use_mains)
-                               power_supply_changed(smb->mains);
-                       if (smb->pdata->use_usb)
-                               power_supply_changed(smb->usb);
-               }
-               handled = true;
-       }
-
-       return handled ? IRQ_HANDLED : IRQ_NONE;
-}
-
-static int smb347_irq_set(struct smb347_charger *smb, bool enable)
-{
-       int ret;
-
-       ret = smb347_set_writable(smb, true);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * Enable/disable interrupts for:
-        *      - under voltage
-        *      - termination current reached
-        *      - charger timeout
-        *      - charger error
-        */
-       ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff,
-                                enable ? CFG_FAULT_IRQ_DCIN_UV : 0);
-       if (ret < 0)
-               goto fail;
-
-       ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff,
-                       enable ? (CFG_STATUS_IRQ_TERMINATION_OR_TAPER |
-                                       CFG_STATUS_IRQ_CHARGE_TIMEOUT) : 0);
-       if (ret < 0)
-               goto fail;
-
-       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR,
-                                enable ? CFG_PIN_EN_CHARGER_ERROR : 0);
-fail:
-       smb347_set_writable(smb, false);
-       return ret;
-}
-
-static inline int smb347_irq_enable(struct smb347_charger *smb)
-{
-       return smb347_irq_set(smb, true);
-}
-
-static inline int smb347_irq_disable(struct smb347_charger *smb)
-{
-       return smb347_irq_set(smb, false);
-}
-
-static int smb347_irq_init(struct smb347_charger *smb,
-                          struct i2c_client *client)
-{
-       const struct smb347_charger_platform_data *pdata = smb->pdata;
-       int ret, irq = gpio_to_irq(pdata->irq_gpio);
-
-       ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
-       if (ret < 0)
-               goto fail;
-
-       ret = request_threaded_irq(irq, NULL, smb347_interrupt,
-                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                  client->name, smb);
-       if (ret < 0)
-               goto fail_gpio;
-
-       ret = smb347_set_writable(smb, true);
-       if (ret < 0)
-               goto fail_irq;
-
-       /*
-        * Configure the STAT output to be suitable for interrupts: disable
-        * all other output (except interrupts) and make it active low.
-        */
-       ret = regmap_update_bits(smb->regmap, CFG_STAT,
-                                CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED,
-                                CFG_STAT_DISABLED);
-       if (ret < 0)
-               goto fail_readonly;
-
-       smb347_set_writable(smb, false);
-       client->irq = irq;
-       return 0;
-
-fail_readonly:
-       smb347_set_writable(smb, false);
-fail_irq:
-       free_irq(irq, smb);
-fail_gpio:
-       gpio_free(pdata->irq_gpio);
-fail:
-       client->irq = 0;
-       return ret;
-}
-
-/*
- * Returns the constant charge current programmed
- * into the charger in uA.
- */
-static int get_const_charge_current(struct smb347_charger *smb)
-{
-       int ret, intval;
-       unsigned int v;
-
-       if (!smb347_is_ps_online(smb))
-               return -ENODATA;
-
-       ret = regmap_read(smb->regmap, STAT_B, &v);
-       if (ret < 0)
-               return ret;
-
-       /*
-        * The current value is composition of FCC and PCC values
-        * and we can detect which table to use from bit 5.
-        */
-       if (v & 0x20) {
-               intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), v & 7);
-       } else {
-               v >>= 3;
-               intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), v & 7);
-       }
-
-       return intval;
-}
-
-/*
- * Returns the constant charge voltage programmed
- * into the charger in uV.
- */
-static int get_const_charge_voltage(struct smb347_charger *smb)
-{
-       int ret, intval;
-       unsigned int v;
-
-       if (!smb347_is_ps_online(smb))
-               return -ENODATA;
-
-       ret = regmap_read(smb->regmap, STAT_A, &v);
-       if (ret < 0)
-               return ret;
-
-       v &= STAT_A_FLOAT_VOLTAGE_MASK;
-       if (v > 0x3d)
-               v = 0x3d;
-
-       intval = 3500000 + v * 20000;
-
-       return intval;
-}
-
-static int smb347_mains_get_property(struct power_supply *psy,
-                                    enum power_supply_property prop,
-                                    union power_supply_propval *val)
-{
-       struct smb347_charger *smb = power_supply_get_drvdata(psy);
-       int ret;
-
-       switch (prop) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = smb->mains_online;
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               ret = get_const_charge_voltage(smb);
-               if (ret < 0)
-                       return ret;
-               else
-                       val->intval = ret;
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               ret = get_const_charge_current(smb);
-               if (ret < 0)
-                       return ret;
-               else
-                       val->intval = ret;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property smb347_mains_properties[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
-};
-
-static int smb347_usb_get_property(struct power_supply *psy,
-                                  enum power_supply_property prop,
-                                  union power_supply_propval *val)
-{
-       struct smb347_charger *smb = power_supply_get_drvdata(psy);
-       int ret;
-
-       switch (prop) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = smb->usb_online;
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
-               ret = get_const_charge_voltage(smb);
-               if (ret < 0)
-                       return ret;
-               else
-                       val->intval = ret;
-               break;
-
-       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
-               ret = get_const_charge_current(smb);
-               if (ret < 0)
-                       return ret;
-               else
-                       val->intval = ret;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property smb347_usb_properties[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
-       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
-};
-
-static int smb347_get_charging_status(struct smb347_charger *smb)
-{
-       int ret, status;
-       unsigned int val;
-
-       if (!smb347_is_ps_online(smb))
-               return POWER_SUPPLY_STATUS_DISCHARGING;
-
-       ret = regmap_read(smb->regmap, STAT_C, &val);
-       if (ret < 0)
-               return ret;
-
-       if ((val & STAT_C_CHARGER_ERROR) ||
-                       (val & STAT_C_HOLDOFF_STAT)) {
-               /*
-                * set to NOT CHARGING upon charger error
-                * or charging has stopped.
-                */
-               status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-       } else {
-               if ((val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT) {
-                       /*
-                        * set to charging if battery is in pre-charge,
-                        * fast charge or taper charging mode.
-                        */
-                       status = POWER_SUPPLY_STATUS_CHARGING;
-               } else if (val & STAT_C_CHG_TERM) {
-                       /*
-                        * set the status to FULL if battery is not in pre
-                        * charge, fast charge or taper charging mode AND
-                        * charging is terminated at least once.
-                        */
-                       status = POWER_SUPPLY_STATUS_FULL;
-               } else {
-                       /*
-                        * in this case no charger error or termination
-                        * occured but charging is not in progress!!!
-                        */
-                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               }
-       }
-
-       return status;
-}
-
-static int smb347_battery_get_property(struct power_supply *psy,
-                                      enum power_supply_property prop,
-                                      union power_supply_propval *val)
-{
-       struct smb347_charger *smb = power_supply_get_drvdata(psy);
-       const struct smb347_charger_platform_data *pdata = smb->pdata;
-       int ret;
-
-       ret = smb347_update_ps_status(smb);
-       if (ret < 0)
-               return ret;
-
-       switch (prop) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = smb347_get_charging_status(smb);
-               if (ret < 0)
-                       return ret;
-               val->intval = ret;
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               if (!smb347_is_ps_online(smb))
-                       return -ENODATA;
-
-               /*
-                * We handle trickle and pre-charging the same, and taper
-                * and none the same.
-                */
-               switch (smb347_charging_status(smb)) {
-               case 1:
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-                       break;
-               case 2:
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
-                       break;
-               default:
-                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
-                       break;
-               }
-               break;
-
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = pdata->battery_info.technology;
-               break;
-
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = pdata->battery_info.voltage_min_design;
-               break;
-
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = pdata->battery_info.voltage_max_design;
-               break;
-
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               val->intval = pdata->battery_info.charge_full_design;
-               break;
-
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = pdata->battery_info.name;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property smb347_battery_properties[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-};
-
-static bool smb347_volatile_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case IRQSTAT_A:
-       case IRQSTAT_C:
-       case IRQSTAT_E:
-       case IRQSTAT_F:
-       case STAT_A:
-       case STAT_B:
-       case STAT_C:
-       case STAT_E:
-               return true;
-       }
-
-       return false;
-}
-
-static bool smb347_readable_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case CFG_CHARGE_CURRENT:
-       case CFG_CURRENT_LIMIT:
-       case CFG_FLOAT_VOLTAGE:
-       case CFG_STAT:
-       case CFG_PIN:
-       case CFG_THERM:
-       case CFG_SYSOK:
-       case CFG_OTHER:
-       case CFG_OTG:
-       case CFG_TEMP_LIMIT:
-       case CFG_FAULT_IRQ:
-       case CFG_STATUS_IRQ:
-       case CFG_ADDRESS:
-       case CMD_A:
-       case CMD_B:
-       case CMD_C:
-               return true;
-       }
-
-       return smb347_volatile_reg(dev, reg);
-}
-
-static const struct regmap_config smb347_regmap = {
-       .reg_bits       = 8,
-       .val_bits       = 8,
-       .max_register   = SMB347_MAX_REGISTER,
-       .volatile_reg   = smb347_volatile_reg,
-       .readable_reg   = smb347_readable_reg,
-};
-
-static const struct power_supply_desc smb347_mains_desc = {
-       .name           = "smb347-mains",
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-       .get_property   = smb347_mains_get_property,
-       .properties     = smb347_mains_properties,
-       .num_properties = ARRAY_SIZE(smb347_mains_properties),
-};
-
-static const struct power_supply_desc smb347_usb_desc = {
-       .name           = "smb347-usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .get_property   = smb347_usb_get_property,
-       .properties     = smb347_usb_properties,
-       .num_properties = ARRAY_SIZE(smb347_usb_properties),
-};
-
-static const struct power_supply_desc smb347_battery_desc = {
-       .name           = "smb347-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property   = smb347_battery_get_property,
-       .properties     = smb347_battery_properties,
-       .num_properties = ARRAY_SIZE(smb347_battery_properties),
-};
-
-static int smb347_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
-{
-       static char *battery[] = { "smb347-battery" };
-       const struct smb347_charger_platform_data *pdata;
-       struct power_supply_config mains_usb_cfg = {}, battery_cfg = {};
-       struct device *dev = &client->dev;
-       struct smb347_charger *smb;
-       int ret;
-
-       pdata = dev->platform_data;
-       if (!pdata)
-               return -EINVAL;
-
-       if (!pdata->use_mains && !pdata->use_usb)
-               return -EINVAL;
-
-       smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL);
-       if (!smb)
-               return -ENOMEM;
-
-       i2c_set_clientdata(client, smb);
-
-       mutex_init(&smb->lock);
-       smb->dev = &client->dev;
-       smb->pdata = pdata;
-
-       smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap);
-       if (IS_ERR(smb->regmap))
-               return PTR_ERR(smb->regmap);
-
-       ret = smb347_hw_init(smb);
-       if (ret < 0)
-               return ret;
-
-       mains_usb_cfg.supplied_to = battery;
-       mains_usb_cfg.num_supplicants = ARRAY_SIZE(battery);
-       mains_usb_cfg.drv_data = smb;
-       if (smb->pdata->use_mains) {
-               smb->mains = power_supply_register(dev, &smb347_mains_desc,
-                                                  &mains_usb_cfg);
-               if (IS_ERR(smb->mains))
-                       return PTR_ERR(smb->mains);
-       }
-
-       if (smb->pdata->use_usb) {
-               smb->usb = power_supply_register(dev, &smb347_usb_desc,
-                                                &mains_usb_cfg);
-               if (IS_ERR(smb->usb)) {
-                       if (smb->pdata->use_mains)
-                               power_supply_unregister(smb->mains);
-                       return PTR_ERR(smb->usb);
-               }
-       }
-
-       battery_cfg.drv_data = smb;
-       smb->battery = power_supply_register(dev, &smb347_battery_desc,
-                                            &battery_cfg);
-       if (IS_ERR(smb->battery)) {
-               if (smb->pdata->use_usb)
-                       power_supply_unregister(smb->usb);
-               if (smb->pdata->use_mains)
-                       power_supply_unregister(smb->mains);
-               return PTR_ERR(smb->battery);
-       }
-
-       /*
-        * Interrupt pin is optional. If it is connected, we setup the
-        * interrupt support here.
-        */
-       if (pdata->irq_gpio >= 0) {
-               ret = smb347_irq_init(smb, client);
-               if (ret < 0) {
-                       dev_warn(dev, "failed to initialize IRQ: %d\n", ret);
-                       dev_warn(dev, "disabling IRQ support\n");
-               } else {
-                       smb347_irq_enable(smb);
-               }
-       }
-
-       return 0;
-}
-
-static int smb347_remove(struct i2c_client *client)
-{
-       struct smb347_charger *smb = i2c_get_clientdata(client);
-
-       if (client->irq) {
-               smb347_irq_disable(smb);
-               free_irq(client->irq, smb);
-               gpio_free(smb->pdata->irq_gpio);
-       }
-
-       power_supply_unregister(smb->battery);
-       if (smb->pdata->use_usb)
-               power_supply_unregister(smb->usb);
-       if (smb->pdata->use_mains)
-               power_supply_unregister(smb->mains);
-       return 0;
-}
-
-static const struct i2c_device_id smb347_id[] = {
-       { "smb347", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, smb347_id);
-
-static struct i2c_driver smb347_driver = {
-       .driver = {
-               .name = "smb347",
-       },
-       .probe        = smb347_probe,
-       .remove       = smb347_remove,
-       .id_table     = smb347_id,
-};
-
-module_i2c_driver(smb347_driver);
-
-MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
-MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
-MODULE_DESCRIPTION("SMB347 battery charger driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/88pm860x_battery.c b/drivers/power/supply/88pm860x_battery.c
new file mode 100644 (file)
index 0000000..63c57dc
--- /dev/null
@@ -0,0 +1,1021 @@
+/*
+ * Battery driver for Marvell 88PM860x PMIC
+ *
+ * Copyright (c) 2012 Marvell International Ltd.
+ * Author:     Jett Zhou <jtzhou@marvell.com>
+ *             Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/delay.h>
+
+/* bit definitions of Status Query Interface 2 */
+#define STATUS2_CHG                    (1 << 2)
+#define STATUS2_BAT                    (1 << 3)
+#define STATUS2_VBUS                   (1 << 4)
+
+/* bit definitions of Measurement Enable 1 Register */
+#define MEAS1_TINT                     (1 << 3)
+#define MEAS1_GP1                      (1 << 5)
+
+/* bit definitions of Measurement Enable 3 Register */
+#define MEAS3_IBAT                     (1 << 0)
+#define MEAS3_BAT_DET                  (1 << 1)
+#define MEAS3_CC                       (1 << 2)
+
+/* bit definitions of Measurement Off Time Register */
+#define MEAS_OFF_SLEEP_EN              (1 << 1)
+
+/* bit definitions of GPADC Bias Current 2 Register */
+#define GPBIAS2_GPADC1_SET             (2 << 4)
+/* GPADC1 Bias Current value in uA unit */
+#define GPBIAS2_GPADC1_UA              ((GPBIAS2_GPADC1_SET >> 4) * 5 + 1)
+
+/* bit definitions of GPADC Misc 1 Register */
+#define GPMISC1_GPADC_EN               (1 << 0)
+
+/* bit definitions of Charger Control 6 Register */
+#define CC6_BAT_DET_GPADC1             1
+
+/* bit definitions of Coulomb Counter Reading Register */
+#define CCNT_AVG_SEL                   (4 << 3)
+
+/* bit definitions of RTC miscellaneous Register1 */
+#define RTC_SOC_5LSB           (0x1F << 3)
+
+/* bit definitions of RTC Register1 */
+#define RTC_SOC_3MSB           (0x7)
+
+/* bit definitions of Power up Log register */
+#define BAT_WU_LOG                     (1<<6)
+
+/* coulomb counter index */
+#define CCNT_POS1                      0
+#define CCNT_POS2                      1
+#define CCNT_NEG1                      2
+#define CCNT_NEG2                      3
+#define CCNT_SPOS                      4
+#define CCNT_SNEG                      5
+
+/* OCV -- Open Circuit Voltage */
+#define OCV_MODE_ACTIVE                        0
+#define OCV_MODE_SLEEP                 1
+
+/* Vbat range of CC for measuring Rbat */
+#define LOW_BAT_THRESHOLD              3600
+#define VBATT_RESISTOR_MIN             3800
+#define VBATT_RESISTOR_MAX             4100
+
+/* TBAT for batt, TINT for chip itself */
+#define PM860X_TEMP_TINT               (0)
+#define PM860X_TEMP_TBAT               (1)
+
+/*
+ * Battery temperature based on NTC resistor, defined
+ * corresponding resistor value  -- Ohm / C degeree.
+ */
+#define TBAT_NEG_25D           127773  /* -25 */
+#define TBAT_NEG_10D           54564   /* -10 */
+#define TBAT_0D                        32330   /* 0 */
+#define TBAT_10D               19785   /* 10 */
+#define TBAT_20D               12468   /* 20 */
+#define TBAT_30D               8072    /* 30 */
+#define TBAT_40D               5356    /* 40 */
+
+struct pm860x_battery_info {
+       struct pm860x_chip *chip;
+       struct i2c_client *i2c;
+       struct device *dev;
+
+       struct power_supply *battery;
+       struct mutex lock;
+       int status;
+       int irq_cc;
+       int irq_batt;
+       int max_capacity;
+       int resistor;           /* Battery Internal Resistor */
+       int last_capacity;
+       int start_soc;
+       unsigned present:1;
+       unsigned temp_type:1;   /* TINT or TBAT */
+};
+
+struct ccnt {
+       unsigned long long int pos;
+       unsigned long long int neg;
+       unsigned int spos;
+       unsigned int sneg;
+
+       int total_chg;          /* mAh(3.6C) */
+       int total_dischg;       /* mAh(3.6C) */
+};
+
+/*
+ * State of Charge.
+ * The first number is mAh(=3.6C), and the second number is percent point.
+ */
+static int array_soc[][2] = {
+       {4170, 100}, {4154, 99}, {4136, 98}, {4122, 97}, {4107, 96},
+       {4102, 95}, {4088, 94}, {4081, 93}, {4070, 92}, {4060, 91},
+       {4053, 90}, {4044, 89}, {4035, 88}, {4028, 87}, {4019, 86},
+       {4013, 85}, {4006, 84}, {3995, 83}, {3987, 82}, {3982, 81},
+       {3976, 80}, {3968, 79}, {3962, 78}, {3954, 77}, {3946, 76},
+       {3941, 75}, {3934, 74}, {3929, 73}, {3922, 72}, {3916, 71},
+       {3910, 70}, {3904, 69}, {3898, 68}, {3892, 67}, {3887, 66},
+       {3880, 65}, {3874, 64}, {3868, 63}, {3862, 62}, {3854, 61},
+       {3849, 60}, {3843, 59}, {3840, 58}, {3833, 57}, {3829, 56},
+       {3824, 55}, {3818, 54}, {3815, 53}, {3810, 52}, {3808, 51},
+       {3804, 50}, {3801, 49}, {3798, 48}, {3796, 47}, {3792, 46},
+       {3789, 45}, {3785, 44}, {3784, 43}, {3782, 42}, {3780, 41},
+       {3777, 40}, {3776, 39}, {3774, 38}, {3772, 37}, {3771, 36},
+       {3769, 35}, {3768, 34}, {3764, 33}, {3763, 32}, {3760, 31},
+       {3760, 30}, {3754, 29}, {3750, 28}, {3749, 27}, {3744, 26},
+       {3740, 25}, {3734, 24}, {3732, 23}, {3728, 22}, {3726, 21},
+       {3720, 20}, {3716, 19}, {3709, 18}, {3703, 17}, {3698, 16},
+       {3692, 15}, {3683, 14}, {3675, 13}, {3670, 12}, {3665, 11},
+       {3661, 10}, {3649, 9}, {3637, 8}, {3622, 7}, {3609, 6},
+       {3580, 5}, {3558, 4}, {3540, 3}, {3510, 2}, {3429, 1},
+};
+
+static struct ccnt ccnt_data;
+
+/*
+ * register 1 bit[7:0] -- bit[11:4] of measured value of voltage
+ * register 0 bit[3:0] -- bit[3:0] of measured value of voltage
+ */
+static int measure_12bit_voltage(struct pm860x_battery_info *info,
+                                int offset, int *data)
+{
+       unsigned char buf[2];
+       int ret;
+
+       ret = pm860x_bulk_read(info->i2c, offset, 2, buf);
+       if (ret < 0)
+               return ret;
+
+       *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
+       /* V_MEAS(mV) = data * 1.8 * 1000 / (2^12) */
+       *data = ((*data & 0xfff) * 9 * 25) >> 9;
+       return 0;
+}
+
+static int measure_vbatt(struct pm860x_battery_info *info, int state,
+                        int *data)
+{
+       unsigned char buf[5];
+       int ret;
+
+       switch (state) {
+       case OCV_MODE_ACTIVE:
+               ret = measure_12bit_voltage(info, PM8607_VBAT_MEAS1, data);
+               if (ret)
+                       return ret;
+               /* V_BATT_MEAS(mV) = value * 3 * 1.8 * 1000 / (2^12) */
+               *data *= 3;
+               break;
+       case OCV_MODE_SLEEP:
+               /*
+                * voltage value of VBATT in sleep mode is saved in different
+                * registers.
+                * bit[11:10] -- bit[7:6] of LDO9(0x18)
+                * bit[9:8] -- bit[7:6] of LDO8(0x17)
+                * bit[7:6] -- bit[7:6] of LDO7(0x16)
+                * bit[5:4] -- bit[7:6] of LDO6(0x15)
+                * bit[3:0] -- bit[7:4] of LDO5(0x14)
+                */
+               ret = pm860x_bulk_read(info->i2c, PM8607_LDO5, 5, buf);
+               if (ret < 0)
+                       return ret;
+               ret = ((buf[4] >> 6) << 10) | ((buf[3] >> 6) << 8)
+                   | ((buf[2] >> 6) << 6) | ((buf[1] >> 6) << 4)
+                   | (buf[0] >> 4);
+               /* V_BATT_MEAS(mV) = data * 3 * 1.8 * 1000 / (2^12) */
+               *data = ((*data & 0xff) * 27 * 25) >> 9;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * Return value is signed data.
+ * Negative value means discharging, and positive value means charging.
+ */
+static int measure_current(struct pm860x_battery_info *info, int *data)
+{
+       unsigned char buf[2];
+       short s;
+       int ret;
+
+       ret = pm860x_bulk_read(info->i2c, PM8607_IBAT_MEAS1, 2, buf);
+       if (ret < 0)
+               return ret;
+
+       s = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
+       /* current(mA) = value * 0.125 */
+       *data = s >> 3;
+       return 0;
+}
+
+static int set_charger_current(struct pm860x_battery_info *info, int data,
+                              int *old)
+{
+       int ret;
+
+       if (data < 50 || data > 1600 || !old)
+               return -EINVAL;
+
+       data = ((data - 50) / 50) & 0x1f;
+       *old = pm860x_reg_read(info->i2c, PM8607_CHG_CTRL2);
+       *old = (*old & 0x1f) * 50 + 50;
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, data);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int read_ccnt(struct pm860x_battery_info *info, int offset,
+                    int *ccnt)
+{
+       unsigned char buf[2];
+       int ret;
+
+       ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7, offset & 7);
+       if (ret < 0)
+               goto out;
+       ret = pm860x_bulk_read(info->i2c, PM8607_CCNT_MEAS1, 2, buf);
+       if (ret < 0)
+               goto out;
+       *ccnt = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
+       return 0;
+out:
+       return ret;
+}
+
+static int calc_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt)
+{
+       unsigned int sum;
+       int ret;
+       int data;
+
+       ret = read_ccnt(info, CCNT_POS1, &data);
+       if (ret)
+               goto out;
+       sum = data & 0xffff;
+       ret = read_ccnt(info, CCNT_POS2, &data);
+       if (ret)
+               goto out;
+       sum |= (data & 0xffff) << 16;
+       ccnt->pos += sum;
+
+       ret = read_ccnt(info, CCNT_NEG1, &data);
+       if (ret)
+               goto out;
+       sum = data & 0xffff;
+       ret = read_ccnt(info, CCNT_NEG2, &data);
+       if (ret)
+               goto out;
+       sum |= (data & 0xffff) << 16;
+       sum = ~sum + 1;         /* since it's negative */
+       ccnt->neg += sum;
+
+       ret = read_ccnt(info, CCNT_SPOS, &data);
+       if (ret)
+               goto out;
+       ccnt->spos += data;
+       ret = read_ccnt(info, CCNT_SNEG, &data);
+       if (ret)
+               goto out;
+
+       /*
+        * charge(mAh)  = count * 1.6984 * 1e(-8)
+        *              = count * 16984 * 1.024 * 1.024 * 1.024 / (2 ^ 40)
+        *              = count * 18236 / (2 ^ 40)
+        */
+       ccnt->total_chg = (int) ((ccnt->pos * 18236) >> 40);
+       ccnt->total_dischg = (int) ((ccnt->neg * 18236) >> 40);
+       return 0;
+out:
+       return ret;
+}
+
+static int clear_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt)
+{
+       int data;
+
+       memset(ccnt, 0, sizeof(*ccnt));
+       /* read to clear ccnt */
+       read_ccnt(info, CCNT_POS1, &data);
+       read_ccnt(info, CCNT_POS2, &data);
+       read_ccnt(info, CCNT_NEG1, &data);
+       read_ccnt(info, CCNT_NEG2, &data);
+       read_ccnt(info, CCNT_SPOS, &data);
+       read_ccnt(info, CCNT_SNEG, &data);
+       return 0;
+}
+
+/* Calculate Open Circuit Voltage */
+static int calc_ocv(struct pm860x_battery_info *info, int *ocv)
+{
+       int ret;
+       int i;
+       int data;
+       int vbatt_avg;
+       int vbatt_sum;
+       int ibatt_avg;
+       int ibatt_sum;
+
+       if (!ocv)
+               return -EINVAL;
+
+       for (i = 0, ibatt_sum = 0, vbatt_sum = 0; i < 10; i++) {
+               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
+               if (ret)
+                       goto out;
+               vbatt_sum += data;
+               ret = measure_current(info, &data);
+               if (ret)
+                       goto out;
+               ibatt_sum += data;
+       }
+       vbatt_avg = vbatt_sum / 10;
+       ibatt_avg = ibatt_sum / 10;
+
+       mutex_lock(&info->lock);
+       if (info->present)
+               *ocv = vbatt_avg - ibatt_avg * info->resistor / 1000;
+       else
+               *ocv = vbatt_avg;
+       mutex_unlock(&info->lock);
+       dev_dbg(info->dev, "VBAT average:%d, OCV:%d\n", vbatt_avg, *ocv);
+       return 0;
+out:
+       return ret;
+}
+
+/* Calculate State of Charge (percent points) */
+static int calc_soc(struct pm860x_battery_info *info, int state, int *soc)
+{
+       int i;
+       int ocv;
+       int count;
+       int ret = -EINVAL;
+
+       if (!soc)
+               return -EINVAL;
+
+       switch (state) {
+       case OCV_MODE_ACTIVE:
+               ret = calc_ocv(info, &ocv);
+               break;
+       case OCV_MODE_SLEEP:
+               ret = measure_vbatt(info, OCV_MODE_SLEEP, &ocv);
+               break;
+       }
+       if (ret)
+               return ret;
+
+       count = ARRAY_SIZE(array_soc);
+       if (ocv < array_soc[count - 1][0]) {
+               *soc = 0;
+               return 0;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (ocv >= array_soc[i][0]) {
+                       *soc = array_soc[i][1];
+                       break;
+               }
+       }
+       return 0;
+}
+
+static irqreturn_t pm860x_coulomb_handler(int irq, void *data)
+{
+       struct pm860x_battery_info *info = data;
+
+       calc_ccnt(info, &ccnt_data);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t pm860x_batt_handler(int irq, void *data)
+{
+       struct pm860x_battery_info *info = data;
+       int ret;
+
+       mutex_lock(&info->lock);
+       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
+       if (ret & STATUS2_BAT) {
+               info->present = 1;
+               info->temp_type = PM860X_TEMP_TBAT;
+       } else {
+               info->present = 0;
+               info->temp_type = PM860X_TEMP_TINT;
+       }
+       mutex_unlock(&info->lock);
+       /* clear ccnt since battery is attached or dettached */
+       clear_ccnt(info, &ccnt_data);
+       return IRQ_HANDLED;
+}
+
+static void pm860x_init_battery(struct pm860x_battery_info *info)
+{
+       unsigned char buf[2];
+       int ret;
+       int data;
+       int bat_remove;
+       int soc;
+
+       /* measure enable on GPADC1 */
+       data = MEAS1_GP1;
+       if (info->temp_type == PM860X_TEMP_TINT)
+               data |= MEAS1_TINT;
+       ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN1, data, data);
+       if (ret)
+               goto out;
+
+       /* measure enable on IBAT, BAT_DET, CC. IBAT is depend on CC. */
+       data = MEAS3_IBAT | MEAS3_BAT_DET | MEAS3_CC;
+       ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN3, data, data);
+       if (ret)
+               goto out;
+
+       /* measure disable CC in sleep time  */
+       ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME1, 0x82);
+       if (ret)
+               goto out;
+       ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME2, 0x6c);
+       if (ret)
+               goto out;
+
+       /* enable GPADC */
+       ret = pm860x_set_bits(info->i2c, PM8607_GPADC_MISC1,
+                           GPMISC1_GPADC_EN, GPMISC1_GPADC_EN);
+       if (ret < 0)
+               goto out;
+
+       /* detect battery via GPADC1 */
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
+                           CC6_BAT_DET_GPADC1, CC6_BAT_DET_GPADC1);
+       if (ret < 0)
+               goto out;
+
+       ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7 << 3,
+                             CCNT_AVG_SEL);
+       if (ret < 0)
+               goto out;
+
+       /* set GPADC1 bias */
+       ret = pm860x_set_bits(info->i2c, PM8607_GP_BIAS2, 0xF << 4,
+                             GPBIAS2_GPADC1_SET);
+       if (ret < 0)
+               goto out;
+
+       /* check whether battery present) */
+       mutex_lock(&info->lock);
+       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
+       if (ret < 0) {
+               mutex_unlock(&info->lock);
+               goto out;
+       }
+       if (ret & STATUS2_BAT) {
+               info->present = 1;
+               info->temp_type = PM860X_TEMP_TBAT;
+       } else {
+               info->present = 0;
+               info->temp_type = PM860X_TEMP_TINT;
+       }
+       mutex_unlock(&info->lock);
+
+       calc_soc(info, OCV_MODE_ACTIVE, &soc);
+
+       data = pm860x_reg_read(info->i2c, PM8607_POWER_UP_LOG);
+       bat_remove = data & BAT_WU_LOG;
+
+       dev_dbg(info->dev, "battery wake up? %s\n",
+               bat_remove != 0 ? "yes" : "no");
+
+       /* restore SOC from RTC domain register */
+       if (bat_remove == 0) {
+               buf[0] = pm860x_reg_read(info->i2c, PM8607_RTC_MISC2);
+               buf[1] = pm860x_reg_read(info->i2c, PM8607_RTC1);
+               data = ((buf[1] & 0x3) << 5) | ((buf[0] >> 3) & 0x1F);
+               if (data > soc + 15)
+                       info->start_soc = soc;
+               else if (data < soc - 15)
+                       info->start_soc = soc;
+               else
+                       info->start_soc = data;
+               dev_dbg(info->dev, "soc_rtc %d, soc_ocv :%d\n", data, soc);
+       } else {
+               pm860x_set_bits(info->i2c, PM8607_POWER_UP_LOG,
+                               BAT_WU_LOG, BAT_WU_LOG);
+               info->start_soc = soc;
+       }
+       info->last_capacity = info->start_soc;
+       dev_dbg(info->dev, "init soc : %d\n", info->last_capacity);
+out:
+       return;
+}
+
+static void set_temp_threshold(struct pm860x_battery_info *info,
+                              int min, int max)
+{
+       int data;
+
+       /* (tmp << 8) / 1800 */
+       if (min <= 0)
+               data = 0;
+       else
+               data = (min << 8) / 1800;
+       pm860x_reg_write(info->i2c, PM8607_GPADC1_HIGHTH, data);
+       dev_dbg(info->dev, "TEMP_HIGHTH : min: %d, 0x%x\n", min, data);
+
+       if (max <= 0)
+               data = 0xff;
+       else
+               data = (max << 8) / 1800;
+       pm860x_reg_write(info->i2c, PM8607_GPADC1_LOWTH, data);
+       dev_dbg(info->dev, "TEMP_LOWTH:max : %d, 0x%x\n", max, data);
+}
+
+static int measure_temp(struct pm860x_battery_info *info, int *data)
+{
+       int ret;
+       int temp;
+       int min;
+       int max;
+
+       if (info->temp_type == PM860X_TEMP_TINT) {
+               ret = measure_12bit_voltage(info, PM8607_TINT_MEAS1, data);
+               if (ret)
+                       return ret;
+               *data = (*data - 884) * 1000 / 3611;
+       } else {
+               ret = measure_12bit_voltage(info, PM8607_GPADC1_MEAS1, data);
+               if (ret)
+                       return ret;
+               /* meausered Vtbat(mV) / Ibias_current(11uA)*/
+               *data = (*data * 1000) / GPBIAS2_GPADC1_UA;
+
+               if (*data > TBAT_NEG_25D) {
+                       temp = -30;     /* over cold , suppose -30 roughly */
+                       max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
+                       set_temp_threshold(info, 0, max);
+               } else if (*data > TBAT_NEG_10D) {
+                       temp = -15;     /* -15 degree, code */
+                       max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
+                       set_temp_threshold(info, 0, max);
+               } else if (*data > TBAT_0D) {
+                       temp = -5;      /* -5 degree */
+                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
+                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
+                       set_temp_threshold(info, min, max);
+               } else if (*data > TBAT_10D) {
+                       temp = 5;       /* in range of (0, 10) */
+                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
+                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
+                       set_temp_threshold(info, min, max);
+               } else if (*data > TBAT_20D) {
+                       temp = 15;      /* in range of (10, 20) */
+                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
+                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
+                       set_temp_threshold(info, min, max);
+               } else if (*data > TBAT_30D) {
+                       temp = 25;      /* in range of (20, 30) */
+                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
+                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
+                       set_temp_threshold(info, min, max);
+               } else if (*data > TBAT_40D) {
+                       temp = 35;      /* in range of (30, 40) */
+                       min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
+                       max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
+                       set_temp_threshold(info, min, max);
+               } else {
+                       min = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
+                       set_temp_threshold(info, min, 0);
+                       temp = 45;      /* over heat ,suppose 45 roughly */
+               }
+
+               dev_dbg(info->dev, "temp_C:%d C,temp_mv:%d mv\n", temp, *data);
+               *data = temp;
+       }
+       return 0;
+}
+
+static int calc_resistor(struct pm860x_battery_info *info)
+{
+       int vbatt_sum1;
+       int vbatt_sum2;
+       int chg_current;
+       int ibatt_sum1;
+       int ibatt_sum2;
+       int data;
+       int ret;
+       int i;
+
+       ret = measure_current(info, &data);
+       /* make sure that charging is launched by data > 0 */
+       if (ret || data < 0)
+               goto out;
+
+       ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
+       if (ret)
+               goto out;
+       /* calculate resistor only in CC charge mode */
+       if (data < VBATT_RESISTOR_MIN || data > VBATT_RESISTOR_MAX)
+               goto out;
+
+       /* current is saved */
+       if (set_charger_current(info, 500, &chg_current))
+               goto out;
+
+       /*
+        * set charge current as 500mA, wait about 500ms till charging
+        * process is launched and stable with the newer charging current.
+        */
+       msleep(500);
+
+       for (i = 0, vbatt_sum1 = 0, ibatt_sum1 = 0; i < 10; i++) {
+               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
+               if (ret)
+                       goto out_meas;
+               vbatt_sum1 += data;
+               ret = measure_current(info, &data);
+               if (ret)
+                       goto out_meas;
+
+               if (data < 0)
+                       ibatt_sum1 = ibatt_sum1 - data; /* discharging */
+               else
+                       ibatt_sum1 = ibatt_sum1 + data; /* charging */
+       }
+
+       if (set_charger_current(info, 100, &ret))
+               goto out_meas;
+       /*
+        * set charge current as 100mA, wait about 500ms till charging
+        * process is launched and stable with the newer charging current.
+        */
+       msleep(500);
+
+       for (i = 0, vbatt_sum2 = 0, ibatt_sum2 = 0; i < 10; i++) {
+               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
+               if (ret)
+                       goto out_meas;
+               vbatt_sum2 += data;
+               ret = measure_current(info, &data);
+               if (ret)
+                       goto out_meas;
+
+               if (data < 0)
+                       ibatt_sum2 = ibatt_sum2 - data; /* discharging */
+               else
+                       ibatt_sum2 = ibatt_sum2 + data; /* charging */
+       }
+
+       /* restore current setting */
+       if (set_charger_current(info, chg_current, &ret))
+               goto out_meas;
+
+       if ((vbatt_sum1 > vbatt_sum2) && (ibatt_sum1 > ibatt_sum2) &&
+                       (ibatt_sum2 > 0)) {
+               /* calculate resistor in discharging case */
+               data = 1000 * (vbatt_sum1 - vbatt_sum2)
+                   / (ibatt_sum1 - ibatt_sum2);
+               if ((data - info->resistor > 0) &&
+                               (data - info->resistor < info->resistor))
+                       info->resistor = data;
+               if ((info->resistor - data > 0) &&
+                               (info->resistor - data < data))
+                       info->resistor = data;
+       }
+       return 0;
+
+out_meas:
+       set_charger_current(info, chg_current, &ret);
+out:
+       return -EINVAL;
+}
+
+static int calc_capacity(struct pm860x_battery_info *info, int *cap)
+{
+       int ret;
+       int data;
+       int ibat;
+       int cap_ocv = 0;
+       int cap_cc = 0;
+
+       ret = calc_ccnt(info, &ccnt_data);
+       if (ret)
+               goto out;
+soc:
+       data = info->max_capacity * info->start_soc / 100;
+       if (ccnt_data.total_dischg - ccnt_data.total_chg <= data) {
+               cap_cc =
+                   data + ccnt_data.total_chg - ccnt_data.total_dischg;
+       } else {
+               clear_ccnt(info, &ccnt_data);
+               calc_soc(info, OCV_MODE_ACTIVE, &info->start_soc);
+               dev_dbg(info->dev, "restart soc = %d !\n",
+                       info->start_soc);
+               goto soc;
+       }
+
+       cap_cc = cap_cc * 100 / info->max_capacity;
+       if (cap_cc < 0)
+               cap_cc = 0;
+       else if (cap_cc > 100)
+               cap_cc = 100;
+
+       dev_dbg(info->dev, "%s, last cap : %d", __func__,
+               info->last_capacity);
+
+       ret = measure_current(info, &ibat);
+       if (ret)
+               goto out;
+       /* Calculate the capacity when discharging(ibat < 0) */
+       if (ibat < 0) {
+               ret = calc_soc(info, OCV_MODE_ACTIVE, &cap_ocv);
+               if (ret)
+                       cap_ocv = info->last_capacity;
+               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
+               if (ret)
+                       goto out;
+               if (data <= LOW_BAT_THRESHOLD) {
+                       /* choose the lower capacity value to report
+                        * between vbat and CC when vbat < 3.6v;
+                        * than 3.6v;
+                        */
+                       *cap = min(cap_ocv, cap_cc);
+               } else {
+                       /* when detect vbat > 3.6v, but cap_cc < 15,and
+                        * cap_ocv is 10% larger than cap_cc, we can think
+                        * CC have some accumulation error, switch to OCV
+                        * to estimate capacity;
+                        * */
+                       if (cap_cc < 15 && cap_ocv - cap_cc > 10)
+                               *cap = cap_ocv;
+                       else
+                               *cap = cap_cc;
+               }
+               /* when discharging, make sure current capacity
+                * is lower than last*/
+               if (*cap > info->last_capacity)
+                       *cap = info->last_capacity;
+       } else {
+               *cap = cap_cc;
+       }
+       info->last_capacity = *cap;
+
+       dev_dbg(info->dev, "%s, cap_ocv:%d cap_cc:%d, cap:%d\n",
+               (ibat < 0) ? "discharging" : "charging",
+                cap_ocv, cap_cc, *cap);
+       /*
+        * store the current capacity to RTC domain register,
+        * after next power up , it will be restored.
+        */
+       pm860x_set_bits(info->i2c, PM8607_RTC_MISC2, RTC_SOC_5LSB,
+                       (*cap & 0x1F) << 3);
+       pm860x_set_bits(info->i2c, PM8607_RTC1, RTC_SOC_3MSB,
+                       ((*cap >> 5) & 0x3));
+       return 0;
+out:
+       return ret;
+}
+
+static void pm860x_external_power_changed(struct power_supply *psy)
+{
+       struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
+
+       calc_resistor(info);
+}
+
+static int pm860x_batt_get_prop(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
+       int data;
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = info->present;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = calc_capacity(info, &data);
+               if (ret)
+                       return ret;
+               if (data < 0)
+                       data = 0;
+               else if (data > 100)
+                       data = 100;
+               /* return 100 if battery is not attached */
+               if (!info->present)
+                       data = 100;
+               val->intval = data;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               /* return real vbatt Voltage */
+               ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
+               if (ret)
+                       return ret;
+               val->intval = data * 1000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               /* return Open Circuit Voltage (not measured voltage) */
+               ret = calc_ocv(info, &data);
+               if (ret)
+                       return ret;
+               val->intval = data * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = measure_current(info, &data);
+               if (ret)
+                       return ret;
+               val->intval = data;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               if (info->present) {
+                       ret = measure_temp(info, &data);
+                       if (ret)
+                               return ret;
+                       data *= 10;
+               } else {
+                       /* Fake Temp 25C Without Battery */
+                       data = 250;
+               }
+               val->intval = data;
+               break;
+       default:
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static int pm860x_batt_set_prop(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      const union power_supply_propval *val)
+{
+       struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               clear_ccnt(info, &ccnt_data);
+               info->start_soc = 100;
+               dev_dbg(info->dev, "chg done, update soc = %d\n",
+                       info->start_soc);
+               break;
+       default:
+               return -EPERM;
+       }
+
+       return 0;
+}
+
+
+static enum power_supply_property pm860x_batt_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static const struct power_supply_desc pm860x_battery_desc = {
+       .name                   = "battery-monitor",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = pm860x_batt_props,
+       .num_properties         = ARRAY_SIZE(pm860x_batt_props),
+       .get_property           = pm860x_batt_get_prop,
+       .set_property           = pm860x_batt_set_prop,
+       .external_power_changed = pm860x_external_power_changed,
+};
+
+static int pm860x_battery_probe(struct platform_device *pdev)
+{
+       struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       struct pm860x_battery_info *info;
+       struct pm860x_power_pdata *pdata;
+       int ret;
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->irq_cc = platform_get_irq(pdev, 0);
+       if (info->irq_cc <= 0) {
+               dev_err(&pdev->dev, "No IRQ resource!\n");
+               return -EINVAL;
+       }
+
+       info->irq_batt = platform_get_irq(pdev, 1);
+       if (info->irq_batt <= 0) {
+               dev_err(&pdev->dev, "No IRQ resource!\n");
+               return -EINVAL;
+       }
+
+       info->chip = chip;
+       info->i2c =
+           (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+       info->dev = &pdev->dev;
+       info->status = POWER_SUPPLY_STATUS_UNKNOWN;
+       pdata = pdev->dev.platform_data;
+
+       mutex_init(&info->lock);
+       platform_set_drvdata(pdev, info);
+
+       pm860x_init_battery(info);
+
+       if (pdata && pdata->max_capacity)
+               info->max_capacity = pdata->max_capacity;
+       else
+               info->max_capacity = 1500;      /* set default capacity */
+       if (pdata && pdata->resistor)
+               info->resistor = pdata->resistor;
+       else
+               info->resistor = 300;   /* set default internal resistor */
+
+       info->battery = devm_power_supply_register(&pdev->dev,
+                                                  &pm860x_battery_desc,
+                                                  NULL);
+       if (IS_ERR(info->battery))
+               return PTR_ERR(info->battery);
+       info->battery->dev.parent = &pdev->dev;
+
+       ret = devm_request_threaded_irq(chip->dev, info->irq_cc, NULL,
+                                       pm860x_coulomb_handler, IRQF_ONESHOT,
+                                       "coulomb", info);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+                       info->irq_cc, ret);
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(chip->dev, info->irq_batt, NULL,
+                                       pm860x_batt_handler,
+                                       IRQF_ONESHOT, "battery", info);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+                       info->irq_batt, ret);
+               return ret;
+       }
+
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm860x_battery_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+       if (device_may_wakeup(dev))
+               chip->wakeup_flag |= 1 << PM8607_IRQ_CC;
+       return 0;
+}
+
+static int pm860x_battery_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+       if (device_may_wakeup(dev))
+               chip->wakeup_flag &= ~(1 << PM8607_IRQ_CC);
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm860x_battery_pm_ops,
+                       pm860x_battery_suspend, pm860x_battery_resume);
+
+static struct platform_driver pm860x_battery_driver = {
+       .driver = {
+                  .name = "88pm860x-battery",
+                  .pm = &pm860x_battery_pm_ops,
+       },
+       .probe = pm860x_battery_probe,
+};
+module_platform_driver(pm860x_battery_driver);
+
+MODULE_DESCRIPTION("Marvell 88PM860x Battery driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/88pm860x_charger.c b/drivers/power/supply/88pm860x_charger.c
new file mode 100644 (file)
index 0000000..2b82e44
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ * Battery driver for Marvell 88PM860x PMIC
+ *
+ * Copyright (c) 2012 Marvell International Ltd.
+ * Author:     Jett Zhou <jtzhou@marvell.com>
+ *             Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <asm/div64.h>
+
+/* bit definitions of Status Query Interface 2 */
+#define STATUS2_CHG            (1 << 2)
+
+/* bit definitions of Reset Out Register */
+#define RESET_SW_PD            (1 << 7)
+
+/* bit definitions of PreReg 1 */
+#define PREREG1_90MA           (0x0)
+#define PREREG1_180MA          (0x1)
+#define PREREG1_450MA          (0x4)
+#define PREREG1_540MA          (0x5)
+#define PREREG1_1350MA         (0xE)
+#define PREREG1_VSYS_4_5V      (3 << 4)
+
+/* bit definitions of Charger Control 1 Register */
+#define CC1_MODE_OFF           (0)
+#define CC1_MODE_PRECHARGE     (1)
+#define CC1_MODE_FASTCHARGE    (2)
+#define CC1_MODE_PULSECHARGE   (3)
+#define CC1_ITERM_20MA         (0 << 2)
+#define CC1_ITERM_60MA         (2 << 2)
+#define CC1_VFCHG_4_2V         (9 << 4)
+
+/* bit definitions of Charger Control 2 Register */
+#define CC2_ICHG_100MA         (0x1)
+#define CC2_ICHG_500MA         (0x9)
+#define CC2_ICHG_1000MA                (0x13)
+
+/* bit definitions of Charger Control 3 Register */
+#define CC3_180MIN_TIMEOUT     (0x6 << 4)
+#define CC3_270MIN_TIMEOUT     (0x7 << 4)
+#define CC3_360MIN_TIMEOUT     (0xA << 4)
+#define CC3_DISABLE_TIMEOUT    (0xF << 4)
+
+/* bit definitions of Charger Control 4 Register */
+#define CC4_IPRE_40MA          (7)
+#define CC4_VPCHG_3_2V         (3 << 4)
+#define CC4_IFCHG_MON_EN       (1 << 6)
+#define CC4_BTEMP_MON_EN       (1 << 7)
+
+/* bit definitions of Charger Control 6 Register */
+#define CC6_BAT_OV_EN          (1 << 2)
+#define CC6_BAT_UV_EN          (1 << 3)
+#define CC6_UV_VBAT_SET                (0x3 << 6)      /* 2.8v */
+
+/* bit definitions of Charger Control 7 Register */
+#define CC7_BAT_REM_EN         (1 << 3)
+#define CC7_IFSM_EN            (1 << 7)
+
+/* bit definitions of Measurement Enable 1 Register */
+#define MEAS1_VBAT             (1 << 0)
+
+/* bit definitions of Measurement Enable 3 Register */
+#define MEAS3_IBAT_EN          (1 << 0)
+#define MEAS3_CC_EN            (1 << 2)
+
+#define FSM_INIT               0
+#define FSM_DISCHARGE          1
+#define FSM_PRECHARGE          2
+#define FSM_FASTCHARGE         3
+
+#define PRECHARGE_THRESHOLD    3100
+#define POWEROFF_THRESHOLD     3400
+#define CHARGE_THRESHOLD       4000
+#define DISCHARGE_THRESHOLD    4180
+
+/* over-temperature on PM8606 setting */
+#define OVER_TEMP_FLAG         (1 << 6)
+#define OVTEMP_AUTORECOVER     (1 << 3)
+
+/* over-voltage protect on vchg setting mv */
+#define VCHG_NORMAL_LOW                4200
+#define VCHG_NORMAL_CHECK      5800
+#define VCHG_NORMAL_HIGH       6000
+#define VCHG_OVP_LOW           5500
+
+struct pm860x_charger_info {
+       struct pm860x_chip *chip;
+       struct i2c_client *i2c;
+       struct i2c_client *i2c_8606;
+       struct device *dev;
+
+       struct power_supply *usb;
+       struct mutex lock;
+       int irq_nums;
+       int irq[7];
+       unsigned state:3;       /* fsm state */
+       unsigned online:1;      /* usb charger */
+       unsigned present:1;     /* battery present */
+       unsigned allowed:1;
+};
+
+static char *pm860x_supplied_to[] = {
+       "battery-monitor",
+};
+
+static int measure_vchg(struct pm860x_charger_info *info, int *data)
+{
+       unsigned char buf[2];
+       int ret = 0;
+
+       ret = pm860x_bulk_read(info->i2c, PM8607_VCHG_MEAS1, 2, buf);
+       if (ret < 0)
+               return ret;
+
+       *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
+       /* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */
+       *data = ((*data & 0xfff) * 9 * 125) >> 9;
+
+       dev_dbg(info->dev, "%s, vchg: %d mv\n", __func__, *data);
+
+       return ret;
+}
+
+static void set_vchg_threshold(struct pm860x_charger_info *info,
+                              int min, int max)
+{
+       int data;
+
+       /* (tmp << 8) * / 5 / 1800 */
+       if (min <= 0)
+               data = 0;
+       else
+               data = (min << 5) / 1125;
+       pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data);
+       dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data);
+
+       if (max <= 0)
+               data = 0xff;
+       else
+               data = (max << 5) / 1125;
+       pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data);
+       dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data);
+
+}
+
+static void set_vbatt_threshold(struct pm860x_charger_info *info,
+                               int min, int max)
+{
+       int data;
+
+       /* (tmp << 8) * 3 / 1800 */
+       if (min <= 0)
+               data = 0;
+       else
+               data = (min << 5) / 675;
+       pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data);
+       dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data);
+
+       if (max <= 0)
+               data = 0xff;
+       else
+               data = (max << 5) / 675;
+       pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data);
+       dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data);
+
+       return;
+}
+
+static int start_precharge(struct pm860x_charger_info *info)
+{
+       int ret;
+
+       dev_dbg(info->dev, "Start Pre-charging!\n");
+       set_vbatt_threshold(info, 0, 0);
+
+       ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
+                              PREREG1_1350MA | PREREG1_VSYS_4_5V);
+       if (ret < 0)
+               goto out;
+       /* stop charging */
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
+                             CC1_MODE_OFF);
+       if (ret < 0)
+               goto out;
+       /* set 270 minutes timeout */
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
+                             CC3_270MIN_TIMEOUT);
+       if (ret < 0)
+               goto out;
+       /* set precharge current, termination voltage, IBAT & TBAT monitor */
+       ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4,
+                              CC4_IPRE_40MA | CC4_VPCHG_3_2V |
+                              CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
+       if (ret < 0)
+               goto out;
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
+                             CC7_BAT_REM_EN | CC7_IFSM_EN,
+                             CC7_BAT_REM_EN | CC7_IFSM_EN);
+       if (ret < 0)
+               goto out;
+       /* trigger precharge */
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
+                             CC1_MODE_PRECHARGE);
+out:
+       return ret;
+}
+
+static int start_fastcharge(struct pm860x_charger_info *info)
+{
+       int ret;
+
+       dev_dbg(info->dev, "Start Fast-charging!\n");
+
+       /* set fastcharge termination current & voltage, disable charging */
+       ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1,
+                              CC1_MODE_OFF | CC1_ITERM_60MA |
+                              CC1_VFCHG_4_2V);
+       if (ret < 0)
+               goto out;
+       ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
+                              PREREG1_540MA | PREREG1_VSYS_4_5V);
+       if (ret < 0)
+               goto out;
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f,
+                             CC2_ICHG_500MA);
+       if (ret < 0)
+               goto out;
+       /* set 270 minutes timeout */
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
+                             CC3_270MIN_TIMEOUT);
+       if (ret < 0)
+               goto out;
+       /* set IBAT & TBAT monitor */
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4,
+                             CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN,
+                             CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
+       if (ret < 0)
+               goto out;
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
+                             CC6_BAT_OV_EN | CC6_BAT_UV_EN |
+                             CC6_UV_VBAT_SET,
+                             CC6_BAT_OV_EN | CC6_BAT_UV_EN |
+                             CC6_UV_VBAT_SET);
+       if (ret < 0)
+               goto out;
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
+                             CC7_BAT_REM_EN | CC7_IFSM_EN,
+                             CC7_BAT_REM_EN | CC7_IFSM_EN);
+       if (ret < 0)
+               goto out;
+       /* launch fast-charge */
+       ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
+                             CC1_MODE_FASTCHARGE);
+       /* vchg threshold setting */
+       set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH);
+out:
+       return ret;
+}
+
+static void stop_charge(struct pm860x_charger_info *info, int vbatt)
+{
+       dev_dbg(info->dev, "Stop charging!\n");
+       pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF);
+       if (vbatt > CHARGE_THRESHOLD && info->online)
+               set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
+}
+
+static void power_off_notification(struct pm860x_charger_info *info)
+{
+       dev_dbg(info->dev, "Power-off notification!\n");
+}
+
+static int set_charging_fsm(struct pm860x_charger_info *info)
+{
+       struct power_supply *psy;
+       union power_supply_propval data;
+       unsigned char fsm_state[][16] = { "init", "discharge", "precharge",
+               "fastcharge",
+       };
+       int ret;
+       int vbatt;
+
+       psy = power_supply_get_by_name(pm860x_supplied_to[0]);
+       if (!psy)
+               return -EINVAL;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,
+                       &data);
+       if (ret) {
+               power_supply_put(psy);
+               return ret;
+       }
+       vbatt = data.intval / 1000;
+
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data);
+       if (ret) {
+               power_supply_put(psy);
+               return ret;
+       }
+       power_supply_put(psy);
+
+       mutex_lock(&info->lock);
+       info->present = data.intval;
+
+       dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, "
+               "Allowed:%d\n",
+               &fsm_state[info->state][0],
+               (info->online) ? "online" : "N/A",
+               (info->present) ? "present" : "N/A", info->allowed);
+       dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt);
+
+       switch (info->state) {
+       case FSM_INIT:
+               if (info->online && info->present && info->allowed) {
+                       if (vbatt < PRECHARGE_THRESHOLD) {
+                               info->state = FSM_PRECHARGE;
+                               start_precharge(info);
+                       } else if (vbatt > DISCHARGE_THRESHOLD) {
+                               info->state = FSM_DISCHARGE;
+                               stop_charge(info, vbatt);
+                       } else if (vbatt < DISCHARGE_THRESHOLD) {
+                               info->state = FSM_FASTCHARGE;
+                               start_fastcharge(info);
+                       }
+               } else {
+                       if (vbatt < POWEROFF_THRESHOLD) {
+                               power_off_notification(info);
+                       } else {
+                               info->state = FSM_DISCHARGE;
+                               stop_charge(info, vbatt);
+                       }
+               }
+               break;
+       case FSM_PRECHARGE:
+               if (info->online && info->present && info->allowed) {
+                       if (vbatt > PRECHARGE_THRESHOLD) {
+                               info->state = FSM_FASTCHARGE;
+                               start_fastcharge(info);
+                       }
+               } else {
+                       info->state = FSM_DISCHARGE;
+                       stop_charge(info, vbatt);
+               }
+               break;
+       case FSM_FASTCHARGE:
+               if (info->online && info->present && info->allowed) {
+                       if (vbatt < PRECHARGE_THRESHOLD) {
+                               info->state = FSM_PRECHARGE;
+                               start_precharge(info);
+                       }
+               } else {
+                       info->state = FSM_DISCHARGE;
+                       stop_charge(info, vbatt);
+               }
+               break;
+       case FSM_DISCHARGE:
+               if (info->online && info->present && info->allowed) {
+                       if (vbatt < PRECHARGE_THRESHOLD) {
+                               info->state = FSM_PRECHARGE;
+                               start_precharge(info);
+                       } else if (vbatt < DISCHARGE_THRESHOLD) {
+                               info->state = FSM_FASTCHARGE;
+                               start_fastcharge(info);
+                       }
+               } else {
+                       if (vbatt < POWEROFF_THRESHOLD)
+                               power_off_notification(info);
+                       else if (vbatt > CHARGE_THRESHOLD && info->online)
+                               set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
+               }
+               break;
+       default:
+               dev_warn(info->dev, "FSM meets wrong state:%d\n",
+                        info->state);
+               break;
+       }
+       dev_dbg(info->dev,
+               "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n",
+               &fsm_state[info->state][0],
+               (info->online) ? "online" : "N/A",
+               (info->present) ? "present" : "N/A", info->allowed);
+       mutex_unlock(&info->lock);
+
+       return 0;
+}
+
+static irqreturn_t pm860x_charger_handler(int irq, void *data)
+{
+       struct pm860x_charger_info *info = data;
+       int ret;
+
+       mutex_lock(&info->lock);
+       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
+       if (ret < 0) {
+               mutex_unlock(&info->lock);
+               goto out;
+       }
+       if (ret & STATUS2_CHG) {
+               info->online = 1;
+               info->allowed = 1;
+       } else {
+               info->online = 0;
+               info->allowed = 0;
+       }
+       mutex_unlock(&info->lock);
+       dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__,
+               (info->online) ? "online" : "N/A", info->allowed);
+
+       set_charging_fsm(info);
+
+       power_supply_changed(info->usb);
+out:
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t pm860x_temp_handler(int irq, void *data)
+{
+       struct power_supply *psy;
+       struct pm860x_charger_info *info = data;
+       union power_supply_propval temp;
+       int value;
+       int ret;
+
+       psy = power_supply_get_by_name(pm860x_supplied_to[0]);
+       if (!psy)
+               return IRQ_HANDLED;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp);
+       if (ret)
+               goto out;
+       value = temp.intval / 10;
+
+       mutex_lock(&info->lock);
+       /* Temperature < -10 C or >40 C, Will not allow charge */
+       if (value < -10 || value > 40)
+               info->allowed = 0;
+       else
+               info->allowed = 1;
+       dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
+       mutex_unlock(&info->lock);
+
+       set_charging_fsm(info);
+out:
+       power_supply_put(psy);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t pm860x_exception_handler(int irq, void *data)
+{
+       struct pm860x_charger_info *info = data;
+
+       mutex_lock(&info->lock);
+       info->allowed = 0;
+       mutex_unlock(&info->lock);
+       dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq);
+
+       set_charging_fsm(info);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t pm860x_done_handler(int irq, void *data)
+{
+       struct pm860x_charger_info *info = data;
+       struct power_supply *psy;
+       union power_supply_propval val;
+       int ret;
+       int vbatt;
+
+       mutex_lock(&info->lock);
+       /* pre-charge done, will transimit to fast-charge stage */
+       if (info->state == FSM_PRECHARGE) {
+               info->allowed = 1;
+               goto out;
+       }
+       /*
+        * Fast charge done, delay to read
+        * the correct status of CHG_DET.
+        */
+       mdelay(5);
+       info->allowed = 0;
+       psy = power_supply_get_by_name(pm860x_supplied_to[0]);
+       if (!psy)
+               goto out;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,
+                       &val);
+       if (ret)
+               goto out_psy_put;
+       vbatt = val.intval / 1000;
+       /*
+        * CHG_DONE interrupt is faster than CHG_DET interrupt when
+        * plug in/out usb, So we can not rely on info->online, we
+        * need check pm8607 status register to check usb is online
+        * or not, then we can decide it is real charge done
+        * automatically or it is triggered by usb plug out;
+        */
+       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
+       if (ret < 0)
+               goto out_psy_put;
+       if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG)
+               power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL,
+                               &val);
+
+out_psy_put:
+       power_supply_put(psy);
+out:
+       mutex_unlock(&info->lock);
+       dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
+       set_charging_fsm(info);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t pm860x_vbattery_handler(int irq, void *data)
+{
+       struct pm860x_charger_info *info = data;
+
+       mutex_lock(&info->lock);
+
+       set_vbatt_threshold(info, 0, 0);
+
+       if (info->present && info->online)
+               info->allowed = 1;
+       else
+               info->allowed = 0;
+       mutex_unlock(&info->lock);
+       dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
+
+       set_charging_fsm(info);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t pm860x_vchg_handler(int irq, void *data)
+{
+       struct pm860x_charger_info *info = data;
+       int vchg = 0;
+
+       if (info->present)
+               goto out;
+
+       measure_vchg(info, &vchg);
+
+       mutex_lock(&info->lock);
+       if (!info->online) {
+               int status;
+               /* check if over-temp on pm8606 or not */
+               status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS);
+               if (status & OVER_TEMP_FLAG) {
+                       /* clear over temp flag and set auto recover */
+                       pm860x_set_bits(info->i2c_8606, PM8606_FLAGS,
+                                       OVER_TEMP_FLAG, OVER_TEMP_FLAG);
+                       pm860x_set_bits(info->i2c_8606,
+                                       PM8606_VSYS,
+                                       OVTEMP_AUTORECOVER,
+                                       OVTEMP_AUTORECOVER);
+                       dev_dbg(info->dev,
+                               "%s, pm8606 over-temp occurred\n", __func__);
+               }
+       }
+
+       if (vchg > VCHG_NORMAL_CHECK) {
+               set_vchg_threshold(info, VCHG_OVP_LOW, 0);
+               info->allowed = 0;
+               dev_dbg(info->dev,
+                       "%s,pm8607 over-vchg occurred,vchg = %dmv\n",
+                       __func__, vchg);
+       } else if (vchg < VCHG_OVP_LOW) {
+               set_vchg_threshold(info, VCHG_NORMAL_LOW,
+                                  VCHG_NORMAL_HIGH);
+               info->allowed = 1;
+               dev_dbg(info->dev,
+                       "%s,pm8607 over-vchg recover,vchg = %dmv\n",
+                       __func__, vchg);
+       }
+       mutex_unlock(&info->lock);
+
+       dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
+       set_charging_fsm(info);
+out:
+       return IRQ_HANDLED;
+}
+
+static int pm860x_usb_get_prop(struct power_supply *psy,
+                              enum power_supply_property psp,
+                              union power_supply_propval *val)
+{
+       struct pm860x_charger_info *info = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (info->state == FSM_FASTCHARGE ||
+                               info->state == FSM_PRECHARGE)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = info->online;
+               break;
+       default:
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static enum power_supply_property pm860x_usb_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int pm860x_init_charger(struct pm860x_charger_info *info)
+{
+       int ret;
+
+       ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&info->lock);
+       info->state = FSM_INIT;
+       if (ret & STATUS2_CHG) {
+               info->online = 1;
+               info->allowed = 1;
+       } else {
+               info->online = 0;
+               info->allowed = 0;
+       }
+       mutex_unlock(&info->lock);
+
+       set_charging_fsm(info);
+       return 0;
+}
+
+static struct pm860x_irq_desc {
+       const char *name;
+       irqreturn_t (*handler)(int irq, void *data);
+} pm860x_irq_descs[] = {
+       { "usb supply detect", pm860x_charger_handler },
+       { "charge done", pm860x_done_handler },
+       { "charge timeout", pm860x_exception_handler },
+       { "charge fault", pm860x_exception_handler },
+       { "temperature", pm860x_temp_handler },
+       { "vbatt", pm860x_vbattery_handler },
+       { "vchg", pm860x_vchg_handler },
+};
+
+static const struct power_supply_desc pm860x_charger_desc = {
+       .name           = "usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties     = pm860x_usb_props,
+       .num_properties = ARRAY_SIZE(pm860x_usb_props),
+       .get_property   = pm860x_usb_get_prop,
+};
+
+static int pm860x_charger_probe(struct platform_device *pdev)
+{
+       struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       struct power_supply_config psy_cfg = {};
+       struct pm860x_charger_info *info;
+       int ret;
+       int count;
+       int i;
+       int j;
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       count = pdev->num_resources;
+       for (i = 0, j = 0; i < count; i++) {
+               info->irq[j] = platform_get_irq(pdev, i);
+               if (info->irq[j] < 0)
+                       continue;
+               j++;
+       }
+       info->irq_nums = j;
+
+       info->chip = chip;
+       info->i2c =
+           (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+       info->i2c_8606 =
+           (chip->id == CHIP_PM8607) ? chip->companion : chip->client;
+       if (!info->i2c_8606) {
+               dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n");
+               ret = -EINVAL;
+               goto out;
+       }
+       info->dev = &pdev->dev;
+
+       /* set init value for the case we are not using battery */
+       set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW);
+
+       mutex_init(&info->lock);
+       platform_set_drvdata(pdev, info);
+
+       psy_cfg.drv_data = info;
+       psy_cfg.supplied_to = pm860x_supplied_to;
+       psy_cfg.num_supplicants = ARRAY_SIZE(pm860x_supplied_to);
+       info->usb = power_supply_register(&pdev->dev, &pm860x_charger_desc,
+                                         &psy_cfg);
+       if (IS_ERR(info->usb)) {
+               ret = PTR_ERR(info->usb);
+               goto out;
+       }
+
+       pm860x_init_charger(info);
+
+       for (i = 0; i < ARRAY_SIZE(info->irq); i++) {
+               ret = request_threaded_irq(info->irq[i], NULL,
+                       pm860x_irq_descs[i].handler,
+                       IRQF_ONESHOT, pm860x_irq_descs[i].name, info);
+               if (ret < 0) {
+                       dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+                               info->irq[i], ret);
+                       goto out_irq;
+               }
+       }
+       return 0;
+
+out_irq:
+       power_supply_unregister(info->usb);
+       while (--i >= 0)
+               free_irq(info->irq[i], info);
+out:
+       return ret;
+}
+
+static int pm860x_charger_remove(struct platform_device *pdev)
+{
+       struct pm860x_charger_info *info = platform_get_drvdata(pdev);
+       int i;
+
+       power_supply_unregister(info->usb);
+       for (i = 0; i < info->irq_nums; i++)
+               free_irq(info->irq[i], info);
+       return 0;
+}
+
+static struct platform_driver pm860x_charger_driver = {
+       .driver = {
+                  .name = "88pm860x-charger",
+       },
+       .probe = pm860x_charger_probe,
+       .remove = pm860x_charger_remove,
+};
+module_platform_driver(pm860x_charger_driver);
+
+MODULE_DESCRIPTION("Marvell 88PM860x Charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
new file mode 100644 (file)
index 0000000..76806a0
--- /dev/null
@@ -0,0 +1,514 @@
+menuconfig POWER_SUPPLY
+       bool "Power supply class support"
+       help
+         Say Y here to enable power supply class support. This allows
+         power supply (batteries, AC, USB) monitoring by userspace
+         via sysfs and uevent (if available) and/or APM kernel interface
+         (if selected below).
+
+if POWER_SUPPLY
+
+config POWER_SUPPLY_DEBUG
+       bool "Power supply debug"
+       help
+         Say Y here to enable debugging messages for power supply class
+         and drivers.
+
+config PDA_POWER
+       tristate "Generic PDA/phone power driver"
+       depends on !S390
+       help
+         Say Y here to enable generic power driver for PDAs and phones with
+         one or two external power supplies (AC/USB) connected to main and
+         backup batteries, and optional builtin charger.
+
+config APM_POWER
+       tristate "APM emulation for class batteries"
+       depends on APM_EMULATION
+       help
+         Say Y here to enable support APM status emulation using
+         battery class devices.
+
+config GENERIC_ADC_BATTERY
+       tristate "Generic battery support using IIO"
+       depends on IIO
+       help
+         Say Y here to enable support for the generic battery driver
+         which uses IIO framework to read adc.
+
+config MAX8925_POWER
+       tristate "MAX8925 battery charger support"
+       depends on MFD_MAX8925
+       help
+         Say Y here to enable support for the battery charger in the Maxim
+         MAX8925 PMIC.
+
+config WM831X_BACKUP
+       tristate "WM831X backup battery charger support"
+       depends on MFD_WM831X
+       help
+         Say Y here to enable support for the backup battery charger
+         in the Wolfson Microelectronics WM831x PMICs.
+
+config WM831X_POWER
+       tristate "WM831X PMU support"
+       depends on MFD_WM831X
+       help
+         Say Y here to enable support for the power management unit
+         provided by Wolfson Microelectronics WM831x PMICs.
+
+config WM8350_POWER
+        tristate "WM8350 PMU support"
+        depends on MFD_WM8350
+        help
+          Say Y here to enable support for the power management unit
+         provided by the Wolfson Microelectronics WM8350 PMIC.
+
+config TEST_POWER
+       tristate "Test power driver"
+       help
+         This driver is used for testing. It's safe to say M here.
+
+config BATTERY_88PM860X
+       tristate "Marvell 88PM860x battery driver"
+       depends on MFD_88PM860X
+       help
+         Say Y here to enable battery monitor for Marvell 88PM860x chip.
+
+config BATTERY_ACT8945A
+       tristate "Active-semi ACT8945A charger driver"
+       depends on MFD_ACT8945A || COMPILE_TEST
+       help
+         Say Y here to enable support for power supply provided by
+         Active-semi ActivePath ACT8945A charger.
+
+config BATTERY_DS2760
+       tristate "DS2760 battery driver (HP iPAQ & others)"
+       depends on W1 && W1_SLAVE_DS2760
+       help
+         Say Y here to enable support for batteries with ds2760 chip.
+
+config BATTERY_DS2780
+       tristate "DS2780 battery driver"
+       depends on HAS_IOMEM
+       select W1
+       select W1_SLAVE_DS2780
+       help
+         Say Y here to enable support for batteries with ds2780 chip.
+
+config BATTERY_DS2781
+       tristate "DS2781 battery driver"
+       depends on HAS_IOMEM
+       select W1
+       select W1_SLAVE_DS2781
+       help
+         If you enable this you will have the DS2781 battery driver support.
+
+         The battery monitor chip is used in many batteries/devices
+         as the one who is responsible for charging/discharging/monitoring
+         Li+ batteries.
+
+         If you are unsure, say N.
+
+config BATTERY_DS2782
+       tristate "DS2782/DS2786 standalone gas-gauge"
+       depends on I2C
+       help
+         Say Y here to enable support for the DS2782/DS2786 standalone battery
+         gas-gauge.
+
+config BATTERY_PMU
+       tristate "Apple PMU battery"
+       depends on PPC32 && ADB_PMU
+       help
+         Say Y here to expose battery information on Apple machines
+         through the generic battery class.
+
+config BATTERY_OLPC
+       tristate "One Laptop Per Child battery"
+       depends on X86_32 && OLPC
+       help
+         Say Y to enable support for the battery on the OLPC laptop.
+
+config BATTERY_TOSA
+       tristate "Sharp SL-6000 (tosa) battery"
+       depends on MACH_TOSA && MFD_TC6393XB && TOUCHSCREEN_WM97XX
+       help
+         Say Y to enable support for the battery on the Sharp Zaurus
+         SL-6000 (tosa) models.
+
+config BATTERY_COLLIE
+       tristate "Sharp SL-5500 (collie) battery"
+       depends on SA1100_COLLIE && MCP_UCB1200
+       help
+         Say Y to enable support for the battery on the Sharp Zaurus
+         SL-5500 (collie) models.
+
+config BATTERY_IPAQ_MICRO
+       tristate "iPAQ Atmel Micro ASIC battery driver"
+       depends on MFD_IPAQ_MICRO
+       help
+         Choose this option if you want to monitor battery status on
+         Compaq/HP iPAQ h3100 and h3600.
+
+config BATTERY_WM97XX
+       bool "WM97xx generic battery driver"
+       depends on TOUCHSCREEN_WM97XX=y
+       help
+         Say Y to enable support for battery measured by WM97xx aux port.
+
+config BATTERY_SBS
+        tristate "SBS Compliant gas gauge"
+        depends on I2C
+        help
+         Say Y to include support for SBS battery driver for SBS-compliant
+         gas gauges.
+
+config BATTERY_BQ27XXX
+       tristate "BQ27xxx battery driver"
+       help
+         Say Y here to enable support for batteries with BQ27xxx chips.
+
+config BATTERY_BQ27XXX_I2C
+       tristate "BQ27xxx I2C support"
+       depends on BATTERY_BQ27XXX
+       depends on I2C
+       default y
+       help
+         Say Y here to enable support for batteries with BQ27xxx chips
+         connected over an I2C bus.
+
+config BATTERY_DA9030
+       tristate "DA9030 battery driver"
+       depends on PMIC_DA903X
+       help
+         Say Y here to enable support for batteries charger integrated into
+         DA9030 PMIC.
+
+config BATTERY_DA9052
+       tristate "Dialog DA9052 Battery"
+       depends on PMIC_DA9052
+       help
+         Say Y here to enable support for batteries charger integrated into
+         DA9052 PMIC.
+
+config CHARGER_DA9150
+       tristate "Dialog Semiconductor DA9150 Charger support"
+       depends on MFD_DA9150
+       depends on DA9150_GPADC
+       depends on IIO
+       help
+         Say Y here to enable support for charger unit of the DA9150
+         Integrated Charger & Fuel-Gauge IC.
+
+         This driver can also be built as a module. If so, the module will be
+         called da9150-charger.
+
+config BATTERY_DA9150
+       tristate "Dialog Semiconductor DA9150 Fuel Gauge support"
+       depends on MFD_DA9150
+       help
+         Say Y here to enable support for the Fuel-Gauge unit of the DA9150
+         Integrated Charger & Fuel-Gauge IC
+
+         This driver can also be built as a module. If so, the module will be
+         called da9150-fg.
+
+config AXP288_CHARGER
+       tristate "X-Powers AXP288 Charger"
+       depends on MFD_AXP20X && EXTCON_AXP288
+       help
+         Say yes here to have support X-Power AXP288 power management IC (PMIC)
+         integrated charger.
+
+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
+       help
+         MAX17040 is fuel-gauge systems for lithium-ion (Li+) batteries
+         in handheld and portable equipment. The MAX17040 is configured
+         to operate with a single lithium cell
+
+config BATTERY_MAX17042
+       tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge"
+       depends on I2C
+       select REGMAP_I2C
+       help
+         MAX17042 is fuel-gauge systems for lithium-ion (Li+) batteries
+         in handheld and portable equipment. The MAX17042 is configured
+         to operate with a single lithium cell. MAX8997 and MAX8966 are
+         multi-function devices that include fuel gauages that are compatible
+         with MAX17042. This driver also supports max17047/50 chips which are
+         improved version of max17042.
+
+config BATTERY_Z2
+       tristate "Z2 battery driver"
+       depends on I2C && MACH_ZIPIT2
+       help
+         Say Y to include support for the battery on the Zipit Z2.
+
+config BATTERY_S3C_ADC
+       tristate "Battery driver for Samsung ADC based monitoring"
+       depends on S3C_ADC
+       help
+         Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery
+
+config BATTERY_TWL4030_MADC
+       tristate "TWL4030 MADC battery driver"
+       depends on TWL4030_MADC
+       help
+         Say Y here to enable this dumb driver for batteries managed
+         through the TWL4030 MADC.
+
+config CHARGER_88PM860X
+       tristate "Marvell 88PM860x Charger driver"
+       depends on MFD_88PM860X && BATTERY_88PM860X
+       help
+         Say Y here to enable charger for Marvell 88PM860x chip.
+
+config CHARGER_PCF50633
+       tristate "NXP PCF50633 MBC"
+       depends on MFD_PCF50633
+       help
+        Say Y to include support for NXP PCF50633 Main Battery Charger.
+
+config BATTERY_JZ4740
+       tristate "Ingenic JZ4740 battery"
+       depends on MACH_JZ4740
+       depends on MFD_JZ4740_ADC
+       help
+         Say Y to enable support for the battery on Ingenic JZ4740 based
+         boards.
+
+         This driver can be build as a module. If so, the module will be
+         called jz4740-battery.
+
+config BATTERY_INTEL_MID
+       tristate "Battery driver for Intel MID platforms"
+       depends on INTEL_SCU_IPC && SPI
+       help
+         Say Y here to enable the battery driver on Intel MID
+         platforms.
+
+config BATTERY_RX51
+       tristate "Nokia RX-51 (N900) battery driver"
+       depends on TWL4030_MADC
+       help
+         Say Y here to enable support for battery information on Nokia
+         RX-51, also known as N900 tablet.
+
+config CHARGER_ISP1704
+       tristate "ISP1704 USB Charger Detection"
+       depends on USB_PHY
+       depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
+       help
+         Say Y to enable support for USB Charger Detection with
+         ISP1707/ISP1704 USB transceivers.
+
+config CHARGER_MAX8903
+       tristate "MAX8903 Battery DC-DC Charger for USB and Adapter Power"
+       help
+         Say Y to enable support for the MAX8903 DC-DC charger and sysfs.
+         The driver supports controlling charger-enable and current-limit
+         pins based on the status of charger connections with interrupt
+         handlers.
+
+config CHARGER_TWL4030
+       tristate "OMAP TWL4030 BCI charger driver"
+       depends on IIO && TWL4030_CORE
+       help
+         Say Y here to enable support for TWL4030 Battery Charge Interface.
+
+config CHARGER_LP8727
+       tristate "TI/National Semiconductor LP8727 charger driver"
+       depends on I2C
+       help
+         Say Y here to enable support for LP8727 Charger Driver.
+
+config CHARGER_LP8788
+       tristate "TI LP8788 charger driver"
+       depends on MFD_LP8788
+       depends on LP8788_ADC
+       depends on IIO
+       help
+         Say Y to enable support for the LP8788 linear charger.
+
+config CHARGER_GPIO
+       tristate "GPIO charger"
+       depends on GPIOLIB || COMPILE_TEST
+       help
+         Say Y to include support for chargers which report their online status
+         through a GPIO pin.
+
+         This driver can be build as a module. If so, the module will be
+         called gpio-charger.
+
+config CHARGER_MANAGER
+       bool "Battery charger manager for multiple chargers"
+       depends on REGULATOR
+       select EXTCON
+       help
+          Say Y to enable charger-manager support, which allows multiple
+          chargers attached to a battery and multiple batteries attached to a
+          system. The charger-manager also can monitor charging status in
+          runtime and in suspend-to-RAM by waking up the system periodically
+          with help of suspend_again support.
+
+config CHARGER_MAX14577
+       tristate "Maxim MAX14577/77836 battery charger driver"
+       depends on MFD_MAX14577
+       help
+         Say Y to enable support for the battery charger control sysfs and
+         platform data of MAX14577/77836 MUICs.
+
+config CHARGER_MAX77693
+       tristate "Maxim MAX77693 battery charger driver"
+       depends on MFD_MAX77693
+       help
+         Say Y to enable support for the Maxim MAX77693 battery charger.
+
+config CHARGER_MAX8997
+       tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
+       depends on MFD_MAX8997 && REGULATOR_MAX8997
+       help
+         Say Y to enable support for the battery charger control sysfs and
+         platform data of MAX8997/LP3974 PMICs.
+
+config CHARGER_MAX8998
+       tristate "Maxim MAX8998/LP3974 PMIC battery charger driver"
+       depends on MFD_MAX8998 && REGULATOR_MAX8998
+       help
+         Say Y to enable support for the battery charger control sysfs and
+         platform data of MAX8998/LP3974 PMICs.
+
+config CHARGER_QCOM_SMBB
+       tristate "Qualcomm Switch-Mode Battery Charger and Boost"
+       depends on MFD_SPMI_PMIC || COMPILE_TEST
+       depends on OF
+       depends on EXTCON
+       help
+         Say Y to include support for the Switch-Mode Battery Charger and
+         Boost (SMBB) hardware found in Qualcomm PM8941 PMICs.  The charger
+         is an integrated, single-cell lithium-ion battery charger.  DT
+         configuration is required for loading, see the devicetree
+         documentation for more detail.  The base name for this driver is
+         'pm8941_charger'.
+
+config CHARGER_BQ2415X
+       tristate "TI BQ2415x battery charger driver"
+       depends on I2C
+       help
+         Say Y to enable support for the TI BQ2415x battery charger
+         PMICs.
+
+         You'll need this driver to charge batteries on e.g. Nokia
+         RX-51/N900.
+
+config CHARGER_BQ24190
+       tristate "TI BQ24190 battery charger driver"
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
+       help
+         Say Y to enable support for the TI BQ24190 battery charger.
+
+config CHARGER_BQ24257
+       tristate "TI BQ24250/24251/24257 battery charger driver"
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
+       depends on REGMAP_I2C
+       help
+         Say Y to enable support for the TI BQ24250, BQ24251, and BQ24257 battery
+         chargers.
+
+config CHARGER_BQ24735
+       tristate "TI BQ24735 battery charger support"
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
+       help
+         Say Y to enable support for the TI BQ24735 battery charger.
+
+config CHARGER_BQ25890
+       tristate "TI BQ25890 battery charger driver"
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
+       select REGMAP_I2C
+       help
+         Say Y to enable support for the TI BQ25890 battery charger.
+
+config CHARGER_SMB347
+       tristate "Summit Microelectronics SMB347 Battery Charger"
+       depends on I2C
+       select REGMAP_I2C
+       help
+         Say Y to include support for Summit Microelectronics SMB347
+         Battery Charger.
+
+config CHARGER_TPS65090
+       tristate "TPS65090 battery charger driver"
+       depends on MFD_TPS65090
+       help
+        Say Y here to enable support for battery charging with TPS65090
+        PMIC chips.
+
+config CHARGER_TPS65217
+       tristate "TPS65217 battery charger driver"
+       depends on MFD_TPS65217
+       help
+        Say Y here to enable support for battery charging with TPS65217
+        PMIC chips.
+
+config BATTERY_GAUGE_LTC2941
+       tristate "LTC2941/LTC2943 Battery Gauge Driver"
+       depends on I2C
+       help
+         Say Y here to include support for LTC2941 and LTC2943 Battery
+         Gauge IC. The driver reports the charge count continuously, and
+         measures the voltage and temperature every 10 seconds.
+
+config AB8500_BM
+       bool "AB8500 Battery Management Driver"
+       depends on AB8500_CORE && AB8500_GPADC
+       help
+         Say Y to include support for AB8500 battery management.
+
+config BATTERY_GOLDFISH
+       tristate "Goldfish battery driver"
+       depends on GOLDFISH || COMPILE_TEST
+       depends on HAS_IOMEM
+       help
+         Say Y to enable support for the battery and AC power in the
+         Goldfish emulator.
+
+config BATTERY_RT5033
+       tristate "RT5033 fuel gauge support"
+       depends on MFD_RT5033
+       help
+         This adds support for battery fuel gauge in Richtek RT5033 PMIC.
+         The fuelgauge calculates and determines the battery state of charge
+         according to battery open circuit voltage.
+
+config CHARGER_RT9455
+       tristate "Richtek RT9455 battery charger driver"
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
+       select REGMAP_I2C
+       help
+         Say Y to enable support for Richtek RT9455 battery charger.
+
+config AXP20X_POWER
+       tristate "AXP20x power supply driver"
+       depends on MFD_AXP20X
+       help
+         This driver provides support for the power supply features of
+         AXP20x PMIC.
+
+endif # POWER_SUPPLY
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
new file mode 100644 (file)
index 0000000..36c599d
--- /dev/null
@@ -0,0 +1,74 @@
+subdir-ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG
+
+power_supply-y                         := power_supply_core.o
+power_supply-$(CONFIG_SYSFS)           += power_supply_sysfs.o
+power_supply-$(CONFIG_LEDS_TRIGGERS)   += power_supply_leds.o
+
+obj-$(CONFIG_POWER_SUPPLY)     += power_supply.o
+obj-$(CONFIG_GENERIC_ADC_BATTERY)      += generic-adc-battery.o
+
+obj-$(CONFIG_PDA_POWER)                += pda_power.o
+obj-$(CONFIG_APM_POWER)                += apm_power.o
+obj-$(CONFIG_AXP20X_POWER)     += axp20x_usb_power.o
+obj-$(CONFIG_MAX8925_POWER)    += max8925_power.o
+obj-$(CONFIG_WM831X_BACKUP)    += wm831x_backup.o
+obj-$(CONFIG_WM831X_POWER)     += wm831x_power.o
+obj-$(CONFIG_WM8350_POWER)     += wm8350_power.o
+obj-$(CONFIG_TEST_POWER)       += test_power.o
+
+obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
+obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
+obj-$(CONFIG_BATTERY_DS2760)   += ds2760_battery.o
+obj-$(CONFIG_BATTERY_DS2780)   += ds2780_battery.o
+obj-$(CONFIG_BATTERY_DS2781)   += ds2781_battery.o
+obj-$(CONFIG_BATTERY_DS2782)   += ds2782_battery.o
+obj-$(CONFIG_BATTERY_GAUGE_LTC2941)    += ltc2941-battery-gauge.o
+obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
+obj-$(CONFIG_BATTERY_PMU)      += pmu_battery.o
+obj-$(CONFIG_BATTERY_OLPC)     += olpc_battery.o
+obj-$(CONFIG_BATTERY_TOSA)     += tosa_battery.o
+obj-$(CONFIG_BATTERY_COLLIE)   += collie_battery.o
+obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
+obj-$(CONFIG_BATTERY_WM97XX)   += wm97xx_battery.o
+obj-$(CONFIG_BATTERY_SBS)      += sbs-battery.o
+obj-$(CONFIG_BATTERY_BQ27XXX)  += bq27xxx_battery.o
+obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
+obj-$(CONFIG_BATTERY_DA9030)   += da9030_battery.o
+obj-$(CONFIG_BATTERY_DA9052)   += da9052-battery.o
+obj-$(CONFIG_CHARGER_DA9150)   += da9150-charger.o
+obj-$(CONFIG_BATTERY_DA9150)   += da9150-fg.o
+obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
+obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
+obj-$(CONFIG_BATTERY_Z2)       += z2_battery.o
+obj-$(CONFIG_BATTERY_RT5033)   += rt5033_battery.o
+obj-$(CONFIG_CHARGER_RT9455)   += rt9455_charger.o
+obj-$(CONFIG_BATTERY_S3C_ADC)  += s3c_adc_battery.o
+obj-$(CONFIG_BATTERY_TWL4030_MADC)     += twl4030_madc_battery.o
+obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
+obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
+obj-$(CONFIG_BATTERY_JZ4740)   += jz4740-battery.o
+obj-$(CONFIG_BATTERY_INTEL_MID)        += intel_mid_battery.o
+obj-$(CONFIG_BATTERY_RX51)     += rx51_battery.o
+obj-$(CONFIG_AB8500_BM)                += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o pm2301_charger.o
+obj-$(CONFIG_CHARGER_ISP1704)  += isp1704_charger.o
+obj-$(CONFIG_CHARGER_MAX8903)  += max8903_charger.o
+obj-$(CONFIG_CHARGER_TWL4030)  += twl4030_charger.o
+obj-$(CONFIG_CHARGER_LP8727)   += lp8727_charger.o
+obj-$(CONFIG_CHARGER_LP8788)   += lp8788-charger.o
+obj-$(CONFIG_CHARGER_GPIO)     += gpio-charger.o
+obj-$(CONFIG_CHARGER_MANAGER)  += charger-manager.o
+obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o
+obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
+obj-$(CONFIG_CHARGER_MAX8997)  += max8997_charger.o
+obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
+obj-$(CONFIG_CHARGER_QCOM_SMBB)        += qcom_smbb.o
+obj-$(CONFIG_CHARGER_BQ2415X)  += bq2415x_charger.o
+obj-$(CONFIG_CHARGER_BQ24190)  += bq24190_charger.o
+obj-$(CONFIG_CHARGER_BQ24257)  += bq24257_charger.o
+obj-$(CONFIG_CHARGER_BQ24735)  += bq24735-charger.o
+obj-$(CONFIG_CHARGER_BQ25890)  += bq25890_charger.o
+obj-$(CONFIG_CHARGER_SMB347)   += smb347-charger.o
+obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
+obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
+obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
+obj-$(CONFIG_AXP288_CHARGER)   += axp288_charger.o
diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c
new file mode 100644 (file)
index 0000000..d298645
--- /dev/null
@@ -0,0 +1,605 @@
+#include <linux/export.h>
+#include <linux/power_supply.h>
+#include <linux/of.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+
+/*
+ * These are the defined batteries that uses a NTC and ID resistor placed
+ * inside of the battery pack.
+ * Note that the res_to_temp table must be strictly sorted by falling resistance
+ * values to work.
+ */
+const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
+       {-5, 53407},
+       { 0, 48594},
+       { 5, 43804},
+       {10, 39188},
+       {15, 34870},
+       {20, 30933},
+       {25, 27422},
+       {30, 24347},
+       {35, 21694},
+       {40, 19431},
+       {45, 17517},
+       {50, 15908},
+       {55, 14561},
+       {60, 13437},
+       {65, 12500},
+};
+EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor);
+
+const int ab8500_temp_tbl_a_size = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor);
+EXPORT_SYMBOL(ab8500_temp_tbl_a_size);
+
+const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
+       {-5, 200000},
+       { 0, 159024},
+       { 5, 151921},
+       {10, 144300},
+       {15, 136424},
+       {20, 128565},
+       {25, 120978},
+       {30, 113875},
+       {35, 107397},
+       {40, 101629},
+       {45,  96592},
+       {50,  92253},
+       {55,  88569},
+       {60,  85461},
+       {65,  82869},
+};
+EXPORT_SYMBOL(ab8500_temp_tbl_b_thermistor);
+
+const int ab8500_temp_tbl_b_size = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor);
+EXPORT_SYMBOL(ab8500_temp_tbl_b_size);
+
+static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
+       {4171,  100},
+       {4114,   95},
+       {4009,   83},
+       {3947,   74},
+       {3907,   67},
+       {3863,   59},
+       {3830,   56},
+       {3813,   53},
+       {3791,   46},
+       {3771,   33},
+       {3754,   25},
+       {3735,   20},
+       {3717,   17},
+       {3681,   13},
+       {3664,    8},
+       {3651,    6},
+       {3635,    5},
+       {3560,    3},
+       {3408,    1},
+       {3247,    0},
+};
+
+static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
+       {4161,  100},
+       {4124,   98},
+       {4044,   90},
+       {4003,   85},
+       {3966,   80},
+       {3933,   75},
+       {3888,   67},
+       {3849,   60},
+       {3813,   55},
+       {3787,   47},
+       {3772,   30},
+       {3751,   25},
+       {3718,   20},
+       {3681,   16},
+       {3660,   14},
+       {3589,   10},
+       {3546,    7},
+       {3495,    4},
+       {3404,    2},
+       {3250,    0},
+};
+
+static const struct abx500_v_to_cap cap_tbl[] = {
+       {4186,  100},
+       {4163,   99},
+       {4114,   95},
+       {4068,   90},
+       {3990,   80},
+       {3926,   70},
+       {3898,   65},
+       {3866,   60},
+       {3833,   55},
+       {3812,   50},
+       {3787,   40},
+       {3768,   30},
+       {3747,   25},
+       {3730,   20},
+       {3705,   15},
+       {3699,   14},
+       {3684,   12},
+       {3672,    9},
+       {3657,    7},
+       {3638,    6},
+       {3556,    4},
+       {3424,    2},
+       {3317,    1},
+       {3094,    0},
+};
+
+/*
+ * Note that the res_to_temp table must be strictly sorted by falling
+ * resistance values to work.
+ */
+static const struct abx500_res_to_temp temp_tbl[] = {
+       {-5, 214834},
+       { 0, 162943},
+       { 5, 124820},
+       {10,  96520},
+       {15,  75306},
+       {20,  59254},
+       {25,  47000},
+       {30,  37566},
+       {35,  30245},
+       {40,  24520},
+       {45,  20010},
+       {50,  16432},
+       {55,  13576},
+       {60,  11280},
+       {65,   9425},
+};
+
+/*
+ * Note that the batres_vs_temp table must be strictly sorted by falling
+ * temperature values to work.
+ */
+static const struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
+       { 40, 120},
+       { 30, 135},
+       { 20, 165},
+       { 10, 230},
+       { 00, 325},
+       {-10, 445},
+       {-20, 595},
+};
+
+/*
+ * Note that the batres_vs_temp table must be strictly sorted by falling
+ * temperature values to work.
+ */
+static const struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
+       { 60, 300},
+       { 30, 300},
+       { 20, 300},
+       { 10, 300},
+       { 00, 300},
+       {-10, 300},
+       {-20, 300},
+};
+
+/* battery resistance table for LI ION 9100 battery */
+static const struct batres_vs_temp temp_to_batres_tbl_9100[] = {
+       { 60, 180},
+       { 30, 180},
+       { 20, 180},
+       { 10, 180},
+       { 00, 180},
+       {-10, 180},
+       {-20, 180},
+};
+
+static struct abx500_battery_type bat_type_thermistor[] = {
+       [BATTERY_UNKNOWN] = {
+               /* First element always represent the UNKNOWN battery */
+               .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
+               .resis_high = 0,
+               .resis_low = 0,
+               .battery_resistance = 300,
+               .charge_full_design = 612,
+               .nominal_voltage = 3700,
+               .termination_vol = 4050,
+               .termination_curr = 200,
+               .recharge_cap = 95,
+               .normal_cur_lvl = 400,
+               .normal_vol_lvl = 4100,
+               .maint_a_cur_lvl = 400,
+               .maint_a_vol_lvl = 4050,
+               .maint_a_chg_timer_h = 60,
+               .maint_b_cur_lvl = 400,
+               .maint_b_vol_lvl = 4000,
+               .maint_b_chg_timer_h = 200,
+               .low_high_cur_lvl = 300,
+               .low_high_vol_lvl = 4000,
+               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+               .r_to_t_tbl = temp_tbl,
+               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+               .v_to_cap_tbl = cap_tbl,
+               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+               .batres_tbl = temp_to_batres_tbl_thermistor,
+       },
+       {
+               .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+               .resis_high = 53407,
+               .resis_low = 12500,
+               .battery_resistance = 300,
+               .charge_full_design = 900,
+               .nominal_voltage = 3600,
+               .termination_vol = 4150,
+               .termination_curr = 80,
+               .recharge_cap = 95,
+               .normal_cur_lvl = 700,
+               .normal_vol_lvl = 4200,
+               .maint_a_cur_lvl = 600,
+               .maint_a_vol_lvl = 4150,
+               .maint_a_chg_timer_h = 60,
+               .maint_b_cur_lvl = 600,
+               .maint_b_vol_lvl = 4100,
+               .maint_b_chg_timer_h = 200,
+               .low_high_cur_lvl = 300,
+               .low_high_vol_lvl = 4000,
+               .n_temp_tbl_elements = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor),
+               .r_to_t_tbl = ab8500_temp_tbl_a_thermistor,
+               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_a_thermistor),
+               .v_to_cap_tbl = cap_tbl_a_thermistor,
+               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+               .batres_tbl = temp_to_batres_tbl_thermistor,
+
+       },
+       {
+               .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+               .resis_high = 200000,
+               .resis_low = 82869,
+               .battery_resistance = 300,
+               .charge_full_design = 900,
+               .nominal_voltage = 3600,
+               .termination_vol = 4150,
+               .termination_curr = 80,
+               .recharge_cap = 95,
+               .normal_cur_lvl = 700,
+               .normal_vol_lvl = 4200,
+               .maint_a_cur_lvl = 600,
+               .maint_a_vol_lvl = 4150,
+               .maint_a_chg_timer_h = 60,
+               .maint_b_cur_lvl = 600,
+               .maint_b_vol_lvl = 4100,
+               .maint_b_chg_timer_h = 200,
+               .low_high_cur_lvl = 300,
+               .low_high_vol_lvl = 4000,
+               .n_temp_tbl_elements = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor),
+               .r_to_t_tbl = ab8500_temp_tbl_b_thermistor,
+               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_b_thermistor),
+               .v_to_cap_tbl = cap_tbl_b_thermistor,
+               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+               .batres_tbl = temp_to_batres_tbl_thermistor,
+       },
+};
+
+static struct abx500_battery_type bat_type_ext_thermistor[] = {
+       [BATTERY_UNKNOWN] = {
+               /* First element always represent the UNKNOWN battery */
+               .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
+               .resis_high = 0,
+               .resis_low = 0,
+               .battery_resistance = 300,
+               .charge_full_design = 612,
+               .nominal_voltage = 3700,
+               .termination_vol = 4050,
+               .termination_curr = 200,
+               .recharge_cap = 95,
+               .normal_cur_lvl = 400,
+               .normal_vol_lvl = 4100,
+               .maint_a_cur_lvl = 400,
+               .maint_a_vol_lvl = 4050,
+               .maint_a_chg_timer_h = 60,
+               .maint_b_cur_lvl = 400,
+               .maint_b_vol_lvl = 4000,
+               .maint_b_chg_timer_h = 200,
+               .low_high_cur_lvl = 300,
+               .low_high_vol_lvl = 4000,
+               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+               .r_to_t_tbl = temp_tbl,
+               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+               .v_to_cap_tbl = cap_tbl,
+               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+               .batres_tbl = temp_to_batres_tbl_thermistor,
+       },
+/*
+ * These are the batteries that doesn't have an internal NTC resistor to measure
+ * its temperature. The temperature in this case is measure with a NTC placed
+ * near the battery but on the PCB.
+ */
+       {
+               .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+               .resis_high = 76000,
+               .resis_low = 53000,
+               .battery_resistance = 300,
+               .charge_full_design = 900,
+               .nominal_voltage = 3700,
+               .termination_vol = 4150,
+               .termination_curr = 100,
+               .recharge_cap = 95,
+               .normal_cur_lvl = 700,
+               .normal_vol_lvl = 4200,
+               .maint_a_cur_lvl = 600,
+               .maint_a_vol_lvl = 4150,
+               .maint_a_chg_timer_h = 60,
+               .maint_b_cur_lvl = 600,
+               .maint_b_vol_lvl = 4100,
+               .maint_b_chg_timer_h = 200,
+               .low_high_cur_lvl = 300,
+               .low_high_vol_lvl = 4000,
+               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+               .r_to_t_tbl = temp_tbl,
+               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+               .v_to_cap_tbl = cap_tbl,
+               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+               .batres_tbl = temp_to_batres_tbl_thermistor,
+       },
+       {
+               .name = POWER_SUPPLY_TECHNOLOGY_LION,
+               .resis_high = 30000,
+               .resis_low = 10000,
+               .battery_resistance = 300,
+               .charge_full_design = 950,
+               .nominal_voltage = 3700,
+               .termination_vol = 4150,
+               .termination_curr = 100,
+               .recharge_cap = 95,
+               .normal_cur_lvl = 700,
+               .normal_vol_lvl = 4200,
+               .maint_a_cur_lvl = 600,
+               .maint_a_vol_lvl = 4150,
+               .maint_a_chg_timer_h = 60,
+               .maint_b_cur_lvl = 600,
+               .maint_b_vol_lvl = 4100,
+               .maint_b_chg_timer_h = 200,
+               .low_high_cur_lvl = 300,
+               .low_high_vol_lvl = 4000,
+               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+               .r_to_t_tbl = temp_tbl,
+               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+               .v_to_cap_tbl = cap_tbl,
+               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+               .batres_tbl = temp_to_batres_tbl_thermistor,
+       },
+       {
+               .name = POWER_SUPPLY_TECHNOLOGY_LION,
+               .resis_high = 95000,
+               .resis_low = 76001,
+               .battery_resistance = 300,
+               .charge_full_design = 950,
+               .nominal_voltage = 3700,
+               .termination_vol = 4150,
+               .termination_curr = 100,
+               .recharge_cap = 95,
+               .normal_cur_lvl = 700,
+               .normal_vol_lvl = 4200,
+               .maint_a_cur_lvl = 600,
+               .maint_a_vol_lvl = 4150,
+               .maint_a_chg_timer_h = 60,
+               .maint_b_cur_lvl = 600,
+               .maint_b_vol_lvl = 4100,
+               .maint_b_chg_timer_h = 200,
+               .low_high_cur_lvl = 300,
+               .low_high_vol_lvl = 4000,
+               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+               .r_to_t_tbl = temp_tbl,
+               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+               .v_to_cap_tbl = cap_tbl,
+               .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+               .batres_tbl = temp_to_batres_tbl_thermistor,
+       },
+};
+
+static const struct abx500_bm_capacity_levels cap_levels = {
+       .critical       = 2,
+       .low            = 10,
+       .normal         = 70,
+       .high           = 95,
+       .full           = 100,
+};
+
+static const struct abx500_fg_parameters fg = {
+       .recovery_sleep_timer = 10,
+       .recovery_total_time = 100,
+       .init_timer = 1,
+       .init_discard_time = 5,
+       .init_total_time = 40,
+       .high_curr_time = 60,
+       .accu_charging = 30,
+       .accu_high_curr = 30,
+       .high_curr_threshold = 50,
+       .lowbat_threshold = 3100,
+       .battok_falling_th_sel0 = 2860,
+       .battok_raising_th_sel1 = 2860,
+       .maint_thres = 95,
+       .user_cap_limit = 15,
+       .pcut_enable = 1,
+       .pcut_max_time = 127,
+       .pcut_flag_time = 112,
+       .pcut_max_restart = 15,
+       .pcut_debounce_time = 2,
+};
+
+static const struct abx500_maxim_parameters ab8500_maxi_params = {
+       .ena_maxi = true,
+       .chg_curr = 910,
+       .wait_cycles = 10,
+       .charger_curr_step = 100,
+};
+
+static const struct abx500_maxim_parameters abx540_maxi_params = {
+        .ena_maxi = true,
+        .chg_curr = 3000,
+        .wait_cycles = 10,
+        .charger_curr_step = 200,
+};
+
+static const struct abx500_bm_charger_parameters chg = {
+       .usb_volt_max           = 5500,
+       .usb_curr_max           = 1500,
+       .ac_volt_max            = 7500,
+       .ac_curr_max            = 1500,
+};
+
+/*
+ * This array maps the raw hex value to charger output current used by the
+ * AB8500 values
+ */
+static int ab8500_charge_output_curr_map[] = {
+        100,    200,    300,    400,    500,    600,    700,    800,
+        900,    1000,   1100,   1200,   1300,   1400,   1500,   1500,
+};
+
+static int ab8540_charge_output_curr_map[] = {
+        0,      0,      0,      75,     100,    125,    150,    175,
+        200,    225,    250,    275,    300,    325,    350,    375,
+        400,    425,    450,    475,    500,    525,    550,    575,
+        600,    625,    650,    675,    700,    725,    750,    775,
+        800,    825,    850,    875,    900,    925,    950,    975,
+        1000,   1025,   1050,   1075,   1100,   1125,   1150,   1175,
+        1200,   1225,   1250,   1275,   1300,   1325,   1350,   1375,
+        1400,   1425,   1450,   1500,   1600,   1700,   1900,   2000,
+};
+
+/*
+ * This array maps the raw hex value to charger input current used by the
+ * AB8500 values
+ */
+static int ab8500_charge_input_curr_map[] = {
+        50,     98,     193,    290,    380,    450,    500,    600,
+        700,    800,    900,    1000,   1100,   1300,   1400,   1500,
+};
+
+static int ab8540_charge_input_curr_map[] = {
+        25,     50,     75,     100,    125,    150,    175,    200,
+        225,    250,    275,    300,    325,    350,    375,    400,
+        425,    450,    475,    500,    525,    550,    575,    600,
+        625,    650,    675,    700,    725,    750,    775,    800,
+        825,    850,    875,    900,    925,    950,    975,    1000,
+        1025,   1050,   1075,   1100,   1125,   1150,   1175,   1200,
+        1225,   1250,   1275,   1300,   1325,   1350,   1375,   1400,
+        1425,   1450,   1475,   1500,   1500,   1500,   1500,   1500,
+};
+
+struct abx500_bm_data ab8500_bm_data = {
+       .temp_under             = 3,
+       .temp_low               = 8,
+       .temp_high              = 43,
+       .temp_over              = 48,
+       .main_safety_tmr_h      = 4,
+       .temp_interval_chg      = 20,
+       .temp_interval_nochg    = 120,
+       .usb_safety_tmr_h       = 4,
+       .bkup_bat_v             = BUP_VCH_SEL_2P6V,
+       .bkup_bat_i             = BUP_ICH_SEL_150UA,
+       .no_maintenance         = false,
+       .capacity_scaling       = false,
+       .adc_therm              = ABx500_ADC_THERM_BATCTRL,
+       .chg_unknown_bat        = false,
+       .enable_overshoot       = false,
+       .fg_res                 = 100,
+       .cap_levels             = &cap_levels,
+       .bat_type               = bat_type_thermistor,
+       .n_btypes               = ARRAY_SIZE(bat_type_thermistor),
+       .batt_id                = 0,
+       .interval_charging      = 5,
+       .interval_not_charging  = 120,
+       .temp_hysteresis        = 3,
+       .gnd_lift_resistance    = 34,
+       .chg_output_curr        = ab8500_charge_output_curr_map,
+       .n_chg_out_curr         = ARRAY_SIZE(ab8500_charge_output_curr_map),
+       .maxi                   = &ab8500_maxi_params,
+       .chg_params             = &chg,
+       .fg_params              = &fg,
+        .chg_input_curr         = ab8500_charge_input_curr_map,
+        .n_chg_in_curr          = ARRAY_SIZE(ab8500_charge_input_curr_map),
+};
+
+struct abx500_bm_data ab8540_bm_data = {
+        .temp_under             = 3,
+        .temp_low               = 8,
+        .temp_high              = 43,
+        .temp_over              = 48,
+        .main_safety_tmr_h      = 4,
+        .temp_interval_chg      = 20,
+        .temp_interval_nochg    = 120,
+        .usb_safety_tmr_h       = 4,
+        .bkup_bat_v             = BUP_VCH_SEL_2P6V,
+        .bkup_bat_i             = BUP_ICH_SEL_150UA,
+        .no_maintenance         = false,
+        .capacity_scaling       = false,
+        .adc_therm              = ABx500_ADC_THERM_BATCTRL,
+        .chg_unknown_bat        = false,
+        .enable_overshoot       = false,
+        .fg_res                 = 100,
+        .cap_levels             = &cap_levels,
+        .bat_type               = bat_type_thermistor,
+        .n_btypes               = ARRAY_SIZE(bat_type_thermistor),
+        .batt_id                = 0,
+        .interval_charging      = 5,
+        .interval_not_charging  = 120,
+        .temp_hysteresis        = 3,
+        .gnd_lift_resistance    = 0,
+        .maxi                   = &abx540_maxi_params,
+        .chg_params             = &chg,
+        .fg_params              = &fg,
+        .chg_output_curr        = ab8540_charge_output_curr_map,
+        .n_chg_out_curr         = ARRAY_SIZE(ab8540_charge_output_curr_map),
+        .chg_input_curr         = ab8540_charge_input_curr_map,
+        .n_chg_in_curr          = ARRAY_SIZE(ab8540_charge_input_curr_map),
+};
+
+int ab8500_bm_of_probe(struct device *dev,
+                      struct device_node *np,
+                      struct abx500_bm_data *bm)
+{
+       const struct batres_vs_temp *tmp_batres_tbl;
+       struct device_node *battery_node;
+       const char *btech;
+       int i;
+
+       /* get phandle to 'battery-info' node */
+       battery_node = of_parse_phandle(np, "battery", 0);
+       if (!battery_node) {
+               dev_err(dev, "battery node or reference missing\n");
+               return -EINVAL;
+       }
+
+       btech = of_get_property(battery_node, "stericsson,battery-type", NULL);
+       if (!btech) {
+               dev_warn(dev, "missing property battery-name/type\n");
+               return -EINVAL;
+       }
+
+       if (strncmp(btech, "LION", 4) == 0) {
+               bm->no_maintenance  = true;
+               bm->chg_unknown_bat = true;
+               bm->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
+               bm->bat_type[BATTERY_UNKNOWN].termination_vol    = 4150;
+               bm->bat_type[BATTERY_UNKNOWN].recharge_cap       = 95;
+               bm->bat_type[BATTERY_UNKNOWN].normal_cur_lvl     = 520;
+               bm->bat_type[BATTERY_UNKNOWN].normal_vol_lvl     = 4200;
+       }
+
+       if (of_property_read_bool(battery_node, "thermistor-on-batctrl")) {
+               if (strncmp(btech, "LION", 4) == 0)
+                       tmp_batres_tbl = temp_to_batres_tbl_9100;
+               else
+                       tmp_batres_tbl = temp_to_batres_tbl_thermistor;
+       } else {
+               bm->n_btypes   = 4;
+               bm->bat_type   = bat_type_ext_thermistor;
+               bm->adc_therm  = ABx500_ADC_THERM_BATTEMP;
+               tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
+       }
+
+       /* select the battery resolution table */
+       for (i = 0; i < bm->n_btypes; ++i)
+               bm->bat_type[i].batres_tbl = tmp_batres_tbl;
+
+       of_node_put(battery_node);
+
+       return 0;
+}
diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c
new file mode 100644 (file)
index 0000000..bf2e5dd
--- /dev/null
@@ -0,0 +1,1212 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Battery temperature driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ *     Johan Palsson <johan.palsson@stericsson.com>
+ *     Karl Komierowski <karl.komierowski@stericsson.com>
+ *     Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/of.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+
+#define VTVOUT_V                       1800
+
+#define BTEMP_THERMAL_LOW_LIMIT                -10
+#define BTEMP_THERMAL_MED_LIMIT                0
+#define BTEMP_THERMAL_HIGH_LIMIT_52    52
+#define BTEMP_THERMAL_HIGH_LIMIT_57    57
+#define BTEMP_THERMAL_HIGH_LIMIT_62    62
+
+#define BTEMP_BATCTRL_CURR_SRC_7UA     7
+#define BTEMP_BATCTRL_CURR_SRC_20UA    20
+
+#define BTEMP_BATCTRL_CURR_SRC_16UA    16
+#define BTEMP_BATCTRL_CURR_SRC_18UA    18
+
+#define BTEMP_BATCTRL_CURR_SRC_60UA    60
+#define BTEMP_BATCTRL_CURR_SRC_120UA   120
+
+/**
+ * struct ab8500_btemp_interrupts - ab8500 interrupts
+ * @name:      name of the interrupt
+ * @isr                function pointer to the isr
+ */
+struct ab8500_btemp_interrupts {
+       char *name;
+       irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab8500_btemp_events {
+       bool batt_rem;
+       bool btemp_high;
+       bool btemp_medhigh;
+       bool btemp_lowmed;
+       bool btemp_low;
+       bool ac_conn;
+       bool usb_conn;
+};
+
+struct ab8500_btemp_ranges {
+       int btemp_high_limit;
+       int btemp_med_limit;
+       int btemp_low_limit;
+};
+
+/**
+ * struct ab8500_btemp - ab8500 BTEMP device information
+ * @dev:               Pointer to the structure device
+ * @node:              List of AB8500 BTEMPs, hence prepared for reentrance
+ * @curr_source:       What current source we use, in uA
+ * @bat_temp:          Dispatched battery temperature in degree Celcius
+ * @prev_bat_temp      Last measured battery temperature in degree Celcius
+ * @parent:            Pointer to the struct ab8500
+ * @gpadc:             Pointer to the struct gpadc
+ * @fg:                        Pointer to the struct fg
+ * @bm:                Platform specific battery management information
+ * @btemp_psy:         Structure for BTEMP specific battery properties
+ * @events:            Structure for information about events triggered
+ * @btemp_ranges:      Battery temperature range structure
+ * @btemp_wq:          Work queue for measuring the temperature periodically
+ * @btemp_periodic_work:       Work for measuring the temperature periodically
+ * @initialized:       True if battery id read.
+ */
+struct ab8500_btemp {
+       struct device *dev;
+       struct list_head node;
+       int curr_source;
+       int bat_temp;
+       int prev_bat_temp;
+       struct ab8500 *parent;
+       struct ab8500_gpadc *gpadc;
+       struct ab8500_fg *fg;
+       struct abx500_bm_data *bm;
+       struct power_supply *btemp_psy;
+       struct ab8500_btemp_events events;
+       struct ab8500_btemp_ranges btemp_ranges;
+       struct workqueue_struct *btemp_wq;
+       struct delayed_work btemp_periodic_work;
+       bool initialized;
+};
+
+/* BTEMP power supply properties */
+static enum power_supply_property ab8500_btemp_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static LIST_HEAD(ab8500_btemp_list);
+
+/**
+ * ab8500_btemp_get() - returns a reference to the primary AB8500 BTEMP
+ * (i.e. the first BTEMP in the instance list)
+ */
+struct ab8500_btemp *ab8500_btemp_get(void)
+{
+       struct ab8500_btemp *btemp;
+       btemp = list_first_entry(&ab8500_btemp_list, struct ab8500_btemp, node);
+
+       return btemp;
+}
+EXPORT_SYMBOL(ab8500_btemp_get);
+
+/**
+ * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
+ * @di:                pointer to the ab8500_btemp structure
+ * @v_batctrl: measured batctrl voltage
+ * @inst_curr: measured instant current
+ *
+ * This function returns the battery resistance that is
+ * derived from the BATCTRL voltage.
+ * Returns value in Ohms.
+ */
+static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
+       int v_batctrl, int inst_curr)
+{
+       int rbs;
+
+       if (is_ab8500_1p1_or_earlier(di->parent)) {
+               /*
+                * For ABB cut1.0 and 1.1 BAT_CTRL is internally
+                * connected to 1.8V through a 450k resistor
+                */
+               return (450000 * (v_batctrl)) / (1800 - v_batctrl);
+       }
+
+       if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
+               /*
+                * If the battery has internal NTC, we use the current
+                * source to calculate the resistance.
+                */
+               rbs = (v_batctrl * 1000
+                      - di->bm->gnd_lift_resistance * inst_curr)
+                     / di->curr_source;
+       } else {
+               /*
+                * BAT_CTRL is internally
+                * connected to 1.8V through a 80k resistor
+                */
+               rbs = (80000 * (v_batctrl)) / (1800 - v_batctrl);
+       }
+
+       return rbs;
+}
+
+/**
+ * ab8500_btemp_read_batctrl_voltage() - measure batctrl voltage
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * This function returns the voltage on BATCTRL. Returns value in mV.
+ */
+static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di)
+{
+       int vbtemp;
+       static int prev;
+
+       vbtemp = ab8500_gpadc_convert(di->gpadc, BAT_CTRL);
+       if (vbtemp < 0) {
+               dev_err(di->dev,
+                       "%s gpadc conversion failed, using previous value",
+                       __func__);
+               return prev;
+       }
+       prev = vbtemp;
+       return vbtemp;
+}
+
+/**
+ * ab8500_btemp_curr_source_enable() - enable/disable batctrl current source
+ * @di:                pointer to the ab8500_btemp structure
+ * @enable:    enable or disable the current source
+ *
+ * Enable or disable the current sources for the BatCtrl AD channel
+ */
+static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
+       bool enable)
+{
+       int curr;
+       int ret = 0;
+
+       /*
+        * BATCTRL current sources are included on AB8500 cut2.0
+        * and future versions
+        */
+       if (is_ab8500_1p1_or_earlier(di->parent))
+               return 0;
+
+       /* Only do this for batteries with internal NTC */
+       if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
+
+               if (is_ab8540(di->parent)) {
+                       if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_60UA)
+                               curr = BAT_CTRL_60U_ENA;
+                       else
+                               curr = BAT_CTRL_120U_ENA;
+               } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+                       if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA)
+                               curr = BAT_CTRL_16U_ENA;
+                       else
+                               curr = BAT_CTRL_18U_ENA;
+               } else {
+                       if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
+                               curr = BAT_CTRL_7U_ENA;
+                       else
+                               curr = BAT_CTRL_20U_ENA;
+               }
+
+               dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       FORCE_BAT_CTRL_CMP_HIGH, FORCE_BAT_CTRL_CMP_HIGH);
+               if (ret) {
+                       dev_err(di->dev, "%s failed setting cmp_force\n",
+                               __func__);
+                       return ret;
+               }
+
+               /*
+                * We have to wait one 32kHz cycle before enabling
+                * the current source, since ForceBatCtrlCmpHigh needs
+                * to be written in a separate cycle
+                */
+               udelay(32);
+
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       FORCE_BAT_CTRL_CMP_HIGH | curr);
+               if (ret) {
+                       dev_err(di->dev, "%s failed enabling current source\n",
+                               __func__);
+                       goto disable_curr_source;
+               }
+       } else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
+               dev_dbg(di->dev, "Disable BATCTRL curr source\n");
+
+               if (is_ab8540(di->parent)) {
+                       /* Write 0 to the curr bits */
+                       ret = abx500_mask_and_set_register_interruptible(
+                               di->dev,
+                               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                               BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA,
+                               ~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA));
+               } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+                       /* Write 0 to the curr bits */
+                       ret = abx500_mask_and_set_register_interruptible(
+                               di->dev,
+                               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                               BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
+                               ~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
+               } else {
+                       /* Write 0 to the curr bits */
+                       ret = abx500_mask_and_set_register_interruptible(
+                               di->dev,
+                               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                               BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+                               ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+               }
+
+               if (ret) {
+                       dev_err(di->dev, "%s failed disabling current source\n",
+                               __func__);
+                       goto disable_curr_source;
+               }
+
+               /* Enable Pull-Up and comparator */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
+                       BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
+               if (ret) {
+                       dev_err(di->dev, "%s failed enabling PU and comp\n",
+                               __func__);
+                       goto enable_pu_comp;
+               }
+
+               /*
+                * We have to wait one 32kHz cycle before disabling
+                * ForceBatCtrlCmpHigh since this needs to be written
+                * in a separate cycle
+                */
+               udelay(32);
+
+               /* Disable 'force comparator' */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
+               if (ret) {
+                       dev_err(di->dev, "%s failed disabling force comp\n",
+                               __func__);
+                       goto disable_force_comp;
+               }
+       }
+       return ret;
+
+       /*
+        * We have to try unsetting FORCE_BAT_CTRL_CMP_HIGH one more time
+        * if we got an error above
+        */
+disable_curr_source:
+       if (is_ab8540(di->parent)) {
+               /* Write 0 to the curr bits */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA,
+                       ~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA));
+       } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+               /* Write 0 to the curr bits */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
+                       ~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
+       } else {
+               /* Write 0 to the curr bits */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+                       BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+                       ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+       }
+
+       if (ret) {
+               dev_err(di->dev, "%s failed disabling current source\n",
+                       __func__);
+               return ret;
+       }
+enable_pu_comp:
+       /* Enable Pull-Up and comparator */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+               BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA,
+               BAT_CTRL_PULL_UP_ENA | BAT_CTRL_CMP_ENA);
+       if (ret) {
+               dev_err(di->dev, "%s failed enabling PU and comp\n",
+                       __func__);
+               return ret;
+       }
+
+disable_force_comp:
+       /*
+        * We have to wait one 32kHz cycle before disabling
+        * ForceBatCtrlCmpHigh since this needs to be written
+        * in a separate cycle
+        */
+       udelay(32);
+
+       /* Disable 'force comparator' */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+               FORCE_BAT_CTRL_CMP_HIGH, ~FORCE_BAT_CTRL_CMP_HIGH);
+       if (ret) {
+               dev_err(di->dev, "%s failed disabling force comp\n",
+                       __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_btemp_get_batctrl_res() - get battery resistance
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * This function returns the battery pack identification resistance.
+ * Returns value in Ohms.
+ */
+static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
+{
+       int ret;
+       int batctrl = 0;
+       int res;
+       int inst_curr;
+       int i;
+
+       /*
+        * BATCTRL current sources are included on AB8500 cut2.0
+        * and future versions
+        */
+       ret = ab8500_btemp_curr_source_enable(di, true);
+       if (ret) {
+               dev_err(di->dev, "%s curr source enabled failed\n", __func__);
+               return ret;
+       }
+
+       if (!di->fg)
+               di->fg = ab8500_fg_get();
+       if (!di->fg) {
+               dev_err(di->dev, "No fg found\n");
+               return -EINVAL;
+       }
+
+       ret = ab8500_fg_inst_curr_start(di->fg);
+
+       if (ret) {
+               dev_err(di->dev, "Failed to start current measurement\n");
+               return ret;
+       }
+
+       do {
+               msleep(20);
+       } while (!ab8500_fg_inst_curr_started(di->fg));
+
+       i = 0;
+
+       do {
+               batctrl += ab8500_btemp_read_batctrl_voltage(di);
+               i++;
+               msleep(20);
+       } while (!ab8500_fg_inst_curr_done(di->fg));
+       batctrl /= i;
+
+       ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr);
+       if (ret) {
+               dev_err(di->dev, "Failed to finalize current measurement\n");
+               return ret;
+       }
+
+       res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);
+
+       ret = ab8500_btemp_curr_source_enable(di, false);
+       if (ret) {
+               dev_err(di->dev, "%s curr source disable failed\n", __func__);
+               return ret;
+       }
+
+       dev_dbg(di->dev, "%s batctrl: %d res: %d inst_curr: %d samples: %d\n",
+               __func__, batctrl, res, inst_curr, i);
+
+       return res;
+}
+
+/**
+ * ab8500_btemp_res_to_temp() - resistance to temperature
+ * @di:                pointer to the ab8500_btemp structure
+ * @tbl:       pointer to the resiatance to temperature table
+ * @tbl_size:  size of the resistance to temperature table
+ * @res:       resistance to calculate the temperature from
+ *
+ * This function returns the battery temperature in degrees Celcius
+ * based on the NTC resistance.
+ */
+static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
+       const struct abx500_res_to_temp *tbl, int tbl_size, int res)
+{
+       int i, temp;
+       /*
+        * Calculate the formula for the straight line
+        * Simple interpolation if we are within
+        * the resistance table limits, extrapolate
+        * if resistance is outside the limits.
+        */
+       if (res > tbl[0].resist)
+               i = 0;
+       else if (res <= tbl[tbl_size - 1].resist)
+               i = tbl_size - 2;
+       else {
+               i = 0;
+               while (!(res <= tbl[i].resist &&
+                       res > tbl[i + 1].resist))
+                       i++;
+       }
+
+       temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
+               (res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
+       return temp;
+}
+
+/**
+ * ab8500_btemp_measure_temp() - measure battery temperature
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * Returns battery temperature (on success) else the previous temperature
+ */
+static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
+{
+       int temp;
+       static int prev;
+       int rbat, rntc, vntc;
+       u8 id;
+
+       id = di->bm->batt_id;
+
+       if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+                       id != BATTERY_UNKNOWN) {
+
+               rbat = ab8500_btemp_get_batctrl_res(di);
+               if (rbat < 0) {
+                       dev_err(di->dev, "%s get batctrl res failed\n",
+                               __func__);
+                       /*
+                        * Return out-of-range temperature so that
+                        * charging is stopped
+                        */
+                       return BTEMP_THERMAL_LOW_LIMIT;
+               }
+
+               temp = ab8500_btemp_res_to_temp(di,
+                       di->bm->bat_type[id].r_to_t_tbl,
+                       di->bm->bat_type[id].n_temp_tbl_elements, rbat);
+       } else {
+               vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL);
+               if (vntc < 0) {
+                       dev_err(di->dev,
+                               "%s gpadc conversion failed,"
+                               " using previous value\n", __func__);
+                       return prev;
+               }
+               /*
+                * The PCB NTC is sourced from VTVOUT via a 230kOhm
+                * resistor.
+                */
+               rntc = 230000 * vntc / (VTVOUT_V - vntc);
+
+               temp = ab8500_btemp_res_to_temp(di,
+                       di->bm->bat_type[id].r_to_t_tbl,
+                       di->bm->bat_type[id].n_temp_tbl_elements, rntc);
+               prev = temp;
+       }
+       dev_dbg(di->dev, "Battery temperature is %d\n", temp);
+       return temp;
+}
+
+/**
+ * ab8500_btemp_id() - Identify the connected battery
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * This function will try to identify the battery by reading the ID
+ * resistor. Some brands use a combined ID resistor with a NTC resistor to
+ * both be able to identify and to read the temperature of it.
+ */
+static int ab8500_btemp_id(struct ab8500_btemp *di)
+{
+       int res;
+       u8 i;
+       if (is_ab8540(di->parent))
+               di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA;
+       else if (is_ab9540(di->parent) || is_ab8505(di->parent))
+               di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
+       else
+               di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
+
+       di->bm->batt_id = BATTERY_UNKNOWN;
+
+       res =  ab8500_btemp_get_batctrl_res(di);
+       if (res < 0) {
+               dev_err(di->dev, "%s get batctrl res failed\n", __func__);
+               return -ENXIO;
+       }
+
+       /* BATTERY_UNKNOWN is defined on position 0, skip it! */
+       for (i = BATTERY_UNKNOWN + 1; i < di->bm->n_btypes; i++) {
+               if ((res <= di->bm->bat_type[i].resis_high) &&
+                       (res >= di->bm->bat_type[i].resis_low)) {
+                       dev_dbg(di->dev, "Battery detected on %s"
+                               " low %d < res %d < high: %d"
+                               " index: %d\n",
+                               di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ?
+                               "BATCTRL" : "BATTEMP",
+                               di->bm->bat_type[i].resis_low, res,
+                               di->bm->bat_type[i].resis_high, i);
+
+                       di->bm->batt_id = i;
+                       break;
+               }
+       }
+
+       if (di->bm->batt_id == BATTERY_UNKNOWN) {
+               dev_warn(di->dev, "Battery identified as unknown"
+                       ", resistance %d Ohm\n", res);
+               return -ENXIO;
+       }
+
+       /*
+        * We only have to change current source if the
+        * detected type is Type 1.
+        */
+       if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+           di->bm->batt_id == 1) {
+               if (is_ab8540(di->parent)) {
+                       dev_dbg(di->dev,
+                               "Set BATCTRL current source to 60uA\n");
+                       di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA;
+               } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+                       dev_dbg(di->dev,
+                               "Set BATCTRL current source to 16uA\n");
+                       di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
+               } else {
+                       dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
+                       di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+               }
+       }
+
+       return di->bm->batt_id;
+}
+
+/**
+ * ab8500_btemp_periodic_work() - Measuring the temperature periodically
+ * @work:      pointer to the work_struct structure
+ *
+ * Work function for measuring the temperature periodically
+ */
+static void ab8500_btemp_periodic_work(struct work_struct *work)
+{
+       int interval;
+       int bat_temp;
+       struct ab8500_btemp *di = container_of(work,
+               struct ab8500_btemp, btemp_periodic_work.work);
+
+       if (!di->initialized) {
+               /* Identify the battery */
+               if (ab8500_btemp_id(di) < 0)
+                       dev_warn(di->dev, "failed to identify the battery\n");
+       }
+
+       bat_temp = ab8500_btemp_measure_temp(di);
+       /*
+        * Filter battery temperature.
+        * Allow direct updates on temperature only if two samples result in
+        * same temperature. Else only allow 1 degree change from previous
+        * reported value in the direction of the new measurement.
+        */
+       if ((bat_temp == di->prev_bat_temp) || !di->initialized) {
+               if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {
+                       di->initialized = true;
+                       di->bat_temp = bat_temp;
+                       power_supply_changed(di->btemp_psy);
+               }
+       } else if (bat_temp < di->prev_bat_temp) {
+               di->bat_temp--;
+               power_supply_changed(di->btemp_psy);
+       } else if (bat_temp > di->prev_bat_temp) {
+               di->bat_temp++;
+               power_supply_changed(di->btemp_psy);
+       }
+       di->prev_bat_temp = bat_temp;
+
+       if (di->events.ac_conn || di->events.usb_conn)
+               interval = di->bm->temp_interval_chg;
+       else
+               interval = di->bm->temp_interval_nochg;
+
+       /* Schedule a new measurement */
+       queue_delayed_work(di->btemp_wq,
+               &di->btemp_periodic_work,
+               round_jiffies(interval * HZ));
+}
+
+/**
+ * ab8500_btemp_batctrlindb_handler() - battery removal detected
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+       dev_err(di->dev, "Battery removal detected!\n");
+
+       di->events.batt_rem = true;
+       power_supply_changed(di->btemp_psy);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_templow_handler() - battery temp lower than 10 degrees
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+
+       if (is_ab8500_3p3_or_earlier(di->parent)) {
+               dev_dbg(di->dev, "Ignore false btemp low irq"
+                       " for ABB cut 1.0, 1.1, 2.0 and 3.3\n");
+       } else {
+               dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
+
+               di->events.btemp_low = true;
+               di->events.btemp_high = false;
+               di->events.btemp_medhigh = false;
+               di->events.btemp_lowmed = false;
+               power_supply_changed(di->btemp_psy);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_temphigh_handler() - battery temp higher than max temp
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+
+       dev_crit(di->dev, "Battery temperature is higher than MAX temp\n");
+
+       di->events.btemp_high = true;
+       di->events.btemp_medhigh = false;
+       di->events.btemp_lowmed = false;
+       di->events.btemp_low = false;
+       power_supply_changed(di->btemp_psy);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_lowmed_handler() - battery temp between low and medium
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+
+       dev_dbg(di->dev, "Battery temperature is between low and medium\n");
+
+       di->events.btemp_lowmed = true;
+       di->events.btemp_medhigh = false;
+       di->events.btemp_high = false;
+       di->events.btemp_low = false;
+       power_supply_changed(di->btemp_psy);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_medhigh_handler() - battery temp between medium and high
+ * @irq:       interrupt number
+ * @_di:       void pointer that has to address of ab8500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di)
+{
+       struct ab8500_btemp *di = _di;
+
+       dev_dbg(di->dev, "Battery temperature is between medium and high\n");
+
+       di->events.btemp_medhigh = true;
+       di->events.btemp_lowmed = false;
+       di->events.btemp_high = false;
+       di->events.btemp_low = false;
+       power_supply_changed(di->btemp_psy);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_btemp_periodic() - Periodic temperature measurements
+ * @di:                pointer to the ab8500_btemp structure
+ * @enable:    enable or disable periodic temperature measurements
+ *
+ * Starts of stops periodic temperature measurements. Periodic measurements
+ * should only be done when a charger is connected.
+ */
+static void ab8500_btemp_periodic(struct ab8500_btemp *di,
+       bool enable)
+{
+       dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",
+               enable);
+       /*
+        * Make sure a new measurement is done directly by cancelling
+        * any pending work
+        */
+       cancel_delayed_work_sync(&di->btemp_periodic_work);
+
+       if (enable)
+               queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
+}
+
+/**
+ * ab8500_btemp_get_temp() - get battery temperature
+ * @di:                pointer to the ab8500_btemp structure
+ *
+ * Returns battery temperature
+ */
+int ab8500_btemp_get_temp(struct ab8500_btemp *di)
+{
+       int temp = 0;
+
+       /*
+        * The BTEMP events are not reliabe on AB8500 cut3.3
+        * and prior versions
+        */
+       if (is_ab8500_3p3_or_earlier(di->parent)) {
+               temp = di->bat_temp * 10;
+       } else {
+               if (di->events.btemp_low) {
+                       if (temp > di->btemp_ranges.btemp_low_limit)
+                               temp = di->btemp_ranges.btemp_low_limit * 10;
+                       else
+                               temp = di->bat_temp * 10;
+               } else if (di->events.btemp_high) {
+                       if (temp < di->btemp_ranges.btemp_high_limit)
+                               temp = di->btemp_ranges.btemp_high_limit * 10;
+                       else
+                               temp = di->bat_temp * 10;
+               } else if (di->events.btemp_lowmed) {
+                       if (temp > di->btemp_ranges.btemp_med_limit)
+                               temp = di->btemp_ranges.btemp_med_limit * 10;
+                       else
+                               temp = di->bat_temp * 10;
+               } else if (di->events.btemp_medhigh) {
+                       if (temp < di->btemp_ranges.btemp_med_limit)
+                               temp = di->btemp_ranges.btemp_med_limit * 10;
+                       else
+                               temp = di->bat_temp * 10;
+               } else
+                       temp = di->bat_temp * 10;
+       }
+       return temp;
+}
+EXPORT_SYMBOL(ab8500_btemp_get_temp);
+
+/**
+ * ab8500_btemp_get_batctrl_temp() - get the temperature
+ * @btemp:      pointer to the btemp structure
+ *
+ * Returns the batctrl temperature in millidegrees
+ */
+int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp)
+{
+       return btemp->bat_temp * 1000;
+}
+EXPORT_SYMBOL(ab8500_btemp_get_batctrl_temp);
+
+/**
+ * ab8500_btemp_get_property() - get the btemp properties
+ * @psy:        pointer to the power_supply structure
+ * @psp:        pointer to the power_supply_property structure
+ * @val:        pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the btemp
+ * properties by reading the sysfs files.
+ * online:     presence of the battery
+ * present:    presence of the battery
+ * technology: battery technology
+ * temp:       battery temperature
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_btemp_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct ab8500_btemp *di = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+       case POWER_SUPPLY_PROP_ONLINE:
+               if (di->events.batt_rem)
+                       val->intval = 0;
+               else
+                       val->intval = 1;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = di->bm->bat_type[di->bm->batt_id].name;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = ab8500_btemp_get_temp(di);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
+{
+       struct power_supply *psy;
+       struct power_supply *ext = dev_get_drvdata(dev);
+       const char **supplicants = (const char **)ext->supplied_to;
+       struct ab8500_btemp *di;
+       union power_supply_propval ret;
+       int j;
+
+       psy = (struct power_supply *)data;
+       di = power_supply_get_drvdata(psy);
+
+       /*
+        * For all psy where the name of your driver
+        * appears in any supplied_to
+        */
+       j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
+       if (j < 0)
+               return 0;
+
+       /* Go through all properties for the psy */
+       for (j = 0; j < ext->desc->num_properties; j++) {
+               enum power_supply_property prop;
+               prop = ext->desc->properties[j];
+
+               if (power_supply_get_property(ext, prop, &ret))
+                       continue;
+
+               switch (prop) {
+               case POWER_SUPPLY_PROP_PRESENT:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               /* AC disconnected */
+                               if (!ret.intval && di->events.ac_conn) {
+                                       di->events.ac_conn = false;
+                               }
+                               /* AC connected */
+                               else if (ret.intval && !di->events.ac_conn) {
+                                       di->events.ac_conn = true;
+                                       if (!di->events.usb_conn)
+                                               ab8500_btemp_periodic(di, true);
+                               }
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               /* USB disconnected */
+                               if (!ret.intval && di->events.usb_conn) {
+                                       di->events.usb_conn = false;
+                               }
+                               /* USB connected */
+                               else if (ret.intval && !di->events.usb_conn) {
+                                       di->events.usb_conn = true;
+                                       if (!di->events.ac_conn)
+                                               ab8500_btemp_periodic(di, true);
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * ab8500_btemp_external_power_changed() - callback for power supply changes
+ * @psy:       pointer to the structure power_supply
+ *
+ * This function is pointing to the function pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in the external power
+ * supply to the btemp.
+ */
+static void ab8500_btemp_external_power_changed(struct power_supply *psy)
+{
+       struct ab8500_btemp *di = power_supply_get_drvdata(psy);
+
+       class_for_each_device(power_supply_class, NULL,
+               di->btemp_psy, ab8500_btemp_get_ext_psy_data);
+}
+
+/* ab8500 btemp driver interrupts and their respective isr */
+static struct ab8500_btemp_interrupts ab8500_btemp_irq[] = {
+       {"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler},
+       {"BTEMP_LOW", ab8500_btemp_templow_handler},
+       {"BTEMP_HIGH", ab8500_btemp_temphigh_handler},
+       {"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler},
+       {"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler},
+};
+
+#if defined(CONFIG_PM)
+static int ab8500_btemp_resume(struct platform_device *pdev)
+{
+       struct ab8500_btemp *di = platform_get_drvdata(pdev);
+
+       ab8500_btemp_periodic(di, true);
+
+       return 0;
+}
+
+static int ab8500_btemp_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct ab8500_btemp *di = platform_get_drvdata(pdev);
+
+       ab8500_btemp_periodic(di, false);
+
+       return 0;
+}
+#else
+#define ab8500_btemp_suspend      NULL
+#define ab8500_btemp_resume       NULL
+#endif
+
+static int ab8500_btemp_remove(struct platform_device *pdev)
+{
+       struct ab8500_btemp *di = platform_get_drvdata(pdev);
+       int i, irq;
+
+       /* Disable interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+               free_irq(irq, di);
+       }
+
+       /* Delete the work queue */
+       destroy_workqueue(di->btemp_wq);
+
+       flush_scheduled_work();
+       power_supply_unregister(di->btemp_psy);
+
+       return 0;
+}
+
+static char *supply_interface[] = {
+       "ab8500_chargalg",
+       "ab8500_fg",
+};
+
+static const struct power_supply_desc ab8500_btemp_desc = {
+       .name                   = "ab8500_btemp",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = ab8500_btemp_props,
+       .num_properties         = ARRAY_SIZE(ab8500_btemp_props),
+       .get_property           = ab8500_btemp_get_property,
+       .external_power_changed = ab8500_btemp_external_power_changed,
+};
+
+static int ab8500_btemp_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct abx500_bm_data *plat = pdev->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       struct ab8500_btemp *di;
+       int irq, i, ret = 0;
+       u8 val;
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       if (!di) {
+               dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__);
+               return -ENOMEM;
+       }
+
+       if (!plat) {
+               dev_err(&pdev->dev, "no battery management data supplied\n");
+               return -EINVAL;
+       }
+       di->bm = plat;
+
+       if (np) {
+               ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get battery information\n");
+                       return ret;
+               }
+       }
+
+       /* get parent data */
+       di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
+       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+       di->initialized = false;
+
+       psy_cfg.supplied_to = supply_interface;
+       psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
+       psy_cfg.drv_data = di;
+
+       /* Create a work queue for the btemp */
+       di->btemp_wq =
+               create_singlethread_workqueue("ab8500_btemp_wq");
+       if (di->btemp_wq == NULL) {
+               dev_err(di->dev, "failed to create work queue\n");
+               return -ENOMEM;
+       }
+
+       /* Init work for measuring temperature periodically */
+       INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
+               ab8500_btemp_periodic_work);
+
+       /* Set BTEMP thermal limits. Low and Med are fixed */
+       di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
+       di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_BTEMP_HIGH_TH, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               goto free_btemp_wq;
+       }
+       switch (val) {
+       case BTEMP_HIGH_TH_57_0:
+       case BTEMP_HIGH_TH_57_1:
+               di->btemp_ranges.btemp_high_limit =
+                       BTEMP_THERMAL_HIGH_LIMIT_57;
+               break;
+       case BTEMP_HIGH_TH_52:
+               di->btemp_ranges.btemp_high_limit =
+                       BTEMP_THERMAL_HIGH_LIMIT_52;
+               break;
+       case BTEMP_HIGH_TH_62:
+               di->btemp_ranges.btemp_high_limit =
+                       BTEMP_THERMAL_HIGH_LIMIT_62;
+               break;
+       }
+
+       /* Register BTEMP power supply class */
+       di->btemp_psy = power_supply_register(di->dev, &ab8500_btemp_desc,
+                                             &psy_cfg);
+       if (IS_ERR(di->btemp_psy)) {
+               dev_err(di->dev, "failed to register BTEMP psy\n");
+               ret = PTR_ERR(di->btemp_psy);
+               goto free_btemp_wq;
+       }
+
+       /* Register interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+               ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr,
+                       IRQF_SHARED | IRQF_NO_SUSPEND,
+                       ab8500_btemp_irq[i].name, di);
+
+               if (ret) {
+                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+                               , ab8500_btemp_irq[i].name, irq, ret);
+                       goto free_irq;
+               }
+               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+                       ab8500_btemp_irq[i].name, irq, ret);
+       }
+
+       platform_set_drvdata(pdev, di);
+
+       /* Kick off periodic temperature measurements */
+       ab8500_btemp_periodic(di, true);
+       list_add_tail(&di->node, &ab8500_btemp_list);
+
+       return ret;
+
+free_irq:
+       power_supply_unregister(di->btemp_psy);
+
+       /* We also have to free all successfully registered irqs */
+       for (i = i - 1; i >= 0; i--) {
+               irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
+               free_irq(irq, di);
+       }
+free_btemp_wq:
+       destroy_workqueue(di->btemp_wq);
+       return ret;
+}
+
+static const struct of_device_id ab8500_btemp_match[] = {
+       { .compatible = "stericsson,ab8500-btemp", },
+       { },
+};
+
+static struct platform_driver ab8500_btemp_driver = {
+       .probe = ab8500_btemp_probe,
+       .remove = ab8500_btemp_remove,
+       .suspend = ab8500_btemp_suspend,
+       .resume = ab8500_btemp_resume,
+       .driver = {
+               .name = "ab8500-btemp",
+               .of_match_table = ab8500_btemp_match,
+       },
+};
+
+static int __init ab8500_btemp_init(void)
+{
+       return platform_driver_register(&ab8500_btemp_driver);
+}
+
+static void __exit ab8500_btemp_exit(void)
+{
+       platform_driver_unregister(&ab8500_btemp_driver);
+}
+
+device_initcall(ab8500_btemp_init);
+module_exit(ab8500_btemp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
+MODULE_ALIAS("platform:ab8500-btemp");
+MODULE_DESCRIPTION("AB8500 battery temperature driver");
diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c
new file mode 100644 (file)
index 0000000..30de5d4
--- /dev/null
@@ -0,0 +1,3765 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Charger driver for AB8500
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ *     Johan Palsson <johan.palsson@stericsson.com>
+ *     Karl Komierowski <karl.komierowski@stericsson.com>
+ *     Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/of.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/usb/otg.h>
+#include <linux/mutex.h>
+
+/* Charger constants */
+#define NO_PW_CONN                     0
+#define AC_PW_CONN                     1
+#define USB_PW_CONN                    2
+
+#define MAIN_WDOG_ENA                  0x01
+#define MAIN_WDOG_KICK                 0x02
+#define MAIN_WDOG_DIS                  0x00
+#define CHARG_WD_KICK                  0x01
+#define MAIN_CH_ENA                    0x01
+#define MAIN_CH_NO_OVERSHOOT_ENA_N     0x02
+#define USB_CH_ENA                     0x01
+#define USB_CHG_NO_OVERSHOOT_ENA_N     0x02
+#define MAIN_CH_DET                    0x01
+#define MAIN_CH_CV_ON                  0x04
+#define USB_CH_CV_ON                   0x08
+#define VBUS_DET_DBNC100               0x02
+#define VBUS_DET_DBNC1                 0x01
+#define OTP_ENABLE_WD                  0x01
+#define DROP_COUNT_RESET               0x01
+#define USB_CH_DET                     0x01
+
+#define MAIN_CH_INPUT_CURR_SHIFT       4
+#define VBUS_IN_CURR_LIM_SHIFT         4
+#define AB8540_VBUS_IN_CURR_LIM_SHIFT  2
+#define AUTO_VBUS_IN_CURR_LIM_SHIFT    4
+#define AB8540_AUTO_VBUS_IN_CURR_MASK  0x3F
+#define VBUS_IN_CURR_LIM_RETRY_SET_TIME        30 /* seconds */
+
+#define LED_INDICATOR_PWM_ENA          0x01
+#define LED_INDICATOR_PWM_DIS          0x00
+#define LED_IND_CUR_5MA                        0x04
+#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
+
+/* HW failure constants */
+#define MAIN_CH_TH_PROT                        0x02
+#define VBUS_CH_NOK                    0x08
+#define USB_CH_TH_PROT                 0x02
+#define VBUS_OVV_TH                    0x01
+#define MAIN_CH_NOK                    0x01
+#define VBUS_DET                       0x80
+
+#define MAIN_CH_STATUS2_MAINCHGDROP            0x80
+#define MAIN_CH_STATUS2_MAINCHARGERDETDBNC     0x40
+#define USB_CH_VBUSDROP                                0x40
+#define USB_CH_VBUSDETDBNC                     0x01
+
+/* UsbLineStatus register bit masks */
+#define AB8500_USB_LINK_STATUS         0x78
+#define AB8505_USB_LINK_STATUS         0xF8
+#define AB8500_STD_HOST_SUSP           0x18
+#define USB_LINK_STATUS_SHIFT          3
+
+/* Watchdog timeout constant */
+#define WD_TIMER                       0x30 /* 4min */
+#define WD_KICK_INTERVAL               (60 * HZ)
+
+/* Lowest charger voltage is 3.39V -> 0x4E */
+#define LOW_VOLT_REG                   0x4E
+
+/* Step up/down delay in us */
+#define STEP_UDELAY                    1000
+
+#define CHARGER_STATUS_POLL 10 /* in ms */
+
+#define CHG_WD_INTERVAL                        (60 * HZ)
+
+#define AB8500_SW_CONTROL_FALLBACK     0x03
+/* Wait for enumeration before charing in us */
+#define WAIT_ACA_RID_ENUMERATION       (5 * 1000)
+/*External charger control*/
+#define AB8500_SYS_CHARGER_CONTROL_REG         0x52
+#define EXTERNAL_CHARGER_DISABLE_REG_VAL       0x03
+#define EXTERNAL_CHARGER_ENABLE_REG_VAL                0x07
+
+/* UsbLineStatus register - usb types */
+enum ab8500_charger_link_status {
+       USB_STAT_NOT_CONFIGURED,
+       USB_STAT_STD_HOST_NC,
+       USB_STAT_STD_HOST_C_NS,
+       USB_STAT_STD_HOST_C_S,
+       USB_STAT_HOST_CHG_NM,
+       USB_STAT_HOST_CHG_HS,
+       USB_STAT_HOST_CHG_HS_CHIRP,
+       USB_STAT_DEDICATED_CHG,
+       USB_STAT_ACA_RID_A,
+       USB_STAT_ACA_RID_B,
+       USB_STAT_ACA_RID_C_NM,
+       USB_STAT_ACA_RID_C_HS,
+       USB_STAT_ACA_RID_C_HS_CHIRP,
+       USB_STAT_HM_IDGND,
+       USB_STAT_RESERVED,
+       USB_STAT_NOT_VALID_LINK,
+       USB_STAT_PHY_EN,
+       USB_STAT_SUP_NO_IDGND_VBUS,
+       USB_STAT_SUP_IDGND_VBUS,
+       USB_STAT_CHARGER_LINE_1,
+       USB_STAT_CARKIT_1,
+       USB_STAT_CARKIT_2,
+       USB_STAT_ACA_DOCK_CHARGER,
+};
+
+enum ab8500_usb_state {
+       AB8500_BM_USB_STATE_RESET_HS,   /* HighSpeed Reset */
+       AB8500_BM_USB_STATE_RESET_FS,   /* FullSpeed/LowSpeed Reset */
+       AB8500_BM_USB_STATE_CONFIGURED,
+       AB8500_BM_USB_STATE_SUSPEND,
+       AB8500_BM_USB_STATE_RESUME,
+       AB8500_BM_USB_STATE_MAX,
+};
+
+/* VBUS input current limits supported in AB8500 in mA */
+#define USB_CH_IP_CUR_LVL_0P05         50
+#define USB_CH_IP_CUR_LVL_0P09         98
+#define USB_CH_IP_CUR_LVL_0P19         193
+#define USB_CH_IP_CUR_LVL_0P29         290
+#define USB_CH_IP_CUR_LVL_0P38         380
+#define USB_CH_IP_CUR_LVL_0P45         450
+#define USB_CH_IP_CUR_LVL_0P5          500
+#define USB_CH_IP_CUR_LVL_0P6          600
+#define USB_CH_IP_CUR_LVL_0P7          700
+#define USB_CH_IP_CUR_LVL_0P8          800
+#define USB_CH_IP_CUR_LVL_0P9          900
+#define USB_CH_IP_CUR_LVL_1P0          1000
+#define USB_CH_IP_CUR_LVL_1P1          1100
+#define USB_CH_IP_CUR_LVL_1P3          1300
+#define USB_CH_IP_CUR_LVL_1P4          1400
+#define USB_CH_IP_CUR_LVL_1P5          1500
+
+#define VBAT_TRESH_IP_CUR_RED          3800
+
+#define to_ab8500_charger_usb_device_info(x) container_of((x), \
+       struct ab8500_charger, usb_chg)
+#define to_ab8500_charger_ac_device_info(x) container_of((x), \
+       struct ab8500_charger, ac_chg)
+
+/**
+ * struct ab8500_charger_interrupts - ab8500 interupts
+ * @name:      name of the interrupt
+ * @isr                function pointer to the isr
+ */
+struct ab8500_charger_interrupts {
+       char *name;
+       irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab8500_charger_info {
+       int charger_connected;
+       int charger_online;
+       int charger_voltage;
+       int cv_active;
+       bool wd_expired;
+       int charger_current;
+};
+
+struct ab8500_charger_event_flags {
+       bool mainextchnotok;
+       bool main_thermal_prot;
+       bool usb_thermal_prot;
+       bool vbus_ovv;
+       bool usbchargernotok;
+       bool chgwdexp;
+       bool vbus_collapse;
+       bool vbus_drop_end;
+};
+
+struct ab8500_charger_usb_state {
+       int usb_current;
+       int usb_current_tmp;
+       enum ab8500_usb_state state;
+       enum ab8500_usb_state state_tmp;
+       spinlock_t usb_lock;
+};
+
+struct ab8500_charger_max_usb_in_curr {
+       int usb_type_max;
+       int set_max;
+       int calculated_max;
+};
+
+/**
+ * struct ab8500_charger - ab8500 Charger device information
+ * @dev:               Pointer to the structure device
+ * @vbus_detected:     VBUS detected
+ * @vbus_detected_start:
+ *                     VBUS detected during startup
+ * @ac_conn:           This will be true when the AC charger has been plugged
+ * @vddadc_en_ac:      Indicate if VDD ADC supply is enabled because AC
+ *                     charger is enabled
+ * @vddadc_en_usb:     Indicate if VDD ADC supply is enabled because USB
+ *                     charger is enabled
+ * @vbat               Battery voltage
+ * @old_vbat           Previously measured battery voltage
+ * @usb_device_is_unrecognised USB device is unrecognised by the hardware
+ * @autopower          Indicate if we should have automatic pwron after pwrloss
+ * @autopower_cfg      platform specific power config support for "pwron after pwrloss"
+ * @invalid_charger_detect_state State when forcing AB to use invalid charger
+ * @is_aca_rid:                Incicate if accessory is ACA type
+ * @current_stepping_sessions:
+ *                     Counter for current stepping sessions
+ * @parent:            Pointer to the struct ab8500
+ * @gpadc:             Pointer to the struct gpadc
+ * @bm:                Platform specific battery management information
+ * @flags:             Structure for information about events triggered
+ * @usb_state:         Structure for usb stack information
+ * @max_usb_in_curr:   Max USB charger input current
+ * @ac_chg:            AC charger power supply
+ * @usb_chg:           USB charger power supply
+ * @ac:                        Structure that holds the AC charger properties
+ * @usb:               Structure that holds the USB charger properties
+ * @regu:              Pointer to the struct regulator
+ * @charger_wq:                Work queue for the IRQs and checking HW state
+ * @usb_ipt_crnt_lock: Lock to protect VBUS input current setting from mutuals
+ * @pm_lock:           Lock to prevent system to suspend
+ * @check_vbat_work    Work for checking vbat threshold to adjust vbus current
+ * @check_hw_failure_work:     Work for checking HW state
+ * @check_usbchgnotok_work:    Work for checking USB charger not ok status
+ * @kick_wd_work:              Work for kicking the charger watchdog in case
+ *                             of ABB rev 1.* due to the watchog logic bug
+ * @ac_charger_attached_work:  Work for checking if AC charger is still
+ *                             connected
+ * @usb_charger_attached_work: Work for checking if USB charger is still
+ *                             connected
+ * @ac_work:                   Work for checking AC charger connection
+ * @detect_usb_type_work:      Work for detecting the USB type connected
+ * @usb_link_status_work:      Work for checking the new USB link status
+ * @usb_state_changed_work:    Work for checking USB state
+ * @attach_work:               Work for detecting USB type
+ * @vbus_drop_end_work:                Work for detecting VBUS drop end
+ * @check_main_thermal_prot_work:
+ *                             Work for checking Main thermal status
+ * @check_usb_thermal_prot_work:
+ *                             Work for checking USB thermal status
+ * @charger_attached_mutex:    For controlling the wakelock
+ */
+struct ab8500_charger {
+       struct device *dev;
+       bool vbus_detected;
+       bool vbus_detected_start;
+       bool ac_conn;
+       bool vddadc_en_ac;
+       bool vddadc_en_usb;
+       int vbat;
+       int old_vbat;
+       bool usb_device_is_unrecognised;
+       bool autopower;
+       bool autopower_cfg;
+       int invalid_charger_detect_state;
+       int is_aca_rid;
+       atomic_t current_stepping_sessions;
+       struct ab8500 *parent;
+       struct ab8500_gpadc *gpadc;
+       struct abx500_bm_data *bm;
+       struct ab8500_charger_event_flags flags;
+       struct ab8500_charger_usb_state usb_state;
+       struct ab8500_charger_max_usb_in_curr max_usb_in_curr;
+       struct ux500_charger ac_chg;
+       struct ux500_charger usb_chg;
+       struct ab8500_charger_info ac;
+       struct ab8500_charger_info usb;
+       struct regulator *regu;
+       struct workqueue_struct *charger_wq;
+       struct mutex usb_ipt_crnt_lock;
+       struct delayed_work check_vbat_work;
+       struct delayed_work check_hw_failure_work;
+       struct delayed_work check_usbchgnotok_work;
+       struct delayed_work kick_wd_work;
+       struct delayed_work usb_state_changed_work;
+       struct delayed_work attach_work;
+       struct delayed_work ac_charger_attached_work;
+       struct delayed_work usb_charger_attached_work;
+       struct delayed_work vbus_drop_end_work;
+       struct work_struct ac_work;
+       struct work_struct detect_usb_type_work;
+       struct work_struct usb_link_status_work;
+       struct work_struct check_main_thermal_prot_work;
+       struct work_struct check_usb_thermal_prot_work;
+       struct usb_phy *usb_phy;
+       struct notifier_block nb;
+       struct mutex charger_attached_mutex;
+};
+
+/* AC properties */
+static enum power_supply_property ab8500_charger_ac_props[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/* USB properties */
+static enum power_supply_property ab8500_charger_usb_props[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/*
+ * Function for enabling and disabling sw fallback mode
+ * should always be disabled when no charger is connected.
+ */
+static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
+               bool fallback)
+{
+       u8 val;
+       u8 reg;
+       u8 bank;
+       u8 bit;
+       int ret;
+
+       dev_dbg(di->dev, "SW Fallback: %d\n", fallback);
+
+       if (is_ab8500(di->parent)) {
+               bank = 0x15;
+               reg = 0x0;
+               bit = 3;
+       } else {
+               bank = AB8500_SYS_CTRL1_BLOCK;
+               reg = AB8500_SW_CONTROL_FALLBACK;
+               bit = 0;
+       }
+
+       /* read the register containing fallback bit */
+       ret = abx500_get_register_interruptible(di->dev, bank, reg, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%d read failed\n", __LINE__);
+               return;
+       }
+
+       if (is_ab8500(di->parent)) {
+               /* enable the OPT emulation registers */
+               ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x2);
+               if (ret) {
+                       dev_err(di->dev, "%d write failed\n", __LINE__);
+                       goto disable_otp;
+               }
+       }
+
+       if (fallback)
+               val |= (1 << bit);
+       else
+               val &= ~(1 << bit);
+
+       /* write back the changed fallback bit value to register */
+       ret = abx500_set_register_interruptible(di->dev, bank, reg, val);
+       if (ret) {
+               dev_err(di->dev, "%d write failed\n", __LINE__);
+       }
+
+disable_otp:
+       if (is_ab8500(di->parent)) {
+               /* disable the set OTP registers again */
+               ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x0);
+               if (ret) {
+                       dev_err(di->dev, "%d write failed\n", __LINE__);
+               }
+       }
+}
+
+/**
+ * ab8500_power_supply_changed - a wrapper with local extentions for
+ * power_supply_changed
+ * @di:          pointer to the ab8500_charger structure
+ * @psy:  pointer to power_supply_that have changed.
+ *
+ */
+static void ab8500_power_supply_changed(struct ab8500_charger *di,
+                                       struct power_supply *psy)
+{
+       if (di->autopower_cfg) {
+               if (!di->usb.charger_connected &&
+                   !di->ac.charger_connected &&
+                   di->autopower) {
+                       di->autopower = false;
+                       ab8500_enable_disable_sw_fallback(di, false);
+               } else if (!di->autopower &&
+                          (di->ac.charger_connected ||
+                           di->usb.charger_connected)) {
+                       di->autopower = true;
+                       ab8500_enable_disable_sw_fallback(di, true);
+               }
+       }
+       power_supply_changed(psy);
+}
+
+static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
+       bool connected)
+{
+       if (connected != di->usb.charger_connected) {
+               dev_dbg(di->dev, "USB connected:%i\n", connected);
+               di->usb.charger_connected = connected;
+
+               if (!connected)
+                       di->flags.vbus_drop_end = false;
+
+               sysfs_notify(&di->usb_chg.psy->dev.kobj, NULL, "present");
+
+               if (connected) {
+                       mutex_lock(&di->charger_attached_mutex);
+                       mutex_unlock(&di->charger_attached_mutex);
+
+                       if (is_ab8500(di->parent))
+                               queue_delayed_work(di->charger_wq,
+                                          &di->usb_charger_attached_work,
+                                          HZ);
+               } else {
+                       cancel_delayed_work_sync(&di->usb_charger_attached_work);
+                       mutex_lock(&di->charger_attached_mutex);
+                       mutex_unlock(&di->charger_attached_mutex);
+               }
+       }
+}
+
+/**
+ * ab8500_charger_get_ac_voltage() - get ac charger voltage
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Returns ac charger voltage (on success)
+ */
+static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di)
+{
+       int vch;
+
+       /* Only measure voltage if the charger is connected */
+       if (di->ac.charger_connected) {
+               vch = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_V);
+               if (vch < 0)
+                       dev_err(di->dev, "%s gpadc conv failed,\n", __func__);
+       } else {
+               vch = 0;
+       }
+       return vch;
+}
+
+/**
+ * ab8500_charger_ac_cv() - check if the main charger is in CV mode
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Returns ac charger CV mode (on success) else error code
+ */
+static int ab8500_charger_ac_cv(struct ab8500_charger *di)
+{
+       u8 val;
+       int ret = 0;
+
+       /* Only check CV mode if the charger is online */
+       if (di->ac.charger_online) {
+               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_STATUS1_REG, &val);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return 0;
+               }
+
+               if (val & MAIN_CH_CV_ON)
+                       ret = 1;
+               else
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_get_vbus_voltage() - get vbus voltage
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * This function returns the vbus voltage.
+ * Returns vbus voltage (on success)
+ */
+static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di)
+{
+       int vch;
+
+       /* Only measure voltage if the charger is connected */
+       if (di->usb.charger_connected) {
+               vch = ab8500_gpadc_convert(di->gpadc, VBUS_V);
+               if (vch < 0)
+                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+       } else {
+               vch = 0;
+       }
+       return vch;
+}
+
+/**
+ * ab8500_charger_get_usb_current() - get usb charger current
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * This function returns the usb charger current.
+ * Returns usb current (on success) and error code on failure
+ */
+static int ab8500_charger_get_usb_current(struct ab8500_charger *di)
+{
+       int ich;
+
+       /* Only measure current if the charger is online */
+       if (di->usb.charger_online) {
+               ich = ab8500_gpadc_convert(di->gpadc, USB_CHARGER_C);
+               if (ich < 0)
+                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+       } else {
+               ich = 0;
+       }
+       return ich;
+}
+
+/**
+ * ab8500_charger_get_ac_current() - get ac charger current
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * This function returns the ac charger current.
+ * Returns ac current (on success) and error code on failure.
+ */
+static int ab8500_charger_get_ac_current(struct ab8500_charger *di)
+{
+       int ich;
+
+       /* Only measure current if the charger is online */
+       if (di->ac.charger_online) {
+               ich = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_C);
+               if (ich < 0)
+                       dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+       } else {
+               ich = 0;
+       }
+       return ich;
+}
+
+/**
+ * ab8500_charger_usb_cv() - check if the usb charger is in CV mode
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Returns ac charger CV mode (on success) else error code
+ */
+static int ab8500_charger_usb_cv(struct ab8500_charger *di)
+{
+       int ret;
+       u8 val;
+
+       /* Only check CV mode if the charger is online */
+       if (di->usb.charger_online) {
+               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_USBCH_STAT1_REG, &val);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return 0;
+               }
+
+               if (val & USB_CH_CV_ON)
+                       ret = 1;
+               else
+                       ret = 0;
+       } else {
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_detect_chargers() - Detect the connected chargers
+ * @di:                pointer to the ab8500_charger structure
+ * @probe:     if probe, don't delay and wait for HW
+ *
+ * Returns the type of charger connected.
+ * For USB it will not mean we can actually charge from it
+ * but that there is a USB cable connected that we have to
+ * identify. This is used during startup when we don't get
+ * interrupts of the charger detection
+ *
+ * Returns an integer value, that means,
+ * NO_PW_CONN  no power supply is connected
+ * AC_PW_CONN  if the AC power supply is connected
+ * USB_PW_CONN  if the USB power supply is connected
+ * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
+ */
+static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
+{
+       int result = NO_PW_CONN;
+       int ret;
+       u8 val;
+
+       /* Check for AC charger */
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CH_STATUS1_REG, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return ret;
+       }
+
+       if (val & MAIN_CH_DET)
+               result = AC_PW_CONN;
+
+       /* Check for USB charger */
+
+       if (!probe) {
+               /*
+                * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
+                * when disconnecting ACA even though no
+                * charger was connected. Try waiting a little
+                * longer than the 100 ms of VBUS_DET_DBNC100...
+                */
+               msleep(110);
+       }
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CH_USBCH_STAT1_REG, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return ret;
+       }
+       dev_dbg(di->dev,
+               "%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__,
+               val);
+       if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
+               result |= USB_PW_CONN;
+
+       return result;
+}
+
+/**
+ * ab8500_charger_max_usb_curr() - get the max curr for the USB type
+ * @di:                        pointer to the ab8500_charger structure
+ * @link_status:       the identified USB type
+ *
+ * Get the maximum current that is allowed to be drawn from the host
+ * based on the USB type.
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
+               enum ab8500_charger_link_status link_status)
+{
+       int ret = 0;
+
+       di->usb_device_is_unrecognised = false;
+
+       /*
+        * Platform only supports USB 2.0.
+        * This means that charging current from USB source
+        * is maximum 500 mA. Every occurence of USB_STAT_*_HOST_*
+        * should set USB_CH_IP_CUR_LVL_0P5.
+        */
+
+       switch (link_status) {
+       case USB_STAT_STD_HOST_NC:
+       case USB_STAT_STD_HOST_C_NS:
+       case USB_STAT_STD_HOST_C_S:
+               dev_dbg(di->dev, "USB Type - Standard host is "
+                       "detected through USB driver\n");
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
+               di->is_aca_rid = 0;
+               break;
+       case USB_STAT_HOST_CHG_HS_CHIRP:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
+               di->is_aca_rid = 0;
+               break;
+       case USB_STAT_HOST_CHG_HS:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
+               di->is_aca_rid = 0;
+               break;
+       case USB_STAT_ACA_RID_C_HS:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P9;
+               di->is_aca_rid = 0;
+               break;
+       case USB_STAT_ACA_RID_A:
+               /*
+                * Dedicated charger level minus maximum current accessory
+                * can consume (900mA). Closest level is 500mA
+                */
+               dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
+               di->is_aca_rid = 1;
+               break;
+       case USB_STAT_ACA_RID_B:
+               /*
+                * Dedicated charger level minus 120mA (20mA for ACA and
+                * 100mA for potential accessory). Closest level is 1300mA
+                */
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P3;
+               dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
+                               di->max_usb_in_curr.usb_type_max);
+               di->is_aca_rid = 1;
+               break;
+       case USB_STAT_HOST_CHG_NM:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
+               di->is_aca_rid = 0;
+               break;
+       case USB_STAT_DEDICATED_CHG:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
+               di->is_aca_rid = 0;
+               break;
+       case USB_STAT_ACA_RID_C_HS_CHIRP:
+       case USB_STAT_ACA_RID_C_NM:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
+               di->is_aca_rid = 1;
+               break;
+       case USB_STAT_NOT_CONFIGURED:
+               if (di->vbus_detected) {
+                       di->usb_device_is_unrecognised = true;
+                       dev_dbg(di->dev, "USB Type - Legacy charger.\n");
+                       di->max_usb_in_curr.usb_type_max =
+                                               USB_CH_IP_CUR_LVL_1P5;
+                       break;
+               }
+       case USB_STAT_HM_IDGND:
+               dev_err(di->dev, "USB Type - Charging not allowed\n");
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
+               ret = -ENXIO;
+               break;
+       case USB_STAT_RESERVED:
+               if (is_ab8500(di->parent)) {
+                       di->flags.vbus_collapse = true;
+                       dev_err(di->dev, "USB Type - USB_STAT_RESERVED "
+                                               "VBUS has collapsed\n");
+                       ret = -ENXIO;
+                       break;
+               } else {
+                       dev_dbg(di->dev, "USB Type - Charging not allowed\n");
+                       di->max_usb_in_curr.usb_type_max =
+                                               USB_CH_IP_CUR_LVL_0P05;
+                       dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
+                               link_status,
+                               di->max_usb_in_curr.usb_type_max);
+                       ret = -ENXIO;
+                       break;
+               }
+       case USB_STAT_CARKIT_1:
+       case USB_STAT_CARKIT_2:
+       case USB_STAT_ACA_DOCK_CHARGER:
+       case USB_STAT_CHARGER_LINE_1:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
+               dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
+                               di->max_usb_in_curr.usb_type_max);
+               break;
+       case USB_STAT_NOT_VALID_LINK:
+               dev_err(di->dev, "USB Type invalid - try charging anyway\n");
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
+               break;
+
+       default:
+               dev_err(di->dev, "USB Type - Unknown\n");
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
+               ret = -ENXIO;
+               break;
+       };
+
+       di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
+       dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
+               link_status, di->max_usb_in_curr.set_max);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_read_usb_type() - read the type of usb connected
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
+{
+       int ret;
+       u8 val;
+
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return ret;
+       }
+       if (is_ab8500(di->parent))
+               ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+                       AB8500_USB_LINE_STAT_REG, &val);
+       else
+               ret = abx500_get_register_interruptible(di->dev,
+                       AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return ret;
+       }
+
+       /* get the USB type */
+       if (is_ab8500(di->parent))
+               val = (val & AB8500_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
+       else
+               val = (val & AB8505_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
+       ret = ab8500_charger_max_usb_curr(di,
+               (enum ab8500_charger_link_status) val);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_detect_usb_type() - get the type of usb connected
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
+{
+       int i, ret;
+       u8 val;
+
+       /*
+        * On getting the VBUS rising edge detect interrupt there
+        * is a 250ms delay after which the register UsbLineStatus
+        * is filled with valid data.
+        */
+       for (i = 0; i < 10; i++) {
+               msleep(250);
+               ret = abx500_get_register_interruptible(di->dev,
+                       AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
+                       &val);
+               dev_dbg(di->dev, "%s AB8500_IT_SOURCE21_REG %x\n",
+                       __func__, val);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return ret;
+               }
+
+               if (is_ab8500(di->parent))
+                       ret = abx500_get_register_interruptible(di->dev,
+                               AB8500_USB, AB8500_USB_LINE_STAT_REG, &val);
+               else
+                       ret = abx500_get_register_interruptible(di->dev,
+                               AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return ret;
+               }
+               dev_dbg(di->dev, "%s AB8500_USB_LINE_STAT_REG %x\n", __func__,
+                       val);
+               /*
+                * Until the IT source register is read the UsbLineStatus
+                * register is not updated, hence doing the same
+                * Revisit this:
+                */
+
+               /* get the USB type */
+               if (is_ab8500(di->parent))
+                       val = (val & AB8500_USB_LINK_STATUS) >>
+                                                       USB_LINK_STATUS_SHIFT;
+               else
+                       val = (val & AB8505_USB_LINK_STATUS) >>
+                                                       USB_LINK_STATUS_SHIFT;
+               if (val)
+                       break;
+       }
+       ret = ab8500_charger_max_usb_curr(di,
+               (enum ab8500_charger_link_status) val);
+
+       return ret;
+}
+
+/*
+ * This array maps the raw hex value to charger voltage used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_charger_voltage_map[] = {
+       3500 ,
+       3525 ,
+       3550 ,
+       3575 ,
+       3600 ,
+       3625 ,
+       3650 ,
+       3675 ,
+       3700 ,
+       3725 ,
+       3750 ,
+       3775 ,
+       3800 ,
+       3825 ,
+       3850 ,
+       3875 ,
+       3900 ,
+       3925 ,
+       3950 ,
+       3975 ,
+       4000 ,
+       4025 ,
+       4050 ,
+       4060 ,
+       4070 ,
+       4080 ,
+       4090 ,
+       4100 ,
+       4110 ,
+       4120 ,
+       4130 ,
+       4140 ,
+       4150 ,
+       4160 ,
+       4170 ,
+       4180 ,
+       4190 ,
+       4200 ,
+       4210 ,
+       4220 ,
+       4230 ,
+       4240 ,
+       4250 ,
+       4260 ,
+       4270 ,
+       4280 ,
+       4290 ,
+       4300 ,
+       4310 ,
+       4320 ,
+       4330 ,
+       4340 ,
+       4350 ,
+       4360 ,
+       4370 ,
+       4380 ,
+       4390 ,
+       4400 ,
+       4410 ,
+       4420 ,
+       4430 ,
+       4440 ,
+       4450 ,
+       4460 ,
+       4470 ,
+       4480 ,
+       4490 ,
+       4500 ,
+       4510 ,
+       4520 ,
+       4530 ,
+       4540 ,
+       4550 ,
+       4560 ,
+       4570 ,
+       4580 ,
+       4590 ,
+       4600 ,
+};
+
+static int ab8500_voltage_to_regval(int voltage)
+{
+       int i;
+
+       /* Special case for voltage below 3.5V */
+       if (voltage < ab8500_charger_voltage_map[0])
+               return LOW_VOLT_REG;
+
+       for (i = 1; i < ARRAY_SIZE(ab8500_charger_voltage_map); i++) {
+               if (voltage < ab8500_charger_voltage_map[i])
+                       return i - 1;
+       }
+
+       /* If not last element, return error */
+       i = ARRAY_SIZE(ab8500_charger_voltage_map) - 1;
+       if (voltage == ab8500_charger_voltage_map[i])
+               return i;
+       else
+               return -1;
+}
+
+static int ab8500_current_to_regval(struct ab8500_charger *di, int curr)
+{
+       int i;
+
+       if (curr < di->bm->chg_output_curr[0])
+               return 0;
+
+       for (i = 0; i < di->bm->n_chg_out_curr; i++) {
+               if (curr < di->bm->chg_output_curr[i])
+                       return i - 1;
+       }
+
+       /* If not last element, return error */
+       i = di->bm->n_chg_out_curr - 1;
+       if (curr == di->bm->chg_output_curr[i])
+               return i;
+       else
+               return -1;
+}
+
+static int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr)
+{
+       int i;
+
+       if (curr < di->bm->chg_input_curr[0])
+               return 0;
+
+       for (i = 0; i < di->bm->n_chg_in_curr; i++) {
+               if (curr < di->bm->chg_input_curr[i])
+                       return i - 1;
+       }
+
+       /* If not last element, return error */
+       i = di->bm->n_chg_in_curr - 1;
+       if (curr == di->bm->chg_input_curr[i])
+               return i;
+       else
+               return -1;
+}
+
+/**
+ * ab8500_charger_get_usb_cur() - get usb current
+ * @di:                pointer to the ab8500_charger structre
+ *
+ * The usb stack provides the maximum current that can be drawn from
+ * the standard usb host. This will be in mA.
+ * This function converts current in mA to a value that can be written
+ * to the register. Returns -1 if charging is not allowed
+ */
+static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
+{
+       int ret = 0;
+       switch (di->usb_state.usb_current) {
+       case 100:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P09;
+               break;
+       case 200:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P19;
+               break;
+       case 300:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P29;
+               break;
+       case 400:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P38;
+               break;
+       case 500:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
+               break;
+       default:
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
+               ret = -EPERM;
+               break;
+       };
+       di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
+       return ret;
+}
+
+/**
+ * ab8500_charger_check_continue_stepping() - Check to allow stepping
+ * @di:                pointer to the ab8500_charger structure
+ * @reg:       select what charger register to check
+ *
+ * Check if current stepping should be allowed to continue.
+ * Checks if charger source has not collapsed. If it has, further stepping
+ * is not allowed.
+ */
+static bool ab8500_charger_check_continue_stepping(struct ab8500_charger *di,
+                                                  int reg)
+{
+       if (reg == AB8500_USBCH_IPT_CRNTLVL_REG)
+               return !di->flags.vbus_drop_end;
+       else
+               return true;
+}
+
+/**
+ * ab8500_charger_set_current() - set charger current
+ * @di:                pointer to the ab8500_charger structure
+ * @ich:       charger current, in mA
+ * @reg:       select what charger register to set
+ *
+ * Set charger current.
+ * There is no state machine in the AB to step up/down the charger
+ * current to avoid dips and spikes on MAIN, VBUS and VBAT when
+ * charging is started. Instead we need to implement
+ * this charger current step-up/down here.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_current(struct ab8500_charger *di,
+       int ich, int reg)
+{
+       int ret = 0;
+       int curr_index, prev_curr_index, shift_value, i;
+       u8 reg_value;
+       u32 step_udelay;
+       bool no_stepping = false;
+
+       atomic_inc(&di->current_stepping_sessions);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+               reg, &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s read failed\n", __func__);
+               goto exit_set_current;
+       }
+
+       switch (reg) {
+       case AB8500_MCH_IPT_CURLVL_REG:
+               shift_value = MAIN_CH_INPUT_CURR_SHIFT;
+               prev_curr_index = (reg_value >> shift_value);
+               curr_index = ab8500_current_to_regval(di, ich);
+               step_udelay = STEP_UDELAY;
+               if (!di->ac.charger_connected)
+                       no_stepping = true;
+               break;
+       case AB8500_USBCH_IPT_CRNTLVL_REG:
+               if (is_ab8540(di->parent))
+                       shift_value = AB8540_VBUS_IN_CURR_LIM_SHIFT;
+               else
+                       shift_value = VBUS_IN_CURR_LIM_SHIFT;
+               prev_curr_index = (reg_value >> shift_value);
+               curr_index = ab8500_vbus_in_curr_to_regval(di, ich);
+               step_udelay = STEP_UDELAY * 100;
+
+               if (!di->usb.charger_connected)
+                       no_stepping = true;
+               break;
+       case AB8500_CH_OPT_CRNTLVL_REG:
+               shift_value = 0;
+               prev_curr_index = (reg_value >> shift_value);
+               curr_index = ab8500_current_to_regval(di, ich);
+               step_udelay = STEP_UDELAY;
+               if (curr_index && (curr_index - prev_curr_index) > 1)
+                       step_udelay *= 100;
+
+               if (!di->usb.charger_connected && !di->ac.charger_connected)
+                       no_stepping = true;
+
+               break;
+       default:
+               dev_err(di->dev, "%s current register not valid\n", __func__);
+               ret = -ENXIO;
+               goto exit_set_current;
+       }
+
+       if (curr_index < 0) {
+               dev_err(di->dev, "requested current limit out-of-range\n");
+               ret = -ENXIO;
+               goto exit_set_current;
+       }
+
+       /* only update current if it's been changed */
+       if (prev_curr_index == curr_index) {
+               dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
+                       __func__, reg);
+               ret = 0;
+               goto exit_set_current;
+       }
+
+       dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
+               __func__, ich, reg);
+
+       if (no_stepping) {
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                                       reg, (u8)curr_index << shift_value);
+               if (ret)
+                       dev_err(di->dev, "%s write failed\n", __func__);
+       } else if (prev_curr_index > curr_index) {
+               for (i = prev_curr_index - 1; i >= curr_index; i--) {
+                       dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
+                               (u8) i << shift_value, reg);
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER, reg, (u8)i << shift_value);
+                       if (ret) {
+                               dev_err(di->dev, "%s write failed\n", __func__);
+                               goto exit_set_current;
+                       }
+                       if (i != curr_index)
+                               usleep_range(step_udelay, step_udelay * 2);
+               }
+       } else {
+               bool allow = true;
+               for (i = prev_curr_index + 1; i <= curr_index && allow; i++) {
+                       dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
+                               (u8)i << shift_value, reg);
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER, reg, (u8)i << shift_value);
+                       if (ret) {
+                               dev_err(di->dev, "%s write failed\n", __func__);
+                               goto exit_set_current;
+                       }
+                       if (i != curr_index)
+                               usleep_range(step_udelay, step_udelay * 2);
+
+                       allow = ab8500_charger_check_continue_stepping(di, reg);
+               }
+       }
+
+exit_set_current:
+       atomic_dec(&di->current_stepping_sessions);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
+ * @di:                pointer to the ab8500_charger structure
+ * @ich_in:    charger input current limit
+ *
+ * Sets the current that can be drawn from the USB host
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
+               int ich_in)
+{
+       int min_value;
+       int ret;
+
+       /* We should always use to lowest current limit */
+       min_value = min(di->bm->chg_params->usb_curr_max, ich_in);
+       if (di->max_usb_in_curr.set_max > 0)
+               min_value = min(di->max_usb_in_curr.set_max, min_value);
+
+       if (di->usb_state.usb_current >= 0)
+               min_value = min(di->usb_state.usb_current, min_value);
+
+       switch (min_value) {
+       case 100:
+               if (di->vbat < VBAT_TRESH_IP_CUR_RED)
+                       min_value = USB_CH_IP_CUR_LVL_0P05;
+               break;
+       case 500:
+               if (di->vbat < VBAT_TRESH_IP_CUR_RED)
+                       min_value = USB_CH_IP_CUR_LVL_0P45;
+               break;
+       default:
+               break;
+       }
+
+       dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value);
+
+       mutex_lock(&di->usb_ipt_crnt_lock);
+       ret = ab8500_charger_set_current(di, min_value,
+               AB8500_USBCH_IPT_CRNTLVL_REG);
+       mutex_unlock(&di->usb_ipt_crnt_lock);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_set_main_in_curr() - set main charger input current
+ * @di:                pointer to the ab8500_charger structure
+ * @ich_in:    input charger current, in mA
+ *
+ * Set main charger input current.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_main_in_curr(struct ab8500_charger *di,
+       int ich_in)
+{
+       return ab8500_charger_set_current(di, ich_in,
+               AB8500_MCH_IPT_CURLVL_REG);
+}
+
+/**
+ * ab8500_charger_set_output_curr() - set charger output current
+ * @di:                pointer to the ab8500_charger structure
+ * @ich_out:   output charger current, in mA
+ *
+ * Set charger output current.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_output_curr(struct ab8500_charger *di,
+       int ich_out)
+{
+       return ab8500_charger_set_current(di, ich_out,
+               AB8500_CH_OPT_CRNTLVL_REG);
+}
+
+/**
+ * ab8500_charger_led_en() - turn on/off chargign led
+ * @di:                pointer to the ab8500_charger structure
+ * @on:                flag to turn on/off the chargign led
+ *
+ * Power ON/OFF charging LED indication
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_led_en(struct ab8500_charger *di, int on)
+{
+       int ret;
+
+       if (on) {
+               /* Power ON charging LED indicator, set LED current to 5mA */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_LED_INDICATOR_PWM_CTRL,
+                       (LED_IND_CUR_5MA | LED_INDICATOR_PWM_ENA));
+               if (ret) {
+                       dev_err(di->dev, "Power ON LED failed\n");
+                       return ret;
+               }
+               /* LED indicator PWM duty cycle 252/256 */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_LED_INDICATOR_PWM_DUTY,
+                       LED_INDICATOR_PWM_DUTY_252_256);
+               if (ret) {
+                       dev_err(di->dev, "Set LED PWM duty cycle failed\n");
+                       return ret;
+               }
+       } else {
+               /* Power off charging LED indicator */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_LED_INDICATOR_PWM_CTRL,
+                       LED_INDICATOR_PWM_DIS);
+               if (ret) {
+                       dev_err(di->dev, "Power-off LED failed\n");
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_ac_en() - enable or disable ac charging
+ * @di:                pointer to the ab8500_charger structure
+ * @enable:    enable/disable flag
+ * @vset:      charging voltage
+ * @iset:      charging current
+ *
+ * Enable/Disable AC/Mains charging and turns on/off the charging led
+ * respectively.
+ **/
+static int ab8500_charger_ac_en(struct ux500_charger *charger,
+       int enable, int vset, int iset)
+{
+       int ret;
+       int volt_index;
+       int curr_index;
+       int input_curr_index;
+       u8 overshoot = 0;
+
+       struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
+
+       if (enable) {
+               /* Check if AC is connected */
+               if (!di->ac.charger_connected) {
+                       dev_err(di->dev, "AC charger not connected\n");
+                       return -ENXIO;
+               }
+
+               /* Enable AC charging */
+               dev_dbg(di->dev, "Enable AC: %dmV %dmA\n", vset, iset);
+
+               /*
+                * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
+                * will be triggered everytime we enable the VDD ADC supply.
+                * This will turn off charging for a short while.
+                * It can be avoided by having the supply on when
+                * there is a charger enabled. Normally the VDD ADC supply
+                * is enabled everytime a GPADC conversion is triggered. We will
+                * force it to be enabled from this driver to have
+                * the GPADC module independant of the AB8500 chargers
+                */
+               if (!di->vddadc_en_ac) {
+                       ret = regulator_enable(di->regu);
+                       if (ret)
+                               dev_warn(di->dev,
+                                       "Failed to enable regulator\n");
+                       else
+                               di->vddadc_en_ac = true;
+               }
+
+               /* Check if the requested voltage or current is valid */
+               volt_index = ab8500_voltage_to_regval(vset);
+               curr_index = ab8500_current_to_regval(di, iset);
+               input_curr_index = ab8500_current_to_regval(di,
+                       di->bm->chg_params->ac_curr_max);
+               if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) {
+                       dev_err(di->dev,
+                               "Charger voltage or current too high, "
+                               "charging not started\n");
+                       return -ENXIO;
+               }
+
+               /* ChVoltLevel: maximum battery charging voltage */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+               /* MainChInputCurr: current that can be drawn from the charger*/
+               ret = ab8500_charger_set_main_in_curr(di,
+                       di->bm->chg_params->ac_curr_max);
+               if (ret) {
+                       dev_err(di->dev, "%s Failed to set MainChInputCurr\n",
+                               __func__);
+                       return ret;
+               }
+               /* ChOutputCurentLevel: protected output current */
+               ret = ab8500_charger_set_output_curr(di, iset);
+               if (ret) {
+                       dev_err(di->dev, "%s "
+                               "Failed to set ChOutputCurentLevel\n",
+                               __func__);
+                       return ret;
+               }
+
+               /* Check if VBAT overshoot control should be enabled */
+               if (!di->bm->enable_overshoot)
+                       overshoot = MAIN_CH_NO_OVERSHOOT_ENA_N;
+
+               /* Enable Main Charger */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_MCH_CTRL1, MAIN_CH_ENA | overshoot);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+
+               /* Power on charging LED indication */
+               ret = ab8500_charger_led_en(di, true);
+               if (ret < 0)
+                       dev_err(di->dev, "failed to enable LED\n");
+
+               di->ac.charger_online = 1;
+       } else {
+               /* Disable AC charging */
+               if (is_ab8500_1p1_or_earlier(di->parent)) {
+                       /*
+                        * For ABB revision 1.0 and 1.1 there is a bug in the
+                        * watchdog logic. That means we have to continously
+                        * kick the charger watchdog even when no charger is
+                        * connected. This is only valid once the AC charger
+                        * has been enabled. This is a bug that is not handled
+                        * by the algorithm and the watchdog have to be kicked
+                        * by the charger driver when the AC charger
+                        * is disabled
+                        */
+                       if (di->ac_conn) {
+                               queue_delayed_work(di->charger_wq,
+                                       &di->kick_wd_work,
+                                       round_jiffies(WD_KICK_INTERVAL));
+                       }
+
+                       /*
+                        * We can't turn off charging completely
+                        * due to a bug in AB8500 cut1.
+                        * If we do, charging will not start again.
+                        * That is why we set the lowest voltage
+                        * and current possible
+                        */
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER,
+                               AB8500_CH_VOLT_LVL_REG, CH_VOL_LVL_3P5);
+                       if (ret) {
+                               dev_err(di->dev,
+                                       "%s write failed\n", __func__);
+                               return ret;
+                       }
+
+                       ret = ab8500_charger_set_output_curr(di, 0);
+                       if (ret) {
+                               dev_err(di->dev, "%s "
+                                       "Failed to set ChOutputCurentLevel\n",
+                                       __func__);
+                               return ret;
+                       }
+               } else {
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER,
+                               AB8500_MCH_CTRL1, 0);
+                       if (ret) {
+                               dev_err(di->dev,
+                                       "%s write failed\n", __func__);
+                               return ret;
+                       }
+               }
+
+               ret = ab8500_charger_led_en(di, false);
+               if (ret < 0)
+                       dev_err(di->dev, "failed to disable LED\n");
+
+               di->ac.charger_online = 0;
+               di->ac.wd_expired = false;
+
+               /* Disable regulator if enabled */
+               if (di->vddadc_en_ac) {
+                       regulator_disable(di->regu);
+                       di->vddadc_en_ac = false;
+               }
+
+               dev_dbg(di->dev, "%s Disabled AC charging\n", __func__);
+       }
+       ab8500_power_supply_changed(di, di->ac_chg.psy);
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_usb_en() - enable usb charging
+ * @di:                pointer to the ab8500_charger structure
+ * @enable:    enable/disable flag
+ * @vset:      charging voltage
+ * @ich_out:   charger output current
+ *
+ * Enable/Disable USB charging and turns on/off the charging led respectively.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_usb_en(struct ux500_charger *charger,
+       int enable, int vset, int ich_out)
+{
+       int ret;
+       int volt_index;
+       int curr_index;
+       u8 overshoot = 0;
+
+       struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
+
+       if (enable) {
+               /* Check if USB is connected */
+               if (!di->usb.charger_connected) {
+                       dev_err(di->dev, "USB charger not connected\n");
+                       return -ENXIO;
+               }
+
+               /*
+                * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
+                * will be triggered everytime we enable the VDD ADC supply.
+                * This will turn off charging for a short while.
+                * It can be avoided by having the supply on when
+                * there is a charger enabled. Normally the VDD ADC supply
+                * is enabled everytime a GPADC conversion is triggered. We will
+                * force it to be enabled from this driver to have
+                * the GPADC module independant of the AB8500 chargers
+                */
+               if (!di->vddadc_en_usb) {
+                       ret = regulator_enable(di->regu);
+                       if (ret)
+                               dev_warn(di->dev,
+                                       "Failed to enable regulator\n");
+                       else
+                               di->vddadc_en_usb = true;
+               }
+
+               /* Enable USB charging */
+               dev_dbg(di->dev, "Enable USB: %dmV %dmA\n", vset, ich_out);
+
+               /* Check if the requested voltage or current is valid */
+               volt_index = ab8500_voltage_to_regval(vset);
+               curr_index = ab8500_current_to_regval(di, ich_out);
+               if (volt_index < 0 || curr_index < 0) {
+                       dev_err(di->dev,
+                               "Charger voltage or current too high, "
+                               "charging not started\n");
+                       return -ENXIO;
+               }
+
+               /* ChVoltLevel: max voltage upto which battery can be charged */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+               /* Check if VBAT overshoot control should be enabled */
+               if (!di->bm->enable_overshoot)
+                       overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
+
+               /* Enable USB Charger */
+               dev_dbg(di->dev,
+                       "Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
+               if (ret) {
+                       dev_err(di->dev, "%s write failed\n", __func__);
+                       return ret;
+               }
+
+               /* If success power on charging LED indication */
+               ret = ab8500_charger_led_en(di, true);
+               if (ret < 0)
+                       dev_err(di->dev, "failed to enable LED\n");
+
+               di->usb.charger_online = 1;
+
+               /* USBChInputCurr: current that can be drawn from the usb */
+               ret = ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr.usb_type_max);
+               if (ret) {
+                       dev_err(di->dev, "setting USBChInputCurr failed\n");
+                       return ret;
+               }
+
+               /* ChOutputCurentLevel: protected output current */
+               ret = ab8500_charger_set_output_curr(di, ich_out);
+               if (ret) {
+                       dev_err(di->dev, "%s "
+                               "Failed to set ChOutputCurentLevel\n",
+                               __func__);
+                       return ret;
+               }
+
+               queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
+
+       } else {
+               /* Disable USB charging */
+               dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_CHARGER,
+                       AB8500_USBCH_CTRL1_REG, 0);
+               if (ret) {
+                       dev_err(di->dev,
+                               "%s write failed\n", __func__);
+                       return ret;
+               }
+
+               ret = ab8500_charger_led_en(di, false);
+               if (ret < 0)
+                       dev_err(di->dev, "failed to disable LED\n");
+               /* USBChInputCurr: current that can be drawn from the usb */
+               ret = ab8500_charger_set_vbus_in_curr(di, 0);
+               if (ret) {
+                       dev_err(di->dev, "setting USBChInputCurr failed\n");
+                       return ret;
+               }
+
+               /* ChOutputCurentLevel: protected output current */
+               ret = ab8500_charger_set_output_curr(di, 0);
+               if (ret) {
+                       dev_err(di->dev, "%s "
+                               "Failed to reset ChOutputCurentLevel\n",
+                               __func__);
+                       return ret;
+               }
+               di->usb.charger_online = 0;
+               di->usb.wd_expired = false;
+
+               /* Disable regulator if enabled */
+               if (di->vddadc_en_usb) {
+                       regulator_disable(di->regu);
+                       di->vddadc_en_usb = false;
+               }
+
+               dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
+
+               /* Cancel any pending Vbat check work */
+               cancel_delayed_work(&di->check_vbat_work);
+
+       }
+       ab8500_power_supply_changed(di, di->usb_chg.psy);
+
+       return ret;
+}
+
+static int ab8500_external_charger_prepare(struct notifier_block *charger_nb,
+                               unsigned long event, void *data)
+{
+       int ret;
+       struct device *dev = data;
+       /*Toggle External charger control pin*/
+       ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
+                                 AB8500_SYS_CHARGER_CONTROL_REG,
+                                 EXTERNAL_CHARGER_DISABLE_REG_VAL);
+       if (ret < 0) {
+               dev_err(dev, "write reg failed %d\n", ret);
+               goto out;
+       }
+       ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
+                                 AB8500_SYS_CHARGER_CONTROL_REG,
+                                 EXTERNAL_CHARGER_ENABLE_REG_VAL);
+       if (ret < 0)
+               dev_err(dev, "Write reg failed %d\n", ret);
+
+out:
+       return ret;
+}
+
+/**
+ * ab8500_charger_usb_check_enable() - enable usb charging
+ * @charger:   pointer to the ux500_charger structure
+ * @vset:      charging voltage
+ * @iset:      charger output current
+ *
+ * Check if the VBUS charger has been disconnected and reconnected without
+ * AB8500 rising an interrupt. Returns 0 on success.
+ */
+static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
+       int vset, int iset)
+{
+       u8 usbch_ctrl1 = 0;
+       int ret = 0;
+
+       struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
+
+       if (!di->usb.charger_connected)
+               return ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                               AB8500_USBCH_CTRL1_REG, &usbch_ctrl1);
+       if (ret < 0) {
+               dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
+               return ret;
+       }
+       dev_dbg(di->dev, "USB charger ctrl: 0x%02x\n", usbch_ctrl1);
+
+       if (!(usbch_ctrl1 & USB_CH_ENA)) {
+               dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                                       AB8500_CHARGER, AB8500_CHARGER_CTRL,
+                                       DROP_COUNT_RESET, DROP_COUNT_RESET);
+               if (ret < 0) {
+                       dev_err(di->dev, "ab8500 write failed %d\n", __LINE__);
+                       return ret;
+               }
+
+               ret = ab8500_charger_usb_en(&di->usb_chg, true, vset, iset);
+               if (ret < 0) {
+                       dev_err(di->dev, "Failed to enable VBUS charger %d\n",
+                                       __LINE__);
+                       return ret;
+               }
+       }
+       return ret;
+}
+
+/**
+ * ab8500_charger_ac_check_enable() - enable usb charging
+ * @charger:   pointer to the ux500_charger structure
+ * @vset:      charging voltage
+ * @iset:      charger output current
+ *
+ * Check if the AC charger has been disconnected and reconnected without
+ * AB8500 rising an interrupt. Returns 0 on success.
+ */
+static int ab8500_charger_ac_check_enable(struct ux500_charger *charger,
+       int vset, int iset)
+{
+       u8 mainch_ctrl1 = 0;
+       int ret = 0;
+
+       struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
+
+       if (!di->ac.charger_connected)
+               return ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                               AB8500_MCH_CTRL1, &mainch_ctrl1);
+       if (ret < 0) {
+               dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
+               return ret;
+       }
+       dev_dbg(di->dev, "AC charger ctrl: 0x%02x\n", mainch_ctrl1);
+
+       if (!(mainch_ctrl1 & MAIN_CH_ENA)) {
+               dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                                       AB8500_CHARGER, AB8500_CHARGER_CTRL,
+                                       DROP_COUNT_RESET, DROP_COUNT_RESET);
+
+               if (ret < 0) {
+                       dev_err(di->dev, "ab8500 write failed %d\n", __LINE__);
+                       return ret;
+               }
+
+               ret = ab8500_charger_ac_en(&di->usb_chg, true, vset, iset);
+               if (ret < 0) {
+                       dev_err(di->dev, "failed to enable AC charger %d\n",
+                               __LINE__);
+                       return ret;
+               }
+       }
+       return ret;
+}
+
+/**
+ * ab8500_charger_watchdog_kick() - kick charger watchdog
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Kick charger watchdog
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_watchdog_kick(struct ux500_charger *charger)
+{
+       int ret;
+       struct ab8500_charger *di;
+
+       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
+               di = to_ab8500_charger_ac_device_info(charger);
+       else if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
+               di = to_ab8500_charger_usb_device_info(charger);
+       else
+               return -ENXIO;
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+       if (ret)
+               dev_err(di->dev, "Failed to kick WD!\n");
+
+       return ret;
+}
+
+/**
+ * ab8500_charger_update_charger_current() - update charger current
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Update the charger output current for the specified charger
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
+               int ich_out)
+{
+       int ret;
+       struct ab8500_charger *di;
+
+       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
+               di = to_ab8500_charger_ac_device_info(charger);
+       else if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
+               di = to_ab8500_charger_usb_device_info(charger);
+       else
+               return -ENXIO;
+
+       ret = ab8500_charger_set_output_curr(di, ich_out);
+       if (ret) {
+               dev_err(di->dev, "%s "
+                       "Failed to set ChOutputCurentLevel\n",
+                       __func__);
+               return ret;
+       }
+
+       /* Reset the main and usb drop input current measurement counter */
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                               AB8500_CHARGER_CTRL, DROP_COUNT_RESET);
+       if (ret) {
+               dev_err(di->dev, "%s write failed\n", __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+/**
+ * ab8540_charger_power_path_enable() - enable usb power path mode
+ * @charger:   pointer to the ux500_charger structure
+ * @enable:    enable/disable flag
+ *
+ * Enable or disable the power path for usb mode
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8540_charger_power_path_enable(struct ux500_charger *charger,
+               bool enable)
+{
+       int ret;
+       struct ab8500_charger *di;
+
+       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
+               di = to_ab8500_charger_usb_device_info(charger);
+       else
+               return -ENXIO;
+
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+                               AB8500_CHARGER, AB8540_USB_PP_MODE_REG,
+                               BUS_POWER_PATH_MODE_ENA, enable);
+       if (ret) {
+               dev_err(di->dev, "%s write failed\n", __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+
+/**
+ * ab8540_charger_usb_pre_chg_enable() - enable usb pre change
+ * @charger:   pointer to the ux500_charger structure
+ * @enable:    enable/disable flag
+ *
+ * Enable or disable the pre-chage for usb mode
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8540_charger_usb_pre_chg_enable(struct ux500_charger *charger,
+               bool enable)
+{
+       int ret;
+       struct ab8500_charger *di;
+
+       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
+               di = to_ab8500_charger_usb_device_info(charger);
+       else
+               return -ENXIO;
+
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+                               AB8500_CHARGER, AB8540_USB_PP_CHR_REG,
+                               BUS_POWER_PATH_PRECHG_ENA, enable);
+       if (ret) {
+               dev_err(di->dev, "%s write failed\n", __func__);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data)
+{
+       struct power_supply *psy;
+       struct power_supply *ext = dev_get_drvdata(dev);
+       const char **supplicants = (const char **)ext->supplied_to;
+       struct ab8500_charger *di;
+       union power_supply_propval ret;
+       int j;
+       struct ux500_charger *usb_chg;
+
+       usb_chg = (struct ux500_charger *)data;
+       psy = usb_chg->psy;
+
+       di = to_ab8500_charger_usb_device_info(usb_chg);
+
+       /* For all psy where the driver name appears in any supplied_to */
+       j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
+       if (j < 0)
+               return 0;
+
+       /* Go through all properties for the psy */
+       for (j = 0; j < ext->desc->num_properties; j++) {
+               enum power_supply_property prop;
+               prop = ext->desc->properties[j];
+
+               if (power_supply_get_property(ext, prop, &ret))
+                       continue;
+
+               switch (prop) {
+               case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               di->vbat = ret.intval / 1000;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * ab8500_charger_check_vbat_work() - keep vbus current within spec
+ * @work       pointer to the work_struct structure
+ *
+ * Due to a asic bug it is necessary to lower the input current to the vbus
+ * charger when charging with at some specific levels. This issue is only valid
+ * for below a certain battery voltage. This function makes sure that the
+ * the allowed current limit isn't exceeded.
+ */
+static void ab8500_charger_check_vbat_work(struct work_struct *work)
+{
+       int t = 10;
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_vbat_work.work);
+
+       class_for_each_device(power_supply_class, NULL,
+               di->usb_chg.psy, ab8500_charger_get_ext_psy_data);
+
+       /* First run old_vbat is 0. */
+       if (di->old_vbat == 0)
+               di->old_vbat = di->vbat;
+
+       if (!((di->old_vbat <= VBAT_TRESH_IP_CUR_RED &&
+               di->vbat <= VBAT_TRESH_IP_CUR_RED) ||
+               (di->old_vbat > VBAT_TRESH_IP_CUR_RED &&
+               di->vbat > VBAT_TRESH_IP_CUR_RED))) {
+
+               dev_dbg(di->dev, "Vbat did cross threshold, curr: %d, new: %d,"
+                       " old: %d\n", di->max_usb_in_curr.usb_type_max,
+                       di->vbat, di->old_vbat);
+               ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr.usb_type_max);
+               power_supply_changed(di->usb_chg.psy);
+       }
+
+       di->old_vbat = di->vbat;
+
+       /*
+        * No need to check the battery voltage every second when not close to
+        * the threshold.
+        */
+       if (di->vbat < (VBAT_TRESH_IP_CUR_RED + 100) &&
+               (di->vbat > (VBAT_TRESH_IP_CUR_RED - 100)))
+                       t = 1;
+
+       queue_delayed_work(di->charger_wq, &di->check_vbat_work, t * HZ);
+}
+
+/**
+ * ab8500_charger_check_hw_failure_work() - check main charger failure
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab8500_charger_check_hw_failure_work(struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_hw_failure_work.work);
+
+       /* Check if the status bits for HW failure is still active */
+       if (di->flags.mainextchnotok) {
+               ret = abx500_get_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return;
+               }
+               if (!(reg_value & MAIN_CH_NOK)) {
+                       di->flags.mainextchnotok = false;
+                       ab8500_power_supply_changed(di, di->ac_chg.psy);
+               }
+       }
+       if (di->flags.vbus_ovv) {
+               ret = abx500_get_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG,
+                       &reg_value);
+               if (ret < 0) {
+                       dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+                       return;
+               }
+               if (!(reg_value & VBUS_OVV_TH)) {
+                       di->flags.vbus_ovv = false;
+                       ab8500_power_supply_changed(di, di->usb_chg.psy);
+               }
+       }
+       /* If we still have a failure, schedule a new check */
+       if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
+               queue_delayed_work(di->charger_wq,
+                       &di->check_hw_failure_work, round_jiffies(HZ));
+       }
+}
+
+/**
+ * ab8500_charger_kick_watchdog_work() - kick the watchdog
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for kicking the charger watchdog.
+ *
+ * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+ * logic. That means we have to continously kick the charger
+ * watchdog even when no charger is connected. This is only
+ * valid once the AC charger has been enabled. This is
+ * a bug that is not handled by the algorithm and the
+ * watchdog have to be kicked by the charger driver
+ * when the AC charger is disabled
+ */
+static void ab8500_charger_kick_watchdog_work(struct work_struct *work)
+{
+       int ret;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, kick_wd_work.work);
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+       if (ret)
+               dev_err(di->dev, "Failed to kick WD!\n");
+
+       /* Schedule a new watchdog kick */
+       queue_delayed_work(di->charger_wq,
+               &di->kick_wd_work, round_jiffies(WD_KICK_INTERVAL));
+}
+
+/**
+ * ab8500_charger_ac_work() - work to get and set main charger status
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab8500_charger_ac_work(struct work_struct *work)
+{
+       int ret;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, ac_work);
+
+       /*
+        * Since we can't be sure that the events are received
+        * synchronously, we have the check if the main charger is
+        * connected by reading the status register
+        */
+       ret = ab8500_charger_detect_chargers(di, false);
+       if (ret < 0)
+               return;
+
+       if (ret & AC_PW_CONN) {
+               di->ac.charger_connected = 1;
+               di->ac_conn = true;
+       } else {
+               di->ac.charger_connected = 0;
+       }
+
+       ab8500_power_supply_changed(di, di->ac_chg.psy);
+       sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present");
+}
+
+static void ab8500_charger_usb_attached_work(struct work_struct *work)
+{
+       struct ab8500_charger *di = container_of(work,
+                                                struct ab8500_charger,
+                                                usb_charger_attached_work.work);
+       int usbch = (USB_CH_VBUSDROP | USB_CH_VBUSDETDBNC);
+       int ret, i;
+       u8 statval;
+
+       for (i = 0; i < 10; i++) {
+               ret = abx500_get_register_interruptible(di->dev,
+                                                       AB8500_CHARGER,
+                                                       AB8500_CH_USBCH_STAT1_REG,
+                                                       &statval);
+               if (ret < 0) {
+                       dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
+                       goto reschedule;
+               }
+               if ((statval & usbch) != usbch)
+                       goto reschedule;
+
+               msleep(CHARGER_STATUS_POLL);
+       }
+
+       ab8500_charger_usb_en(&di->usb_chg, 0, 0, 0);
+
+       mutex_lock(&di->charger_attached_mutex);
+       mutex_unlock(&di->charger_attached_mutex);
+
+       return;
+
+reschedule:
+       queue_delayed_work(di->charger_wq,
+                          &di->usb_charger_attached_work,
+                          HZ);
+}
+
+static void ab8500_charger_ac_attached_work(struct work_struct *work)
+{
+
+       struct ab8500_charger *di = container_of(work,
+                                                struct ab8500_charger,
+                                                ac_charger_attached_work.work);
+       int mainch = (MAIN_CH_STATUS2_MAINCHGDROP |
+                     MAIN_CH_STATUS2_MAINCHARGERDETDBNC);
+       int ret, i;
+       u8 statval;
+
+       for (i = 0; i < 10; i++) {
+               ret = abx500_get_register_interruptible(di->dev,
+                                                       AB8500_CHARGER,
+                                                       AB8500_CH_STATUS2_REG,
+                                                       &statval);
+               if (ret < 0) {
+                       dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
+                       goto reschedule;
+               }
+
+               if ((statval & mainch) != mainch)
+                       goto reschedule;
+
+               msleep(CHARGER_STATUS_POLL);
+       }
+
+       ab8500_charger_ac_en(&di->ac_chg, 0, 0, 0);
+       queue_work(di->charger_wq, &di->ac_work);
+
+       mutex_lock(&di->charger_attached_mutex);
+       mutex_unlock(&di->charger_attached_mutex);
+
+       return;
+
+reschedule:
+       queue_delayed_work(di->charger_wq,
+                          &di->ac_charger_attached_work,
+                          HZ);
+}
+
+/**
+ * ab8500_charger_detect_usb_type_work() - work to detect USB type
+ * @work:      Pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
+{
+       int ret;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, detect_usb_type_work);
+
+       /*
+        * Since we can't be sure that the events are received
+        * synchronously, we have the check if is
+        * connected by reading the status register
+        */
+       ret = ab8500_charger_detect_chargers(di, false);
+       if (ret < 0)
+               return;
+
+       if (!(ret & USB_PW_CONN)) {
+               dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__);
+               di->vbus_detected = false;
+               ab8500_charger_set_usb_connected(di, false);
+               ab8500_power_supply_changed(di, di->usb_chg.psy);
+       } else {
+               dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
+               di->vbus_detected = true;
+
+               if (is_ab8500_1p1_or_earlier(di->parent)) {
+                       ret = ab8500_charger_detect_usb_type(di);
+                       if (!ret) {
+                               ab8500_charger_set_usb_connected(di, true);
+                               ab8500_power_supply_changed(di,
+                                                           di->usb_chg.psy);
+                       }
+               } else {
+                       /*
+                        * For ABB cut2.0 and onwards we have an IRQ,
+                        * USB_LINK_STATUS that will be triggered when the USB
+                        * link status changes. The exception is USB connected
+                        * during startup. Then we don't get a
+                        * USB_LINK_STATUS IRQ
+                        */
+                       if (di->vbus_detected_start) {
+                               di->vbus_detected_start = false;
+                               ret = ab8500_charger_detect_usb_type(di);
+                               if (!ret) {
+                                       ab8500_charger_set_usb_connected(di,
+                                               true);
+                                       ab8500_power_supply_changed(di,
+                                               di->usb_chg.psy);
+                               }
+                       }
+               }
+       }
+}
+
+/**
+ * ab8500_charger_usb_link_attach_work() - work to detect USB type
+ * @work:      pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
+{
+       struct ab8500_charger *di =
+               container_of(work, struct ab8500_charger, attach_work.work);
+       int ret;
+
+       /* Update maximum input current if USB enumeration is not detected */
+       if (!di->usb.charger_online) {
+               ret = ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr.usb_type_max);
+               if (ret)
+                       return;
+       }
+
+       ab8500_charger_set_usb_connected(di, true);
+       ab8500_power_supply_changed(di, di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_usb_link_status_work() - work to detect USB type
+ * @work:      pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_usb_link_status_work(struct work_struct *work)
+{
+       int detected_chargers;
+       int ret;
+       u8 val;
+       u8 link_status;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, usb_link_status_work);
+
+       /*
+        * Since we can't be sure that the events are received
+        * synchronously, we have the check if  is
+        * connected by reading the status register
+        */
+       detected_chargers = ab8500_charger_detect_chargers(di, false);
+       if (detected_chargers < 0)
+               return;
+
+       /*
+        * Some chargers that breaks the USB spec is
+        * identified as invalid by AB8500 and it refuse
+        * to start the charging process. but by jumping
+        * thru a few hoops it can be forced to start.
+        */
+       if (is_ab8500(di->parent))
+               ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+                                       AB8500_USB_LINE_STAT_REG, &val);
+       else
+               ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+                                       AB8500_USB_LINK1_STAT_REG, &val);
+
+       if (ret >= 0)
+               dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
+       else
+               dev_dbg(di->dev, "Error reading USB link status\n");
+
+       if (is_ab8500(di->parent))
+               link_status = AB8500_USB_LINK_STATUS;
+       else
+               link_status = AB8505_USB_LINK_STATUS;
+
+       if (detected_chargers & USB_PW_CONN) {
+               if (((val & link_status) >> USB_LINK_STATUS_SHIFT) ==
+                               USB_STAT_NOT_VALID_LINK &&
+                               di->invalid_charger_detect_state == 0) {
+                       dev_dbg(di->dev,
+                                       "Invalid charger detected, state= 0\n");
+                       /*Enable charger*/
+                       abx500_mask_and_set_register_interruptible(di->dev,
+                                       AB8500_CHARGER, AB8500_USBCH_CTRL1_REG,
+                                       USB_CH_ENA, USB_CH_ENA);
+                       /*Enable charger detection*/
+                       abx500_mask_and_set_register_interruptible(di->dev,
+                                       AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
+                                       USB_CH_DET, USB_CH_DET);
+                       di->invalid_charger_detect_state = 1;
+                       /*exit and wait for new link status interrupt.*/
+                       return;
+
+               }
+               if (di->invalid_charger_detect_state == 1) {
+                       dev_dbg(di->dev,
+                                       "Invalid charger detected, state= 1\n");
+                       /*Stop charger detection*/
+                       abx500_mask_and_set_register_interruptible(di->dev,
+                                       AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
+                                       USB_CH_DET, 0x00);
+                       /*Check link status*/
+                       if (is_ab8500(di->parent))
+                               ret = abx500_get_register_interruptible(di->dev,
+                                       AB8500_USB, AB8500_USB_LINE_STAT_REG,
+                                       &val);
+                       else
+                               ret = abx500_get_register_interruptible(di->dev,
+                                       AB8500_USB, AB8500_USB_LINK1_STAT_REG,
+                                       &val);
+
+                       dev_dbg(di->dev, "USB link status= 0x%02x\n",
+                               (val & link_status) >> USB_LINK_STATUS_SHIFT);
+                       di->invalid_charger_detect_state = 2;
+               }
+       } else {
+               di->invalid_charger_detect_state = 0;
+       }
+
+       if (!(detected_chargers & USB_PW_CONN)) {
+               di->vbus_detected = false;
+               ab8500_charger_set_usb_connected(di, false);
+               ab8500_power_supply_changed(di, di->usb_chg.psy);
+               return;
+       }
+
+       dev_dbg(di->dev,"%s di->vbus_detected = true\n",__func__);
+       di->vbus_detected = true;
+       ret = ab8500_charger_read_usb_type(di);
+       if (ret) {
+               if (ret == -ENXIO) {
+                       /* No valid charger type detected */
+                       ab8500_charger_set_usb_connected(di, false);
+                       ab8500_power_supply_changed(di, di->usb_chg.psy);
+               }
+               return;
+       }
+
+       if (di->usb_device_is_unrecognised) {
+               dev_dbg(di->dev,
+                       "Potential Legacy Charger device. "
+                       "Delay work for %d msec for USB enum "
+                       "to finish",
+                       WAIT_ACA_RID_ENUMERATION);
+               queue_delayed_work(di->charger_wq,
+                                  &di->attach_work,
+                                  msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
+       } else if (di->is_aca_rid == 1) {
+               /* Only wait once */
+               di->is_aca_rid++;
+               dev_dbg(di->dev,
+                       "%s Wait %d msec for USB enum to finish",
+                       __func__, WAIT_ACA_RID_ENUMERATION);
+               queue_delayed_work(di->charger_wq,
+                                  &di->attach_work,
+                                  msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
+       } else {
+               queue_delayed_work(di->charger_wq,
+                                  &di->attach_work,
+                                  0);
+       }
+}
+
+static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
+{
+       int ret;
+       unsigned long flags;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, usb_state_changed_work.work);
+
+       if (!di->vbus_detected) {
+               dev_dbg(di->dev,
+                       "%s !di->vbus_detected\n",
+                       __func__);
+               return;
+       }
+
+       spin_lock_irqsave(&di->usb_state.usb_lock, flags);
+       di->usb_state.state = di->usb_state.state_tmp;
+       di->usb_state.usb_current = di->usb_state.usb_current_tmp;
+       spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
+
+       dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
+               __func__, di->usb_state.state, di->usb_state.usb_current);
+
+       switch (di->usb_state.state) {
+       case AB8500_BM_USB_STATE_RESET_HS:
+       case AB8500_BM_USB_STATE_RESET_FS:
+       case AB8500_BM_USB_STATE_SUSPEND:
+       case AB8500_BM_USB_STATE_MAX:
+               ab8500_charger_set_usb_connected(di, false);
+               ab8500_power_supply_changed(di, di->usb_chg.psy);
+               break;
+
+       case AB8500_BM_USB_STATE_RESUME:
+               /*
+                * when suspend->resume there should be delay
+                * of 1sec for enabling charging
+                */
+               msleep(1000);
+               /* Intentional fall through */
+       case AB8500_BM_USB_STATE_CONFIGURED:
+               /*
+                * USB is configured, enable charging with the charging
+                * input current obtained from USB driver
+                */
+               if (!ab8500_charger_get_usb_cur(di)) {
+                       /* Update maximum input current */
+                       ret = ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr.usb_type_max);
+                       if (ret)
+                               return;
+
+                       ab8500_charger_set_usb_connected(di, true);
+                       ab8500_power_supply_changed(di, di->usb_chg.psy);
+               }
+               break;
+
+       default:
+               break;
+       };
+}
+
+/**
+ * ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB charger Not OK status
+ */
+static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+       bool prev_status;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_usbchgnotok_work.work);
+
+       /* Check if the status bit for usbchargernotok is still active */
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return;
+       }
+       prev_status = di->flags.usbchargernotok;
+
+       if (reg_value & VBUS_CH_NOK) {
+               di->flags.usbchargernotok = true;
+               /* Check again in 1sec */
+               queue_delayed_work(di->charger_wq,
+                       &di->check_usbchgnotok_work, HZ);
+       } else {
+               di->flags.usbchargernotok = false;
+               di->flags.vbus_collapse = false;
+       }
+
+       if (prev_status != di->flags.usbchargernotok)
+               ab8500_power_supply_changed(di, di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_check_main_thermal_prot_work() - check main thermal status
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the Main thermal prot status
+ */
+static void ab8500_charger_check_main_thermal_prot_work(
+       struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_main_thermal_prot_work);
+
+       /* Check if the status bit for main_thermal_prot is still active */
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return;
+       }
+       if (reg_value & MAIN_CH_TH_PROT)
+               di->flags.main_thermal_prot = true;
+       else
+               di->flags.main_thermal_prot = false;
+
+       ab8500_power_supply_changed(di, di->ac_chg.psy);
+}
+
+/**
+ * ab8500_charger_check_usb_thermal_prot_work() - check usb thermal status
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB thermal prot status
+ */
+static void ab8500_charger_check_usb_thermal_prot_work(
+       struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, check_usb_thermal_prot_work);
+
+       /* Check if the status bit for usb_thermal_prot is still active */
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return;
+       }
+       if (reg_value & USB_CH_TH_PROT)
+               di->flags.usb_thermal_prot = true;
+       else
+               di->flags.usb_thermal_prot = false;
+
+       ab8500_power_supply_changed(di, di->usb_chg.psy);
+}
+
+/**
+ * ab8500_charger_mainchunplugdet_handler() - main charger unplugged
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Main charger unplugged\n");
+       queue_work(di->charger_wq, &di->ac_work);
+
+       cancel_delayed_work_sync(&di->ac_charger_attached_work);
+       mutex_lock(&di->charger_attached_mutex);
+       mutex_unlock(&di->charger_attached_mutex);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchplugdet_handler() - main charger plugged
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Main charger plugged\n");
+       queue_work(di->charger_wq, &di->ac_work);
+
+       mutex_lock(&di->charger_attached_mutex);
+       mutex_unlock(&di->charger_attached_mutex);
+
+       if (is_ab8500(di->parent))
+               queue_delayed_work(di->charger_wq,
+                          &di->ac_charger_attached_work,
+                          HZ);
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainextchnotok_handler() - main charger not ok
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainextchnotok_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Main charger not ok\n");
+       di->flags.mainextchnotok = true;
+       ab8500_power_supply_changed(di, di->ac_chg.psy);
+
+       /* Schedule a new HW failure check */
+       queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchthprotr_handler() - Die temp is above main charger
+ * thermal protection threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchthprotr_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev,
+               "Die temp above Main charger thermal protection threshold\n");
+       queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_mainchthprotf_handler() - Die temp is below main charger
+ * thermal protection threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev,
+               "Die temp ok for Main charger thermal protection threshold\n");
+       queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
+
+       return IRQ_HANDLED;
+}
+
+static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
+{
+       struct ab8500_charger *di = container_of(work,
+               struct ab8500_charger, vbus_drop_end_work.work);
+       int ret, curr;
+       u8 reg_value;
+
+       di->flags.vbus_drop_end = false;
+
+       /* Reset the drop counter */
+       abx500_set_register_interruptible(di->dev,
+                                 AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
+
+       if (is_ab8540(di->parent))
+               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                               AB8540_CH_USBCH_STAT3_REG, &reg_value);
+       else
+               ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                               AB8500_CH_USBCH_STAT2_REG, &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s read failed\n", __func__);
+               return;
+       }
+
+       if (is_ab8540(di->parent))
+               curr = di->bm->chg_input_curr[
+                       reg_value & AB8540_AUTO_VBUS_IN_CURR_MASK];
+       else
+               curr = di->bm->chg_input_curr[
+                       reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT];
+
+       if (di->max_usb_in_curr.calculated_max != curr) {
+               /* USB source is collapsing */
+               di->max_usb_in_curr.calculated_max = curr;
+               dev_dbg(di->dev,
+                        "VBUS input current limiting to %d mA\n",
+                        di->max_usb_in_curr.calculated_max);
+       } else {
+               /*
+                * USB source can not give more than this amount.
+                * Taking more will collapse the source.
+                */
+               di->max_usb_in_curr.set_max =
+                       di->max_usb_in_curr.calculated_max;
+               dev_dbg(di->dev,
+                        "VBUS input current limited to %d mA\n",
+                        di->max_usb_in_curr.set_max);
+       }
+
+       if (di->usb.charger_connected)
+               ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr.usb_type_max);
+}
+
+/**
+ * ab8500_charger_vbusdetf_handler() - VBUS falling detected
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       di->vbus_detected = false;
+       dev_dbg(di->dev, "VBUS falling detected\n");
+       queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusdetr_handler() - VBUS rising detected
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusdetr_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       di->vbus_detected = true;
+       dev_dbg(di->dev, "VBUS rising detected\n");
+
+       queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usblinkstatus_handler() - USB link status has changed
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usblinkstatus_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "USB link status changed\n");
+
+       queue_work(di->charger_wq, &di->usb_link_status_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchthprotr_handler() - Die temp is above usb charger
+ * thermal protection threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchthprotr_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev,
+               "Die temp above USB charger thermal protection threshold\n");
+       queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchthprotf_handler() - Die temp is below usb charger
+ * thermal protection threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchthprotf_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev,
+               "Die temp ok for USB charger thermal protection threshold\n");
+       queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_usbchargernotokr_handler() - USB charger not ok detected
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_usbchargernotokr_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Not allowed USB charger detected\n");
+       queue_delayed_work(di->charger_wq, &di->check_usbchgnotok_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_chwdexp_handler() - Charger watchdog expired
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "Charger watchdog expired\n");
+
+       /*
+        * The charger that was online when the watchdog expired
+        * needs to be restarted for charging to start again
+        */
+       if (di->ac.charger_online) {
+               di->ac.wd_expired = true;
+               ab8500_power_supply_changed(di, di->ac_chg.psy);
+       }
+       if (di->usb.charger_online) {
+               di->usb.wd_expired = true;
+               ab8500_power_supply_changed(di, di->usb_chg.psy);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbuschdropend_handler() - VBUS drop removed
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "VBUS charger drop ended\n");
+       di->flags.vbus_drop_end = true;
+
+       /*
+        * VBUS might have dropped due to bad connection.
+        * Schedule a new input limit set to the value SW requests.
+        */
+       queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
+                          round_jiffies(VBUS_IN_CURR_LIM_RETRY_SET_TIME * HZ));
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbusovv_handler(int irq, void *_di)
+{
+       struct ab8500_charger *di = _di;
+
+       dev_dbg(di->dev, "VBUS overvoltage detected\n");
+       di->flags.vbus_ovv = true;
+       ab8500_power_supply_changed(di, di->usb_chg.psy);
+
+       /* Schedule a new HW failure check */
+       queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_charger_ac_get_property() - get the ac/mains properties
+ * @psy:       pointer to the power_supply structure
+ * @psp:       pointer to the power_supply_property structure
+ * @val:       pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the ac/mains
+ * properties by reading the sysfs files.
+ * AC/Mains properties are online, present and voltage.
+ * online:     ac/mains charging is in progress or not
+ * present:    presence of the ac/mains
+ * voltage:    AC/Mains voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_ac_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct ab8500_charger *di;
+       int ret;
+
+       di = to_ab8500_charger_ac_device_info(psy_to_ux500_charger(psy));
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (di->flags.mainextchnotok)
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               else if (di->ac.wd_expired || di->usb.wd_expired)
+                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               else if (di->flags.main_thermal_prot)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = di->ac.charger_online;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = di->ac.charger_connected;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = ab8500_charger_get_ac_voltage(di);
+               if (ret >= 0)
+                       di->ac.charger_voltage = ret;
+               /* On error, use previous value */
+               val->intval = di->ac.charger_voltage * 1000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               /*
+                * This property is used to indicate when CV mode is entered
+                * for the AC charger
+                */
+               di->ac.cv_active = ab8500_charger_ac_cv(di);
+               val->intval = di->ac.cv_active;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = ab8500_charger_get_ac_current(di);
+               if (ret >= 0)
+                       di->ac.charger_current = ret;
+               val->intval = di->ac.charger_current * 1000;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/**
+ * ab8500_charger_usb_get_property() - get the usb properties
+ * @psy:        pointer to the power_supply structure
+ * @psp:        pointer to the power_supply_property structure
+ * @val:        pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the usb
+ * properties by reading the sysfs files.
+ * USB properties are online, present and voltage.
+ * online:     usb charging is in progress or not
+ * present:    presence of the usb
+ * voltage:    vbus voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_usb_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct ab8500_charger *di;
+       int ret;
+
+       di = to_ab8500_charger_usb_device_info(psy_to_ux500_charger(psy));
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (di->flags.usbchargernotok)
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               else if (di->ac.wd_expired || di->usb.wd_expired)
+                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               else if (di->flags.usb_thermal_prot)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else if (di->flags.vbus_ovv)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = di->usb.charger_online;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = di->usb.charger_connected;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = ab8500_charger_get_vbus_voltage(di);
+               if (ret >= 0)
+                       di->usb.charger_voltage = ret;
+               val->intval = di->usb.charger_voltage * 1000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               /*
+                * This property is used to indicate when CV mode is entered
+                * for the USB charger
+                */
+               di->usb.cv_active = ab8500_charger_usb_cv(di);
+               val->intval = di->usb.cv_active;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = ab8500_charger_get_usb_current(di);
+               if (ret >= 0)
+                       di->usb.charger_current = ret;
+               val->intval = di->usb.charger_current * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               /*
+                * This property is used to indicate when VBUS has collapsed
+                * due to too high output current from the USB charger
+                */
+               if (di->flags.vbus_collapse)
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/**
+ * ab8500_charger_init_hw_registers() - Set up charger related registers
+ * @di:                pointer to the ab8500_charger structure
+ *
+ * Set up charger OVV, watchdog and maximum voltage registers as well as
+ * charging of the backup battery
+ */
+static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
+{
+       int ret = 0;
+       u8 bup_vch_range = 0, vbup33_vrtcn = 0;
+
+       /* Setup maximum charger current and voltage for ABB cut2.0 */
+       if (!is_ab8500_1p1_or_earlier(di->parent)) {
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_CHARGER,
+                       AB8500_CH_VOLT_LVL_MAX_REG, CH_VOL_LVL_4P6);
+               if (ret) {
+                       dev_err(di->dev,
+                               "failed to set CH_VOLT_LVL_MAX_REG\n");
+                       goto out;
+               }
+
+               if (is_ab8540(di->parent))
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG,
+                               CH_OP_CUR_LVL_2P);
+               else
+                       ret = abx500_set_register_interruptible(di->dev,
+                               AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG,
+                               CH_OP_CUR_LVL_1P6);
+               if (ret) {
+                       dev_err(di->dev,
+                               "failed to set CH_OPT_CRNTLVL_MAX_REG\n");
+                       goto out;
+               }
+       }
+
+       if (is_ab9540_2p0(di->parent) || is_ab9540_3p0(di->parent)
+        || is_ab8505_2p0(di->parent) || is_ab8540(di->parent))
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER,
+                       AB8500_USBCH_CTRL2_REG,
+                       VBUS_AUTO_IN_CURR_LIM_ENA,
+                       VBUS_AUTO_IN_CURR_LIM_ENA);
+       else
+               /*
+                * VBUS OVV set to 6.3V and enable automatic current limitation
+                */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_CHARGER,
+                       AB8500_USBCH_CTRL2_REG,
+                       VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
+       if (ret) {
+               dev_err(di->dev,
+                       "failed to set automatic current limitation\n");
+               goto out;
+       }
+
+       /* Enable main watchdog in OTP */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_OTP_EMUL, AB8500_OTP_CONF_15, OTP_ENABLE_WD);
+       if (ret) {
+               dev_err(di->dev, "failed to enable main WD in OTP\n");
+               goto out;
+       }
+
+       /* Enable main watchdog */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_SYS_CTRL2_BLOCK,
+               AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_ENA);
+       if (ret) {
+               dev_err(di->dev, "faile to enable main watchdog\n");
+               goto out;
+       }
+
+       /*
+        * Due to internal synchronisation, Enable and Kick watchdog bits
+        * cannot be enabled in a single write.
+        * A minimum delay of 2*32 kHz period (62.5µs) must be inserted
+        * between writing Enable then Kick bits.
+        */
+       udelay(63);
+
+       /* Kick main watchdog */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_SYS_CTRL2_BLOCK,
+               AB8500_MAIN_WDOG_CTRL_REG,
+               (MAIN_WDOG_ENA | MAIN_WDOG_KICK));
+       if (ret) {
+               dev_err(di->dev, "failed to kick main watchdog\n");
+               goto out;
+       }
+
+       /* Disable main watchdog */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_SYS_CTRL2_BLOCK,
+               AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_DIS);
+       if (ret) {
+               dev_err(di->dev, "failed to disable main watchdog\n");
+               goto out;
+       }
+
+       /* Set watchdog timeout */
+       ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+               AB8500_CH_WD_TIMER_REG, WD_TIMER);
+       if (ret) {
+               dev_err(di->dev, "failed to set charger watchdog timeout\n");
+               goto out;
+       }
+
+       ret = ab8500_charger_led_en(di, false);
+       if (ret < 0) {
+               dev_err(di->dev, "failed to disable LED\n");
+               goto out;
+       }
+
+       /* Backup battery voltage and current */
+       if (di->bm->bkup_bat_v > BUP_VCH_SEL_3P1V)
+               bup_vch_range = BUP_VCH_RANGE;
+       if (di->bm->bkup_bat_v == BUP_VCH_SEL_3P3V)
+               vbup33_vrtcn = VBUP33_VRTCN;
+
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_RTC,
+               AB8500_RTC_BACKUP_CHG_REG,
+               (di->bm->bkup_bat_v & 0x3) | di->bm->bkup_bat_i);
+       if (ret) {
+               dev_err(di->dev, "failed to setup backup battery charging\n");
+               goto out;
+       }
+       if (is_ab8540(di->parent)) {
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_RTC,
+                       AB8500_RTC_CTRL1_REG,
+                       bup_vch_range | vbup33_vrtcn);
+               if (ret) {
+                       dev_err(di->dev,
+                               "failed to setup backup battery charging\n");
+                       goto out;
+               }
+       }
+
+       /* Enable backup battery charging */
+       abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_RTC, AB8500_RTC_CTRL_REG,
+               RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
+       if (ret < 0)
+               dev_err(di->dev, "%s mask and set failed\n", __func__);
+
+       if (is_ab8540(di->parent)) {
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8540_USB_PP_MODE_REG,
+                       BUS_VSYS_VOL_SELECT_MASK, BUS_VSYS_VOL_SELECT_3P6V);
+               if (ret) {
+                       dev_err(di->dev,
+                               "failed to setup usb power path vsys voltage\n");
+                       goto out;
+               }
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_CHARGER, AB8540_USB_PP_CHR_REG,
+                       BUS_PP_PRECHG_CURRENT_MASK, 0);
+               if (ret) {
+                       dev_err(di->dev,
+                               "failed to setup usb power path prechage current\n");
+                       goto out;
+               }
+       }
+
+out:
+       return ret;
+}
+
+/*
+ * ab8500 charger driver interrupts and their respective isr
+ */
+static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
+       {"MAIN_CH_UNPLUG_DET", ab8500_charger_mainchunplugdet_handler},
+       {"MAIN_CHARGE_PLUG_DET", ab8500_charger_mainchplugdet_handler},
+       {"MAIN_EXT_CH_NOT_OK", ab8500_charger_mainextchnotok_handler},
+       {"MAIN_CH_TH_PROT_R", ab8500_charger_mainchthprotr_handler},
+       {"MAIN_CH_TH_PROT_F", ab8500_charger_mainchthprotf_handler},
+       {"VBUS_DET_F", ab8500_charger_vbusdetf_handler},
+       {"VBUS_DET_R", ab8500_charger_vbusdetr_handler},
+       {"USB_LINK_STATUS", ab8500_charger_usblinkstatus_handler},
+       {"USB_CH_TH_PROT_R", ab8500_charger_usbchthprotr_handler},
+       {"USB_CH_TH_PROT_F", ab8500_charger_usbchthprotf_handler},
+       {"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
+       {"VBUS_OVV", ab8500_charger_vbusovv_handler},
+       {"CH_WD_EXP", ab8500_charger_chwdexp_handler},
+       {"VBUS_CH_DROP_END", ab8500_charger_vbuschdropend_handler},
+};
+
+static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
+               unsigned long event, void *power)
+{
+       struct ab8500_charger *di =
+               container_of(nb, struct ab8500_charger, nb);
+       enum ab8500_usb_state bm_usb_state;
+       unsigned mA = *((unsigned *)power);
+
+       if (!di)
+               return NOTIFY_DONE;
+
+       if (event != USB_EVENT_VBUS) {
+               dev_dbg(di->dev, "not a standard host, returning\n");
+               return NOTIFY_DONE;
+       }
+
+       /* TODO: State is fabricate  here. See if charger really needs USB
+        * state or if mA is enough
+        */
+       if ((di->usb_state.usb_current == 2) && (mA > 2))
+               bm_usb_state = AB8500_BM_USB_STATE_RESUME;
+       else if (mA == 0)
+               bm_usb_state = AB8500_BM_USB_STATE_RESET_HS;
+       else if (mA == 2)
+               bm_usb_state = AB8500_BM_USB_STATE_SUSPEND;
+       else if (mA >= 8) /* 8, 100, 500 */
+               bm_usb_state = AB8500_BM_USB_STATE_CONFIGURED;
+       else /* Should never occur */
+               bm_usb_state = AB8500_BM_USB_STATE_RESET_FS;
+
+       dev_dbg(di->dev, "%s usb_state: 0x%02x mA: %d\n",
+               __func__, bm_usb_state, mA);
+
+       spin_lock(&di->usb_state.usb_lock);
+       di->usb_state.state_tmp = bm_usb_state;
+       di->usb_state.usb_current_tmp = mA;
+       spin_unlock(&di->usb_state.usb_lock);
+
+       /*
+        * wait for some time until you get updates from the usb stack
+        * and negotiations are completed
+        */
+       queue_delayed_work(di->charger_wq, &di->usb_state_changed_work, HZ/2);
+
+       return NOTIFY_OK;
+}
+
+#if defined(CONFIG_PM)
+static int ab8500_charger_resume(struct platform_device *pdev)
+{
+       int ret;
+       struct ab8500_charger *di = platform_get_drvdata(pdev);
+
+       /*
+        * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+        * logic. That means we have to continously kick the charger
+        * watchdog even when no charger is connected. This is only
+        * valid once the AC charger has been enabled. This is
+        * a bug that is not handled by the algorithm and the
+        * watchdog have to be kicked by the charger driver
+        * when the AC charger is disabled
+        */
+       if (di->ac_conn && is_ab8500_1p1_or_earlier(di->parent)) {
+               ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+                       AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
+               if (ret)
+                       dev_err(di->dev, "Failed to kick WD!\n");
+
+               /* If not already pending start a new timer */
+               queue_delayed_work(di->charger_wq, &di->kick_wd_work,
+                                  round_jiffies(WD_KICK_INTERVAL));
+       }
+
+       /* If we still have a HW failure, schedule a new check */
+       if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
+               queue_delayed_work(di->charger_wq,
+                       &di->check_hw_failure_work, 0);
+       }
+
+       if (di->flags.vbus_drop_end)
+               queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work, 0);
+
+       return 0;
+}
+
+static int ab8500_charger_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct ab8500_charger *di = platform_get_drvdata(pdev);
+
+       /* Cancel any pending jobs */
+       cancel_delayed_work(&di->check_hw_failure_work);
+       cancel_delayed_work(&di->vbus_drop_end_work);
+
+       flush_delayed_work(&di->attach_work);
+       flush_delayed_work(&di->usb_charger_attached_work);
+       flush_delayed_work(&di->ac_charger_attached_work);
+       flush_delayed_work(&di->check_usbchgnotok_work);
+       flush_delayed_work(&di->check_vbat_work);
+       flush_delayed_work(&di->kick_wd_work);
+
+       flush_work(&di->usb_link_status_work);
+       flush_work(&di->ac_work);
+       flush_work(&di->detect_usb_type_work);
+
+       if (atomic_read(&di->current_stepping_sessions))
+               return -EAGAIN;
+
+       return 0;
+}
+#else
+#define ab8500_charger_suspend      NULL
+#define ab8500_charger_resume       NULL
+#endif
+
+static struct notifier_block charger_nb = {
+       .notifier_call = ab8500_external_charger_prepare,
+};
+
+static int ab8500_charger_remove(struct platform_device *pdev)
+{
+       struct ab8500_charger *di = platform_get_drvdata(pdev);
+       int i, irq, ret;
+
+       /* Disable AC charging */
+       ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
+
+       /* Disable USB charging */
+       ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
+
+       /* Disable interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+               free_irq(irq, di);
+       }
+
+       /* Backup battery voltage and current disable */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
+       if (ret < 0)
+               dev_err(di->dev, "%s mask and set failed\n", __func__);
+
+       usb_unregister_notifier(di->usb_phy, &di->nb);
+       usb_put_phy(di->usb_phy);
+
+       /* Delete the work queue */
+       destroy_workqueue(di->charger_wq);
+
+       /* Unregister external charger enable notifier */
+       if (!di->ac_chg.enabled)
+               blocking_notifier_chain_unregister(
+                       &charger_notifier_list, &charger_nb);
+
+       flush_scheduled_work();
+       if (di->usb_chg.enabled)
+               power_supply_unregister(di->usb_chg.psy);
+
+       if (di->ac_chg.enabled && !di->ac_chg.external)
+               power_supply_unregister(di->ac_chg.psy);
+
+       return 0;
+}
+
+static char *supply_interface[] = {
+       "ab8500_chargalg",
+       "ab8500_fg",
+       "ab8500_btemp",
+};
+
+static const struct power_supply_desc ab8500_ac_chg_desc = {
+       .name           = "ab8500_ac",
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+       .properties     = ab8500_charger_ac_props,
+       .num_properties = ARRAY_SIZE(ab8500_charger_ac_props),
+       .get_property   = ab8500_charger_ac_get_property,
+};
+
+static const struct power_supply_desc ab8500_usb_chg_desc = {
+       .name           = "ab8500_usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties     = ab8500_charger_usb_props,
+       .num_properties = ARRAY_SIZE(ab8500_charger_usb_props),
+       .get_property   = ab8500_charger_usb_get_property,
+};
+
+static int ab8500_charger_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct abx500_bm_data *plat = pdev->dev.platform_data;
+       struct power_supply_config ac_psy_cfg = {}, usb_psy_cfg = {};
+       struct ab8500_charger *di;
+       int irq, i, charger_status, ret = 0, ch_stat;
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       if (!di) {
+               dev_err(&pdev->dev, "%s no mem for ab8500_charger\n", __func__);
+               return -ENOMEM;
+       }
+
+       if (!plat) {
+               dev_err(&pdev->dev, "no battery management data supplied\n");
+               return -EINVAL;
+       }
+       di->bm = plat;
+
+       if (np) {
+               ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get battery information\n");
+                       return ret;
+               }
+               di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
+       } else
+               di->autopower_cfg = false;
+
+       /* get parent data */
+       di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
+       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+       /* initialize lock */
+       spin_lock_init(&di->usb_state.usb_lock);
+       mutex_init(&di->usb_ipt_crnt_lock);
+
+       di->autopower = false;
+       di->invalid_charger_detect_state = 0;
+
+       /* AC and USB supply config */
+       ac_psy_cfg.supplied_to = supply_interface;
+       ac_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
+       ac_psy_cfg.drv_data = &di->ac_chg;
+       usb_psy_cfg.supplied_to = supply_interface;
+       usb_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
+       usb_psy_cfg.drv_data = &di->usb_chg;
+
+       /* AC supply */
+       /* ux500_charger sub-class */
+       di->ac_chg.ops.enable = &ab8500_charger_ac_en;
+       di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable;
+       di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
+       di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
+       di->ac_chg.max_out_volt = ab8500_charger_voltage_map[
+               ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
+       di->ac_chg.max_out_curr =
+               di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1];
+       di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
+       di->ac_chg.enabled = di->bm->ac_enabled;
+       di->ac_chg.external = false;
+
+       /*notifier for external charger enabling*/
+       if (!di->ac_chg.enabled)
+               blocking_notifier_chain_register(
+                       &charger_notifier_list, &charger_nb);
+
+       /* USB supply */
+       /* ux500_charger sub-class */
+       di->usb_chg.ops.enable = &ab8500_charger_usb_en;
+       di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable;
+       di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
+       di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
+       di->usb_chg.ops.pp_enable = &ab8540_charger_power_path_enable;
+       di->usb_chg.ops.pre_chg_enable = &ab8540_charger_usb_pre_chg_enable;
+       di->usb_chg.max_out_volt = ab8500_charger_voltage_map[
+               ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
+       di->usb_chg.max_out_curr =
+               di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1];
+       di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
+       di->usb_chg.enabled = di->bm->usb_enabled;
+       di->usb_chg.external = false;
+       di->usb_chg.power_path = di->bm->usb_power_path;
+       di->usb_state.usb_current = -1;
+
+       /* Create a work queue for the charger */
+       di->charger_wq =
+               create_singlethread_workqueue("ab8500_charger_wq");
+       if (di->charger_wq == NULL) {
+               dev_err(di->dev, "failed to create work queue\n");
+               return -ENOMEM;
+       }
+
+       mutex_init(&di->charger_attached_mutex);
+
+       /* Init work for HW failure check */
+       INIT_DEFERRABLE_WORK(&di->check_hw_failure_work,
+               ab8500_charger_check_hw_failure_work);
+       INIT_DEFERRABLE_WORK(&di->check_usbchgnotok_work,
+               ab8500_charger_check_usbchargernotok_work);
+
+       INIT_DELAYED_WORK(&di->ac_charger_attached_work,
+                         ab8500_charger_ac_attached_work);
+       INIT_DELAYED_WORK(&di->usb_charger_attached_work,
+                         ab8500_charger_usb_attached_work);
+
+       /*
+        * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
+        * logic. That means we have to continously kick the charger
+        * watchdog even when no charger is connected. This is only
+        * valid once the AC charger has been enabled. This is
+        * a bug that is not handled by the algorithm and the
+        * watchdog have to be kicked by the charger driver
+        * when the AC charger is disabled
+        */
+       INIT_DEFERRABLE_WORK(&di->kick_wd_work,
+               ab8500_charger_kick_watchdog_work);
+
+       INIT_DEFERRABLE_WORK(&di->check_vbat_work,
+               ab8500_charger_check_vbat_work);
+
+       INIT_DELAYED_WORK(&di->attach_work,
+               ab8500_charger_usb_link_attach_work);
+
+       INIT_DELAYED_WORK(&di->usb_state_changed_work,
+               ab8500_charger_usb_state_changed_work);
+
+       INIT_DELAYED_WORK(&di->vbus_drop_end_work,
+               ab8500_charger_vbus_drop_end_work);
+
+       /* Init work for charger detection */
+       INIT_WORK(&di->usb_link_status_work,
+               ab8500_charger_usb_link_status_work);
+       INIT_WORK(&di->ac_work, ab8500_charger_ac_work);
+       INIT_WORK(&di->detect_usb_type_work,
+               ab8500_charger_detect_usb_type_work);
+
+       /* Init work for checking HW status */
+       INIT_WORK(&di->check_main_thermal_prot_work,
+               ab8500_charger_check_main_thermal_prot_work);
+       INIT_WORK(&di->check_usb_thermal_prot_work,
+               ab8500_charger_check_usb_thermal_prot_work);
+
+       /*
+        * VDD ADC supply needs to be enabled from this driver when there
+        * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
+        * interrupts during charging
+        */
+       di->regu = devm_regulator_get(di->dev, "vddadc");
+       if (IS_ERR(di->regu)) {
+               ret = PTR_ERR(di->regu);
+               dev_err(di->dev, "failed to get vddadc regulator\n");
+               goto free_charger_wq;
+       }
+
+
+       /* Initialize OVV, and other registers */
+       ret = ab8500_charger_init_hw_registers(di);
+       if (ret) {
+               dev_err(di->dev, "failed to initialize ABB registers\n");
+               goto free_charger_wq;
+       }
+
+       /* Register AC charger class */
+       if (di->ac_chg.enabled) {
+               di->ac_chg.psy = power_supply_register(di->dev,
+                                                      &ab8500_ac_chg_desc,
+                                                      &ac_psy_cfg);
+               if (IS_ERR(di->ac_chg.psy)) {
+                       dev_err(di->dev, "failed to register AC charger\n");
+                       ret = PTR_ERR(di->ac_chg.psy);
+                       goto free_charger_wq;
+               }
+       }
+
+       /* Register USB charger class */
+       if (di->usb_chg.enabled) {
+               di->usb_chg.psy = power_supply_register(di->dev,
+                                                       &ab8500_usb_chg_desc,
+                                                       &usb_psy_cfg);
+               if (IS_ERR(di->usb_chg.psy)) {
+                       dev_err(di->dev, "failed to register USB charger\n");
+                       ret = PTR_ERR(di->usb_chg.psy);
+                       goto free_ac;
+               }
+       }
+
+       di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (IS_ERR_OR_NULL(di->usb_phy)) {
+               dev_err(di->dev, "failed to get usb transceiver\n");
+               ret = -EINVAL;
+               goto free_usb;
+       }
+       di->nb.notifier_call = ab8500_charger_usb_notifier_call;
+       ret = usb_register_notifier(di->usb_phy, &di->nb);
+       if (ret) {
+               dev_err(di->dev, "failed to register usb notifier\n");
+               goto put_usb_phy;
+       }
+
+       /* Identify the connected charger types during startup */
+       charger_status = ab8500_charger_detect_chargers(di, true);
+       if (charger_status & AC_PW_CONN) {
+               di->ac.charger_connected = 1;
+               di->ac_conn = true;
+               ab8500_power_supply_changed(di, di->ac_chg.psy);
+               sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present");
+       }
+
+       if (charger_status & USB_PW_CONN) {
+               di->vbus_detected = true;
+               di->vbus_detected_start = true;
+               queue_work(di->charger_wq,
+                       &di->detect_usb_type_work);
+       }
+
+       /* Register interrupts */
+       for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+               ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr,
+                       IRQF_SHARED | IRQF_NO_SUSPEND,
+                       ab8500_charger_irq[i].name, di);
+
+               if (ret != 0) {
+                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+                               , ab8500_charger_irq[i].name, irq, ret);
+                       goto free_irq;
+               }
+               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+                       ab8500_charger_irq[i].name, irq, ret);
+       }
+
+       platform_set_drvdata(pdev, di);
+
+       mutex_lock(&di->charger_attached_mutex);
+
+       ch_stat = ab8500_charger_detect_chargers(di, false);
+
+       if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
+               if (is_ab8500(di->parent))
+                       queue_delayed_work(di->charger_wq,
+                                          &di->ac_charger_attached_work,
+                                          HZ);
+       }
+       if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) {
+               if (is_ab8500(di->parent))
+                       queue_delayed_work(di->charger_wq,
+                                          &di->usb_charger_attached_work,
+                                          HZ);
+       }
+
+       mutex_unlock(&di->charger_attached_mutex);
+
+       return ret;
+
+free_irq:
+       usb_unregister_notifier(di->usb_phy, &di->nb);
+
+       /* We also have to free all successfully registered irqs */
+       for (i = i - 1; i >= 0; i--) {
+               irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
+               free_irq(irq, di);
+       }
+put_usb_phy:
+       usb_put_phy(di->usb_phy);
+free_usb:
+       if (di->usb_chg.enabled)
+               power_supply_unregister(di->usb_chg.psy);
+free_ac:
+       if (di->ac_chg.enabled)
+               power_supply_unregister(di->ac_chg.psy);
+free_charger_wq:
+       destroy_workqueue(di->charger_wq);
+       return ret;
+}
+
+static const struct of_device_id ab8500_charger_match[] = {
+       { .compatible = "stericsson,ab8500-charger", },
+       { },
+};
+
+static struct platform_driver ab8500_charger_driver = {
+       .probe = ab8500_charger_probe,
+       .remove = ab8500_charger_remove,
+       .suspend = ab8500_charger_suspend,
+       .resume = ab8500_charger_resume,
+       .driver = {
+               .name = "ab8500-charger",
+               .of_match_table = ab8500_charger_match,
+       },
+};
+
+static int __init ab8500_charger_init(void)
+{
+       return platform_driver_register(&ab8500_charger_driver);
+}
+
+static void __exit ab8500_charger_exit(void)
+{
+       platform_driver_unregister(&ab8500_charger_driver);
+}
+
+subsys_initcall_sync(ab8500_charger_init);
+module_exit(ab8500_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
+MODULE_ALIAS("platform:ab8500-charger");
+MODULE_DESCRIPTION("AB8500 charger management driver");
diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c
new file mode 100644 (file)
index 0000000..5a36cf8
--- /dev/null
@@ -0,0 +1,3272 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2012
+ *
+ * Main and Back-up battery management driver.
+ *
+ * Note: Backup battery management is required in case of Li-Ion battery and not
+ * for capacitive battery. HREF boards have capacitive battery and hence backup
+ * battery management is not used and the supported code is available in this
+ * driver.
+ *
+ * License Terms: GNU General Public License v2
+ * Author:
+ *     Johan Palsson <johan.palsson@stericsson.com>
+ *     Karl Komierowski <karl.komierowski@stericsson.com>
+ *     Arun R Murthy <arun.murthy@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/time64.h>
+#include <linux/of.h>
+#include <linux/completion.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/kernel.h>
+
+#define MILLI_TO_MICRO                 1000
+#define FG_LSB_IN_MA                   1627
+#define QLSB_NANO_AMP_HOURS_X10                1071
+#define INS_CURR_TIMEOUT               (3 * HZ)
+
+#define SEC_TO_SAMPLE(S)               (S * 4)
+
+#define NBR_AVG_SAMPLES                        20
+
+#define LOW_BAT_CHECK_INTERVAL         (HZ / 16) /* 62.5 ms */
+
+#define VALID_CAPACITY_SEC             (45 * 60) /* 45 minutes */
+#define BATT_OK_MIN                    2360 /* mV */
+#define BATT_OK_INCREMENT              50 /* mV */
+#define BATT_OK_MAX_NR_INCREMENTS      0xE
+
+/* FG constants */
+#define BATT_OVV                       0x01
+
+#define interpolate(x, x1, y1, x2, y2) \
+       ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
+
+/**
+ * struct ab8500_fg_interrupts - ab8500 fg interupts
+ * @name:      name of the interrupt
+ * @isr                function pointer to the isr
+ */
+struct ab8500_fg_interrupts {
+       char *name;
+       irqreturn_t (*isr)(int irq, void *data);
+};
+
+enum ab8500_fg_discharge_state {
+       AB8500_FG_DISCHARGE_INIT,
+       AB8500_FG_DISCHARGE_INITMEASURING,
+       AB8500_FG_DISCHARGE_INIT_RECOVERY,
+       AB8500_FG_DISCHARGE_RECOVERY,
+       AB8500_FG_DISCHARGE_READOUT_INIT,
+       AB8500_FG_DISCHARGE_READOUT,
+       AB8500_FG_DISCHARGE_WAKEUP,
+};
+
+static char *discharge_state[] = {
+       "DISCHARGE_INIT",
+       "DISCHARGE_INITMEASURING",
+       "DISCHARGE_INIT_RECOVERY",
+       "DISCHARGE_RECOVERY",
+       "DISCHARGE_READOUT_INIT",
+       "DISCHARGE_READOUT",
+       "DISCHARGE_WAKEUP",
+};
+
+enum ab8500_fg_charge_state {
+       AB8500_FG_CHARGE_INIT,
+       AB8500_FG_CHARGE_READOUT,
+};
+
+static char *charge_state[] = {
+       "CHARGE_INIT",
+       "CHARGE_READOUT",
+};
+
+enum ab8500_fg_calibration_state {
+       AB8500_FG_CALIB_INIT,
+       AB8500_FG_CALIB_WAIT,
+       AB8500_FG_CALIB_END,
+};
+
+struct ab8500_fg_avg_cap {
+       int avg;
+       int samples[NBR_AVG_SAMPLES];
+       time64_t time_stamps[NBR_AVG_SAMPLES];
+       int pos;
+       int nbr_samples;
+       int sum;
+};
+
+struct ab8500_fg_cap_scaling {
+       bool enable;
+       int cap_to_scale[2];
+       int disable_cap_level;
+       int scaled_cap;
+};
+
+struct ab8500_fg_battery_capacity {
+       int max_mah_design;
+       int max_mah;
+       int mah;
+       int permille;
+       int level;
+       int prev_mah;
+       int prev_percent;
+       int prev_level;
+       int user_mah;
+       struct ab8500_fg_cap_scaling cap_scale;
+};
+
+struct ab8500_fg_flags {
+       bool fg_enabled;
+       bool conv_done;
+       bool charging;
+       bool fully_charged;
+       bool force_full;
+       bool low_bat_delay;
+       bool low_bat;
+       bool bat_ovv;
+       bool batt_unknown;
+       bool calibrate;
+       bool user_cap;
+       bool batt_id_received;
+};
+
+struct inst_curr_result_list {
+       struct list_head list;
+       int *result;
+};
+
+/**
+ * struct ab8500_fg - ab8500 FG device information
+ * @dev:               Pointer to the structure device
+ * @node:              a list of AB8500 FGs, hence prepared for reentrance
+ * @irq                        holds the CCEOC interrupt number
+ * @vbat:              Battery voltage in mV
+ * @vbat_nom:          Nominal battery voltage in mV
+ * @inst_curr:         Instantenous battery current in mA
+ * @avg_curr:          Average battery current in mA
+ * @bat_temp           battery temperature
+ * @fg_samples:                Number of samples used in the FG accumulation
+ * @accu_charge:       Accumulated charge from the last conversion
+ * @recovery_cnt:      Counter for recovery mode
+ * @high_curr_cnt:     Counter for high current mode
+ * @init_cnt:          Counter for init mode
+ * @low_bat_cnt                Counter for number of consecutive low battery measures
+ * @nbr_cceoc_irq_cnt  Counter for number of CCEOC irqs received since enabled
+ * @recovery_needed:   Indicate if recovery is needed
+ * @high_curr_mode:    Indicate if we're in high current mode
+ * @init_capacity:     Indicate if initial capacity measuring should be done
+ * @turn_off_fg:       True if fg was off before current measurement
+ * @calib_state                State during offset calibration
+ * @discharge_state:   Current discharge state
+ * @charge_state:      Current charge state
+ * @ab8500_fg_started  Completion struct used for the instant current start
+ * @ab8500_fg_complete Completion struct used for the instant current reading
+ * @flags:             Structure for information about events triggered
+ * @bat_cap:           Structure for battery capacity specific parameters
+ * @avg_cap:           Average capacity filter
+ * @parent:            Pointer to the struct ab8500
+ * @gpadc:             Pointer to the struct gpadc
+ * @bm:                Platform specific battery management information
+ * @fg_psy:            Structure that holds the FG specific battery properties
+ * @fg_wq:             Work queue for running the FG algorithm
+ * @fg_periodic_work:  Work to run the FG algorithm periodically
+ * @fg_low_bat_work:   Work to check low bat condition
+ * @fg_reinit_work     Work used to reset and reinitialise the FG algorithm
+ * @fg_work:           Work to run the FG algorithm instantly
+ * @fg_acc_cur_work:   Work to read the FG accumulator
+ * @fg_check_hw_failure_work:  Work for checking HW state
+ * @cc_lock:           Mutex for locking the CC
+ * @fg_kobject:                Structure of type kobject
+ */
+struct ab8500_fg {
+       struct device *dev;
+       struct list_head node;
+       int irq;
+       int vbat;
+       int vbat_nom;
+       int inst_curr;
+       int avg_curr;
+       int bat_temp;
+       int fg_samples;
+       int accu_charge;
+       int recovery_cnt;
+       int high_curr_cnt;
+       int init_cnt;
+       int low_bat_cnt;
+       int nbr_cceoc_irq_cnt;
+       bool recovery_needed;
+       bool high_curr_mode;
+       bool init_capacity;
+       bool turn_off_fg;
+       enum ab8500_fg_calibration_state calib_state;
+       enum ab8500_fg_discharge_state discharge_state;
+       enum ab8500_fg_charge_state charge_state;
+       struct completion ab8500_fg_started;
+       struct completion ab8500_fg_complete;
+       struct ab8500_fg_flags flags;
+       struct ab8500_fg_battery_capacity bat_cap;
+       struct ab8500_fg_avg_cap avg_cap;
+       struct ab8500 *parent;
+       struct ab8500_gpadc *gpadc;
+       struct abx500_bm_data *bm;
+       struct power_supply *fg_psy;
+       struct workqueue_struct *fg_wq;
+       struct delayed_work fg_periodic_work;
+       struct delayed_work fg_low_bat_work;
+       struct delayed_work fg_reinit_work;
+       struct work_struct fg_work;
+       struct work_struct fg_acc_cur_work;
+       struct delayed_work fg_check_hw_failure_work;
+       struct mutex cc_lock;
+       struct kobject fg_kobject;
+};
+static LIST_HEAD(ab8500_fg_list);
+
+/**
+ * ab8500_fg_get() - returns a reference to the primary AB8500 fuel gauge
+ * (i.e. the first fuel gauge in the instance list)
+ */
+struct ab8500_fg *ab8500_fg_get(void)
+{
+       struct ab8500_fg *fg;
+
+       if (list_empty(&ab8500_fg_list))
+               return NULL;
+
+       fg = list_first_entry(&ab8500_fg_list, struct ab8500_fg, node);
+       return fg;
+}
+
+/* Main battery properties */
+static enum power_supply_property ab8500_fg_props[] = {
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+       POWER_SUPPLY_PROP_ENERGY_FULL,
+       POWER_SUPPLY_PROP_ENERGY_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+};
+
+/*
+ * This array maps the raw hex value to lowbat voltage used by the AB8500
+ * Values taken from the UM0836
+ */
+static int ab8500_fg_lowbat_voltage_map[] = {
+       2300 ,
+       2325 ,
+       2350 ,
+       2375 ,
+       2400 ,
+       2425 ,
+       2450 ,
+       2475 ,
+       2500 ,
+       2525 ,
+       2550 ,
+       2575 ,
+       2600 ,
+       2625 ,
+       2650 ,
+       2675 ,
+       2700 ,
+       2725 ,
+       2750 ,
+       2775 ,
+       2800 ,
+       2825 ,
+       2850 ,
+       2875 ,
+       2900 ,
+       2925 ,
+       2950 ,
+       2975 ,
+       3000 ,
+       3025 ,
+       3050 ,
+       3075 ,
+       3100 ,
+       3125 ,
+       3150 ,
+       3175 ,
+       3200 ,
+       3225 ,
+       3250 ,
+       3275 ,
+       3300 ,
+       3325 ,
+       3350 ,
+       3375 ,
+       3400 ,
+       3425 ,
+       3450 ,
+       3475 ,
+       3500 ,
+       3525 ,
+       3550 ,
+       3575 ,
+       3600 ,
+       3625 ,
+       3650 ,
+       3675 ,
+       3700 ,
+       3725 ,
+       3750 ,
+       3775 ,
+       3800 ,
+       3825 ,
+       3850 ,
+       3850 ,
+};
+
+static u8 ab8500_volt_to_regval(int voltage)
+{
+       int i;
+
+       if (voltage < ab8500_fg_lowbat_voltage_map[0])
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(ab8500_fg_lowbat_voltage_map); i++) {
+               if (voltage < ab8500_fg_lowbat_voltage_map[i])
+                       return (u8) i - 1;
+       }
+
+       /* If not captured above, return index of last element */
+       return (u8) ARRAY_SIZE(ab8500_fg_lowbat_voltage_map) - 1;
+}
+
+/**
+ * ab8500_fg_is_low_curr() - Low or high current mode
+ * @di:                pointer to the ab8500_fg structure
+ * @curr:      the current to base or our decision on
+ *
+ * Low current mode if the current consumption is below a certain threshold
+ */
+static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
+{
+       /*
+        * We want to know if we're in low current mode
+        */
+       if (curr > -di->bm->fg_params->high_curr_threshold)
+               return true;
+       else
+               return false;
+}
+
+/**
+ * ab8500_fg_add_cap_sample() - Add capacity to average filter
+ * @di:                pointer to the ab8500_fg structure
+ * @sample:    the capacity in mAh to add to the filter
+ *
+ * A capacity is added to the filter and a new mean capacity is calculated and
+ * returned
+ */
+static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
+{
+       struct timespec64 ts64;
+       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+       getnstimeofday64(&ts64);
+
+       do {
+               avg->sum += sample - avg->samples[avg->pos];
+               avg->samples[avg->pos] = sample;
+               avg->time_stamps[avg->pos] = ts64.tv_sec;
+               avg->pos++;
+
+               if (avg->pos == NBR_AVG_SAMPLES)
+                       avg->pos = 0;
+
+               if (avg->nbr_samples < NBR_AVG_SAMPLES)
+                       avg->nbr_samples++;
+
+               /*
+                * Check the time stamp for each sample. If too old,
+                * replace with latest sample
+                */
+       } while (ts64.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
+
+       avg->avg = avg->sum / avg->nbr_samples;
+
+       return avg->avg;
+}
+
+/**
+ * ab8500_fg_clear_cap_samples() - Clear average filter
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * The capacity filter is is reset to zero.
+ */
+static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di)
+{
+       int i;
+       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+       avg->pos = 0;
+       avg->nbr_samples = 0;
+       avg->sum = 0;
+       avg->avg = 0;
+
+       for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+               avg->samples[i] = 0;
+               avg->time_stamps[i] = 0;
+       }
+}
+
+/**
+ * ab8500_fg_fill_cap_sample() - Fill average filter
+ * @di:                pointer to the ab8500_fg structure
+ * @sample:    the capacity in mAh to fill the filter with
+ *
+ * The capacity filter is filled with a capacity in mAh
+ */
+static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
+{
+       int i;
+       struct timespec64 ts64;
+       struct ab8500_fg_avg_cap *avg = &di->avg_cap;
+
+       getnstimeofday64(&ts64);
+
+       for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+               avg->samples[i] = sample;
+               avg->time_stamps[i] = ts64.tv_sec;
+       }
+
+       avg->pos = 0;
+       avg->nbr_samples = NBR_AVG_SAMPLES;
+       avg->sum = sample * NBR_AVG_SAMPLES;
+       avg->avg = sample;
+}
+
+/**
+ * ab8500_fg_coulomb_counter() - enable coulomb counter
+ * @di:                pointer to the ab8500_fg structure
+ * @enable:    enable/disable
+ *
+ * Enable/Disable coulomb counter.
+ * On failure returns negative value.
+ */
+static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
+{
+       int ret = 0;
+       mutex_lock(&di->cc_lock);
+       if (enable) {
+               /* To be able to reprogram the number of samples, we have to
+                * first stop the CC and then enable it again */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG, 0x00);
+               if (ret)
+                       goto cc_err;
+
+               /* Program the samples */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+                       di->fg_samples);
+               if (ret)
+                       goto cc_err;
+
+               /* Start the CC */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG,
+                       (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+               if (ret)
+                       goto cc_err;
+
+               di->flags.fg_enabled = true;
+       } else {
+               /* Clear any pending read requests */
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       (RESET_ACCU | READ_REQ), 0);
+               if (ret)
+                       goto cc_err;
+
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL, 0);
+               if (ret)
+                       goto cc_err;
+
+               /* Stop the CC */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG, 0);
+               if (ret)
+                       goto cc_err;
+
+               di->flags.fg_enabled = false;
+
+       }
+       dev_dbg(di->dev, " CC enabled: %d Samples: %d\n",
+               enable, di->fg_samples);
+
+       mutex_unlock(&di->cc_lock);
+
+       return ret;
+cc_err:
+       dev_err(di->dev, "%s Enabling coulomb counter failed\n", __func__);
+       mutex_unlock(&di->cc_lock);
+       return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_start() - start battery instantaneous current
+ * @di:         pointer to the ab8500_fg structure
+ *
+ * Returns 0 or error code
+ * Note: This is part "one" and has to be called before
+ * ab8500_fg_inst_curr_finalize()
+ */
+int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
+{
+       u8 reg_val;
+       int ret;
+
+       mutex_lock(&di->cc_lock);
+
+       di->nbr_cceoc_irq_cnt = 0;
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+               AB8500_RTC_CC_CONF_REG, &reg_val);
+       if (ret < 0)
+               goto fail;
+
+       if (!(reg_val & CC_PWR_UP_ENA)) {
+               dev_dbg(di->dev, "%s Enable FG\n", __func__);
+               di->turn_off_fg = true;
+
+               /* Program the samples */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+                       SEC_TO_SAMPLE(10));
+               if (ret)
+                       goto fail;
+
+               /* Start the CC */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG,
+                       (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA));
+               if (ret)
+                       goto fail;
+       } else {
+               di->turn_off_fg = false;
+       }
+
+       /* Return and WFI */
+       reinit_completion(&di->ab8500_fg_started);
+       reinit_completion(&di->ab8500_fg_complete);
+       enable_irq(di->irq);
+
+       /* Note: cc_lock is still locked */
+       return 0;
+fail:
+       mutex_unlock(&di->cc_lock);
+       return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_started() - check if fg conversion has started
+ * @di:         pointer to the ab8500_fg structure
+ *
+ * Returns 1 if conversion started, 0 if still waiting
+ */
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di)
+{
+       return completion_done(&di->ab8500_fg_started);
+}
+
+/**
+ * ab8500_fg_inst_curr_done() - check if fg conversion is done
+ * @di:         pointer to the ab8500_fg structure
+ *
+ * Returns 1 if conversion done, 0 if still waiting
+ */
+int ab8500_fg_inst_curr_done(struct ab8500_fg *di)
+{
+       return completion_done(&di->ab8500_fg_complete);
+}
+
+/**
+ * ab8500_fg_inst_curr_finalize() - battery instantaneous current
+ * @di:         pointer to the ab8500_fg structure
+ * @res:       battery instantenous current(on success)
+ *
+ * Returns 0 or an error code
+ * Note: This is part "two" and has to be called at earliest 250 ms
+ * after ab8500_fg_inst_curr_start()
+ */
+int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
+{
+       u8 low, high;
+       int val;
+       int ret;
+       unsigned long timeout;
+
+       if (!completion_done(&di->ab8500_fg_complete)) {
+               timeout = wait_for_completion_timeout(
+                       &di->ab8500_fg_complete,
+                       INS_CURR_TIMEOUT);
+               dev_dbg(di->dev, "Finalize time: %d ms\n",
+                       jiffies_to_msecs(INS_CURR_TIMEOUT - timeout));
+               if (!timeout) {
+                       ret = -ETIME;
+                       disable_irq(di->irq);
+                       di->nbr_cceoc_irq_cnt = 0;
+                       dev_err(di->dev, "completion timed out [%d]\n",
+                               __LINE__);
+                       goto fail;
+               }
+       }
+
+       disable_irq(di->irq);
+       di->nbr_cceoc_irq_cnt = 0;
+
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       READ_REQ, READ_REQ);
+
+       /* 100uS between read request and read is needed */
+       usleep_range(100, 100);
+
+       /* Read CC Sample conversion value Low and high */
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_SMPL_CNVL_REG,  &low);
+       if (ret < 0)
+               goto fail;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_SMPL_CNVH_REG,  &high);
+       if (ret < 0)
+               goto fail;
+
+       /*
+        * negative value for Discharging
+        * convert 2's compliment into decimal
+        */
+       if (high & 0x10)
+               val = (low | (high << 8) | 0xFFFFE000);
+       else
+               val = (low | (high << 8));
+
+       /*
+        * Convert to unit value in mA
+        * Full scale input voltage is
+        * 63.160mV => LSB = 63.160mV/(4096*res) = 1.542mA
+        * Given a 250ms conversion cycle time the LSB corresponds
+        * to 107.1 nAh. Convert to current by dividing by the conversion
+        * time in hours (250ms = 1 / (3600 * 4)h)
+        * 107.1nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+        */
+       val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) /
+               (1000 * di->bm->fg_res);
+
+       if (di->turn_off_fg) {
+               dev_dbg(di->dev, "%s Disable FG\n", __func__);
+
+               /* Clear any pending read requests */
+               ret = abx500_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+               if (ret)
+                       goto fail;
+
+               /* Stop the CC */
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8500_RTC_CC_CONF_REG, 0);
+               if (ret)
+                       goto fail;
+       }
+       mutex_unlock(&di->cc_lock);
+       (*res) = val;
+
+       return 0;
+fail:
+       mutex_unlock(&di->cc_lock);
+       return ret;
+}
+
+/**
+ * ab8500_fg_inst_curr_blocking() - battery instantaneous current
+ * @di:         pointer to the ab8500_fg structure
+ * @res:       battery instantenous current(on success)
+ *
+ * Returns 0 else error code
+ */
+int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
+{
+       int ret;
+       unsigned long timeout;
+       int res = 0;
+
+       ret = ab8500_fg_inst_curr_start(di);
+       if (ret) {
+               dev_err(di->dev, "Failed to initialize fg_inst\n");
+               return 0;
+       }
+
+       /* Wait for CC to actually start */
+       if (!completion_done(&di->ab8500_fg_started)) {
+               timeout = wait_for_completion_timeout(
+                       &di->ab8500_fg_started,
+                       INS_CURR_TIMEOUT);
+               dev_dbg(di->dev, "Start time: %d ms\n",
+                       jiffies_to_msecs(INS_CURR_TIMEOUT - timeout));
+               if (!timeout) {
+                       ret = -ETIME;
+                       dev_err(di->dev, "completion timed out [%d]\n",
+                               __LINE__);
+                       goto fail;
+               }
+       }
+
+       ret = ab8500_fg_inst_curr_finalize(di, &res);
+       if (ret) {
+               dev_err(di->dev, "Failed to finalize fg_inst\n");
+               return 0;
+       }
+
+       dev_dbg(di->dev, "%s instant current: %d", __func__, res);
+       return res;
+fail:
+       disable_irq(di->irq);
+       mutex_unlock(&di->cc_lock);
+       return ret;
+}
+
+/**
+ * ab8500_fg_acc_cur_work() - average battery current
+ * @work:      pointer to the work_struct structure
+ *
+ * Updated the average battery current obtained from the
+ * coulomb counter.
+ */
+static void ab8500_fg_acc_cur_work(struct work_struct *work)
+{
+       int val;
+       int ret;
+       u8 low, med, high;
+
+       struct ab8500_fg *di = container_of(work,
+               struct ab8500_fg, fg_acc_cur_work);
+
+       mutex_lock(&di->cc_lock);
+       ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_CTRL, RD_NCONV_ACCU_REQ);
+       if (ret)
+               goto exit;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_LOW,  &low);
+       if (ret < 0)
+               goto exit;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_MED,  &med);
+       if (ret < 0)
+               goto exit;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+               AB8500_GASG_CC_NCOV_ACCU_HIGH, &high);
+       if (ret < 0)
+               goto exit;
+
+       /* Check for sign bit in case of negative value, 2's compliment */
+       if (high & 0x10)
+               val = (low | (med << 8) | (high << 16) | 0xFFE00000);
+       else
+               val = (low | (med << 8) | (high << 16));
+
+       /*
+        * Convert to uAh
+        * Given a 250ms conversion cycle time the LSB corresponds
+        * to 112.9 nAh.
+        * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+        */
+       di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10) /
+               (100 * di->bm->fg_res);
+
+       /*
+        * Convert to unit value in mA
+        * by dividing by the conversion
+        * time in hours (= samples / (3600 * 4)h)
+        * and multiply with 1000
+        */
+       di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
+               (1000 * di->bm->fg_res * (di->fg_samples / 4));
+
+       di->flags.conv_done = true;
+
+       mutex_unlock(&di->cc_lock);
+
+       queue_work(di->fg_wq, &di->fg_work);
+
+       dev_dbg(di->dev, "fg_res: %d, fg_samples: %d, gasg: %d, accu_charge: %d \n",
+                               di->bm->fg_res, di->fg_samples, val, di->accu_charge);
+       return;
+exit:
+       dev_err(di->dev,
+               "Failed to read or write gas gauge registers\n");
+       mutex_unlock(&di->cc_lock);
+       queue_work(di->fg_wq, &di->fg_work);
+}
+
+/**
+ * ab8500_fg_bat_voltage() - get battery voltage
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Returns battery voltage(on success) else error code
+ */
+static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
+{
+       int vbat;
+       static int prev;
+
+       vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V);
+       if (vbat < 0) {
+               dev_err(di->dev,
+                       "%s gpadc conversion failed, using previous value\n",
+                       __func__);
+               return prev;
+       }
+
+       prev = vbat;
+       return vbat;
+}
+
+/**
+ * ab8500_fg_volt_to_capacity() - Voltage based capacity
+ * @di:                pointer to the ab8500_fg structure
+ * @voltage:   The voltage to convert to a capacity
+ *
+ * Returns battery capacity in per mille based on voltage
+ */
+static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
+{
+       int i, tbl_size;
+       const struct abx500_v_to_cap *tbl;
+       int cap = 0;
+
+       tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl,
+       tbl_size = di->bm->bat_type[di->bm->batt_id].n_v_cap_tbl_elements;
+
+       for (i = 0; i < tbl_size; ++i) {
+               if (voltage > tbl[i].voltage)
+                       break;
+       }
+
+       if ((i > 0) && (i < tbl_size)) {
+               cap = interpolate(voltage,
+                       tbl[i].voltage,
+                       tbl[i].capacity * 10,
+                       tbl[i-1].voltage,
+                       tbl[i-1].capacity * 10);
+       } else if (i == 0) {
+               cap = 1000;
+       } else {
+               cap = 0;
+       }
+
+       dev_dbg(di->dev, "%s Vbat: %d, Cap: %d per mille",
+               __func__, voltage, cap);
+
+       return cap;
+}
+
+/**
+ * ab8500_fg_uncomp_volt_to_capacity() - Uncompensated voltage based capacity
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is not compensated
+ * for the voltage drop due to the load
+ */
+static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
+{
+       di->vbat = ab8500_fg_bat_voltage(di);
+       return ab8500_fg_volt_to_capacity(di, di->vbat);
+}
+
+/**
+ * ab8500_fg_battery_resistance() - Returns the battery inner resistance
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Returns battery inner resistance added with the fuel gauge resistor value
+ * to get the total resistance in the whole link from gnd to bat+ node.
+ */
+static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
+{
+       int i, tbl_size;
+       const struct batres_vs_temp *tbl;
+       int resist = 0;
+
+       tbl = di->bm->bat_type[di->bm->batt_id].batres_tbl;
+       tbl_size = di->bm->bat_type[di->bm->batt_id].n_batres_tbl_elements;
+
+       for (i = 0; i < tbl_size; ++i) {
+               if (di->bat_temp / 10 > tbl[i].temp)
+                       break;
+       }
+
+       if ((i > 0) && (i < tbl_size)) {
+               resist = interpolate(di->bat_temp / 10,
+                       tbl[i].temp,
+                       tbl[i].resist,
+                       tbl[i-1].temp,
+                       tbl[i-1].resist);
+       } else if (i == 0) {
+               resist = tbl[0].resist;
+       } else {
+               resist = tbl[tbl_size - 1].resist;
+       }
+
+       dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d"
+           " fg resistance %d, total: %d (mOhm)\n",
+               __func__, di->bat_temp, resist, di->bm->fg_res / 10,
+               (di->bm->fg_res / 10) + resist);
+
+       /* fg_res variable is in 0.1mOhm */
+       resist += di->bm->fg_res / 10;
+
+       return resist;
+}
+
+/**
+ * ab8500_fg_load_comp_volt_to_capacity() - Load compensated voltage based capacity
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is load compensated
+ * for the voltage drop
+ */
+static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
+{
+       int vbat_comp, res;
+       int i = 0;
+       int vbat = 0;
+
+       ab8500_fg_inst_curr_start(di);
+
+       do {
+               vbat += ab8500_fg_bat_voltage(di);
+               i++;
+               usleep_range(5000, 6000);
+       } while (!ab8500_fg_inst_curr_done(di));
+
+       ab8500_fg_inst_curr_finalize(di, &di->inst_curr);
+
+       di->vbat = vbat / i;
+       res = ab8500_fg_battery_resistance(di);
+
+       /* Use Ohms law to get the load compensated voltage */
+       vbat_comp = di->vbat - (di->inst_curr * res) / 1000;
+
+       dev_dbg(di->dev, "%s Measured Vbat: %dmV,Compensated Vbat %dmV, "
+               "R: %dmOhm, Current: %dmA Vbat Samples: %d\n",
+               __func__, di->vbat, vbat_comp, res, di->inst_curr, i);
+
+       return ab8500_fg_volt_to_capacity(di, vbat_comp);
+}
+
+/**
+ * ab8500_fg_convert_mah_to_permille() - Capacity in mAh to permille
+ * @di:                pointer to the ab8500_fg structure
+ * @cap_mah:   capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in permille
+ */
+static int ab8500_fg_convert_mah_to_permille(struct ab8500_fg *di, int cap_mah)
+{
+       return (cap_mah * 1000) / di->bat_cap.max_mah_design;
+}
+
+/**
+ * ab8500_fg_convert_permille_to_mah() - Capacity in permille to mAh
+ * @di:                pointer to the ab8500_fg structure
+ * @cap_pm:    capacity in permille
+ *
+ * Converts capacity in permille to capacity in mAh
+ */
+static int ab8500_fg_convert_permille_to_mah(struct ab8500_fg *di, int cap_pm)
+{
+       return cap_pm * di->bat_cap.max_mah_design / 1000;
+}
+
+/**
+ * ab8500_fg_convert_mah_to_uwh() - Capacity in mAh to uWh
+ * @di:                pointer to the ab8500_fg structure
+ * @cap_mah:   capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in uWh
+ */
+static int ab8500_fg_convert_mah_to_uwh(struct ab8500_fg *di, int cap_mah)
+{
+       u64 div_res;
+       u32 div_rem;
+
+       div_res = ((u64) cap_mah) * ((u64) di->vbat_nom);
+       div_rem = do_div(div_res, 1000);
+
+       /* Make sure to round upwards if necessary */
+       if (div_rem >= 1000 / 2)
+               div_res++;
+
+       return (int) div_res;
+}
+
+/**
+ * ab8500_fg_calc_cap_charging() - Calculate remaining capacity while charging
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. The filter is filled with this capacity
+ */
+static int ab8500_fg_calc_cap_charging(struct ab8500_fg *di)
+{
+       dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+               __func__,
+               di->bat_cap.mah,
+               di->accu_charge);
+
+       /* Capacity should not be less than 0 */
+       if (di->bat_cap.mah + di->accu_charge > 0)
+               di->bat_cap.mah += di->accu_charge;
+       else
+               di->bat_cap.mah = 0;
+       /*
+        * We force capacity to 100% once when the algorithm
+        * reports that it's full.
+        */
+       if (di->bat_cap.mah >= di->bat_cap.max_mah_design ||
+               di->flags.force_full) {
+               di->bat_cap.mah = di->bat_cap.max_mah_design;
+       }
+
+       ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+       di->bat_cap.permille =
+               ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+       /* We need to update battery voltage and inst current when charging */
+       di->vbat = ab8500_fg_bat_voltage(di);
+       di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+       return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_calc_cap_discharge_voltage() - Capacity in discharge with voltage
+ * @di:                pointer to the ab8500_fg structure
+ * @comp:      if voltage should be load compensated before capacity calc
+ *
+ * Return the capacity in mAh based on the battery voltage. The voltage can
+ * either be load compensated or not. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab8500_fg_calc_cap_discharge_voltage(struct ab8500_fg *di, bool comp)
+{
+       int permille, mah;
+
+       if (comp)
+               permille = ab8500_fg_load_comp_volt_to_capacity(di);
+       else
+               permille = ab8500_fg_uncomp_volt_to_capacity(di);
+
+       mah = ab8500_fg_convert_permille_to_mah(di, permille);
+
+       di->bat_cap.mah = ab8500_fg_add_cap_sample(di, mah);
+       di->bat_cap.permille =
+               ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+       return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_calc_cap_discharge_fg() - Capacity in discharge with FG
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab8500_fg_calc_cap_discharge_fg(struct ab8500_fg *di)
+{
+       int permille_volt, permille;
+
+       dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+               __func__,
+               di->bat_cap.mah,
+               di->accu_charge);
+
+       /* Capacity should not be less than 0 */
+       if (di->bat_cap.mah + di->accu_charge > 0)
+               di->bat_cap.mah += di->accu_charge;
+       else
+               di->bat_cap.mah = 0;
+
+       if (di->bat_cap.mah >= di->bat_cap.max_mah_design)
+               di->bat_cap.mah = di->bat_cap.max_mah_design;
+
+       /*
+        * Check against voltage based capacity. It can not be lower
+        * than what the uncompensated voltage says
+        */
+       permille = ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+       permille_volt = ab8500_fg_uncomp_volt_to_capacity(di);
+
+       if (permille < permille_volt) {
+               di->bat_cap.permille = permille_volt;
+               di->bat_cap.mah = ab8500_fg_convert_permille_to_mah(di,
+                       di->bat_cap.permille);
+
+               dev_dbg(di->dev, "%s voltage based: perm %d perm_volt %d\n",
+                       __func__,
+                       permille,
+                       permille_volt);
+
+               ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+       } else {
+               ab8500_fg_fill_cap_sample(di, di->bat_cap.mah);
+               di->bat_cap.permille =
+                       ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+       }
+
+       return di->bat_cap.mah;
+}
+
+/**
+ * ab8500_fg_capacity_level() - Get the battery capacity level
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Get the battery capacity level based on the capacity in percent
+ */
+static int ab8500_fg_capacity_level(struct ab8500_fg *di)
+{
+       int ret, percent;
+
+       percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
+
+       if (percent <= di->bm->cap_levels->critical ||
+               di->flags.low_bat)
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+       else if (percent <= di->bm->cap_levels->low)
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+       else if (percent <= di->bm->cap_levels->normal)
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+       else if (percent <= di->bm->cap_levels->high)
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+       else
+               ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+
+       return ret;
+}
+
+/**
+ * ab8500_fg_calculate_scaled_capacity() - Capacity scaling
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Calculates the capacity to be shown to upper layers. Scales the capacity
+ * to have 100% as a reference from the actual capacity upon removal of charger
+ * when charging is in maintenance mode.
+ */
+static int ab8500_fg_calculate_scaled_capacity(struct ab8500_fg *di)
+{
+       struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale;
+       int capacity = di->bat_cap.prev_percent;
+
+       if (!cs->enable)
+               return capacity;
+
+       /*
+        * As long as we are in fully charge mode scale the capacity
+        * to show 100%.
+        */
+       if (di->flags.fully_charged) {
+               cs->cap_to_scale[0] = 100;
+               cs->cap_to_scale[1] =
+                       max(capacity, di->bm->fg_params->maint_thres);
+               dev_dbg(di->dev, "Scale cap with %d/%d\n",
+                        cs->cap_to_scale[0], cs->cap_to_scale[1]);
+       }
+
+       /* Calculates the scaled capacity. */
+       if ((cs->cap_to_scale[0] != cs->cap_to_scale[1])
+                                       && (cs->cap_to_scale[1] > 0))
+               capacity = min(100,
+                                DIV_ROUND_CLOSEST(di->bat_cap.prev_percent *
+                                                cs->cap_to_scale[0],
+                                                cs->cap_to_scale[1]));
+
+       if (di->flags.charging) {
+               if (capacity < cs->disable_cap_level) {
+                       cs->disable_cap_level = capacity;
+                       dev_dbg(di->dev, "Cap to stop scale lowered %d%%\n",
+                               cs->disable_cap_level);
+               } else if (!di->flags.fully_charged) {
+                       if (di->bat_cap.prev_percent >=
+                           cs->disable_cap_level) {
+                               dev_dbg(di->dev, "Disabling scaled capacity\n");
+                               cs->enable = false;
+                               capacity = di->bat_cap.prev_percent;
+                       } else {
+                               dev_dbg(di->dev,
+                                       "Waiting in cap to level %d%%\n",
+                                       cs->disable_cap_level);
+                               capacity = cs->disable_cap_level;
+                       }
+               }
+       }
+
+       return capacity;
+}
+
+/**
+ * ab8500_fg_update_cap_scalers() - Capacity scaling
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * To be called when state change from charge<->discharge to update
+ * the capacity scalers.
+ */
+static void ab8500_fg_update_cap_scalers(struct ab8500_fg *di)
+{
+       struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale;
+
+       if (!cs->enable)
+               return;
+       if (di->flags.charging) {
+               di->bat_cap.cap_scale.disable_cap_level =
+                       di->bat_cap.cap_scale.scaled_cap;
+               dev_dbg(di->dev, "Cap to stop scale at charge %d%%\n",
+                               di->bat_cap.cap_scale.disable_cap_level);
+       } else {
+               if (cs->scaled_cap != 100) {
+                       cs->cap_to_scale[0] = cs->scaled_cap;
+                       cs->cap_to_scale[1] = di->bat_cap.prev_percent;
+               } else {
+                       cs->cap_to_scale[0] = 100;
+                       cs->cap_to_scale[1] =
+                               max(di->bat_cap.prev_percent,
+                                   di->bm->fg_params->maint_thres);
+               }
+
+               dev_dbg(di->dev, "Cap to scale at discharge %d/%d\n",
+                               cs->cap_to_scale[0], cs->cap_to_scale[1]);
+       }
+}
+
+/**
+ * ab8500_fg_check_capacity_limits() - Check if capacity has changed
+ * @di:                pointer to the ab8500_fg structure
+ * @init:      capacity is allowed to go up in init mode
+ *
+ * Check if capacity or capacity limit has changed and notify the system
+ * about it using the power_supply framework
+ */
+static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
+{
+       bool changed = false;
+       int percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
+
+       di->bat_cap.level = ab8500_fg_capacity_level(di);
+
+       if (di->bat_cap.level != di->bat_cap.prev_level) {
+               /*
+                * We do not allow reported capacity level to go up
+                * unless we're charging or if we're in init
+                */
+               if (!(!di->flags.charging && di->bat_cap.level >
+                       di->bat_cap.prev_level) || init) {
+                       dev_dbg(di->dev, "level changed from %d to %d\n",
+                               di->bat_cap.prev_level,
+                               di->bat_cap.level);
+                       di->bat_cap.prev_level = di->bat_cap.level;
+                       changed = true;
+               } else {
+                       dev_dbg(di->dev, "level not allowed to go up "
+                               "since no charger is connected: %d to %d\n",
+                               di->bat_cap.prev_level,
+                               di->bat_cap.level);
+               }
+       }
+
+       /*
+        * If we have received the LOW_BAT IRQ, set capacity to 0 to initiate
+        * shutdown
+        */
+       if (di->flags.low_bat) {
+               dev_dbg(di->dev, "Battery low, set capacity to 0\n");
+               di->bat_cap.prev_percent = 0;
+               di->bat_cap.permille = 0;
+               percent = 0;
+               di->bat_cap.prev_mah = 0;
+               di->bat_cap.mah = 0;
+               changed = true;
+       } else if (di->flags.fully_charged) {
+               /*
+                * We report 100% if algorithm reported fully charged
+                * and show 100% during maintenance charging (scaling).
+                */
+               if (di->flags.force_full) {
+                       di->bat_cap.prev_percent = percent;
+                       di->bat_cap.prev_mah = di->bat_cap.mah;
+
+                       changed = true;
+
+                       if (!di->bat_cap.cap_scale.enable &&
+                                               di->bm->capacity_scaling) {
+                               di->bat_cap.cap_scale.enable = true;
+                               di->bat_cap.cap_scale.cap_to_scale[0] = 100;
+                               di->bat_cap.cap_scale.cap_to_scale[1] =
+                                               di->bat_cap.prev_percent;
+                               di->bat_cap.cap_scale.disable_cap_level = 100;
+                       }
+               } else if (di->bat_cap.prev_percent != percent) {
+                       dev_dbg(di->dev,
+                               "battery reported full "
+                               "but capacity dropping: %d\n",
+                               percent);
+                       di->bat_cap.prev_percent = percent;
+                       di->bat_cap.prev_mah = di->bat_cap.mah;
+
+                       changed = true;
+               }
+       } else if (di->bat_cap.prev_percent != percent) {
+               if (percent == 0) {
+                       /*
+                        * We will not report 0% unless we've got
+                        * the LOW_BAT IRQ, no matter what the FG
+                        * algorithm says.
+                        */
+                       di->bat_cap.prev_percent = 1;
+                       percent = 1;
+
+                       changed = true;
+               } else if (!(!di->flags.charging &&
+                       percent > di->bat_cap.prev_percent) || init) {
+                       /*
+                        * We do not allow reported capacity to go up
+                        * unless we're charging or if we're in init
+                        */
+                       dev_dbg(di->dev,
+                               "capacity changed from %d to %d (%d)\n",
+                               di->bat_cap.prev_percent,
+                               percent,
+                               di->bat_cap.permille);
+                       di->bat_cap.prev_percent = percent;
+                       di->bat_cap.prev_mah = di->bat_cap.mah;
+
+                       changed = true;
+               } else {
+                       dev_dbg(di->dev, "capacity not allowed to go up since "
+                               "no charger is connected: %d to %d (%d)\n",
+                               di->bat_cap.prev_percent,
+                               percent,
+                               di->bat_cap.permille);
+               }
+       }
+
+       if (changed) {
+               if (di->bm->capacity_scaling) {
+                       di->bat_cap.cap_scale.scaled_cap =
+                               ab8500_fg_calculate_scaled_capacity(di);
+
+                       dev_info(di->dev, "capacity=%d (%d)\n",
+                               di->bat_cap.prev_percent,
+                               di->bat_cap.cap_scale.scaled_cap);
+               }
+               power_supply_changed(di->fg_psy);
+               if (di->flags.fully_charged && di->flags.force_full) {
+                       dev_dbg(di->dev, "Battery full, notifying.\n");
+                       di->flags.force_full = false;
+                       sysfs_notify(&di->fg_kobject, NULL, "charge_full");
+               }
+               sysfs_notify(&di->fg_kobject, NULL, "charge_now");
+       }
+}
+
+static void ab8500_fg_charge_state_to(struct ab8500_fg *di,
+       enum ab8500_fg_charge_state new_state)
+{
+       dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n",
+               di->charge_state,
+               charge_state[di->charge_state],
+               new_state,
+               charge_state[new_state]);
+
+       di->charge_state = new_state;
+}
+
+static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
+       enum ab8500_fg_discharge_state new_state)
+{
+       dev_dbg(di->dev, "Disharge state from %d [%s] to %d [%s]\n",
+               di->discharge_state,
+               discharge_state[di->discharge_state],
+               new_state,
+               discharge_state[new_state]);
+
+       di->discharge_state = new_state;
+}
+
+/**
+ * ab8500_fg_algorithm_charging() - FG algorithm for when charging
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're charging
+ */
+static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
+{
+       /*
+        * If we change to discharge mode
+        * we should start with recovery
+        */
+       if (di->discharge_state != AB8500_FG_DISCHARGE_INIT_RECOVERY)
+               ab8500_fg_discharge_state_to(di,
+                       AB8500_FG_DISCHARGE_INIT_RECOVERY);
+
+       switch (di->charge_state) {
+       case AB8500_FG_CHARGE_INIT:
+               di->fg_samples = SEC_TO_SAMPLE(
+                       di->bm->fg_params->accu_charging);
+
+               ab8500_fg_coulomb_counter(di, true);
+               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT);
+
+               break;
+
+       case AB8500_FG_CHARGE_READOUT:
+               /*
+                * Read the FG and calculate the new capacity
+                */
+               mutex_lock(&di->cc_lock);
+               if (!di->flags.conv_done && !di->flags.force_full) {
+                       /* Wasn't the CC IRQ that got us here */
+                       mutex_unlock(&di->cc_lock);
+                       dev_dbg(di->dev, "%s CC conv not done\n",
+                               __func__);
+
+                       break;
+               }
+               di->flags.conv_done = false;
+               mutex_unlock(&di->cc_lock);
+
+               ab8500_fg_calc_cap_charging(di);
+
+               break;
+
+       default:
+               break;
+       }
+
+       /* Check capacity limits */
+       ab8500_fg_check_capacity_limits(di, false);
+}
+
+static void force_capacity(struct ab8500_fg *di)
+{
+       int cap;
+
+       ab8500_fg_clear_cap_samples(di);
+       cap = di->bat_cap.user_mah;
+       if (cap > di->bat_cap.max_mah_design) {
+               dev_dbg(di->dev, "Remaining cap %d can't be bigger than total"
+                       " %d\n", cap, di->bat_cap.max_mah_design);
+               cap = di->bat_cap.max_mah_design;
+       }
+       ab8500_fg_fill_cap_sample(di, di->bat_cap.user_mah);
+       di->bat_cap.permille = ab8500_fg_convert_mah_to_permille(di, cap);
+       di->bat_cap.mah = cap;
+       ab8500_fg_check_capacity_limits(di, true);
+}
+
+static bool check_sysfs_capacity(struct ab8500_fg *di)
+{
+       int cap, lower, upper;
+       int cap_permille;
+
+       cap = di->bat_cap.user_mah;
+
+       cap_permille = ab8500_fg_convert_mah_to_permille(di,
+               di->bat_cap.user_mah);
+
+       lower = di->bat_cap.permille - di->bm->fg_params->user_cap_limit * 10;
+       upper = di->bat_cap.permille + di->bm->fg_params->user_cap_limit * 10;
+
+       if (lower < 0)
+               lower = 0;
+       /* 1000 is permille, -> 100 percent */
+       if (upper > 1000)
+               upper = 1000;
+
+       dev_dbg(di->dev, "Capacity limits:"
+               " (Lower: %d User: %d Upper: %d) [user: %d, was: %d]\n",
+               lower, cap_permille, upper, cap, di->bat_cap.mah);
+
+       /* If within limits, use the saved capacity and exit estimation...*/
+       if (cap_permille > lower && cap_permille < upper) {
+               dev_dbg(di->dev, "OK! Using users cap %d uAh now\n", cap);
+               force_capacity(di);
+               return true;
+       }
+       dev_dbg(di->dev, "Capacity from user out of limits, ignoring");
+       return false;
+}
+
+/**
+ * ab8500_fg_algorithm_discharging() - FG algorithm for when discharging
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're discharging
+ */
+static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
+{
+       int sleep_time;
+
+       /* If we change to charge mode we should start with init */
+       if (di->charge_state != AB8500_FG_CHARGE_INIT)
+               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+
+       switch (di->discharge_state) {
+       case AB8500_FG_DISCHARGE_INIT:
+               /* We use the FG IRQ to work on */
+               di->init_cnt = 0;
+               di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
+               ab8500_fg_coulomb_counter(di, true);
+               ab8500_fg_discharge_state_to(di,
+                       AB8500_FG_DISCHARGE_INITMEASURING);
+
+               /* Intentional fallthrough */
+       case AB8500_FG_DISCHARGE_INITMEASURING:
+               /*
+                * Discard a number of samples during startup.
+                * After that, use compensated voltage for a few
+                * samples to get an initial capacity.
+                * Then go to READOUT
+                */
+               sleep_time = di->bm->fg_params->init_timer;
+
+               /* Discard the first [x] seconds */
+               if (di->init_cnt > di->bm->fg_params->init_discard_time) {
+                       ab8500_fg_calc_cap_discharge_voltage(di, true);
+
+                       ab8500_fg_check_capacity_limits(di, true);
+               }
+
+               di->init_cnt += sleep_time;
+               if (di->init_cnt > di->bm->fg_params->init_total_time)
+                       ab8500_fg_discharge_state_to(di,
+                               AB8500_FG_DISCHARGE_READOUT_INIT);
+
+               break;
+
+       case AB8500_FG_DISCHARGE_INIT_RECOVERY:
+               di->recovery_cnt = 0;
+               di->recovery_needed = true;
+               ab8500_fg_discharge_state_to(di,
+                       AB8500_FG_DISCHARGE_RECOVERY);
+
+               /* Intentional fallthrough */
+
+       case AB8500_FG_DISCHARGE_RECOVERY:
+               sleep_time = di->bm->fg_params->recovery_sleep_timer;
+
+               /*
+                * We should check the power consumption
+                * If low, go to READOUT (after x min) or
+                * RECOVERY_SLEEP if time left.
+                * If high, go to READOUT
+                */
+               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+               if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
+                       if (di->recovery_cnt >
+                               di->bm->fg_params->recovery_total_time) {
+                               di->fg_samples = SEC_TO_SAMPLE(
+                                       di->bm->fg_params->accu_high_curr);
+                               ab8500_fg_coulomb_counter(di, true);
+                               ab8500_fg_discharge_state_to(di,
+                                       AB8500_FG_DISCHARGE_READOUT);
+                               di->recovery_needed = false;
+                       } else {
+                               queue_delayed_work(di->fg_wq,
+                                       &di->fg_periodic_work,
+                                       sleep_time * HZ);
+                       }
+                       di->recovery_cnt += sleep_time;
+               } else {
+                       di->fg_samples = SEC_TO_SAMPLE(
+                               di->bm->fg_params->accu_high_curr);
+                       ab8500_fg_coulomb_counter(di, true);
+                       ab8500_fg_discharge_state_to(di,
+                               AB8500_FG_DISCHARGE_READOUT);
+               }
+               break;
+
+       case AB8500_FG_DISCHARGE_READOUT_INIT:
+               di->fg_samples = SEC_TO_SAMPLE(
+                       di->bm->fg_params->accu_high_curr);
+               ab8500_fg_coulomb_counter(di, true);
+               ab8500_fg_discharge_state_to(di,
+                               AB8500_FG_DISCHARGE_READOUT);
+               break;
+
+       case AB8500_FG_DISCHARGE_READOUT:
+               di->inst_curr = ab8500_fg_inst_curr_blocking(di);
+
+               if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
+                       /* Detect mode change */
+                       if (di->high_curr_mode) {
+                               di->high_curr_mode = false;
+                               di->high_curr_cnt = 0;
+                       }
+
+                       if (di->recovery_needed) {
+                               ab8500_fg_discharge_state_to(di,
+                                       AB8500_FG_DISCHARGE_INIT_RECOVERY);
+
+                               queue_delayed_work(di->fg_wq,
+                                       &di->fg_periodic_work, 0);
+
+                               break;
+                       }
+
+                       ab8500_fg_calc_cap_discharge_voltage(di, true);
+               } else {
+                       mutex_lock(&di->cc_lock);
+                       if (!di->flags.conv_done) {
+                               /* Wasn't the CC IRQ that got us here */
+                               mutex_unlock(&di->cc_lock);
+                               dev_dbg(di->dev, "%s CC conv not done\n",
+                                       __func__);
+
+                               break;
+                       }
+                       di->flags.conv_done = false;
+                       mutex_unlock(&di->cc_lock);
+
+                       /* Detect mode change */
+                       if (!di->high_curr_mode) {
+                               di->high_curr_mode = true;
+                               di->high_curr_cnt = 0;
+                       }
+
+                       di->high_curr_cnt +=
+                               di->bm->fg_params->accu_high_curr;
+                       if (di->high_curr_cnt >
+                               di->bm->fg_params->high_curr_time)
+                               di->recovery_needed = true;
+
+                       ab8500_fg_calc_cap_discharge_fg(di);
+               }
+
+               ab8500_fg_check_capacity_limits(di, false);
+
+               break;
+
+       case AB8500_FG_DISCHARGE_WAKEUP:
+               ab8500_fg_calc_cap_discharge_voltage(di, true);
+
+               di->fg_samples = SEC_TO_SAMPLE(
+                       di->bm->fg_params->accu_high_curr);
+               ab8500_fg_coulomb_counter(di, true);
+               ab8500_fg_discharge_state_to(di,
+                               AB8500_FG_DISCHARGE_READOUT);
+
+               ab8500_fg_check_capacity_limits(di, false);
+
+               break;
+
+       default:
+               break;
+       }
+}
+
+/**
+ * ab8500_fg_algorithm_calibrate() - Internal columb counter offset calibration
+ * @di:                pointer to the ab8500_fg structure
+ *
+ */
+static void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di)
+{
+       int ret;
+
+       switch (di->calib_state) {
+       case AB8500_FG_CALIB_INIT:
+               dev_dbg(di->dev, "Calibration ongoing...\n");
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       CC_INT_CAL_N_AVG_MASK, CC_INT_CAL_SAMPLES_8);
+               if (ret < 0)
+                       goto err;
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA);
+               if (ret < 0)
+                       goto err;
+               di->calib_state = AB8500_FG_CALIB_WAIT;
+               break;
+       case AB8500_FG_CALIB_END:
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                       AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+                       CC_MUXOFFSET, CC_MUXOFFSET);
+               if (ret < 0)
+                       goto err;
+               di->flags.calibrate = false;
+               dev_dbg(di->dev, "Calibration done...\n");
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+               break;
+       case AB8500_FG_CALIB_WAIT:
+               dev_dbg(di->dev, "Calibration WFI\n");
+       default:
+               break;
+       }
+       return;
+err:
+       /* Something went wrong, don't calibrate then */
+       dev_err(di->dev, "failed to calibrate the CC\n");
+       di->flags.calibrate = false;
+       di->calib_state = AB8500_FG_CALIB_INIT;
+       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+}
+
+/**
+ * ab8500_fg_algorithm() - Entry point for the FG algorithm
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Entry point for the battery capacity calculation state machine
+ */
+static void ab8500_fg_algorithm(struct ab8500_fg *di)
+{
+       if (di->flags.calibrate)
+               ab8500_fg_algorithm_calibrate(di);
+       else {
+               if (di->flags.charging)
+                       ab8500_fg_algorithm_charging(di);
+               else
+                       ab8500_fg_algorithm_discharging(di);
+       }
+
+       dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d %d "
+               "%d %d %d %d %d %d %d\n",
+               di->bat_cap.max_mah_design,
+               di->bat_cap.max_mah,
+               di->bat_cap.mah,
+               di->bat_cap.permille,
+               di->bat_cap.level,
+               di->bat_cap.prev_mah,
+               di->bat_cap.prev_percent,
+               di->bat_cap.prev_level,
+               di->vbat,
+               di->inst_curr,
+               di->avg_curr,
+               di->accu_charge,
+               di->flags.charging,
+               di->charge_state,
+               di->discharge_state,
+               di->high_curr_mode,
+               di->recovery_needed);
+}
+
+/**
+ * ab8500_fg_periodic_work() - Run the FG state machine periodically
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for periodic work
+ */
+static void ab8500_fg_periodic_work(struct work_struct *work)
+{
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+               fg_periodic_work.work);
+
+       if (di->init_capacity) {
+               /* Get an initial capacity calculation */
+               ab8500_fg_calc_cap_discharge_voltage(di, true);
+               ab8500_fg_check_capacity_limits(di, true);
+               di->init_capacity = false;
+
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       } else if (di->flags.user_cap) {
+               if (check_sysfs_capacity(di)) {
+                       ab8500_fg_check_capacity_limits(di, true);
+                       if (di->flags.charging)
+                               ab8500_fg_charge_state_to(di,
+                                       AB8500_FG_CHARGE_INIT);
+                       else
+                               ab8500_fg_discharge_state_to(di,
+                                       AB8500_FG_DISCHARGE_READOUT_INIT);
+               }
+               di->flags.user_cap = false;
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       } else
+               ab8500_fg_algorithm(di);
+
+}
+
+/**
+ * ab8500_fg_check_hw_failure_work() - Check OVV_BAT condition
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the OVV_BAT condition
+ */
+static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
+{
+       int ret;
+       u8 reg_value;
+
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+               fg_check_hw_failure_work.work);
+
+       /*
+        * If we have had a battery over-voltage situation,
+        * check ovv-bit to see if it should be reset.
+        */
+       ret = abx500_get_register_interruptible(di->dev,
+               AB8500_CHARGER, AB8500_CH_STAT_REG,
+               &reg_value);
+       if (ret < 0) {
+               dev_err(di->dev, "%s ab8500 read failed\n", __func__);
+               return;
+       }
+       if ((reg_value & BATT_OVV) == BATT_OVV) {
+               if (!di->flags.bat_ovv) {
+                       dev_dbg(di->dev, "Battery OVV\n");
+                       di->flags.bat_ovv = true;
+                       power_supply_changed(di->fg_psy);
+               }
+               /* Not yet recovered from ovv, reschedule this test */
+               queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
+                                  HZ);
+               } else {
+                       dev_dbg(di->dev, "Battery recovered from OVV\n");
+                       di->flags.bat_ovv = false;
+                       power_supply_changed(di->fg_psy);
+       }
+}
+
+/**
+ * ab8500_fg_low_bat_work() - Check LOW_BAT condition
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for checking the LOW_BAT condition
+ */
+static void ab8500_fg_low_bat_work(struct work_struct *work)
+{
+       int vbat;
+
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+               fg_low_bat_work.work);
+
+       vbat = ab8500_fg_bat_voltage(di);
+
+       /* Check if LOW_BAT still fulfilled */
+       if (vbat < di->bm->fg_params->lowbat_threshold) {
+               /* Is it time to shut down? */
+               if (di->low_bat_cnt < 1) {
+                       di->flags.low_bat = true;
+                       dev_warn(di->dev, "Shut down pending...\n");
+               } else {
+                       /*
+                       * Else we need to re-schedule this check to be able to detect
+                       * if the voltage increases again during charging or
+                       * due to decreasing load.
+                       */
+                       di->low_bat_cnt--;
+                       dev_warn(di->dev, "Battery voltage still LOW\n");
+                       queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+                               round_jiffies(LOW_BAT_CHECK_INTERVAL));
+               }
+       } else {
+               di->flags.low_bat_delay = false;
+               di->low_bat_cnt = 10;
+               dev_warn(di->dev, "Battery voltage OK again\n");
+       }
+
+       /* This is needed to dispatch LOW_BAT */
+       ab8500_fg_check_capacity_limits(di, false);
+}
+
+/**
+ * ab8500_fg_battok_calc - calculate the bit pattern corresponding
+ * to the target voltage.
+ * @di:       pointer to the ab8500_fg structure
+ * @target    target voltage
+ *
+ * Returns bit pattern closest to the target voltage
+ * valid return values are 0-14. (0-BATT_OK_MAX_NR_INCREMENTS)
+ */
+
+static int ab8500_fg_battok_calc(struct ab8500_fg *di, int target)
+{
+       if (target > BATT_OK_MIN +
+               (BATT_OK_INCREMENT * BATT_OK_MAX_NR_INCREMENTS))
+               return BATT_OK_MAX_NR_INCREMENTS;
+       if (target < BATT_OK_MIN)
+               return 0;
+       return (target - BATT_OK_MIN) / BATT_OK_INCREMENT;
+}
+
+/**
+ * ab8500_fg_battok_init_hw_register - init battok levels
+ * @di:       pointer to the ab8500_fg structure
+ *
+ */
+
+static int ab8500_fg_battok_init_hw_register(struct ab8500_fg *di)
+{
+       int selected;
+       int sel0;
+       int sel1;
+       int cbp_sel0;
+       int cbp_sel1;
+       int ret;
+       int new_val;
+
+       sel0 = di->bm->fg_params->battok_falling_th_sel0;
+       sel1 = di->bm->fg_params->battok_raising_th_sel1;
+
+       cbp_sel0 = ab8500_fg_battok_calc(di, sel0);
+       cbp_sel1 = ab8500_fg_battok_calc(di, sel1);
+
+       selected = BATT_OK_MIN + cbp_sel0 * BATT_OK_INCREMENT;
+
+       if (selected != sel0)
+               dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n",
+                       sel0, selected, cbp_sel0);
+
+       selected = BATT_OK_MIN + cbp_sel1 * BATT_OK_INCREMENT;
+
+       if (selected != sel1)
+               dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n",
+                       sel1, selected, cbp_sel1);
+
+       new_val = cbp_sel0 | (cbp_sel1 << 4);
+
+       dev_dbg(di->dev, "using: %x %d %d\n", new_val, cbp_sel0, cbp_sel1);
+       ret = abx500_set_register_interruptible(di->dev, AB8500_SYS_CTRL2_BLOCK,
+               AB8500_BATT_OK_REG, new_val);
+       return ret;
+}
+
+/**
+ * ab8500_fg_instant_work() - Run the FG state machine instantly
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for instant work
+ */
+static void ab8500_fg_instant_work(struct work_struct *work)
+{
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg, fg_work);
+
+       ab8500_fg_algorithm(di);
+}
+
+/**
+ * ab8500_fg_cc_data_end_handler() - end of data conversion isr.
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+       if (!di->nbr_cceoc_irq_cnt) {
+               di->nbr_cceoc_irq_cnt++;
+               complete(&di->ab8500_fg_started);
+       } else {
+               di->nbr_cceoc_irq_cnt = 0;
+               complete(&di->ab8500_fg_complete);
+       }
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cc_int_calib_handler () - end of calibration isr.
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+       di->calib_state = AB8500_FG_CALIB_END;
+       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+
+       queue_work(di->fg_wq, &di->fg_acc_cur_work);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_batt_ovv_handler() - Battery OVV occured
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+
+       dev_dbg(di->dev, "Battery OVV\n");
+
+       /* Schedule a new HW failure check */
+       queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_lowbatf_handler() - Battery voltage is below LOW threshold
+ * @irq:       interrupt number
+ * @_di:       pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di)
+{
+       struct ab8500_fg *di = _di;
+
+       /* Initiate handling in ab8500_fg_low_bat_work() if not already initiated. */
+       if (!di->flags.low_bat_delay) {
+               dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
+               di->flags.low_bat_delay = true;
+               /*
+                * Start a timer to check LOW_BAT again after some time
+                * This is done to avoid shutdown on single voltage dips
+                */
+               queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+                       round_jiffies(LOW_BAT_CHECK_INTERVAL));
+       }
+       return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_get_property() - get the fg properties
+ * @psy:       pointer to the power_supply structure
+ * @psp:       pointer to the power_supply_property structure
+ * @val:       pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * fg properties by reading the sysfs files.
+ * voltage_now:                battery voltage
+ * current_now:                battery instant current
+ * current_avg:                battery average current
+ * charge_full_design: capacity where battery is considered full
+ * charge_now:         battery capacity in nAh
+ * capacity:           capacity in percent
+ * capacity_level:     capacity level
+ *
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab8500_fg_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       /*
+        * If battery is identified as unknown and charging of unknown
+        * batteries is disabled, we always report 100% capacity and
+        * capacity level UNKNOWN, since we can't calculate
+        * remaining capacity
+        */
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (di->flags.bat_ovv)
+                       val->intval = BATT_OVV_VALUE * 1000;
+               else
+                       val->intval = di->vbat * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = di->inst_curr * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               val->intval = di->avg_curr * 1000;
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+               val->intval = ab8500_fg_convert_mah_to_uwh(di,
+                               di->bat_cap.max_mah_design);
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_FULL:
+               val->intval = ab8500_fg_convert_mah_to_uwh(di,
+                               di->bat_cap.max_mah);
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_NOW:
+               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
+                               di->flags.batt_id_received)
+                       val->intval = ab8500_fg_convert_mah_to_uwh(di,
+                                       di->bat_cap.max_mah);
+               else
+                       val->intval = ab8500_fg_convert_mah_to_uwh(di,
+                                       di->bat_cap.prev_mah);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = di->bat_cap.max_mah_design;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               val->intval = di->bat_cap.max_mah;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
+                               di->flags.batt_id_received)
+                       val->intval = di->bat_cap.max_mah;
+               else
+                       val->intval = di->bat_cap.prev_mah;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
+                               di->flags.batt_id_received)
+                       val->intval = 100;
+               else
+                       val->intval = di->bat_cap.prev_percent;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
+                               di->flags.batt_id_received)
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+               else
+                       val->intval = di->bat_cap.prev_level;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
+{
+       struct power_supply *psy;
+       struct power_supply *ext = dev_get_drvdata(dev);
+       const char **supplicants = (const char **)ext->supplied_to;
+       struct ab8500_fg *di;
+       union power_supply_propval ret;
+       int j;
+
+       psy = (struct power_supply *)data;
+       di = power_supply_get_drvdata(psy);
+
+       /*
+        * For all psy where the name of your driver
+        * appears in any supplied_to
+        */
+       j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
+       if (j < 0)
+               return 0;
+
+       /* Go through all properties for the psy */
+       for (j = 0; j < ext->desc->num_properties; j++) {
+               enum power_supply_property prop;
+               prop = ext->desc->properties[j];
+
+               if (power_supply_get_property(ext, prop, &ret))
+                       continue;
+
+               switch (prop) {
+               case POWER_SUPPLY_PROP_STATUS:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               switch (ret.intval) {
+                               case POWER_SUPPLY_STATUS_UNKNOWN:
+                               case POWER_SUPPLY_STATUS_DISCHARGING:
+                               case POWER_SUPPLY_STATUS_NOT_CHARGING:
+                                       if (!di->flags.charging)
+                                               break;
+                                       di->flags.charging = false;
+                                       di->flags.fully_charged = false;
+                                       if (di->bm->capacity_scaling)
+                                               ab8500_fg_update_cap_scalers(di);
+                                       queue_work(di->fg_wq, &di->fg_work);
+                                       break;
+                               case POWER_SUPPLY_STATUS_FULL:
+                                       if (di->flags.fully_charged)
+                                               break;
+                                       di->flags.fully_charged = true;
+                                       di->flags.force_full = true;
+                                       /* Save current capacity as maximum */
+                                       di->bat_cap.max_mah = di->bat_cap.mah;
+                                       queue_work(di->fg_wq, &di->fg_work);
+                                       break;
+                               case POWER_SUPPLY_STATUS_CHARGING:
+                                       if (di->flags.charging &&
+                                               !di->flags.fully_charged)
+                                               break;
+                                       di->flags.charging = true;
+                                       di->flags.fully_charged = false;
+                                       if (di->bm->capacity_scaling)
+                                               ab8500_fg_update_cap_scalers(di);
+                                       queue_work(di->fg_wq, &di->fg_work);
+                                       break;
+                               };
+                       default:
+                               break;
+                       };
+                       break;
+               case POWER_SUPPLY_PROP_TECHNOLOGY:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               if (!di->flags.batt_id_received &&
+                                   di->bm->batt_id != BATTERY_UNKNOWN) {
+                                       const struct abx500_battery_type *b;
+
+                                       b = &(di->bm->bat_type[di->bm->batt_id]);
+
+                                       di->flags.batt_id_received = true;
+
+                                       di->bat_cap.max_mah_design =
+                                               MILLI_TO_MICRO *
+                                               b->charge_full_design;
+
+                                       di->bat_cap.max_mah =
+                                               di->bat_cap.max_mah_design;
+
+                                       di->vbat_nom = b->nominal_voltage;
+                               }
+
+                               if (ret.intval)
+                                       di->flags.batt_unknown = false;
+                               else
+                                       di->flags.batt_unknown = true;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case POWER_SUPPLY_PROP_TEMP:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               if (di->flags.batt_id_received)
+                                       di->bat_temp = ret.intval;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * ab8500_fg_init_hw_registers() - Set up FG related registers
+ * @di:                pointer to the ab8500_fg structure
+ *
+ * Set up battery OVV, low battery voltage registers
+ */
+static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
+{
+       int ret;
+
+       /* Set VBAT OVV threshold */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_CHARGER,
+               AB8500_BATT_OVV,
+               BATT_OVV_TH_4P75,
+               BATT_OVV_TH_4P75);
+       if (ret) {
+               dev_err(di->dev, "failed to set BATT_OVV\n");
+               goto out;
+       }
+
+       /* Enable VBAT OVV detection */
+       ret = abx500_mask_and_set_register_interruptible(di->dev,
+               AB8500_CHARGER,
+               AB8500_BATT_OVV,
+               BATT_OVV_ENA,
+               BATT_OVV_ENA);
+       if (ret) {
+               dev_err(di->dev, "failed to enable BATT_OVV\n");
+               goto out;
+       }
+
+       /* Low Battery Voltage */
+       ret = abx500_set_register_interruptible(di->dev,
+               AB8500_SYS_CTRL2_BLOCK,
+               AB8500_LOW_BAT_REG,
+               ab8500_volt_to_regval(
+                       di->bm->fg_params->lowbat_threshold) << 1 |
+               LOW_BAT_ENABLE);
+       if (ret) {
+               dev_err(di->dev, "%s write failed\n", __func__);
+               goto out;
+       }
+
+       /* Battery OK threshold */
+       ret = ab8500_fg_battok_init_hw_register(di);
+       if (ret) {
+               dev_err(di->dev, "BattOk init write failed.\n");
+               goto out;
+       }
+
+       if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
+                       abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
+                       || is_ab8540(di->parent)) {
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time);
+
+               if (ret) {
+                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__);
+                       goto out;
+               };
+
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time);
+
+               if (ret) {
+                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__);
+                       goto out;
+               };
+
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart);
+
+               if (ret) {
+                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__);
+                       goto out;
+               };
+
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time);
+
+               if (ret) {
+                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__);
+                       goto out;
+               };
+
+               ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                       AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable);
+
+               if (ret) {
+                       dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__);
+                       goto out;
+               };
+       }
+out:
+       return ret;
+}
+
+/**
+ * ab8500_fg_external_power_changed() - callback for power supply changes
+ * @psy:       pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void ab8500_fg_external_power_changed(struct power_supply *psy)
+{
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       class_for_each_device(power_supply_class, NULL,
+               di->fg_psy, ab8500_fg_get_ext_psy_data);
+}
+
+/**
+ * abab8500_fg_reinit_work() - work to reset the FG algorithm
+ * @work:      pointer to the work_struct structure
+ *
+ * Used to reset the current battery capacity to be able to
+ * retrigger a new voltage base capacity calculation. For
+ * test and verification purpose.
+ */
+static void ab8500_fg_reinit_work(struct work_struct *work)
+{
+       struct ab8500_fg *di = container_of(work, struct ab8500_fg,
+               fg_reinit_work.work);
+
+       if (di->flags.calibrate == false) {
+               dev_dbg(di->dev, "Resetting FG state machine to init.\n");
+               ab8500_fg_clear_cap_samples(di);
+               ab8500_fg_calc_cap_discharge_voltage(di, true);
+               ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+               ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+       } else {
+               dev_err(di->dev, "Residual offset calibration ongoing "
+                       "retrying..\n");
+               /* Wait one second until next try*/
+               queue_delayed_work(di->fg_wq, &di->fg_reinit_work,
+                       round_jiffies(1));
+       }
+}
+
+/* Exposure to the sysfs interface */
+
+struct ab8500_fg_sysfs_entry {
+       struct attribute attr;
+       ssize_t (*show)(struct ab8500_fg *, char *);
+       ssize_t (*store)(struct ab8500_fg *, const char *, size_t);
+};
+
+static ssize_t charge_full_show(struct ab8500_fg *di, char *buf)
+{
+       return sprintf(buf, "%d\n", di->bat_cap.max_mah);
+}
+
+static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf,
+                                size_t count)
+{
+       unsigned long charge_full;
+       ssize_t ret;
+
+       ret = kstrtoul(buf, 10, &charge_full);
+
+       dev_dbg(di->dev, "Ret %zd charge_full %lu", ret, charge_full);
+
+       if (!ret) {
+               di->bat_cap.max_mah = (int) charge_full;
+               ret = count;
+       }
+       return ret;
+}
+
+static ssize_t charge_now_show(struct ab8500_fg *di, char *buf)
+{
+       return sprintf(buf, "%d\n", di->bat_cap.prev_mah);
+}
+
+static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf,
+                                size_t count)
+{
+       unsigned long charge_now;
+       ssize_t ret;
+
+       ret = kstrtoul(buf, 10, &charge_now);
+
+       dev_dbg(di->dev, "Ret %zd charge_now %lu was %d",
+               ret, charge_now, di->bat_cap.prev_mah);
+
+       if (!ret) {
+               di->bat_cap.user_mah = (int) charge_now;
+               di->flags.user_cap = true;
+               ret = count;
+               queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+       }
+       return ret;
+}
+
+static struct ab8500_fg_sysfs_entry charge_full_attr =
+       __ATTR(charge_full, 0644, charge_full_show, charge_full_store);
+
+static struct ab8500_fg_sysfs_entry charge_now_attr =
+       __ATTR(charge_now, 0644, charge_now_show, charge_now_store);
+
+static ssize_t
+ab8500_fg_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+       struct ab8500_fg_sysfs_entry *entry;
+       struct ab8500_fg *di;
+
+       entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr);
+       di = container_of(kobj, struct ab8500_fg, fg_kobject);
+
+       if (!entry->show)
+               return -EIO;
+
+       return entry->show(di, buf);
+}
+static ssize_t
+ab8500_fg_store(struct kobject *kobj, struct attribute *attr, const char *buf,
+               size_t count)
+{
+       struct ab8500_fg_sysfs_entry *entry;
+       struct ab8500_fg *di;
+
+       entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr);
+       di = container_of(kobj, struct ab8500_fg, fg_kobject);
+
+       if (!entry->store)
+               return -EIO;
+
+       return entry->store(di, buf, count);
+}
+
+static const struct sysfs_ops ab8500_fg_sysfs_ops = {
+       .show = ab8500_fg_show,
+       .store = ab8500_fg_store,
+};
+
+static struct attribute *ab8500_fg_attrs[] = {
+       &charge_full_attr.attr,
+       &charge_now_attr.attr,
+       NULL,
+};
+
+static struct kobj_type ab8500_fg_ktype = {
+       .sysfs_ops = &ab8500_fg_sysfs_ops,
+       .default_attrs = ab8500_fg_attrs,
+};
+
+/**
+ * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry
+ * @di:                pointer to the struct ab8500_chargalg
+ *
+ * This function removes the entry in sysfs.
+ */
+static void ab8500_fg_sysfs_exit(struct ab8500_fg *di)
+{
+       kobject_del(&di->fg_kobject);
+}
+
+/**
+ * ab8500_chargalg_sysfs_init() - init of sysfs entry
+ * @di:                pointer to the struct ab8500_chargalg
+ *
+ * This function adds an entry in sysfs.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
+{
+       int ret = 0;
+
+       ret = kobject_init_and_add(&di->fg_kobject,
+               &ab8500_fg_ktype,
+               NULL, "battery");
+       if (ret < 0)
+               dev_err(di->dev, "failed to create sysfs entry\n");
+
+       return ret;
+}
+
+static ssize_t ab8505_powercut_flagtime_read(struct device *dev,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+               AB8505_RTC_PCUT_FLAG_TIME_REG, &reg_value);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n");
+               goto fail;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
+
+fail:
+       return ret;
+}
+
+static ssize_t ab8505_powercut_flagtime_write(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       int ret;
+       long unsigned reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       reg_value = simple_strtoul(buf, NULL, 10);
+
+       if (reg_value > 0x7F) {
+               dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n");
+               goto fail;
+       }
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+               AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value);
+
+       if (ret < 0)
+               dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n");
+
+fail:
+       return count;
+}
+
+static ssize_t ab8505_powercut_maxtime_read(struct device *dev,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+               AB8505_RTC_PCUT_MAX_TIME_REG, &reg_value);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n");
+               goto fail;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
+
+fail:
+       return ret;
+
+}
+
+static ssize_t ab8505_powercut_maxtime_write(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       int ret;
+       int reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       reg_value = simple_strtoul(buf, NULL, 10);
+       if (reg_value > 0x7F) {
+               dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n");
+               goto fail;
+       }
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+               AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value);
+
+       if (ret < 0)
+               dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n");
+
+fail:
+       return count;
+}
+
+static ssize_t ab8505_powercut_restart_read(struct device *dev,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+               AB8505_RTC_PCUT_RESTART_REG, &reg_value);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
+               goto fail;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF));
+
+fail:
+       return ret;
+}
+
+static ssize_t ab8505_powercut_restart_write(struct device *dev,
+                                            struct device_attribute *attr,
+                                            const char *buf, size_t count)
+{
+       int ret;
+       int reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       reg_value = simple_strtoul(buf, NULL, 10);
+       if (reg_value > 0xF) {
+               dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n");
+               goto fail;
+       }
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value);
+
+       if (ret < 0)
+               dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n");
+
+fail:
+       return count;
+
+}
+
+static ssize_t ab8505_powercut_timer_read(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_TIME_REG, &reg_value);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n");
+               goto fail;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F));
+
+fail:
+       return ret;
+}
+
+static ssize_t ab8505_powercut_restart_counter_read(struct device *dev,
+                                                   struct device_attribute *attr,
+                                                   char *buf)
+{
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_RESTART_REG, &reg_value);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n");
+               goto fail;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4);
+
+fail:
+       return ret;
+}
+
+static ssize_t ab8505_powercut_read(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
+
+       if (ret < 0)
+               goto fail;
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1));
+
+fail:
+       return ret;
+}
+
+static ssize_t ab8505_powercut_write(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       int ret;
+       int reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       reg_value = simple_strtoul(buf, NULL, 10);
+       if (reg_value > 0x1) {
+               dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n");
+               goto fail;
+       }
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value);
+
+       if (ret < 0)
+               dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n");
+
+fail:
+       return count;
+}
+
+static ssize_t ab8505_powercut_flag_read(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_CTL_STATUS_REG,  &reg_value);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
+               goto fail;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4));
+
+fail:
+       return ret;
+}
+
+static ssize_t ab8505_powercut_debounce_read(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buf)
+{
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_DEBOUNCE_REG,  &reg_value);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n");
+               goto fail;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7));
+
+fail:
+       return ret;
+}
+
+static ssize_t ab8505_powercut_debounce_write(struct device *dev,
+                                             struct device_attribute *attr,
+                                             const char *buf, size_t count)
+{
+       int ret;
+       int reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       reg_value = simple_strtoul(buf, NULL, 10);
+       if (reg_value > 0x7) {
+               dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n");
+               goto fail;
+       }
+
+       ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value);
+
+       if (ret < 0)
+               dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n");
+
+fail:
+       return count;
+}
+
+static ssize_t ab8505_powercut_enable_status_read(struct device *dev,
+                                                 struct device_attribute *attr,
+                                                 char *buf)
+{
+       int ret;
+       u8 reg_value;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di = power_supply_get_drvdata(psy);
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
+                                               AB8505_RTC_PCUT_CTL_STATUS_REG, &reg_value);
+
+       if (ret < 0) {
+               dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n");
+               goto fail;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5));
+
+fail:
+       return ret;
+}
+
+static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = {
+       __ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write),
+       __ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write),
+       __ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8505_powercut_restart_read, ab8505_powercut_restart_write),
+       __ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL),
+       __ATTR(powercut_restart_counter, S_IRUGO,
+               ab8505_powercut_restart_counter_read, NULL),
+       __ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8505_powercut_read, ab8505_powercut_write),
+       __ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL),
+       __ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8505_powercut_debounce_read, ab8505_powercut_debounce_write),
+       __ATTR(powercut_enable_status, S_IRUGO,
+               ab8505_powercut_enable_status_read, NULL),
+};
+
+static int ab8500_fg_sysfs_psy_create_attrs(struct ab8500_fg *di)
+{
+       unsigned int i;
+
+       if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
+            abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
+           || is_ab8540(di->parent)) {
+               for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
+                       if (device_create_file(&di->fg_psy->dev,
+                                              &ab8505_fg_sysfs_psy_attrs[i]))
+                               goto sysfs_psy_create_attrs_failed_ab8505;
+       }
+       return 0;
+sysfs_psy_create_attrs_failed_ab8505:
+       dev_err(&di->fg_psy->dev, "Failed creating sysfs psy attrs for ab8505.\n");
+       while (i--)
+               device_remove_file(&di->fg_psy->dev,
+                                  &ab8505_fg_sysfs_psy_attrs[i]);
+
+       return -EIO;
+}
+
+static void ab8500_fg_sysfs_psy_remove_attrs(struct ab8500_fg *di)
+{
+       unsigned int i;
+
+       if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
+            abx500_get_chip_id(di->dev) >= AB8500_CUT2P0)
+           || is_ab8540(di->parent)) {
+               for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
+                       (void)device_remove_file(&di->fg_psy->dev,
+                                                &ab8505_fg_sysfs_psy_attrs[i]);
+       }
+}
+
+/* Exposure to the sysfs interface <<END>> */
+
+#if defined(CONFIG_PM)
+static int ab8500_fg_resume(struct platform_device *pdev)
+{
+       struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+       /*
+        * Change state if we're not charging. If we're charging we will wake
+        * up on the FG IRQ
+        */
+       if (!di->flags.charging) {
+               ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_WAKEUP);
+               queue_work(di->fg_wq, &di->fg_work);
+       }
+
+       return 0;
+}
+
+static int ab8500_fg_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+       flush_delayed_work(&di->fg_periodic_work);
+       flush_work(&di->fg_work);
+       flush_work(&di->fg_acc_cur_work);
+       flush_delayed_work(&di->fg_reinit_work);
+       flush_delayed_work(&di->fg_low_bat_work);
+       flush_delayed_work(&di->fg_check_hw_failure_work);
+
+       /*
+        * If the FG is enabled we will disable it before going to suspend
+        * only if we're not charging
+        */
+       if (di->flags.fg_enabled && !di->flags.charging)
+               ab8500_fg_coulomb_counter(di, false);
+
+       return 0;
+}
+#else
+#define ab8500_fg_suspend      NULL
+#define ab8500_fg_resume       NULL
+#endif
+
+static int ab8500_fg_remove(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct ab8500_fg *di = platform_get_drvdata(pdev);
+
+       list_del(&di->node);
+
+       /* Disable coulomb counter */
+       ret = ab8500_fg_coulomb_counter(di, false);
+       if (ret)
+               dev_err(di->dev, "failed to disable coulomb counter\n");
+
+       destroy_workqueue(di->fg_wq);
+       ab8500_fg_sysfs_exit(di);
+
+       flush_scheduled_work();
+       ab8500_fg_sysfs_psy_remove_attrs(di);
+       power_supply_unregister(di->fg_psy);
+       return ret;
+}
+
+/* ab8500 fg driver interrupts and their respective isr */
+static struct ab8500_fg_interrupts ab8500_fg_irq_th[] = {
+       {"NCONV_ACCU", ab8500_fg_cc_convend_handler},
+       {"BATT_OVV", ab8500_fg_batt_ovv_handler},
+       {"LOW_BAT_F", ab8500_fg_lowbatf_handler},
+       {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler},
+};
+
+static struct ab8500_fg_interrupts ab8500_fg_irq_bh[] = {
+       {"CCEOC", ab8500_fg_cc_data_end_handler},
+};
+
+static char *supply_interface[] = {
+       "ab8500_chargalg",
+       "ab8500_usb",
+};
+
+static const struct power_supply_desc ab8500_fg_desc = {
+       .name                   = "ab8500_fg",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = ab8500_fg_props,
+       .num_properties         = ARRAY_SIZE(ab8500_fg_props),
+       .get_property           = ab8500_fg_get_property,
+       .external_power_changed = ab8500_fg_external_power_changed,
+};
+
+static int ab8500_fg_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct abx500_bm_data *plat = pdev->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       struct ab8500_fg *di;
+       int i, irq;
+       int ret = 0;
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       if (!di) {
+               dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__);
+               return -ENOMEM;
+       }
+
+       if (!plat) {
+               dev_err(&pdev->dev, "no battery management data supplied\n");
+               return -EINVAL;
+       }
+       di->bm = plat;
+
+       if (np) {
+               ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get battery information\n");
+                       return ret;
+               }
+       }
+
+       mutex_init(&di->cc_lock);
+
+       /* get parent data */
+       di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
+       di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+       psy_cfg.supplied_to = supply_interface;
+       psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
+       psy_cfg.drv_data = di;
+
+       di->bat_cap.max_mah_design = MILLI_TO_MICRO *
+               di->bm->bat_type[di->bm->batt_id].charge_full_design;
+
+       di->bat_cap.max_mah = di->bat_cap.max_mah_design;
+
+       di->vbat_nom = di->bm->bat_type[di->bm->batt_id].nominal_voltage;
+
+       di->init_capacity = true;
+
+       ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+       ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+
+       /* Create a work queue for running the FG algorithm */
+       di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
+       if (di->fg_wq == NULL) {
+               dev_err(di->dev, "failed to create work queue\n");
+               return -ENOMEM;
+       }
+
+       /* Init work for running the fg algorithm instantly */
+       INIT_WORK(&di->fg_work, ab8500_fg_instant_work);
+
+       /* Init work for getting the battery accumulated current */
+       INIT_WORK(&di->fg_acc_cur_work, ab8500_fg_acc_cur_work);
+
+       /* Init work for reinitialising the fg algorithm */
+       INIT_DEFERRABLE_WORK(&di->fg_reinit_work,
+               ab8500_fg_reinit_work);
+
+       /* Work delayed Queue to run the state machine */
+       INIT_DEFERRABLE_WORK(&di->fg_periodic_work,
+               ab8500_fg_periodic_work);
+
+       /* Work to check low battery condition */
+       INIT_DEFERRABLE_WORK(&di->fg_low_bat_work,
+               ab8500_fg_low_bat_work);
+
+       /* Init work for HW failure check */
+       INIT_DEFERRABLE_WORK(&di->fg_check_hw_failure_work,
+               ab8500_fg_check_hw_failure_work);
+
+       /* Reset battery low voltage flag */
+       di->flags.low_bat = false;
+
+       /* Initialize low battery counter */
+       di->low_bat_cnt = 10;
+
+       /* Initialize OVV, and other registers */
+       ret = ab8500_fg_init_hw_registers(di);
+       if (ret) {
+               dev_err(di->dev, "failed to initialize registers\n");
+               goto free_inst_curr_wq;
+       }
+
+       /* Consider battery unknown until we're informed otherwise */
+       di->flags.batt_unknown = true;
+       di->flags.batt_id_received = false;
+
+       /* Register FG power supply class */
+       di->fg_psy = power_supply_register(di->dev, &ab8500_fg_desc, &psy_cfg);
+       if (IS_ERR(di->fg_psy)) {
+               dev_err(di->dev, "failed to register FG psy\n");
+               ret = PTR_ERR(di->fg_psy);
+               goto free_inst_curr_wq;
+       }
+
+       di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
+       ab8500_fg_coulomb_counter(di, true);
+
+       /*
+        * Initialize completion used to notify completion and start
+        * of inst current
+        */
+       init_completion(&di->ab8500_fg_started);
+       init_completion(&di->ab8500_fg_complete);
+
+       /* Register primary interrupt handlers */
+       for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name);
+               ret = request_irq(irq, ab8500_fg_irq_th[i].isr,
+                                 IRQF_SHARED | IRQF_NO_SUSPEND,
+                                 ab8500_fg_irq_th[i].name, di);
+
+               if (ret != 0) {
+                       dev_err(di->dev, "failed to request %s IRQ %d: %d\n",
+                               ab8500_fg_irq_th[i].name, irq, ret);
+                       goto free_irq;
+               }
+               dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+                       ab8500_fg_irq_th[i].name, irq, ret);
+       }
+
+       /* Register threaded interrupt handler */
+       irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name);
+       ret = request_threaded_irq(irq, NULL, ab8500_fg_irq_bh[0].isr,
+                               IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
+                       ab8500_fg_irq_bh[0].name, di);
+
+       if (ret != 0) {
+               dev_err(di->dev, "failed to request %s IRQ %d: %d\n",
+                       ab8500_fg_irq_bh[0].name, irq, ret);
+               goto free_irq;
+       }
+       dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+               ab8500_fg_irq_bh[0].name, irq, ret);
+
+       di->irq = platform_get_irq_byname(pdev, "CCEOC");
+       disable_irq(di->irq);
+       di->nbr_cceoc_irq_cnt = 0;
+
+       platform_set_drvdata(pdev, di);
+
+       ret = ab8500_fg_sysfs_init(di);
+       if (ret) {
+               dev_err(di->dev, "failed to create sysfs entry\n");
+               goto free_irq;
+       }
+
+       ret = ab8500_fg_sysfs_psy_create_attrs(di);
+       if (ret) {
+               dev_err(di->dev, "failed to create FG psy\n");
+               ab8500_fg_sysfs_exit(di);
+               goto free_irq;
+       }
+
+       /* Calibrate the fg first time */
+       di->flags.calibrate = true;
+       di->calib_state = AB8500_FG_CALIB_INIT;
+
+       /* Use room temp as default value until we get an update from driver. */
+       di->bat_temp = 210;
+
+       /* Run the FG algorithm */
+       queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+       list_add_tail(&di->node, &ab8500_fg_list);
+
+       return ret;
+
+free_irq:
+       power_supply_unregister(di->fg_psy);
+
+       /* We also have to free all registered irqs */
+       for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) {
+               irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name);
+               free_irq(irq, di);
+       }
+       irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name);
+       free_irq(irq, di);
+free_inst_curr_wq:
+       destroy_workqueue(di->fg_wq);
+       return ret;
+}
+
+static const struct of_device_id ab8500_fg_match[] = {
+       { .compatible = "stericsson,ab8500-fg", },
+       { },
+};
+
+static struct platform_driver ab8500_fg_driver = {
+       .probe = ab8500_fg_probe,
+       .remove = ab8500_fg_remove,
+       .suspend = ab8500_fg_suspend,
+       .resume = ab8500_fg_resume,
+       .driver = {
+               .name = "ab8500-fg",
+               .of_match_table = ab8500_fg_match,
+       },
+};
+
+static int __init ab8500_fg_init(void)
+{
+       return platform_driver_register(&ab8500_fg_driver);
+}
+
+static void __exit ab8500_fg_exit(void)
+{
+       platform_driver_unregister(&ab8500_fg_driver);
+}
+
+subsys_initcall_sync(ab8500_fg_init);
+module_exit(ab8500_fg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:ab8500-fg");
+MODULE_DESCRIPTION("AB8500 Fuel Gauge driver");
diff --git a/drivers/power/supply/abx500_chargalg.c b/drivers/power/supply/abx500_chargalg.c
new file mode 100644 (file)
index 0000000..d9104b1
--- /dev/null
@@ -0,0 +1,2166 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ * Copyright (c) 2012 Sony Mobile Communications AB
+ *
+ * Charging algorithm driver for abx500 variants
+ *
+ * License Terms: GNU General Public License v2
+ * Authors:
+ *     Johan Palsson <johan.palsson@stericsson.com>
+ *     Karl Komierowski <karl.komierowski@stericsson.com>
+ *     Arun R Murthy <arun.murthy@stericsson.com>
+ *     Author: Imre Sunyi <imre.sunyi@sonymobile.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/of.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/notifier.h>
+
+/* Watchdog kick interval */
+#define CHG_WD_INTERVAL                        (6 * HZ)
+
+/* End-of-charge criteria counter */
+#define EOC_COND_CNT                   10
+
+/* One hour expressed in seconds */
+#define ONE_HOUR_IN_SECONDS            3600
+
+/* Five minutes expressed in seconds */
+#define FIVE_MINUTES_IN_SECONDS        300
+
+/* Plus margin for the low battery threshold */
+#define BAT_PLUS_MARGIN                (100)
+
+#define CHARGALG_CURR_STEP_LOW         0
+#define CHARGALG_CURR_STEP_HIGH        100
+
+enum abx500_chargers {
+       NO_CHG,
+       AC_CHG,
+       USB_CHG,
+};
+
+struct abx500_chargalg_charger_info {
+       enum abx500_chargers conn_chg;
+       enum abx500_chargers prev_conn_chg;
+       enum abx500_chargers online_chg;
+       enum abx500_chargers prev_online_chg;
+       enum abx500_chargers charger_type;
+       bool usb_chg_ok;
+       bool ac_chg_ok;
+       int usb_volt;
+       int usb_curr;
+       int ac_volt;
+       int ac_curr;
+       int usb_vset;
+       int usb_iset;
+       int ac_vset;
+       int ac_iset;
+};
+
+struct abx500_chargalg_suspension_status {
+       bool suspended_change;
+       bool ac_suspended;
+       bool usb_suspended;
+};
+
+struct abx500_chargalg_current_step_status {
+       bool curr_step_change;
+       int curr_step;
+};
+
+struct abx500_chargalg_battery_data {
+       int temp;
+       int volt;
+       int avg_curr;
+       int inst_curr;
+       int percent;
+};
+
+enum abx500_chargalg_states {
+       STATE_HANDHELD_INIT,
+       STATE_HANDHELD,
+       STATE_CHG_NOT_OK_INIT,
+       STATE_CHG_NOT_OK,
+       STATE_HW_TEMP_PROTECT_INIT,
+       STATE_HW_TEMP_PROTECT,
+       STATE_NORMAL_INIT,
+       STATE_USB_PP_PRE_CHARGE,
+       STATE_NORMAL,
+       STATE_WAIT_FOR_RECHARGE_INIT,
+       STATE_WAIT_FOR_RECHARGE,
+       STATE_MAINTENANCE_A_INIT,
+       STATE_MAINTENANCE_A,
+       STATE_MAINTENANCE_B_INIT,
+       STATE_MAINTENANCE_B,
+       STATE_TEMP_UNDEROVER_INIT,
+       STATE_TEMP_UNDEROVER,
+       STATE_TEMP_LOWHIGH_INIT,
+       STATE_TEMP_LOWHIGH,
+       STATE_SUSPENDED_INIT,
+       STATE_SUSPENDED,
+       STATE_OVV_PROTECT_INIT,
+       STATE_OVV_PROTECT,
+       STATE_SAFETY_TIMER_EXPIRED_INIT,
+       STATE_SAFETY_TIMER_EXPIRED,
+       STATE_BATT_REMOVED_INIT,
+       STATE_BATT_REMOVED,
+       STATE_WD_EXPIRED_INIT,
+       STATE_WD_EXPIRED,
+};
+
+static const char *states[] = {
+       "HANDHELD_INIT",
+       "HANDHELD",
+       "CHG_NOT_OK_INIT",
+       "CHG_NOT_OK",
+       "HW_TEMP_PROTECT_INIT",
+       "HW_TEMP_PROTECT",
+       "NORMAL_INIT",
+       "USB_PP_PRE_CHARGE",
+       "NORMAL",
+       "WAIT_FOR_RECHARGE_INIT",
+       "WAIT_FOR_RECHARGE",
+       "MAINTENANCE_A_INIT",
+       "MAINTENANCE_A",
+       "MAINTENANCE_B_INIT",
+       "MAINTENANCE_B",
+       "TEMP_UNDEROVER_INIT",
+       "TEMP_UNDEROVER",
+       "TEMP_LOWHIGH_INIT",
+       "TEMP_LOWHIGH",
+       "SUSPENDED_INIT",
+       "SUSPENDED",
+       "OVV_PROTECT_INIT",
+       "OVV_PROTECT",
+       "SAFETY_TIMER_EXPIRED_INIT",
+       "SAFETY_TIMER_EXPIRED",
+       "BATT_REMOVED_INIT",
+       "BATT_REMOVED",
+       "WD_EXPIRED_INIT",
+       "WD_EXPIRED",
+};
+
+struct abx500_chargalg_events {
+       bool batt_unknown;
+       bool mainextchnotok;
+       bool batt_ovv;
+       bool batt_rem;
+       bool btemp_underover;
+       bool btemp_lowhigh;
+       bool main_thermal_prot;
+       bool usb_thermal_prot;
+       bool main_ovv;
+       bool vbus_ovv;
+       bool usbchargernotok;
+       bool safety_timer_expired;
+       bool maintenance_timer_expired;
+       bool ac_wd_expired;
+       bool usb_wd_expired;
+       bool ac_cv_active;
+       bool usb_cv_active;
+       bool vbus_collapsed;
+};
+
+/**
+ * struct abx500_charge_curr_maximization - Charger maximization parameters
+ * @original_iset:     the non optimized/maximised charger current
+ * @current_iset:      the charging current used at this moment
+ * @test_delta_i:      the delta between the current we want to charge and the
+                       current that is really going into the battery
+ * @condition_cnt:     number of iterations needed before a new charger current
+                       is set
+ * @max_current:       maximum charger current
+ * @wait_cnt:          to avoid too fast current step down in case of charger
+ *                     voltage collapse, we insert this delay between step
+ *                     down
+ * @level:             tells in how many steps the charging current has been
+                       increased
+ */
+struct abx500_charge_curr_maximization {
+       int original_iset;
+       int current_iset;
+       int test_delta_i;
+       int condition_cnt;
+       int max_current;
+       int wait_cnt;
+       u8 level;
+};
+
+enum maxim_ret {
+       MAXIM_RET_NOACTION,
+       MAXIM_RET_CHANGE,
+       MAXIM_RET_IBAT_TOO_HIGH,
+};
+
+/**
+ * struct abx500_chargalg - abx500 Charging algorithm device information
+ * @dev:               pointer to the structure device
+ * @charge_status:     battery operating status
+ * @eoc_cnt:           counter used to determine end-of_charge
+ * @maintenance_chg:   indicate if maintenance charge is active
+ * @t_hyst_norm                temperature hysteresis when the temperature has been
+ *                     over or under normal limits
+ * @t_hyst_lowhigh     temperature hysteresis when the temperature has been
+ *                     over or under the high or low limits
+ * @charge_state:      current state of the charging algorithm
+ * @ccm                        charging current maximization parameters
+ * @chg_info:          information about connected charger types
+ * @batt_data:         data of the battery
+ * @susp_status:       current charger suspension status
+ * @bm:                Platform specific battery management information
+ * @curr_status:       Current step status for over-current protection
+ * @parent:            pointer to the struct abx500
+ * @chargalg_psy:      structure that holds the battery properties exposed by
+ *                     the charging algorithm
+ * @events:            structure for information about events triggered
+ * @chargalg_wq:               work queue for running the charging algorithm
+ * @chargalg_periodic_work:    work to run the charging algorithm periodically
+ * @chargalg_wd_work:          work to kick the charger watchdog periodically
+ * @chargalg_work:             work to run the charging algorithm instantly
+ * @safety_timer:              charging safety timer
+ * @maintenance_timer:         maintenance charging timer
+ * @chargalg_kobject:          structure of type kobject
+ */
+struct abx500_chargalg {
+       struct device *dev;
+       int charge_status;
+       int eoc_cnt;
+       bool maintenance_chg;
+       int t_hyst_norm;
+       int t_hyst_lowhigh;
+       enum abx500_chargalg_states charge_state;
+       struct abx500_charge_curr_maximization ccm;
+       struct abx500_chargalg_charger_info chg_info;
+       struct abx500_chargalg_battery_data batt_data;
+       struct abx500_chargalg_suspension_status susp_status;
+       struct ab8500 *parent;
+       struct abx500_chargalg_current_step_status curr_status;
+       struct abx500_bm_data *bm;
+       struct power_supply *chargalg_psy;
+       struct ux500_charger *ac_chg;
+       struct ux500_charger *usb_chg;
+       struct abx500_chargalg_events events;
+       struct workqueue_struct *chargalg_wq;
+       struct delayed_work chargalg_periodic_work;
+       struct delayed_work chargalg_wd_work;
+       struct work_struct chargalg_work;
+       struct hrtimer safety_timer;
+       struct hrtimer maintenance_timer;
+       struct kobject chargalg_kobject;
+};
+
+/*External charger prepare notifier*/
+BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
+
+/* Main battery properties */
+static enum power_supply_property abx500_chargalg_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+};
+
+struct abx500_chargalg_sysfs_entry {
+       struct attribute attr;
+       ssize_t (*show)(struct abx500_chargalg *, char *);
+       ssize_t (*store)(struct abx500_chargalg *, const char *, size_t);
+};
+
+/**
+ * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer
+ * @timer:     pointer to the hrtimer structure
+ *
+ * This function gets called when the safety timer for the charger
+ * expires
+ */
+static enum hrtimer_restart
+abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
+{
+       struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
+                                                 safety_timer);
+       dev_err(di->dev, "Safety timer expired\n");
+       di->events.safety_timer_expired = true;
+
+       /* Trigger execution of the algorithm instantly */
+       queue_work(di->chargalg_wq, &di->chargalg_work);
+
+       return HRTIMER_NORESTART;
+}
+
+/**
+ * abx500_chargalg_maintenance_timer_expired() - Expiration of
+ * the maintenance timer
+ * @timer:     pointer to the timer structure
+ *
+ * This function gets called when the maintenence timer
+ * expires
+ */
+static enum hrtimer_restart
+abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
+{
+
+       struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg,
+                                                 maintenance_timer);
+
+       dev_dbg(di->dev, "Maintenance timer expired\n");
+       di->events.maintenance_timer_expired = true;
+
+       /* Trigger execution of the algorithm instantly */
+       queue_work(di->chargalg_wq, &di->chargalg_work);
+
+       return HRTIMER_NORESTART;
+}
+
+/**
+ * abx500_chargalg_state_to() - Change charge state
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function gets called when a charge state change should occur
+ */
+static void abx500_chargalg_state_to(struct abx500_chargalg *di,
+       enum abx500_chargalg_states state)
+{
+       dev_dbg(di->dev,
+               "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n",
+               di->charge_state == state ? "NO" : "YES",
+               di->charge_state,
+               states[di->charge_state],
+               state,
+               states[state]);
+
+       di->charge_state = state;
+}
+
+static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
+{
+       switch (di->charge_state) {
+       case STATE_NORMAL:
+       case STATE_MAINTENANCE_A:
+       case STATE_MAINTENANCE_B:
+               break;
+       default:
+               return 0;
+       }
+
+       if (di->chg_info.charger_type & USB_CHG) {
+               return di->usb_chg->ops.check_enable(di->usb_chg,
+                         di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
+                         di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
+       } else if ((di->chg_info.charger_type & AC_CHG) &&
+                  !(di->ac_chg->external)) {
+               return di->ac_chg->ops.check_enable(di->ac_chg,
+                         di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
+                         di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
+       }
+       return 0;
+}
+
+/**
+ * abx500_chargalg_check_charger_connection() - Check charger connection change
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function will check if there is a change in the charger connection
+ * and change charge state accordingly. AC has precedence over USB.
+ */
+static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
+{
+       if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg ||
+               di->susp_status.suspended_change) {
+               /*
+                * Charger state changed or suspension
+                * has changed since last update
+                */
+               if ((di->chg_info.conn_chg & AC_CHG) &&
+                       !di->susp_status.ac_suspended) {
+                       dev_dbg(di->dev, "Charging source is AC\n");
+                       if (di->chg_info.charger_type != AC_CHG) {
+                               di->chg_info.charger_type = AC_CHG;
+                               abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+                       }
+               } else if ((di->chg_info.conn_chg & USB_CHG) &&
+                       !di->susp_status.usb_suspended) {
+                       dev_dbg(di->dev, "Charging source is USB\n");
+                       di->chg_info.charger_type = USB_CHG;
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               } else if (di->chg_info.conn_chg &&
+                       (di->susp_status.ac_suspended ||
+                       di->susp_status.usb_suspended)) {
+                       dev_dbg(di->dev, "Charging is suspended\n");
+                       di->chg_info.charger_type = NO_CHG;
+                       abx500_chargalg_state_to(di, STATE_SUSPENDED_INIT);
+               } else {
+                       dev_dbg(di->dev, "Charging source is OFF\n");
+                       di->chg_info.charger_type = NO_CHG;
+                       abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+               }
+               di->chg_info.prev_conn_chg = di->chg_info.conn_chg;
+               di->susp_status.suspended_change = false;
+       }
+       return di->chg_info.conn_chg;
+}
+
+/**
+ * abx500_chargalg_check_current_step_status() - Check charging current
+ * step status.
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function will check if there is a change in the charging current step
+ * and change charge state accordingly.
+ */
+static void abx500_chargalg_check_current_step_status
+       (struct abx500_chargalg *di)
+{
+       if (di->curr_status.curr_step_change)
+               abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+       di->curr_status.curr_step_change = false;
+}
+
+/**
+ * abx500_chargalg_start_safety_timer() - Start charging safety timer
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The safety timer is used to avoid overcharging of old or bad batteries.
+ * There are different timers for AC and USB
+ */
+static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
+{
+       /* Charger-dependent expiration time in hours*/
+       int timer_expiration = 0;
+
+       switch (di->chg_info.charger_type) {
+       case AC_CHG:
+               timer_expiration = di->bm->main_safety_tmr_h;
+               break;
+
+       case USB_CHG:
+               timer_expiration = di->bm->usb_safety_tmr_h;
+               break;
+
+       default:
+               dev_err(di->dev, "Unknown charger to charge from\n");
+               break;
+       }
+
+       di->events.safety_timer_expired = false;
+       hrtimer_set_expires_range(&di->safety_timer,
+               ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0),
+               ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
+       hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL);
+}
+
+/**
+ * abx500_chargalg_stop_safety_timer() - Stop charging safety timer
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The safety timer is stopped whenever the NORMAL state is exited
+ */
+static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
+{
+       if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
+               di->events.safety_timer_expired = false;
+}
+
+/**
+ * abx500_chargalg_start_maintenance_timer() - Start charging maintenance timer
+ * @di:                pointer to the abx500_chargalg structure
+ * @duration:  duration of ther maintenance timer in hours
+ *
+ * The maintenance timer is used to maintain the charge in the battery once
+ * the battery is considered full. These timers are chosen to match the
+ * discharge curve of the battery
+ */
+static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
+       int duration)
+{
+       hrtimer_set_expires_range(&di->maintenance_timer,
+               ktime_set(duration * ONE_HOUR_IN_SECONDS, 0),
+               ktime_set(FIVE_MINUTES_IN_SECONDS, 0));
+       di->events.maintenance_timer_expired = false;
+       hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
+}
+
+/**
+ * abx500_chargalg_stop_maintenance_timer() - Stop maintenance timer
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The maintenance timer is stopped whenever maintenance ends or when another
+ * state is entered
+ */
+static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
+{
+       if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
+               di->events.maintenance_timer_expired = false;
+}
+
+/**
+ * abx500_chargalg_kick_watchdog() - Kick charger watchdog
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The charger watchdog have to be kicked periodically whenever the charger is
+ * on, else the ABB will reset the system
+ */
+static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
+{
+       /* Check if charger exists and kick watchdog if charging */
+       if (di->ac_chg && di->ac_chg->ops.kick_wd &&
+           di->chg_info.online_chg & AC_CHG) {
+               /*
+                * If AB charger watchdog expired, pm2xxx charging
+                * gets disabled. To be safe, kick both AB charger watchdog
+                * and pm2xxx watchdog.
+                */
+               if (di->ac_chg->external &&
+                   di->usb_chg && di->usb_chg->ops.kick_wd)
+                       di->usb_chg->ops.kick_wd(di->usb_chg);
+
+               return di->ac_chg->ops.kick_wd(di->ac_chg);
+       }
+       else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
+                       di->chg_info.online_chg & USB_CHG)
+               return di->usb_chg->ops.kick_wd(di->usb_chg);
+
+       return -ENXIO;
+}
+
+/**
+ * abx500_chargalg_ac_en() - Turn on/off the AC charger
+ * @di:                pointer to the abx500_chargalg structure
+ * @enable:    charger on/off
+ * @vset:      requested charger output voltage
+ * @iset:      requested charger output current
+ *
+ * The AC charger will be turned on/off with the requested charge voltage and
+ * current
+ */
+static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
+       int vset, int iset)
+{
+       static int abx500_chargalg_ex_ac_enable_toggle;
+
+       if (!di->ac_chg || !di->ac_chg->ops.enable)
+               return -ENXIO;
+
+       /* Select maximum of what both the charger and the battery supports */
+       if (di->ac_chg->max_out_volt)
+               vset = min(vset, di->ac_chg->max_out_volt);
+       if (di->ac_chg->max_out_curr)
+               iset = min(iset, di->ac_chg->max_out_curr);
+
+       di->chg_info.ac_iset = iset;
+       di->chg_info.ac_vset = vset;
+
+       /* Enable external charger */
+       if (enable && di->ac_chg->external &&
+           !abx500_chargalg_ex_ac_enable_toggle) {
+               blocking_notifier_call_chain(&charger_notifier_list,
+                                            0, di->dev);
+               abx500_chargalg_ex_ac_enable_toggle++;
+       }
+
+       return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
+}
+
+/**
+ * abx500_chargalg_usb_en() - Turn on/off the USB charger
+ * @di:                pointer to the abx500_chargalg structure
+ * @enable:    charger on/off
+ * @vset:      requested charger output voltage
+ * @iset:      requested charger output current
+ *
+ * The USB charger will be turned on/off with the requested charge voltage and
+ * current
+ */
+static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
+       int vset, int iset)
+{
+       if (!di->usb_chg || !di->usb_chg->ops.enable)
+               return -ENXIO;
+
+       /* Select maximum of what both the charger and the battery supports */
+       if (di->usb_chg->max_out_volt)
+               vset = min(vset, di->usb_chg->max_out_volt);
+       if (di->usb_chg->max_out_curr)
+               iset = min(iset, di->usb_chg->max_out_curr);
+
+       di->chg_info.usb_iset = iset;
+       di->chg_info.usb_vset = vset;
+
+       return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
+}
+
+ /**
+ * ab8540_chargalg_usb_pp_en() - Enable/ disable USB power path
+ * @di:                pointer to the abx500_chargalg structure
+ * @enable:    power path enable/disable
+ *
+ * The USB power path will be enable/ disable
+ */
+static int ab8540_chargalg_usb_pp_en(struct abx500_chargalg *di, bool enable)
+{
+       if (!di->usb_chg || !di->usb_chg->ops.pp_enable)
+               return -ENXIO;
+
+       return di->usb_chg->ops.pp_enable(di->usb_chg, enable);
+}
+
+/**
+ * ab8540_chargalg_usb_pre_chg_en() - Enable/ disable USB pre-charge
+ * @di:                pointer to the abx500_chargalg structure
+ * @enable:    USB pre-charge enable/disable
+ *
+ * The USB USB pre-charge will be enable/ disable
+ */
+static int ab8540_chargalg_usb_pre_chg_en(struct abx500_chargalg *di,
+                                         bool enable)
+{
+       if (!di->usb_chg || !di->usb_chg->ops.pre_chg_enable)
+               return -ENXIO;
+
+       return di->usb_chg->ops.pre_chg_enable(di->usb_chg, enable);
+}
+
+/**
+ * abx500_chargalg_update_chg_curr() - Update charger current
+ * @di:                pointer to the abx500_chargalg structure
+ * @iset:      requested charger output current
+ *
+ * The charger output current will be updated for the charger
+ * that is currently in use
+ */
+static int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di,
+               int iset)
+{
+       /* Check if charger exists and update current if charging */
+       if (di->ac_chg && di->ac_chg->ops.update_curr &&
+                       di->chg_info.charger_type & AC_CHG) {
+               /*
+                * Select maximum of what both the charger
+                * and the battery supports
+                */
+               if (di->ac_chg->max_out_curr)
+                       iset = min(iset, di->ac_chg->max_out_curr);
+
+               di->chg_info.ac_iset = iset;
+
+               return di->ac_chg->ops.update_curr(di->ac_chg, iset);
+       } else if (di->usb_chg && di->usb_chg->ops.update_curr &&
+                       di->chg_info.charger_type & USB_CHG) {
+               /*
+                * Select maximum of what both the charger
+                * and the battery supports
+                */
+               if (di->usb_chg->max_out_curr)
+                       iset = min(iset, di->usb_chg->max_out_curr);
+
+               di->chg_info.usb_iset = iset;
+
+               return di->usb_chg->ops.update_curr(di->usb_chg, iset);
+       }
+
+       return -ENXIO;
+}
+
+/**
+ * abx500_chargalg_stop_charging() - Stop charging
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function is called from any state where charging should be stopped.
+ * All charging is disabled and all status parameters and timers are changed
+ * accordingly
+ */
+static void abx500_chargalg_stop_charging(struct abx500_chargalg *di)
+{
+       abx500_chargalg_ac_en(di, false, 0, 0);
+       abx500_chargalg_usb_en(di, false, 0, 0);
+       abx500_chargalg_stop_safety_timer(di);
+       abx500_chargalg_stop_maintenance_timer(di);
+       di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       di->maintenance_chg = false;
+       cancel_delayed_work(&di->chargalg_wd_work);
+       power_supply_changed(di->chargalg_psy);
+}
+
+/**
+ * abx500_chargalg_hold_charging() - Pauses charging
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This function is called in the case where maintenance charging has been
+ * disabled and instead a battery voltage mode is entered to check when the
+ * battery voltage has reached a certain recharge voltage
+ */
+static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
+{
+       abx500_chargalg_ac_en(di, false, 0, 0);
+       abx500_chargalg_usb_en(di, false, 0, 0);
+       abx500_chargalg_stop_safety_timer(di);
+       abx500_chargalg_stop_maintenance_timer(di);
+       di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+       di->maintenance_chg = false;
+       cancel_delayed_work(&di->chargalg_wd_work);
+       power_supply_changed(di->chargalg_psy);
+}
+
+/**
+ * abx500_chargalg_start_charging() - Start the charger
+ * @di:                pointer to the abx500_chargalg structure
+ * @vset:      requested charger output voltage
+ * @iset:      requested charger output current
+ *
+ * A charger will be enabled depending on the requested charger type that was
+ * detected previously.
+ */
+static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
+       int vset, int iset)
+{
+       switch (di->chg_info.charger_type) {
+       case AC_CHG:
+               dev_dbg(di->dev,
+                       "AC parameters: Vset %d, Ich %d\n", vset, iset);
+               abx500_chargalg_usb_en(di, false, 0, 0);
+               abx500_chargalg_ac_en(di, true, vset, iset);
+               break;
+
+       case USB_CHG:
+               dev_dbg(di->dev,
+                       "USB parameters: Vset %d, Ich %d\n", vset, iset);
+               abx500_chargalg_ac_en(di, false, 0, 0);
+               abx500_chargalg_usb_en(di, true, vset, iset);
+               break;
+
+       default:
+               dev_err(di->dev, "Unknown charger to charge from\n");
+               break;
+       }
+}
+
+/**
+ * abx500_chargalg_check_temp() - Check battery temperature ranges
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * The battery temperature is checked against the predefined limits and the
+ * charge state is changed accordingly
+ */
+static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
+{
+       if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) &&
+               di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) {
+               /* Temp OK! */
+               di->events.btemp_underover = false;
+               di->events.btemp_lowhigh = false;
+               di->t_hyst_norm = 0;
+               di->t_hyst_lowhigh = 0;
+       } else {
+               if (((di->batt_data.temp >= di->bm->temp_high) &&
+                       (di->batt_data.temp <
+                               (di->bm->temp_over - di->t_hyst_lowhigh))) ||
+                       ((di->batt_data.temp >
+                               (di->bm->temp_under + di->t_hyst_lowhigh)) &&
+                       (di->batt_data.temp <= di->bm->temp_low))) {
+                       /* TEMP minor!!!!! */
+                       di->events.btemp_underover = false;
+                       di->events.btemp_lowhigh = true;
+                       di->t_hyst_norm = di->bm->temp_hysteresis;
+                       di->t_hyst_lowhigh = 0;
+               } else if (di->batt_data.temp <= di->bm->temp_under ||
+                       di->batt_data.temp >= di->bm->temp_over) {
+                       /* TEMP major!!!!! */
+                       di->events.btemp_underover = true;
+                       di->events.btemp_lowhigh = false;
+                       di->t_hyst_norm = 0;
+                       di->t_hyst_lowhigh = di->bm->temp_hysteresis;
+               } else {
+               /* Within hysteresis */
+               dev_dbg(di->dev, "Within hysteresis limit temp: %d "
+                               "hyst_lowhigh %d, hyst normal %d\n",
+                               di->batt_data.temp, di->t_hyst_lowhigh,
+                               di->t_hyst_norm);
+               }
+       }
+}
+
+/**
+ * abx500_chargalg_check_charger_voltage() - Check charger voltage
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * Charger voltage is checked against maximum limit
+ */
+static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
+{
+       if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max)
+               di->chg_info.usb_chg_ok = false;
+       else
+               di->chg_info.usb_chg_ok = true;
+
+       if (di->chg_info.ac_volt > di->bm->chg_params->ac_volt_max)
+               di->chg_info.ac_chg_ok = false;
+       else
+               di->chg_info.ac_chg_ok = true;
+
+}
+
+/**
+ * abx500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * End-of-charge criteria is fulfilled when the battery voltage is above a
+ * certain limit and the battery current is below a certain limit for a
+ * predefined number of consecutive seconds. If true, the battery is full
+ */
+static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
+{
+       if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
+               di->charge_state == STATE_NORMAL &&
+               !di->maintenance_chg && (di->batt_data.volt >=
+               di->bm->bat_type[di->bm->batt_id].termination_vol ||
+               di->events.usb_cv_active || di->events.ac_cv_active) &&
+               di->batt_data.avg_curr <
+               di->bm->bat_type[di->bm->batt_id].termination_curr &&
+               di->batt_data.avg_curr > 0) {
+               if (++di->eoc_cnt >= EOC_COND_CNT) {
+                       di->eoc_cnt = 0;
+                       if ((di->chg_info.charger_type & USB_CHG) &&
+                          (di->usb_chg->power_path))
+                               ab8540_chargalg_usb_pp_en(di, true);
+                       di->charge_status = POWER_SUPPLY_STATUS_FULL;
+                       di->maintenance_chg = true;
+                       dev_dbg(di->dev, "EOC reached!\n");
+                       power_supply_changed(di->chargalg_psy);
+               } else {
+                       dev_dbg(di->dev,
+                               " EOC limit reached for the %d"
+                               " time, out of %d before EOC\n",
+                               di->eoc_cnt,
+                               EOC_COND_CNT);
+               }
+       } else {
+               di->eoc_cnt = 0;
+       }
+}
+
+static void init_maxim_chg_curr(struct abx500_chargalg *di)
+{
+       di->ccm.original_iset =
+               di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
+       di->ccm.current_iset =
+               di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
+       di->ccm.test_delta_i = di->bm->maxi->charger_curr_step;
+       di->ccm.max_current = di->bm->maxi->chg_curr;
+       di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
+       di->ccm.level = 0;
+}
+
+/**
+ * abx500_chargalg_chg_curr_maxim - increases the charger current to
+ *                     compensate for the system load
+ * @di         pointer to the abx500_chargalg structure
+ *
+ * This maximization function is used to raise the charger current to get the
+ * battery current as close to the optimal value as possible. The battery
+ * current during charging is affected by the system load
+ */
+static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
+{
+       int delta_i;
+
+       if (!di->bm->maxi->ena_maxi)
+               return MAXIM_RET_NOACTION;
+
+       delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
+
+       if (di->events.vbus_collapsed) {
+               dev_dbg(di->dev, "Charger voltage has collapsed %d\n",
+                               di->ccm.wait_cnt);
+               if (di->ccm.wait_cnt == 0) {
+                       dev_dbg(di->dev, "lowering current\n");
+                       di->ccm.wait_cnt++;
+                       di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
+                       di->ccm.max_current =
+                               di->ccm.current_iset - di->ccm.test_delta_i;
+                       di->ccm.current_iset = di->ccm.max_current;
+                       di->ccm.level--;
+                       return MAXIM_RET_CHANGE;
+               } else {
+                       dev_dbg(di->dev, "waiting\n");
+                       /* Let's go in here twice before lowering curr again */
+                       di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 3;
+                       return MAXIM_RET_NOACTION;
+               }
+       }
+
+       di->ccm.wait_cnt = 0;
+
+       if ((di->batt_data.inst_curr > di->ccm.original_iset)) {
+               dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
+                       " (limit %dmA) (current iset: %dmA)!\n",
+                       di->batt_data.inst_curr, di->ccm.original_iset,
+                       di->ccm.current_iset);
+
+               if (di->ccm.current_iset == di->ccm.original_iset)
+                       return MAXIM_RET_NOACTION;
+
+               di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
+               di->ccm.current_iset = di->ccm.original_iset;
+               di->ccm.level = 0;
+
+               return MAXIM_RET_IBAT_TOO_HIGH;
+       }
+
+       if (delta_i > di->ccm.test_delta_i &&
+               (di->ccm.current_iset + di->ccm.test_delta_i) <
+               di->ccm.max_current) {
+               if (di->ccm.condition_cnt-- == 0) {
+                       /* Increse the iset with cco.test_delta_i */
+                       di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
+                       di->ccm.current_iset += di->ccm.test_delta_i;
+                       di->ccm.level++;
+                       dev_dbg(di->dev, " Maximization needed, increase"
+                               " with %d mA to %dmA (Optimal ibat: %d)"
+                               " Level %d\n",
+                               di->ccm.test_delta_i,
+                               di->ccm.current_iset,
+                               di->ccm.original_iset,
+                               di->ccm.level);
+                       return MAXIM_RET_CHANGE;
+               } else {
+                       return MAXIM_RET_NOACTION;
+               }
+       }  else {
+               di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
+               return MAXIM_RET_NOACTION;
+       }
+}
+
+static void handle_maxim_chg_curr(struct abx500_chargalg *di)
+{
+       enum maxim_ret ret;
+       int result;
+
+       ret = abx500_chargalg_chg_curr_maxim(di);
+       switch (ret) {
+       case MAXIM_RET_CHANGE:
+               result = abx500_chargalg_update_chg_curr(di,
+                       di->ccm.current_iset);
+               if (result)
+                       dev_err(di->dev, "failed to set chg curr\n");
+               break;
+       case MAXIM_RET_IBAT_TOO_HIGH:
+               result = abx500_chargalg_update_chg_curr(di,
+                       di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
+               if (result)
+                       dev_err(di->dev, "failed to set chg curr\n");
+               break;
+
+       case MAXIM_RET_NOACTION:
+       default:
+               /* Do nothing..*/
+               break;
+       }
+}
+
+static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
+{
+       struct power_supply *psy;
+       struct power_supply *ext = dev_get_drvdata(dev);
+       const char **supplicants = (const char **)ext->supplied_to;
+       struct abx500_chargalg *di;
+       union power_supply_propval ret;
+       int j;
+       bool capacity_updated = false;
+
+       psy = (struct power_supply *)data;
+       di = power_supply_get_drvdata(psy);
+       /* For all psy where the driver name appears in any supplied_to */
+       j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
+       if (j < 0)
+               return 0;
+
+       /*
+        *  If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its
+        * property because of handling that sysfs entry on its own, this is
+        * the place to get the battery capacity.
+        */
+       if (!power_supply_get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) {
+               di->batt_data.percent = ret.intval;
+               capacity_updated = true;
+       }
+
+       /* Go through all properties for the psy */
+       for (j = 0; j < ext->desc->num_properties; j++) {
+               enum power_supply_property prop;
+               prop = ext->desc->properties[j];
+
+               /*
+                * Initialize chargers if not already done.
+                * The ab8500_charger*/
+               if (!di->ac_chg &&
+                       ext->desc->type == POWER_SUPPLY_TYPE_MAINS)
+                       di->ac_chg = psy_to_ux500_charger(ext);
+               else if (!di->usb_chg &&
+                       ext->desc->type == POWER_SUPPLY_TYPE_USB)
+                       di->usb_chg = psy_to_ux500_charger(ext);
+
+               if (power_supply_get_property(ext, prop, &ret))
+                       continue;
+               switch (prop) {
+               case POWER_SUPPLY_PROP_PRESENT:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               /* Battery present */
+                               if (ret.intval)
+                                       di->events.batt_rem = false;
+                               /* Battery removed */
+                               else
+                                       di->events.batt_rem = true;
+                               break;
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               /* AC disconnected */
+                               if (!ret.intval &&
+                                       (di->chg_info.conn_chg & AC_CHG)) {
+                                       di->chg_info.prev_conn_chg =
+                                               di->chg_info.conn_chg;
+                                       di->chg_info.conn_chg &= ~AC_CHG;
+                               }
+                               /* AC connected */
+                               else if (ret.intval &&
+                                       !(di->chg_info.conn_chg & AC_CHG)) {
+                                       di->chg_info.prev_conn_chg =
+                                               di->chg_info.conn_chg;
+                                       di->chg_info.conn_chg |= AC_CHG;
+                               }
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               /* USB disconnected */
+                               if (!ret.intval &&
+                                       (di->chg_info.conn_chg & USB_CHG)) {
+                                       di->chg_info.prev_conn_chg =
+                                               di->chg_info.conn_chg;
+                                       di->chg_info.conn_chg &= ~USB_CHG;
+                               }
+                               /* USB connected */
+                               else if (ret.intval &&
+                                       !(di->chg_info.conn_chg & USB_CHG)) {
+                                       di->chg_info.prev_conn_chg =
+                                               di->chg_info.conn_chg;
+                                       di->chg_info.conn_chg |= USB_CHG;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_ONLINE:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               break;
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               /* AC offline */
+                               if (!ret.intval &&
+                                       (di->chg_info.online_chg & AC_CHG)) {
+                                       di->chg_info.prev_online_chg =
+                                               di->chg_info.online_chg;
+                                       di->chg_info.online_chg &= ~AC_CHG;
+                               }
+                               /* AC online */
+                               else if (ret.intval &&
+                                       !(di->chg_info.online_chg & AC_CHG)) {
+                                       di->chg_info.prev_online_chg =
+                                               di->chg_info.online_chg;
+                                       di->chg_info.online_chg |= AC_CHG;
+                                       queue_delayed_work(di->chargalg_wq,
+                                               &di->chargalg_wd_work, 0);
+                               }
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               /* USB offline */
+                               if (!ret.intval &&
+                                       (di->chg_info.online_chg & USB_CHG)) {
+                                       di->chg_info.prev_online_chg =
+                                               di->chg_info.online_chg;
+                                       di->chg_info.online_chg &= ~USB_CHG;
+                               }
+                               /* USB online */
+                               else if (ret.intval &&
+                                       !(di->chg_info.online_chg & USB_CHG)) {
+                                       di->chg_info.prev_online_chg =
+                                               di->chg_info.online_chg;
+                                       di->chg_info.online_chg |= USB_CHG;
+                                       queue_delayed_work(di->chargalg_wq,
+                                               &di->chargalg_wd_work, 0);
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_HEALTH:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               break;
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               switch (ret.intval) {
+                               case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+                                       di->events.mainextchnotok = true;
+                                       di->events.main_thermal_prot = false;
+                                       di->events.main_ovv = false;
+                                       di->events.ac_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_DEAD:
+                                       di->events.ac_wd_expired = true;
+                                       di->events.mainextchnotok = false;
+                                       di->events.main_ovv = false;
+                                       di->events.main_thermal_prot = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_COLD:
+                               case POWER_SUPPLY_HEALTH_OVERHEAT:
+                                       di->events.main_thermal_prot = true;
+                                       di->events.mainextchnotok = false;
+                                       di->events.main_ovv = false;
+                                       di->events.ac_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+                                       di->events.main_ovv = true;
+                                       di->events.mainextchnotok = false;
+                                       di->events.main_thermal_prot = false;
+                                       di->events.ac_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_GOOD:
+                                       di->events.main_thermal_prot = false;
+                                       di->events.mainextchnotok = false;
+                                       di->events.main_ovv = false;
+                                       di->events.ac_wd_expired = false;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               break;
+
+                       case POWER_SUPPLY_TYPE_USB:
+                               switch (ret.intval) {
+                               case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+                                       di->events.usbchargernotok = true;
+                                       di->events.usb_thermal_prot = false;
+                                       di->events.vbus_ovv = false;
+                                       di->events.usb_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_DEAD:
+                                       di->events.usb_wd_expired = true;
+                                       di->events.usbchargernotok = false;
+                                       di->events.usb_thermal_prot = false;
+                                       di->events.vbus_ovv = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_COLD:
+                               case POWER_SUPPLY_HEALTH_OVERHEAT:
+                                       di->events.usb_thermal_prot = true;
+                                       di->events.usbchargernotok = false;
+                                       di->events.vbus_ovv = false;
+                                       di->events.usb_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+                                       di->events.vbus_ovv = true;
+                                       di->events.usbchargernotok = false;
+                                       di->events.usb_thermal_prot = false;
+                                       di->events.usb_wd_expired = false;
+                                       break;
+                               case POWER_SUPPLY_HEALTH_GOOD:
+                                       di->events.usbchargernotok = false;
+                                       di->events.usb_thermal_prot = false;
+                                       di->events.vbus_ovv = false;
+                                       di->events.usb_wd_expired = false;
+                                       break;
+                               default:
+                                       break;
+                               }
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               di->batt_data.volt = ret.intval / 1000;
+                               break;
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               di->chg_info.ac_volt = ret.intval / 1000;
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               di->chg_info.usb_volt = ret.intval / 1000;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_MAINS:
+                               /* AVG is used to indicate when we are
+                                * in CV mode */
+                               if (ret.intval)
+                                       di->events.ac_cv_active = true;
+                               else
+                                       di->events.ac_cv_active = false;
+
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               /* AVG is used to indicate when we are
+                                * in CV mode */
+                               if (ret.intval)
+                                       di->events.usb_cv_active = true;
+                               else
+                                       di->events.usb_cv_active = false;
+
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_TECHNOLOGY:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               if (ret.intval)
+                                       di->events.batt_unknown = false;
+                               else
+                                       di->events.batt_unknown = true;
+
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_TEMP:
+                       di->batt_data.temp = ret.intval / 10;
+                       break;
+
+               case POWER_SUPPLY_PROP_CURRENT_NOW:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_MAINS:
+                                       di->chg_info.ac_curr =
+                                               ret.intval / 1000;
+                                       break;
+                       case POWER_SUPPLY_TYPE_USB:
+                                       di->chg_info.usb_curr =
+                                               ret.intval / 1000;
+                               break;
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               di->batt_data.inst_curr = ret.intval / 1000;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+
+               case POWER_SUPPLY_PROP_CURRENT_AVG:
+                       switch (ext->desc->type) {
+                       case POWER_SUPPLY_TYPE_BATTERY:
+                               di->batt_data.avg_curr = ret.intval / 1000;
+                               break;
+                       case POWER_SUPPLY_TYPE_USB:
+                               if (ret.intval)
+                                       di->events.vbus_collapsed = true;
+                               else
+                                       di->events.vbus_collapsed = false;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case POWER_SUPPLY_PROP_CAPACITY:
+                       if (!capacity_updated)
+                               di->batt_data.percent = ret.intval;
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * abx500_chargalg_external_power_changed() - callback for power supply changes
+ * @psy:       pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void abx500_chargalg_external_power_changed(struct power_supply *psy)
+{
+       struct abx500_chargalg *di = power_supply_get_drvdata(psy);
+
+       /*
+        * Trigger execution of the algorithm instantly and read
+        * all power_supply properties there instead
+        */
+       queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_algorithm() - Main function for the algorithm
+ * @di:                pointer to the abx500_chargalg structure
+ *
+ * This is the main control function for the charging algorithm.
+ * It is called periodically or when something happens that will
+ * trigger a state change
+ */
+static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
+{
+       int charger_status;
+       int ret;
+       int curr_step_lvl;
+
+       /* Collect data from all power_supply class devices */
+       class_for_each_device(power_supply_class, NULL,
+               di->chargalg_psy, abx500_chargalg_get_ext_psy_data);
+
+       abx500_chargalg_end_of_charge(di);
+       abx500_chargalg_check_temp(di);
+       abx500_chargalg_check_charger_voltage(di);
+
+       charger_status = abx500_chargalg_check_charger_connection(di);
+       abx500_chargalg_check_current_step_status(di);
+
+       if (is_ab8500(di->parent)) {
+               ret = abx500_chargalg_check_charger_enable(di);
+               if (ret < 0)
+                       dev_err(di->dev, "Checking charger is enabled error"
+                                       ": Returned Value %d\n", ret);
+       }
+
+       /*
+        * First check if we have a charger connected.
+        * Also we don't allow charging of unknown batteries if configured
+        * this way
+        */
+       if (!charger_status ||
+               (di->events.batt_unknown && !di->bm->chg_unknown_bat)) {
+               if (di->charge_state != STATE_HANDHELD) {
+                       di->events.safety_timer_expired = false;
+                       abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
+               }
+       }
+
+       /* If suspended, we should not continue checking the flags */
+       else if (di->charge_state == STATE_SUSPENDED_INIT ||
+               di->charge_state == STATE_SUSPENDED) {
+               /* We don't do anything here, just don,t continue */
+       }
+
+       /* Safety timer expiration */
+       else if (di->events.safety_timer_expired) {
+               if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED)
+                       abx500_chargalg_state_to(di,
+                               STATE_SAFETY_TIMER_EXPIRED_INIT);
+       }
+       /*
+        * Check if any interrupts has occured
+        * that will prevent us from charging
+        */
+
+       /* Battery removed */
+       else if (di->events.batt_rem) {
+               if (di->charge_state != STATE_BATT_REMOVED)
+                       abx500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT);
+       }
+       /* Main or USB charger not ok. */
+       else if (di->events.mainextchnotok || di->events.usbchargernotok) {
+               /*
+                * If vbus_collapsed is set, we have to lower the charger
+                * current, which is done in the normal state below
+                */
+               if (di->charge_state != STATE_CHG_NOT_OK &&
+                               !di->events.vbus_collapsed)
+                       abx500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
+       }
+       /* VBUS, Main or VBAT OVV. */
+       else if (di->events.vbus_ovv ||
+                       di->events.main_ovv ||
+                       di->events.batt_ovv ||
+                       !di->chg_info.usb_chg_ok ||
+                       !di->chg_info.ac_chg_ok) {
+               if (di->charge_state != STATE_OVV_PROTECT)
+                       abx500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT);
+       }
+       /* USB Thermal, stop charging */
+       else if (di->events.main_thermal_prot ||
+               di->events.usb_thermal_prot) {
+               if (di->charge_state != STATE_HW_TEMP_PROTECT)
+                       abx500_chargalg_state_to(di,
+                               STATE_HW_TEMP_PROTECT_INIT);
+       }
+       /* Battery temp over/under */
+       else if (di->events.btemp_underover) {
+               if (di->charge_state != STATE_TEMP_UNDEROVER)
+                       abx500_chargalg_state_to(di,
+                               STATE_TEMP_UNDEROVER_INIT);
+       }
+       /* Watchdog expired */
+       else if (di->events.ac_wd_expired ||
+               di->events.usb_wd_expired) {
+               if (di->charge_state != STATE_WD_EXPIRED)
+                       abx500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT);
+       }
+       /* Battery temp high/low */
+       else if (di->events.btemp_lowhigh) {
+               if (di->charge_state != STATE_TEMP_LOWHIGH)
+                       abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT);
+       }
+
+       dev_dbg(di->dev,
+               "[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d "
+               "State %s Active_chg %d Chg_status %d AC %d USB %d "
+               "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d "
+               "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n",
+               di->batt_data.volt,
+               di->batt_data.avg_curr,
+               di->batt_data.inst_curr,
+               di->batt_data.temp,
+               di->batt_data.percent,
+               di->maintenance_chg,
+               states[di->charge_state],
+               di->chg_info.charger_type,
+               di->charge_status,
+               di->chg_info.conn_chg & AC_CHG,
+               di->chg_info.conn_chg & USB_CHG,
+               di->chg_info.online_chg & AC_CHG,
+               di->chg_info.online_chg & USB_CHG,
+               di->events.ac_cv_active,
+               di->events.usb_cv_active,
+               di->chg_info.ac_curr,
+               di->chg_info.usb_curr,
+               di->chg_info.ac_vset,
+               di->chg_info.ac_iset,
+               di->chg_info.usb_vset,
+               di->chg_info.usb_iset);
+
+       switch (di->charge_state) {
+       case STATE_HANDHELD_INIT:
+               abx500_chargalg_stop_charging(di);
+               di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+               abx500_chargalg_state_to(di, STATE_HANDHELD);
+               /* Intentional fallthrough */
+
+       case STATE_HANDHELD:
+               break;
+
+       case STATE_SUSPENDED_INIT:
+               if (di->susp_status.ac_suspended)
+                       abx500_chargalg_ac_en(di, false, 0, 0);
+               if (di->susp_status.usb_suspended)
+                       abx500_chargalg_usb_en(di, false, 0, 0);
+               abx500_chargalg_stop_safety_timer(di);
+               abx500_chargalg_stop_maintenance_timer(di);
+               di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               di->maintenance_chg = false;
+               abx500_chargalg_state_to(di, STATE_SUSPENDED);
+               power_supply_changed(di->chargalg_psy);
+               /* Intentional fallthrough */
+
+       case STATE_SUSPENDED:
+               /* CHARGING is suspended */
+               break;
+
+       case STATE_BATT_REMOVED_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_BATT_REMOVED);
+               /* Intentional fallthrough */
+
+       case STATE_BATT_REMOVED:
+               if (!di->events.batt_rem)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_HW_TEMP_PROTECT_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT);
+               /* Intentional fallthrough */
+
+       case STATE_HW_TEMP_PROTECT:
+               if (!di->events.main_thermal_prot &&
+                               !di->events.usb_thermal_prot)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_OVV_PROTECT_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_OVV_PROTECT);
+               /* Intentional fallthrough */
+
+       case STATE_OVV_PROTECT:
+               if (!di->events.vbus_ovv &&
+                               !di->events.main_ovv &&
+                               !di->events.batt_ovv &&
+                               di->chg_info.usb_chg_ok &&
+                               di->chg_info.ac_chg_ok)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_CHG_NOT_OK_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_CHG_NOT_OK);
+               /* Intentional fallthrough */
+
+       case STATE_CHG_NOT_OK:
+               if (!di->events.mainextchnotok &&
+                               !di->events.usbchargernotok)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_SAFETY_TIMER_EXPIRED_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED);
+               /* Intentional fallthrough */
+
+       case STATE_SAFETY_TIMER_EXPIRED:
+               /* We exit this state when charger is removed */
+               break;
+
+       case STATE_NORMAL_INIT:
+               if ((di->chg_info.charger_type & USB_CHG) &&
+                               di->usb_chg->power_path) {
+                       if (di->batt_data.volt >
+                           (di->bm->fg_params->lowbat_threshold +
+                            BAT_PLUS_MARGIN)) {
+                               ab8540_chargalg_usb_pre_chg_en(di, false);
+                               ab8540_chargalg_usb_pp_en(di, false);
+                       } else {
+                               ab8540_chargalg_usb_pp_en(di, true);
+                               ab8540_chargalg_usb_pre_chg_en(di, true);
+                               abx500_chargalg_state_to(di,
+                                       STATE_USB_PP_PRE_CHARGE);
+                               break;
+                       }
+               }
+
+               if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW)
+                       abx500_chargalg_stop_charging(di);
+               else {
+                       curr_step_lvl = di->bm->bat_type[
+                               di->bm->batt_id].normal_cur_lvl
+                               * di->curr_status.curr_step
+                               / CHARGALG_CURR_STEP_HIGH;
+                       abx500_chargalg_start_charging(di,
+                               di->bm->bat_type[di->bm->batt_id]
+                               .normal_vol_lvl, curr_step_lvl);
+               }
+
+               abx500_chargalg_state_to(di, STATE_NORMAL);
+               abx500_chargalg_start_safety_timer(di);
+               abx500_chargalg_stop_maintenance_timer(di);
+               init_maxim_chg_curr(di);
+               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+               di->eoc_cnt = 0;
+               di->maintenance_chg = false;
+               power_supply_changed(di->chargalg_psy);
+
+               break;
+
+       case STATE_USB_PP_PRE_CHARGE:
+               if (di->batt_data.volt >
+                       (di->bm->fg_params->lowbat_threshold +
+                       BAT_PLUS_MARGIN))
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_NORMAL:
+               handle_maxim_chg_curr(di);
+               if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
+                       di->maintenance_chg) {
+                       if (di->bm->no_maintenance)
+                               abx500_chargalg_state_to(di,
+                                       STATE_WAIT_FOR_RECHARGE_INIT);
+                       else
+                               abx500_chargalg_state_to(di,
+                                       STATE_MAINTENANCE_A_INIT);
+               }
+               break;
+
+       /* This state will be used when the maintenance state is disabled */
+       case STATE_WAIT_FOR_RECHARGE_INIT:
+               abx500_chargalg_hold_charging(di);
+               abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
+               /* Intentional fallthrough */
+
+       case STATE_WAIT_FOR_RECHARGE:
+               if (di->batt_data.percent <=
+                   di->bm->bat_type[di->bm->batt_id].
+                   recharge_cap)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_MAINTENANCE_A_INIT:
+               abx500_chargalg_stop_safety_timer(di);
+               abx500_chargalg_start_maintenance_timer(di,
+                       di->bm->bat_type[
+                               di->bm->batt_id].maint_a_chg_timer_h);
+               abx500_chargalg_start_charging(di,
+                       di->bm->bat_type[
+                               di->bm->batt_id].maint_a_vol_lvl,
+                       di->bm->bat_type[
+                               di->bm->batt_id].maint_a_cur_lvl);
+               abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
+               power_supply_changed(di->chargalg_psy);
+               /* Intentional fallthrough*/
+
+       case STATE_MAINTENANCE_A:
+               if (di->events.maintenance_timer_expired) {
+                       abx500_chargalg_stop_maintenance_timer(di);
+                       abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT);
+               }
+               break;
+
+       case STATE_MAINTENANCE_B_INIT:
+               abx500_chargalg_start_maintenance_timer(di,
+                       di->bm->bat_type[
+                               di->bm->batt_id].maint_b_chg_timer_h);
+               abx500_chargalg_start_charging(di,
+                       di->bm->bat_type[
+                               di->bm->batt_id].maint_b_vol_lvl,
+                       di->bm->bat_type[
+                               di->bm->batt_id].maint_b_cur_lvl);
+               abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
+               power_supply_changed(di->chargalg_psy);
+               /* Intentional fallthrough*/
+
+       case STATE_MAINTENANCE_B:
+               if (di->events.maintenance_timer_expired) {
+                       abx500_chargalg_stop_maintenance_timer(di);
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               }
+               break;
+
+       case STATE_TEMP_LOWHIGH_INIT:
+               abx500_chargalg_start_charging(di,
+                       di->bm->bat_type[
+                               di->bm->batt_id].low_high_vol_lvl,
+                       di->bm->bat_type[
+                               di->bm->batt_id].low_high_cur_lvl);
+               abx500_chargalg_stop_maintenance_timer(di);
+               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+               abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
+               power_supply_changed(di->chargalg_psy);
+               /* Intentional fallthrough */
+
+       case STATE_TEMP_LOWHIGH:
+               if (!di->events.btemp_lowhigh)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_WD_EXPIRED_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_WD_EXPIRED);
+               /* Intentional fallthrough */
+
+       case STATE_WD_EXPIRED:
+               if (!di->events.ac_wd_expired &&
+                               !di->events.usb_wd_expired)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
+       case STATE_TEMP_UNDEROVER_INIT:
+               abx500_chargalg_stop_charging(di);
+               abx500_chargalg_state_to(di, STATE_TEMP_UNDEROVER);
+               /* Intentional fallthrough */
+
+       case STATE_TEMP_UNDEROVER:
+               if (!di->events.btemp_underover)
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+       }
+
+       /* Start charging directly if the new state is a charge state */
+       if (di->charge_state == STATE_NORMAL_INIT ||
+                       di->charge_state == STATE_MAINTENANCE_A_INIT ||
+                       di->charge_state == STATE_MAINTENANCE_B_INIT)
+               queue_work(di->chargalg_wq, &di->chargalg_work);
+}
+
+/**
+ * abx500_chargalg_periodic_work() - Periodic work for the algorithm
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for the charging algorithm
+ */
+static void abx500_chargalg_periodic_work(struct work_struct *work)
+{
+       struct abx500_chargalg *di = container_of(work,
+               struct abx500_chargalg, chargalg_periodic_work.work);
+
+       abx500_chargalg_algorithm(di);
+
+       /*
+        * If a charger is connected then the battery has to be monitored
+        * frequently, else the work can be delayed.
+        */
+       if (di->chg_info.conn_chg)
+               queue_delayed_work(di->chargalg_wq,
+                       &di->chargalg_periodic_work,
+                       di->bm->interval_charging * HZ);
+       else
+               queue_delayed_work(di->chargalg_wq,
+                       &di->chargalg_periodic_work,
+                       di->bm->interval_not_charging * HZ);
+}
+
+/**
+ * abx500_chargalg_wd_work() - periodic work to kick the charger watchdog
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for kicking the charger watchdog
+ */
+static void abx500_chargalg_wd_work(struct work_struct *work)
+{
+       int ret;
+       struct abx500_chargalg *di = container_of(work,
+               struct abx500_chargalg, chargalg_wd_work.work);
+
+       dev_dbg(di->dev, "abx500_chargalg_wd_work\n");
+
+       ret = abx500_chargalg_kick_watchdog(di);
+       if (ret < 0)
+               dev_err(di->dev, "failed to kick watchdog\n");
+
+       queue_delayed_work(di->chargalg_wq,
+               &di->chargalg_wd_work, CHG_WD_INTERVAL);
+}
+
+/**
+ * abx500_chargalg_work() - Work to run the charging algorithm instantly
+ * @work:      pointer to the work_struct structure
+ *
+ * Work queue function for calling the charging algorithm
+ */
+static void abx500_chargalg_work(struct work_struct *work)
+{
+       struct abx500_chargalg *di = container_of(work,
+               struct abx500_chargalg, chargalg_work);
+
+       abx500_chargalg_algorithm(di);
+}
+
+/**
+ * abx500_chargalg_get_property() - get the chargalg properties
+ * @psy:       pointer to the power_supply structure
+ * @psp:       pointer to the power_supply_property structure
+ * @val:       pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * chargalg properties by reading the sysfs files.
+ * status:     charging/discharging/full/unknown
+ * health:     health of the battery
+ * Returns error code in case of failure else 0 on success
+ */
+static int abx500_chargalg_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct abx500_chargalg *di = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = di->charge_status;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (di->events.batt_ovv) {
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               } else if (di->events.btemp_underover) {
+                       if (di->batt_data.temp <= di->bm->temp_under)
+                               val->intval = POWER_SUPPLY_HEALTH_COLD;
+                       else
+                               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               } else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED ||
+                          di->charge_state == STATE_SAFETY_TIMER_EXPIRED_INIT) {
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               } else {
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* Exposure to the sysfs interface */
+
+static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di,
+                                             char *buf)
+{
+       return sprintf(buf, "%d\n", di->curr_status.curr_step);
+}
+
+static ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di,
+                                              const char *buf, size_t length)
+{
+       long int param;
+       int ret;
+
+       ret = kstrtol(buf, 10, &param);
+       if (ret < 0)
+               return ret;
+
+       di->curr_status.curr_step = param;
+       if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW &&
+               di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) {
+               di->curr_status.curr_step_change = true;
+               queue_work(di->chargalg_wq, &di->chargalg_work);
+       } else
+               dev_info(di->dev, "Wrong current step\n"
+                       "Enter 0. Disable AC/USB Charging\n"
+                       "1--100. Set AC/USB charging current step\n"
+                       "100. Enable AC/USB Charging\n");
+
+       return strlen(buf);
+}
+
+
+static ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di,
+                                      char *buf)
+{
+       return sprintf(buf, "%d\n",
+                      di->susp_status.ac_suspended &&
+                      di->susp_status.usb_suspended);
+}
+
+static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
+       const char *buf, size_t length)
+{
+       long int param;
+       int ac_usb;
+       int ret;
+
+       ret = kstrtol(buf, 10, &param);
+       if (ret < 0)
+               return ret;
+
+       ac_usb = param;
+       switch (ac_usb) {
+       case 0:
+               /* Disable charging */
+               di->susp_status.ac_suspended = true;
+               di->susp_status.usb_suspended = true;
+               di->susp_status.suspended_change = true;
+               /* Trigger a state change */
+               queue_work(di->chargalg_wq,
+                       &di->chargalg_work);
+               break;
+       case 1:
+               /* Enable AC Charging */
+               di->susp_status.ac_suspended = false;
+               di->susp_status.suspended_change = true;
+               /* Trigger a state change */
+               queue_work(di->chargalg_wq,
+                       &di->chargalg_work);
+               break;
+       case 2:
+               /* Enable USB charging */
+               di->susp_status.usb_suspended = false;
+               di->susp_status.suspended_change = true;
+               /* Trigger a state change */
+               queue_work(di->chargalg_wq,
+                       &di->chargalg_work);
+               break;
+       default:
+               dev_info(di->dev, "Wrong input\n"
+                       "Enter 0. Disable AC/USB Charging\n"
+                       "1. Enable AC charging\n"
+                       "2. Enable USB Charging\n");
+       };
+       return strlen(buf);
+}
+
+static struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger =
+       __ATTR(chargalg, 0644, abx500_chargalg_en_show,
+                               abx500_chargalg_en_store);
+
+static struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step =
+       __ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show,
+                                       abx500_chargalg_curr_step_store);
+
+static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
+       struct attribute *attr, char *buf)
+{
+       struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
+               struct abx500_chargalg_sysfs_entry, attr);
+
+       struct abx500_chargalg *di = container_of(kobj,
+               struct abx500_chargalg, chargalg_kobject);
+
+       if (!entry->show)
+               return -EIO;
+
+       return entry->show(di, buf);
+}
+
+static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
+       struct attribute *attr, const char *buf, size_t length)
+{
+       struct abx500_chargalg_sysfs_entry *entry = container_of(attr,
+               struct abx500_chargalg_sysfs_entry, attr);
+
+       struct abx500_chargalg *di = container_of(kobj,
+               struct abx500_chargalg, chargalg_kobject);
+
+       if (!entry->store)
+               return -EIO;
+
+       return entry->store(di, buf, length);
+}
+
+static struct attribute *abx500_chargalg_chg[] = {
+       &abx500_chargalg_en_charger.attr,
+       &abx500_chargalg_curr_step.attr,
+       NULL,
+};
+
+static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
+       .show = abx500_chargalg_sysfs_show,
+       .store = abx500_chargalg_sysfs_charger,
+};
+
+static struct kobj_type abx500_chargalg_ktype = {
+       .sysfs_ops = &abx500_chargalg_sysfs_ops,
+       .default_attrs = abx500_chargalg_chg,
+};
+
+/**
+ * abx500_chargalg_sysfs_exit() - de-init of sysfs entry
+ * @di:                pointer to the struct abx500_chargalg
+ *
+ * This function removes the entry in sysfs.
+ */
+static void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di)
+{
+       kobject_del(&di->chargalg_kobject);
+}
+
+/**
+ * abx500_chargalg_sysfs_init() - init of sysfs entry
+ * @di:                pointer to the struct abx500_chargalg
+ *
+ * This function adds an entry in sysfs.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int abx500_chargalg_sysfs_init(struct abx500_chargalg *di)
+{
+       int ret = 0;
+
+       ret = kobject_init_and_add(&di->chargalg_kobject,
+               &abx500_chargalg_ktype,
+               NULL, "abx500_chargalg");
+       if (ret < 0)
+               dev_err(di->dev, "failed to create sysfs entry\n");
+
+       return ret;
+}
+/* Exposure to the sysfs interface <<END>> */
+
+#if defined(CONFIG_PM)
+static int abx500_chargalg_resume(struct platform_device *pdev)
+{
+       struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+       /* Kick charger watchdog if charging (any charger online) */
+       if (di->chg_info.online_chg)
+               queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
+
+       /*
+        * Run the charging algorithm directly to be sure we don't
+        * do it too seldom
+        */
+       queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
+
+       return 0;
+}
+
+static int abx500_chargalg_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+       if (di->chg_info.online_chg)
+               cancel_delayed_work_sync(&di->chargalg_wd_work);
+
+       cancel_delayed_work_sync(&di->chargalg_periodic_work);
+
+       return 0;
+}
+#else
+#define abx500_chargalg_suspend      NULL
+#define abx500_chargalg_resume       NULL
+#endif
+
+static int abx500_chargalg_remove(struct platform_device *pdev)
+{
+       struct abx500_chargalg *di = platform_get_drvdata(pdev);
+
+       /* sysfs interface to enable/disbale charging from user space */
+       abx500_chargalg_sysfs_exit(di);
+
+       hrtimer_cancel(&di->safety_timer);
+       hrtimer_cancel(&di->maintenance_timer);
+
+       cancel_delayed_work_sync(&di->chargalg_periodic_work);
+       cancel_delayed_work_sync(&di->chargalg_wd_work);
+       cancel_work_sync(&di->chargalg_work);
+
+       /* Delete the work queue */
+       destroy_workqueue(di->chargalg_wq);
+
+       power_supply_unregister(di->chargalg_psy);
+
+       return 0;
+}
+
+static char *supply_interface[] = {
+       "ab8500_fg",
+};
+
+static const struct power_supply_desc abx500_chargalg_desc = {
+       .name                   = "abx500_chargalg",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = abx500_chargalg_props,
+       .num_properties         = ARRAY_SIZE(abx500_chargalg_props),
+       .get_property           = abx500_chargalg_get_property,
+       .external_power_changed = abx500_chargalg_external_power_changed,
+};
+
+static int abx500_chargalg_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct abx500_bm_data *plat = pdev->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       struct abx500_chargalg *di;
+       int ret = 0;
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       if (!di) {
+               dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__);
+               return -ENOMEM;
+       }
+
+       if (!plat) {
+               dev_err(&pdev->dev, "no battery management data supplied\n");
+               return -EINVAL;
+       }
+       di->bm = plat;
+
+       if (np) {
+               ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to get battery information\n");
+                       return ret;
+               }
+       }
+
+       /* get device struct and parent */
+       di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
+
+       psy_cfg.supplied_to = supply_interface;
+       psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
+       psy_cfg.drv_data = di;
+
+       /* Initilialize safety timer */
+       hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
+       di->safety_timer.function = abx500_chargalg_safety_timer_expired;
+
+       /* Initilialize maintenance timer */
+       hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
+       di->maintenance_timer.function =
+               abx500_chargalg_maintenance_timer_expired;
+
+       /* Create a work queue for the chargalg */
+       di->chargalg_wq =
+               create_singlethread_workqueue("abx500_chargalg_wq");
+       if (di->chargalg_wq == NULL) {
+               dev_err(di->dev, "failed to create work queue\n");
+               return -ENOMEM;
+       }
+
+       /* Init work for chargalg */
+       INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work,
+               abx500_chargalg_periodic_work);
+       INIT_DEFERRABLE_WORK(&di->chargalg_wd_work,
+               abx500_chargalg_wd_work);
+
+       /* Init work for chargalg */
+       INIT_WORK(&di->chargalg_work, abx500_chargalg_work);
+
+       /* To detect charger at startup */
+       di->chg_info.prev_conn_chg = -1;
+
+       /* Register chargalg power supply class */
+       di->chargalg_psy = power_supply_register(di->dev, &abx500_chargalg_desc,
+                                                &psy_cfg);
+       if (IS_ERR(di->chargalg_psy)) {
+               dev_err(di->dev, "failed to register chargalg psy\n");
+               ret = PTR_ERR(di->chargalg_psy);
+               goto free_chargalg_wq;
+       }
+
+       platform_set_drvdata(pdev, di);
+
+       /* sysfs interface to enable/disable charging from user space */
+       ret = abx500_chargalg_sysfs_init(di);
+       if (ret) {
+               dev_err(di->dev, "failed to create sysfs entry\n");
+               goto free_psy;
+       }
+       di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH;
+
+       /* Run the charging algorithm */
+       queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0);
+
+       dev_info(di->dev, "probe success\n");
+       return ret;
+
+free_psy:
+       power_supply_unregister(di->chargalg_psy);
+free_chargalg_wq:
+       destroy_workqueue(di->chargalg_wq);
+       return ret;
+}
+
+static const struct of_device_id ab8500_chargalg_match[] = {
+       { .compatible = "stericsson,ab8500-chargalg", },
+       { },
+};
+
+static struct platform_driver abx500_chargalg_driver = {
+       .probe = abx500_chargalg_probe,
+       .remove = abx500_chargalg_remove,
+       .suspend = abx500_chargalg_suspend,
+       .resume = abx500_chargalg_resume,
+       .driver = {
+               .name = "ab8500-chargalg",
+               .of_match_table = ab8500_chargalg_match,
+       },
+};
+
+module_platform_driver(abx500_chargalg_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:abx500-chargalg");
+MODULE_DESCRIPTION("abx500 battery charging algorithm");
diff --git a/drivers/power/supply/act8945a_charger.c b/drivers/power/supply/act8945a_charger.c
new file mode 100644 (file)
index 0000000..b5c00e4
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Power supply driver for the Active-semi ACT8945A PMIC
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Wenyou Yang <wenyou.yang@atmel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+
+static const char *act8945a_charger_model = "ACT8945A";
+static const char *act8945a_charger_manufacturer = "Active-semi";
+
+/**
+ * ACT8945A Charger Register Map
+ */
+
+/* 0x70: Reserved */
+#define ACT8945A_APCH_CFG              0x71
+#define ACT8945A_APCH_STATUS           0x78
+#define ACT8945A_APCH_CTRL             0x79
+#define ACT8945A_APCH_STATE            0x7A
+
+/* ACT8945A_APCH_CFG */
+#define APCH_CFG_OVPSET                        (0x3 << 0)
+#define APCH_CFG_OVPSET_6V6            (0x0 << 0)
+#define APCH_CFG_OVPSET_7V             (0x1 << 0)
+#define APCH_CFG_OVPSET_7V5            (0x2 << 0)
+#define APCH_CFG_OVPSET_8V             (0x3 << 0)
+#define APCH_CFG_PRETIMO               (0x3 << 2)
+#define APCH_CFG_PRETIMO_40_MIN                (0x0 << 2)
+#define APCH_CFG_PRETIMO_60_MIN                (0x1 << 2)
+#define APCH_CFG_PRETIMO_80_MIN                (0x2 << 2)
+#define APCH_CFG_PRETIMO_DISABLED      (0x3 << 2)
+#define APCH_CFG_TOTTIMO               (0x3 << 4)
+#define APCH_CFG_TOTTIMO_3_HOUR                (0x0 << 4)
+#define APCH_CFG_TOTTIMO_4_HOUR                (0x1 << 4)
+#define APCH_CFG_TOTTIMO_5_HOUR                (0x2 << 4)
+#define APCH_CFG_TOTTIMO_DISABLED      (0x3 << 4)
+#define APCH_CFG_SUSCHG                        (0x1 << 7)
+
+#define APCH_STATUS_CHGDAT             BIT(0)
+#define APCH_STATUS_INDAT              BIT(1)
+#define APCH_STATUS_TEMPDAT            BIT(2)
+#define APCH_STATUS_TIMRDAT            BIT(3)
+#define APCH_STATUS_CHGSTAT            BIT(4)
+#define APCH_STATUS_INSTAT             BIT(5)
+#define APCH_STATUS_TEMPSTAT           BIT(6)
+#define APCH_STATUS_TIMRSTAT           BIT(7)
+
+#define APCH_CTRL_CHGEOCOUT            BIT(0)
+#define APCH_CTRL_INDIS                        BIT(1)
+#define APCH_CTRL_TEMPOUT              BIT(2)
+#define APCH_CTRL_TIMRPRE              BIT(3)
+#define APCH_CTRL_CHGEOCIN             BIT(4)
+#define APCH_CTRL_INCON                        BIT(5)
+#define APCH_CTRL_TEMPIN               BIT(6)
+#define APCH_CTRL_TIMRTOT              BIT(7)
+
+#define APCH_STATE_ACINSTAT            (0x1 << 1)
+#define APCH_STATE_CSTATE              (0x3 << 4)
+#define APCH_STATE_CSTATE_SHIFT                4
+#define APCH_STATE_CSTATE_DISABLED     0x00
+#define APCH_STATE_CSTATE_EOC          0x01
+#define APCH_STATE_CSTATE_FAST         0x02
+#define APCH_STATE_CSTATE_PRE          0x03
+
+struct act8945a_charger {
+       struct regmap *regmap;
+       bool battery_temperature;
+};
+
+static int act8945a_get_charger_state(struct regmap *regmap, int *val)
+{
+       int ret;
+       unsigned int status, state;
+
+       ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
+       if (ret < 0)
+               return ret;
+
+       ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
+       if (ret < 0)
+               return ret;
+
+       state &= APCH_STATE_CSTATE;
+       state >>= APCH_STATE_CSTATE_SHIFT;
+
+       if (state == APCH_STATE_CSTATE_EOC) {
+               if (status & APCH_STATUS_CHGDAT)
+                       *val = POWER_SUPPLY_STATUS_FULL;
+               else
+                       *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else if ((state == APCH_STATE_CSTATE_FAST) ||
+                  (state == APCH_STATE_CSTATE_PRE)) {
+               *val = POWER_SUPPLY_STATUS_CHARGING;
+       } else {
+               *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       }
+
+       return 0;
+}
+
+static int act8945a_get_charge_type(struct regmap *regmap, int *val)
+{
+       int ret;
+       unsigned int state;
+
+       ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
+       if (ret < 0)
+               return ret;
+
+       state &= APCH_STATE_CSTATE;
+       state >>= APCH_STATE_CSTATE_SHIFT;
+
+       switch (state) {
+       case APCH_STATE_CSTATE_PRE:
+               *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+               break;
+       case APCH_STATE_CSTATE_FAST:
+               *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               break;
+       case APCH_STATE_CSTATE_EOC:
+       case APCH_STATE_CSTATE_DISABLED:
+       default:
+               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+       }
+
+       return 0;
+}
+
+static int act8945a_get_battery_health(struct act8945a_charger *charger,
+                                      struct regmap *regmap, int *val)
+{
+       int ret;
+       unsigned int status;
+
+       ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
+       if (ret < 0)
+               return ret;
+
+       if (charger->battery_temperature && !(status & APCH_STATUS_TEMPDAT))
+               *val = POWER_SUPPLY_HEALTH_OVERHEAT;
+       else if (!(status & APCH_STATUS_INDAT))
+               *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+       else if (status & APCH_STATUS_TIMRDAT)
+               *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+       else
+               *val = POWER_SUPPLY_HEALTH_GOOD;
+
+       return 0;
+}
+
+static enum power_supply_property act8945a_charger_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER
+};
+
+static int act8945a_charger_get_property(struct power_supply *psy,
+                                        enum power_supply_property prop,
+                                        union power_supply_propval *val)
+{
+       struct act8945a_charger *charger = power_supply_get_drvdata(psy);
+       struct regmap *regmap = charger->regmap;
+       int ret = 0;
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = act8945a_get_charger_state(regmap, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               ret = act8945a_get_charge_type(regmap, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = act8945a_get_battery_health(charger,
+                                                 regmap, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = act8945a_charger_model;
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = act8945a_charger_manufacturer;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+static const struct power_supply_desc act8945a_charger_desc = {
+       .name           = "act8945a-charger",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property   = act8945a_charger_get_property,
+       .properties     = act8945a_charger_props,
+       .num_properties = ARRAY_SIZE(act8945a_charger_props),
+};
+
+#define DEFAULT_TOTAL_TIME_OUT         3
+#define DEFAULT_PRE_TIME_OUT           40
+#define DEFAULT_INPUT_OVP_THRESHOLD    6600
+
+static int act8945a_charger_config(struct device *dev,
+                                  struct act8945a_charger *charger)
+{
+       struct device_node *np = dev->of_node;
+       enum of_gpio_flags flags;
+       struct regmap *regmap = charger->regmap;
+
+       u32 total_time_out;
+       u32 pre_time_out;
+       u32 input_voltage_threshold;
+       int chglev_pin;
+
+       unsigned int value = 0;
+
+       if (!np) {
+               dev_err(dev, "no charger of node\n");
+               return -EINVAL;
+       }
+
+       charger->battery_temperature = of_property_read_bool(np,
+                               "active-semi,check-battery-temperature");
+
+       chglev_pin = of_get_named_gpio_flags(np,
+                               "active-semi,chglev-gpios", 0, &flags);
+
+       if (gpio_is_valid(chglev_pin)) {
+               gpio_set_value(chglev_pin,
+                              ((flags == OF_GPIO_ACTIVE_LOW) ? 0 : 1));
+       }
+
+       if (of_property_read_u32(np,
+                                "active-semi,input-voltage-threshold-microvolt",
+                                &input_voltage_threshold))
+               input_voltage_threshold = DEFAULT_INPUT_OVP_THRESHOLD;
+
+       if (of_property_read_u32(np,
+                                "active-semi,precondition-timeout",
+                                &pre_time_out))
+               pre_time_out = DEFAULT_PRE_TIME_OUT;
+
+       if (of_property_read_u32(np, "active-semi,total-timeout",
+                                &total_time_out))
+               total_time_out = DEFAULT_TOTAL_TIME_OUT;
+
+       switch (input_voltage_threshold) {
+       case 8000:
+               value |= APCH_CFG_OVPSET_8V;
+               break;
+       case 7500:
+               value |= APCH_CFG_OVPSET_7V5;
+               break;
+       case 7000:
+               value |= APCH_CFG_OVPSET_7V;
+               break;
+       case 6600:
+       default:
+               value |= APCH_CFG_OVPSET_6V6;
+               break;
+       }
+
+       switch (pre_time_out) {
+       case 60:
+               value |= APCH_CFG_PRETIMO_60_MIN;
+               break;
+       case 80:
+               value |= APCH_CFG_PRETIMO_80_MIN;
+               break;
+       case 0:
+               value |= APCH_CFG_PRETIMO_DISABLED;
+               break;
+       case 40:
+       default:
+               value |= APCH_CFG_PRETIMO_40_MIN;
+               break;
+       }
+
+       switch (total_time_out) {
+       case 4:
+               value |= APCH_CFG_TOTTIMO_4_HOUR;
+               break;
+       case 5:
+               value |= APCH_CFG_TOTTIMO_5_HOUR;
+               break;
+       case 0:
+               value |= APCH_CFG_TOTTIMO_DISABLED;
+               break;
+       case 3:
+       default:
+               value |= APCH_CFG_TOTTIMO_3_HOUR;
+               break;
+       }
+
+       return regmap_write(regmap, ACT8945A_APCH_CFG, value);
+}
+
+static int act8945a_charger_probe(struct platform_device *pdev)
+{
+       struct act8945a_charger *charger;
+       struct power_supply *psy;
+       struct power_supply_config psy_cfg = {};
+       int ret;
+
+       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
+       if (!charger)
+               return -ENOMEM;
+
+       charger->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!charger->regmap) {
+               dev_err(&pdev->dev, "Parent did not provide regmap\n");
+               return -EINVAL;
+       }
+
+       ret = act8945a_charger_config(pdev->dev.parent, charger);
+       if (ret)
+               return ret;
+
+       psy_cfg.of_node = pdev->dev.parent->of_node;
+       psy_cfg.drv_data = charger;
+
+       psy = devm_power_supply_register(&pdev->dev,
+                                        &act8945a_charger_desc,
+                                        &psy_cfg);
+       if (IS_ERR(psy)) {
+               dev_err(&pdev->dev, "failed to register power supply\n");
+               return PTR_ERR(psy);
+       }
+
+       return 0;
+}
+
+static struct platform_driver act8945a_charger_driver = {
+       .driver = {
+               .name = "act8945a-charger",
+       },
+       .probe  = act8945a_charger_probe,
+};
+module_platform_driver(act8945a_charger_driver);
+
+MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");
+MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/apm_power.c b/drivers/power/supply/apm_power.c
new file mode 100644 (file)
index 0000000..9d1a7fb
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright Â© 2007 Anton Vorontsov <cbou@mail.ru>
+ * Copyright Â© 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
+ *
+ * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/power_supply.h>
+#include <linux/apm-emulation.h>
+
+
+#define PSY_PROP(psy, prop, val) (power_supply_get_property(psy, \
+                        POWER_SUPPLY_PROP_##prop, val))
+
+#define _MPSY_PROP(prop, val) (power_supply_get_property(main_battery, \
+                                                        prop, val))
+
+#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
+
+static DEFINE_MUTEX(apm_mutex);
+static struct power_supply *main_battery;
+
+enum apm_source {
+       SOURCE_ENERGY,
+       SOURCE_CHARGE,
+       SOURCE_VOLTAGE,
+};
+
+struct find_bat_param {
+       struct power_supply *main;
+       struct power_supply *bat;
+       struct power_supply *max_charge_bat;
+       struct power_supply *max_energy_bat;
+       union power_supply_propval full;
+       int max_charge;
+       int max_energy;
+};
+
+static int __find_main_battery(struct device *dev, void *data)
+{
+       struct find_bat_param *bp = (struct find_bat_param *)data;
+
+       bp->bat = dev_get_drvdata(dev);
+
+       if (bp->bat->desc->use_for_apm) {
+               /* nice, we explicitly asked to report this battery. */
+               bp->main = bp->bat;
+               return 1;
+       }
+
+       if (!PSY_PROP(bp->bat, CHARGE_FULL_DESIGN, &bp->full) ||
+                       !PSY_PROP(bp->bat, CHARGE_FULL, &bp->full)) {
+               if (bp->full.intval > bp->max_charge) {
+                       bp->max_charge_bat = bp->bat;
+                       bp->max_charge = bp->full.intval;
+               }
+       } else if (!PSY_PROP(bp->bat, ENERGY_FULL_DESIGN, &bp->full) ||
+                       !PSY_PROP(bp->bat, ENERGY_FULL, &bp->full)) {
+               if (bp->full.intval > bp->max_energy) {
+                       bp->max_energy_bat = bp->bat;
+                       bp->max_energy = bp->full.intval;
+               }
+       }
+       return 0;
+}
+
+static void find_main_battery(void)
+{
+       struct find_bat_param bp;
+       int error;
+
+       memset(&bp, 0, sizeof(struct find_bat_param));
+       main_battery = NULL;
+       bp.main = main_battery;
+
+       error = class_for_each_device(power_supply_class, NULL, &bp,
+                                     __find_main_battery);
+       if (error) {
+               main_battery = bp.main;
+               return;
+       }
+
+       if ((bp.max_energy_bat && bp.max_charge_bat) &&
+                       (bp.max_energy_bat != bp.max_charge_bat)) {
+               /* try guess battery with more capacity */
+               if (!PSY_PROP(bp.max_charge_bat, VOLTAGE_MAX_DESIGN,
+                             &bp.full)) {
+                       if (bp.max_energy > bp.max_charge * bp.full.intval)
+                               main_battery = bp.max_energy_bat;
+                       else
+                               main_battery = bp.max_charge_bat;
+               } else if (!PSY_PROP(bp.max_energy_bat, VOLTAGE_MAX_DESIGN,
+                                                                 &bp.full)) {
+                       if (bp.max_charge > bp.max_energy / bp.full.intval)
+                               main_battery = bp.max_charge_bat;
+                       else
+                               main_battery = bp.max_energy_bat;
+               } else {
+                       /* give up, choice any */
+                       main_battery = bp.max_energy_bat;
+               }
+       } else if (bp.max_charge_bat) {
+               main_battery = bp.max_charge_bat;
+       } else if (bp.max_energy_bat) {
+               main_battery = bp.max_energy_bat;
+       } else {
+               /* give up, try the last if any */
+               main_battery = bp.bat;
+       }
+}
+
+static int do_calculate_time(int status, enum apm_source source)
+{
+       union power_supply_propval full;
+       union power_supply_propval empty;
+       union power_supply_propval cur;
+       union power_supply_propval I;
+       enum power_supply_property full_prop;
+       enum power_supply_property full_design_prop;
+       enum power_supply_property empty_prop;
+       enum power_supply_property empty_design_prop;
+       enum power_supply_property cur_avg_prop;
+       enum power_supply_property cur_now_prop;
+
+       if (MPSY_PROP(CURRENT_AVG, &I)) {
+               /* if battery can't report average value, use momentary */
+               if (MPSY_PROP(CURRENT_NOW, &I))
+                       return -1;
+       }
+
+       if (!I.intval)
+               return 0;
+
+       switch (source) {
+       case SOURCE_CHARGE:
+               full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
+               full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
+               empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
+               empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
+               cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
+               cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
+               break;
+       case SOURCE_ENERGY:
+               full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
+               full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
+               empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
+               empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
+               cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
+               cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
+               break;
+       case SOURCE_VOLTAGE:
+               full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
+               full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
+               empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
+               empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
+               cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
+               cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+               break;
+       default:
+               printk(KERN_ERR "Unsupported source: %d\n", source);
+               return -1;
+       }
+
+       if (_MPSY_PROP(full_prop, &full)) {
+               /* if battery can't report this property, use design value */
+               if (_MPSY_PROP(full_design_prop, &full))
+                       return -1;
+       }
+
+       if (_MPSY_PROP(empty_prop, &empty)) {
+               /* if battery can't report this property, use design value */
+               if (_MPSY_PROP(empty_design_prop, &empty))
+                       empty.intval = 0;
+       }
+
+       if (_MPSY_PROP(cur_avg_prop, &cur)) {
+               /* if battery can't report average value, use momentary */
+               if (_MPSY_PROP(cur_now_prop, &cur))
+                       return -1;
+       }
+
+       if (status == POWER_SUPPLY_STATUS_CHARGING)
+               return ((cur.intval - full.intval) * 60L) / I.intval;
+       else
+               return -((cur.intval - empty.intval) * 60L) / I.intval;
+}
+
+static int calculate_time(int status)
+{
+       int time;
+
+       time = do_calculate_time(status, SOURCE_ENERGY);
+       if (time != -1)
+               return time;
+
+       time = do_calculate_time(status, SOURCE_CHARGE);
+       if (time != -1)
+               return time;
+
+       time = do_calculate_time(status, SOURCE_VOLTAGE);
+       if (time != -1)
+               return time;
+
+       return -1;
+}
+
+static int calculate_capacity(enum apm_source source)
+{
+       enum power_supply_property full_prop, empty_prop;
+       enum power_supply_property full_design_prop, empty_design_prop;
+       enum power_supply_property now_prop, avg_prop;
+       union power_supply_propval empty, full, cur;
+       int ret;
+
+       switch (source) {
+       case SOURCE_CHARGE:
+               full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
+               empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
+               full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
+               empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
+               now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
+               avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
+               break;
+       case SOURCE_ENERGY:
+               full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
+               empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
+               full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
+               empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
+               now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
+               avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
+               break;
+       case SOURCE_VOLTAGE:
+               full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX;
+               empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN;
+               full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN;
+               empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN;
+               now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+               avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG;
+               break;
+       default:
+               printk(KERN_ERR "Unsupported source: %d\n", source);
+               return -1;
+       }
+
+       if (_MPSY_PROP(full_prop, &full)) {
+               /* if battery can't report this property, use design value */
+               if (_MPSY_PROP(full_design_prop, &full))
+                       return -1;
+       }
+
+       if (_MPSY_PROP(avg_prop, &cur)) {
+               /* if battery can't report average value, use momentary */
+               if (_MPSY_PROP(now_prop, &cur))
+                       return -1;
+       }
+
+       if (_MPSY_PROP(empty_prop, &empty)) {
+               /* if battery can't report this property, use design value */
+               if (_MPSY_PROP(empty_design_prop, &empty))
+                       empty.intval = 0;
+       }
+
+       if (full.intval - empty.intval)
+               ret =  ((cur.intval - empty.intval) * 100L) /
+                      (full.intval - empty.intval);
+       else
+               return -1;
+
+       if (ret > 100)
+               return 100;
+       else if (ret < 0)
+               return 0;
+
+       return ret;
+}
+
+static void apm_battery_apm_get_power_status(struct apm_power_info *info)
+{
+       union power_supply_propval status;
+       union power_supply_propval capacity, time_to_full, time_to_empty;
+
+       mutex_lock(&apm_mutex);
+       find_main_battery();
+       if (!main_battery) {
+               mutex_unlock(&apm_mutex);
+               return;
+       }
+
+       /* status */
+
+       if (MPSY_PROP(STATUS, &status))
+               status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       /* ac line status */
+
+       if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
+           (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
+           (status.intval == POWER_SUPPLY_STATUS_FULL))
+               info->ac_line_status = APM_AC_ONLINE;
+       else
+               info->ac_line_status = APM_AC_OFFLINE;
+
+       /* battery life (i.e. capacity, in percents) */
+
+       if (MPSY_PROP(CAPACITY, &capacity) == 0) {
+               info->battery_life = capacity.intval;
+       } else {
+               /* try calculate using energy */
+               info->battery_life = calculate_capacity(SOURCE_ENERGY);
+               /* if failed try calculate using charge instead */
+               if (info->battery_life == -1)
+                       info->battery_life = calculate_capacity(SOURCE_CHARGE);
+               if (info->battery_life == -1)
+                       info->battery_life = calculate_capacity(SOURCE_VOLTAGE);
+       }
+
+       /* charging status */
+
+       if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
+               info->battery_status = APM_BATTERY_STATUS_CHARGING;
+       } else {
+               if (info->battery_life > 50)
+                       info->battery_status = APM_BATTERY_STATUS_HIGH;
+               else if (info->battery_life > 5)
+                       info->battery_status = APM_BATTERY_STATUS_LOW;
+               else
+                       info->battery_status = APM_BATTERY_STATUS_CRITICAL;
+       }
+       info->battery_flag = info->battery_status;
+
+       /* time */
+
+       info->units = APM_UNITS_MINS;
+
+       if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
+               if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
+                               !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
+                       info->time = time_to_full.intval / 60;
+               else
+                       info->time = calculate_time(status.intval);
+       } else {
+               if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
+                             !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
+                       info->time = time_to_empty.intval / 60;
+               else
+                       info->time = calculate_time(status.intval);
+       }
+
+       mutex_unlock(&apm_mutex);
+}
+
+static int __init apm_battery_init(void)
+{
+       printk(KERN_INFO "APM Battery Driver\n");
+
+       apm_get_power_status = apm_battery_apm_get_power_status;
+       return 0;
+}
+
+static void __exit apm_battery_exit(void)
+{
+       apm_get_power_status = NULL;
+}
+
+module_init(apm_battery_init);
+module_exit(apm_battery_exit);
+
+MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
+MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
new file mode 100644 (file)
index 0000000..6af6feb
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * AXP20x PMIC USB power supply status driver
+ *
+ * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org>
+ *
+ * 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;  either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRVNAME "axp20x-usb-power-supply"
+
+#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
+#define AXP20X_PWR_STATUS_VBUS_USED    BIT(4)
+
+#define AXP20X_USB_STATUS_VBUS_VALID   BIT(2)
+
+#define AXP20X_VBUS_VHOLD_uV(b)                (4000000 + (((b) >> 3) & 7) * 100000)
+#define AXP20X_VBUS_CLIMIT_MASK                3
+#define AXP20X_VBUC_CLIMIT_900mA       0
+#define AXP20X_VBUC_CLIMIT_500mA       1
+#define AXP20X_VBUC_CLIMIT_100mA       2
+#define AXP20X_VBUC_CLIMIT_NONE                3
+
+#define AXP20X_ADC_EN1_VBUS_CURR       BIT(2)
+#define AXP20X_ADC_EN1_VBUS_VOLT       BIT(3)
+
+#define AXP20X_VBUS_MON_VBUS_VALID     BIT(3)
+
+struct axp20x_usb_power {
+       struct device_node *np;
+       struct regmap *regmap;
+       struct power_supply *supply;
+};
+
+static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
+{
+       struct axp20x_usb_power *power = devid;
+
+       power_supply_changed(power->supply);
+
+       return IRQ_HANDLED;
+}
+
+static int axp20x_usb_power_get_property(struct power_supply *psy,
+       enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
+       unsigned int input, v;
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+               ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
+               if (ret)
+                       return ret;
+
+               val->intval = AXP20X_VBUS_VHOLD_uV(v);
+               return 0;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = axp20x_read_variable_width(power->regmap,
+                                                AXP20X_VBUS_V_ADC_H, 12);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret * 1700; /* 1 step = 1.7 mV */
+               return 0;
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
+               if (ret)
+                       return ret;
+
+               switch (v & AXP20X_VBUS_CLIMIT_MASK) {
+               case AXP20X_VBUC_CLIMIT_100mA:
+                       if (of_device_is_compatible(power->np,
+                                       "x-powers,axp202-usb-power-supply")) {
+                               val->intval = 100000;
+                       } else {
+                               val->intval = -1; /* No 100mA limit */
+                       }
+                       break;
+               case AXP20X_VBUC_CLIMIT_500mA:
+                       val->intval = 500000;
+                       break;
+               case AXP20X_VBUC_CLIMIT_900mA:
+                       val->intval = 900000;
+                       break;
+               case AXP20X_VBUC_CLIMIT_NONE:
+                       val->intval = -1;
+                       break;
+               }
+               return 0;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = axp20x_read_variable_width(power->regmap,
+                                                AXP20X_VBUS_I_ADC_H, 12);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret * 375; /* 1 step = 0.375 mA */
+               return 0;
+       default:
+               break;
+       }
+
+       /* All the properties below need the input-status reg value */
+       ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input);
+       if (ret)
+               return ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
+                       val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+                       break;
+               }
+
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+               if (of_device_is_compatible(power->np,
+                               "x-powers,axp202-usb-power-supply")) {
+                       ret = regmap_read(power->regmap,
+                                         AXP20X_USB_OTG_STATUS, &v);
+                       if (ret)
+                               return ret;
+
+                       if (!(v & AXP20X_USB_STATUS_VBUS_VALID))
+                               val->intval =
+                                       POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               }
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property axp20x_usb_power_properties[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static enum power_supply_property axp22x_usb_power_properties[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static const struct power_supply_desc axp20x_usb_power_desc = {
+       .name = "axp20x-usb",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = axp20x_usb_power_properties,
+       .num_properties = ARRAY_SIZE(axp20x_usb_power_properties),
+       .get_property = axp20x_usb_power_get_property,
+};
+
+static const struct power_supply_desc axp22x_usb_power_desc = {
+       .name = "axp20x-usb",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = axp22x_usb_power_properties,
+       .num_properties = ARRAY_SIZE(axp22x_usb_power_properties),
+       .get_property = axp20x_usb_power_get_property,
+};
+
+static int axp20x_usb_power_probe(struct platform_device *pdev)
+{
+       struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+       struct power_supply_config psy_cfg = {};
+       struct axp20x_usb_power *power;
+       static const char * const axp20x_irq_names[] = { "VBUS_PLUGIN",
+               "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL };
+       static const char * const axp22x_irq_names[] = {
+               "VBUS_PLUGIN", "VBUS_REMOVAL", NULL };
+       static const char * const *irq_names;
+       const struct power_supply_desc *usb_power_desc;
+       int i, irq, ret;
+
+       if (!of_device_is_available(pdev->dev.of_node))
+               return -ENODEV;
+
+       if (!axp20x) {
+               dev_err(&pdev->dev, "Parent drvdata not set\n");
+               return -EINVAL;
+       }
+
+       power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
+       if (!power)
+               return -ENOMEM;
+
+       power->np = pdev->dev.of_node;
+       power->regmap = axp20x->regmap;
+
+       if (of_device_is_compatible(power->np,
+                       "x-powers,axp202-usb-power-supply")) {
+               /* Enable vbus valid checking */
+               ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
+                                        AXP20X_VBUS_MON_VBUS_VALID,
+                                        AXP20X_VBUS_MON_VBUS_VALID);
+               if (ret)
+                       return ret;
+
+               /* Enable vbus voltage and current measurement */
+               ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
+                       AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT,
+                       AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT);
+               if (ret)
+                       return ret;
+
+               usb_power_desc = &axp20x_usb_power_desc;
+               irq_names = axp20x_irq_names;
+       } else if (of_device_is_compatible(power->np,
+                       "x-powers,axp221-usb-power-supply")) {
+               usb_power_desc = &axp22x_usb_power_desc;
+               irq_names = axp22x_irq_names;
+       } else {
+               dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
+                       axp20x->variant);
+               return -EINVAL;
+       }
+
+       psy_cfg.of_node = pdev->dev.of_node;
+       psy_cfg.drv_data = power;
+
+       power->supply = devm_power_supply_register(&pdev->dev, usb_power_desc,
+                                                  &psy_cfg);
+       if (IS_ERR(power->supply))
+               return PTR_ERR(power->supply);
+
+       /* Request irqs after registering, as irqs may trigger immediately */
+       for (i = 0; irq_names[i]; i++) {
+               irq = platform_get_irq_byname(pdev, irq_names[i]);
+               if (irq < 0) {
+                       dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
+                                irq_names[i], irq);
+                       continue;
+               }
+               irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
+               ret = devm_request_any_context_irq(&pdev->dev, irq,
+                               axp20x_usb_power_irq, 0, DRVNAME, power);
+               if (ret < 0)
+                       dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
+                                irq_names[i], ret);
+       }
+
+       return 0;
+}
+
+static const struct of_device_id axp20x_usb_power_match[] = {
+       { .compatible = "x-powers,axp202-usb-power-supply" },
+       { .compatible = "x-powers,axp221-usb-power-supply" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
+
+static struct platform_driver axp20x_usb_power_driver = {
+       .probe = axp20x_usb_power_probe,
+       .driver = {
+               .name = DRVNAME,
+               .of_match_table = axp20x_usb_power_match,
+       },
+};
+
+module_platform_driver(axp20x_usb_power_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c
new file mode 100644 (file)
index 0000000..4030eeb
--- /dev/null
@@ -0,0 +1,970 @@
+/*
+ * axp288_charger.c - X-power AXP288 PMIC Charger driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed 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/device.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/usb/otg.h>
+#include <linux/notifier.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/property.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/extcon.h>
+
+#define PS_STAT_VBUS_TRIGGER           (1 << 0)
+#define PS_STAT_BAT_CHRG_DIR           (1 << 2)
+#define PS_STAT_VBAT_ABOVE_VHOLD       (1 << 3)
+#define PS_STAT_VBUS_VALID             (1 << 4)
+#define PS_STAT_VBUS_PRESENT           (1 << 5)
+
+#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 VBUS_ISPOUT_CUR_LIM_MASK       0x03
+#define VBUS_ISPOUT_CUR_LIM_BIT_POS    0
+#define VBUS_ISPOUT_CUR_LIM_900MA      0x0     /* 900mA */
+#define VBUS_ISPOUT_CUR_LIM_1500MA     0x1     /* 1500mA */
+#define VBUS_ISPOUT_CUR_LIM_2000MA     0x2     /* 2000mA */
+#define VBUS_ISPOUT_CUR_NO_LIM         0x3     /* 2500mA */
+#define VBUS_ISPOUT_VHOLD_SET_MASK     0x31
+#define VBUS_ISPOUT_VHOLD_SET_BIT_POS  0x3
+#define VBUS_ISPOUT_VHOLD_SET_OFFSET   4000    /* 4000mV */
+#define VBUS_ISPOUT_VHOLD_SET_LSB_RES  100     /* 100mV */
+#define VBUS_ISPOUT_VHOLD_SET_4300MV   0x3     /* 4300mV */
+#define VBUS_ISPOUT_VBUS_PATH_DIS      (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 CNTL2_CC_TIMEOUT_MASK          0x3     /* 2 bits */
+#define CNTL2_CC_TIMEOUT_OFFSET                6       /* 6 Hrs */
+#define CNTL2_CC_TIMEOUT_LSB_RES       2       /* 2 Hrs */
+#define CNTL2_CC_TIMEOUT_12HRS         0x3     /* 12 Hrs */
+#define CNTL2_CHGLED_TYPEB             (1 << 4)
+#define CNTL2_CHG_OUT_TURNON           (1 << 5)
+#define CNTL2_PC_TIMEOUT_MASK          0xC0
+#define CNTL2_PC_TIMEOUT_OFFSET                40      /* 40 mins */
+#define CNTL2_PC_TIMEOUT_LSB_RES       10      /* 10 mins */
+#define CNTL2_PC_TIMEOUT_70MINS                0x3
+
+#define CHRG_ILIM_TEMP_LOOP_EN         (1 << 3)
+#define CHRG_VBUS_ILIM_MASK            0xf0
+#define CHRG_VBUS_ILIM_BIT_POS         4
+#define CHRG_VBUS_ILIM_100MA           0x0     /* 100mA */
+#define CHRG_VBUS_ILIM_500MA           0x1     /* 500mA */
+#define CHRG_VBUS_ILIM_900MA           0x2     /* 900mA */
+#define CHRG_VBUS_ILIM_1500MA          0x3     /* 1500mA */
+#define CHRG_VBUS_ILIM_2000MA          0x4     /* 2000mA */
+#define CHRG_VBUS_ILIM_2500MA          0x5     /* 2500mA */
+#define CHRG_VBUS_ILIM_3000MA          0x6     /* 3000mA */
+
+#define CHRG_VLTFC_0C                  0xA5    /* 0 DegC */
+#define CHRG_VHTFC_45C                 0x1F    /* 45 DegC */
+
+#define BAT_IRQ_CFG_CHRG_DONE          (1 << 2)
+#define BAT_IRQ_CFG_CHRG_START         (1 << 3)
+#define BAT_IRQ_CFG_BAT_SAFE_EXIT      (1 << 4)
+#define BAT_IRQ_CFG_BAT_SAFE_ENTER     (1 << 5)
+#define BAT_IRQ_CFG_BAT_DISCON         (1 << 6)
+#define BAT_IRQ_CFG_BAT_CONN           (1 << 7)
+#define BAT_IRQ_CFG_BAT_MASK           0xFC
+
+#define TEMP_IRQ_CFG_QCBTU             (1 << 4)
+#define TEMP_IRQ_CFG_CBTU              (1 << 5)
+#define TEMP_IRQ_CFG_QCBTO             (1 << 6)
+#define TEMP_IRQ_CFG_CBTO              (1 << 7)
+#define TEMP_IRQ_CFG_MASK              0xF0
+
+#define FG_CNTL_OCV_ADJ_EN             (1 << 3)
+
+#define CV_4100MV                      4100    /* 4100mV */
+#define CV_4150MV                      4150    /* 4150mV */
+#define CV_4200MV                      4200    /* 4200mV */
+#define CV_4350MV                      4350    /* 4350mV */
+
+#define CC_200MA                       200     /*  200mA */
+#define CC_600MA                       600     /*  600mA */
+#define CC_800MA                       800     /*  800mA */
+#define CC_1000MA                      1000    /* 1000mA */
+#define CC_1600MA                      1600    /* 1600mA */
+#define CC_2000MA                      2000    /* 2000mA */
+
+#define ILIM_100MA                     100     /* 100mA */
+#define ILIM_500MA                     500     /* 500mA */
+#define ILIM_900MA                     900     /* 900mA */
+#define ILIM_1500MA                    1500    /* 1500mA */
+#define ILIM_2000MA                    2000    /* 2000mA */
+#define ILIM_2500MA                    2500    /* 2500mA */
+#define ILIM_3000MA                    3000    /* 3000mA */
+
+#define AXP288_EXTCON_DEV_NAME         "axp288_extcon"
+
+enum {
+       VBUS_OV_IRQ = 0,
+       CHARGE_DONE_IRQ,
+       CHARGE_CHARGING_IRQ,
+       BAT_SAFE_QUIT_IRQ,
+       BAT_SAFE_ENTER_IRQ,
+       QCBTU_IRQ,
+       CBTU_IRQ,
+       QCBTO_IRQ,
+       CBTO_IRQ,
+       CHRG_INTR_END,
+};
+
+struct axp288_chrg_info {
+       struct platform_device *pdev;
+       struct axp20x_chrg_pdata *pdata;
+       struct regmap *regmap;
+       struct regmap_irq_chip_data *regmap_irqc;
+       int irq[CHRG_INTR_END];
+       struct power_supply *psy_usb;
+       struct mutex lock;
+
+       /* OTG/Host mode */
+       struct {
+               struct work_struct work;
+               struct extcon_dev *cable;
+               struct notifier_block id_nb;
+               bool id_short;
+       } otg;
+
+       /* SDP/CDP/DCP USB charging cable notifications */
+       struct {
+               struct extcon_dev *edev;
+               bool connected;
+               enum power_supply_type chg_type;
+               struct notifier_block nb;
+               struct work_struct work;
+       } cable;
+
+       int health;
+       int inlmt;
+       int cc;
+       int cv;
+       int max_cc;
+       int max_cv;
+       bool online;
+       bool present;
+       bool enable_charger;
+       bool is_charger_enabled;
+};
+
+static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
+{
+       u8 reg_val;
+       int ret;
+
+       if (cc < CHRG_CCCV_CC_OFFSET)
+               cc = CHRG_CCCV_CC_OFFSET;
+       else if (cc > info->max_cc)
+               cc = info->max_cc;
+
+       reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
+       cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
+       reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
+
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_CC_MASK, reg_val);
+       if (ret >= 0)
+               info->cc = cc;
+
+       return ret;
+}
+
+static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
+{
+       u8 reg_val;
+       int ret;
+
+       if (cv <= CV_4100MV) {
+               reg_val = CHRG_CCCV_CV_4100MV;
+               cv = CV_4100MV;
+       } else if (cv <= CV_4150MV) {
+               reg_val = CHRG_CCCV_CV_4150MV;
+               cv = CV_4150MV;
+       } else if (cv <= CV_4200MV) {
+               reg_val = CHRG_CCCV_CV_4200MV;
+               cv = CV_4200MV;
+       } else {
+               reg_val = CHRG_CCCV_CV_4350MV;
+               cv = CV_4350MV;
+       }
+
+       reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
+
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_CV_MASK, reg_val);
+
+       if (ret >= 0)
+               info->cv = cv;
+
+       return ret;
+}
+
+static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
+                                          int inlmt)
+{
+       int ret;
+       unsigned int val;
+       u8 reg_val;
+
+       /* Read in limit register */
+       ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val);
+       if (ret < 0)
+               goto set_inlmt_fail;
+
+       if (inlmt <= ILIM_100MA) {
+               reg_val = CHRG_VBUS_ILIM_100MA;
+               inlmt = ILIM_100MA;
+       } else if (inlmt <= ILIM_500MA) {
+               reg_val = CHRG_VBUS_ILIM_500MA;
+               inlmt = ILIM_500MA;
+       } else if (inlmt <= ILIM_900MA) {
+               reg_val = CHRG_VBUS_ILIM_900MA;
+               inlmt = ILIM_900MA;
+       } else if (inlmt <= ILIM_1500MA) {
+               reg_val = CHRG_VBUS_ILIM_1500MA;
+               inlmt = ILIM_1500MA;
+       } else if (inlmt <= ILIM_2000MA) {
+               reg_val = CHRG_VBUS_ILIM_2000MA;
+               inlmt = ILIM_2000MA;
+       } else if (inlmt <= ILIM_2500MA) {
+               reg_val = CHRG_VBUS_ILIM_2500MA;
+               inlmt = ILIM_2500MA;
+       } else {
+               reg_val = CHRG_VBUS_ILIM_3000MA;
+               inlmt = ILIM_3000MA;
+       }
+
+       reg_val = (val & ~CHRG_VBUS_ILIM_MASK)
+                       | (reg_val << CHRG_VBUS_ILIM_BIT_POS);
+       ret = regmap_write(info->regmap, AXP20X_CHRG_BAK_CTRL, reg_val);
+       if (ret >= 0)
+               info->inlmt = inlmt;
+       else
+               dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
+
+
+set_inlmt_fail:
+       return ret;
+}
+
+static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
+                                                               bool enable)
+{
+       int ret;
+
+       if (enable)
+               ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
+                                       VBUS_ISPOUT_VBUS_PATH_DIS, 0);
+       else
+               ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
+                       VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
+
+       if (ret < 0)
+               dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
+
+
+       return ret;
+}
+
+static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
+                                                               bool enable)
+{
+       int ret;
+
+       if (enable)
+               ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
+       else
+               ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_CHG_EN, 0);
+       if (ret < 0)
+               dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
+       else
+               info->is_charger_enabled = enable;
+
+       return ret;
+}
+
+static int axp288_charger_is_present(struct axp288_chrg_info *info)
+{
+       int ret, present = 0;
+       unsigned int val;
+
+       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+       if (ret < 0)
+               return ret;
+
+       if (val & PS_STAT_VBUS_PRESENT)
+               present = 1;
+       return present;
+}
+
+static int axp288_charger_is_online(struct axp288_chrg_info *info)
+{
+       int ret, online = 0;
+       unsigned int val;
+
+       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+       if (ret < 0)
+               return ret;
+
+       if (val & PS_STAT_VBUS_VALID)
+               online = 1;
+       return online;
+}
+
+static int axp288_get_charger_health(struct axp288_chrg_info *info)
+{
+       int ret, pwr_stat, chrg_stat;
+       int health = POWER_SUPPLY_HEALTH_UNKNOWN;
+       unsigned int val;
+
+       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+       if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT))
+               goto health_read_fail;
+       else
+               pwr_stat = val;
+
+       ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val);
+       if (ret < 0)
+               goto health_read_fail;
+       else
+               chrg_stat = val;
+
+       if (!(pwr_stat & PS_STAT_VBUS_VALID))
+               health = POWER_SUPPLY_HEALTH_DEAD;
+       else if (chrg_stat & CHRG_STAT_PMIC_OTP)
+               health = POWER_SUPPLY_HEALTH_OVERHEAT;
+       else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE)
+               health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+       else
+               health = POWER_SUPPLY_HEALTH_GOOD;
+
+health_read_fail:
+       return health;
+}
+
+static int axp288_charger_usb_set_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   const union power_supply_propval *val)
+{
+       struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
+       int ret = 0;
+       int scaled_val;
+
+       mutex_lock(&info->lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               scaled_val = min(val->intval, info->max_cc);
+               scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
+               ret = axp288_charger_set_cc(info, scaled_val);
+               if (ret < 0)
+                       dev_warn(&info->pdev->dev, "set charge current failed\n");
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               scaled_val = min(val->intval, info->max_cv);
+               scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
+               ret = axp288_charger_set_cv(info, scaled_val);
+               if (ret < 0)
+                       dev_warn(&info->pdev->dev, "set charge voltage failed\n");
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       mutex_unlock(&info->lock);
+       return ret;
+}
+
+static int axp288_charger_usb_get_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   union power_supply_propval *val)
+{
+       struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       mutex_lock(&info->lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+               /* Check for OTG case first */
+               if (info->otg.id_short) {
+                       val->intval = 0;
+                       break;
+               }
+               ret = axp288_charger_is_present(info);
+               if (ret < 0)
+                       goto psy_get_prop_fail;
+               info->present = ret;
+               val->intval = info->present;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               /* Check for OTG case first */
+               if (info->otg.id_short) {
+                       val->intval = 0;
+                       break;
+               }
+               ret = axp288_charger_is_online(info);
+               if (ret < 0)
+                       goto psy_get_prop_fail;
+               info->online = ret;
+               val->intval = info->online;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = axp288_get_charger_health(info);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               val->intval = info->cc * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               val->intval = info->max_cc * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               val->intval = info->cv * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               val->intval = info->max_cv * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               val->intval = info->inlmt * 1000;
+               break;
+       default:
+               ret = -EINVAL;
+               goto psy_get_prop_fail;
+       }
+
+psy_get_prop_fail:
+       mutex_unlock(&info->lock);
+       return ret;
+}
+
+static int axp288_charger_property_is_writeable(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = 1;
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property axp288_usb_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_TYPE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+};
+
+static const struct power_supply_desc axp288_charger_desc = {
+       .name                   = "axp288_charger",
+       .type                   = POWER_SUPPLY_TYPE_USB,
+       .properties             = axp288_usb_props,
+       .num_properties         = ARRAY_SIZE(axp288_usb_props),
+       .get_property           = axp288_charger_usb_get_property,
+       .set_property           = axp288_charger_usb_set_property,
+       .property_is_writeable  = axp288_charger_property_is_writeable,
+};
+
+static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
+{
+       struct axp288_chrg_info *info = dev;
+       int i;
+
+       for (i = 0; i < CHRG_INTR_END; i++) {
+               if (info->irq[i] == irq)
+                       break;
+       }
+
+       if (i >= CHRG_INTR_END) {
+               dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
+               return IRQ_NONE;
+       }
+
+       switch (i) {
+       case VBUS_OV_IRQ:
+               dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
+               break;
+       case CHARGE_DONE_IRQ:
+               dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
+               break;
+       case CHARGE_CHARGING_IRQ:
+               dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
+               break;
+       case BAT_SAFE_QUIT_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Quit Safe Mode(restart timer) Charging IRQ\n");
+               break;
+       case BAT_SAFE_ENTER_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Enter Safe Mode(timer expire) Charging IRQ\n");
+               break;
+       case QCBTU_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Quit Battery Under Temperature(CHRG) INTR\n");
+               break;
+       case CBTU_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Hit Battery Under Temperature(CHRG) INTR\n");
+               break;
+       case QCBTO_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Quit Battery Over Temperature(CHRG) INTR\n");
+               break;
+       case CBTO_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Hit Battery Over Temperature(CHRG) INTR\n");
+               break;
+       default:
+               dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
+               goto out;
+       }
+
+       power_supply_changed(info->psy_usb);
+out:
+       return IRQ_HANDLED;
+}
+
+static void axp288_charger_extcon_evt_worker(struct work_struct *work)
+{
+       struct axp288_chrg_info *info =
+           container_of(work, struct axp288_chrg_info, cable.work);
+       int ret, current_limit;
+       bool changed = false;
+       struct extcon_dev *edev = info->cable.edev;
+       bool old_connected = info->cable.connected;
+
+       /* Determine cable/charger type */
+       if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_SDP) > 0) {
+               dev_dbg(&info->pdev->dev, "USB SDP charger  is connected");
+               info->cable.connected = true;
+               info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
+       } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_CDP) > 0) {
+               dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
+               info->cable.connected = true;
+               info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
+       } else if (extcon_get_cable_state_(edev, EXTCON_CHG_USB_DCP) > 0) {
+               dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
+               info->cable.connected = true;
+               info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
+       } else {
+               if (old_connected)
+                       dev_dbg(&info->pdev->dev, "USB charger disconnected");
+               info->cable.connected = false;
+               info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
+       }
+
+       /* Cable status changed */
+       if (old_connected != info->cable.connected)
+               changed = true;
+
+       if (!changed)
+               return;
+
+       mutex_lock(&info->lock);
+
+       if (info->is_charger_enabled && !info->cable.connected) {
+               info->enable_charger = false;
+               ret = axp288_charger_enable_charger(info, info->enable_charger);
+               if (ret < 0)
+                       dev_err(&info->pdev->dev,
+                               "cannot disable charger (%d)", ret);
+
+       } else if (!info->is_charger_enabled && info->cable.connected) {
+               switch (info->cable.chg_type) {
+               case POWER_SUPPLY_TYPE_USB:
+                       current_limit = ILIM_500MA;
+                       break;
+               case POWER_SUPPLY_TYPE_USB_CDP:
+                       current_limit = ILIM_1500MA;
+                       break;
+               case POWER_SUPPLY_TYPE_USB_DCP:
+                       current_limit = ILIM_2000MA;
+                       break;
+               default:
+                       /* Unknown */
+                       current_limit = 0;
+                       break;
+               }
+
+               /* Set vbus current limit first, then enable charger */
+               ret = axp288_charger_set_vbus_inlmt(info, current_limit);
+               if (ret < 0) {
+                       dev_err(&info->pdev->dev,
+                               "error setting current limit (%d)", ret);
+               } else {
+                       info->enable_charger = (current_limit > 0);
+                       ret = axp288_charger_enable_charger(info,
+                                                       info->enable_charger);
+                       if (ret < 0)
+                               dev_err(&info->pdev->dev,
+                                       "cannot enable charger (%d)", ret);
+               }
+       }
+
+       if (changed)
+               info->health = axp288_get_charger_health(info);
+
+       mutex_unlock(&info->lock);
+
+       if (changed)
+               power_supply_changed(info->psy_usb);
+}
+
+static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
+                                         unsigned long event, void *param)
+{
+       struct axp288_chrg_info *info =
+           container_of(nb, struct axp288_chrg_info, cable.nb);
+
+       schedule_work(&info->cable.work);
+
+       return NOTIFY_OK;
+}
+
+static void axp288_charger_otg_evt_worker(struct work_struct *work)
+{
+       struct axp288_chrg_info *info =
+           container_of(work, struct axp288_chrg_info, otg.work);
+       int ret;
+
+       /* Disable VBUS path before enabling the 5V boost */
+       ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "vbus path disable failed\n");
+}
+
+static int axp288_charger_handle_otg_evt(struct notifier_block *nb,
+                                  unsigned long event, void *param)
+{
+       struct axp288_chrg_info *info =
+           container_of(nb, struct axp288_chrg_info, otg.id_nb);
+       struct extcon_dev *edev = info->otg.cable;
+       int usb_host = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+
+       dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
+                               usb_host ? "attached" : "detached");
+
+       /*
+        * Set usb_id_short flag to avoid running charger detection logic
+        * in case usb host.
+        */
+       info->otg.id_short = usb_host;
+       schedule_work(&info->otg.work);
+
+       return NOTIFY_OK;
+}
+
+static void charger_init_hw_regs(struct axp288_chrg_info *info)
+{
+       int ret, cc, cv;
+       unsigned int val;
+
+       /* Program temperature thresholds */
+       ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                                       AXP20X_V_LTF_CHRG, ret);
+
+       ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                                       AXP20X_V_HTF_CHRG, ret);
+
+       /* Do not turn-off charger o/p after charge cycle ends */
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CHRG_CTRL2,
+                               CNTL2_CHG_OUT_TURNON, 1);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_CHRG_CTRL2, ret);
+
+       /* Enable interrupts */
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_IRQ2_EN,
+                               BAT_IRQ_CFG_BAT_MASK, 1);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_IRQ2_EN, ret);
+
+       ret = regmap_update_bits(info->regmap, AXP20X_IRQ3_EN,
+                               TEMP_IRQ_CFG_MASK, 1);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_IRQ3_EN, ret);
+
+       /* Setup ending condition for charging to be 10% of I(chrg) */
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_ITERM_20P, 0);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_CHRG_CTRL1, ret);
+
+       /* Disable OCV-SOC curve calibration */
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CC_CTRL,
+                               FG_CNTL_OCV_ADJ_EN, 0);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_CC_CTRL, ret);
+
+       /* Init charging current and voltage */
+       info->max_cc = info->pdata->max_cc;
+       info->max_cv = info->pdata->max_cv;
+
+       /* Read current charge voltage and current limit */
+       ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
+       if (ret < 0) {
+               /* Assume default if cannot read */
+               info->cc = info->pdata->def_cc;
+               info->cv = info->pdata->def_cv;
+       } else {
+               /* Determine charge voltage */
+               cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
+               switch (cv) {
+               case CHRG_CCCV_CV_4100MV:
+                       info->cv = CV_4100MV;
+                       break;
+               case CHRG_CCCV_CV_4150MV:
+                       info->cv = CV_4150MV;
+                       break;
+               case CHRG_CCCV_CV_4200MV:
+                       info->cv = CV_4200MV;
+                       break;
+               case CHRG_CCCV_CV_4350MV:
+                       info->cv = CV_4350MV;
+                       break;
+               default:
+                       info->cv = INT_MAX;
+                       break;
+               }
+
+               /* Determine charge current limit */
+               cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
+               cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
+               info->cc = cc;
+
+               /* Program default charging voltage and current */
+               cc = min(info->pdata->def_cc, info->max_cc);
+               cv = min(info->pdata->def_cv, info->max_cv);
+
+               ret = axp288_charger_set_cc(info, cc);
+               if (ret < 0)
+                       dev_warn(&info->pdev->dev,
+                                       "error(%d) in setting CC\n", ret);
+
+               ret = axp288_charger_set_cv(info, cv);
+               if (ret < 0)
+                       dev_warn(&info->pdev->dev,
+                                       "error(%d) in setting CV\n", ret);
+       }
+}
+
+static int axp288_charger_probe(struct platform_device *pdev)
+{
+       int ret, i, pirq;
+       struct axp288_chrg_info *info;
+       struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+       struct power_supply_config charger_cfg = {};
+
+       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->pdata = pdev->dev.platform_data;
+
+       if (!info->pdata) {
+               /* Try ACPI provided pdata via device properties */
+               if (!device_property_present(&pdev->dev,
+                                               "axp288_charger_data\n"))
+                       dev_err(&pdev->dev, "failed to get platform data\n");
+               return -ENODEV;
+       }
+
+       info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
+       if (info->cable.edev == NULL) {
+               dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
+                       AXP288_EXTCON_DEV_NAME);
+               return -EPROBE_DEFER;
+       }
+
+       /* Register for extcon notification */
+       INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
+       info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
+       ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
+                                       &info->cable.nb);
+       if (ret) {
+               dev_err(&info->pdev->dev,
+                       "failed to register extcon notifier for SDP %d\n", ret);
+               return ret;
+       }
+
+       ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
+                                       &info->cable.nb);
+       if (ret) {
+               dev_err(&info->pdev->dev,
+                       "failed to register extcon notifier for CDP %d\n", ret);
+               extcon_unregister_notifier(info->cable.edev,
+                               EXTCON_CHG_USB_SDP, &info->cable.nb);
+               return ret;
+       }
+
+       ret = extcon_register_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
+                                       &info->cable.nb);
+       if (ret) {
+               dev_err(&info->pdev->dev,
+                       "failed to register extcon notifier for DCP %d\n", ret);
+               extcon_unregister_notifier(info->cable.edev,
+                               EXTCON_CHG_USB_SDP, &info->cable.nb);
+               extcon_unregister_notifier(info->cable.edev,
+                               EXTCON_CHG_USB_CDP, &info->cable.nb);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, info);
+       mutex_init(&info->lock);
+
+       /* Register with power supply class */
+       charger_cfg.drv_data = info;
+       info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc,
+                                               &charger_cfg);
+       if (IS_ERR(info->psy_usb)) {
+               dev_err(&pdev->dev, "failed to register power supply charger\n");
+               ret = PTR_ERR(info->psy_usb);
+               goto psy_reg_failed;
+       }
+
+       /* Register for OTG notification */
+       INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
+       info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
+       ret = extcon_register_notifier(info->otg.cable, EXTCON_USB_HOST,
+                                      &info->otg.id_nb);
+       if (ret)
+               dev_warn(&pdev->dev, "failed to register otg notifier\n");
+
+       if (info->otg.cable)
+               info->otg.id_short = extcon_get_cable_state_(
+                                       info->otg.cable, EXTCON_USB_HOST);
+
+       /* Register charger interrupts */
+       for (i = 0; i < CHRG_INTR_END; 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,
+                               "failed to get virtual interrupt=%d\n", pirq);
+                       ret = info->irq[i];
+                       goto intr_reg_failed;
+               }
+               ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
+                                       NULL, axp288_charger_irq_thread_handler,
+                                       IRQF_ONESHOT, info->pdev->name, info);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to request interrupt=%d\n",
+                                                               info->irq[i]);
+                       goto intr_reg_failed;
+               }
+       }
+
+       charger_init_hw_regs(info);
+
+       return 0;
+
+intr_reg_failed:
+       if (info->otg.cable)
+               extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST,
+                                       &info->otg.id_nb);
+       power_supply_unregister(info->psy_usb);
+psy_reg_failed:
+       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
+                                       &info->cable.nb);
+       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
+                                       &info->cable.nb);
+       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
+                                       &info->cable.nb);
+       return ret;
+}
+
+static int axp288_charger_remove(struct platform_device *pdev)
+{
+       struct axp288_chrg_info *info =  dev_get_drvdata(&pdev->dev);
+
+       if (info->otg.cable)
+               extcon_unregister_notifier(info->otg.cable, EXTCON_USB_HOST,
+                                       &info->otg.id_nb);
+
+       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_SDP,
+                                       &info->cable.nb);
+       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_CDP,
+                                       &info->cable.nb);
+       extcon_unregister_notifier(info->cable.edev, EXTCON_CHG_USB_DCP,
+                                       &info->cable.nb);
+       power_supply_unregister(info->psy_usb);
+
+       return 0;
+}
+
+static struct platform_driver axp288_charger_driver = {
+       .probe = axp288_charger_probe,
+       .remove = axp288_charger_remove,
+       .driver = {
+               .name = "axp288_charger",
+       },
+};
+
+module_platform_driver(axp288_charger_driver);
+
+MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
+MODULE_DESCRIPTION("X-power AXP288 Charger Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c
new file mode 100644 (file)
index 0000000..50c0110
--- /dev/null
@@ -0,0 +1,1155 @@
+/*
+ * 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 = power_supply_get_drvdata(ps);
+       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 = power_supply_get_drvdata(ps);
+       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 = power_supply_get_drvdata(psy);
+
+       power_supply_changed(info->bat);
+}
+
+static const struct power_supply_desc fuel_gauge_desc = {
+       .name                   = DEV_NAME,
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = fuel_gauge_props,
+       .num_properties         = ARRAY_SIZE(fuel_gauge_props),
+       .get_property           = fuel_gauge_get_property,
+       .set_property           = fuel_gauge_set_property,
+       .property_is_writeable  = fuel_gauge_property_is_writeable,
+       .external_power_changed = fuel_gauge_external_power_changed,
+};
+
+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 = 0;
+       struct axp288_fg_info *info;
+       struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+       struct power_supply_config psy_cfg = {};
+
+       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);
+
+       psy_cfg.drv_data = info;
+       info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
+       if (IS_ERR(info->bat)) {
+               ret = PTR_ERR(info->bat);
+               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 const 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("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
+MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>");
+MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/bq2415x_charger.c b/drivers/power/supply/bq2415x_charger.c
new file mode 100644 (file)
index 0000000..73e2f0b
--- /dev/null
@@ -0,0 +1,1815 @@
+/*
+ * bq2415x charger driver
+ *
+ * Copyright (C) 2011-2013  Pali Rohár <pali.rohar@gmail.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * Datasheets:
+ * http://www.ti.com/product/bq24150
+ * http://www.ti.com/product/bq24150a
+ * http://www.ti.com/product/bq24152
+ * http://www.ti.com/product/bq24153
+ * http://www.ti.com/product/bq24153a
+ * http://www.ti.com/product/bq24155
+ * http://www.ti.com/product/bq24157s
+ * http://www.ti.com/product/bq24158
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+
+#include <linux/power/bq2415x_charger.h>
+
+/* timeout for resetting chip timer */
+#define BQ2415X_TIMER_TIMEOUT          10
+
+#define BQ2415X_REG_STATUS             0x00
+#define BQ2415X_REG_CONTROL            0x01
+#define BQ2415X_REG_VOLTAGE            0x02
+#define BQ2415X_REG_VENDER             0x03
+#define BQ2415X_REG_CURRENT            0x04
+
+/* reset state for all registers */
+#define BQ2415X_RESET_STATUS           BIT(6)
+#define BQ2415X_RESET_CONTROL          (BIT(4)|BIT(5))
+#define BQ2415X_RESET_VOLTAGE          (BIT(1)|BIT(3))
+#define BQ2415X_RESET_CURRENT          (BIT(0)|BIT(3)|BIT(7))
+
+/* status register */
+#define BQ2415X_BIT_TMR_RST            7
+#define BQ2415X_BIT_OTG                        7
+#define BQ2415X_BIT_EN_STAT            6
+#define BQ2415X_MASK_STAT              (BIT(4)|BIT(5))
+#define BQ2415X_SHIFT_STAT             4
+#define BQ2415X_BIT_BOOST              3
+#define BQ2415X_MASK_FAULT             (BIT(0)|BIT(1)|BIT(2))
+#define BQ2415X_SHIFT_FAULT            0
+
+/* control register */
+#define BQ2415X_MASK_LIMIT             (BIT(6)|BIT(7))
+#define BQ2415X_SHIFT_LIMIT            6
+#define BQ2415X_MASK_VLOWV             (BIT(4)|BIT(5))
+#define BQ2415X_SHIFT_VLOWV            4
+#define BQ2415X_BIT_TE                 3
+#define BQ2415X_BIT_CE                 2
+#define BQ2415X_BIT_HZ_MODE            1
+#define BQ2415X_BIT_OPA_MODE           0
+
+/* voltage register */
+#define BQ2415X_MASK_VO                (BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
+#define BQ2415X_SHIFT_VO               2
+#define BQ2415X_BIT_OTG_PL             1
+#define BQ2415X_BIT_OTG_EN             0
+
+/* vender register */
+#define BQ2415X_MASK_VENDER            (BIT(5)|BIT(6)|BIT(7))
+#define BQ2415X_SHIFT_VENDER           5
+#define BQ2415X_MASK_PN                        (BIT(3)|BIT(4))
+#define BQ2415X_SHIFT_PN               3
+#define BQ2415X_MASK_REVISION          (BIT(0)|BIT(1)|BIT(2))
+#define BQ2415X_SHIFT_REVISION         0
+
+/* current register */
+#define BQ2415X_MASK_RESET             BIT(7)
+#define BQ2415X_MASK_VI_CHRG           (BIT(4)|BIT(5)|BIT(6))
+#define BQ2415X_SHIFT_VI_CHRG          4
+/* N/A                                 BIT(3) */
+#define BQ2415X_MASK_VI_TERM           (BIT(0)|BIT(1)|BIT(2))
+#define BQ2415X_SHIFT_VI_TERM          0
+
+
+enum bq2415x_command {
+       BQ2415X_TIMER_RESET,
+       BQ2415X_OTG_STATUS,
+       BQ2415X_STAT_PIN_STATUS,
+       BQ2415X_STAT_PIN_ENABLE,
+       BQ2415X_STAT_PIN_DISABLE,
+       BQ2415X_CHARGE_STATUS,
+       BQ2415X_BOOST_STATUS,
+       BQ2415X_FAULT_STATUS,
+
+       BQ2415X_CHARGE_TERMINATION_STATUS,
+       BQ2415X_CHARGE_TERMINATION_ENABLE,
+       BQ2415X_CHARGE_TERMINATION_DISABLE,
+       BQ2415X_CHARGER_STATUS,
+       BQ2415X_CHARGER_ENABLE,
+       BQ2415X_CHARGER_DISABLE,
+       BQ2415X_HIGH_IMPEDANCE_STATUS,
+       BQ2415X_HIGH_IMPEDANCE_ENABLE,
+       BQ2415X_HIGH_IMPEDANCE_DISABLE,
+       BQ2415X_BOOST_MODE_STATUS,
+       BQ2415X_BOOST_MODE_ENABLE,
+       BQ2415X_BOOST_MODE_DISABLE,
+
+       BQ2415X_OTG_LEVEL,
+       BQ2415X_OTG_ACTIVATE_HIGH,
+       BQ2415X_OTG_ACTIVATE_LOW,
+       BQ2415X_OTG_PIN_STATUS,
+       BQ2415X_OTG_PIN_ENABLE,
+       BQ2415X_OTG_PIN_DISABLE,
+
+       BQ2415X_VENDER_CODE,
+       BQ2415X_PART_NUMBER,
+       BQ2415X_REVISION,
+};
+
+enum bq2415x_chip {
+       BQUNKNOWN,
+       BQ24150,
+       BQ24150A,
+       BQ24151,
+       BQ24151A,
+       BQ24152,
+       BQ24153,
+       BQ24153A,
+       BQ24155,
+       BQ24156,
+       BQ24156A,
+       BQ24157S,
+       BQ24158,
+};
+
+static char *bq2415x_chip_name[] = {
+       "unknown",
+       "bq24150",
+       "bq24150a",
+       "bq24151",
+       "bq24151a",
+       "bq24152",
+       "bq24153",
+       "bq24153a",
+       "bq24155",
+       "bq24156",
+       "bq24156a",
+       "bq24157s",
+       "bq24158",
+};
+
+struct bq2415x_device {
+       struct device *dev;
+       struct bq2415x_platform_data init_data;
+       struct power_supply *charger;
+       struct power_supply_desc charger_desc;
+       struct delayed_work work;
+       struct device_node *notify_node;
+       struct notifier_block nb;
+       enum bq2415x_mode reported_mode;/* mode reported by hook function */
+       enum bq2415x_mode mode;         /* currently configured mode */
+       enum bq2415x_chip chip;
+       const char *timer_error;
+       char *model;
+       char *name;
+       int autotimer;  /* 1 - if driver automatically reset timer, 0 - not */
+       int automode;   /* 1 - enabled, 0 - disabled; -1 - not supported */
+       int id;
+};
+
+/* each registered chip must have unique id */
+static DEFINE_IDR(bq2415x_id);
+
+static DEFINE_MUTEX(bq2415x_id_mutex);
+static DEFINE_MUTEX(bq2415x_timer_mutex);
+static DEFINE_MUTEX(bq2415x_i2c_mutex);
+
+/**** i2c read functions ****/
+
+/* read value from register */
+static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg)
+{
+       struct i2c_client *client = to_i2c_client(bq->dev);
+       struct i2c_msg msg[2];
+       u8 val;
+       int ret;
+
+       if (!client->adapter)
+               return -ENODEV;
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].buf = &reg;
+       msg[0].len = sizeof(reg);
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].buf = &val;
+       msg[1].len = sizeof(val);
+
+       mutex_lock(&bq2415x_i2c_mutex);
+       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+       mutex_unlock(&bq2415x_i2c_mutex);
+
+       if (ret < 0)
+               return ret;
+
+       return val;
+}
+
+/* read value from register, apply mask and right shift it */
+static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg,
+                                u8 mask, u8 shift)
+{
+       int ret;
+
+       if (shift > 8)
+               return -EINVAL;
+
+       ret = bq2415x_i2c_read(bq, reg);
+       if (ret < 0)
+               return ret;
+       return (ret & mask) >> shift;
+}
+
+/* read value from register and return one specified bit */
+static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit)
+{
+       if (bit > 8)
+               return -EINVAL;
+       return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit);
+}
+
+/**** i2c write functions ****/
+
+/* write value to register */
+static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val)
+{
+       struct i2c_client *client = to_i2c_client(bq->dev);
+       struct i2c_msg msg[1];
+       u8 data[2];
+       int ret;
+
+       data[0] = reg;
+       data[1] = val;
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].buf = data;
+       msg[0].len = ARRAY_SIZE(data);
+
+       mutex_lock(&bq2415x_i2c_mutex);
+       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+       mutex_unlock(&bq2415x_i2c_mutex);
+
+       /* i2c_transfer returns number of messages transferred */
+       if (ret < 0)
+               return ret;
+       else if (ret != 1)
+               return -EIO;
+
+       return 0;
+}
+
+/* read value from register, change it with mask left shifted and write back */
+static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val,
+                                 u8 mask, u8 shift)
+{
+       int ret;
+
+       if (shift > 8)
+               return -EINVAL;
+
+       ret = bq2415x_i2c_read(bq, reg);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~mask;
+       ret |= val << shift;
+
+       return bq2415x_i2c_write(bq, reg, ret);
+}
+
+/* change only one bit in register */
+static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg,
+                                bool val, u8 bit)
+{
+       if (bit > 8)
+               return -EINVAL;
+       return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit);
+}
+
+/**** global functions ****/
+
+/* exec command function */
+static int bq2415x_exec_command(struct bq2415x_device *bq,
+                               enum bq2415x_command command)
+{
+       int ret;
+
+       switch (command) {
+       case BQ2415X_TIMER_RESET:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS,
+                               1, BQ2415X_BIT_TMR_RST);
+       case BQ2415X_OTG_STATUS:
+               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
+                               BQ2415X_BIT_OTG);
+       case BQ2415X_STAT_PIN_STATUS:
+               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
+                               BQ2415X_BIT_EN_STAT);
+       case BQ2415X_STAT_PIN_ENABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 1,
+                               BQ2415X_BIT_EN_STAT);
+       case BQ2415X_STAT_PIN_DISABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 0,
+                               BQ2415X_BIT_EN_STAT);
+       case BQ2415X_CHARGE_STATUS:
+               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
+                               BQ2415X_MASK_STAT, BQ2415X_SHIFT_STAT);
+       case BQ2415X_BOOST_STATUS:
+               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
+                               BQ2415X_BIT_BOOST);
+       case BQ2415X_FAULT_STATUS:
+               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
+                       BQ2415X_MASK_FAULT, BQ2415X_SHIFT_FAULT);
+
+       case BQ2415X_CHARGE_TERMINATION_STATUS:
+               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
+                               BQ2415X_BIT_TE);
+       case BQ2415X_CHARGE_TERMINATION_ENABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+                               1, BQ2415X_BIT_TE);
+       case BQ2415X_CHARGE_TERMINATION_DISABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+                               0, BQ2415X_BIT_TE);
+       case BQ2415X_CHARGER_STATUS:
+               ret = bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
+                       BQ2415X_BIT_CE);
+               if (ret < 0)
+                       return ret;
+               return ret > 0 ? 0 : 1;
+       case BQ2415X_CHARGER_ENABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+                               0, BQ2415X_BIT_CE);
+       case BQ2415X_CHARGER_DISABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+                               1, BQ2415X_BIT_CE);
+       case BQ2415X_HIGH_IMPEDANCE_STATUS:
+               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
+                               BQ2415X_BIT_HZ_MODE);
+       case BQ2415X_HIGH_IMPEDANCE_ENABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+                               1, BQ2415X_BIT_HZ_MODE);
+       case BQ2415X_HIGH_IMPEDANCE_DISABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+                               0, BQ2415X_BIT_HZ_MODE);
+       case BQ2415X_BOOST_MODE_STATUS:
+               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
+                               BQ2415X_BIT_OPA_MODE);
+       case BQ2415X_BOOST_MODE_ENABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+                               1, BQ2415X_BIT_OPA_MODE);
+       case BQ2415X_BOOST_MODE_DISABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+                               0, BQ2415X_BIT_OPA_MODE);
+
+       case BQ2415X_OTG_LEVEL:
+               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
+                               BQ2415X_BIT_OTG_PL);
+       case BQ2415X_OTG_ACTIVATE_HIGH:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
+                               1, BQ2415X_BIT_OTG_PL);
+       case BQ2415X_OTG_ACTIVATE_LOW:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
+                               0, BQ2415X_BIT_OTG_PL);
+       case BQ2415X_OTG_PIN_STATUS:
+               return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
+                               BQ2415X_BIT_OTG_EN);
+       case BQ2415X_OTG_PIN_ENABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
+                               1, BQ2415X_BIT_OTG_EN);
+       case BQ2415X_OTG_PIN_DISABLE:
+               return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
+                               0, BQ2415X_BIT_OTG_EN);
+
+       case BQ2415X_VENDER_CODE:
+               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
+                       BQ2415X_MASK_VENDER, BQ2415X_SHIFT_VENDER);
+       case BQ2415X_PART_NUMBER:
+               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
+                               BQ2415X_MASK_PN, BQ2415X_SHIFT_PN);
+       case BQ2415X_REVISION:
+               return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
+                       BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION);
+       }
+       return -EINVAL;
+}
+
+/* detect chip type */
+static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq)
+{
+       struct i2c_client *client = to_i2c_client(bq->dev);
+       int ret = bq2415x_exec_command(bq, BQ2415X_PART_NUMBER);
+
+       if (ret < 0)
+               return ret;
+
+       switch (client->addr) {
+       case 0x6b:
+               switch (ret) {
+               case 0:
+                       if (bq->chip == BQ24151A)
+                               return bq->chip;
+                       return BQ24151;
+               case 1:
+                       if (bq->chip == BQ24150A ||
+                               bq->chip == BQ24152 ||
+                               bq->chip == BQ24155)
+                               return bq->chip;
+                       return BQ24150;
+               case 2:
+                       if (bq->chip == BQ24153A)
+                               return bq->chip;
+                       return BQ24153;
+               default:
+                       return BQUNKNOWN;
+               }
+               break;
+
+       case 0x6a:
+               switch (ret) {
+               case 0:
+                       if (bq->chip == BQ24156A)
+                               return bq->chip;
+                       return BQ24156;
+               case 2:
+                       if (bq->chip == BQ24157S)
+                               return bq->chip;
+                       return BQ24158;
+               default:
+                       return BQUNKNOWN;
+               }
+               break;
+       }
+
+       return BQUNKNOWN;
+}
+
+/* detect chip revision */
+static int bq2415x_detect_revision(struct bq2415x_device *bq)
+{
+       int ret = bq2415x_exec_command(bq, BQ2415X_REVISION);
+       int chip = bq2415x_detect_chip(bq);
+
+       if (ret < 0 || chip < 0)
+               return -1;
+
+       switch (chip) {
+       case BQ24150:
+       case BQ24150A:
+       case BQ24151:
+       case BQ24151A:
+       case BQ24152:
+               if (ret >= 0 && ret <= 3)
+                       return ret;
+               return -1;
+       case BQ24153:
+       case BQ24153A:
+       case BQ24156:
+       case BQ24156A:
+       case BQ24157S:
+       case BQ24158:
+               if (ret == 3)
+                       return 0;
+               else if (ret == 1)
+                       return 1;
+               return -1;
+       case BQ24155:
+               if (ret == 3)
+                       return 3;
+               return -1;
+       case BQUNKNOWN:
+               return -1;
+       }
+
+       return -1;
+}
+
+/* return chip vender code */
+static int bq2415x_get_vender_code(struct bq2415x_device *bq)
+{
+       int ret;
+
+       ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE);
+       if (ret < 0)
+               return 0;
+
+       /* convert to binary */
+       return (ret & 0x1) +
+              ((ret >> 1) & 0x1) * 10 +
+              ((ret >> 2) & 0x1) * 100;
+}
+
+/* reset all chip registers to default state */
+static void bq2415x_reset_chip(struct bq2415x_device *bq)
+{
+       bq2415x_i2c_write(bq, BQ2415X_REG_CURRENT, BQ2415X_RESET_CURRENT);
+       bq2415x_i2c_write(bq, BQ2415X_REG_VOLTAGE, BQ2415X_RESET_VOLTAGE);
+       bq2415x_i2c_write(bq, BQ2415X_REG_CONTROL, BQ2415X_RESET_CONTROL);
+       bq2415x_i2c_write(bq, BQ2415X_REG_STATUS, BQ2415X_RESET_STATUS);
+       bq->timer_error = NULL;
+}
+
+/**** properties functions ****/
+
+/* set current limit in mA */
+static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA)
+{
+       int val;
+
+       if (mA <= 100)
+               val = 0;
+       else if (mA <= 500)
+               val = 1;
+       else if (mA <= 800)
+               val = 2;
+       else
+               val = 3;
+
+       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
+                       BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
+}
+
+/* get current limit in mA */
+static int bq2415x_get_current_limit(struct bq2415x_device *bq)
+{
+       int ret;
+
+       ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
+                       BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
+       if (ret < 0)
+               return ret;
+       else if (ret == 0)
+               return 100;
+       else if (ret == 1)
+               return 500;
+       else if (ret == 2)
+               return 800;
+       else if (ret == 3)
+               return 1800;
+       return -EINVAL;
+}
+
+/* set weak battery voltage in mV */
+static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV)
+{
+       int val;
+
+       /* round to 100mV */
+       if (mV <= 3400 + 50)
+               val = 0;
+       else if (mV <= 3500 + 50)
+               val = 1;
+       else if (mV <= 3600 + 50)
+               val = 2;
+       else
+               val = 3;
+
+       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
+                       BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
+}
+
+/* get weak battery voltage in mV */
+static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq)
+{
+       int ret;
+
+       ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
+                       BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
+       if (ret < 0)
+               return ret;
+       return 100 * (34 + ret);
+}
+
+/* set battery regulation voltage in mV */
+static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq,
+                                                 int mV)
+{
+       int val = (mV/10 - 350) / 2;
+
+       /*
+        * According to datasheet, maximum battery regulation voltage is
+        * 4440mV which is b101111 = 47.
+        */
+       if (val < 0)
+               val = 0;
+       else if (val > 47)
+               return -EINVAL;
+
+       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val,
+                       BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
+}
+
+/* get battery regulation voltage in mV */
+static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq)
+{
+       int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE,
+                       BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
+
+       if (ret < 0)
+               return ret;
+       return 10 * (350 + 2*ret);
+}
+
+/* set charge current in mA (platform data must provide resistor sense) */
+static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA)
+{
+       int val;
+
+       if (bq->init_data.resistor_sense <= 0)
+               return -EINVAL;
+
+       val = (mA * bq->init_data.resistor_sense - 37400) / 6800;
+       if (val < 0)
+               val = 0;
+       else if (val > 7)
+               val = 7;
+
+       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
+                       BQ2415X_MASK_VI_CHRG | BQ2415X_MASK_RESET,
+                       BQ2415X_SHIFT_VI_CHRG);
+}
+
+/* get charge current in mA (platform data must provide resistor sense) */
+static int bq2415x_get_charge_current(struct bq2415x_device *bq)
+{
+       int ret;
+
+       if (bq->init_data.resistor_sense <= 0)
+               return -EINVAL;
+
+       ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
+                       BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG);
+       if (ret < 0)
+               return ret;
+       return (37400 + 6800*ret) / bq->init_data.resistor_sense;
+}
+
+/* set termination current in mA (platform data must provide resistor sense) */
+static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA)
+{
+       int val;
+
+       if (bq->init_data.resistor_sense <= 0)
+               return -EINVAL;
+
+       val = (mA * bq->init_data.resistor_sense - 3400) / 3400;
+       if (val < 0)
+               val = 0;
+       else if (val > 7)
+               val = 7;
+
+       return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
+                       BQ2415X_MASK_VI_TERM | BQ2415X_MASK_RESET,
+                       BQ2415X_SHIFT_VI_TERM);
+}
+
+/* get termination current in mA (platform data must provide resistor sense) */
+static int bq2415x_get_termination_current(struct bq2415x_device *bq)
+{
+       int ret;
+
+       if (bq->init_data.resistor_sense <= 0)
+               return -EINVAL;
+
+       ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
+                       BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM);
+       if (ret < 0)
+               return ret;
+       return (3400 + 3400*ret) / bq->init_data.resistor_sense;
+}
+
+/* set default value of property */
+#define bq2415x_set_default_value(bq, prop) \
+       do { \
+               int ret = 0; \
+               if (bq->init_data.prop != -1) \
+                       ret = bq2415x_set_##prop(bq, bq->init_data.prop); \
+               if (ret < 0) \
+                       return ret; \
+       } while (0)
+
+/* set default values of all properties */
+static int bq2415x_set_defaults(struct bq2415x_device *bq)
+{
+       bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
+       bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
+       bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_DISABLE);
+
+       bq2415x_set_default_value(bq, current_limit);
+       bq2415x_set_default_value(bq, weak_battery_voltage);
+       bq2415x_set_default_value(bq, battery_regulation_voltage);
+
+       if (bq->init_data.resistor_sense > 0) {
+               bq2415x_set_default_value(bq, charge_current);
+               bq2415x_set_default_value(bq, termination_current);
+               bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE);
+       }
+
+       bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
+       return 0;
+}
+
+/**** charger mode functions ****/
+
+/* set charger mode */
+static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
+{
+       int ret = 0;
+       int charger = 0;
+       int boost = 0;
+
+       if (mode == BQ2415X_MODE_BOOST)
+               boost = 1;
+       else if (mode != BQ2415X_MODE_OFF)
+               charger = 1;
+
+       if (!charger)
+               ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
+
+       if (!boost)
+               ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
+
+       if (ret < 0)
+               return ret;
+
+       switch (mode) {
+       case BQ2415X_MODE_OFF:
+               dev_dbg(bq->dev, "changing mode to: Offline\n");
+               ret = bq2415x_set_current_limit(bq, 100);
+               break;
+       case BQ2415X_MODE_NONE:
+               dev_dbg(bq->dev, "changing mode to: N/A\n");
+               ret = bq2415x_set_current_limit(bq, 100);
+               break;
+       case BQ2415X_MODE_HOST_CHARGER:
+               dev_dbg(bq->dev, "changing mode to: Host/HUB charger\n");
+               ret = bq2415x_set_current_limit(bq, 500);
+               break;
+       case BQ2415X_MODE_DEDICATED_CHARGER:
+               dev_dbg(bq->dev, "changing mode to: Dedicated charger\n");
+               ret = bq2415x_set_current_limit(bq, 1800);
+               break;
+       case BQ2415X_MODE_BOOST: /* Boost mode */
+               dev_dbg(bq->dev, "changing mode to: Boost\n");
+               ret = bq2415x_set_current_limit(bq, 100);
+               break;
+       }
+
+       if (ret < 0)
+               return ret;
+
+       if (charger)
+               ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
+       else if (boost)
+               ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_ENABLE);
+
+       if (ret < 0)
+               return ret;
+
+       bq2415x_set_default_value(bq, weak_battery_voltage);
+       bq2415x_set_default_value(bq, battery_regulation_voltage);
+
+       bq->mode = mode;
+       sysfs_notify(&bq->charger->dev.kobj, NULL, "mode");
+
+       return 0;
+
+}
+
+static bool bq2415x_update_reported_mode(struct bq2415x_device *bq, int mA)
+{
+       enum bq2415x_mode mode;
+
+       if (mA == 0)
+               mode = BQ2415X_MODE_OFF;
+       else if (mA < 500)
+               mode = BQ2415X_MODE_NONE;
+       else if (mA < 1800)
+               mode = BQ2415X_MODE_HOST_CHARGER;
+       else
+               mode = BQ2415X_MODE_DEDICATED_CHARGER;
+
+       if (bq->reported_mode == mode)
+               return false;
+
+       bq->reported_mode = mode;
+       return true;
+}
+
+static int bq2415x_notifier_call(struct notifier_block *nb,
+               unsigned long val, void *v)
+{
+       struct bq2415x_device *bq =
+               container_of(nb, struct bq2415x_device, nb);
+       struct power_supply *psy = v;
+       union power_supply_propval prop;
+       int ret;
+
+       if (val != PSY_EVENT_PROP_CHANGED)
+               return NOTIFY_OK;
+
+       /* Ignore event if it was not send by notify_node/notify_device */
+       if (bq->notify_node) {
+               if (!psy->dev.parent ||
+                   psy->dev.parent->of_node != bq->notify_node)
+                       return NOTIFY_OK;
+       } else if (bq->init_data.notify_device) {
+               if (strcmp(psy->desc->name, bq->init_data.notify_device) != 0)
+                       return NOTIFY_OK;
+       }
+
+       dev_dbg(bq->dev, "notifier call was called\n");
+
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+                       &prop);
+       if (ret != 0)
+               return NOTIFY_OK;
+
+       if (!bq2415x_update_reported_mode(bq, prop.intval))
+               return NOTIFY_OK;
+
+       /* if automode is not enabled do not tell about reported_mode */
+       if (bq->automode < 1)
+               return NOTIFY_OK;
+
+       schedule_delayed_work(&bq->work, 0);
+
+       return NOTIFY_OK;
+}
+
+/**** timer functions ****/
+
+/* enable/disable auto resetting chip timer */
+static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state)
+{
+       mutex_lock(&bq2415x_timer_mutex);
+
+       if (bq->autotimer == state) {
+               mutex_unlock(&bq2415x_timer_mutex);
+               return;
+       }
+
+       bq->autotimer = state;
+
+       if (state) {
+               schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
+               bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
+               bq->timer_error = NULL;
+       } else {
+               cancel_delayed_work_sync(&bq->work);
+       }
+
+       mutex_unlock(&bq2415x_timer_mutex);
+}
+
+/* called by bq2415x_timer_work on timer error */
+static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
+{
+       bq->timer_error = msg;
+       sysfs_notify(&bq->charger->dev.kobj, NULL, "timer");
+       dev_err(bq->dev, "%s\n", msg);
+       if (bq->automode > 0)
+               bq->automode = 0;
+       bq2415x_set_mode(bq, BQ2415X_MODE_OFF);
+       bq2415x_set_autotimer(bq, 0);
+}
+
+/* delayed work function for auto resetting chip timer */
+static void bq2415x_timer_work(struct work_struct *work)
+{
+       struct bq2415x_device *bq = container_of(work, struct bq2415x_device,
+                                                work.work);
+       int ret;
+       int error;
+       int boost;
+
+       if (bq->automode > 0 && (bq->reported_mode != bq->mode)) {
+               sysfs_notify(&bq->charger->dev.kobj, NULL, "reported_mode");
+               bq2415x_set_mode(bq, bq->reported_mode);
+       }
+
+       if (!bq->autotimer)
+               return;
+
+       ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
+       if (ret < 0) {
+               bq2415x_timer_error(bq, "Resetting timer failed");
+               return;
+       }
+
+       boost = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_STATUS);
+       if (boost < 0) {
+               bq2415x_timer_error(bq, "Unknown error");
+               return;
+       }
+
+       error = bq2415x_exec_command(bq, BQ2415X_FAULT_STATUS);
+       if (error < 0) {
+               bq2415x_timer_error(bq, "Unknown error");
+               return;
+       }
+
+       if (boost) {
+               switch (error) {
+               /* Non fatal errors, chip is OK */
+               case 0: /* No error */
+                       break;
+               case 6: /* Timer expired */
+                       dev_err(bq->dev, "Timer expired\n");
+                       break;
+               case 3: /* Battery voltage too low */
+                       dev_err(bq->dev, "Battery voltage to low\n");
+                       break;
+
+               /* Fatal errors, disable and reset chip */
+               case 1: /* Overvoltage protection (chip fried) */
+                       bq2415x_timer_error(bq,
+                               "Overvoltage protection (chip fried)");
+                       return;
+               case 2: /* Overload */
+                       bq2415x_timer_error(bq, "Overload");
+                       return;
+               case 4: /* Battery overvoltage protection */
+                       bq2415x_timer_error(bq,
+                               "Battery overvoltage protection");
+                       return;
+               case 5: /* Thermal shutdown (too hot) */
+                       bq2415x_timer_error(bq,
+                                       "Thermal shutdown (too hot)");
+                       return;
+               case 7: /* N/A */
+                       bq2415x_timer_error(bq, "Unknown error");
+                       return;
+               }
+       } else {
+               switch (error) {
+               /* Non fatal errors, chip is OK */
+               case 0: /* No error */
+                       break;
+               case 2: /* Sleep mode */
+                       dev_err(bq->dev, "Sleep mode\n");
+                       break;
+               case 3: /* Poor input source */
+                       dev_err(bq->dev, "Poor input source\n");
+                       break;
+               case 6: /* Timer expired */
+                       dev_err(bq->dev, "Timer expired\n");
+                       break;
+               case 7: /* No battery */
+                       dev_err(bq->dev, "No battery\n");
+                       break;
+
+               /* Fatal errors, disable and reset chip */
+               case 1: /* Overvoltage protection (chip fried) */
+                       bq2415x_timer_error(bq,
+                               "Overvoltage protection (chip fried)");
+                       return;
+               case 4: /* Battery overvoltage protection */
+                       bq2415x_timer_error(bq,
+                               "Battery overvoltage protection");
+                       return;
+               case 5: /* Thermal shutdown (too hot) */
+                       bq2415x_timer_error(bq,
+                               "Thermal shutdown (too hot)");
+                       return;
+               }
+       }
+
+       schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
+}
+
+/**** power supply interface code ****/
+
+static enum power_supply_property bq2415x_power_supply_props[] = {
+       /* TODO: maybe add more power supply properties */
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static int bq2415x_power_supply_get_property(struct power_supply *psy,
+                                            enum power_supply_property psp,
+                                            union power_supply_propval *val)
+{
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS);
+               if (ret < 0)
+                       return ret;
+               else if (ret == 0) /* Ready */
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else if (ret == 1) /* Charge in progress */
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (ret == 2) /* Charge done */
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = bq->model;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int bq2415x_power_supply_init(struct bq2415x_device *bq)
+{
+       int ret;
+       int chip;
+       char revstr[8];
+       struct power_supply_config psy_cfg = { .drv_data = bq, };
+
+       bq->charger_desc.name = bq->name;
+       bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
+       bq->charger_desc.properties = bq2415x_power_supply_props;
+       bq->charger_desc.num_properties =
+                       ARRAY_SIZE(bq2415x_power_supply_props);
+       bq->charger_desc.get_property = bq2415x_power_supply_get_property;
+
+       ret = bq2415x_detect_chip(bq);
+       if (ret < 0)
+               chip = BQUNKNOWN;
+       else
+               chip = ret;
+
+       ret = bq2415x_detect_revision(bq);
+       if (ret < 0)
+               strcpy(revstr, "unknown");
+       else
+               sprintf(revstr, "1.%d", ret);
+
+       bq->model = kasprintf(GFP_KERNEL,
+                               "chip %s, revision %s, vender code %.3d",
+                               bq2415x_chip_name[chip], revstr,
+                               bq2415x_get_vender_code(bq));
+       if (!bq->model) {
+               dev_err(bq->dev, "failed to allocate model name\n");
+               return -ENOMEM;
+       }
+
+       bq->charger = power_supply_register(bq->dev, &bq->charger_desc,
+                                           &psy_cfg);
+       if (IS_ERR(bq->charger)) {
+               kfree(bq->model);
+               return PTR_ERR(bq->charger);
+       }
+
+       return 0;
+}
+
+static void bq2415x_power_supply_exit(struct bq2415x_device *bq)
+{
+       bq->autotimer = 0;
+       if (bq->automode > 0)
+               bq->automode = 0;
+       cancel_delayed_work_sync(&bq->work);
+       power_supply_unregister(bq->charger);
+       kfree(bq->model);
+}
+
+/**** additional sysfs entries for power supply interface ****/
+
+/* show *_status entries */
+static ssize_t bq2415x_sysfs_show_status(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       enum bq2415x_command command;
+       int ret;
+
+       if (strcmp(attr->attr.name, "otg_status") == 0)
+               command = BQ2415X_OTG_STATUS;
+       else if (strcmp(attr->attr.name, "charge_status") == 0)
+               command = BQ2415X_CHARGE_STATUS;
+       else if (strcmp(attr->attr.name, "boost_status") == 0)
+               command = BQ2415X_BOOST_STATUS;
+       else if (strcmp(attr->attr.name, "fault_status") == 0)
+               command = BQ2415X_FAULT_STATUS;
+       else
+               return -EINVAL;
+
+       ret = bq2415x_exec_command(bq, command);
+       if (ret < 0)
+               return ret;
+       return sprintf(buf, "%d\n", ret);
+}
+
+/*
+ * set timer entry:
+ *    auto - enable auto mode
+ *    off - disable auto mode
+ *    (other values) - reset chip timer
+ */
+static ssize_t bq2415x_sysfs_set_timer(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf,
+                                      size_t count)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       if (strncmp(buf, "auto", 4) == 0)
+               bq2415x_set_autotimer(bq, 1);
+       else if (strncmp(buf, "off", 3) == 0)
+               bq2415x_set_autotimer(bq, 0);
+       else
+               ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
+
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+/* show timer entry (auto or off) */
+static ssize_t bq2415x_sysfs_show_timer(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+
+       if (bq->timer_error)
+               return sprintf(buf, "%s\n", bq->timer_error);
+
+       if (bq->autotimer)
+               return sprintf(buf, "auto\n");
+       return sprintf(buf, "off\n");
+}
+
+/*
+ * set mode entry:
+ *    auto - if automode is supported, enable it and set mode to reported
+ *    none - disable charger and boost mode
+ *    host - charging mode for host/hub chargers (current limit 500mA)
+ *    dedicated - charging mode for dedicated chargers (unlimited current limit)
+ *    boost - disable charger and enable boost mode
+ */
+static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf,
+                                     size_t count)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       enum bq2415x_mode mode;
+       int ret = 0;
+
+       if (strncmp(buf, "auto", 4) == 0) {
+               if (bq->automode < 0)
+                       return -EINVAL;
+               bq->automode = 1;
+               mode = bq->reported_mode;
+       } else if (strncmp(buf, "off", 3) == 0) {
+               if (bq->automode > 0)
+                       bq->automode = 0;
+               mode = BQ2415X_MODE_OFF;
+       } else if (strncmp(buf, "none", 4) == 0) {
+               if (bq->automode > 0)
+                       bq->automode = 0;
+               mode = BQ2415X_MODE_NONE;
+       } else if (strncmp(buf, "host", 4) == 0) {
+               if (bq->automode > 0)
+                       bq->automode = 0;
+               mode = BQ2415X_MODE_HOST_CHARGER;
+       } else if (strncmp(buf, "dedicated", 9) == 0) {
+               if (bq->automode > 0)
+                       bq->automode = 0;
+               mode = BQ2415X_MODE_DEDICATED_CHARGER;
+       } else if (strncmp(buf, "boost", 5) == 0) {
+               if (bq->automode > 0)
+                       bq->automode = 0;
+               mode = BQ2415X_MODE_BOOST;
+       } else if (strncmp(buf, "reset", 5) == 0) {
+               bq2415x_reset_chip(bq);
+               bq2415x_set_defaults(bq);
+               if (bq->automode <= 0)
+                       return count;
+               bq->automode = 1;
+               mode = bq->reported_mode;
+       } else {
+               return -EINVAL;
+       }
+
+       ret = bq2415x_set_mode(bq, mode);
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+/* show mode entry (auto, none, host, dedicated or boost) */
+static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       ssize_t ret = 0;
+
+       if (bq->automode > 0)
+               ret += sprintf(buf+ret, "auto (");
+
+       switch (bq->mode) {
+       case BQ2415X_MODE_OFF:
+               ret += sprintf(buf+ret, "off");
+               break;
+       case BQ2415X_MODE_NONE:
+               ret += sprintf(buf+ret, "none");
+               break;
+       case BQ2415X_MODE_HOST_CHARGER:
+               ret += sprintf(buf+ret, "host");
+               break;
+       case BQ2415X_MODE_DEDICATED_CHARGER:
+               ret += sprintf(buf+ret, "dedicated");
+               break;
+       case BQ2415X_MODE_BOOST:
+               ret += sprintf(buf+ret, "boost");
+               break;
+       }
+
+       if (bq->automode > 0)
+               ret += sprintf(buf+ret, ")");
+
+       ret += sprintf(buf+ret, "\n");
+       return ret;
+}
+
+/* show reported_mode entry (none, host, dedicated or boost) */
+static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
+                                               struct device_attribute *attr,
+                                               char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+
+       if (bq->automode < 0)
+               return -EINVAL;
+
+       switch (bq->reported_mode) {
+       case BQ2415X_MODE_OFF:
+               return sprintf(buf, "off\n");
+       case BQ2415X_MODE_NONE:
+               return sprintf(buf, "none\n");
+       case BQ2415X_MODE_HOST_CHARGER:
+               return sprintf(buf, "host\n");
+       case BQ2415X_MODE_DEDICATED_CHARGER:
+               return sprintf(buf, "dedicated\n");
+       case BQ2415X_MODE_BOOST:
+               return sprintf(buf, "boost\n");
+       }
+
+       return -EINVAL;
+}
+
+/* directly set raw value to chip register, format: 'register value' */
+static ssize_t bq2415x_sysfs_set_registers(struct device *dev,
+                                          struct device_attribute *attr,
+                                          const char *buf,
+                                          size_t count)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       ssize_t ret = 0;
+       unsigned int reg;
+       unsigned int val;
+
+       if (sscanf(buf, "%x %x", &reg, &val) != 2)
+               return -EINVAL;
+
+       if (reg > 4 || val > 255)
+               return -EINVAL;
+
+       ret = bq2415x_i2c_write(bq, reg, val);
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+/* print value of chip register, format: 'register=value' */
+static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq,
+                                      u8 reg,
+                                      char *buf)
+{
+       int ret = bq2415x_i2c_read(bq, reg);
+
+       if (ret < 0)
+               return sprintf(buf, "%#.2x=error %d\n", reg, ret);
+       return sprintf(buf, "%#.2x=%#.2x\n", reg, ret);
+}
+
+/* show all raw values of chip register, format per line: 'register=value' */
+static ssize_t bq2415x_sysfs_show_registers(struct device *dev,
+                                           struct device_attribute *attr,
+                                           char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       ssize_t ret = 0;
+
+       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret);
+       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CONTROL, buf+ret);
+       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VOLTAGE, buf+ret);
+       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VENDER, buf+ret);
+       ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CURRENT, buf+ret);
+       return ret;
+}
+
+/* set current and voltage limit entries (in mA or mV) */
+static ssize_t bq2415x_sysfs_set_limit(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf,
+                                      size_t count)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       long val;
+       int ret;
+
+       if (kstrtol(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       if (strcmp(attr->attr.name, "current_limit") == 0)
+               ret = bq2415x_set_current_limit(bq, val);
+       else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
+               ret = bq2415x_set_weak_battery_voltage(bq, val);
+       else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
+               ret = bq2415x_set_battery_regulation_voltage(bq, val);
+       else if (strcmp(attr->attr.name, "charge_current") == 0)
+               ret = bq2415x_set_charge_current(bq, val);
+       else if (strcmp(attr->attr.name, "termination_current") == 0)
+               ret = bq2415x_set_termination_current(bq, val);
+       else
+               return -EINVAL;
+
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+/* show current and voltage limit entries (in mA or mV) */
+static ssize_t bq2415x_sysfs_show_limit(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       int ret;
+
+       if (strcmp(attr->attr.name, "current_limit") == 0)
+               ret = bq2415x_get_current_limit(bq);
+       else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
+               ret = bq2415x_get_weak_battery_voltage(bq);
+       else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
+               ret = bq2415x_get_battery_regulation_voltage(bq);
+       else if (strcmp(attr->attr.name, "charge_current") == 0)
+               ret = bq2415x_get_charge_current(bq);
+       else if (strcmp(attr->attr.name, "termination_current") == 0)
+               ret = bq2415x_get_termination_current(bq);
+       else
+               return -EINVAL;
+
+       if (ret < 0)
+               return ret;
+       return sprintf(buf, "%d\n", ret);
+}
+
+/* set *_enable entries */
+static ssize_t bq2415x_sysfs_set_enable(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf,
+                                       size_t count)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       enum bq2415x_command command;
+       long val;
+       int ret;
+
+       if (kstrtol(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
+               command = val ? BQ2415X_CHARGE_TERMINATION_ENABLE :
+                       BQ2415X_CHARGE_TERMINATION_DISABLE;
+       else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
+               command = val ? BQ2415X_HIGH_IMPEDANCE_ENABLE :
+                       BQ2415X_HIGH_IMPEDANCE_DISABLE;
+       else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
+               command = val ? BQ2415X_OTG_PIN_ENABLE :
+                       BQ2415X_OTG_PIN_DISABLE;
+       else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
+               command = val ? BQ2415X_STAT_PIN_ENABLE :
+                       BQ2415X_STAT_PIN_DISABLE;
+       else
+               return -EINVAL;
+
+       ret = bq2415x_exec_command(bq, command);
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+/* show *_enable entries */
+static ssize_t bq2415x_sysfs_show_enable(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq2415x_device *bq = power_supply_get_drvdata(psy);
+       enum bq2415x_command command;
+       int ret;
+
+       if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
+               command = BQ2415X_CHARGE_TERMINATION_STATUS;
+       else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
+               command = BQ2415X_HIGH_IMPEDANCE_STATUS;
+       else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
+               command = BQ2415X_OTG_PIN_STATUS;
+       else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
+               command = BQ2415X_STAT_PIN_STATUS;
+       else
+               return -EINVAL;
+
+       ret = bq2415x_exec_command(bq, command);
+       if (ret < 0)
+               return ret;
+       return sprintf(buf, "%d\n", ret);
+}
+
+static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+static DEVICE_ATTR(weak_battery_voltage, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+static DEVICE_ATTR(battery_regulation_voltage, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+static DEVICE_ATTR(charge_current, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+static DEVICE_ATTR(termination_current, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+
+static DEVICE_ATTR(charge_termination_enable, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
+static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
+static DEVICE_ATTR(otg_pin_enable, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
+static DEVICE_ATTR(stat_pin_enable, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
+
+static DEVICE_ATTR(reported_mode, S_IRUGO,
+               bq2415x_sysfs_show_reported_mode, NULL);
+static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_mode, bq2415x_sysfs_set_mode);
+static DEVICE_ATTR(timer, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_timer, bq2415x_sysfs_set_timer);
+
+static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO,
+               bq2415x_sysfs_show_registers, bq2415x_sysfs_set_registers);
+
+static DEVICE_ATTR(otg_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
+static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
+static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
+static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
+
+static struct attribute *bq2415x_sysfs_attributes[] = {
+       /*
+        * TODO: some (appropriate) of these attrs should be switched to
+        * use power supply class props.
+        */
+       &dev_attr_current_limit.attr,
+       &dev_attr_weak_battery_voltage.attr,
+       &dev_attr_battery_regulation_voltage.attr,
+       &dev_attr_charge_current.attr,
+       &dev_attr_termination_current.attr,
+
+       &dev_attr_charge_termination_enable.attr,
+       &dev_attr_high_impedance_enable.attr,
+       &dev_attr_otg_pin_enable.attr,
+       &dev_attr_stat_pin_enable.attr,
+
+       &dev_attr_reported_mode.attr,
+       &dev_attr_mode.attr,
+       &dev_attr_timer.attr,
+
+       &dev_attr_registers.attr,
+
+       &dev_attr_otg_status.attr,
+       &dev_attr_charge_status.attr,
+       &dev_attr_boost_status.attr,
+       &dev_attr_fault_status.attr,
+       NULL,
+};
+
+static const struct attribute_group bq2415x_sysfs_attr_group = {
+       .attrs = bq2415x_sysfs_attributes,
+};
+
+static int bq2415x_sysfs_init(struct bq2415x_device *bq)
+{
+       return sysfs_create_group(&bq->charger->dev.kobj,
+                       &bq2415x_sysfs_attr_group);
+}
+
+static void bq2415x_sysfs_exit(struct bq2415x_device *bq)
+{
+       sysfs_remove_group(&bq->charger->dev.kobj, &bq2415x_sysfs_attr_group);
+}
+
+/* main bq2415x probe function */
+static int bq2415x_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       int ret;
+       int num;
+       char *name = NULL;
+       struct bq2415x_device *bq;
+       struct device_node *np = client->dev.of_node;
+       struct bq2415x_platform_data *pdata = client->dev.platform_data;
+       const struct acpi_device_id *acpi_id = NULL;
+       struct power_supply *notify_psy = NULL;
+       union power_supply_propval prop;
+
+       if (!np && !pdata && !ACPI_HANDLE(&client->dev)) {
+               dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n");
+               return -ENODEV;
+       }
+
+       /* Get new ID for the new device */
+       mutex_lock(&bq2415x_id_mutex);
+       num = idr_alloc(&bq2415x_id, client, 0, 0, GFP_KERNEL);
+       mutex_unlock(&bq2415x_id_mutex);
+       if (num < 0)
+               return num;
+
+       if (id) {
+               name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
+       } else if (ACPI_HANDLE(&client->dev)) {
+               acpi_id =
+                       acpi_match_device(client->dev.driver->acpi_match_table,
+                                         &client->dev);
+               name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
+       }
+       if (!name) {
+               dev_err(&client->dev, "failed to allocate device name\n");
+               ret = -ENOMEM;
+               goto error_1;
+       }
+
+       bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
+       if (!bq) {
+               ret = -ENOMEM;
+               goto error_2;
+       }
+
+       i2c_set_clientdata(client, bq);
+
+       bq->id = num;
+       bq->dev = &client->dev;
+       if (id)
+               bq->chip = id->driver_data;
+       else if (ACPI_HANDLE(bq->dev))
+               bq->chip = acpi_id->driver_data;
+       bq->name = name;
+       bq->mode = BQ2415X_MODE_OFF;
+       bq->reported_mode = BQ2415X_MODE_OFF;
+       bq->autotimer = 0;
+       bq->automode = 0;
+
+       if (np || ACPI_HANDLE(bq->dev)) {
+               ret = device_property_read_u32(bq->dev,
+                                              "ti,current-limit",
+                                              &bq->init_data.current_limit);
+               if (ret)
+                       goto error_2;
+               ret = device_property_read_u32(bq->dev,
+                                       "ti,weak-battery-voltage",
+                                       &bq->init_data.weak_battery_voltage);
+               if (ret)
+                       goto error_2;
+               ret = device_property_read_u32(bq->dev,
+                               "ti,battery-regulation-voltage",
+                               &bq->init_data.battery_regulation_voltage);
+               if (ret)
+                       goto error_2;
+               ret = device_property_read_u32(bq->dev,
+                                              "ti,charge-current",
+                                              &bq->init_data.charge_current);
+               if (ret)
+                       goto error_2;
+               ret = device_property_read_u32(bq->dev,
+                               "ti,termination-current",
+                               &bq->init_data.termination_current);
+               if (ret)
+                       goto error_2;
+               ret = device_property_read_u32(bq->dev,
+                                              "ti,resistor-sense",
+                                              &bq->init_data.resistor_sense);
+               if (ret)
+                       goto error_2;
+               if (np)
+                       bq->notify_node = of_parse_phandle(np,
+                                               "ti,usb-charger-detection", 0);
+       } else {
+               memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
+       }
+
+       bq2415x_reset_chip(bq);
+
+       ret = bq2415x_power_supply_init(bq);
+       if (ret) {
+               dev_err(bq->dev, "failed to register power supply: %d\n", ret);
+               goto error_2;
+       }
+
+       ret = bq2415x_sysfs_init(bq);
+       if (ret) {
+               dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
+               goto error_3;
+       }
+
+       ret = bq2415x_set_defaults(bq);
+       if (ret) {
+               dev_err(bq->dev, "failed to set default values: %d\n", ret);
+               goto error_4;
+       }
+
+       if (bq->notify_node || bq->init_data.notify_device) {
+               bq->nb.notifier_call = bq2415x_notifier_call;
+               ret = power_supply_reg_notifier(&bq->nb);
+               if (ret) {
+                       dev_err(bq->dev, "failed to reg notifier: %d\n", ret);
+                       goto error_4;
+               }
+
+               bq->automode = 1;
+               dev_info(bq->dev, "automode supported, waiting for events\n");
+       } else {
+               bq->automode = -1;
+               dev_info(bq->dev, "automode not supported\n");
+       }
+
+       /* Query for initial reported_mode and set it */
+       if (bq->nb.notifier_call) {
+               if (np) {
+                       notify_psy = power_supply_get_by_phandle(np,
+                                               "ti,usb-charger-detection");
+                       if (IS_ERR(notify_psy))
+                               notify_psy = NULL;
+               } else if (bq->init_data.notify_device) {
+                       notify_psy = power_supply_get_by_name(
+                                               bq->init_data.notify_device);
+               }
+       }
+       if (notify_psy) {
+               ret = power_supply_get_property(notify_psy,
+                                       POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
+               power_supply_put(notify_psy);
+
+               if (ret == 0) {
+                       bq2415x_update_reported_mode(bq, prop.intval);
+                       bq2415x_set_mode(bq, bq->reported_mode);
+               }
+       }
+
+       INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work);
+       bq2415x_set_autotimer(bq, 1);
+
+       dev_info(bq->dev, "driver registered\n");
+       return 0;
+
+error_4:
+       bq2415x_sysfs_exit(bq);
+error_3:
+       bq2415x_power_supply_exit(bq);
+error_2:
+       if (bq)
+               of_node_put(bq->notify_node);
+       kfree(name);
+error_1:
+       mutex_lock(&bq2415x_id_mutex);
+       idr_remove(&bq2415x_id, num);
+       mutex_unlock(&bq2415x_id_mutex);
+
+       return ret;
+}
+
+/* main bq2415x remove function */
+
+static int bq2415x_remove(struct i2c_client *client)
+{
+       struct bq2415x_device *bq = i2c_get_clientdata(client);
+
+       if (bq->nb.notifier_call)
+               power_supply_unreg_notifier(&bq->nb);
+
+       of_node_put(bq->notify_node);
+       bq2415x_sysfs_exit(bq);
+       bq2415x_power_supply_exit(bq);
+
+       bq2415x_reset_chip(bq);
+
+       mutex_lock(&bq2415x_id_mutex);
+       idr_remove(&bq2415x_id, bq->id);
+       mutex_unlock(&bq2415x_id_mutex);
+
+       dev_info(bq->dev, "driver unregistered\n");
+
+       kfree(bq->name);
+
+       return 0;
+}
+
+static const struct i2c_device_id bq2415x_i2c_id_table[] = {
+       { "bq2415x", BQUNKNOWN },
+       { "bq24150", BQ24150 },
+       { "bq24150a", BQ24150A },
+       { "bq24151", BQ24151 },
+       { "bq24151a", BQ24151A },
+       { "bq24152", BQ24152 },
+       { "bq24153", BQ24153 },
+       { "bq24153a", BQ24153A },
+       { "bq24155", BQ24155 },
+       { "bq24156", BQ24156 },
+       { "bq24156a", BQ24156A },
+       { "bq24157s", BQ24157S },
+       { "bq24158", BQ24158 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id bq2415x_i2c_acpi_match[] = {
+       { "BQ2415X", BQUNKNOWN },
+       { "BQ241500", BQ24150 },
+       { "BQA24150", BQ24150A },
+       { "BQ241510", BQ24151 },
+       { "BQA24151", BQ24151A },
+       { "BQ241520", BQ24152 },
+       { "BQ241530", BQ24153 },
+       { "BQA24153", BQ24153A },
+       { "BQ241550", BQ24155 },
+       { "BQ241560", BQ24156 },
+       { "BQA24156", BQ24156A },
+       { "BQS24157", BQ24157S },
+       { "BQ241580", BQ24158 },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, bq2415x_i2c_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id bq2415x_of_match_table[] = {
+       { .compatible = "ti,bq24150" },
+       { .compatible = "ti,bq24150a" },
+       { .compatible = "ti,bq24151" },
+       { .compatible = "ti,bq24151a" },
+       { .compatible = "ti,bq24152" },
+       { .compatible = "ti,bq24153" },
+       { .compatible = "ti,bq24153a" },
+       { .compatible = "ti,bq24155" },
+       { .compatible = "ti,bq24156" },
+       { .compatible = "ti,bq24156a" },
+       { .compatible = "ti,bq24157s" },
+       { .compatible = "ti,bq24158" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bq2415x_of_match_table);
+#endif
+
+static struct i2c_driver bq2415x_driver = {
+       .driver = {
+               .name = "bq2415x-charger",
+               .of_match_table = of_match_ptr(bq2415x_of_match_table),
+               .acpi_match_table = ACPI_PTR(bq2415x_i2c_acpi_match),
+       },
+       .probe = bq2415x_probe,
+       .remove = bq2415x_remove,
+       .id_table = bq2415x_i2c_id_table,
+};
+module_i2c_driver(bq2415x_driver);
+
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_DESCRIPTION("bq2415x charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c
new file mode 100644 (file)
index 0000000..f5746b9
--- /dev/null
@@ -0,0 +1,1546 @@
+/*
+ * Driver for the TI bq24190 battery charger.
+ *
+ * Author: Mark A. Greer <mgreer@animalcreek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+#include <linux/power/bq24190_charger.h>
+
+
+#define        BQ24190_MANUFACTURER    "Texas Instruments"
+
+#define BQ24190_REG_ISC                0x00 /* Input Source Control */
+#define BQ24190_REG_ISC_EN_HIZ_MASK            BIT(7)
+#define BQ24190_REG_ISC_EN_HIZ_SHIFT           7
+#define BQ24190_REG_ISC_VINDPM_MASK            (BIT(6) | BIT(5) | BIT(4) | \
+                                                BIT(3))
+#define BQ24190_REG_ISC_VINDPM_SHIFT           3
+#define BQ24190_REG_ISC_IINLIM_MASK            (BIT(2) | BIT(1) | BIT(0))
+#define BQ24190_REG_ISC_IINLIM_SHIFT           0
+
+#define BQ24190_REG_POC                0x01 /* Power-On Configuration */
+#define BQ24190_REG_POC_RESET_MASK             BIT(7)
+#define BQ24190_REG_POC_RESET_SHIFT            7
+#define BQ24190_REG_POC_WDT_RESET_MASK         BIT(6)
+#define BQ24190_REG_POC_WDT_RESET_SHIFT                6
+#define BQ24190_REG_POC_CHG_CONFIG_MASK                (BIT(5) | BIT(4))
+#define BQ24190_REG_POC_CHG_CONFIG_SHIFT       4
+#define BQ24190_REG_POC_SYS_MIN_MASK           (BIT(3) | BIT(2) | BIT(1))
+#define BQ24190_REG_POC_SYS_MIN_SHIFT          1
+#define BQ24190_REG_POC_BOOST_LIM_MASK         BIT(0)
+#define BQ24190_REG_POC_BOOST_LIM_SHIFT                0
+
+#define BQ24190_REG_CCC                0x02 /* Charge Current Control */
+#define BQ24190_REG_CCC_ICHG_MASK              (BIT(7) | BIT(6) | BIT(5) | \
+                                                BIT(4) | BIT(3) | BIT(2))
+#define BQ24190_REG_CCC_ICHG_SHIFT             2
+#define BQ24190_REG_CCC_FORCE_20PCT_MASK       BIT(0)
+#define BQ24190_REG_CCC_FORCE_20PCT_SHIFT      0
+
+#define BQ24190_REG_PCTCC      0x03 /* Pre-charge/Termination Current Cntl */
+#define BQ24190_REG_PCTCC_IPRECHG_MASK         (BIT(7) | BIT(6) | BIT(5) | \
+                                                BIT(4))
+#define BQ24190_REG_PCTCC_IPRECHG_SHIFT                4
+#define BQ24190_REG_PCTCC_ITERM_MASK           (BIT(3) | BIT(2) | BIT(1) | \
+                                                BIT(0))
+#define BQ24190_REG_PCTCC_ITERM_SHIFT          0
+
+#define BQ24190_REG_CVC                0x04 /* Charge Voltage Control */
+#define BQ24190_REG_CVC_VREG_MASK              (BIT(7) | BIT(6) | BIT(5) | \
+                                                BIT(4) | BIT(3) | BIT(2))
+#define BQ24190_REG_CVC_VREG_SHIFT             2
+#define BQ24190_REG_CVC_BATLOWV_MASK           BIT(1)
+#define BQ24190_REG_CVC_BATLOWV_SHIFT          1
+#define BQ24190_REG_CVC_VRECHG_MASK            BIT(0)
+#define BQ24190_REG_CVC_VRECHG_SHIFT           0
+
+#define BQ24190_REG_CTTC       0x05 /* Charge Term/Timer Control */
+#define BQ24190_REG_CTTC_EN_TERM_MASK          BIT(7)
+#define BQ24190_REG_CTTC_EN_TERM_SHIFT         7
+#define BQ24190_REG_CTTC_TERM_STAT_MASK                BIT(6)
+#define BQ24190_REG_CTTC_TERM_STAT_SHIFT       6
+#define BQ24190_REG_CTTC_WATCHDOG_MASK         (BIT(5) | BIT(4))
+#define BQ24190_REG_CTTC_WATCHDOG_SHIFT                4
+#define BQ24190_REG_CTTC_EN_TIMER_MASK         BIT(3)
+#define BQ24190_REG_CTTC_EN_TIMER_SHIFT                3
+#define BQ24190_REG_CTTC_CHG_TIMER_MASK                (BIT(2) | BIT(1))
+#define BQ24190_REG_CTTC_CHG_TIMER_SHIFT       1
+#define BQ24190_REG_CTTC_JEITA_ISET_MASK       BIT(0)
+#define BQ24190_REG_CTTC_JEITA_ISET_SHIFT      0
+
+#define BQ24190_REG_ICTRC      0x06 /* IR Comp/Thermal Regulation Control */
+#define BQ24190_REG_ICTRC_BAT_COMP_MASK                (BIT(7) | BIT(6) | BIT(5))
+#define BQ24190_REG_ICTRC_BAT_COMP_SHIFT       5
+#define BQ24190_REG_ICTRC_VCLAMP_MASK          (BIT(4) | BIT(3) | BIT(2))
+#define BQ24190_REG_ICTRC_VCLAMP_SHIFT         2
+#define BQ24190_REG_ICTRC_TREG_MASK            (BIT(1) | BIT(0))
+#define BQ24190_REG_ICTRC_TREG_SHIFT           0
+
+#define BQ24190_REG_MOC                0x07 /* Misc. Operation Control */
+#define BQ24190_REG_MOC_DPDM_EN_MASK           BIT(7)
+#define BQ24190_REG_MOC_DPDM_EN_SHIFT          7
+#define BQ24190_REG_MOC_TMR2X_EN_MASK          BIT(6)
+#define BQ24190_REG_MOC_TMR2X_EN_SHIFT         6
+#define BQ24190_REG_MOC_BATFET_DISABLE_MASK    BIT(5)
+#define BQ24190_REG_MOC_BATFET_DISABLE_SHIFT   5
+#define BQ24190_REG_MOC_JEITA_VSET_MASK                BIT(4)
+#define BQ24190_REG_MOC_JEITA_VSET_SHIFT       4
+#define BQ24190_REG_MOC_INT_MASK_MASK          (BIT(1) | BIT(0))
+#define BQ24190_REG_MOC_INT_MASK_SHIFT         0
+
+#define BQ24190_REG_SS         0x08 /* System Status */
+#define BQ24190_REG_SS_VBUS_STAT_MASK          (BIT(7) | BIT(6))
+#define BQ24190_REG_SS_VBUS_STAT_SHIFT         6
+#define BQ24190_REG_SS_CHRG_STAT_MASK          (BIT(5) | BIT(4))
+#define BQ24190_REG_SS_CHRG_STAT_SHIFT         4
+#define BQ24190_REG_SS_DPM_STAT_MASK           BIT(3)
+#define BQ24190_REG_SS_DPM_STAT_SHIFT          3
+#define BQ24190_REG_SS_PG_STAT_MASK            BIT(2)
+#define BQ24190_REG_SS_PG_STAT_SHIFT           2
+#define BQ24190_REG_SS_THERM_STAT_MASK         BIT(1)
+#define BQ24190_REG_SS_THERM_STAT_SHIFT                1
+#define BQ24190_REG_SS_VSYS_STAT_MASK          BIT(0)
+#define BQ24190_REG_SS_VSYS_STAT_SHIFT         0
+
+#define BQ24190_REG_F          0x09 /* Fault */
+#define BQ24190_REG_F_WATCHDOG_FAULT_MASK      BIT(7)
+#define BQ24190_REG_F_WATCHDOG_FAULT_SHIFT     7
+#define BQ24190_REG_F_BOOST_FAULT_MASK         BIT(6)
+#define BQ24190_REG_F_BOOST_FAULT_SHIFT                6
+#define BQ24190_REG_F_CHRG_FAULT_MASK          (BIT(5) | BIT(4))
+#define BQ24190_REG_F_CHRG_FAULT_SHIFT         4
+#define BQ24190_REG_F_BAT_FAULT_MASK           BIT(3)
+#define BQ24190_REG_F_BAT_FAULT_SHIFT          3
+#define BQ24190_REG_F_NTC_FAULT_MASK           (BIT(2) | BIT(1) | BIT(0))
+#define BQ24190_REG_F_NTC_FAULT_SHIFT          0
+
+#define BQ24190_REG_VPRS       0x0A /* Vendor/Part/Revision Status */
+#define BQ24190_REG_VPRS_PN_MASK               (BIT(5) | BIT(4) | BIT(3))
+#define BQ24190_REG_VPRS_PN_SHIFT              3
+#define BQ24190_REG_VPRS_PN_24190                      0x4
+#define BQ24190_REG_VPRS_PN_24192                      0x5 /* Also 24193 */
+#define BQ24190_REG_VPRS_PN_24192I                     0x3
+#define BQ24190_REG_VPRS_TS_PROFILE_MASK       BIT(2)
+#define BQ24190_REG_VPRS_TS_PROFILE_SHIFT      2
+#define BQ24190_REG_VPRS_DEV_REG_MASK          (BIT(1) | BIT(0))
+#define BQ24190_REG_VPRS_DEV_REG_SHIFT         0
+
+/*
+ * The FAULT register is latched by the bq24190 (except for NTC_FAULT)
+ * so the first read after a fault returns the latched value and subsequent
+ * reads return the current value.  In order to return the fault status
+ * to the user, have the interrupt handler save the reg's value and retrieve
+ * it in the appropriate health/status routine.  Each routine has its own
+ * flag indicating whether it should use the value stored by the last run
+ * of the interrupt handler or do an actual reg read.  That way each routine
+ * can report back whatever fault may have occured.
+ */
+struct bq24190_dev_info {
+       struct i2c_client               *client;
+       struct device                   *dev;
+       struct power_supply             *charger;
+       struct power_supply             *battery;
+       char                            model_name[I2C_NAME_SIZE];
+       kernel_ulong_t                  model;
+       unsigned int                    gpio_int;
+       unsigned int                    irq;
+       struct mutex                    f_reg_lock;
+       bool                            first_time;
+       bool                            charger_health_valid;
+       bool                            battery_health_valid;
+       bool                            battery_status_valid;
+       u8                              f_reg;
+       u8                              ss_reg;
+       u8                              watchdog;
+};
+
+/*
+ * The tables below provide a 2-way mapping for the value that goes in
+ * the register field and the real-world value that it represents.
+ * The index of the array is the value that goes in the register; the
+ * number at that index in the array is the real-world value that it
+ * represents.
+ */
+/* REG02[7:2] (ICHG) in uAh */
+static const int bq24190_ccc_ichg_values[] = {
+        512000,  576000,  640000,  704000,  768000,  832000,  896000,  960000,
+       1024000, 1088000, 1152000, 1216000, 1280000, 1344000, 1408000, 1472000,
+       1536000, 1600000, 1664000, 1728000, 1792000, 1856000, 1920000, 1984000,
+       2048000, 2112000, 2176000, 2240000, 2304000, 2368000, 2432000, 2496000,
+       2560000, 2624000, 2688000, 2752000, 2816000, 2880000, 2944000, 3008000,
+       3072000, 3136000, 3200000, 3264000, 3328000, 3392000, 3456000, 3520000,
+       3584000, 3648000, 3712000, 3776000, 3840000, 3904000, 3968000, 4032000,
+       4096000, 4160000, 4224000, 4288000, 4352000, 4416000, 4480000, 4544000
+};
+
+/* REG04[7:2] (VREG) in uV */
+static const int bq24190_cvc_vreg_values[] = {
+       3504000, 3520000, 3536000, 3552000, 3568000, 3584000, 3600000, 3616000,
+       3632000, 3648000, 3664000, 3680000, 3696000, 3712000, 3728000, 3744000,
+       3760000, 3776000, 3792000, 3808000, 3824000, 3840000, 3856000, 3872000,
+       3888000, 3904000, 3920000, 3936000, 3952000, 3968000, 3984000, 4000000,
+       4016000, 4032000, 4048000, 4064000, 4080000, 4096000, 4112000, 4128000,
+       4144000, 4160000, 4176000, 4192000, 4208000, 4224000, 4240000, 4256000,
+       4272000, 4288000, 4304000, 4320000, 4336000, 4352000, 4368000, 4384000,
+       4400000
+};
+
+/* REG06[1:0] (TREG) in tenths of degrees Celcius */
+static const int bq24190_ictrc_treg_values[] = {
+       600, 800, 1000, 1200
+};
+
+/*
+ * Return the index in 'tbl' of greatest value that is less than or equal to
+ * 'val'.  The index range returned is 0 to 'tbl_size' - 1.  Assumes that
+ * the values in 'tbl' are sorted from smallest to largest and 'tbl_size'
+ * is less than 2^8.
+ */
+static u8 bq24190_find_idx(const int tbl[], int tbl_size, int v)
+{
+       int i;
+
+       for (i = 1; i < tbl_size; i++)
+               if (v < tbl[i])
+                       break;
+
+       return i - 1;
+}
+
+/* Basic driver I/O routines */
+
+static int bq24190_read(struct bq24190_dev_info *bdi, u8 reg, u8 *data)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(bdi->client, reg);
+       if (ret < 0)
+               return ret;
+
+       *data = ret;
+       return 0;
+}
+
+static int bq24190_write(struct bq24190_dev_info *bdi, u8 reg, u8 data)
+{
+       return i2c_smbus_write_byte_data(bdi->client, reg, data);
+}
+
+static int bq24190_read_mask(struct bq24190_dev_info *bdi, u8 reg,
+               u8 mask, u8 shift, u8 *data)
+{
+       u8 v;
+       int ret;
+
+       ret = bq24190_read(bdi, reg, &v);
+       if (ret < 0)
+               return ret;
+
+       v &= mask;
+       v >>= shift;
+       *data = v;
+
+       return 0;
+}
+
+static int bq24190_write_mask(struct bq24190_dev_info *bdi, u8 reg,
+               u8 mask, u8 shift, u8 data)
+{
+       u8 v;
+       int ret;
+
+       ret = bq24190_read(bdi, reg, &v);
+       if (ret < 0)
+               return ret;
+
+       v &= ~mask;
+       v |= ((data << shift) & mask);
+
+       return bq24190_write(bdi, reg, v);
+}
+
+static int bq24190_get_field_val(struct bq24190_dev_info *bdi,
+               u8 reg, u8 mask, u8 shift,
+               const int tbl[], int tbl_size,
+               int *val)
+{
+       u8 v;
+       int ret;
+
+       ret = bq24190_read_mask(bdi, reg, mask, shift, &v);
+       if (ret < 0)
+               return ret;
+
+       v = (v >= tbl_size) ? (tbl_size - 1) : v;
+       *val = tbl[v];
+
+       return 0;
+}
+
+static int bq24190_set_field_val(struct bq24190_dev_info *bdi,
+               u8 reg, u8 mask, u8 shift,
+               const int tbl[], int tbl_size,
+               int val)
+{
+       u8 idx;
+
+       idx = bq24190_find_idx(tbl, tbl_size, val);
+
+       return bq24190_write_mask(bdi, reg, mask, shift, idx);
+}
+
+#ifdef CONFIG_SYSFS
+/*
+ * There are a numerous options that are configurable on the bq24190
+ * that go well beyond what the power_supply properties provide access to.
+ * Provide sysfs access to them so they can be examined and possibly modified
+ * on the fly.  They will be provided for the charger power_supply object only
+ * and will be prefixed by 'f_' to make them easier to recognize.
+ */
+
+#define BQ24190_SYSFS_FIELD(_name, r, f, m, store)                     \
+{                                                                      \
+       .attr   = __ATTR(f_##_name, m, bq24190_sysfs_show, store),      \
+       .reg    = BQ24190_REG_##r,                                      \
+       .mask   = BQ24190_REG_##r##_##f##_MASK,                         \
+       .shift  = BQ24190_REG_##r##_##f##_SHIFT,                        \
+}
+
+#define BQ24190_SYSFS_FIELD_RW(_name, r, f)                            \
+               BQ24190_SYSFS_FIELD(_name, r, f, S_IWUSR | S_IRUGO,     \
+                               bq24190_sysfs_store)
+
+#define BQ24190_SYSFS_FIELD_RO(_name, r, f)                            \
+               BQ24190_SYSFS_FIELD(_name, r, f, S_IRUGO, NULL)
+
+static ssize_t bq24190_sysfs_show(struct device *dev,
+               struct device_attribute *attr, char *buf);
+static ssize_t bq24190_sysfs_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count);
+
+struct bq24190_sysfs_field_info {
+       struct device_attribute attr;
+       u8      reg;
+       u8      mask;
+       u8      shift;
+};
+
+/* On i386 ptrace-abi.h defines SS that breaks the macro calls below. */
+#undef SS
+
+static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = {
+                       /*      sysfs name      reg     field in reg */
+       BQ24190_SYSFS_FIELD_RW(en_hiz,          ISC,    EN_HIZ),
+       BQ24190_SYSFS_FIELD_RW(vindpm,          ISC,    VINDPM),
+       BQ24190_SYSFS_FIELD_RW(iinlim,          ISC,    IINLIM),
+       BQ24190_SYSFS_FIELD_RW(chg_config,      POC,    CHG_CONFIG),
+       BQ24190_SYSFS_FIELD_RW(sys_min,         POC,    SYS_MIN),
+       BQ24190_SYSFS_FIELD_RW(boost_lim,       POC,    BOOST_LIM),
+       BQ24190_SYSFS_FIELD_RW(ichg,            CCC,    ICHG),
+       BQ24190_SYSFS_FIELD_RW(force_20_pct,    CCC,    FORCE_20PCT),
+       BQ24190_SYSFS_FIELD_RW(iprechg,         PCTCC,  IPRECHG),
+       BQ24190_SYSFS_FIELD_RW(iterm,           PCTCC,  ITERM),
+       BQ24190_SYSFS_FIELD_RW(vreg,            CVC,    VREG),
+       BQ24190_SYSFS_FIELD_RW(batlowv,         CVC,    BATLOWV),
+       BQ24190_SYSFS_FIELD_RW(vrechg,          CVC,    VRECHG),
+       BQ24190_SYSFS_FIELD_RW(en_term,         CTTC,   EN_TERM),
+       BQ24190_SYSFS_FIELD_RW(term_stat,       CTTC,   TERM_STAT),
+       BQ24190_SYSFS_FIELD_RO(watchdog,        CTTC,   WATCHDOG),
+       BQ24190_SYSFS_FIELD_RW(en_timer,        CTTC,   EN_TIMER),
+       BQ24190_SYSFS_FIELD_RW(chg_timer,       CTTC,   CHG_TIMER),
+       BQ24190_SYSFS_FIELD_RW(jeta_iset,       CTTC,   JEITA_ISET),
+       BQ24190_SYSFS_FIELD_RW(bat_comp,        ICTRC,  BAT_COMP),
+       BQ24190_SYSFS_FIELD_RW(vclamp,          ICTRC,  VCLAMP),
+       BQ24190_SYSFS_FIELD_RW(treg,            ICTRC,  TREG),
+       BQ24190_SYSFS_FIELD_RW(dpdm_en,         MOC,    DPDM_EN),
+       BQ24190_SYSFS_FIELD_RW(tmr2x_en,        MOC,    TMR2X_EN),
+       BQ24190_SYSFS_FIELD_RW(batfet_disable,  MOC,    BATFET_DISABLE),
+       BQ24190_SYSFS_FIELD_RW(jeita_vset,      MOC,    JEITA_VSET),
+       BQ24190_SYSFS_FIELD_RO(int_mask,        MOC,    INT_MASK),
+       BQ24190_SYSFS_FIELD_RO(vbus_stat,       SS,     VBUS_STAT),
+       BQ24190_SYSFS_FIELD_RO(chrg_stat,       SS,     CHRG_STAT),
+       BQ24190_SYSFS_FIELD_RO(dpm_stat,        SS,     DPM_STAT),
+       BQ24190_SYSFS_FIELD_RO(pg_stat,         SS,     PG_STAT),
+       BQ24190_SYSFS_FIELD_RO(therm_stat,      SS,     THERM_STAT),
+       BQ24190_SYSFS_FIELD_RO(vsys_stat,       SS,     VSYS_STAT),
+       BQ24190_SYSFS_FIELD_RO(watchdog_fault,  F,      WATCHDOG_FAULT),
+       BQ24190_SYSFS_FIELD_RO(boost_fault,     F,      BOOST_FAULT),
+       BQ24190_SYSFS_FIELD_RO(chrg_fault,      F,      CHRG_FAULT),
+       BQ24190_SYSFS_FIELD_RO(bat_fault,       F,      BAT_FAULT),
+       BQ24190_SYSFS_FIELD_RO(ntc_fault,       F,      NTC_FAULT),
+       BQ24190_SYSFS_FIELD_RO(pn,              VPRS,   PN),
+       BQ24190_SYSFS_FIELD_RO(ts_profile,      VPRS,   TS_PROFILE),
+       BQ24190_SYSFS_FIELD_RO(dev_reg,         VPRS,   DEV_REG),
+};
+
+static struct attribute *
+       bq24190_sysfs_attrs[ARRAY_SIZE(bq24190_sysfs_field_tbl) + 1];
+
+static const struct attribute_group bq24190_sysfs_attr_group = {
+       .attrs = bq24190_sysfs_attrs,
+};
+
+static void bq24190_sysfs_init_attrs(void)
+{
+       int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl);
+
+       for (i = 0; i < limit; i++)
+               bq24190_sysfs_attrs[i] = &bq24190_sysfs_field_tbl[i].attr.attr;
+
+       bq24190_sysfs_attrs[limit] = NULL; /* Has additional entry for this */
+}
+
+static struct bq24190_sysfs_field_info *bq24190_sysfs_field_lookup(
+               const char *name)
+{
+       int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl);
+
+       for (i = 0; i < limit; i++)
+               if (!strcmp(name, bq24190_sysfs_field_tbl[i].attr.attr.name))
+                       break;
+
+       if (i >= limit)
+               return NULL;
+
+       return &bq24190_sysfs_field_tbl[i];
+}
+
+static ssize_t bq24190_sysfs_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
+       struct bq24190_sysfs_field_info *info;
+       int ret;
+       u8 v;
+
+       info = bq24190_sysfs_field_lookup(attr->attr.name);
+       if (!info)
+               return -EINVAL;
+
+       ret = bq24190_read_mask(bdi, info->reg, info->mask, info->shift, &v);
+       if (ret)
+               return ret;
+
+       return scnprintf(buf, PAGE_SIZE, "%hhx\n", v);
+}
+
+static ssize_t bq24190_sysfs_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
+       struct bq24190_sysfs_field_info *info;
+       int ret;
+       u8 v;
+
+       info = bq24190_sysfs_field_lookup(attr->attr.name);
+       if (!info)
+               return -EINVAL;
+
+       ret = kstrtou8(buf, 0, &v);
+       if (ret < 0)
+               return ret;
+
+       ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi)
+{
+       bq24190_sysfs_init_attrs();
+
+       return sysfs_create_group(&bdi->charger->dev.kobj,
+                       &bq24190_sysfs_attr_group);
+}
+
+static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi)
+{
+       sysfs_remove_group(&bdi->charger->dev.kobj, &bq24190_sysfs_attr_group);
+}
+#else
+static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi)
+{
+       return 0;
+}
+
+static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {}
+#endif
+
+/*
+ * According to the "Host Mode and default Mode" section of the
+ * manual, a write to any register causes the bq24190 to switch
+ * from default mode to host mode.  It will switch back to default
+ * mode after a WDT timeout unless the WDT is turned off as well.
+ * So, by simply turning off the WDT, we accomplish both with the
+ * same write.
+ */
+static int bq24190_set_mode_host(struct bq24190_dev_info *bdi)
+{
+       int ret;
+       u8 v;
+
+       ret = bq24190_read(bdi, BQ24190_REG_CTTC, &v);
+       if (ret < 0)
+               return ret;
+
+       bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >>
+                                       BQ24190_REG_CTTC_WATCHDOG_SHIFT);
+       v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK;
+
+       return bq24190_write(bdi, BQ24190_REG_CTTC, v);
+}
+
+static int bq24190_register_reset(struct bq24190_dev_info *bdi)
+{
+       int ret, limit = 100;
+       u8 v;
+
+       /* Reset the registers */
+       ret = bq24190_write_mask(bdi, BQ24190_REG_POC,
+                       BQ24190_REG_POC_RESET_MASK,
+                       BQ24190_REG_POC_RESET_SHIFT,
+                       0x1);
+       if (ret < 0)
+               return ret;
+
+       /* Reset bit will be cleared by hardware so poll until it is */
+       do {
+               ret = bq24190_read_mask(bdi, BQ24190_REG_POC,
+                               BQ24190_REG_POC_RESET_MASK,
+                               BQ24190_REG_POC_RESET_SHIFT,
+                               &v);
+               if (ret < 0)
+                       return ret;
+
+               if (!v)
+                       break;
+
+               udelay(10);
+       } while (--limit);
+
+       if (!limit)
+               return -EIO;
+
+       return 0;
+}
+
+/* Charger power supply property routines */
+
+static int bq24190_charger_get_charge_type(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       u8 v;
+       int type, ret;
+
+       ret = bq24190_read_mask(bdi, BQ24190_REG_POC,
+                       BQ24190_REG_POC_CHG_CONFIG_MASK,
+                       BQ24190_REG_POC_CHG_CONFIG_SHIFT,
+                       &v);
+       if (ret < 0)
+               return ret;
+
+       /* If POC[CHG_CONFIG] (REG01[5:4]) == 0, charge is disabled */
+       if (!v) {
+               type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+       } else {
+               ret = bq24190_read_mask(bdi, BQ24190_REG_CCC,
+                               BQ24190_REG_CCC_FORCE_20PCT_MASK,
+                               BQ24190_REG_CCC_FORCE_20PCT_SHIFT,
+                               &v);
+               if (ret < 0)
+                       return ret;
+
+               type = (v) ? POWER_SUPPLY_CHARGE_TYPE_TRICKLE :
+                            POWER_SUPPLY_CHARGE_TYPE_FAST;
+       }
+
+       val->intval = type;
+
+       return 0;
+}
+
+static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi,
+               const union power_supply_propval *val)
+{
+       u8 chg_config, force_20pct, en_term;
+       int ret;
+
+       /*
+        * According to the "Termination when REG02[0] = 1" section of
+        * the bq24190 manual, the trickle charge could be less than the
+        * termination current so it recommends turning off the termination
+        * function.
+        *
+        * Note: AFAICT from the datasheet, the user will have to manually
+        * turn off the charging when in 20% mode.  If its not turned off,
+        * there could be battery damage.  So, use this mode at your own risk.
+        */
+       switch (val->intval) {
+       case POWER_SUPPLY_CHARGE_TYPE_NONE:
+               chg_config = 0x0;
+               break;
+       case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
+               chg_config = 0x1;
+               force_20pct = 0x1;
+               en_term = 0x0;
+               break;
+       case POWER_SUPPLY_CHARGE_TYPE_FAST:
+               chg_config = 0x1;
+               force_20pct = 0x0;
+               en_term = 0x1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (chg_config) { /* Enabling the charger */
+               ret = bq24190_write_mask(bdi, BQ24190_REG_CCC,
+                               BQ24190_REG_CCC_FORCE_20PCT_MASK,
+                               BQ24190_REG_CCC_FORCE_20PCT_SHIFT,
+                               force_20pct);
+               if (ret < 0)
+                       return ret;
+
+               ret = bq24190_write_mask(bdi, BQ24190_REG_CTTC,
+                               BQ24190_REG_CTTC_EN_TERM_MASK,
+                               BQ24190_REG_CTTC_EN_TERM_SHIFT,
+                               en_term);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return bq24190_write_mask(bdi, BQ24190_REG_POC,
+                       BQ24190_REG_POC_CHG_CONFIG_MASK,
+                       BQ24190_REG_POC_CHG_CONFIG_SHIFT, chg_config);
+}
+
+static int bq24190_charger_get_health(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       u8 v;
+       int health, ret;
+
+       mutex_lock(&bdi->f_reg_lock);
+
+       if (bdi->charger_health_valid) {
+               v = bdi->f_reg;
+               bdi->charger_health_valid = false;
+               mutex_unlock(&bdi->f_reg_lock);
+       } else {
+               mutex_unlock(&bdi->f_reg_lock);
+
+               ret = bq24190_read(bdi, BQ24190_REG_F, &v);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {
+               /*
+                * This could be over-current or over-voltage but there's
+                * no way to tell which.  Return 'OVERVOLTAGE' since there
+                * isn't an 'OVERCURRENT' value defined that we can return
+                * even if it was over-current.
+                */
+               health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+       } else {
+               v &= BQ24190_REG_F_CHRG_FAULT_MASK;
+               v >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
+
+               switch (v) {
+               case 0x0: /* Normal */
+                       health = POWER_SUPPLY_HEALTH_GOOD;
+                       break;
+               case 0x1: /* Input Fault (VBUS OVP or VBAT<VBUS<3.8V) */
+                       /*
+                        * This could be over-voltage or under-voltage
+                        * and there's no way to tell which.  Instead
+                        * of looking foolish and returning 'OVERVOLTAGE'
+                        * when its really under-voltage, just return
+                        * 'UNSPEC_FAILURE'.
+                        */
+                       health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+                       break;
+               case 0x2: /* Thermal Shutdown */
+                       health = POWER_SUPPLY_HEALTH_OVERHEAT;
+                       break;
+               case 0x3: /* Charge Safety Timer Expiration */
+                       health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+                       break;
+               default:
+                       health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               }
+       }
+
+       val->intval = health;
+
+       return 0;
+}
+
+static int bq24190_charger_get_online(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       u8 v;
+       int ret;
+
+       ret = bq24190_read_mask(bdi, BQ24190_REG_SS,
+                       BQ24190_REG_SS_PG_STAT_MASK,
+                       BQ24190_REG_SS_PG_STAT_SHIFT, &v);
+       if (ret < 0)
+               return ret;
+
+       val->intval = v;
+       return 0;
+}
+
+static int bq24190_charger_get_current(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       u8 v;
+       int curr, ret;
+
+       ret = bq24190_get_field_val(bdi, BQ24190_REG_CCC,
+                       BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT,
+                       bq24190_ccc_ichg_values,
+                       ARRAY_SIZE(bq24190_ccc_ichg_values), &curr);
+       if (ret < 0)
+               return ret;
+
+       ret = bq24190_read_mask(bdi, BQ24190_REG_CCC,
+                       BQ24190_REG_CCC_FORCE_20PCT_MASK,
+                       BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v);
+       if (ret < 0)
+               return ret;
+
+       /* If FORCE_20PCT is enabled, then current is 20% of ICHG value */
+       if (v)
+               curr /= 5;
+
+       val->intval = curr;
+       return 0;
+}
+
+static int bq24190_charger_get_current_max(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       int idx = ARRAY_SIZE(bq24190_ccc_ichg_values) - 1;
+
+       val->intval = bq24190_ccc_ichg_values[idx];
+       return 0;
+}
+
+static int bq24190_charger_set_current(struct bq24190_dev_info *bdi,
+               const union power_supply_propval *val)
+{
+       u8 v;
+       int ret, curr = val->intval;
+
+       ret = bq24190_read_mask(bdi, BQ24190_REG_CCC,
+                       BQ24190_REG_CCC_FORCE_20PCT_MASK,
+                       BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v);
+       if (ret < 0)
+               return ret;
+
+       /* If FORCE_20PCT is enabled, have to multiply value passed in by 5 */
+       if (v)
+               curr *= 5;
+
+       return bq24190_set_field_val(bdi, BQ24190_REG_CCC,
+                       BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT,
+                       bq24190_ccc_ichg_values,
+                       ARRAY_SIZE(bq24190_ccc_ichg_values), curr);
+}
+
+static int bq24190_charger_get_voltage(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       int voltage, ret;
+
+       ret = bq24190_get_field_val(bdi, BQ24190_REG_CVC,
+                       BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT,
+                       bq24190_cvc_vreg_values,
+                       ARRAY_SIZE(bq24190_cvc_vreg_values), &voltage);
+       if (ret < 0)
+               return ret;
+
+       val->intval = voltage;
+       return 0;
+}
+
+static int bq24190_charger_get_voltage_max(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       int idx = ARRAY_SIZE(bq24190_cvc_vreg_values) - 1;
+
+       val->intval = bq24190_cvc_vreg_values[idx];
+       return 0;
+}
+
+static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi,
+               const union power_supply_propval *val)
+{
+       return bq24190_set_field_val(bdi, BQ24190_REG_CVC,
+                       BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT,
+                       bq24190_cvc_vreg_values,
+                       ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval);
+}
+
+static int bq24190_charger_get_property(struct power_supply *psy,
+               enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
+       int ret;
+
+       dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+       pm_runtime_get_sync(bdi->dev);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               ret = bq24190_charger_get_charge_type(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = bq24190_charger_get_health(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = bq24190_charger_get_online(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               ret = bq24190_charger_get_current(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               ret = bq24190_charger_get_current_max(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = bq24190_charger_get_voltage(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               ret = bq24190_charger_get_voltage_max(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+               ret = 0;
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = bdi->model_name;
+               ret = 0;
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = BQ24190_MANUFACTURER;
+               ret = 0;
+               break;
+       default:
+               ret = -ENODATA;
+       }
+
+       pm_runtime_put_sync(bdi->dev);
+       return ret;
+}
+
+static int bq24190_charger_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
+       int ret;
+
+       dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+       pm_runtime_get_sync(bdi->dev);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               ret = bq24190_charger_set_charge_type(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               ret = bq24190_charger_set_current(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = bq24190_charger_set_voltage(bdi, val);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       pm_runtime_put_sync(bdi->dev);
+       return ret;
+}
+
+static int bq24190_charger_property_is_writeable(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = 1;
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property bq24190_charger_properties[] = {
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_SCOPE,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static char *bq24190_charger_supplied_to[] = {
+       "main-battery",
+};
+
+static const struct power_supply_desc bq24190_charger_desc = {
+       .name                   = "bq24190-charger",
+       .type                   = POWER_SUPPLY_TYPE_USB,
+       .properties             = bq24190_charger_properties,
+       .num_properties         = ARRAY_SIZE(bq24190_charger_properties),
+       .get_property           = bq24190_charger_get_property,
+       .set_property           = bq24190_charger_set_property,
+       .property_is_writeable  = bq24190_charger_property_is_writeable,
+};
+
+/* Battery power supply property routines */
+
+static int bq24190_battery_get_status(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       u8 ss_reg, chrg_fault;
+       int status, ret;
+
+       mutex_lock(&bdi->f_reg_lock);
+
+       if (bdi->battery_status_valid) {
+               chrg_fault = bdi->f_reg;
+               bdi->battery_status_valid = false;
+               mutex_unlock(&bdi->f_reg_lock);
+       } else {
+               mutex_unlock(&bdi->f_reg_lock);
+
+               ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault);
+               if (ret < 0)
+                       return ret;
+       }
+
+       chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK;
+       chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
+
+       ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * The battery must be discharging when any of these are true:
+        * - there is no good power source;
+        * - there is a charge fault.
+        * Could also be discharging when in "supplement mode" but
+        * there is no way to tell when its in that mode.
+        */
+       if (!(ss_reg & BQ24190_REG_SS_PG_STAT_MASK) || chrg_fault) {
+               status = POWER_SUPPLY_STATUS_DISCHARGING;
+       } else {
+               ss_reg &= BQ24190_REG_SS_CHRG_STAT_MASK;
+               ss_reg >>= BQ24190_REG_SS_CHRG_STAT_SHIFT;
+
+               switch (ss_reg) {
+               case 0x0: /* Not Charging */
+                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+                       break;
+               case 0x1: /* Pre-charge */
+               case 0x2: /* Fast Charging */
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+                       break;
+               case 0x3: /* Charge Termination Done */
+                       status = POWER_SUPPLY_STATUS_FULL;
+                       break;
+               default:
+                       ret = -EIO;
+               }
+       }
+
+       if (!ret)
+               val->intval = status;
+
+       return ret;
+}
+
+static int bq24190_battery_get_health(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       u8 v;
+       int health, ret;
+
+       mutex_lock(&bdi->f_reg_lock);
+
+       if (bdi->battery_health_valid) {
+               v = bdi->f_reg;
+               bdi->battery_health_valid = false;
+               mutex_unlock(&bdi->f_reg_lock);
+       } else {
+               mutex_unlock(&bdi->f_reg_lock);
+
+               ret = bq24190_read(bdi, BQ24190_REG_F, &v);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (v & BQ24190_REG_F_BAT_FAULT_MASK) {
+               health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+       } else {
+               v &= BQ24190_REG_F_NTC_FAULT_MASK;
+               v >>= BQ24190_REG_F_NTC_FAULT_SHIFT;
+
+               switch (v) {
+               case 0x0: /* Normal */
+                       health = POWER_SUPPLY_HEALTH_GOOD;
+                       break;
+               case 0x1: /* TS1 Cold */
+               case 0x3: /* TS2 Cold */
+               case 0x5: /* Both Cold */
+                       health = POWER_SUPPLY_HEALTH_COLD;
+                       break;
+               case 0x2: /* TS1 Hot */
+               case 0x4: /* TS2 Hot */
+               case 0x6: /* Both Hot */
+                       health = POWER_SUPPLY_HEALTH_OVERHEAT;
+                       break;
+               default:
+                       health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               }
+       }
+
+       val->intval = health;
+       return 0;
+}
+
+static int bq24190_battery_get_online(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       u8 batfet_disable;
+       int ret;
+
+       ret = bq24190_read_mask(bdi, BQ24190_REG_MOC,
+                       BQ24190_REG_MOC_BATFET_DISABLE_MASK,
+                       BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, &batfet_disable);
+       if (ret < 0)
+               return ret;
+
+       val->intval = !batfet_disable;
+       return 0;
+}
+
+static int bq24190_battery_set_online(struct bq24190_dev_info *bdi,
+               const union power_supply_propval *val)
+{
+       return bq24190_write_mask(bdi, BQ24190_REG_MOC,
+                       BQ24190_REG_MOC_BATFET_DISABLE_MASK,
+                       BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, !val->intval);
+}
+
+static int bq24190_battery_get_temp_alert_max(struct bq24190_dev_info *bdi,
+               union power_supply_propval *val)
+{
+       int temp, ret;
+
+       ret = bq24190_get_field_val(bdi, BQ24190_REG_ICTRC,
+                       BQ24190_REG_ICTRC_TREG_MASK,
+                       BQ24190_REG_ICTRC_TREG_SHIFT,
+                       bq24190_ictrc_treg_values,
+                       ARRAY_SIZE(bq24190_ictrc_treg_values), &temp);
+       if (ret < 0)
+               return ret;
+
+       val->intval = temp;
+       return 0;
+}
+
+static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi,
+               const union power_supply_propval *val)
+{
+       return bq24190_set_field_val(bdi, BQ24190_REG_ICTRC,
+                       BQ24190_REG_ICTRC_TREG_MASK,
+                       BQ24190_REG_ICTRC_TREG_SHIFT,
+                       bq24190_ictrc_treg_values,
+                       ARRAY_SIZE(bq24190_ictrc_treg_values), val->intval);
+}
+
+static int bq24190_battery_get_property(struct power_supply *psy,
+               enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
+       int ret;
+
+       dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+       pm_runtime_get_sync(bdi->dev);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = bq24190_battery_get_status(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = bq24190_battery_get_health(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = bq24190_battery_get_online(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               /* Could be Li-on or Li-polymer but no way to tell which */
+               val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+               ret = 0;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = bq24190_battery_get_temp_alert_max(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+               ret = 0;
+               break;
+       default:
+               ret = -ENODATA;
+       }
+
+       pm_runtime_put_sync(bdi->dev);
+       return ret;
+}
+
+static int bq24190_battery_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
+       int ret;
+
+       dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+       pm_runtime_put_sync(bdi->dev);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = bq24190_battery_set_online(bdi, val);
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = bq24190_battery_set_temp_alert_max(bdi, val);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       pm_runtime_put_sync(bdi->dev);
+       return ret;
+}
+
+static int bq24190_battery_property_is_writeable(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = 1;
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property bq24190_battery_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
+       POWER_SUPPLY_PROP_SCOPE,
+};
+
+static const struct power_supply_desc bq24190_battery_desc = {
+       .name                   = "bq24190-battery",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = bq24190_battery_properties,
+       .num_properties         = ARRAY_SIZE(bq24190_battery_properties),
+       .get_property           = bq24190_battery_get_property,
+       .set_property           = bq24190_battery_set_property,
+       .property_is_writeable  = bq24190_battery_property_is_writeable,
+};
+
+static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
+{
+       struct bq24190_dev_info *bdi = data;
+       bool alert_userspace = false;
+       u8 ss_reg = 0, f_reg = 0;
+       int ret;
+
+       pm_runtime_get_sync(bdi->dev);
+
+       ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg);
+       if (ret < 0) {
+               dev_err(bdi->dev, "Can't read SS reg: %d\n", ret);
+               goto out;
+       }
+
+       if (ss_reg != bdi->ss_reg) {
+               /*
+                * The device is in host mode so when PG_STAT goes from 1->0
+                * (i.e., power removed) HIZ needs to be disabled.
+                */
+               if ((bdi->ss_reg & BQ24190_REG_SS_PG_STAT_MASK) &&
+                               !(ss_reg & BQ24190_REG_SS_PG_STAT_MASK)) {
+                       ret = bq24190_write_mask(bdi, BQ24190_REG_ISC,
+                                       BQ24190_REG_ISC_EN_HIZ_MASK,
+                                       BQ24190_REG_ISC_EN_HIZ_SHIFT,
+                                       0);
+                       if (ret < 0)
+                               dev_err(bdi->dev, "Can't access ISC reg: %d\n",
+                                       ret);
+               }
+
+               bdi->ss_reg = ss_reg;
+               alert_userspace = true;
+       }
+
+       mutex_lock(&bdi->f_reg_lock);
+
+       ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg);
+       if (ret < 0) {
+               mutex_unlock(&bdi->f_reg_lock);
+               dev_err(bdi->dev, "Can't read F reg: %d\n", ret);
+               goto out;
+       }
+
+       if (f_reg != bdi->f_reg) {
+               bdi->f_reg = f_reg;
+               bdi->charger_health_valid = true;
+               bdi->battery_health_valid = true;
+               bdi->battery_status_valid = true;
+
+               alert_userspace = true;
+       }
+
+       mutex_unlock(&bdi->f_reg_lock);
+
+       /*
+        * Sometimes bq24190 gives a steady trickle of interrupts even
+        * though the watchdog timer is turned off and neither the STATUS
+        * nor FAULT registers have changed.  Weed out these sprurious
+        * interrupts so userspace isn't alerted for no reason.
+        * In addition, the chip always generates an interrupt after
+        * register reset so we should ignore that one (the very first
+        * interrupt received).
+        */
+       if (alert_userspace) {
+               if (!bdi->first_time) {
+                       power_supply_changed(bdi->charger);
+                       power_supply_changed(bdi->battery);
+               } else {
+                       bdi->first_time = false;
+               }
+       }
+
+out:
+       pm_runtime_put_sync(bdi->dev);
+
+       dev_dbg(bdi->dev, "ss_reg: 0x%02x, f_reg: 0x%02x\n", ss_reg, f_reg);
+
+       return IRQ_HANDLED;
+}
+
+static int bq24190_hw_init(struct bq24190_dev_info *bdi)
+{
+       u8 v;
+       int ret;
+
+       pm_runtime_get_sync(bdi->dev);
+
+       /* First check that the device really is what its supposed to be */
+       ret = bq24190_read_mask(bdi, BQ24190_REG_VPRS,
+                       BQ24190_REG_VPRS_PN_MASK,
+                       BQ24190_REG_VPRS_PN_SHIFT,
+                       &v);
+       if (ret < 0)
+               goto out;
+
+       if (v != bdi->model) {
+               ret = -ENODEV;
+               goto out;
+       }
+
+       ret = bq24190_register_reset(bdi);
+       if (ret < 0)
+               goto out;
+
+       ret = bq24190_set_mode_host(bdi);
+out:
+       pm_runtime_put_sync(bdi->dev);
+       return ret;
+}
+
+#ifdef CONFIG_OF
+static int bq24190_setup_dt(struct bq24190_dev_info *bdi)
+{
+       bdi->irq = irq_of_parse_and_map(bdi->dev->of_node, 0);
+       if (bdi->irq <= 0)
+               return -1;
+
+       return 0;
+}
+#else
+static int bq24190_setup_dt(struct bq24190_dev_info *bdi)
+{
+       return -1;
+}
+#endif
+
+static int bq24190_setup_pdata(struct bq24190_dev_info *bdi,
+               struct bq24190_platform_data *pdata)
+{
+       int ret;
+
+       if (!gpio_is_valid(pdata->gpio_int))
+               return -1;
+
+       ret = gpio_request(pdata->gpio_int, dev_name(bdi->dev));
+       if (ret < 0)
+               return -1;
+
+       ret = gpio_direction_input(pdata->gpio_int);
+       if (ret < 0)
+               goto out;
+
+       bdi->irq = gpio_to_irq(pdata->gpio_int);
+       if (!bdi->irq)
+               goto out;
+
+       bdi->gpio_int = pdata->gpio_int;
+       return 0;
+
+out:
+       gpio_free(pdata->gpio_int);
+       return -1;
+}
+
+static int bq24190_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct device *dev = &client->dev;
+       struct bq24190_platform_data *pdata = client->dev.platform_data;
+       struct power_supply_config charger_cfg = {}, battery_cfg = {};
+       struct bq24190_dev_info *bdi;
+       int ret;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+               return -ENODEV;
+       }
+
+       bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL);
+       if (!bdi) {
+               dev_err(dev, "Can't alloc bdi struct\n");
+               return -ENOMEM;
+       }
+
+       bdi->client = client;
+       bdi->dev = dev;
+       bdi->model = id->driver_data;
+       strncpy(bdi->model_name, id->name, I2C_NAME_SIZE);
+       mutex_init(&bdi->f_reg_lock);
+       bdi->first_time = true;
+       bdi->charger_health_valid = false;
+       bdi->battery_health_valid = false;
+       bdi->battery_status_valid = false;
+
+       i2c_set_clientdata(client, bdi);
+
+       if (dev->of_node)
+               ret = bq24190_setup_dt(bdi);
+       else
+               ret = bq24190_setup_pdata(bdi, pdata);
+
+       if (ret) {
+               dev_err(dev, "Can't get irq info\n");
+               return -EINVAL;
+       }
+
+       ret = devm_request_threaded_irq(dev, bdi->irq, NULL,
+                       bq24190_irq_handler_thread,
+                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                       "bq24190-charger", bdi);
+       if (ret < 0) {
+               dev_err(dev, "Can't set up irq handler\n");
+               goto out1;
+       }
+
+       pm_runtime_enable(dev);
+       pm_runtime_resume(dev);
+
+       ret = bq24190_hw_init(bdi);
+       if (ret < 0) {
+               dev_err(dev, "Hardware init failed\n");
+               goto out2;
+       }
+
+       charger_cfg.drv_data = bdi;
+       charger_cfg.supplied_to = bq24190_charger_supplied_to;
+       charger_cfg.num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to),
+       bdi->charger = power_supply_register(dev, &bq24190_charger_desc,
+                                               &charger_cfg);
+       if (IS_ERR(bdi->charger)) {
+               dev_err(dev, "Can't register charger\n");
+               ret = PTR_ERR(bdi->charger);
+               goto out2;
+       }
+
+       battery_cfg.drv_data = bdi;
+       bdi->battery = power_supply_register(dev, &bq24190_battery_desc,
+                                               &battery_cfg);
+       if (IS_ERR(bdi->battery)) {
+               dev_err(dev, "Can't register battery\n");
+               ret = PTR_ERR(bdi->battery);
+               goto out3;
+       }
+
+       ret = bq24190_sysfs_create_group(bdi);
+       if (ret) {
+               dev_err(dev, "Can't create sysfs entries\n");
+               goto out4;
+       }
+
+       return 0;
+
+out4:
+       power_supply_unregister(bdi->battery);
+out3:
+       power_supply_unregister(bdi->charger);
+out2:
+       pm_runtime_disable(dev);
+out1:
+       if (bdi->gpio_int)
+               gpio_free(bdi->gpio_int);
+
+       return ret;
+}
+
+static int bq24190_remove(struct i2c_client *client)
+{
+       struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
+
+       pm_runtime_get_sync(bdi->dev);
+       bq24190_register_reset(bdi);
+       pm_runtime_put_sync(bdi->dev);
+
+       bq24190_sysfs_remove_group(bdi);
+       power_supply_unregister(bdi->battery);
+       power_supply_unregister(bdi->charger);
+       pm_runtime_disable(bdi->dev);
+
+       if (bdi->gpio_int)
+               gpio_free(bdi->gpio_int);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bq24190_pm_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
+
+       pm_runtime_get_sync(bdi->dev);
+       bq24190_register_reset(bdi);
+       pm_runtime_put_sync(bdi->dev);
+
+       return 0;
+}
+
+static int bq24190_pm_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct bq24190_dev_info *bdi = i2c_get_clientdata(client);
+
+       bdi->charger_health_valid = false;
+       bdi->battery_health_valid = false;
+       bdi->battery_status_valid = false;
+
+       pm_runtime_get_sync(bdi->dev);
+       bq24190_register_reset(bdi);
+       pm_runtime_put_sync(bdi->dev);
+
+       /* Things may have changed while suspended so alert upper layer */
+       power_supply_changed(bdi->charger);
+       power_supply_changed(bdi->battery);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(bq24190_pm_ops, bq24190_pm_suspend, bq24190_pm_resume);
+
+/*
+ * Only support the bq24190 right now.  The bq24192, bq24192i, and bq24193
+ * are similar but not identical so the driver needs to be extended to
+ * support them.
+ */
+static const struct i2c_device_id bq24190_i2c_ids[] = {
+       { "bq24190", BQ24190_REG_VPRS_PN_24190 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id bq24190_of_match[] = {
+       { .compatible = "ti,bq24190", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, bq24190_of_match);
+#else
+static const struct of_device_id bq24190_of_match[] = {
+       { },
+};
+#endif
+
+static struct i2c_driver bq24190_driver = {
+       .probe          = bq24190_probe,
+       .remove         = bq24190_remove,
+       .id_table       = bq24190_i2c_ids,
+       .driver = {
+               .name           = "bq24190-charger",
+               .pm             = &bq24190_pm_ops,
+               .of_match_table = of_match_ptr(bq24190_of_match),
+       },
+};
+module_i2c_driver(bq24190_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
+MODULE_DESCRIPTION("TI BQ24190 Charger Driver");
diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c
new file mode 100644 (file)
index 0000000..1fea2c7
--- /dev/null
@@ -0,0 +1,1196 @@
+/*
+ * TI BQ24257 charger driver
+ *
+ * Copyright (C) 2015 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * Datasheets:
+ * http://www.ti.com/product/bq24250
+ * http://www.ti.com/product/bq24251
+ * http://www.ti.com/product/bq24257
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/acpi.h>
+#include <linux/of.h>
+
+#define BQ24257_REG_1                  0x00
+#define BQ24257_REG_2                  0x01
+#define BQ24257_REG_3                  0x02
+#define BQ24257_REG_4                  0x03
+#define BQ24257_REG_5                  0x04
+#define BQ24257_REG_6                  0x05
+#define BQ24257_REG_7                  0x06
+
+#define BQ24257_MANUFACTURER           "Texas Instruments"
+#define BQ24257_PG_GPIO                        "pg"
+
+#define BQ24257_ILIM_SET_DELAY         1000    /* msec */
+
+/*
+ * When adding support for new devices make sure that enum bq2425x_chip and
+ * bq2425x_chip_name[] always stay in sync!
+ */
+enum bq2425x_chip {
+       BQ24250,
+       BQ24251,
+       BQ24257,
+};
+
+static const char *const bq2425x_chip_name[] = {
+       "bq24250",
+       "bq24251",
+       "bq24257",
+};
+
+enum bq24257_fields {
+       F_WD_FAULT, F_WD_EN, F_STAT, F_FAULT,                       /* REG 1 */
+       F_RESET, F_IILIMIT, F_EN_STAT, F_EN_TERM, F_CE, F_HZ_MODE,  /* REG 2 */
+       F_VBAT, F_USB_DET,                                          /* REG 3 */
+       F_ICHG, F_ITERM,                                            /* REG 4 */
+       F_LOOP_STATUS, F_LOW_CHG, F_DPDM_EN, F_CE_STATUS, F_VINDPM, /* REG 5 */
+       F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_EN, F_TS_STAT,           /* REG 6 */
+       F_VOVP, F_CLR_VDP, F_FORCE_BATDET, F_FORCE_PTM,             /* REG 7 */
+
+       F_MAX_FIELDS
+};
+
+/* initial field values, converted from uV/uA */
+struct bq24257_init_data {
+       u8 ichg;        /* charge current      */
+       u8 vbat;        /* regulation voltage  */
+       u8 iterm;       /* termination current */
+       u8 iilimit;     /* input current limit */
+       u8 vovp;        /* over voltage protection voltage */
+       u8 vindpm;      /* VDMP input threshold voltage */
+};
+
+struct bq24257_state {
+       u8 status;
+       u8 fault;
+       bool power_good;
+};
+
+struct bq24257_device {
+       struct i2c_client *client;
+       struct device *dev;
+       struct power_supply *charger;
+
+       enum bq2425x_chip chip;
+
+       struct regmap *rmap;
+       struct regmap_field *rmap_fields[F_MAX_FIELDS];
+
+       struct gpio_desc *pg;
+
+       struct delayed_work iilimit_setup_work;
+
+       struct bq24257_init_data init_data;
+       struct bq24257_state state;
+
+       struct mutex lock; /* protect state data */
+
+       bool iilimit_autoset_enable;
+};
+
+static bool bq24257_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case BQ24257_REG_2:
+       case BQ24257_REG_4:
+               return false;
+
+       default:
+               return true;
+       }
+}
+
+static const struct regmap_config bq24257_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = BQ24257_REG_7,
+       .cache_type = REGCACHE_RBTREE,
+
+       .volatile_reg = bq24257_is_volatile_reg,
+};
+
+static const struct reg_field bq24257_reg_fields[] = {
+       /* REG 1 */
+       [F_WD_FAULT]            = REG_FIELD(BQ24257_REG_1, 7, 7),
+       [F_WD_EN]               = REG_FIELD(BQ24257_REG_1, 6, 6),
+       [F_STAT]                = REG_FIELD(BQ24257_REG_1, 4, 5),
+       [F_FAULT]               = REG_FIELD(BQ24257_REG_1, 0, 3),
+       /* REG 2 */
+       [F_RESET]               = REG_FIELD(BQ24257_REG_2, 7, 7),
+       [F_IILIMIT]             = REG_FIELD(BQ24257_REG_2, 4, 6),
+       [F_EN_STAT]             = REG_FIELD(BQ24257_REG_2, 3, 3),
+       [F_EN_TERM]             = REG_FIELD(BQ24257_REG_2, 2, 2),
+       [F_CE]                  = REG_FIELD(BQ24257_REG_2, 1, 1),
+       [F_HZ_MODE]             = REG_FIELD(BQ24257_REG_2, 0, 0),
+       /* REG 3 */
+       [F_VBAT]                = REG_FIELD(BQ24257_REG_3, 2, 7),
+       [F_USB_DET]             = REG_FIELD(BQ24257_REG_3, 0, 1),
+       /* REG 4 */
+       [F_ICHG]                = REG_FIELD(BQ24257_REG_4, 3, 7),
+       [F_ITERM]               = REG_FIELD(BQ24257_REG_4, 0, 2),
+       /* REG 5 */
+       [F_LOOP_STATUS]         = REG_FIELD(BQ24257_REG_5, 6, 7),
+       [F_LOW_CHG]             = REG_FIELD(BQ24257_REG_5, 5, 5),
+       [F_DPDM_EN]             = REG_FIELD(BQ24257_REG_5, 4, 4),
+       [F_CE_STATUS]           = REG_FIELD(BQ24257_REG_5, 3, 3),
+       [F_VINDPM]              = REG_FIELD(BQ24257_REG_5, 0, 2),
+       /* REG 6 */
+       [F_X2_TMR_EN]           = REG_FIELD(BQ24257_REG_6, 7, 7),
+       [F_TMR]                 = REG_FIELD(BQ24257_REG_6, 5, 6),
+       [F_SYSOFF]              = REG_FIELD(BQ24257_REG_6, 4, 4),
+       [F_TS_EN]               = REG_FIELD(BQ24257_REG_6, 3, 3),
+       [F_TS_STAT]             = REG_FIELD(BQ24257_REG_6, 0, 2),
+       /* REG 7 */
+       [F_VOVP]                = REG_FIELD(BQ24257_REG_7, 5, 7),
+       [F_CLR_VDP]             = REG_FIELD(BQ24257_REG_7, 4, 4),
+       [F_FORCE_BATDET]        = REG_FIELD(BQ24257_REG_7, 3, 3),
+       [F_FORCE_PTM]           = REG_FIELD(BQ24257_REG_7, 2, 2)
+};
+
+static const u32 bq24257_vbat_map[] = {
+       3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000,
+       3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000,
+       3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000,
+       3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000,
+       4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000,
+       4300000, 4320000, 4340000, 4360000, 4380000, 4400000, 4420000, 4440000
+};
+
+#define BQ24257_VBAT_MAP_SIZE          ARRAY_SIZE(bq24257_vbat_map)
+
+static const u32 bq24257_ichg_map[] = {
+       500000, 550000, 600000, 650000, 700000, 750000, 800000, 850000, 900000,
+       950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000,
+       1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000,
+       1750000, 1800000, 1850000, 1900000, 1950000, 2000000
+};
+
+#define BQ24257_ICHG_MAP_SIZE          ARRAY_SIZE(bq24257_ichg_map)
+
+static const u32 bq24257_iterm_map[] = {
+       50000, 75000, 100000, 125000, 150000, 175000, 200000, 225000
+};
+
+#define BQ24257_ITERM_MAP_SIZE         ARRAY_SIZE(bq24257_iterm_map)
+
+static const u32 bq24257_iilimit_map[] = {
+       100000, 150000, 500000, 900000, 1500000, 2000000
+};
+
+#define BQ24257_IILIMIT_MAP_SIZE       ARRAY_SIZE(bq24257_iilimit_map)
+
+static const u32 bq24257_vovp_map[] = {
+       6000000, 6500000, 7000000, 8000000, 9000000, 9500000, 10000000,
+       10500000
+};
+
+#define BQ24257_VOVP_MAP_SIZE          ARRAY_SIZE(bq24257_vovp_map)
+
+static const u32 bq24257_vindpm_map[] = {
+       4200000, 4280000, 4360000, 4440000, 4520000, 4600000, 4680000,
+       4760000
+};
+
+#define BQ24257_VINDPM_MAP_SIZE                ARRAY_SIZE(bq24257_vindpm_map)
+
+static int bq24257_field_read(struct bq24257_device *bq,
+                             enum bq24257_fields field_id)
+{
+       int ret;
+       int val;
+
+       ret = regmap_field_read(bq->rmap_fields[field_id], &val);
+       if (ret < 0)
+               return ret;
+
+       return val;
+}
+
+static int bq24257_field_write(struct bq24257_device *bq,
+                              enum bq24257_fields field_id, u8 val)
+{
+       return regmap_field_write(bq->rmap_fields[field_id], val);
+}
+
+static u8 bq24257_find_idx(u32 value, const u32 *map, u8 map_size)
+{
+       u8 idx;
+
+       for (idx = 1; idx < map_size; idx++)
+               if (value < map[idx])
+                       break;
+
+       return idx - 1;
+}
+
+enum bq24257_status {
+       STATUS_READY,
+       STATUS_CHARGE_IN_PROGRESS,
+       STATUS_CHARGE_DONE,
+       STATUS_FAULT,
+};
+
+enum bq24257_fault {
+       FAULT_NORMAL,
+       FAULT_INPUT_OVP,
+       FAULT_INPUT_UVLO,
+       FAULT_SLEEP,
+       FAULT_BAT_TS,
+       FAULT_BAT_OVP,
+       FAULT_TS,
+       FAULT_TIMER,
+       FAULT_NO_BAT,
+       FAULT_ISET,
+       FAULT_INPUT_LDO_LOW,
+};
+
+static int bq24257_get_input_current_limit(struct bq24257_device *bq,
+                                          union power_supply_propval *val)
+{
+       int ret;
+
+       ret = bq24257_field_read(bq, F_IILIMIT);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * The "External ILIM" and "Production & Test" modes are not exposed
+        * through this driver and not being covered by the lookup table.
+        * Should such a mode have become active let's return an error rather
+        * than exceeding the bounds of the lookup table and returning
+        * garbage.
+        */
+       if (ret >= BQ24257_IILIMIT_MAP_SIZE)
+               return -ENODATA;
+
+       val->intval = bq24257_iilimit_map[ret];
+
+       return 0;
+}
+
+static int bq24257_set_input_current_limit(struct bq24257_device *bq,
+                                       const union power_supply_propval *val)
+{
+       /*
+        * Address the case where the user manually sets an input current limit
+        * while the charger auto-detection mechanism is is active. In this
+        * case we want to abort and go straight to the user-specified value.
+        */
+       if (bq->iilimit_autoset_enable)
+               cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+       return bq24257_field_write(bq, F_IILIMIT,
+                                  bq24257_find_idx(val->intval,
+                                                   bq24257_iilimit_map,
+                                                   BQ24257_IILIMIT_MAP_SIZE));
+}
+
+static int bq24257_power_supply_get_property(struct power_supply *psy,
+                                            enum power_supply_property psp,
+                                            union power_supply_propval *val)
+{
+       struct bq24257_device *bq = power_supply_get_drvdata(psy);
+       struct bq24257_state state;
+
+       mutex_lock(&bq->lock);
+       state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (!state.power_good)
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (state.status == STATUS_READY)
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else if (state.status == STATUS_CHARGE_IN_PROGRESS)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (state.status == STATUS_CHARGE_DONE)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = BQ24257_MANUFACTURER;
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = bq2425x_chip_name[bq->chip];
+               break;
+
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = state.power_good;
+               break;
+
+       case POWER_SUPPLY_PROP_HEALTH:
+               switch (state.fault) {
+               case FAULT_NORMAL:
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+                       break;
+
+               case FAULT_INPUT_OVP:
+               case FAULT_BAT_OVP:
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+                       break;
+
+               case FAULT_TS:
+               case FAULT_BAT_TS:
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+                       break;
+
+               case FAULT_TIMER:
+                       val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+                       break;
+
+               default:
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+                       break;
+               }
+
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               val->intval = bq24257_ichg_map[bq->init_data.ichg];
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               val->intval = bq24257_ichg_map[BQ24257_ICHG_MAP_SIZE - 1];
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               val->intval = bq24257_vbat_map[bq->init_data.vbat];
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               val->intval = bq24257_vbat_map[BQ24257_VBAT_MAP_SIZE - 1];
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+               val->intval = bq24257_iterm_map[bq->init_data.iterm];
+               break;
+
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               return bq24257_get_input_current_limit(bq, val);
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int bq24257_power_supply_set_property(struct power_supply *psy,
+                                       enum power_supply_property prop,
+                                       const union power_supply_propval *val)
+{
+       struct bq24257_device *bq = power_supply_get_drvdata(psy);
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               return bq24257_set_input_current_limit(bq, val);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int bq24257_power_supply_property_is_writeable(struct power_supply *psy,
+                                       enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static int bq24257_get_chip_state(struct bq24257_device *bq,
+                                 struct bq24257_state *state)
+{
+       int ret;
+
+       ret = bq24257_field_read(bq, F_STAT);
+       if (ret < 0)
+               return ret;
+
+       state->status = ret;
+
+       ret = bq24257_field_read(bq, F_FAULT);
+       if (ret < 0)
+               return ret;
+
+       state->fault = ret;
+
+       if (bq->pg)
+               state->power_good = !gpiod_get_value_cansleep(bq->pg);
+       else
+               /*
+                * If we have a chip without a dedicated power-good GPIO or
+                * some other explicit bit that would provide this information
+                * assume the power is good if there is no supply related
+                * fault - and not good otherwise. There is a possibility for
+                * other errors to mask that power in fact is not good but this
+                * is probably the best we can do here.
+                */
+               switch (state->fault) {
+               case FAULT_INPUT_OVP:
+               case FAULT_INPUT_UVLO:
+               case FAULT_INPUT_LDO_LOW:
+                       state->power_good = false;
+                       break;
+               default:
+                       state->power_good = true;
+               }
+
+       return 0;
+}
+
+static bool bq24257_state_changed(struct bq24257_device *bq,
+                                 struct bq24257_state *new_state)
+{
+       int ret;
+
+       mutex_lock(&bq->lock);
+       ret = (bq->state.status != new_state->status ||
+              bq->state.fault != new_state->fault ||
+              bq->state.power_good != new_state->power_good);
+       mutex_unlock(&bq->lock);
+
+       return ret;
+}
+
+enum bq24257_loop_status {
+       LOOP_STATUS_NONE,
+       LOOP_STATUS_IN_DPM,
+       LOOP_STATUS_IN_CURRENT_LIMIT,
+       LOOP_STATUS_THERMAL,
+};
+
+enum bq24257_in_ilimit {
+       IILIMIT_100,
+       IILIMIT_150,
+       IILIMIT_500,
+       IILIMIT_900,
+       IILIMIT_1500,
+       IILIMIT_2000,
+       IILIMIT_EXT,
+       IILIMIT_NONE,
+};
+
+enum bq24257_vovp {
+       VOVP_6000,
+       VOVP_6500,
+       VOVP_7000,
+       VOVP_8000,
+       VOVP_9000,
+       VOVP_9500,
+       VOVP_10000,
+       VOVP_10500
+};
+
+enum bq24257_vindpm {
+       VINDPM_4200,
+       VINDPM_4280,
+       VINDPM_4360,
+       VINDPM_4440,
+       VINDPM_4520,
+       VINDPM_4600,
+       VINDPM_4680,
+       VINDPM_4760
+};
+
+enum bq24257_port_type {
+       PORT_TYPE_DCP,          /* Dedicated Charging Port */
+       PORT_TYPE_CDP,          /* Charging Downstream Port */
+       PORT_TYPE_SDP,          /* Standard Downstream Port */
+       PORT_TYPE_NON_STANDARD,
+};
+
+enum bq24257_safety_timer {
+       SAFETY_TIMER_45,
+       SAFETY_TIMER_360,
+       SAFETY_TIMER_540,
+       SAFETY_TIMER_NONE,
+};
+
+static int bq24257_iilimit_autoset(struct bq24257_device *bq)
+{
+       int loop_status;
+       int iilimit;
+       int port_type;
+       int ret;
+       const u8 new_iilimit[] = {
+               [PORT_TYPE_DCP] = IILIMIT_2000,
+               [PORT_TYPE_CDP] = IILIMIT_2000,
+               [PORT_TYPE_SDP] = IILIMIT_500,
+               [PORT_TYPE_NON_STANDARD] = IILIMIT_500
+       };
+
+       ret = bq24257_field_read(bq, F_LOOP_STATUS);
+       if (ret < 0)
+               goto error;
+
+       loop_status = ret;
+
+       ret = bq24257_field_read(bq, F_IILIMIT);
+       if (ret < 0)
+               goto error;
+
+       iilimit = ret;
+
+       /*
+        * All USB ports should be able to handle 500mA. If not, DPM will lower
+        * the charging current to accommodate the power source. No need to set
+        * a lower IILIMIT value.
+        */
+       if (loop_status == LOOP_STATUS_IN_DPM && iilimit == IILIMIT_500)
+               return 0;
+
+       ret = bq24257_field_read(bq, F_USB_DET);
+       if (ret < 0)
+               goto error;
+
+       port_type = ret;
+
+       ret = bq24257_field_write(bq, F_IILIMIT, new_iilimit[port_type]);
+       if (ret < 0)
+               goto error;
+
+       ret = bq24257_field_write(bq, F_TMR, SAFETY_TIMER_360);
+       if (ret < 0)
+               goto error;
+
+       ret = bq24257_field_write(bq, F_CLR_VDP, 1);
+       if (ret < 0)
+               goto error;
+
+       dev_dbg(bq->dev, "port/loop = %d/%d -> iilimit = %d\n",
+               port_type, loop_status, new_iilimit[port_type]);
+
+       return 0;
+
+error:
+       dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__);
+       return ret;
+}
+
+static void bq24257_iilimit_setup_work(struct work_struct *work)
+{
+       struct bq24257_device *bq = container_of(work, struct bq24257_device,
+                                                iilimit_setup_work.work);
+
+       bq24257_iilimit_autoset(bq);
+}
+
+static void bq24257_handle_state_change(struct bq24257_device *bq,
+                                       struct bq24257_state *new_state)
+{
+       int ret;
+       struct bq24257_state old_state;
+
+       mutex_lock(&bq->lock);
+       old_state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       /*
+        * Handle BQ2425x state changes observing whether the D+/D- based input
+        * current limit autoset functionality is enabled.
+        */
+       if (!new_state->power_good) {
+               dev_dbg(bq->dev, "Power removed\n");
+               if (bq->iilimit_autoset_enable) {
+                       cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+                       /* activate D+/D- port detection algorithm */
+                       ret = bq24257_field_write(bq, F_DPDM_EN, 1);
+                       if (ret < 0)
+                               goto error;
+               }
+               /*
+                * When power is removed always return to the default input
+                * current limit as configured during probe.
+                */
+               ret = bq24257_field_write(bq, F_IILIMIT, bq->init_data.iilimit);
+               if (ret < 0)
+                       goto error;
+       } else if (!old_state.power_good) {
+               dev_dbg(bq->dev, "Power inserted\n");
+
+               if (bq->iilimit_autoset_enable)
+                       /* configure input current limit */
+                       schedule_delayed_work(&bq->iilimit_setup_work,
+                                     msecs_to_jiffies(BQ24257_ILIM_SET_DELAY));
+       } else if (new_state->fault == FAULT_NO_BAT) {
+               dev_warn(bq->dev, "Battery removed\n");
+       } else if (new_state->fault == FAULT_TIMER) {
+               dev_err(bq->dev, "Safety timer expired! Battery dead?\n");
+       }
+
+       return;
+
+error:
+       dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__);
+}
+
+static irqreturn_t bq24257_irq_handler_thread(int irq, void *private)
+{
+       int ret;
+       struct bq24257_device *bq = private;
+       struct bq24257_state state;
+
+       ret = bq24257_get_chip_state(bq, &state);
+       if (ret < 0)
+               return IRQ_HANDLED;
+
+       if (!bq24257_state_changed(bq, &state))
+               return IRQ_HANDLED;
+
+       dev_dbg(bq->dev, "irq(state changed): status/fault/pg = %d/%d/%d\n",
+               state.status, state.fault, state.power_good);
+
+       bq24257_handle_state_change(bq, &state);
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       power_supply_changed(bq->charger);
+
+       return IRQ_HANDLED;
+}
+
+static int bq24257_hw_init(struct bq24257_device *bq)
+{
+       int ret;
+       int i;
+       struct bq24257_state state;
+
+       const struct {
+               int field;
+               u32 value;
+       } init_data[] = {
+               {F_ICHG, bq->init_data.ichg},
+               {F_VBAT, bq->init_data.vbat},
+               {F_ITERM, bq->init_data.iterm},
+               {F_VOVP, bq->init_data.vovp},
+               {F_VINDPM, bq->init_data.vindpm},
+       };
+
+       /*
+        * Disable the watchdog timer to prevent the IC from going back to
+        * default settings after 50 seconds of I2C inactivity.
+        */
+       ret = bq24257_field_write(bq, F_WD_EN, 0);
+       if (ret < 0)
+               return ret;
+
+       /* configure the charge currents and voltages */
+       for (i = 0; i < ARRAY_SIZE(init_data); i++) {
+               ret = bq24257_field_write(bq, init_data[i].field,
+                                         init_data[i].value);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = bq24257_get_chip_state(bq, &state);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       if (!bq->iilimit_autoset_enable) {
+               dev_dbg(bq->dev, "manually setting iilimit = %u\n",
+                       bq->init_data.iilimit);
+
+               /* program fixed input current limit */
+               ret = bq24257_field_write(bq, F_IILIMIT,
+                                         bq->init_data.iilimit);
+               if (ret < 0)
+                       return ret;
+       } else if (!state.power_good)
+               /* activate D+/D- detection algorithm */
+               ret = bq24257_field_write(bq, F_DPDM_EN, 1);
+       else if (state.fault != FAULT_NO_BAT)
+               ret = bq24257_iilimit_autoset(bq);
+
+       return ret;
+}
+
+static enum power_supply_property bq24257_power_supply_props[] = {
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+       POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+};
+
+static char *bq24257_charger_supplied_to[] = {
+       "main-battery",
+};
+
+static const struct power_supply_desc bq24257_power_supply_desc = {
+       .name = "bq24257-charger",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = bq24257_power_supply_props,
+       .num_properties = ARRAY_SIZE(bq24257_power_supply_props),
+       .get_property = bq24257_power_supply_get_property,
+       .set_property = bq24257_power_supply_set_property,
+       .property_is_writeable = bq24257_power_supply_property_is_writeable,
+};
+
+static ssize_t bq24257_show_ovp_voltage(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq24257_device *bq = power_supply_get_drvdata(psy);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n",
+                        bq24257_vovp_map[bq->init_data.vovp]);
+}
+
+static ssize_t bq24257_show_in_dpm_voltage(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq24257_device *bq = power_supply_get_drvdata(psy);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n",
+                        bq24257_vindpm_map[bq->init_data.vindpm]);
+}
+
+static ssize_t bq24257_sysfs_show_enable(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq24257_device *bq = power_supply_get_drvdata(psy);
+       int ret;
+
+       if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
+               ret = bq24257_field_read(bq, F_HZ_MODE);
+       else if (strcmp(attr->attr.name, "sysoff_enable") == 0)
+               ret = bq24257_field_read(bq, F_SYSOFF);
+       else
+               return -EINVAL;
+
+       if (ret < 0)
+               return ret;
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t bq24257_sysfs_set_enable(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf,
+                                       size_t count)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct bq24257_device *bq = power_supply_get_drvdata(psy);
+       long val;
+       int ret;
+
+       if (kstrtol(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
+               ret = bq24257_field_write(bq, F_HZ_MODE, (bool)val);
+       else if (strcmp(attr->attr.name, "sysoff_enable") == 0)
+               ret = bq24257_field_write(bq, F_SYSOFF, (bool)val);
+       else
+               return -EINVAL;
+
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(ovp_voltage, S_IRUGO, bq24257_show_ovp_voltage, NULL);
+static DEVICE_ATTR(in_dpm_voltage, S_IRUGO, bq24257_show_in_dpm_voltage, NULL);
+static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
+                  bq24257_sysfs_show_enable, bq24257_sysfs_set_enable);
+static DEVICE_ATTR(sysoff_enable, S_IWUSR | S_IRUGO,
+                  bq24257_sysfs_show_enable, bq24257_sysfs_set_enable);
+
+static struct attribute *bq24257_charger_attr[] = {
+       &dev_attr_ovp_voltage.attr,
+       &dev_attr_in_dpm_voltage.attr,
+       &dev_attr_high_impedance_enable.attr,
+       &dev_attr_sysoff_enable.attr,
+       NULL,
+};
+
+static const struct attribute_group bq24257_attr_group = {
+       .attrs = bq24257_charger_attr,
+};
+
+static int bq24257_power_supply_init(struct bq24257_device *bq)
+{
+       struct power_supply_config psy_cfg = { .drv_data = bq, };
+
+       psy_cfg.supplied_to = bq24257_charger_supplied_to;
+       psy_cfg.num_supplicants = ARRAY_SIZE(bq24257_charger_supplied_to);
+
+       bq->charger = devm_power_supply_register(bq->dev,
+                                                &bq24257_power_supply_desc,
+                                                &psy_cfg);
+
+       return PTR_ERR_OR_ZERO(bq->charger);
+}
+
+static void bq24257_pg_gpio_probe(struct bq24257_device *bq)
+{
+       bq->pg = devm_gpiod_get_optional(bq->dev, BQ24257_PG_GPIO, GPIOD_IN);
+
+       if (PTR_ERR(bq->pg) == -EPROBE_DEFER) {
+               dev_info(bq->dev, "probe retry requested for PG pin\n");
+               return;
+       } else if (IS_ERR(bq->pg)) {
+               dev_err(bq->dev, "error probing PG pin\n");
+               bq->pg = NULL;
+               return;
+       }
+
+       if (bq->pg)
+               dev_dbg(bq->dev, "probed PG pin = %d\n", desc_to_gpio(bq->pg));
+}
+
+static int bq24257_fw_probe(struct bq24257_device *bq)
+{
+       int ret;
+       u32 property;
+
+       /* Required properties */
+       ret = device_property_read_u32(bq->dev, "ti,charge-current", &property);
+       if (ret < 0)
+               return ret;
+
+       bq->init_data.ichg = bq24257_find_idx(property, bq24257_ichg_map,
+                                             BQ24257_ICHG_MAP_SIZE);
+
+       ret = device_property_read_u32(bq->dev, "ti,battery-regulation-voltage",
+                                      &property);
+       if (ret < 0)
+               return ret;
+
+       bq->init_data.vbat = bq24257_find_idx(property, bq24257_vbat_map,
+                                             BQ24257_VBAT_MAP_SIZE);
+
+       ret = device_property_read_u32(bq->dev, "ti,termination-current",
+                                      &property);
+       if (ret < 0)
+               return ret;
+
+       bq->init_data.iterm = bq24257_find_idx(property, bq24257_iterm_map,
+                                              BQ24257_ITERM_MAP_SIZE);
+
+       /* Optional properties. If not provided use reasonable default. */
+       ret = device_property_read_u32(bq->dev, "ti,current-limit",
+                                      &property);
+       if (ret < 0) {
+               bq->iilimit_autoset_enable = true;
+
+               /*
+                * Explicitly set a default value which will be needed for
+                * devices that don't support the automatic setting of the input
+                * current limit through the charger type detection mechanism.
+                */
+               bq->init_data.iilimit = IILIMIT_500;
+       } else
+               bq->init_data.iilimit =
+                               bq24257_find_idx(property,
+                                                bq24257_iilimit_map,
+                                                BQ24257_IILIMIT_MAP_SIZE);
+
+       ret = device_property_read_u32(bq->dev, "ti,ovp-voltage",
+                                      &property);
+       if (ret < 0)
+               bq->init_data.vovp = VOVP_6500;
+       else
+               bq->init_data.vovp = bq24257_find_idx(property,
+                                                     bq24257_vovp_map,
+                                                     BQ24257_VOVP_MAP_SIZE);
+
+       ret = device_property_read_u32(bq->dev, "ti,in-dpm-voltage",
+                                      &property);
+       if (ret < 0)
+               bq->init_data.vindpm = VINDPM_4360;
+       else
+               bq->init_data.vindpm =
+                               bq24257_find_idx(property,
+                                                bq24257_vindpm_map,
+                                                BQ24257_VINDPM_MAP_SIZE);
+
+       return 0;
+}
+
+static int bq24257_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct device *dev = &client->dev;
+       const struct acpi_device_id *acpi_id;
+       struct bq24257_device *bq;
+       int ret;
+       int i;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+               return -ENODEV;
+       }
+
+       bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
+       if (!bq)
+               return -ENOMEM;
+
+       bq->client = client;
+       bq->dev = dev;
+
+       if (ACPI_HANDLE(dev)) {
+               acpi_id = acpi_match_device(dev->driver->acpi_match_table,
+                                           &client->dev);
+               if (!acpi_id) {
+                       dev_err(dev, "Failed to match ACPI device\n");
+                       return -ENODEV;
+               }
+               bq->chip = (enum bq2425x_chip)acpi_id->driver_data;
+       } else {
+               bq->chip = (enum bq2425x_chip)id->driver_data;
+       }
+
+       mutex_init(&bq->lock);
+
+       bq->rmap = devm_regmap_init_i2c(client, &bq24257_regmap_config);
+       if (IS_ERR(bq->rmap)) {
+               dev_err(dev, "failed to allocate register map\n");
+               return PTR_ERR(bq->rmap);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(bq24257_reg_fields); i++) {
+               const struct reg_field *reg_fields = bq24257_reg_fields;
+
+               bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
+                                                            reg_fields[i]);
+               if (IS_ERR(bq->rmap_fields[i])) {
+                       dev_err(dev, "cannot allocate regmap field\n");
+                       return PTR_ERR(bq->rmap_fields[i]);
+               }
+       }
+
+       i2c_set_clientdata(client, bq);
+
+       if (!dev->platform_data) {
+               ret = bq24257_fw_probe(bq);
+               if (ret < 0) {
+                       dev_err(dev, "Cannot read device properties.\n");
+                       return ret;
+               }
+       } else {
+               return -ENODEV;
+       }
+
+       /*
+        * The BQ24250 doesn't support the D+/D- based charger type detection
+        * used for the automatic setting of the input current limit setting so
+        * explicitly disable that feature.
+        */
+       if (bq->chip == BQ24250)
+               bq->iilimit_autoset_enable = false;
+
+       if (bq->iilimit_autoset_enable)
+               INIT_DELAYED_WORK(&bq->iilimit_setup_work,
+                                 bq24257_iilimit_setup_work);
+
+       /*
+        * The BQ24250 doesn't have a dedicated Power Good (PG) pin so let's
+        * not probe for it and instead use a SW-based approach to determine
+        * the PG state. We also use a SW-based approach for all other devices
+        * if the PG pin is either not defined or can't be probed.
+        */
+       if (bq->chip != BQ24250)
+               bq24257_pg_gpio_probe(bq);
+
+       if (PTR_ERR(bq->pg) == -EPROBE_DEFER)
+               return PTR_ERR(bq->pg);
+       else if (!bq->pg)
+               dev_info(bq->dev, "using SW-based power-good detection\n");
+
+       /* reset all registers to defaults */
+       ret = bq24257_field_write(bq, F_RESET, 1);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Put the RESET bit back to 0, in cache. For some reason the HW always
+        * returns 1 on this bit, so this is the only way to avoid resetting the
+        * chip every time we update another field in this register.
+        */
+       ret = bq24257_field_write(bq, F_RESET, 0);
+       if (ret < 0)
+               return ret;
+
+       ret = bq24257_hw_init(bq);
+       if (ret < 0) {
+               dev_err(dev, "Cannot initialize the chip.\n");
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(dev, client->irq, NULL,
+                                       bq24257_irq_handler_thread,
+                                       IRQF_TRIGGER_FALLING |
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       bq2425x_chip_name[bq->chip], bq);
+       if (ret) {
+               dev_err(dev, "Failed to request IRQ #%d\n", client->irq);
+               return ret;
+       }
+
+       ret = bq24257_power_supply_init(bq);
+       if (ret < 0) {
+               dev_err(dev, "Failed to register power supply\n");
+               return ret;
+       }
+
+       ret = sysfs_create_group(&bq->charger->dev.kobj, &bq24257_attr_group);
+       if (ret < 0) {
+               dev_err(dev, "Can't create sysfs entries\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bq24257_remove(struct i2c_client *client)
+{
+       struct bq24257_device *bq = i2c_get_clientdata(client);
+
+       if (bq->iilimit_autoset_enable)
+               cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+       sysfs_remove_group(&bq->charger->dev.kobj, &bq24257_attr_group);
+
+       bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bq24257_suspend(struct device *dev)
+{
+       struct bq24257_device *bq = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (bq->iilimit_autoset_enable)
+               cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+       /* reset all registers to default (and activate standalone mode) */
+       ret = bq24257_field_write(bq, F_RESET, 1);
+       if (ret < 0)
+               dev_err(bq->dev, "Cannot reset chip to standalone mode.\n");
+
+       return ret;
+}
+
+static int bq24257_resume(struct device *dev)
+{
+       int ret;
+       struct bq24257_device *bq = dev_get_drvdata(dev);
+
+       ret = regcache_drop_region(bq->rmap, BQ24257_REG_1, BQ24257_REG_7);
+       if (ret < 0)
+               return ret;
+
+       ret = bq24257_field_write(bq, F_RESET, 0);
+       if (ret < 0)
+               return ret;
+
+       ret = bq24257_hw_init(bq);
+       if (ret < 0) {
+               dev_err(bq->dev, "Cannot init chip after resume.\n");
+               return ret;
+       }
+
+       /* signal userspace, maybe state changed while suspended */
+       power_supply_changed(bq->charger);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops bq24257_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(bq24257_suspend, bq24257_resume)
+};
+
+static const struct i2c_device_id bq24257_i2c_ids[] = {
+       { "bq24250", BQ24250 },
+       { "bq24251", BQ24251 },
+       { "bq24257", BQ24257 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids);
+
+static const struct of_device_id bq24257_of_match[] = {
+       { .compatible = "ti,bq24250", },
+       { .compatible = "ti,bq24251", },
+       { .compatible = "ti,bq24257", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, bq24257_of_match);
+
+static const struct acpi_device_id bq24257_acpi_match[] = {
+       { "BQ242500", BQ24250 },
+       { "BQ242510", BQ24251 },
+       { "BQ242570", BQ24257 },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, bq24257_acpi_match);
+
+static struct i2c_driver bq24257_driver = {
+       .driver = {
+               .name = "bq24257-charger",
+               .of_match_table = of_match_ptr(bq24257_of_match),
+               .acpi_match_table = ACPI_PTR(bq24257_acpi_match),
+               .pm = &bq24257_pm,
+       },
+       .probe = bq24257_probe,
+       .remove = bq24257_remove,
+       .id_table = bq24257_i2c_ids,
+};
+module_i2c_driver(bq24257_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
+MODULE_DESCRIPTION("bq24257 charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c
new file mode 100644 (file)
index 0000000..fa454c1
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * Battery charger driver for TI BQ24735
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * 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;
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#include <linux/power/bq24735-charger.h>
+
+#define BQ24735_CHG_OPT                        0x12
+#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0)
+#define BQ24735_CHG_OPT_AC_PRESENT     (1 << 4)
+#define BQ24735_CHARGE_CURRENT         0x14
+#define BQ24735_CHARGE_CURRENT_MASK    0x1fc0
+#define BQ24735_CHARGE_VOLTAGE         0x15
+#define BQ24735_CHARGE_VOLTAGE_MASK    0x7ff0
+#define BQ24735_INPUT_CURRENT          0x3f
+#define BQ24735_INPUT_CURRENT_MASK     0x1f80
+#define BQ24735_MANUFACTURER_ID                0xfe
+#define BQ24735_DEVICE_ID              0xff
+
+struct bq24735 {
+       struct power_supply             *charger;
+       struct power_supply_desc        charger_desc;
+       struct i2c_client               *client;
+       struct bq24735_platform         *pdata;
+       struct mutex                    lock;
+       bool                            charging;
+};
+
+static inline struct bq24735 *to_bq24735(struct power_supply *psy)
+{
+       return power_supply_get_drvdata(psy);
+}
+
+static enum power_supply_property bq24735_charger_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int bq24735_charger_property_is_writeable(struct power_supply *psy,
+                                                enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               return 1;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static inline int bq24735_write_word(struct i2c_client *client, u8 reg,
+                                    u16 value)
+{
+       return i2c_smbus_write_word_data(client, reg, le16_to_cpu(value));
+}
+
+static inline int bq24735_read_word(struct i2c_client *client, u8 reg)
+{
+       s32 ret = i2c_smbus_read_word_data(client, reg);
+
+       return ret < 0 ? ret : le16_to_cpu(ret);
+}
+
+static int bq24735_update_word(struct i2c_client *client, u8 reg,
+                              u16 mask, u16 value)
+{
+       unsigned int tmp;
+       int ret;
+
+       ret = bq24735_read_word(client, reg);
+       if (ret < 0)
+               return ret;
+
+       tmp = ret & ~mask;
+       tmp |= value & mask;
+
+       return bq24735_write_word(client, reg, tmp);
+}
+
+static inline int bq24735_enable_charging(struct bq24735 *charger)
+{
+       if (charger->pdata->ext_control)
+               return 0;
+
+       return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
+                                  BQ24735_CHG_OPT_CHARGE_DISABLE,
+                                  ~BQ24735_CHG_OPT_CHARGE_DISABLE);
+}
+
+static inline int bq24735_disable_charging(struct bq24735 *charger)
+{
+       if (charger->pdata->ext_control)
+               return 0;
+
+       return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
+                                  BQ24735_CHG_OPT_CHARGE_DISABLE,
+                                  BQ24735_CHG_OPT_CHARGE_DISABLE);
+}
+
+static int bq24735_config_charger(struct bq24735 *charger)
+{
+       struct bq24735_platform *pdata = charger->pdata;
+       int ret;
+       u16 value;
+
+       if (pdata->ext_control)
+               return 0;
+
+       if (pdata->charge_current) {
+               value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK;
+
+               ret = bq24735_write_word(charger->client,
+                                        BQ24735_CHARGE_CURRENT, value);
+               if (ret < 0) {
+                       dev_err(&charger->client->dev,
+                               "Failed to write charger current : %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       if (pdata->charge_voltage) {
+               value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK;
+
+               ret = bq24735_write_word(charger->client,
+                                        BQ24735_CHARGE_VOLTAGE, value);
+               if (ret < 0) {
+                       dev_err(&charger->client->dev,
+                               "Failed to write charger voltage : %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       if (pdata->input_current) {
+               value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK;
+
+               ret = bq24735_write_word(charger->client,
+                                        BQ24735_INPUT_CURRENT, value);
+               if (ret < 0) {
+                       dev_err(&charger->client->dev,
+                               "Failed to write input current : %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static bool bq24735_charger_is_present(struct bq24735 *charger)
+{
+       struct bq24735_platform *pdata = charger->pdata;
+       int ret;
+
+       if (pdata->status_gpio_valid) {
+               ret = gpio_get_value_cansleep(pdata->status_gpio);
+               return ret ^= pdata->status_gpio_active_low == 0;
+       } else {
+               int ac = 0;
+
+               ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
+               if (ac < 0) {
+                       dev_err(&charger->client->dev,
+                               "Failed to read charger options : %d\n",
+                               ac);
+                       return false;
+               }
+               return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
+       }
+
+       return false;
+}
+
+static int bq24735_charger_is_charging(struct bq24735 *charger)
+{
+       int ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
+
+       if (ret < 0)
+               return ret;
+
+       return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
+}
+
+static irqreturn_t bq24735_charger_isr(int irq, void *devid)
+{
+       struct power_supply *psy = devid;
+       struct bq24735 *charger = to_bq24735(psy);
+
+       mutex_lock(&charger->lock);
+
+       if (charger->charging && bq24735_charger_is_present(charger))
+               bq24735_enable_charging(charger);
+       else
+               bq24735_disable_charging(charger);
+
+       mutex_unlock(&charger->lock);
+
+       power_supply_changed(psy);
+
+       return IRQ_HANDLED;
+}
+
+static int bq24735_charger_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct bq24735 *charger = to_bq24735(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = bq24735_charger_is_present(charger) ? 1 : 0;
+               break;
+       case POWER_SUPPLY_PROP_STATUS:
+               switch (bq24735_charger_is_charging(charger)) {
+               case 1:
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+                       break;
+               case 0:
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+                       break;
+               default:
+                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+                       break;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int bq24735_charger_set_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       const union power_supply_propval *val)
+{
+       struct bq24735 *charger = to_bq24735(psy);
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               switch (val->intval) {
+               case POWER_SUPPLY_STATUS_CHARGING:
+                       mutex_lock(&charger->lock);
+                       charger->charging = true;
+                       ret = bq24735_enable_charging(charger);
+                       mutex_unlock(&charger->lock);
+                       if (ret)
+                               return ret;
+                       bq24735_config_charger(charger);
+                       break;
+               case POWER_SUPPLY_STATUS_DISCHARGING:
+               case POWER_SUPPLY_STATUS_NOT_CHARGING:
+                       mutex_lock(&charger->lock);
+                       charger->charging = false;
+                       ret = bq24735_disable_charging(charger);
+                       mutex_unlock(&charger->lock);
+                       if (ret)
+                               return ret;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               power_supply_changed(psy);
+               break;
+       default:
+               return -EPERM;
+       }
+
+       return 0;
+}
+
+static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client)
+{
+       struct bq24735_platform *pdata;
+       struct device_node *np = client->dev.of_node;
+       u32 val;
+       int ret;
+       enum of_gpio_flags flags;
+
+       pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata) {
+               dev_err(&client->dev,
+                       "Memory alloc for bq24735 pdata failed\n");
+               return NULL;
+       }
+
+       pdata->status_gpio = of_get_named_gpio_flags(np, "ti,ac-detect-gpios",
+                                                    0, &flags);
+
+       if (flags & OF_GPIO_ACTIVE_LOW)
+               pdata->status_gpio_active_low = 1;
+
+       ret = of_property_read_u32(np, "ti,charge-current", &val);
+       if (!ret)
+               pdata->charge_current = val;
+
+       ret = of_property_read_u32(np, "ti,charge-voltage", &val);
+       if (!ret)
+               pdata->charge_voltage = val;
+
+       ret = of_property_read_u32(np, "ti,input-current", &val);
+       if (!ret)
+               pdata->input_current = val;
+
+       pdata->ext_control = of_property_read_bool(np, "ti,external-control");
+
+       return pdata;
+}
+
+static int bq24735_charger_probe(struct i2c_client *client,
+                                const struct i2c_device_id *id)
+{
+       int ret;
+       struct bq24735 *charger;
+       struct power_supply_desc *supply_desc;
+       struct power_supply_config psy_cfg = {};
+       char *name;
+
+       charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);
+       if (!charger)
+               return -ENOMEM;
+
+       mutex_init(&charger->lock);
+       charger->charging = true;
+       charger->pdata = client->dev.platform_data;
+
+       if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)
+               charger->pdata = bq24735_parse_dt_data(client);
+
+       if (!charger->pdata) {
+               dev_err(&client->dev, "no platform data provided\n");
+               return -EINVAL;
+       }
+
+       name = (char *)charger->pdata->name;
+       if (!name) {
+               name = devm_kasprintf(&client->dev, GFP_KERNEL,
+                                     "bq24735@%s",
+                                     dev_name(&client->dev));
+               if (!name) {
+                       dev_err(&client->dev, "Failed to alloc device name\n");
+                       return -ENOMEM;
+               }
+       }
+
+       charger->client = client;
+
+       supply_desc = &charger->charger_desc;
+
+       supply_desc->name = name;
+       supply_desc->type = POWER_SUPPLY_TYPE_MAINS;
+       supply_desc->properties = bq24735_charger_properties;
+       supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties);
+       supply_desc->get_property = bq24735_charger_get_property;
+       supply_desc->set_property = bq24735_charger_set_property;
+       supply_desc->property_is_writeable =
+                               bq24735_charger_property_is_writeable;
+
+       psy_cfg.supplied_to = charger->pdata->supplied_to;
+       psy_cfg.num_supplicants = charger->pdata->num_supplicants;
+       psy_cfg.of_node = client->dev.of_node;
+       psy_cfg.drv_data = charger;
+
+       i2c_set_clientdata(client, charger);
+
+       if (gpio_is_valid(charger->pdata->status_gpio)) {
+               ret = devm_gpio_request(&client->dev,
+                                       charger->pdata->status_gpio,
+                                       name);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "Failed GPIO request for GPIO %d: %d\n",
+                               charger->pdata->status_gpio, ret);
+               }
+
+               charger->pdata->status_gpio_valid = !ret;
+       }
+
+       if (!charger->pdata->status_gpio_valid
+           || bq24735_charger_is_present(charger)) {
+               ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
+                               ret);
+                       return ret;
+               } else if (ret != 0x0040) {
+                       dev_err(&client->dev,
+                               "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
+                       return -ENODEV;
+               }
+
+               ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Failed to read device id : %d\n", ret);
+                       return ret;
+               } else if (ret != 0x000B) {
+                       dev_err(&client->dev,
+                               "device id mismatch. 0x000b != 0x%04x\n", ret);
+                       return -ENODEV;
+               }
+       }
+
+       ret = bq24735_config_charger(charger);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed in configuring charger");
+               return ret;
+       }
+
+       /* check for AC adapter presence */
+       if (bq24735_charger_is_present(charger)) {
+               ret = bq24735_enable_charging(charger);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Failed to enable charging\n");
+                       return ret;
+               }
+       }
+
+       charger->charger = devm_power_supply_register(&client->dev, supply_desc,
+                                                     &psy_cfg);
+       if (IS_ERR(charger->charger)) {
+               ret = PTR_ERR(charger->charger);
+               dev_err(&client->dev, "Failed to register power supply: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (client->irq) {
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                               NULL, bq24735_charger_isr,
+                                               IRQF_TRIGGER_RISING |
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_ONESHOT,
+                                               supply_desc->name,
+                                               charger->charger);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "Unable to register IRQ %d err %d\n",
+                               client->irq, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct i2c_device_id bq24735_charger_id[] = {
+       { "bq24735-charger", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);
+
+static const struct of_device_id bq24735_match_ids[] = {
+       { .compatible = "ti,bq24735", },
+       { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, bq24735_match_ids);
+
+static struct i2c_driver bq24735_charger_driver = {
+       .driver = {
+               .name = "bq24735-charger",
+               .of_match_table = bq24735_match_ids,
+       },
+       .probe = bq24735_charger_probe,
+       .id_table = bq24735_charger_id,
+};
+
+module_i2c_driver(bq24735_charger_driver);
+
+MODULE_DESCRIPTION("bq24735 battery charging driver");
+MODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c
new file mode 100644 (file)
index 0000000..f993a55
--- /dev/null
@@ -0,0 +1,994 @@
+/*
+ * TI BQ25890 charger driver
+ *
+ * Copyright (C) 2015 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/usb/phy.h>
+
+#include <linux/acpi.h>
+#include <linux/of.h>
+
+#define BQ25890_MANUFACTURER           "Texas Instruments"
+#define BQ25890_IRQ_PIN                        "bq25890_irq"
+
+#define BQ25890_ID                     3
+
+enum bq25890_fields {
+       F_EN_HIZ, F_EN_ILIM, F_IILIM,                                /* Reg00 */
+       F_BHOT, F_BCOLD, F_VINDPM_OFS,                               /* Reg01 */
+       F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN,
+       F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN,          /* Reg02 */
+       F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN,    /* Reg03 */
+       F_PUMPX_EN, F_ICHG,                                          /* Reg04 */
+       F_IPRECHG, F_ITERM,                                          /* Reg05 */
+       F_VREG, F_BATLOWV, F_VRECHG,                                 /* Reg06 */
+       F_TERM_EN, F_STAT_DIS, F_WD, F_TMR_EN, F_CHG_TMR,
+       F_JEITA_ISET,                                                /* Reg07 */
+       F_BATCMP, F_VCLAMP, F_TREG,                                  /* Reg08 */
+       F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET,
+       F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN,       /* Reg09 */
+       F_BOOSTV, F_BOOSTI,                                          /* Reg0A */
+       F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_VSYS_STAT, /* Reg0B */
+       F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT,
+       F_NTC_FAULT,                                                 /* Reg0C */
+       F_FORCE_VINDPM, F_VINDPM,                                    /* Reg0D */
+       F_THERM_STAT, F_BATV,                                        /* Reg0E */
+       F_SYSV,                                                      /* Reg0F */
+       F_TSPCT,                                                     /* Reg10 */
+       F_VBUS_GD, F_VBUSV,                                          /* Reg11 */
+       F_ICHGR,                                                     /* Reg12 */
+       F_VDPM_STAT, F_IDPM_STAT, F_IDPM_LIM,                        /* Reg13 */
+       F_REG_RST, F_ICO_OPTIMIZED, F_PN, F_TS_PROFILE, F_DEV_REV,   /* Reg14 */
+
+       F_MAX_FIELDS
+};
+
+/* initial field values, converted to register values */
+struct bq25890_init_data {
+       u8 ichg;        /* charge current               */
+       u8 vreg;        /* regulation voltage           */
+       u8 iterm;       /* termination current          */
+       u8 iprechg;     /* precharge current            */
+       u8 sysvmin;     /* minimum system voltage limit */
+       u8 boostv;      /* boost regulation voltage     */
+       u8 boosti;      /* boost current limit          */
+       u8 boostf;      /* boost frequency              */
+       u8 ilim_en;     /* enable ILIM pin              */
+       u8 treg;        /* thermal regulation threshold */
+};
+
+struct bq25890_state {
+       u8 online;
+       u8 chrg_status;
+       u8 chrg_fault;
+       u8 vsys_status;
+       u8 boost_fault;
+       u8 bat_fault;
+};
+
+struct bq25890_device {
+       struct i2c_client *client;
+       struct device *dev;
+       struct power_supply *charger;
+
+       struct usb_phy *usb_phy;
+       struct notifier_block usb_nb;
+       struct work_struct usb_work;
+       unsigned long usb_event;
+
+       struct regmap *rmap;
+       struct regmap_field *rmap_fields[F_MAX_FIELDS];
+
+       int chip_id;
+       struct bq25890_init_data init_data;
+       struct bq25890_state state;
+
+       struct mutex lock; /* protect state data */
+};
+
+static const struct regmap_range bq25890_readonly_reg_ranges[] = {
+       regmap_reg_range(0x0b, 0x0c),
+       regmap_reg_range(0x0e, 0x13),
+};
+
+static const struct regmap_access_table bq25890_writeable_regs = {
+       .no_ranges = bq25890_readonly_reg_ranges,
+       .n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges),
+};
+
+static const struct regmap_range bq25890_volatile_reg_ranges[] = {
+       regmap_reg_range(0x00, 0x00),
+       regmap_reg_range(0x09, 0x09),
+       regmap_reg_range(0x0b, 0x0c),
+       regmap_reg_range(0x0e, 0x14),
+};
+
+static const struct regmap_access_table bq25890_volatile_regs = {
+       .yes_ranges = bq25890_volatile_reg_ranges,
+       .n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges),
+};
+
+static const struct regmap_config bq25890_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = 0x14,
+       .cache_type = REGCACHE_RBTREE,
+
+       .wr_table = &bq25890_writeable_regs,
+       .volatile_table = &bq25890_volatile_regs,
+};
+
+static const struct reg_field bq25890_reg_fields[] = {
+       /* REG00 */
+       [F_EN_HIZ]              = REG_FIELD(0x00, 7, 7),
+       [F_EN_ILIM]             = REG_FIELD(0x00, 6, 6),
+       [F_IILIM]               = REG_FIELD(0x00, 0, 5),
+       /* REG01 */
+       [F_BHOT]                = REG_FIELD(0x01, 6, 7),
+       [F_BCOLD]               = REG_FIELD(0x01, 5, 5),
+       [F_VINDPM_OFS]          = REG_FIELD(0x01, 0, 4),
+       /* REG02 */
+       [F_CONV_START]          = REG_FIELD(0x02, 7, 7),
+       [F_CONV_RATE]           = REG_FIELD(0x02, 6, 6),
+       [F_BOOSTF]              = REG_FIELD(0x02, 5, 5),
+       [F_ICO_EN]              = REG_FIELD(0x02, 4, 4),
+       [F_HVDCP_EN]            = REG_FIELD(0x02, 3, 3),
+       [F_MAXC_EN]             = REG_FIELD(0x02, 2, 2),
+       [F_FORCE_DPM]           = REG_FIELD(0x02, 1, 1),
+       [F_AUTO_DPDM_EN]        = REG_FIELD(0x02, 0, 0),
+       /* REG03 */
+       [F_BAT_LOAD_EN]         = REG_FIELD(0x03, 7, 7),
+       [F_WD_RST]              = REG_FIELD(0x03, 6, 6),
+       [F_OTG_CFG]             = REG_FIELD(0x03, 5, 5),
+       [F_CHG_CFG]             = REG_FIELD(0x03, 4, 4),
+       [F_SYSVMIN]             = REG_FIELD(0x03, 1, 3),
+       /* REG04 */
+       [F_PUMPX_EN]            = REG_FIELD(0x04, 7, 7),
+       [F_ICHG]                = REG_FIELD(0x04, 0, 6),
+       /* REG05 */
+       [F_IPRECHG]             = REG_FIELD(0x05, 4, 7),
+       [F_ITERM]               = REG_FIELD(0x05, 0, 3),
+       /* REG06 */
+       [F_VREG]                = REG_FIELD(0x06, 2, 7),
+       [F_BATLOWV]             = REG_FIELD(0x06, 1, 1),
+       [F_VRECHG]              = REG_FIELD(0x06, 0, 0),
+       /* REG07 */
+       [F_TERM_EN]             = REG_FIELD(0x07, 7, 7),
+       [F_STAT_DIS]            = REG_FIELD(0x07, 6, 6),
+       [F_WD]                  = REG_FIELD(0x07, 4, 5),
+       [F_TMR_EN]              = REG_FIELD(0x07, 3, 3),
+       [F_CHG_TMR]             = REG_FIELD(0x07, 1, 2),
+       [F_JEITA_ISET]          = REG_FIELD(0x07, 0, 0),
+       /* REG08 */
+       [F_BATCMP]              = REG_FIELD(0x08, 6, 7),
+       [F_VCLAMP]              = REG_FIELD(0x08, 2, 4),
+       [F_TREG]                = REG_FIELD(0x08, 0, 1),
+       /* REG09 */
+       [F_FORCE_ICO]           = REG_FIELD(0x09, 7, 7),
+       [F_TMR2X_EN]            = REG_FIELD(0x09, 6, 6),
+       [F_BATFET_DIS]          = REG_FIELD(0x09, 5, 5),
+       [F_JEITA_VSET]          = REG_FIELD(0x09, 4, 4),
+       [F_BATFET_DLY]          = REG_FIELD(0x09, 3, 3),
+       [F_BATFET_RST_EN]       = REG_FIELD(0x09, 2, 2),
+       [F_PUMPX_UP]            = REG_FIELD(0x09, 1, 1),
+       [F_PUMPX_DN]            = REG_FIELD(0x09, 0, 0),
+       /* REG0A */
+       [F_BOOSTV]              = REG_FIELD(0x0A, 4, 7),
+       [F_BOOSTI]              = REG_FIELD(0x0A, 0, 2),
+       /* REG0B */
+       [F_VBUS_STAT]           = REG_FIELD(0x0B, 5, 7),
+       [F_CHG_STAT]            = REG_FIELD(0x0B, 3, 4),
+       [F_PG_STAT]             = REG_FIELD(0x0B, 2, 2),
+       [F_SDP_STAT]            = REG_FIELD(0x0B, 1, 1),
+       [F_VSYS_STAT]           = REG_FIELD(0x0B, 0, 0),
+       /* REG0C */
+       [F_WD_FAULT]            = REG_FIELD(0x0C, 7, 7),
+       [F_BOOST_FAULT]         = REG_FIELD(0x0C, 6, 6),
+       [F_CHG_FAULT]           = REG_FIELD(0x0C, 4, 5),
+       [F_BAT_FAULT]           = REG_FIELD(0x0C, 3, 3),
+       [F_NTC_FAULT]           = REG_FIELD(0x0C, 0, 2),
+       /* REG0D */
+       [F_FORCE_VINDPM]        = REG_FIELD(0x0D, 7, 7),
+       [F_VINDPM]              = REG_FIELD(0x0D, 0, 6),
+       /* REG0E */
+       [F_THERM_STAT]          = REG_FIELD(0x0E, 7, 7),
+       [F_BATV]                = REG_FIELD(0x0E, 0, 6),
+       /* REG0F */
+       [F_SYSV]                = REG_FIELD(0x0F, 0, 6),
+       /* REG10 */
+       [F_TSPCT]               = REG_FIELD(0x10, 0, 6),
+       /* REG11 */
+       [F_VBUS_GD]             = REG_FIELD(0x11, 7, 7),
+       [F_VBUSV]               = REG_FIELD(0x11, 0, 6),
+       /* REG12 */
+       [F_ICHGR]               = REG_FIELD(0x12, 0, 6),
+       /* REG13 */
+       [F_VDPM_STAT]           = REG_FIELD(0x13, 7, 7),
+       [F_IDPM_STAT]           = REG_FIELD(0x13, 6, 6),
+       [F_IDPM_LIM]            = REG_FIELD(0x13, 0, 5),
+       /* REG14 */
+       [F_REG_RST]             = REG_FIELD(0x14, 7, 7),
+       [F_ICO_OPTIMIZED]       = REG_FIELD(0x14, 6, 6),
+       [F_PN]                  = REG_FIELD(0x14, 3, 5),
+       [F_TS_PROFILE]          = REG_FIELD(0x14, 2, 2),
+       [F_DEV_REV]             = REG_FIELD(0x14, 0, 1)
+};
+
+/*
+ * Most of the val -> idx conversions can be computed, given the minimum,
+ * maximum and the step between values. For the rest of conversions, we use
+ * lookup tables.
+ */
+enum bq25890_table_ids {
+       /* range tables */
+       TBL_ICHG,
+       TBL_ITERM,
+       TBL_IPRECHG,
+       TBL_VREG,
+       TBL_BATCMP,
+       TBL_VCLAMP,
+       TBL_BOOSTV,
+       TBL_SYSVMIN,
+
+       /* lookup tables */
+       TBL_TREG,
+       TBL_BOOSTI,
+};
+
+/* Thermal Regulation Threshold lookup table, in degrees Celsius */
+static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 };
+
+#define BQ25890_TREG_TBL_SIZE          ARRAY_SIZE(bq25890_treg_tbl)
+
+/* Boost mode current limit lookup table, in uA */
+static const u32 bq25890_boosti_tbl[] = {
+       500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000
+};
+
+#define BQ25890_BOOSTI_TBL_SIZE                ARRAY_SIZE(bq25890_boosti_tbl)
+
+struct bq25890_range {
+       u32 min;
+       u32 max;
+       u32 step;
+};
+
+struct bq25890_lookup {
+       const u32 *tbl;
+       u32 size;
+};
+
+static const union {
+       struct bq25890_range  rt;
+       struct bq25890_lookup lt;
+} bq25890_tables[] = {
+       /* range tables */
+       [TBL_ICHG] =    { .rt = {0,       5056000, 64000} },     /* uA */
+       [TBL_ITERM] =   { .rt = {64000,   1024000, 64000} },     /* uA */
+       [TBL_VREG] =    { .rt = {3840000, 4608000, 16000} },     /* uV */
+       [TBL_BATCMP] =  { .rt = {0,       140,     20} },        /* mOhm */
+       [TBL_VCLAMP] =  { .rt = {0,       224000,  32000} },     /* uV */
+       [TBL_BOOSTV] =  { .rt = {4550000, 5510000, 64000} },     /* uV */
+       [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} },    /* uV */
+
+       /* lookup tables */
+       [TBL_TREG] =    { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
+       [TBL_BOOSTI] =  { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }
+};
+
+static int bq25890_field_read(struct bq25890_device *bq,
+                             enum bq25890_fields field_id)
+{
+       int ret;
+       int val;
+
+       ret = regmap_field_read(bq->rmap_fields[field_id], &val);
+       if (ret < 0)
+               return ret;
+
+       return val;
+}
+
+static int bq25890_field_write(struct bq25890_device *bq,
+                              enum bq25890_fields field_id, u8 val)
+{
+       return regmap_field_write(bq->rmap_fields[field_id], val);
+}
+
+static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id)
+{
+       u8 idx;
+
+       if (id >= TBL_TREG) {
+               const u32 *tbl = bq25890_tables[id].lt.tbl;
+               u32 tbl_size = bq25890_tables[id].lt.size;
+
+               for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++)
+                       ;
+       } else {
+               const struct bq25890_range *rtbl = &bq25890_tables[id].rt;
+               u8 rtbl_size;
+
+               rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1;
+
+               for (idx = 1;
+                    idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value);
+                    idx++)
+                       ;
+       }
+
+       return idx - 1;
+}
+
+static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id)
+{
+       const struct bq25890_range *rtbl;
+
+       /* lookup table? */
+       if (id >= TBL_TREG)
+               return bq25890_tables[id].lt.tbl[idx];
+
+       /* range table */
+       rtbl = &bq25890_tables[id].rt;
+
+       return (rtbl->min + idx * rtbl->step);
+}
+
+enum bq25890_status {
+       STATUS_NOT_CHARGING,
+       STATUS_PRE_CHARGING,
+       STATUS_FAST_CHARGING,
+       STATUS_TERMINATION_DONE,
+};
+
+enum bq25890_chrg_fault {
+       CHRG_FAULT_NORMAL,
+       CHRG_FAULT_INPUT,
+       CHRG_FAULT_THERMAL_SHUTDOWN,
+       CHRG_FAULT_TIMER_EXPIRED,
+};
+
+static int bq25890_power_supply_get_property(struct power_supply *psy,
+                                            enum power_supply_property psp,
+                                            union power_supply_propval *val)
+{
+       int ret;
+       struct bq25890_device *bq = power_supply_get_drvdata(psy);
+       struct bq25890_state state;
+
+       mutex_lock(&bq->lock);
+       state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (!state.online)
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (state.chrg_status == STATUS_NOT_CHARGING)
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else if (state.chrg_status == STATUS_PRE_CHARGING ||
+                        state.chrg_status == STATUS_FAST_CHARGING)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (state.chrg_status == STATUS_TERMINATION_DONE)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = BQ25890_MANUFACTURER;
+               break;
+
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = state.online;
+               break;
+
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (!state.chrg_fault && !state.bat_fault && !state.boost_fault)
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               else if (state.bat_fault)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED)
+                       val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+               else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */
+               if (ret < 0)
+                       return ret;
+
+               /* converted_val = ADC_val * 50mA (table 10.3.19) */
+               val->intval = ret * 50000;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               val->intval = bq25890_tables[TBL_ICHG].rt.max;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               if (!state.online) {
+                       val->intval = 0;
+                       break;
+               }
+
+               ret = bq25890_field_read(bq, F_BATV); /* read measured value */
+               if (ret < 0)
+                       return ret;
+
+               /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */
+               val->intval = 2304000 + ret * 20000;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               val->intval = bq25890_tables[TBL_VREG].rt.max;
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+               val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int bq25890_get_chip_state(struct bq25890_device *bq,
+                                 struct bq25890_state *state)
+{
+       int i, ret;
+
+       struct {
+               enum bq25890_fields id;
+               u8 *data;
+       } state_fields[] = {
+               {F_CHG_STAT,    &state->chrg_status},
+               {F_PG_STAT,     &state->online},
+               {F_VSYS_STAT,   &state->vsys_status},
+               {F_BOOST_FAULT, &state->boost_fault},
+               {F_BAT_FAULT,   &state->bat_fault},
+               {F_CHG_FAULT,   &state->chrg_fault}
+       };
+
+       for (i = 0; i < ARRAY_SIZE(state_fields); i++) {
+               ret = bq25890_field_read(bq, state_fields[i].id);
+               if (ret < 0)
+                       return ret;
+
+               *state_fields[i].data = ret;
+       }
+
+       dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT=%d/%d/%d\n",
+               state->chrg_status, state->online, state->vsys_status,
+               state->chrg_fault, state->boost_fault, state->bat_fault);
+
+       return 0;
+}
+
+static bool bq25890_state_changed(struct bq25890_device *bq,
+                                 struct bq25890_state *new_state)
+{
+       struct bq25890_state old_state;
+
+       mutex_lock(&bq->lock);
+       old_state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       return (old_state.chrg_status != new_state->chrg_status ||
+               old_state.chrg_fault != new_state->chrg_fault   ||
+               old_state.online != new_state->online           ||
+               old_state.bat_fault != new_state->bat_fault     ||
+               old_state.boost_fault != new_state->boost_fault ||
+               old_state.vsys_status != new_state->vsys_status);
+}
+
+static void bq25890_handle_state_change(struct bq25890_device *bq,
+                                       struct bq25890_state *new_state)
+{
+       int ret;
+       struct bq25890_state old_state;
+
+       mutex_lock(&bq->lock);
+       old_state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       if (!new_state->online) {                            /* power removed */
+               /* disable ADC */
+               ret = bq25890_field_write(bq, F_CONV_START, 0);
+               if (ret < 0)
+                       goto error;
+       } else if (!old_state.online) {                     /* power inserted */
+               /* enable ADC, to have control of charge current/voltage */
+               ret = bq25890_field_write(bq, F_CONV_START, 1);
+               if (ret < 0)
+                       goto error;
+       }
+
+       return;
+
+error:
+       dev_err(bq->dev, "Error communicating with the chip.\n");
+}
+
+static irqreturn_t bq25890_irq_handler_thread(int irq, void *private)
+{
+       struct bq25890_device *bq = private;
+       int ret;
+       struct bq25890_state state;
+
+       ret = bq25890_get_chip_state(bq, &state);
+       if (ret < 0)
+               goto handled;
+
+       if (!bq25890_state_changed(bq, &state))
+               goto handled;
+
+       bq25890_handle_state_change(bq, &state);
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       power_supply_changed(bq->charger);
+
+handled:
+       return IRQ_HANDLED;
+}
+
+static int bq25890_chip_reset(struct bq25890_device *bq)
+{
+       int ret;
+       int rst_check_counter = 10;
+
+       ret = bq25890_field_write(bq, F_REG_RST, 1);
+       if (ret < 0)
+               return ret;
+
+       do {
+               ret = bq25890_field_read(bq, F_REG_RST);
+               if (ret < 0)
+                       return ret;
+
+               usleep_range(5, 10);
+       } while (ret == 1 && --rst_check_counter);
+
+       if (!rst_check_counter)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int bq25890_hw_init(struct bq25890_device *bq)
+{
+       int ret;
+       int i;
+       struct bq25890_state state;
+
+       const struct {
+               enum bq25890_fields id;
+               u32 value;
+       } init_data[] = {
+               {F_ICHG,         bq->init_data.ichg},
+               {F_VREG,         bq->init_data.vreg},
+               {F_ITERM,        bq->init_data.iterm},
+               {F_IPRECHG,      bq->init_data.iprechg},
+               {F_SYSVMIN,      bq->init_data.sysvmin},
+               {F_BOOSTV,       bq->init_data.boostv},
+               {F_BOOSTI,       bq->init_data.boosti},
+               {F_BOOSTF,       bq->init_data.boostf},
+               {F_EN_ILIM,      bq->init_data.ilim_en},
+               {F_TREG,         bq->init_data.treg}
+       };
+
+       ret = bq25890_chip_reset(bq);
+       if (ret < 0)
+               return ret;
+
+       /* disable watchdog */
+       ret = bq25890_field_write(bq, F_WD, 0);
+       if (ret < 0)
+               return ret;
+
+       /* initialize currents/voltages and other parameters */
+       for (i = 0; i < ARRAY_SIZE(init_data); i++) {
+               ret = bq25890_field_write(bq, init_data[i].id,
+                                         init_data[i].value);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Configure ADC for continuous conversions. This does not enable it. */
+       ret = bq25890_field_write(bq, F_CONV_RATE, 1);
+       if (ret < 0)
+               return ret;
+
+       ret = bq25890_get_chip_state(bq, &state);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       return 0;
+}
+
+static enum power_supply_property bq25890_power_supply_props[] = {
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+};
+
+static char *bq25890_charger_supplied_to[] = {
+       "main-battery",
+};
+
+static const struct power_supply_desc bq25890_power_supply_desc = {
+       .name = "bq25890-charger",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = bq25890_power_supply_props,
+       .num_properties = ARRAY_SIZE(bq25890_power_supply_props),
+       .get_property = bq25890_power_supply_get_property,
+};
+
+static int bq25890_power_supply_init(struct bq25890_device *bq)
+{
+       struct power_supply_config psy_cfg = { .drv_data = bq, };
+
+       psy_cfg.supplied_to = bq25890_charger_supplied_to;
+       psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to);
+
+       bq->charger = power_supply_register(bq->dev, &bq25890_power_supply_desc,
+                                           &psy_cfg);
+
+       return PTR_ERR_OR_ZERO(bq->charger);
+}
+
+static void bq25890_usb_work(struct work_struct *data)
+{
+       int ret;
+       struct bq25890_device *bq =
+                       container_of(data, struct bq25890_device, usb_work);
+
+       switch (bq->usb_event) {
+       case USB_EVENT_ID:
+               /* Enable boost mode */
+               ret = bq25890_field_write(bq, F_OTG_CFG, 1);
+               if (ret < 0)
+                       goto error;
+               break;
+
+       case USB_EVENT_NONE:
+               /* Disable boost mode */
+               ret = bq25890_field_write(bq, F_OTG_CFG, 0);
+               if (ret < 0)
+                       goto error;
+
+               power_supply_changed(bq->charger);
+               break;
+       }
+
+       return;
+
+error:
+       dev_err(bq->dev, "Error switching to boost/charger mode.\n");
+}
+
+static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
+                               void *priv)
+{
+       struct bq25890_device *bq =
+                       container_of(nb, struct bq25890_device, usb_nb);
+
+       bq->usb_event = val;
+       queue_work(system_power_efficient_wq, &bq->usb_work);
+
+       return NOTIFY_OK;
+}
+
+static int bq25890_irq_probe(struct bq25890_device *bq)
+{
+       struct gpio_desc *irq;
+
+       irq = devm_gpiod_get_index(bq->dev, BQ25890_IRQ_PIN, 0, GPIOD_IN);
+       if (IS_ERR(irq)) {
+               dev_err(bq->dev, "Could not probe irq pin.\n");
+               return PTR_ERR(irq);
+       }
+
+       return gpiod_to_irq(irq);
+}
+
+static int bq25890_fw_read_u32_props(struct bq25890_device *bq)
+{
+       int ret;
+       u32 property;
+       int i;
+       struct bq25890_init_data *init = &bq->init_data;
+       struct {
+               char *name;
+               bool optional;
+               enum bq25890_table_ids tbl_id;
+               u8 *conv_data; /* holds converted value from given property */
+       } props[] = {
+               /* required properties */
+               {"ti,charge-current", false, TBL_ICHG, &init->ichg},
+               {"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg},
+               {"ti,termination-current", false, TBL_ITERM, &init->iterm},
+               {"ti,precharge-current", false, TBL_ITERM, &init->iprechg},
+               {"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin},
+               {"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv},
+               {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti},
+
+               /* optional properties */
+               {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg}
+       };
+
+       /* initialize data for optional properties */
+       init->treg = 3; /* 120 degrees Celsius */
+
+       for (i = 0; i < ARRAY_SIZE(props); i++) {
+               ret = device_property_read_u32(bq->dev, props[i].name,
+                                              &property);
+               if (ret < 0) {
+                       if (props[i].optional)
+                               continue;
+
+                       return ret;
+               }
+
+               *props[i].conv_data = bq25890_find_idx(property,
+                                                      props[i].tbl_id);
+       }
+
+       return 0;
+}
+
+static int bq25890_fw_probe(struct bq25890_device *bq)
+{
+       int ret;
+       struct bq25890_init_data *init = &bq->init_data;
+
+       ret = bq25890_fw_read_u32_props(bq);
+       if (ret < 0)
+               return ret;
+
+       init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin");
+       init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq");
+
+       return 0;
+}
+
+static int bq25890_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct device *dev = &client->dev;
+       struct bq25890_device *bq;
+       int ret;
+       int i;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+               return -ENODEV;
+       }
+
+       bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
+       if (!bq)
+               return -ENOMEM;
+
+       bq->client = client;
+       bq->dev = dev;
+
+       mutex_init(&bq->lock);
+
+       bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config);
+       if (IS_ERR(bq->rmap)) {
+               dev_err(dev, "failed to allocate register map\n");
+               return PTR_ERR(bq->rmap);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) {
+               const struct reg_field *reg_fields = bq25890_reg_fields;
+
+               bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
+                                                            reg_fields[i]);
+               if (IS_ERR(bq->rmap_fields[i])) {
+                       dev_err(dev, "cannot allocate regmap field\n");
+                       return PTR_ERR(bq->rmap_fields[i]);
+               }
+       }
+
+       i2c_set_clientdata(client, bq);
+
+       bq->chip_id = bq25890_field_read(bq, F_PN);
+       if (bq->chip_id < 0) {
+               dev_err(dev, "Cannot read chip ID.\n");
+               return bq->chip_id;
+       }
+
+       if (bq->chip_id != BQ25890_ID) {
+               dev_err(dev, "Chip with ID=%d, not supported!\n", bq->chip_id);
+               return -ENODEV;
+       }
+
+       if (!dev->platform_data) {
+               ret = bq25890_fw_probe(bq);
+               if (ret < 0) {
+                       dev_err(dev, "Cannot read device properties.\n");
+                       return ret;
+               }
+       } else {
+               return -ENODEV;
+       }
+
+       ret = bq25890_hw_init(bq);
+       if (ret < 0) {
+               dev_err(dev, "Cannot initialize the chip.\n");
+               return ret;
+       }
+
+       if (client->irq <= 0)
+               client->irq = bq25890_irq_probe(bq);
+
+       if (client->irq < 0) {
+               dev_err(dev, "No irq resource found.\n");
+               return client->irq;
+       }
+
+       /* OTG reporting */
+       bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+       if (!IS_ERR_OR_NULL(bq->usb_phy)) {
+               INIT_WORK(&bq->usb_work, bq25890_usb_work);
+               bq->usb_nb.notifier_call = bq25890_usb_notifier;
+               usb_register_notifier(bq->usb_phy, &bq->usb_nb);
+       }
+
+       ret = devm_request_threaded_irq(dev, client->irq, NULL,
+                                       bq25890_irq_handler_thread,
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       BQ25890_IRQ_PIN, bq);
+       if (ret)
+               goto irq_fail;
+
+       ret = bq25890_power_supply_init(bq);
+       if (ret < 0) {
+               dev_err(dev, "Failed to register power supply\n");
+               goto irq_fail;
+       }
+
+       return 0;
+
+irq_fail:
+       if (!IS_ERR_OR_NULL(bq->usb_phy))
+               usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
+
+       return ret;
+}
+
+static int bq25890_remove(struct i2c_client *client)
+{
+       struct bq25890_device *bq = i2c_get_clientdata(client);
+
+       power_supply_unregister(bq->charger);
+
+       if (!IS_ERR_OR_NULL(bq->usb_phy))
+               usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
+
+       /* reset all registers to default values */
+       bq25890_chip_reset(bq);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bq25890_suspend(struct device *dev)
+{
+       struct bq25890_device *bq = dev_get_drvdata(dev);
+
+       /*
+        * If charger is removed, while in suspend, make sure ADC is diabled
+        * since it consumes slightly more power.
+        */
+       return bq25890_field_write(bq, F_CONV_START, 0);
+}
+
+static int bq25890_resume(struct device *dev)
+{
+       int ret;
+       struct bq25890_state state;
+       struct bq25890_device *bq = dev_get_drvdata(dev);
+
+       ret = bq25890_get_chip_state(bq, &state);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       /* Re-enable ADC only if charger is plugged in. */
+       if (state.online) {
+               ret = bq25890_field_write(bq, F_CONV_START, 1);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* signal userspace, maybe state changed while suspended */
+       power_supply_changed(bq->charger);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops bq25890_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume)
+};
+
+static const struct i2c_device_id bq25890_i2c_ids[] = {
+       { "bq25890", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids);
+
+static const struct of_device_id bq25890_of_match[] = {
+       { .compatible = "ti,bq25890", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, bq25890_of_match);
+
+static const struct acpi_device_id bq25890_acpi_match[] = {
+       {"BQ258900", 0},
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match);
+
+static struct i2c_driver bq25890_driver = {
+       .driver = {
+               .name = "bq25890-charger",
+               .of_match_table = of_match_ptr(bq25890_of_match),
+               .acpi_match_table = ACPI_PTR(bq25890_acpi_match),
+               .pm = &bq25890_pm,
+       },
+       .probe = bq25890_probe,
+       .remove = bq25890_remove,
+       .id_table = bq25890_i2c_ids,
+};
+module_i2c_driver(bq25890_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
+MODULE_DESCRIPTION("bq25890 charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
new file mode 100644 (file)
index 0000000..323d05a
--- /dev/null
@@ -0,0 +1,1102 @@
+/*
+ * BQ27xxx battery driver
+ *
+ * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
+ * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Datasheets:
+ * http://www.ti.com/product/bq27000
+ * http://www.ti.com/product/bq27200
+ * http://www.ti.com/product/bq27010
+ * http://www.ti.com/product/bq27210
+ * http://www.ti.com/product/bq27500
+ * http://www.ti.com/product/bq27510-g3
+ * http://www.ti.com/product/bq27520-g4
+ * http://www.ti.com/product/bq27530-g1
+ * http://www.ti.com/product/bq27531-g1
+ * http://www.ti.com/product/bq27541-g1
+ * http://www.ti.com/product/bq27542-g1
+ * http://www.ti.com/product/bq27546-g1
+ * http://www.ti.com/product/bq27742-g1
+ * http://www.ti.com/product/bq27545-g1
+ * http://www.ti.com/product/bq27421-g1
+ * http://www.ti.com/product/bq27425-g1
+ * http://www.ti.com/product/bq27411-g1
+ * http://www.ti.com/product/bq27621-g1
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#include <linux/power/bq27xxx_battery.h>
+
+#define DRIVER_VERSION         "1.2.0"
+
+#define BQ27XXX_MANUFACTURER   "Texas Instruments"
+
+/* BQ27XXX Flags */
+#define BQ27XXX_FLAG_DSC       BIT(0)
+#define BQ27XXX_FLAG_SOCF      BIT(1) /* State-of-Charge threshold final */
+#define BQ27XXX_FLAG_SOC1      BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_FC                BIT(9)
+#define BQ27XXX_FLAG_OTD       BIT(14)
+#define BQ27XXX_FLAG_OTC       BIT(15)
+#define BQ27XXX_FLAG_UT                BIT(14)
+#define BQ27XXX_FLAG_OT                BIT(15)
+
+/* BQ27000 has different layout for Flags register */
+#define BQ27000_FLAG_EDVF      BIT(0) /* Final End-of-Discharge-Voltage flag */
+#define BQ27000_FLAG_EDV1      BIT(1) /* First End-of-Discharge-Voltage flag */
+#define BQ27000_FLAG_CI                BIT(4) /* Capacity Inaccurate flag */
+#define BQ27000_FLAG_FC                BIT(5)
+#define BQ27000_FLAG_CHGS      BIT(7) /* Charge state flag */
+
+#define BQ27XXX_RS                     (20) /* Resistor sense mOhm */
+#define BQ27XXX_POWER_CONSTANT         (29200) /* 29.2 ÂµV^2 * 1000 */
+#define BQ27XXX_CURRENT_CONSTANT       (3570) /* 3.57 ÂµV * 1000 */
+
+#define INVALID_REG_ADDR       0xff
+
+/*
+ * bq27xxx_reg_index - Register names
+ *
+ * These are indexes into a device's register mapping array.
+ */
+
+enum bq27xxx_reg_index {
+       BQ27XXX_REG_CTRL = 0,   /* Control */
+       BQ27XXX_REG_TEMP,       /* Temperature */
+       BQ27XXX_REG_INT_TEMP,   /* Internal Temperature */
+       BQ27XXX_REG_VOLT,       /* Voltage */
+       BQ27XXX_REG_AI,         /* Average Current */
+       BQ27XXX_REG_FLAGS,      /* Flags */
+       BQ27XXX_REG_TTE,        /* Time-to-Empty */
+       BQ27XXX_REG_TTF,        /* Time-to-Full */
+       BQ27XXX_REG_TTES,       /* Time-to-Empty Standby */
+       BQ27XXX_REG_TTECP,      /* Time-to-Empty at Constant Power */
+       BQ27XXX_REG_NAC,        /* Nominal Available Capacity */
+       BQ27XXX_REG_FCC,        /* Full Charge Capacity */
+       BQ27XXX_REG_CYCT,       /* Cycle Count */
+       BQ27XXX_REG_AE,         /* Available Energy */
+       BQ27XXX_REG_SOC,        /* State-of-Charge */
+       BQ27XXX_REG_DCAP,       /* Design Capacity */
+       BQ27XXX_REG_AP,         /* Average Power */
+       BQ27XXX_REG_MAX,        /* sentinel */
+};
+
+/* Register mappings */
+static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
+       [BQ27000] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = 0x18,
+               [BQ27XXX_REG_TTES] = 0x1c,
+               [BQ27XXX_REG_TTECP] = 0x26,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = 0x22,
+               [BQ27XXX_REG_SOC] = 0x0b,
+               [BQ27XXX_REG_DCAP] = 0x76,
+               [BQ27XXX_REG_AP] = 0x24,
+       },
+       [BQ27010] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = 0x18,
+               [BQ27XXX_REG_TTES] = 0x1c,
+               [BQ27XXX_REG_TTECP] = 0x26,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x0b,
+               [BQ27XXX_REG_DCAP] = 0x76,
+               [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+       },
+       [BQ27500] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = 0x28,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = 0x1a,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x2c,
+               [BQ27XXX_REG_DCAP] = 0x3c,
+               [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+       },
+       [BQ27530] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = 0x32,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x2c,
+               [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_AP] = 0x24,
+       },
+       [BQ27541] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = 0x28,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x2c,
+               [BQ27XXX_REG_DCAP] = 0x3c,
+               [BQ27XXX_REG_AP] = 0x24,
+       },
+       [BQ27545] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x06,
+               [BQ27XXX_REG_INT_TEMP] = 0x28,
+               [BQ27XXX_REG_VOLT] = 0x08,
+               [BQ27XXX_REG_AI] = 0x14,
+               [BQ27XXX_REG_FLAGS] = 0x0a,
+               [BQ27XXX_REG_TTE] = 0x16,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x0c,
+               [BQ27XXX_REG_FCC] = 0x12,
+               [BQ27XXX_REG_CYCT] = 0x2a,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x2c,
+               [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_AP] = 0x24,
+       },
+       [BQ27421] = {
+               [BQ27XXX_REG_CTRL] = 0x00,
+               [BQ27XXX_REG_TEMP] = 0x02,
+               [BQ27XXX_REG_INT_TEMP] = 0x1e,
+               [BQ27XXX_REG_VOLT] = 0x04,
+               [BQ27XXX_REG_AI] = 0x10,
+               [BQ27XXX_REG_FLAGS] = 0x06,
+               [BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_NAC] = 0x08,
+               [BQ27XXX_REG_FCC] = 0x0e,
+               [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+               [BQ27XXX_REG_SOC] = 0x1c,
+               [BQ27XXX_REG_DCAP] = 0x3c,
+               [BQ27XXX_REG_AP] = 0x18,
+       },
+};
+
+static enum power_supply_property bq27000_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_ENERGY_NOW,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27010_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27500_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27530_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27541_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27545_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27421_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+#define BQ27XXX_PROP(_id, _prop)               \
+       [_id] = {                               \
+               .props = _prop,                 \
+               .size = ARRAY_SIZE(_prop),      \
+       }
+
+static struct {
+       enum power_supply_property *props;
+       size_t size;
+} bq27xxx_battery_props[] = {
+       BQ27XXX_PROP(BQ27000, bq27000_battery_props),
+       BQ27XXX_PROP(BQ27010, bq27010_battery_props),
+       BQ27XXX_PROP(BQ27500, bq27500_battery_props),
+       BQ27XXX_PROP(BQ27530, bq27530_battery_props),
+       BQ27XXX_PROP(BQ27541, bq27541_battery_props),
+       BQ27XXX_PROP(BQ27545, bq27545_battery_props),
+       BQ27XXX_PROP(BQ27421, bq27421_battery_props),
+};
+
+static unsigned int poll_interval = 360;
+module_param(poll_interval, uint, 0644);
+MODULE_PARM_DESC(poll_interval,
+                "battery poll interval in seconds - 0 disables polling");
+
+/*
+ * Common code for BQ27xxx devices
+ */
+
+static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
+                              bool single)
+{
+       /* Reports EINVAL for invalid/missing registers */
+       if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
+               return -EINVAL;
+
+       return di->bus.read(di, di->regs[reg_index], single);
+}
+
+/*
+ * Return the battery State-of-Charge
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di)
+{
+       int soc;
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true);
+       else
+               soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false);
+
+       if (soc < 0)
+               dev_dbg(di->dev, "error reading State-of-Charge\n");
+
+       return soc;
+}
+
+/*
+ * Return a battery charge value in ÂµAh
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
+{
+       int charge;
+
+       charge = bq27xxx_read(di, reg, false);
+       if (charge < 0) {
+               dev_dbg(di->dev, "error reading charge register %02x: %d\n",
+                       reg, charge);
+               return charge;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+       else
+               charge *= 1000;
+
+       return charge;
+}
+
+/*
+ * Return the battery Nominal available capacity in ÂµAh
+ * Or < 0 if something fails.
+ */
+static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di)
+{
+       int flags;
+
+       if (di->chip == BQ27000 || di->chip == BQ27010) {
+               flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
+               if (flags >= 0 && (flags & BQ27000_FLAG_CI))
+                       return -ENODATA;
+       }
+
+       return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC);
+}
+
+/*
+ * Return the battery Full Charge Capacity in ÂµAh
+ * Or < 0 if something fails.
+ */
+static inline int bq27xxx_battery_read_fcc(struct bq27xxx_device_info *di)
+{
+       return bq27xxx_battery_read_charge(di, BQ27XXX_REG_FCC);
+}
+
+/*
+ * Return the Design Capacity in ÂµAh
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di)
+{
+       int dcap;
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true);
+       else
+               dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false);
+
+       if (dcap < 0) {
+               dev_dbg(di->dev, "error reading initial last measured discharge\n");
+               return dcap;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+       else
+               dcap *= 1000;
+
+       return dcap;
+}
+
+/*
+ * Return the battery Available energy in ÂµWh
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di)
+{
+       int ae;
+
+       ae = bq27xxx_read(di, BQ27XXX_REG_AE, false);
+       if (ae < 0) {
+               dev_dbg(di->dev, "error reading available energy\n");
+               return ae;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS;
+       else
+               ae *= 1000;
+
+       return ae;
+}
+
+/*
+ * Return the battery temperature in tenths of degree Kelvin
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di)
+{
+       int temp;
+
+       temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false);
+       if (temp < 0) {
+               dev_err(di->dev, "error reading temperature\n");
+               return temp;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               temp = 5 * temp / 2;
+
+       return temp;
+}
+
+/*
+ * Return the battery Cycle count total
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_cyct(struct bq27xxx_device_info *di)
+{
+       int cyct;
+
+       cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false);
+       if (cyct < 0)
+               dev_err(di->dev, "error reading cycle count total\n");
+
+       return cyct;
+}
+
+/*
+ * Read a time register.
+ * Return < 0 if something fails.
+ */
+static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg)
+{
+       int tval;
+
+       tval = bq27xxx_read(di, reg, false);
+       if (tval < 0) {
+               dev_dbg(di->dev, "error reading time register %02x: %d\n",
+                       reg, tval);
+               return tval;
+       }
+
+       if (tval == 65535)
+               return -ENODATA;
+
+       return tval * 60;
+}
+
+/*
+ * Read an average power register.
+ * Return < 0 if something fails.
+ */
+static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)
+{
+       int tval;
+
+       tval = bq27xxx_read(di, BQ27XXX_REG_AP, false);
+       if (tval < 0) {
+               dev_err(di->dev, "error reading average power register  %02x: %d\n",
+                       BQ27XXX_REG_AP, tval);
+               return tval;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS;
+       else
+               return tval;
+}
+
+/*
+ * Returns true if a battery over temperature condition is detected
+ */
+static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
+{
+       if (di->chip == BQ27500 || di->chip == BQ27541 || di->chip == BQ27545)
+               return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
+       if (di->chip == BQ27530 || di->chip == BQ27421)
+               return flags & BQ27XXX_FLAG_OT;
+
+       return false;
+}
+
+/*
+ * Returns true if a battery under temperature condition is detected
+ */
+static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags)
+{
+       if (di->chip == BQ27530 || di->chip == BQ27421)
+               return flags & BQ27XXX_FLAG_UT;
+
+       return false;
+}
+
+/*
+ * Returns true if a low state of charge condition is detected
+ */
+static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
+{
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF);
+       else
+               return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
+}
+
+/*
+ * Read flag register.
+ * Return < 0 if something fails.
+ */
+static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
+{
+       int flags;
+
+       flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
+       if (flags < 0) {
+               dev_err(di->dev, "error reading flag register:%d\n", flags);
+               return flags;
+       }
+
+       /* Unlikely but important to return first */
+       if (unlikely(bq27xxx_battery_overtemp(di, flags)))
+               return POWER_SUPPLY_HEALTH_OVERHEAT;
+       if (unlikely(bq27xxx_battery_undertemp(di, flags)))
+               return POWER_SUPPLY_HEALTH_COLD;
+       if (unlikely(bq27xxx_battery_dead(di, flags)))
+               return POWER_SUPPLY_HEALTH_DEAD;
+
+       return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+void bq27xxx_battery_update(struct bq27xxx_device_info *di)
+{
+       struct bq27xxx_reg_cache cache = {0, };
+       bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010;
+       bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010;
+
+       cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
+       if ((cache.flags & 0xff) == 0xff)
+               cache.flags = -1; /* read error */
+       if (cache.flags >= 0) {
+               cache.temperature = bq27xxx_battery_read_temperature(di);
+               if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) {
+                       dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n");
+                       cache.capacity = -ENODATA;
+                       cache.energy = -ENODATA;
+                       cache.time_to_empty = -ENODATA;
+                       cache.time_to_empty_avg = -ENODATA;
+                       cache.time_to_full = -ENODATA;
+                       cache.charge_full = -ENODATA;
+                       cache.health = -ENODATA;
+               } else {
+                       if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
+                               cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
+                       if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
+                               cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
+                       if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
+                               cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
+                       cache.charge_full = bq27xxx_battery_read_fcc(di);
+                       cache.capacity = bq27xxx_battery_read_soc(di);
+                       if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
+                               cache.energy = bq27xxx_battery_read_energy(di);
+                       cache.health = bq27xxx_battery_read_health(di);
+               }
+               if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
+                       cache.cycle_count = bq27xxx_battery_read_cyct(di);
+               if (di->regs[BQ27XXX_REG_AP] != INVALID_REG_ADDR)
+                       cache.power_avg = bq27xxx_battery_read_pwr_avg(di);
+
+               /* We only have to read charge design full once */
+               if (di->charge_design_full <= 0)
+                       di->charge_design_full = bq27xxx_battery_read_dcap(di);
+       }
+
+       if (di->cache.capacity != cache.capacity)
+               power_supply_changed(di->bat);
+
+       if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
+               di->cache = cache;
+
+       di->last_update = jiffies;
+}
+EXPORT_SYMBOL_GPL(bq27xxx_battery_update);
+
+static void bq27xxx_battery_poll(struct work_struct *work)
+{
+       struct bq27xxx_device_info *di =
+                       container_of(work, struct bq27xxx_device_info,
+                                    work.work);
+
+       bq27xxx_battery_update(di);
+
+       if (poll_interval > 0)
+               schedule_delayed_work(&di->work, poll_interval * HZ);
+}
+
+/*
+ * Return the battery average current in ÂµA
+ * Note that current can be negative signed as well
+ * Or 0 if something fails.
+ */
+static int bq27xxx_battery_current(struct bq27xxx_device_info *di,
+                                  union power_supply_propval *val)
+{
+       int curr;
+       int flags;
+
+       curr = bq27xxx_read(di, BQ27XXX_REG_AI, false);
+       if (curr < 0) {
+               dev_err(di->dev, "error reading current\n");
+               return curr;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010) {
+               flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
+               if (flags & BQ27000_FLAG_CHGS) {
+                       dev_dbg(di->dev, "negative current!\n");
+                       curr = -curr;
+               }
+
+               val->intval = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+       } else {
+               /* Other gauges return signed value */
+               val->intval = (int)((s16)curr) * 1000;
+       }
+
+       return 0;
+}
+
+static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
+                                 union power_supply_propval *val)
+{
+       int status;
+
+       if (di->chip == BQ27000 || di->chip == BQ27010) {
+               if (di->cache.flags & BQ27000_FLAG_FC)
+                       status = POWER_SUPPLY_STATUS_FULL;
+               else if (di->cache.flags & BQ27000_FLAG_CHGS)
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+               else if (power_supply_am_i_supplied(di->bat))
+                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_DISCHARGING;
+       } else {
+               if (di->cache.flags & BQ27XXX_FLAG_FC)
+                       status = POWER_SUPPLY_STATUS_FULL;
+               else if (di->cache.flags & BQ27XXX_FLAG_DSC)
+                       status = POWER_SUPPLY_STATUS_DISCHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+       }
+
+       val->intval = status;
+
+       return 0;
+}
+
+static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di,
+                                         union power_supply_propval *val)
+{
+       int level;
+
+       if (di->chip == BQ27000 || di->chip == BQ27010) {
+               if (di->cache.flags & BQ27000_FLAG_FC)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+               else if (di->cache.flags & BQ27000_FLAG_EDV1)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+               else if (di->cache.flags & BQ27000_FLAG_EDVF)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+               else
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+       } else {
+               if (di->cache.flags & BQ27XXX_FLAG_FC)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+               else if (di->cache.flags & BQ27XXX_FLAG_SOC1)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+               else if (di->cache.flags & BQ27XXX_FLAG_SOCF)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+               else
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+       }
+
+       val->intval = level;
+
+       return 0;
+}
+
+/*
+ * Return the battery Voltage in millivolts
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di,
+                                  union power_supply_propval *val)
+{
+       int volt;
+
+       volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false);
+       if (volt < 0) {
+               dev_err(di->dev, "error reading voltage\n");
+               return volt;
+       }
+
+       val->intval = volt * 1000;
+
+       return 0;
+}
+
+static int bq27xxx_simple_value(int value,
+                               union power_supply_propval *val)
+{
+       if (value < 0)
+               return value;
+
+       val->intval = value;
+
+       return 0;
+}
+
+static int bq27xxx_battery_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       int ret = 0;
+       struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);
+
+       mutex_lock(&di->lock);
+       if (time_is_before_jiffies(di->last_update + 5 * HZ)) {
+               cancel_delayed_work_sync(&di->work);
+               bq27xxx_battery_poll(&di->work.work);
+       }
+       mutex_unlock(&di->lock);
+
+       if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0)
+               return -ENODEV;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = bq27xxx_battery_status(di, val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = bq27xxx_battery_voltage(di, val);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = di->cache.flags < 0 ? 0 : 1;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = bq27xxx_battery_current(di, val);
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = bq27xxx_simple_value(di->cache.capacity, val);
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+               ret = bq27xxx_battery_capacity_level(di, val);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = bq27xxx_simple_value(di->cache.temperature, val);
+               if (ret == 0)
+                       val->intval -= 2731; /* convert decidegree k to c */
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+               ret = bq27xxx_simple_value(di->cache.time_to_empty, val);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+               ret = bq27xxx_simple_value(di->cache.time_to_empty_avg, val);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
+               ret = bq27xxx_simple_value(di->cache.time_to_full, val);
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               ret = bq27xxx_simple_value(bq27xxx_battery_read_nac(di), val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               ret = bq27xxx_simple_value(di->cache.charge_full, val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               ret = bq27xxx_simple_value(di->charge_design_full, val);
+               break;
+       case POWER_SUPPLY_PROP_CYCLE_COUNT:
+               ret = bq27xxx_simple_value(di->cache.cycle_count, val);
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_NOW:
+               ret = bq27xxx_simple_value(di->cache.energy, val);
+               break;
+       case POWER_SUPPLY_PROP_POWER_AVG:
+               ret = bq27xxx_simple_value(di->cache.power_avg, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = bq27xxx_simple_value(di->cache.health, val);
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = BQ27XXX_MANUFACTURER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+static void bq27xxx_external_power_changed(struct power_supply *psy)
+{
+       struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);
+
+       cancel_delayed_work_sync(&di->work);
+       schedule_delayed_work(&di->work, 0);
+}
+
+int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
+{
+       struct power_supply_desc *psy_desc;
+       struct power_supply_config psy_cfg = { .drv_data = di, };
+
+       INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
+       mutex_init(&di->lock);
+       di->regs = bq27xxx_regs[di->chip];
+
+       psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL);
+       if (!psy_desc)
+               return -ENOMEM;
+
+       psy_desc->name = di->name;
+       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
+       psy_desc->properties = bq27xxx_battery_props[di->chip].props;
+       psy_desc->num_properties = bq27xxx_battery_props[di->chip].size;
+       psy_desc->get_property = bq27xxx_battery_get_property;
+       psy_desc->external_power_changed = bq27xxx_external_power_changed;
+
+       di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg);
+       if (IS_ERR(di->bat)) {
+               dev_err(di->dev, "failed to register battery\n");
+               return PTR_ERR(di->bat);
+       }
+
+       dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
+
+       bq27xxx_battery_update(di);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(bq27xxx_battery_setup);
+
+void bq27xxx_battery_teardown(struct bq27xxx_device_info *di)
+{
+       /*
+        * power_supply_unregister call bq27xxx_battery_get_property which
+        * call bq27xxx_battery_poll.
+        * Make sure that bq27xxx_battery_poll will not call
+        * schedule_delayed_work again after unregister (which cause OOPS).
+        */
+       poll_interval = 0;
+
+       cancel_delayed_work_sync(&di->work);
+
+       power_supply_unregister(di->bat);
+
+       mutex_destroy(&di->lock);
+}
+EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown);
+
+static int bq27xxx_battery_platform_read(struct bq27xxx_device_info *di, u8 reg,
+                                        bool single)
+{
+       struct device *dev = di->dev;
+       struct bq27xxx_platform_data *pdata = dev->platform_data;
+       unsigned int timeout = 3;
+       int upper, lower;
+       int temp;
+
+       if (!single) {
+               /* Make sure the value has not changed in between reading the
+                * lower and the upper part */
+               upper = pdata->read(dev, reg + 1);
+               do {
+                       temp = upper;
+                       if (upper < 0)
+                               return upper;
+
+                       lower = pdata->read(dev, reg);
+                       if (lower < 0)
+                               return lower;
+
+                       upper = pdata->read(dev, reg + 1);
+               } while (temp != upper && --timeout);
+
+               if (timeout == 0)
+                       return -EIO;
+
+               return (upper << 8) | lower;
+       }
+
+       return pdata->read(dev, reg);
+}
+
+static int bq27xxx_battery_platform_probe(struct platform_device *pdev)
+{
+       struct bq27xxx_device_info *di;
+       struct bq27xxx_platform_data *pdata = pdev->dev.platform_data;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform_data supplied\n");
+               return -EINVAL;
+       }
+
+       if (!pdata->read) {
+               dev_err(&pdev->dev, "no hdq read callback supplied\n");
+               return -EINVAL;
+       }
+
+       if (!pdata->chip) {
+               dev_err(&pdev->dev, "no device supplied\n");
+               return -EINVAL;
+       }
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       if (!di)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, di);
+
+       di->dev = &pdev->dev;
+       di->chip = pdata->chip;
+       di->name = pdata->name ?: dev_name(&pdev->dev);
+       di->bus.read = bq27xxx_battery_platform_read;
+
+       return bq27xxx_battery_setup(di);
+}
+
+static int bq27xxx_battery_platform_remove(struct platform_device *pdev)
+{
+       struct bq27xxx_device_info *di = platform_get_drvdata(pdev);
+
+       bq27xxx_battery_teardown(di);
+
+       return 0;
+}
+
+static const struct platform_device_id bq27xxx_battery_platform_id_table[] = {
+       { "bq27000-battery", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, bq27xxx_battery_platform_id_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id bq27xxx_battery_platform_of_match_table[] = {
+       { .compatible = "ti,bq27000" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bq27xxx_battery_platform_of_match_table);
+#endif
+
+static struct platform_driver bq27xxx_battery_platform_driver = {
+       .probe  = bq27xxx_battery_platform_probe,
+       .remove = bq27xxx_battery_platform_remove,
+       .driver = {
+               .name = "bq27000-battery",
+               .of_match_table = of_match_ptr(bq27xxx_battery_platform_of_match_table),
+       },
+       .id_table = bq27xxx_battery_platform_id_table,
+};
+module_platform_driver(bq27xxx_battery_platform_driver);
+
+MODULE_ALIAS("platform:bq27000-battery");
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("BQ27xxx battery monitor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
new file mode 100644 (file)
index 0000000..85d4ea2
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * BQ27xxx battery monitor I2C driver
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ *     Andrew F. Davis <afd@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <asm/unaligned.h>
+
+#include <linux/power/bq27xxx_battery.h>
+
+static DEFINE_IDR(battery_id);
+static DEFINE_MUTEX(battery_mutex);
+
+static irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data)
+{
+       struct bq27xxx_device_info *di = data;
+
+       bq27xxx_battery_update(di);
+
+       return IRQ_HANDLED;
+}
+
+static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
+                                   bool single)
+{
+       struct i2c_client *client = to_i2c_client(di->dev);
+       struct i2c_msg msg[2];
+       unsigned char data[2];
+       int ret;
+
+       if (!client->adapter)
+               return -ENODEV;
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].buf = &reg;
+       msg[0].len = sizeof(reg);
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].buf = data;
+       if (single)
+               msg[1].len = 1;
+       else
+               msg[1].len = 2;
+
+       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+       if (ret < 0)
+               return ret;
+
+       if (!single)
+               ret = get_unaligned_le16(data);
+       else
+               ret = data[0];
+
+       return ret;
+}
+
+static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
+                                    const struct i2c_device_id *id)
+{
+       struct bq27xxx_device_info *di;
+       int ret;
+       char *name;
+       int num;
+
+       /* Get new ID for the new battery device */
+       mutex_lock(&battery_mutex);
+       num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
+       mutex_unlock(&battery_mutex);
+       if (num < 0)
+               return num;
+
+       name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
+       if (!name)
+               goto err_mem;
+
+       di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
+       if (!di)
+               goto err_mem;
+
+       di->id = num;
+       di->dev = &client->dev;
+       di->chip = id->driver_data;
+       di->name = name;
+       di->bus.read = bq27xxx_battery_i2c_read;
+
+       ret = bq27xxx_battery_setup(di);
+       if (ret)
+               goto err_failed;
+
+       /* Schedule a polling after about 1 min */
+       schedule_delayed_work(&di->work, 60 * HZ);
+
+       i2c_set_clientdata(client, di);
+
+       if (client->irq) {
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                               NULL, bq27xxx_battery_irq_handler_thread,
+                               IRQF_ONESHOT,
+                               di->name, di);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "Unable to register IRQ %d error %d\n",
+                               client->irq, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+
+err_mem:
+       ret = -ENOMEM;
+
+err_failed:
+       mutex_lock(&battery_mutex);
+       idr_remove(&battery_id, num);
+       mutex_unlock(&battery_mutex);
+
+       return ret;
+}
+
+static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
+{
+       struct bq27xxx_device_info *di = i2c_get_clientdata(client);
+
+       bq27xxx_battery_teardown(di);
+
+       mutex_lock(&battery_mutex);
+       idr_remove(&battery_id, di->id);
+       mutex_unlock(&battery_mutex);
+
+       return 0;
+}
+
+static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
+       { "bq27200", BQ27000 },
+       { "bq27210", BQ27010 },
+       { "bq27500", BQ27500 },
+       { "bq27510", BQ27500 },
+       { "bq27520", BQ27500 },
+       { "bq27530", BQ27530 },
+       { "bq27531", BQ27530 },
+       { "bq27541", BQ27541 },
+       { "bq27542", BQ27541 },
+       { "bq27546", BQ27541 },
+       { "bq27742", BQ27541 },
+       { "bq27545", BQ27545 },
+       { "bq27421", BQ27421 },
+       { "bq27425", BQ27421 },
+       { "bq27441", BQ27421 },
+       { "bq27621", BQ27421 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
+       { .compatible = "ti,bq27200" },
+       { .compatible = "ti,bq27210" },
+       { .compatible = "ti,bq27500" },
+       { .compatible = "ti,bq27510" },
+       { .compatible = "ti,bq27520" },
+       { .compatible = "ti,bq27530" },
+       { .compatible = "ti,bq27531" },
+       { .compatible = "ti,bq27541" },
+       { .compatible = "ti,bq27542" },
+       { .compatible = "ti,bq27546" },
+       { .compatible = "ti,bq27742" },
+       { .compatible = "ti,bq27545" },
+       { .compatible = "ti,bq27421" },
+       { .compatible = "ti,bq27425" },
+       { .compatible = "ti,bq27441" },
+       { .compatible = "ti,bq27621" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
+#endif
+
+static struct i2c_driver bq27xxx_battery_i2c_driver = {
+       .driver = {
+               .name = "bq27xxx-battery",
+               .of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table),
+       },
+       .probe = bq27xxx_battery_i2c_probe,
+       .remove = bq27xxx_battery_i2c_remove,
+       .id_table = bq27xxx_i2c_id_table,
+};
+module_i2c_driver(bq27xxx_battery_i2c_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c
new file mode 100644 (file)
index 0000000..e664ca7
--- /dev/null
@@ -0,0 +1,2074 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This driver enables to monitor battery health and control charger
+ * during suspend-to-mem.
+ * Charger manager depends on other devices. register this later than
+ * the depending devices.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+**/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/power/charger-manager.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/of.h>
+#include <linux/thermal.h>
+
+/*
+ * Default termperature threshold for charging.
+ * Every temperature units are in tenth of centigrade.
+ */
+#define CM_DEFAULT_RECHARGE_TEMP_DIFF  50
+#define CM_DEFAULT_CHARGE_TEMP_MAX     500
+
+static const char * const default_event_names[] = {
+       [CM_EVENT_UNKNOWN] = "Unknown",
+       [CM_EVENT_BATT_FULL] = "Battery Full",
+       [CM_EVENT_BATT_IN] = "Battery Inserted",
+       [CM_EVENT_BATT_OUT] = "Battery Pulled Out",
+       [CM_EVENT_BATT_OVERHEAT] = "Battery Overheat",
+       [CM_EVENT_BATT_COLD] = "Battery Cold",
+       [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach",
+       [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop",
+       [CM_EVENT_OTHERS] = "Other battery events"
+};
+
+/*
+ * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
+ * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
+ * without any delays.
+ */
+#define        CM_JIFFIES_SMALL        (2)
+
+/* If y is valid (> 0) and smaller than x, do x = y */
+#define CM_MIN_VALID(x, y)     x = (((y > 0) && ((x) > (y))) ? (y) : (x))
+
+/*
+ * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking
+ * rtc alarm. It should be 2 or larger
+ */
+#define CM_RTC_SMALL           (2)
+
+#define UEVENT_BUF_SIZE                32
+
+static LIST_HEAD(cm_list);
+static DEFINE_MUTEX(cm_list_mtx);
+
+/* About in-suspend (suspend-again) monitoring */
+static struct alarm *cm_timer;
+
+static bool cm_suspended;
+static bool cm_timer_set;
+static unsigned long cm_suspend_duration_ms;
+
+/* About normal (not suspended) monitoring */
+static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */
+static unsigned long next_polling; /* Next appointed polling time */
+static struct workqueue_struct *cm_wq; /* init at driver add */
+static struct delayed_work cm_monitor_work; /* init at driver add */
+
+/**
+ * is_batt_present - See if the battery presents in place.
+ * @cm: the Charger Manager representing the battery.
+ */
+static bool is_batt_present(struct charger_manager *cm)
+{
+       union power_supply_propval val;
+       struct power_supply *psy;
+       bool present = false;
+       int i, ret;
+
+       switch (cm->desc->battery_present) {
+       case CM_BATTERY_PRESENT:
+               present = true;
+               break;
+       case CM_NO_BATTERY:
+               break;
+       case CM_FUEL_GAUGE:
+               psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+               if (!psy)
+                       break;
+
+               ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,
+                               &val);
+               if (ret == 0 && val.intval)
+                       present = true;
+               power_supply_put(psy);
+               break;
+       case CM_CHARGER_STAT:
+               for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+                       psy = power_supply_get_by_name(
+                                       cm->desc->psy_charger_stat[i]);
+                       if (!psy) {
+                               dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+                                       cm->desc->psy_charger_stat[i]);
+                               continue;
+                       }
+
+                       ret = power_supply_get_property(psy,
+                               POWER_SUPPLY_PROP_PRESENT, &val);
+                       power_supply_put(psy);
+                       if (ret == 0 && val.intval) {
+                               present = true;
+                               break;
+                       }
+               }
+               break;
+       }
+
+       return present;
+}
+
+/**
+ * is_ext_pwr_online - See if an external power source is attached to charge
+ * @cm: the Charger Manager representing the battery.
+ *
+ * Returns true if at least one of the chargers of the battery has an external
+ * power source attached to charge the battery regardless of whether it is
+ * actually charging or not.
+ */
+static bool is_ext_pwr_online(struct charger_manager *cm)
+{
+       union power_supply_propval val;
+       struct power_supply *psy;
+       bool online = false;
+       int i, ret;
+
+       /* If at least one of them has one, it's yes. */
+       for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+               psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
+               if (!psy) {
+                       dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+                                       cm->desc->psy_charger_stat[i]);
+                       continue;
+               }
+
+               ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
+                               &val);
+               power_supply_put(psy);
+               if (ret == 0 && val.intval) {
+                       online = true;
+                       break;
+               }
+       }
+
+       return online;
+}
+
+/**
+ * get_batt_uV - Get the voltage level of the battery
+ * @cm: the Charger Manager representing the battery.
+ * @uV: the voltage level returned.
+ *
+ * Returns 0 if there is no error.
+ * Returns a negative value on error.
+ */
+static int get_batt_uV(struct charger_manager *cm, int *uV)
+{
+       union power_supply_propval val;
+       struct power_supply *fuel_gauge;
+       int ret;
+
+       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+       if (!fuel_gauge)
+               return -ENODEV;
+
+       ret = power_supply_get_property(fuel_gauge,
+                               POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
+       power_supply_put(fuel_gauge);
+       if (ret)
+               return ret;
+
+       *uV = val.intval;
+       return 0;
+}
+
+/**
+ * is_charging - Returns true if the battery is being charged.
+ * @cm: the Charger Manager representing the battery.
+ */
+static bool is_charging(struct charger_manager *cm)
+{
+       int i, ret;
+       bool charging = false;
+       struct power_supply *psy;
+       union power_supply_propval val;
+
+       /* If there is no battery, it cannot be charged */
+       if (!is_batt_present(cm))
+               return false;
+
+       /* If at least one of the charger is charging, return yes */
+       for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+               /* 1. The charger sholuld not be DISABLED */
+               if (cm->emergency_stop)
+                       continue;
+               if (!cm->charger_enabled)
+                       continue;
+
+               psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
+               if (!psy) {
+                       dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+                                       cm->desc->psy_charger_stat[i]);
+                       continue;
+               }
+
+               /* 2. The charger should be online (ext-power) */
+               ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE,
+                               &val);
+               if (ret) {
+                       dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
+                                cm->desc->psy_charger_stat[i]);
+                       power_supply_put(psy);
+                       continue;
+               }
+               if (val.intval == 0) {
+                       power_supply_put(psy);
+                       continue;
+               }
+
+               /*
+                * 3. The charger should not be FULL, DISCHARGING,
+                * or NOT_CHARGING.
+                */
+               ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
+                               &val);
+               power_supply_put(psy);
+               if (ret) {
+                       dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
+                                cm->desc->psy_charger_stat[i]);
+                       continue;
+               }
+               if (val.intval == POWER_SUPPLY_STATUS_FULL ||
+                               val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
+                               val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
+                       continue;
+
+               /* Then, this is charging. */
+               charging = true;
+               break;
+       }
+
+       return charging;
+}
+
+/**
+ * is_full_charged - Returns true if the battery is fully charged.
+ * @cm: the Charger Manager representing the battery.
+ */
+static bool is_full_charged(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+       union power_supply_propval val;
+       struct power_supply *fuel_gauge;
+       bool is_full = false;
+       int ret = 0;
+       int uV;
+
+       /* If there is no battery, it cannot be charged */
+       if (!is_batt_present(cm))
+               return false;
+
+       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+       if (!fuel_gauge)
+               return false;
+
+       if (desc->fullbatt_full_capacity > 0) {
+               val.intval = 0;
+
+               /* Not full if capacity of fuel gauge isn't full */
+               ret = power_supply_get_property(fuel_gauge,
+                               POWER_SUPPLY_PROP_CHARGE_FULL, &val);
+               if (!ret && val.intval > desc->fullbatt_full_capacity) {
+                       is_full = true;
+                       goto out;
+               }
+       }
+
+       /* Full, if it's over the fullbatt voltage */
+       if (desc->fullbatt_uV > 0) {
+               ret = get_batt_uV(cm, &uV);
+               if (!ret && uV >= desc->fullbatt_uV) {
+                       is_full = true;
+                       goto out;
+               }
+       }
+
+       /* Full, if the capacity is more than fullbatt_soc */
+       if (desc->fullbatt_soc > 0) {
+               val.intval = 0;
+
+               ret = power_supply_get_property(fuel_gauge,
+                               POWER_SUPPLY_PROP_CAPACITY, &val);
+               if (!ret && val.intval >= desc->fullbatt_soc) {
+                       is_full = true;
+                       goto out;
+               }
+       }
+
+out:
+       power_supply_put(fuel_gauge);
+       return is_full;
+}
+
+/**
+ * is_polling_required - Return true if need to continue polling for this CM.
+ * @cm: the Charger Manager representing the battery.
+ */
+static bool is_polling_required(struct charger_manager *cm)
+{
+       switch (cm->desc->polling_mode) {
+       case CM_POLL_DISABLE:
+               return false;
+       case CM_POLL_ALWAYS:
+               return true;
+       case CM_POLL_EXTERNAL_POWER_ONLY:
+               return is_ext_pwr_online(cm);
+       case CM_POLL_CHARGING_ONLY:
+               return is_charging(cm);
+       default:
+               dev_warn(cm->dev, "Incorrect polling_mode (%d)\n",
+                        cm->desc->polling_mode);
+       }
+
+       return false;
+}
+
+/**
+ * try_charger_enable - Enable/Disable chargers altogether
+ * @cm: the Charger Manager representing the battery.
+ * @enable: true: enable / false: disable
+ *
+ * Note that Charger Manager keeps the charger enabled regardless whether
+ * the charger is charging or not (because battery is full or no external
+ * power source exists) except when CM needs to disable chargers forcibly
+ * bacause of emergency causes; when the battery is overheated or too cold.
+ */
+static int try_charger_enable(struct charger_manager *cm, bool enable)
+{
+       int err = 0, i;
+       struct charger_desc *desc = cm->desc;
+
+       /* Ignore if it's redundent command */
+       if (enable == cm->charger_enabled)
+               return 0;
+
+       if (enable) {
+               if (cm->emergency_stop)
+                       return -EAGAIN;
+
+               /*
+                * Save start time of charging to limit
+                * maximum possible charging time.
+                */
+               cm->charging_start_time = ktime_to_ms(ktime_get());
+               cm->charging_end_time = 0;
+
+               for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+                       if (desc->charger_regulators[i].externally_control)
+                               continue;
+
+                       err = regulator_enable(desc->charger_regulators[i].consumer);
+                       if (err < 0) {
+                               dev_warn(cm->dev, "Cannot enable %s regulator\n",
+                                        desc->charger_regulators[i].regulator_name);
+                       }
+               }
+       } else {
+               /*
+                * Save end time of charging to maintain fully charged state
+                * of battery after full-batt.
+                */
+               cm->charging_start_time = 0;
+               cm->charging_end_time = ktime_to_ms(ktime_get());
+
+               for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+                       if (desc->charger_regulators[i].externally_control)
+                               continue;
+
+                       err = regulator_disable(desc->charger_regulators[i].consumer);
+                       if (err < 0) {
+                               dev_warn(cm->dev, "Cannot disable %s regulator\n",
+                                        desc->charger_regulators[i].regulator_name);
+                       }
+               }
+
+               /*
+                * Abnormal battery state - Stop charging forcibly,
+                * even if charger was enabled at the other places
+                */
+               for (i = 0; i < desc->num_charger_regulators; i++) {
+                       if (regulator_is_enabled(
+                                   desc->charger_regulators[i].consumer)) {
+                               regulator_force_disable(
+                                       desc->charger_regulators[i].consumer);
+                               dev_warn(cm->dev, "Disable regulator(%s) forcibly\n",
+                                        desc->charger_regulators[i].regulator_name);
+                       }
+               }
+       }
+
+       if (!err)
+               cm->charger_enabled = enable;
+
+       return err;
+}
+
+/**
+ * try_charger_restart - Restart charging.
+ * @cm: the Charger Manager representing the battery.
+ *
+ * Restart charging by turning off and on the charger.
+ */
+static int try_charger_restart(struct charger_manager *cm)
+{
+       int err;
+
+       if (cm->emergency_stop)
+               return -EAGAIN;
+
+       err = try_charger_enable(cm, false);
+       if (err)
+               return err;
+
+       return try_charger_enable(cm, true);
+}
+
+/**
+ * uevent_notify - Let users know something has changed.
+ * @cm: the Charger Manager representing the battery.
+ * @event: the event string.
+ *
+ * If @event is null, it implies that uevent_notify is called
+ * by resume function. When called in the resume function, cm_suspended
+ * should be already reset to false in order to let uevent_notify
+ * notify the recent event during the suspend to users. While
+ * suspended, uevent_notify does not notify users, but tracks
+ * events so that uevent_notify can notify users later after resumed.
+ */
+static void uevent_notify(struct charger_manager *cm, const char *event)
+{
+       static char env_str[UEVENT_BUF_SIZE + 1] = "";
+       static char env_str_save[UEVENT_BUF_SIZE + 1] = "";
+
+       if (cm_suspended) {
+               /* Nothing in suspended-event buffer */
+               if (env_str_save[0] == 0) {
+                       if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
+                               return; /* status not changed */
+                       strncpy(env_str_save, event, UEVENT_BUF_SIZE);
+                       return;
+               }
+
+               if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
+                       return; /* Duplicated. */
+               strncpy(env_str_save, event, UEVENT_BUF_SIZE);
+               return;
+       }
+
+       if (event == NULL) {
+               /* No messages pending */
+               if (!env_str_save[0])
+                       return;
+
+               strncpy(env_str, env_str_save, UEVENT_BUF_SIZE);
+               kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
+               env_str_save[0] = 0;
+
+               return;
+       }
+
+       /* status not changed */
+       if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
+               return;
+
+       /* save the status and notify the update */
+       strncpy(env_str, event, UEVENT_BUF_SIZE);
+       kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
+
+       dev_info(cm->dev, "%s\n", event);
+}
+
+/**
+ * fullbatt_vchk - Check voltage drop some times after "FULL" event.
+ * @work: the work_struct appointing the function
+ *
+ * If a user has designated "fullbatt_vchkdrop_ms/uV" values with
+ * charger_desc, Charger Manager checks voltage drop after the battery
+ * "FULL" event. It checks whether the voltage has dropped more than
+ * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms.
+ */
+static void fullbatt_vchk(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct charger_manager *cm = container_of(dwork,
+                       struct charger_manager, fullbatt_vchk_work);
+       struct charger_desc *desc = cm->desc;
+       int batt_uV, err, diff;
+
+       /* remove the appointment for fullbatt_vchk */
+       cm->fullbatt_vchk_jiffies_at = 0;
+
+       if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
+               return;
+
+       err = get_batt_uV(cm, &batt_uV);
+       if (err) {
+               dev_err(cm->dev, "%s: get_batt_uV error(%d)\n", __func__, err);
+               return;
+       }
+
+       diff = desc->fullbatt_uV - batt_uV;
+       if (diff < 0)
+               return;
+
+       dev_info(cm->dev, "VBATT dropped %duV after full-batt\n", diff);
+
+       if (diff > desc->fullbatt_vchkdrop_uV) {
+               try_charger_restart(cm);
+               uevent_notify(cm, "Recharging");
+       }
+}
+
+/**
+ * check_charging_duration - Monitor charging/discharging duration
+ * @cm: the Charger Manager representing the battery.
+ *
+ * If whole charging duration exceed 'charging_max_duration_ms',
+ * cm stop charging to prevent overcharge/overheat. If discharging
+ * duration exceed 'discharging _max_duration_ms', charger cable is
+ * attached, after full-batt, cm start charging to maintain fully
+ * charged state for battery.
+ */
+static int check_charging_duration(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+       u64 curr = ktime_to_ms(ktime_get());
+       u64 duration;
+       int ret = false;
+
+       if (!desc->charging_max_duration_ms &&
+                       !desc->discharging_max_duration_ms)
+               return ret;
+
+       if (cm->charger_enabled) {
+               duration = curr - cm->charging_start_time;
+
+               if (duration > desc->charging_max_duration_ms) {
+                       dev_info(cm->dev, "Charging duration exceed %ums\n",
+                                desc->charging_max_duration_ms);
+                       uevent_notify(cm, "Discharging");
+                       try_charger_enable(cm, false);
+                       ret = true;
+               }
+       } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) {
+               duration = curr - cm->charging_end_time;
+
+               if (duration > desc->charging_max_duration_ms &&
+                               is_ext_pwr_online(cm)) {
+                       dev_info(cm->dev, "Discharging duration exceed %ums\n",
+                                desc->discharging_max_duration_ms);
+                       uevent_notify(cm, "Recharging");
+                       try_charger_enable(cm, true);
+                       ret = true;
+               }
+       }
+
+       return ret;
+}
+
+static int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
+                                       int *temp)
+{
+       struct power_supply *fuel_gauge;
+       int ret;
+
+       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+       if (!fuel_gauge)
+               return -ENODEV;
+
+       ret = power_supply_get_property(fuel_gauge,
+                               POWER_SUPPLY_PROP_TEMP,
+                               (union power_supply_propval *)temp);
+       power_supply_put(fuel_gauge);
+
+       return ret;
+}
+
+static int cm_get_battery_temperature(struct charger_manager *cm,
+                                       int *temp)
+{
+       int ret;
+
+       if (!cm->desc->measure_battery_temp)
+               return -ENODEV;
+
+#ifdef CONFIG_THERMAL
+       if (cm->tzd_batt) {
+               ret = thermal_zone_get_temp(cm->tzd_batt, temp);
+               if (!ret)
+                       /* Calibrate temperature unit */
+                       *temp /= 100;
+       } else
+#endif
+       {
+               /* if-else continued from CONFIG_THERMAL */
+               ret = cm_get_battery_temperature_by_psy(cm, temp);
+       }
+
+       return ret;
+}
+
+static int cm_check_thermal_status(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+       int temp, upper_limit, lower_limit;
+       int ret = 0;
+
+       ret = cm_get_battery_temperature(cm, &temp);
+       if (ret) {
+               /* FIXME:
+                * No information of battery temperature might
+                * occur hazadous result. We have to handle it
+                * depending on battery type.
+                */
+               dev_err(cm->dev, "Failed to get battery temperature\n");
+               return 0;
+       }
+
+       upper_limit = desc->temp_max;
+       lower_limit = desc->temp_min;
+
+       if (cm->emergency_stop) {
+               upper_limit -= desc->temp_diff;
+               lower_limit += desc->temp_diff;
+       }
+
+       if (temp > upper_limit)
+               ret = CM_EVENT_BATT_OVERHEAT;
+       else if (temp < lower_limit)
+               ret = CM_EVENT_BATT_COLD;
+
+       return ret;
+}
+
+/**
+ * _cm_monitor - Monitor the temperature and return true for exceptions.
+ * @cm: the Charger Manager representing the battery.
+ *
+ * Returns true if there is an event to notify for the battery.
+ * (True if the status of "emergency_stop" changes)
+ */
+static bool _cm_monitor(struct charger_manager *cm)
+{
+       int temp_alrt;
+
+       temp_alrt = cm_check_thermal_status(cm);
+
+       /* It has been stopped already */
+       if (temp_alrt && cm->emergency_stop)
+               return false;
+
+       /*
+        * Check temperature whether overheat or cold.
+        * If temperature is out of range normal state, stop charging.
+        */
+       if (temp_alrt) {
+               cm->emergency_stop = temp_alrt;
+               if (!try_charger_enable(cm, false))
+                       uevent_notify(cm, default_event_names[temp_alrt]);
+
+       /*
+        * Check whole charging duration and discharing duration
+        * after full-batt.
+        */
+       } else if (!cm->emergency_stop && check_charging_duration(cm)) {
+               dev_dbg(cm->dev,
+                       "Charging/Discharging duration is out of range\n");
+       /*
+        * Check dropped voltage of battery. If battery voltage is more
+        * dropped than fullbatt_vchkdrop_uV after fully charged state,
+        * charger-manager have to recharge battery.
+        */
+       } else if (!cm->emergency_stop && is_ext_pwr_online(cm) &&
+                       !cm->charger_enabled) {
+               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+
+       /*
+        * Check whether fully charged state to protect overcharge
+        * if charger-manager is charging for battery.
+        */
+       } else if (!cm->emergency_stop && is_full_charged(cm) &&
+                       cm->charger_enabled) {
+               dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n");
+               uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
+
+               try_charger_enable(cm, false);
+
+               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+       } else {
+               cm->emergency_stop = 0;
+               if (is_ext_pwr_online(cm)) {
+                       if (!try_charger_enable(cm, true))
+                               uevent_notify(cm, "CHARGING");
+               }
+       }
+
+       return true;
+}
+
+/**
+ * cm_monitor - Monitor every battery.
+ *
+ * Returns true if there is an event to notify from any of the batteries.
+ * (True if the status of "emergency_stop" changes)
+ */
+static bool cm_monitor(void)
+{
+       bool stop = false;
+       struct charger_manager *cm;
+
+       mutex_lock(&cm_list_mtx);
+
+       list_for_each_entry(cm, &cm_list, entry) {
+               if (_cm_monitor(cm))
+                       stop = true;
+       }
+
+       mutex_unlock(&cm_list_mtx);
+
+       return stop;
+}
+
+/**
+ * _setup_polling - Setup the next instance of polling.
+ * @work: work_struct of the function _setup_polling.
+ */
+static void _setup_polling(struct work_struct *work)
+{
+       unsigned long min = ULONG_MAX;
+       struct charger_manager *cm;
+       bool keep_polling = false;
+       unsigned long _next_polling;
+
+       mutex_lock(&cm_list_mtx);
+
+       list_for_each_entry(cm, &cm_list, entry) {
+               if (is_polling_required(cm) && cm->desc->polling_interval_ms) {
+                       keep_polling = true;
+
+                       if (min > cm->desc->polling_interval_ms)
+                               min = cm->desc->polling_interval_ms;
+               }
+       }
+
+       polling_jiffy = msecs_to_jiffies(min);
+       if (polling_jiffy <= CM_JIFFIES_SMALL)
+               polling_jiffy = CM_JIFFIES_SMALL + 1;
+
+       if (!keep_polling)
+               polling_jiffy = ULONG_MAX;
+       if (polling_jiffy == ULONG_MAX)
+               goto out;
+
+       WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
+                           ". try it later. %s\n", __func__);
+
+       /*
+        * Use mod_delayed_work() iff the next polling interval should
+        * occur before the currently scheduled one.  If @cm_monitor_work
+        * isn't active, the end result is the same, so no need to worry
+        * about stale @next_polling.
+        */
+       _next_polling = jiffies + polling_jiffy;
+
+       if (time_before(_next_polling, next_polling)) {
+               mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
+               next_polling = _next_polling;
+       } else {
+               if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy))
+                       next_polling = _next_polling;
+       }
+out:
+       mutex_unlock(&cm_list_mtx);
+}
+static DECLARE_WORK(setup_polling, _setup_polling);
+
+/**
+ * cm_monitor_poller - The Monitor / Poller.
+ * @work: work_struct of the function cm_monitor_poller
+ *
+ * During non-suspended state, cm_monitor_poller is used to poll and monitor
+ * the batteries.
+ */
+static void cm_monitor_poller(struct work_struct *work)
+{
+       cm_monitor();
+       schedule_work(&setup_polling);
+}
+
+/**
+ * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL
+ * @cm: the Charger Manager representing the battery.
+ */
+static void fullbatt_handler(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+
+       if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms)
+               goto out;
+
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       mod_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+                        msecs_to_jiffies(desc->fullbatt_vchkdrop_ms));
+       cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies(
+                                      desc->fullbatt_vchkdrop_ms);
+
+       if (cm->fullbatt_vchk_jiffies_at == 0)
+               cm->fullbatt_vchk_jiffies_at = 1;
+
+out:
+       dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n");
+       uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]);
+}
+
+/**
+ * battout_handler - Event handler for CM_EVENT_BATT_OUT
+ * @cm: the Charger Manager representing the battery.
+ */
+static void battout_handler(struct charger_manager *cm)
+{
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       if (!is_batt_present(cm)) {
+               dev_emerg(cm->dev, "Battery Pulled Out!\n");
+               uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]);
+       } else {
+               uevent_notify(cm, "Battery Reinserted?");
+       }
+}
+
+/**
+ * misc_event_handler - Handler for other evnets
+ * @cm: the Charger Manager representing the battery.
+ * @type: the Charger Manager representing the battery.
+ */
+static void misc_event_handler(struct charger_manager *cm,
+                       enum cm_event_types type)
+{
+       if (cm_suspended)
+               device_set_wakeup_capable(cm->dev, true);
+
+       if (is_polling_required(cm) && cm->desc->polling_interval_ms)
+               schedule_work(&setup_polling);
+       uevent_notify(cm, default_event_names[type]);
+}
+
+static int charger_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct charger_manager *cm = power_supply_get_drvdata(psy);
+       struct charger_desc *desc = cm->desc;
+       struct power_supply *fuel_gauge = NULL;
+       int ret = 0;
+       int uV;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (is_charging(cm))
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (is_ext_pwr_online(cm))
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (cm->emergency_stop > 0)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else if (cm->emergency_stop < 0)
+                       val->intval = POWER_SUPPLY_HEALTH_COLD;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               if (is_batt_present(cm))
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = get_batt_uV(cm, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+               if (!fuel_gauge) {
+                       ret = -ENODEV;
+                       break;
+               }
+               ret = power_supply_get_property(fuel_gauge,
+                               POWER_SUPPLY_PROP_CURRENT_NOW, val);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+       case POWER_SUPPLY_PROP_TEMP_AMBIENT:
+               return cm_get_battery_temperature(cm, &val->intval);
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (!is_batt_present(cm)) {
+                       /* There is no battery. Assume 100% */
+                       val->intval = 100;
+                       break;
+               }
+
+               fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+               if (!fuel_gauge) {
+                       ret = -ENODEV;
+                       break;
+               }
+
+               ret = power_supply_get_property(fuel_gauge,
+                                       POWER_SUPPLY_PROP_CAPACITY, val);
+               if (ret)
+                       break;
+
+               if (val->intval > 100) {
+                       val->intval = 100;
+                       break;
+               }
+               if (val->intval < 0)
+                       val->intval = 0;
+
+               /* Do not adjust SOC when charging: voltage is overrated */
+               if (is_charging(cm))
+                       break;
+
+               /*
+                * If the capacity value is inconsistent, calibrate it base on
+                * the battery voltage values and the thresholds given as desc
+                */
+               ret = get_batt_uV(cm, &uV);
+               if (ret) {
+                       /* Voltage information not available. No calibration */
+                       ret = 0;
+                       break;
+               }
+
+               if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
+                   !is_charging(cm)) {
+                       val->intval = 100;
+                       break;
+               }
+
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               if (is_ext_pwr_online(cm))
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               if (is_full_charged(cm))
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               ret = 0;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               if (is_charging(cm)) {
+                       fuel_gauge = power_supply_get_by_name(
+                                       cm->desc->psy_fuel_gauge);
+                       if (!fuel_gauge) {
+                               ret = -ENODEV;
+                               break;
+                       }
+
+                       ret = power_supply_get_property(fuel_gauge,
+                                               POWER_SUPPLY_PROP_CHARGE_NOW,
+                                               val);
+                       if (ret) {
+                               val->intval = 1;
+                               ret = 0;
+                       } else {
+                               /* If CHARGE_NOW is supplied, use it */
+                               val->intval = (val->intval > 0) ?
+                                               val->intval : 1;
+                       }
+               } else {
+                       val->intval = 0;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       if (fuel_gauge)
+               power_supply_put(fuel_gauge);
+       return ret;
+}
+
+#define NUM_CHARGER_PSY_OPTIONAL       (4)
+static enum power_supply_property default_charger_props[] = {
+       /* Guaranteed to provide */
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       /*
+        * Optional properties are:
+        * POWER_SUPPLY_PROP_CHARGE_NOW,
+        * POWER_SUPPLY_PROP_CURRENT_NOW,
+        * POWER_SUPPLY_PROP_TEMP, and
+        * POWER_SUPPLY_PROP_TEMP_AMBIENT,
+        */
+};
+
+static const struct power_supply_desc psy_default = {
+       .name = "battery",
+       .type = POWER_SUPPLY_TYPE_BATTERY,
+       .properties = default_charger_props,
+       .num_properties = ARRAY_SIZE(default_charger_props),
+       .get_property = charger_get_property,
+       .no_thermal = true,
+};
+
+/**
+ * cm_setup_timer - For in-suspend monitoring setup wakeup alarm
+ *                 for suspend_again.
+ *
+ * Returns true if the alarm is set for Charger Manager to use.
+ * Returns false if
+ *     cm_setup_timer fails to set an alarm,
+ *     cm_setup_timer does not need to set an alarm for Charger Manager,
+ *     or an alarm previously configured is to be used.
+ */
+static bool cm_setup_timer(void)
+{
+       struct charger_manager *cm;
+       unsigned int wakeup_ms = UINT_MAX;
+       int timer_req = 0;
+
+       if (time_after(next_polling, jiffies))
+               CM_MIN_VALID(wakeup_ms,
+                       jiffies_to_msecs(next_polling - jiffies));
+
+       mutex_lock(&cm_list_mtx);
+       list_for_each_entry(cm, &cm_list, entry) {
+               unsigned int fbchk_ms = 0;
+
+               /* fullbatt_vchk is required. setup timer for that */
+               if (cm->fullbatt_vchk_jiffies_at) {
+                       fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at
+                                                   - jiffies);
+                       if (time_is_before_eq_jiffies(
+                               cm->fullbatt_vchk_jiffies_at) ||
+                               msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) {
+                               fullbatt_vchk(&cm->fullbatt_vchk_work.work);
+                               fbchk_ms = 0;
+                       }
+               }
+               CM_MIN_VALID(wakeup_ms, fbchk_ms);
+
+               /* Skip if polling is not required for this CM */
+               if (!is_polling_required(cm) && !cm->emergency_stop)
+                       continue;
+               timer_req++;
+               if (cm->desc->polling_interval_ms == 0)
+                       continue;
+               CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
+       }
+       mutex_unlock(&cm_list_mtx);
+
+       if (timer_req && cm_timer) {
+               ktime_t now, add;
+
+               /*
+                * Set alarm with the polling interval (wakeup_ms)
+                * The alarm time should be NOW + CM_RTC_SMALL or later.
+                */
+               if (wakeup_ms == UINT_MAX ||
+                       wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC)
+                       wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC;
+
+               pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms);
+
+               now = ktime_get_boottime();
+               add = ktime_set(wakeup_ms / MSEC_PER_SEC,
+                               (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC);
+               alarm_start(cm_timer, ktime_add(now, add));
+
+               cm_suspend_duration_ms = wakeup_ms;
+
+               return true;
+       }
+       return false;
+}
+
+/**
+ * charger_extcon_work - enable/diable charger according to the state
+ *                     of charger cable
+ *
+ * @work: work_struct of the function charger_extcon_work.
+ */
+static void charger_extcon_work(struct work_struct *work)
+{
+       struct charger_cable *cable =
+                       container_of(work, struct charger_cable, wq);
+       int ret;
+
+       if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) {
+               ret = regulator_set_current_limit(cable->charger->consumer,
+                                       cable->min_uA, cable->max_uA);
+               if (ret < 0) {
+                       pr_err("Cannot set current limit of %s (%s)\n",
+                              cable->charger->regulator_name, cable->name);
+                       return;
+               }
+
+               pr_info("Set current limit of %s : %duA ~ %duA\n",
+                       cable->charger->regulator_name,
+                       cable->min_uA, cable->max_uA);
+       }
+
+       try_charger_enable(cable->cm, cable->attached);
+}
+
+/**
+ * charger_extcon_notifier - receive the state of charger cable
+ *                     when registered cable is attached or detached.
+ *
+ * @self: the notifier block of the charger_extcon_notifier.
+ * @event: the cable state.
+ * @ptr: the data pointer of notifier block.
+ */
+static int charger_extcon_notifier(struct notifier_block *self,
+                       unsigned long event, void *ptr)
+{
+       struct charger_cable *cable =
+               container_of(self, struct charger_cable, nb);
+
+       /*
+        * The newly state of charger cable.
+        * If cable is attached, cable->attached is true.
+        */
+       cable->attached = event;
+
+       /*
+        * Setup monitoring to check battery state
+        * when charger cable is attached.
+        */
+       if (cable->attached && is_polling_required(cable->cm)) {
+               cancel_work_sync(&setup_polling);
+               schedule_work(&setup_polling);
+       }
+
+       /*
+        * Setup work for controlling charger(regulator)
+        * according to charger cable.
+        */
+       schedule_work(&cable->wq);
+
+       return NOTIFY_DONE;
+}
+
+/**
+ * charger_extcon_init - register external connector to use it
+ *                     as the charger cable
+ *
+ * @cm: the Charger Manager representing the battery.
+ * @cable: the Charger cable representing the external connector.
+ */
+static int charger_extcon_init(struct charger_manager *cm,
+               struct charger_cable *cable)
+{
+       int ret = 0;
+
+       /*
+        * Charger manager use Extcon framework to identify
+        * the charger cable among various external connector
+        * cable (e.g., TA, USB, MHL, Dock).
+        */
+       INIT_WORK(&cable->wq, charger_extcon_work);
+       cable->nb.notifier_call = charger_extcon_notifier;
+       ret = extcon_register_interest(&cable->extcon_dev,
+                       cable->extcon_name, cable->name, &cable->nb);
+       if (ret < 0) {
+               pr_info("Cannot register extcon_dev for %s(cable: %s)\n",
+                       cable->extcon_name, cable->name);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/**
+ * charger_manager_register_extcon - Register extcon device to recevie state
+ *                                  of charger cable.
+ * @cm: the Charger Manager representing the battery.
+ *
+ * This function support EXTCON(External Connector) subsystem to detect the
+ * state of charger cables for enabling or disabling charger(regulator) and
+ * select the charger cable for charging among a number of external cable
+ * according to policy of H/W board.
+ */
+static int charger_manager_register_extcon(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+       struct charger_regulator *charger;
+       int ret = 0;
+       int i;
+       int j;
+
+       for (i = 0; i < desc->num_charger_regulators; i++) {
+               charger = &desc->charger_regulators[i];
+
+               charger->consumer = regulator_get(cm->dev,
+                                       charger->regulator_name);
+               if (IS_ERR(charger->consumer)) {
+                       dev_err(cm->dev, "Cannot find charger(%s)\n",
+                               charger->regulator_name);
+                       return PTR_ERR(charger->consumer);
+               }
+               charger->cm = cm;
+
+               for (j = 0; j < charger->num_cables; j++) {
+                       struct charger_cable *cable = &charger->cables[j];
+
+                       ret = charger_extcon_init(cm, cable);
+                       if (ret < 0) {
+                               dev_err(cm->dev, "Cannot initialize charger(%s)\n",
+                                       charger->regulator_name);
+                               goto err;
+                       }
+                       cable->charger = charger;
+                       cable->cm = cm;
+               }
+       }
+
+err:
+       return ret;
+}
+
+/* help function of sysfs node to control charger(regulator) */
+static ssize_t charger_name_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct charger_regulator *charger
+               = container_of(attr, struct charger_regulator, attr_name);
+
+       return sprintf(buf, "%s\n", charger->regulator_name);
+}
+
+static ssize_t charger_state_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct charger_regulator *charger
+               = container_of(attr, struct charger_regulator, attr_state);
+       int state = 0;
+
+       if (!charger->externally_control)
+               state = regulator_is_enabled(charger->consumer);
+
+       return sprintf(buf, "%s\n", state ? "enabled" : "disabled");
+}
+
+static ssize_t charger_externally_control_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct charger_regulator *charger = container_of(attr,
+                       struct charger_regulator, attr_externally_control);
+
+       return sprintf(buf, "%d\n", charger->externally_control);
+}
+
+static ssize_t charger_externally_control_store(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
+{
+       struct charger_regulator *charger
+               = container_of(attr, struct charger_regulator,
+                                       attr_externally_control);
+       struct charger_manager *cm = charger->cm;
+       struct charger_desc *desc = cm->desc;
+       int i;
+       int ret;
+       int externally_control;
+       int chargers_externally_control = 1;
+
+       ret = sscanf(buf, "%d", &externally_control);
+       if (ret == 0) {
+               ret = -EINVAL;
+               return ret;
+       }
+
+       if (!externally_control) {
+               charger->externally_control = 0;
+               return count;
+       }
+
+       for (i = 0; i < desc->num_charger_regulators; i++) {
+               if (&desc->charger_regulators[i] != charger &&
+                       !desc->charger_regulators[i].externally_control) {
+                       /*
+                        * At least, one charger is controlled by
+                        * charger-manager
+                        */
+                       chargers_externally_control = 0;
+                       break;
+               }
+       }
+
+       if (!chargers_externally_control) {
+               if (cm->charger_enabled) {
+                       try_charger_enable(charger->cm, false);
+                       charger->externally_control = externally_control;
+                       try_charger_enable(charger->cm, true);
+               } else {
+                       charger->externally_control = externally_control;
+               }
+       } else {
+               dev_warn(cm->dev,
+                        "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging\n",
+                        charger->regulator_name);
+       }
+
+       return count;
+}
+
+/**
+ * charger_manager_register_sysfs - Register sysfs entry for each charger
+ * @cm: the Charger Manager representing the battery.
+ *
+ * This function add sysfs entry for charger(regulator) to control charger from
+ * user-space. If some development board use one more chargers for charging
+ * but only need one charger on specific case which is dependent on user
+ * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/
+ * class/power_supply/battery/charger.[index]/externally_control'. For example,
+ * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/
+ * externally_control, this charger isn't controlled from charger-manager and
+ * always stay off state of regulator.
+ */
+static int charger_manager_register_sysfs(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+       struct charger_regulator *charger;
+       int chargers_externally_control = 1;
+       char buf[11];
+       char *str;
+       int ret = 0;
+       int i;
+
+       /* Create sysfs entry to control charger(regulator) */
+       for (i = 0; i < desc->num_charger_regulators; i++) {
+               charger = &desc->charger_regulators[i];
+
+               snprintf(buf, 10, "charger.%d", i);
+               str = devm_kzalloc(cm->dev,
+                               sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
+               if (!str) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+               strcpy(str, buf);
+
+               charger->attrs[0] = &charger->attr_name.attr;
+               charger->attrs[1] = &charger->attr_state.attr;
+               charger->attrs[2] = &charger->attr_externally_control.attr;
+               charger->attrs[3] = NULL;
+               charger->attr_g.name = str;
+               charger->attr_g.attrs = charger->attrs;
+
+               sysfs_attr_init(&charger->attr_name.attr);
+               charger->attr_name.attr.name = "name";
+               charger->attr_name.attr.mode = 0444;
+               charger->attr_name.show = charger_name_show;
+
+               sysfs_attr_init(&charger->attr_state.attr);
+               charger->attr_state.attr.name = "state";
+               charger->attr_state.attr.mode = 0444;
+               charger->attr_state.show = charger_state_show;
+
+               sysfs_attr_init(&charger->attr_externally_control.attr);
+               charger->attr_externally_control.attr.name
+                               = "externally_control";
+               charger->attr_externally_control.attr.mode = 0644;
+               charger->attr_externally_control.show
+                               = charger_externally_control_show;
+               charger->attr_externally_control.store
+                               = charger_externally_control_store;
+
+               if (!desc->charger_regulators[i].externally_control ||
+                               !chargers_externally_control)
+                       chargers_externally_control = 0;
+
+               dev_info(cm->dev, "'%s' regulator's externally_control is %d\n",
+                        charger->regulator_name, charger->externally_control);
+
+               ret = sysfs_create_group(&cm->charger_psy->dev.kobj,
+                                       &charger->attr_g);
+               if (ret < 0) {
+                       dev_err(cm->dev, "Cannot create sysfs entry of %s regulator\n",
+                               charger->regulator_name);
+                       ret = -EINVAL;
+                       goto err;
+               }
+       }
+
+       if (chargers_externally_control) {
+               dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+err:
+       return ret;
+}
+
+static int cm_init_thermal_data(struct charger_manager *cm,
+               struct power_supply *fuel_gauge)
+{
+       struct charger_desc *desc = cm->desc;
+       union power_supply_propval val;
+       int ret;
+
+       /* Verify whether fuel gauge provides battery temperature */
+       ret = power_supply_get_property(fuel_gauge,
+                                       POWER_SUPPLY_PROP_TEMP, &val);
+
+       if (!ret) {
+               cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
+                               POWER_SUPPLY_PROP_TEMP;
+               cm->charger_psy_desc.num_properties++;
+               cm->desc->measure_battery_temp = true;
+       }
+#ifdef CONFIG_THERMAL
+       if (ret && desc->thermal_zone) {
+               cm->tzd_batt =
+                       thermal_zone_get_zone_by_name(desc->thermal_zone);
+               if (IS_ERR(cm->tzd_batt))
+                       return PTR_ERR(cm->tzd_batt);
+
+               /* Use external thermometer */
+               cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
+                               POWER_SUPPLY_PROP_TEMP_AMBIENT;
+               cm->charger_psy_desc.num_properties++;
+               cm->desc->measure_battery_temp = true;
+               ret = 0;
+       }
+#endif
+       if (cm->desc->measure_battery_temp) {
+               /* NOTICE : Default allowable minimum charge temperature is 0 */
+               if (!desc->temp_max)
+                       desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX;
+               if (!desc->temp_diff)
+                       desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF;
+       }
+
+       return ret;
+}
+
+static const struct of_device_id charger_manager_match[] = {
+       {
+               .compatible = "charger-manager",
+       },
+       {},
+};
+
+static struct charger_desc *of_cm_parse_desc(struct device *dev)
+{
+       struct charger_desc *desc;
+       struct device_node *np = dev->of_node;
+       u32 poll_mode = CM_POLL_DISABLE;
+       u32 battery_stat = CM_NO_BATTERY;
+       int num_chgs = 0;
+
+       desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+       if (!desc)
+               return ERR_PTR(-ENOMEM);
+
+       of_property_read_string(np, "cm-name", &desc->psy_name);
+
+       of_property_read_u32(np, "cm-poll-mode", &poll_mode);
+       desc->polling_mode = poll_mode;
+
+       of_property_read_u32(np, "cm-poll-interval",
+                               &desc->polling_interval_ms);
+
+       of_property_read_u32(np, "cm-fullbatt-vchkdrop-ms",
+                                       &desc->fullbatt_vchkdrop_ms);
+       of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt",
+                                       &desc->fullbatt_vchkdrop_uV);
+       of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV);
+       of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc);
+       of_property_read_u32(np, "cm-fullbatt-capacity",
+                                       &desc->fullbatt_full_capacity);
+
+       of_property_read_u32(np, "cm-battery-stat", &battery_stat);
+       desc->battery_present = battery_stat;
+
+       /* chargers */
+       of_property_read_u32(np, "cm-num-chargers", &num_chgs);
+       if (num_chgs) {
+               /* Allocate empty bin at the tail of array */
+               desc->psy_charger_stat = devm_kzalloc(dev, sizeof(char *)
+                                               * (num_chgs + 1), GFP_KERNEL);
+               if (desc->psy_charger_stat) {
+                       int i;
+                       for (i = 0; i < num_chgs; i++)
+                               of_property_read_string_index(np, "cm-chargers",
+                                               i, &desc->psy_charger_stat[i]);
+               } else {
+                       return ERR_PTR(-ENOMEM);
+               }
+       }
+
+       of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge);
+
+       of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone);
+
+       of_property_read_u32(np, "cm-battery-cold", &desc->temp_min);
+       if (of_get_property(np, "cm-battery-cold-in-minus", NULL))
+               desc->temp_min *= -1;
+       of_property_read_u32(np, "cm-battery-hot", &desc->temp_max);
+       of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff);
+
+       of_property_read_u32(np, "cm-charging-max",
+                               &desc->charging_max_duration_ms);
+       of_property_read_u32(np, "cm-discharging-max",
+                               &desc->discharging_max_duration_ms);
+
+       /* battery charger regualtors */
+       desc->num_charger_regulators = of_get_child_count(np);
+       if (desc->num_charger_regulators) {
+               struct charger_regulator *chg_regs;
+               struct device_node *child;
+
+               chg_regs = devm_kzalloc(dev, sizeof(*chg_regs)
+                                       * desc->num_charger_regulators,
+                                       GFP_KERNEL);
+               if (!chg_regs)
+                       return ERR_PTR(-ENOMEM);
+
+               desc->charger_regulators = chg_regs;
+
+               for_each_child_of_node(np, child) {
+                       struct charger_cable *cables;
+                       struct device_node *_child;
+
+                       of_property_read_string(child, "cm-regulator-name",
+                                       &chg_regs->regulator_name);
+
+                       /* charger cables */
+                       chg_regs->num_cables = of_get_child_count(child);
+                       if (chg_regs->num_cables) {
+                               cables = devm_kzalloc(dev, sizeof(*cables)
+                                               * chg_regs->num_cables,
+                                               GFP_KERNEL);
+                               if (!cables) {
+                                       of_node_put(child);
+                                       return ERR_PTR(-ENOMEM);
+                               }
+
+                               chg_regs->cables = cables;
+
+                               for_each_child_of_node(child, _child) {
+                                       of_property_read_string(_child,
+                                       "cm-cable-name", &cables->name);
+                                       of_property_read_string(_child,
+                                       "cm-cable-extcon",
+                                       &cables->extcon_name);
+                                       of_property_read_u32(_child,
+                                       "cm-cable-min",
+                                       &cables->min_uA);
+                                       of_property_read_u32(_child,
+                                       "cm-cable-max",
+                                       &cables->max_uA);
+                                       cables++;
+                               }
+                       }
+                       chg_regs++;
+               }
+       }
+       return desc;
+}
+
+static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev)
+{
+       if (pdev->dev.of_node)
+               return of_cm_parse_desc(&pdev->dev);
+       return dev_get_platdata(&pdev->dev);
+}
+
+static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now)
+{
+       cm_timer_set = false;
+       return ALARMTIMER_NORESTART;
+}
+
+static int charger_manager_probe(struct platform_device *pdev)
+{
+       struct charger_desc *desc = cm_get_drv_data(pdev);
+       struct charger_manager *cm;
+       int ret = 0, i = 0;
+       int j = 0;
+       union power_supply_propval val;
+       struct power_supply *fuel_gauge;
+       struct power_supply_config psy_cfg = {};
+
+       if (IS_ERR(desc)) {
+               dev_err(&pdev->dev, "No platform data (desc) found\n");
+               return -ENODEV;
+       }
+
+       cm = devm_kzalloc(&pdev->dev,
+                       sizeof(struct charger_manager), GFP_KERNEL);
+       if (!cm)
+               return -ENOMEM;
+
+       /* Basic Values. Unspecified are Null or 0 */
+       cm->dev = &pdev->dev;
+       cm->desc = desc;
+       psy_cfg.drv_data = cm;
+
+       /* Initialize alarm timer */
+       if (alarmtimer_get_rtcdev()) {
+               cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
+               alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
+       }
+
+       /*
+        * The following two do not need to be errors.
+        * Users may intentionally ignore those two features.
+        */
+       if (desc->fullbatt_uV == 0) {
+               dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n");
+       }
+       if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) {
+               dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n");
+               desc->fullbatt_vchkdrop_ms = 0;
+               desc->fullbatt_vchkdrop_uV = 0;
+       }
+       if (desc->fullbatt_soc == 0) {
+               dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n");
+       }
+       if (desc->fullbatt_full_capacity == 0) {
+               dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n");
+       }
+
+       if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
+               dev_err(&pdev->dev, "charger_regulators undefined\n");
+               return -EINVAL;
+       }
+
+       if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
+               dev_err(&pdev->dev, "No power supply defined\n");
+               return -EINVAL;
+       }
+
+       if (!desc->psy_fuel_gauge) {
+               dev_err(&pdev->dev, "No fuel gauge power supply defined\n");
+               return -EINVAL;
+       }
+
+       /* Counting index only */
+       while (desc->psy_charger_stat[i])
+               i++;
+
+       /* Check if charger's supplies are present at probe */
+       for (i = 0; desc->psy_charger_stat[i]; i++) {
+               struct power_supply *psy;
+
+               psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
+               if (!psy) {
+                       dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
+                               desc->psy_charger_stat[i]);
+                       return -ENODEV;
+               }
+               power_supply_put(psy);
+       }
+
+       if (desc->polling_interval_ms == 0 ||
+           msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
+               dev_err(&pdev->dev, "polling_interval_ms is too small\n");
+               return -EINVAL;
+       }
+
+       if (!desc->charging_max_duration_ms ||
+                       !desc->discharging_max_duration_ms) {
+               dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n");
+               desc->charging_max_duration_ms = 0;
+               desc->discharging_max_duration_ms = 0;
+       }
+
+       platform_set_drvdata(pdev, cm);
+
+       memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default));
+
+       if (!desc->psy_name)
+               strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
+       else
+               strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
+       cm->charger_psy_desc.name = cm->psy_name_buf;
+
+       /* Allocate for psy properties because they may vary */
+       cm->charger_psy_desc.properties = devm_kzalloc(&pdev->dev,
+                               sizeof(enum power_supply_property)
+                               * (ARRAY_SIZE(default_charger_props) +
+                               NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL);
+       if (!cm->charger_psy_desc.properties)
+               return -ENOMEM;
+
+       memcpy(cm->charger_psy_desc.properties, default_charger_props,
+               sizeof(enum power_supply_property) *
+               ARRAY_SIZE(default_charger_props));
+       cm->charger_psy_desc.num_properties = psy_default.num_properties;
+
+       /* Find which optional psy-properties are available */
+       fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
+       if (!fuel_gauge) {
+               dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
+                       desc->psy_fuel_gauge);
+               return -ENODEV;
+       }
+       if (!power_supply_get_property(fuel_gauge,
+                                         POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
+               cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
+                               POWER_SUPPLY_PROP_CHARGE_NOW;
+               cm->charger_psy_desc.num_properties++;
+       }
+       if (!power_supply_get_property(fuel_gauge,
+                                         POWER_SUPPLY_PROP_CURRENT_NOW,
+                                         &val)) {
+               cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] =
+                               POWER_SUPPLY_PROP_CURRENT_NOW;
+               cm->charger_psy_desc.num_properties++;
+       }
+
+       ret = cm_init_thermal_data(cm, fuel_gauge);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to initialize thermal data\n");
+               cm->desc->measure_battery_temp = false;
+       }
+       power_supply_put(fuel_gauge);
+
+       INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
+
+       cm->charger_psy = power_supply_register(&pdev->dev,
+                                               &cm->charger_psy_desc,
+                                               &psy_cfg);
+       if (IS_ERR(cm->charger_psy)) {
+               dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
+                       cm->charger_psy_desc.name);
+               return PTR_ERR(cm->charger_psy);
+       }
+
+       /* Register extcon device for charger cable */
+       ret = charger_manager_register_extcon(cm);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Cannot initialize extcon device\n");
+               goto err_reg_extcon;
+       }
+
+       /* Register sysfs entry for charger(regulator) */
+       ret = charger_manager_register_sysfs(cm);
+       if (ret < 0) {
+               dev_err(&pdev->dev,
+                       "Cannot initialize sysfs entry of regulator\n");
+               goto err_reg_sysfs;
+       }
+
+       /* Add to the list */
+       mutex_lock(&cm_list_mtx);
+       list_add(&cm->entry, &cm_list);
+       mutex_unlock(&cm_list_mtx);
+
+       /*
+        * Charger-manager is capable of waking up the systme from sleep
+        * when event is happend through cm_notify_event()
+        */
+       device_init_wakeup(&pdev->dev, true);
+       device_set_wakeup_capable(&pdev->dev, false);
+
+       /*
+        * Charger-manager have to check the charging state right after
+        * tialization of charger-manager and then update current charging
+        * state.
+        */
+       cm_monitor();
+
+       schedule_work(&setup_polling);
+
+       return 0;
+
+err_reg_sysfs:
+       for (i = 0; i < desc->num_charger_regulators; i++) {
+               struct charger_regulator *charger;
+
+               charger = &desc->charger_regulators[i];
+               sysfs_remove_group(&cm->charger_psy->dev.kobj,
+                               &charger->attr_g);
+       }
+err_reg_extcon:
+       for (i = 0; i < desc->num_charger_regulators; i++) {
+               struct charger_regulator *charger;
+
+               charger = &desc->charger_regulators[i];
+               for (j = 0; j < charger->num_cables; j++) {
+                       struct charger_cable *cable = &charger->cables[j];
+                       /* Remove notifier block if only edev exists */
+                       if (cable->extcon_dev.edev)
+                               extcon_unregister_interest(&cable->extcon_dev);
+               }
+
+               regulator_put(desc->charger_regulators[i].consumer);
+       }
+
+       power_supply_unregister(cm->charger_psy);
+
+       return ret;
+}
+
+static int charger_manager_remove(struct platform_device *pdev)
+{
+       struct charger_manager *cm = platform_get_drvdata(pdev);
+       struct charger_desc *desc = cm->desc;
+       int i = 0;
+       int j = 0;
+
+       /* Remove from the list */
+       mutex_lock(&cm_list_mtx);
+       list_del(&cm->entry);
+       mutex_unlock(&cm_list_mtx);
+
+       cancel_work_sync(&setup_polling);
+       cancel_delayed_work_sync(&cm_monitor_work);
+
+       for (i = 0 ; i < desc->num_charger_regulators ; i++) {
+               struct charger_regulator *charger
+                               = &desc->charger_regulators[i];
+               for (j = 0 ; j < charger->num_cables ; j++) {
+                       struct charger_cable *cable = &charger->cables[j];
+                       extcon_unregister_interest(&cable->extcon_dev);
+               }
+       }
+
+       for (i = 0 ; i < desc->num_charger_regulators ; i++)
+               regulator_put(desc->charger_regulators[i].consumer);
+
+       power_supply_unregister(cm->charger_psy);
+
+       try_charger_enable(cm, false);
+
+       return 0;
+}
+
+static const struct platform_device_id charger_manager_id[] = {
+       { "charger-manager", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(platform, charger_manager_id);
+
+static int cm_suspend_noirq(struct device *dev)
+{
+       int ret = 0;
+
+       if (device_may_wakeup(dev)) {
+               device_set_wakeup_capable(dev, false);
+               ret = -EAGAIN;
+       }
+
+       return ret;
+}
+
+static bool cm_need_to_awake(void)
+{
+       struct charger_manager *cm;
+
+       if (cm_timer)
+               return false;
+
+       mutex_lock(&cm_list_mtx);
+       list_for_each_entry(cm, &cm_list, entry) {
+               if (is_charging(cm)) {
+                       mutex_unlock(&cm_list_mtx);
+                       return true;
+               }
+       }
+       mutex_unlock(&cm_list_mtx);
+
+       return false;
+}
+
+static int cm_suspend_prepare(struct device *dev)
+{
+       struct charger_manager *cm = dev_get_drvdata(dev);
+
+       if (cm_need_to_awake())
+               return -EBUSY;
+
+       if (!cm_suspended)
+               cm_suspended = true;
+
+       cm_timer_set = cm_setup_timer();
+
+       if (cm_timer_set) {
+               cancel_work_sync(&setup_polling);
+               cancel_delayed_work_sync(&cm_monitor_work);
+               cancel_delayed_work(&cm->fullbatt_vchk_work);
+       }
+
+       return 0;
+}
+
+static void cm_suspend_complete(struct device *dev)
+{
+       struct charger_manager *cm = dev_get_drvdata(dev);
+
+       if (cm_suspended)
+               cm_suspended = false;
+
+       if (cm_timer_set) {
+               ktime_t remain;
+
+               alarm_cancel(cm_timer);
+               cm_timer_set = false;
+               remain = alarm_expires_remaining(cm_timer);
+               cm_suspend_duration_ms -= ktime_to_ms(remain);
+               schedule_work(&setup_polling);
+       }
+
+       _cm_monitor(cm);
+
+       /* Re-enqueue delayed work (fullbatt_vchk_work) */
+       if (cm->fullbatt_vchk_jiffies_at) {
+               unsigned long delay = 0;
+               unsigned long now = jiffies + CM_JIFFIES_SMALL;
+
+               if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) {
+                       delay = (unsigned long)((long)now
+                               - (long)(cm->fullbatt_vchk_jiffies_at));
+                       delay = jiffies_to_msecs(delay);
+               } else {
+                       delay = 0;
+               }
+
+               /*
+                * Account for cm_suspend_duration_ms with assuming that
+                * timer stops in suspend.
+                */
+               if (delay > cm_suspend_duration_ms)
+                       delay -= cm_suspend_duration_ms;
+               else
+                       delay = 0;
+
+               queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work,
+                                  msecs_to_jiffies(delay));
+       }
+       device_set_wakeup_capable(cm->dev, false);
+}
+
+static const struct dev_pm_ops charger_manager_pm = {
+       .prepare        = cm_suspend_prepare,
+       .suspend_noirq  = cm_suspend_noirq,
+       .complete       = cm_suspend_complete,
+};
+
+static struct platform_driver charger_manager_driver = {
+       .driver = {
+               .name = "charger-manager",
+               .pm = &charger_manager_pm,
+               .of_match_table = charger_manager_match,
+       },
+       .probe = charger_manager_probe,
+       .remove = charger_manager_remove,
+       .id_table = charger_manager_id,
+};
+
+static int __init charger_manager_init(void)
+{
+       cm_wq = create_freezable_workqueue("charger_manager");
+       INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller);
+
+       return platform_driver_register(&charger_manager_driver);
+}
+late_initcall(charger_manager_init);
+
+static void __exit charger_manager_cleanup(void)
+{
+       destroy_workqueue(cm_wq);
+       cm_wq = NULL;
+
+       platform_driver_unregister(&charger_manager_driver);
+}
+module_exit(charger_manager_cleanup);
+
+/**
+ * cm_notify_event - charger driver notify Charger Manager of charger event
+ * @psy: pointer to instance of charger's power_supply
+ * @type: type of charger event
+ * @msg: optional message passed to uevent_notify fuction
+ */
+void cm_notify_event(struct power_supply *psy, enum cm_event_types type,
+                    char *msg)
+{
+       struct charger_manager *cm;
+       bool found_power_supply = false;
+
+       if (psy == NULL)
+               return;
+
+       mutex_lock(&cm_list_mtx);
+       list_for_each_entry(cm, &cm_list, entry) {
+               if (match_string(cm->desc->psy_charger_stat, -1,
+                                psy->desc->name) >= 0) {
+                       found_power_supply = true;
+                       break;
+               }
+       }
+       mutex_unlock(&cm_list_mtx);
+
+       if (!found_power_supply)
+               return;
+
+       switch (type) {
+       case CM_EVENT_BATT_FULL:
+               fullbatt_handler(cm);
+               break;
+       case CM_EVENT_BATT_OUT:
+               battout_handler(cm);
+               break;
+       case CM_EVENT_BATT_IN:
+       case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP:
+               misc_event_handler(cm, type);
+               break;
+       case CM_EVENT_UNKNOWN:
+       case CM_EVENT_OTHERS:
+               uevent_notify(cm, msg ? msg : default_event_names[type]);
+               break;
+       default:
+               dev_err(cm->dev, "%s: type not specified\n", __func__);
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(cm_notify_event);
+
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_DESCRIPTION("Charger Manager");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/collie_battery.c b/drivers/power/supply/collie_battery.c
new file mode 100644 (file)
index 0000000..3a0bc60
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Battery and Power Management code for the Sharp SL-5x00
+ *
+ * Copyright (C) 2009 Thomas Kunze
+ *
+ * based on tosa_battery.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mfd/ucb1x00.h>
+
+#include <asm/mach/sharpsl_param.h>
+#include <asm/mach-types.h>
+#include <mach/collie.h>
+
+static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
+static struct work_struct bat_work;
+static struct ucb1x00 *ucb;
+
+struct collie_bat {
+       int status;
+       struct power_supply *psy;
+       int full_chrg;
+
+       struct mutex work_lock; /* protects data */
+
+       bool (*is_present)(struct collie_bat *bat);
+       int gpio_full;
+       int gpio_charge_on;
+
+       int technology;
+
+       int gpio_bat;
+       int adc_bat;
+       int adc_bat_divider;
+       int bat_max;
+       int bat_min;
+
+       int gpio_temp;
+       int adc_temp;
+       int adc_temp_divider;
+};
+
+static struct collie_bat collie_bat_main;
+
+static unsigned long collie_read_bat(struct collie_bat *bat)
+{
+       unsigned long value = 0;
+
+       if (bat->gpio_bat < 0 || bat->adc_bat < 0)
+               return 0;
+       mutex_lock(&bat_lock);
+       gpio_set_value(bat->gpio_bat, 1);
+       msleep(5);
+       ucb1x00_adc_enable(ucb);
+       value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
+       ucb1x00_adc_disable(ucb);
+       gpio_set_value(bat->gpio_bat, 0);
+       mutex_unlock(&bat_lock);
+       value = value * 1000000 / bat->adc_bat_divider;
+
+       return value;
+}
+
+static unsigned long collie_read_temp(struct collie_bat *bat)
+{
+       unsigned long value = 0;
+       if (bat->gpio_temp < 0 || bat->adc_temp < 0)
+               return 0;
+
+       mutex_lock(&bat_lock);
+       gpio_set_value(bat->gpio_temp, 1);
+       msleep(5);
+       ucb1x00_adc_enable(ucb);
+       value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
+       ucb1x00_adc_disable(ucb);
+       gpio_set_value(bat->gpio_temp, 0);
+       mutex_unlock(&bat_lock);
+
+       value = value * 10000 / bat->adc_temp_divider;
+
+       return value;
+}
+
+static int collie_bat_get_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       int ret = 0;
+       struct collie_bat *bat = power_supply_get_drvdata(psy);
+
+       if (bat->is_present && !bat->is_present(bat)
+                       && psp != POWER_SUPPLY_PROP_PRESENT) {
+               return -ENODEV;
+       }
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = bat->status;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = bat->technology;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = collie_read_bat(bat);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               if (bat->full_chrg == -1)
+                       val->intval = bat->bat_max;
+               else
+                       val->intval = bat->full_chrg;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = bat->bat_max;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = bat->bat_min;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = collie_read_temp(bat);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = bat->is_present ? bat->is_present(bat) : 1;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static void collie_bat_external_power_changed(struct power_supply *psy)
+{
+       schedule_work(&bat_work);
+}
+
+static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
+{
+       pr_info("collie_bat_gpio irq\n");
+       schedule_work(&bat_work);
+       return IRQ_HANDLED;
+}
+
+static void collie_bat_update(struct collie_bat *bat)
+{
+       int old;
+       struct power_supply *psy = bat->psy;
+
+       mutex_lock(&bat->work_lock);
+
+       old = bat->status;
+
+       if (bat->is_present && !bat->is_present(bat)) {
+               printk(KERN_NOTICE "%s not present\n", psy->desc->name);
+               bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
+               bat->full_chrg = -1;
+       } else if (power_supply_am_i_supplied(psy)) {
+               if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+                       gpio_set_value(bat->gpio_charge_on, 1);
+                       mdelay(15);
+               }
+
+               if (gpio_get_value(bat->gpio_full)) {
+                       if (old == POWER_SUPPLY_STATUS_CHARGING ||
+                                       bat->full_chrg == -1)
+                               bat->full_chrg = collie_read_bat(bat);
+
+                       gpio_set_value(bat->gpio_charge_on, 0);
+                       bat->status = POWER_SUPPLY_STATUS_FULL;
+               } else {
+                       gpio_set_value(bat->gpio_charge_on, 1);
+                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
+               }
+       } else {
+               gpio_set_value(bat->gpio_charge_on, 0);
+               bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+
+       if (old != bat->status)
+               power_supply_changed(psy);
+
+       mutex_unlock(&bat->work_lock);
+}
+
+static void collie_bat_work(struct work_struct *work)
+{
+       collie_bat_update(&collie_bat_main);
+}
+
+
+static enum power_supply_property collie_bat_main_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static enum power_supply_property collie_bat_bu_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_PRESENT,
+};
+
+static const struct power_supply_desc collie_bat_main_desc = {
+       .name           = "main-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = collie_bat_main_props,
+       .num_properties = ARRAY_SIZE(collie_bat_main_props),
+       .get_property   = collie_bat_get_property,
+       .external_power_changed = collie_bat_external_power_changed,
+       .use_for_apm    = 1,
+};
+
+static struct collie_bat collie_bat_main = {
+       .status = POWER_SUPPLY_STATUS_DISCHARGING,
+       .full_chrg = -1,
+       .psy = NULL,
+
+       .gpio_full = COLLIE_GPIO_CO,
+       .gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
+
+       .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
+
+       .gpio_bat = COLLIE_GPIO_MBAT_ON,
+       .adc_bat = UCB_ADC_INP_AD1,
+       .adc_bat_divider = 155,
+       .bat_max = 4310000,
+       .bat_min = 1551 * 1000000 / 414,
+
+       .gpio_temp = COLLIE_GPIO_TMP_ON,
+       .adc_temp = UCB_ADC_INP_AD0,
+       .adc_temp_divider = 10000,
+};
+
+static const struct power_supply_desc collie_bat_bu_desc = {
+       .name           = "backup-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = collie_bat_bu_props,
+       .num_properties = ARRAY_SIZE(collie_bat_bu_props),
+       .get_property   = collie_bat_get_property,
+       .external_power_changed = collie_bat_external_power_changed,
+};
+
+static struct collie_bat collie_bat_bu = {
+       .status = POWER_SUPPLY_STATUS_UNKNOWN,
+       .full_chrg = -1,
+       .psy = NULL,
+
+       .gpio_full = -1,
+       .gpio_charge_on = -1,
+
+       .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
+
+       .gpio_bat = COLLIE_GPIO_BBAT_ON,
+       .adc_bat = UCB_ADC_INP_AD1,
+       .adc_bat_divider = 155,
+       .bat_max = 3000000,
+       .bat_min = 1900000,
+
+       .gpio_temp = -1,
+       .adc_temp = -1,
+       .adc_temp_divider = -1,
+};
+
+static struct gpio collie_batt_gpios[] = {
+       { COLLIE_GPIO_CO,           GPIOF_IN,           "main battery full" },
+       { COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN,           "main battery low" },
+       { COLLIE_GPIO_CHARGE_ON,    GPIOF_OUT_INIT_LOW, "main charge on" },
+       { COLLIE_GPIO_MBAT_ON,      GPIOF_OUT_INIT_LOW, "main battery" },
+       { COLLIE_GPIO_TMP_ON,       GPIOF_OUT_INIT_LOW, "main battery temp" },
+       { COLLIE_GPIO_BBAT_ON,      GPIOF_OUT_INIT_LOW, "backup battery" },
+};
+
+#ifdef CONFIG_PM
+static int wakeup_enabled;
+
+static int collie_bat_suspend(struct ucb1x00_dev *dev)
+{
+       /* flush all pending status updates */
+       flush_work(&bat_work);
+
+       if (device_may_wakeup(&dev->ucb->dev) &&
+           collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
+               wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
+       else
+               wakeup_enabled = 0;
+
+       return 0;
+}
+
+static int collie_bat_resume(struct ucb1x00_dev *dev)
+{
+       if (wakeup_enabled)
+               disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
+
+       /* things may have changed while we were away */
+       schedule_work(&bat_work);
+       return 0;
+}
+#else
+#define collie_bat_suspend NULL
+#define collie_bat_resume NULL
+#endif
+
+static int collie_bat_probe(struct ucb1x00_dev *dev)
+{
+       int ret;
+       struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
+
+       if (!machine_is_collie())
+               return -ENODEV;
+
+       ucb = dev->ucb;
+
+       ret = gpio_request_array(collie_batt_gpios,
+                                ARRAY_SIZE(collie_batt_gpios));
+       if (ret)
+               return ret;
+
+       mutex_init(&collie_bat_main.work_lock);
+
+       INIT_WORK(&bat_work, collie_bat_work);
+
+       psy_main_cfg.drv_data = &collie_bat_main;
+       collie_bat_main.psy = power_supply_register(&dev->ucb->dev,
+                                                   &collie_bat_main_desc,
+                                                   &psy_main_cfg);
+       if (IS_ERR(collie_bat_main.psy)) {
+               ret = PTR_ERR(collie_bat_main.psy);
+               goto err_psy_reg_main;
+       }
+
+       psy_bu_cfg.drv_data = &collie_bat_bu;
+       collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
+                                                 &collie_bat_bu_desc,
+                                                 &psy_bu_cfg);
+       if (IS_ERR(collie_bat_bu.psy)) {
+               ret = PTR_ERR(collie_bat_bu.psy);
+               goto err_psy_reg_bu;
+       }
+
+       ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
+                               collie_bat_gpio_isr,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               "main full", &collie_bat_main);
+       if (ret)
+               goto err_irq;
+
+       device_init_wakeup(&ucb->dev, 1);
+       schedule_work(&bat_work);
+
+       return 0;
+
+err_irq:
+       power_supply_unregister(collie_bat_bu.psy);
+err_psy_reg_bu:
+       power_supply_unregister(collie_bat_main.psy);
+err_psy_reg_main:
+
+       /* see comment in collie_bat_remove */
+       cancel_work_sync(&bat_work);
+       gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
+       return ret;
+}
+
+static void collie_bat_remove(struct ucb1x00_dev *dev)
+{
+       free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
+
+       power_supply_unregister(collie_bat_bu.psy);
+       power_supply_unregister(collie_bat_main.psy);
+
+       /*
+        * Now cancel the bat_work.  We won't get any more schedules,
+        * since all sources (isr and external_power_changed) are
+        * unregistered now.
+        */
+       cancel_work_sync(&bat_work);
+       gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
+}
+
+static struct ucb1x00_driver collie_bat_driver = {
+       .add            = collie_bat_probe,
+       .remove         = collie_bat_remove,
+       .suspend        = collie_bat_suspend,
+       .resume         = collie_bat_resume,
+};
+
+static int __init collie_bat_init(void)
+{
+       return ucb1x00_register_driver(&collie_bat_driver);
+}
+
+static void __exit collie_bat_exit(void)
+{
+       ucb1x00_unregister_driver(&collie_bat_driver);
+}
+
+module_init(collie_bat_init);
+module_exit(collie_bat_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Kunze");
+MODULE_DESCRIPTION("Collie battery driver");
diff --git a/drivers/power/supply/da9030_battery.c b/drivers/power/supply/da9030_battery.c
new file mode 100644 (file)
index 0000000..5ca0f4d
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Battery charger driver for Dialog Semiconductor DA9030
+ *
+ * Copyright (C) 2008 Compulab, Ltd.
+ *     Mike Rapoport <mike@compulab.co.il>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/da903x.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/notifier.h>
+
+#define DA9030_FAULT_LOG               0x0a
+#define DA9030_FAULT_LOG_OVER_TEMP     (1 << 7)
+#define DA9030_FAULT_LOG_VBAT_OVER     (1 << 4)
+
+#define DA9030_CHARGE_CONTROL          0x28
+#define DA9030_CHRG_CHARGER_ENABLE     (1 << 7)
+
+#define DA9030_ADC_MAN_CONTROL         0x30
+#define DA9030_ADC_TBATREF_ENABLE      (1 << 5)
+#define DA9030_ADC_LDO_INT_ENABLE      (1 << 4)
+
+#define DA9030_ADC_AUTO_CONTROL                0x31
+#define DA9030_ADC_TBAT_ENABLE         (1 << 5)
+#define DA9030_ADC_VBAT_IN_TXON                (1 << 4)
+#define DA9030_ADC_VCH_ENABLE          (1 << 3)
+#define DA9030_ADC_ICH_ENABLE          (1 << 2)
+#define DA9030_ADC_VBAT_ENABLE         (1 << 1)
+#define DA9030_ADC_AUTO_SLEEP_ENABLE   (1 << 0)
+
+#define DA9030_VBATMON         0x32
+#define DA9030_VBATMONTXON     0x33
+#define DA9030_TBATHIGHP       0x34
+#define DA9030_TBATHIGHN       0x35
+#define DA9030_TBATLOW         0x36
+
+#define DA9030_VBAT_RES                0x41
+#define DA9030_VBATMIN_RES     0x42
+#define DA9030_VBATMINTXON_RES 0x43
+#define DA9030_ICHMAX_RES      0x44
+#define DA9030_ICHMIN_RES      0x45
+#define DA9030_ICHAVERAGE_RES  0x46
+#define DA9030_VCHMAX_RES      0x47
+#define DA9030_VCHMIN_RES      0x48
+#define DA9030_TBAT_RES                0x49
+
+struct da9030_adc_res {
+       uint8_t vbat_res;
+       uint8_t vbatmin_res;
+       uint8_t vbatmintxon;
+       uint8_t ichmax_res;
+       uint8_t ichmin_res;
+       uint8_t ichaverage_res;
+       uint8_t vchmax_res;
+       uint8_t vchmin_res;
+       uint8_t tbat_res;
+       uint8_t adc_in4_res;
+       uint8_t adc_in5_res;
+};
+
+struct da9030_battery_thresholds {
+       int tbat_low;
+       int tbat_high;
+       int tbat_restart;
+
+       int vbat_low;
+       int vbat_crit;
+       int vbat_charge_start;
+       int vbat_charge_stop;
+       int vbat_charge_restart;
+
+       int vcharge_min;
+       int vcharge_max;
+};
+
+struct da9030_charger {
+       struct power_supply *psy;
+       struct power_supply_desc psy_desc;
+
+       struct device *master;
+
+       struct da9030_adc_res adc;
+       struct delayed_work work;
+       unsigned int interval;
+
+       struct power_supply_info *battery_info;
+
+       struct da9030_battery_thresholds thresholds;
+
+       unsigned int charge_milliamp;
+       unsigned int charge_millivolt;
+
+       /* charger status */
+       bool chdet;
+       uint8_t fault;
+       int mA;
+       int mV;
+       bool is_on;
+
+       struct notifier_block nb;
+
+       /* platform callbacks for battery low and critical events */
+       void (*battery_low)(void);
+       void (*battery_critical)(void);
+
+       struct dentry *debug_file;
+};
+
+static inline int da9030_reg_to_mV(int reg)
+{
+       return ((reg * 2650) >> 8) + 2650;
+}
+
+static inline int da9030_millivolt_to_reg(int mV)
+{
+       return ((mV - 2650) << 8) / 2650;
+}
+
+static inline int da9030_reg_to_mA(int reg)
+{
+       return ((reg * 24000) >> 8) / 15;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int bat_debug_show(struct seq_file *s, void *data)
+{
+       struct da9030_charger *charger = s->private;
+
+       seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
+       if (charger->chdet) {
+               seq_printf(s, "iset = %dmA, vset = %dmV\n",
+                          charger->mA, charger->mV);
+       }
+
+       seq_printf(s, "vbat_res = %d (%dmV)\n",
+                  charger->adc.vbat_res,
+                  da9030_reg_to_mV(charger->adc.vbat_res));
+       seq_printf(s, "vbatmin_res = %d (%dmV)\n",
+                  charger->adc.vbatmin_res,
+                  da9030_reg_to_mV(charger->adc.vbatmin_res));
+       seq_printf(s, "vbatmintxon = %d (%dmV)\n",
+                  charger->adc.vbatmintxon,
+                  da9030_reg_to_mV(charger->adc.vbatmintxon));
+       seq_printf(s, "ichmax_res = %d (%dmA)\n",
+                  charger->adc.ichmax_res,
+                  da9030_reg_to_mV(charger->adc.ichmax_res));
+       seq_printf(s, "ichmin_res = %d (%dmA)\n",
+                  charger->adc.ichmin_res,
+                  da9030_reg_to_mA(charger->adc.ichmin_res));
+       seq_printf(s, "ichaverage_res = %d (%dmA)\n",
+                  charger->adc.ichaverage_res,
+                  da9030_reg_to_mA(charger->adc.ichaverage_res));
+       seq_printf(s, "vchmax_res = %d (%dmV)\n",
+                  charger->adc.vchmax_res,
+                  da9030_reg_to_mA(charger->adc.vchmax_res));
+       seq_printf(s, "vchmin_res = %d (%dmV)\n",
+                  charger->adc.vchmin_res,
+                  da9030_reg_to_mV(charger->adc.vchmin_res));
+
+       return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, bat_debug_show, inode->i_private);
+}
+
+static const struct file_operations bat_debug_fops = {
+       .open           = debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
+{
+       charger->debug_file = debugfs_create_file("charger", 0666, NULL,
+                                                 charger, &bat_debug_fops);
+       return charger->debug_file;
+}
+
+static void da9030_bat_remove_debugfs(struct da9030_charger *charger)
+{
+       debugfs_remove(charger->debug_file);
+}
+#else
+static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
+{
+       return NULL;
+}
+static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)
+{
+}
+#endif
+
+static inline void da9030_read_adc(struct da9030_charger *charger,
+                                  struct da9030_adc_res *adc)
+{
+       da903x_reads(charger->master, DA9030_VBAT_RES,
+                    sizeof(*adc), (uint8_t *)adc);
+}
+
+static void da9030_charger_update_state(struct da9030_charger *charger)
+{
+       uint8_t val;
+
+       da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);
+       charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;
+       charger->mA = ((val >> 3) & 0xf) * 100;
+       charger->mV = (val & 0x7) * 50 + 4000;
+
+       da9030_read_adc(charger, &charger->adc);
+       da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);
+       charger->chdet = da903x_query_status(charger->master,
+                                                    DA9030_STATUS_CHDET);
+}
+
+static void da9030_set_charge(struct da9030_charger *charger, int on)
+{
+       uint8_t val;
+
+       if (on) {
+               val = DA9030_CHRG_CHARGER_ENABLE;
+               val |= (charger->charge_milliamp / 100) << 3;
+               val |= (charger->charge_millivolt - 4000) / 50;
+               charger->is_on = 1;
+       } else {
+               val = 0;
+               charger->is_on = 0;
+       }
+
+       da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);
+
+       power_supply_changed(charger->psy);
+}
+
+static void da9030_charger_check_state(struct da9030_charger *charger)
+{
+       da9030_charger_update_state(charger);
+
+       /* we wake or boot with external power on */
+       if (!charger->is_on) {
+               if ((charger->chdet) &&
+                   (charger->adc.vbat_res <
+                    charger->thresholds.vbat_charge_start)) {
+                       da9030_set_charge(charger, 1);
+               }
+       } else {
+               /* Charger has been pulled out */
+               if (!charger->chdet) {
+                       da9030_set_charge(charger, 0);
+                       return;
+               }
+
+               if (charger->adc.vbat_res >=
+                   charger->thresholds.vbat_charge_stop) {
+                       da9030_set_charge(charger, 0);
+                       da903x_write(charger->master, DA9030_VBATMON,
+                                      charger->thresholds.vbat_charge_restart);
+               } else if (charger->adc.vbat_res >
+                          charger->thresholds.vbat_low) {
+                       /* we are charging and passed LOW_THRESH,
+                          so upate DA9030 VBAT threshold
+                        */
+                       da903x_write(charger->master, DA9030_VBATMON,
+                                    charger->thresholds.vbat_low);
+               }
+               if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||
+                   charger->adc.vchmin_res < charger->thresholds.vcharge_min ||
+                   /* Tempreture readings are negative */
+                   charger->adc.tbat_res < charger->thresholds.tbat_high ||
+                   charger->adc.tbat_res > charger->thresholds.tbat_low) {
+                       /* disable charger */
+                       da9030_set_charge(charger, 0);
+               }
+       }
+}
+
+static void da9030_charging_monitor(struct work_struct *work)
+{
+       struct da9030_charger *charger;
+
+       charger = container_of(work, struct da9030_charger, work.work);
+
+       da9030_charger_check_state(charger);
+
+       /* reschedule for the next time */
+       schedule_delayed_work(&charger->work, charger->interval);
+}
+
+static enum power_supply_property da9030_battery_props[] = {
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+};
+
+static void da9030_battery_check_status(struct da9030_charger *charger,
+                                   union power_supply_propval *val)
+{
+       if (charger->chdet) {
+               if (charger->is_on)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+}
+
+static void da9030_battery_check_health(struct da9030_charger *charger,
+                                   union power_supply_propval *val)
+{
+       if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)
+               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+       else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)
+               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+       else
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int da9030_battery_get_property(struct power_supply *psy,
+                                  enum power_supply_property psp,
+                                  union power_supply_propval *val)
+{
+       struct da9030_charger *charger = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               da9030_battery_check_status(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               da9030_battery_check_health(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = charger->battery_info->technology;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = charger->battery_info->voltage_max_design;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = charger->battery_info->voltage_min_design;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               val->intval =
+                       da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = charger->battery_info->name;
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static void da9030_battery_vbat_event(struct da9030_charger *charger)
+{
+       da9030_read_adc(charger, &charger->adc);
+
+       if (charger->is_on)
+               return;
+
+       if (charger->adc.vbat_res < charger->thresholds.vbat_low) {
+               /* set VBAT threshold for critical */
+               da903x_write(charger->master, DA9030_VBATMON,
+                            charger->thresholds.vbat_crit);
+               if (charger->battery_low)
+                       charger->battery_low();
+       } else if (charger->adc.vbat_res <
+                  charger->thresholds.vbat_crit) {
+               /* notify the system of battery critical */
+               if (charger->battery_critical)
+                       charger->battery_critical();
+       }
+}
+
+static int da9030_battery_event(struct notifier_block *nb, unsigned long event,
+                               void *data)
+{
+       struct da9030_charger *charger =
+               container_of(nb, struct da9030_charger, nb);
+
+       switch (event) {
+       case DA9030_EVENT_CHDET:
+               cancel_delayed_work_sync(&charger->work);
+               schedule_work(&charger->work.work);
+               break;
+       case DA9030_EVENT_VBATMON:
+               da9030_battery_vbat_event(charger);
+               break;
+       case DA9030_EVENT_CHIOVER:
+       case DA9030_EVENT_TBAT:
+               da9030_set_charge(charger, 0);
+               break;
+       }
+
+       return 0;
+}
+
+static void da9030_battery_convert_thresholds(struct da9030_charger *charger,
+                                             struct da9030_battery_info *pdata)
+{
+       charger->thresholds.tbat_low = pdata->tbat_low;
+       charger->thresholds.tbat_high = pdata->tbat_high;
+       charger->thresholds.tbat_restart  = pdata->tbat_restart;
+
+       charger->thresholds.vbat_low =
+               da9030_millivolt_to_reg(pdata->vbat_low);
+       charger->thresholds.vbat_crit =
+               da9030_millivolt_to_reg(pdata->vbat_crit);
+       charger->thresholds.vbat_charge_start =
+               da9030_millivolt_to_reg(pdata->vbat_charge_start);
+       charger->thresholds.vbat_charge_stop =
+               da9030_millivolt_to_reg(pdata->vbat_charge_stop);
+       charger->thresholds.vbat_charge_restart =
+               da9030_millivolt_to_reg(pdata->vbat_charge_restart);
+
+       charger->thresholds.vcharge_min =
+               da9030_millivolt_to_reg(pdata->vcharge_min);
+       charger->thresholds.vcharge_max =
+               da9030_millivolt_to_reg(pdata->vcharge_max);
+}
+
+static void da9030_battery_setup_psy(struct da9030_charger *charger)
+{
+       struct power_supply_desc *psy_desc = &charger->psy_desc;
+       struct power_supply_info *info = charger->battery_info;
+
+       psy_desc->name = info->name;
+       psy_desc->use_for_apm = info->use_for_apm;
+       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
+       psy_desc->get_property = da9030_battery_get_property;
+
+       psy_desc->properties = da9030_battery_props;
+       psy_desc->num_properties = ARRAY_SIZE(da9030_battery_props);
+};
+
+static int da9030_battery_charger_init(struct da9030_charger *charger)
+{
+       char v[5];
+       int ret;
+
+       v[0] = v[1] = charger->thresholds.vbat_low;
+       v[2] = charger->thresholds.tbat_high;
+       v[3] = charger->thresholds.tbat_restart;
+       v[4] = charger->thresholds.tbat_low;
+
+       ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);
+       if (ret)
+               return ret;
+
+       /*
+        * Enable reference voltage supply for ADC from the LDO_INTERNAL
+        * regulator. Must be set before ADC measurements can be made.
+        */
+       ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,
+                          DA9030_ADC_LDO_INT_ENABLE |
+                          DA9030_ADC_TBATREF_ENABLE);
+       if (ret)
+               return ret;
+
+       /* enable auto ADC measuremnts */
+       return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,
+                           DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |
+                           DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |
+                           DA9030_ADC_VBAT_ENABLE |
+                           DA9030_ADC_AUTO_SLEEP_ENABLE);
+}
+
+static int da9030_battery_probe(struct platform_device *pdev)
+{
+       struct da9030_charger *charger;
+       struct power_supply_config psy_cfg = {};
+       struct da9030_battery_info *pdata = pdev->dev.platform_data;
+       int ret;
+
+       if (pdata == NULL)
+               return -EINVAL;
+
+       if (pdata->charge_milliamp >= 1500 ||
+           pdata->charge_millivolt < 4000 ||
+           pdata->charge_millivolt > 4350)
+               return -EINVAL;
+
+       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
+       if (charger == NULL)
+               return -ENOMEM;
+
+       charger->master = pdev->dev.parent;
+
+       /* 10 seconds between monitor runs unless platform defines other
+          interval */
+       charger->interval = msecs_to_jiffies(
+               (pdata->batmon_interval ? : 10) * 1000);
+
+       charger->charge_milliamp = pdata->charge_milliamp;
+       charger->charge_millivolt = pdata->charge_millivolt;
+       charger->battery_info = pdata->battery_info;
+       charger->battery_low = pdata->battery_low;
+       charger->battery_critical = pdata->battery_critical;
+
+       da9030_battery_convert_thresholds(charger, pdata);
+
+       ret = da9030_battery_charger_init(charger);
+       if (ret)
+               goto err_charger_init;
+
+       INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
+       schedule_delayed_work(&charger->work, charger->interval);
+
+       charger->nb.notifier_call = da9030_battery_event;
+       ret = da903x_register_notifier(charger->master, &charger->nb,
+                                      DA9030_EVENT_CHDET |
+                                      DA9030_EVENT_VBATMON |
+                                      DA9030_EVENT_CHIOVER |
+                                      DA9030_EVENT_TBAT);
+       if (ret)
+               goto err_notifier;
+
+       da9030_battery_setup_psy(charger);
+       psy_cfg.drv_data = charger;
+       charger->psy = power_supply_register(&pdev->dev, &charger->psy_desc,
+                                            &psy_cfg);
+       if (IS_ERR(charger->psy)) {
+               ret = PTR_ERR(charger->psy);
+               goto err_ps_register;
+       }
+
+       charger->debug_file = da9030_bat_create_debugfs(charger);
+       platform_set_drvdata(pdev, charger);
+       return 0;
+
+err_ps_register:
+       da903x_unregister_notifier(charger->master, &charger->nb,
+                                  DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
+                                  DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
+err_notifier:
+       cancel_delayed_work(&charger->work);
+
+err_charger_init:
+       return ret;
+}
+
+static int da9030_battery_remove(struct platform_device *dev)
+{
+       struct da9030_charger *charger = platform_get_drvdata(dev);
+
+       da9030_bat_remove_debugfs(charger);
+
+       da903x_unregister_notifier(charger->master, &charger->nb,
+                                  DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
+                                  DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
+       cancel_delayed_work_sync(&charger->work);
+       da9030_set_charge(charger, 0);
+       power_supply_unregister(charger->psy);
+
+       return 0;
+}
+
+static struct platform_driver da903x_battery_driver = {
+       .driver = {
+               .name   = "da903x-battery",
+       },
+       .probe = da9030_battery_probe,
+       .remove = da9030_battery_remove,
+};
+
+module_platform_driver(da903x_battery_driver);
+
+MODULE_DESCRIPTION("DA9030 battery charger driver");
+MODULE_AUTHOR("Mike Rapoport, CompuLab");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/da9052-battery.c b/drivers/power/supply/da9052-battery.c
new file mode 100644 (file)
index 0000000..830ec46
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * Batttery Driver for Dialog DA9052 PMICs
+ *
+ * Copyright(c) 2011 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * 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;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/fs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/pdata.h>
+#include <linux/mfd/da9052/reg.h>
+
+/* STATIC CONFIGURATION */
+#define DA9052_BAT_CUTOFF_VOLT         2800
+#define DA9052_BAT_TSH                 62000
+#define DA9052_BAT_LOW_CAP             4
+#define DA9052_AVG_SZ                  4
+#define DA9052_VC_TBL_SZ               68
+#define DA9052_VC_TBL_REF_SZ           3
+
+#define DA9052_ISET_USB_MASK           0x0F
+#define DA9052_CHG_USB_ILIM_MASK       0x40
+#define DA9052_CHG_LIM_COLS            16
+
+#define DA9052_MEAN(x, y)              ((x + y) / 2)
+
+enum charger_type_enum {
+       DA9052_NOCHARGER = 1,
+       DA9052_CHARGER,
+};
+
+static const u16 da9052_chg_current_lim[2][DA9052_CHG_LIM_COLS] = {
+       {70,  80,  90,  100, 110, 120, 400,  450,
+        500, 550, 600, 650, 700, 900, 1100, 1300},
+       {80,  90,  100, 110,  120,  400,  450,  500,
+        550, 600, 800, 1000, 1200, 1400, 1600, 1800},
+};
+
+static const u16 vc_tbl_ref[3] = {10, 25, 40};
+/* Lookup table for voltage vs capacity */
+static u32 const vc_tbl[3][68][2] = {
+       /* For temperature 10 degree Celsius */
+       {
+       {4082, 100}, {4036, 98},
+       {4020, 96}, {4008, 95},
+       {3997, 93}, {3983, 91},
+       {3964, 90}, {3943, 88},
+       {3926, 87}, {3912, 85},
+       {3900, 84}, {3890, 82},
+       {3881, 80}, {3873, 79},
+       {3865, 77}, {3857, 76},
+       {3848, 74}, {3839, 73},
+       {3829, 71}, {3820, 70},
+       {3811, 68}, {3802, 67},
+       {3794, 65}, {3785, 64},
+       {3778, 62}, {3770, 61},
+       {3763, 59}, {3756, 58},
+       {3750, 56}, {3744, 55},
+       {3738, 53}, {3732, 52},
+       {3727, 50}, {3722, 49},
+       {3717, 47}, {3712, 46},
+       {3708, 44}, {3703, 43},
+       {3700, 41}, {3696, 40},
+       {3693, 38}, {3691, 37},
+       {3688, 35}, {3686, 34},
+       {3683, 32}, {3681, 31},
+       {3678, 29}, {3675, 28},
+       {3672, 26}, {3669, 25},
+       {3665, 23}, {3661, 22},
+       {3656, 21}, {3651, 19},
+       {3645, 18}, {3639, 16},
+       {3631, 15}, {3622, 13},
+       {3611, 12}, {3600, 10},
+       {3587, 9}, {3572, 7},
+       {3548, 6}, {3503, 5},
+       {3420, 3}, {3268, 2},
+       {2992, 1}, {2746, 0}
+       },
+       /* For temperature 25 degree Celsius */
+       {
+       {4102, 100}, {4065, 98},
+       {4048, 96}, {4034, 95},
+       {4021, 93}, {4011, 92},
+       {4001, 90}, {3986, 88},
+       {3968, 87}, {3952, 85},
+       {3938, 84}, {3926, 82},
+       {3916, 81}, {3908, 79},
+       {3900, 77}, {3892, 76},
+       {3883, 74}, {3874, 73},
+       {3864, 71}, {3855, 70},
+       {3846, 68}, {3836, 67},
+       {3827, 65}, {3819, 64},
+       {3810, 62}, {3801, 61},
+       {3793, 59}, {3786, 58},
+       {3778, 56}, {3772, 55},
+       {3765, 53}, {3759, 52},
+       {3754, 50}, {3748, 49},
+       {3743, 47}, {3738, 46},
+       {3733, 44}, {3728, 43},
+       {3724, 41}, {3720, 40},
+       {3716, 38}, {3712, 37},
+       {3709, 35}, {3706, 34},
+       {3703, 33}, {3701, 31},
+       {3698, 30}, {3696, 28},
+       {3693, 27}, {3690, 25},
+       {3687, 24}, {3683, 22},
+       {3680, 21}, {3675, 19},
+       {3671, 18}, {3666, 17},
+       {3660, 15}, {3654, 14},
+       {3647, 12}, {3639, 11},
+       {3630, 9}, {3621, 8},
+       {3613, 6}, {3606, 5},
+       {3597, 4}, {3582, 2},
+       {3546, 1}, {2747, 0}
+       },
+       /* For temperature 40 degree Celsius */
+       {
+       {4114, 100}, {4081, 98},
+       {4065, 96}, {4050, 95},
+       {4036, 93}, {4024, 92},
+       {4013, 90}, {4002, 88},
+       {3990, 87}, {3976, 85},
+       {3962, 84}, {3950, 82},
+       {3939, 81}, {3930, 79},
+       {3921, 77}, {3912, 76},
+       {3902, 74}, {3893, 73},
+       {3883, 71}, {3874, 70},
+       {3865, 68}, {3856, 67},
+       {3847, 65}, {3838, 64},
+       {3829, 62}, {3820, 61},
+       {3812, 59}, {3803, 58},
+       {3795, 56}, {3787, 55},
+       {3780, 53}, {3773, 52},
+       {3767, 50}, {3761, 49},
+       {3756, 47}, {3751, 46},
+       {3746, 44}, {3741, 43},
+       {3736, 41}, {3732, 40},
+       {3728, 38}, {3724, 37},
+       {3720, 35}, {3716, 34},
+       {3713, 33}, {3710, 31},
+       {3707, 30}, {3704, 28},
+       {3701, 27}, {3698, 25},
+       {3695, 24}, {3691, 22},
+       {3686, 21}, {3681, 19},
+       {3676, 18}, {3671, 17},
+       {3666, 15}, {3661, 14},
+       {3655, 12}, {3648, 11},
+       {3640, 9}, {3632, 8},
+       {3622, 6}, {3616, 5},
+       {3611, 4}, {3604, 2},
+       {3594, 1}, {2747, 0}
+       }
+};
+
+struct da9052_battery {
+       struct da9052 *da9052;
+       struct power_supply *psy;
+       struct notifier_block nb;
+       int charger_type;
+       int status;
+       int health;
+};
+
+static inline int volt_reg_to_mV(int value)
+{
+       return ((value * 1000) / 512) + 2500;
+}
+
+static inline int ichg_reg_to_mA(int value)
+{
+       return (value * 3900) / 1000;
+}
+
+static int da9052_read_chgend_current(struct da9052_battery *bat,
+                                      int *current_mA)
+{
+       int ret;
+
+       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
+               return -EINVAL;
+
+       ret = da9052_reg_read(bat->da9052, DA9052_ICHG_END_REG);
+       if (ret < 0)
+               return ret;
+
+       *current_mA = ichg_reg_to_mA(ret & DA9052_ICHGEND_ICHGEND);
+
+       return 0;
+}
+
+static int da9052_read_chg_current(struct da9052_battery *bat, int *current_mA)
+{
+       int ret;
+
+       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
+               return -EINVAL;
+
+       ret = da9052_reg_read(bat->da9052, DA9052_ICHG_AV_REG);
+       if (ret < 0)
+               return ret;
+
+       *current_mA = ichg_reg_to_mA(ret & DA9052_ICHGAV_ICHGAV);
+
+       return 0;
+}
+
+static int da9052_bat_check_status(struct da9052_battery *bat, int *status)
+{
+       u8 v[2] = {0, 0};
+       u8 bat_status;
+       u8 chg_end;
+       int ret;
+       int chg_current;
+       int chg_end_current;
+       bool dcinsel;
+       bool dcindet;
+       bool vbussel;
+       bool vbusdet;
+       bool dc;
+       bool vbus;
+
+       ret = da9052_group_read(bat->da9052, DA9052_STATUS_A_REG, 2, v);
+       if (ret < 0)
+               return ret;
+
+       bat_status = v[0];
+       chg_end = v[1];
+
+       dcinsel = bat_status & DA9052_STATUSA_DCINSEL;
+       dcindet = bat_status & DA9052_STATUSA_DCINDET;
+       vbussel = bat_status & DA9052_STATUSA_VBUSSEL;
+       vbusdet = bat_status & DA9052_STATUSA_VBUSDET;
+       dc = dcinsel && dcindet;
+       vbus = vbussel && vbusdet;
+
+       /* Preference to WALL(DCIN) charger unit */
+       if (dc || vbus) {
+               bat->charger_type = DA9052_CHARGER;
+
+               /* If charging end flag is set and Charging current is greater
+                * than charging end limit then battery is charging
+               */
+               if ((chg_end & DA9052_STATUSB_CHGEND) != 0) {
+                       ret = da9052_read_chg_current(bat, &chg_current);
+                       if (ret < 0)
+                               return ret;
+                       ret = da9052_read_chgend_current(bat, &chg_end_current);
+                       if (ret < 0)
+                               return ret;
+
+                       if (chg_current >= chg_end_current)
+                               bat->status = POWER_SUPPLY_STATUS_CHARGING;
+                       else
+                               bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               } else {
+                       /* If Charging end flag is cleared then battery is
+                        * charging
+                       */
+                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
+               }
+       } else if (dcindet || vbusdet) {
+                       bat->charger_type = DA9052_CHARGER;
+                       bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               bat->charger_type = DA9052_NOCHARGER;
+               bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+
+       if (status != NULL)
+               *status = bat->status;
+       return 0;
+}
+
+static int da9052_bat_read_volt(struct da9052_battery *bat, int *volt_mV)
+{
+       int volt;
+
+       volt = da9052_adc_manual_read(bat->da9052, DA9052_ADC_MAN_MUXSEL_VBAT);
+       if (volt < 0)
+               return volt;
+
+       *volt_mV = volt_reg_to_mV(volt);
+
+       return 0;
+}
+
+static int da9052_bat_check_presence(struct da9052_battery *bat, int *illegal)
+{
+       int bat_temp;
+
+       bat_temp = da9052_adc_read_temp(bat->da9052);
+       if (bat_temp < 0)
+               return bat_temp;
+
+       if (bat_temp > DA9052_BAT_TSH)
+               *illegal = 1;
+       else
+               *illegal = 0;
+
+       return 0;
+}
+
+static int da9052_bat_interpolate(int vbat_lower, int  vbat_upper,
+                                  int level_lower, int level_upper,
+                                  int bat_voltage)
+{
+       int tmp;
+
+       tmp = ((level_upper - level_lower) * 1000) / (vbat_upper - vbat_lower);
+       tmp = level_lower + (((bat_voltage - vbat_lower) * tmp) / 1000);
+
+       return tmp;
+}
+
+static unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp)
+{
+       int i;
+
+       if (adc_temp <= vc_tbl_ref[0])
+               return 0;
+
+       if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1])
+               return DA9052_VC_TBL_REF_SZ - 1;
+
+       for (i = 0; i < DA9052_VC_TBL_REF_SZ - 1; i++) {
+               if ((adc_temp > vc_tbl_ref[i]) &&
+                   (adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])))
+                               return i;
+               if ((adc_temp > DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1]))
+                    && (adc_temp <= vc_tbl_ref[i]))
+                               return i + 1;
+       }
+       /*
+        * For some reason authors of the driver didn't presume that we can
+        * end up here. It might be OK, but might be not, no one knows for
+        * sure. Go check your battery, is it on fire?
+        */
+       WARN_ON(1);
+       return 0;
+}
+
+static int da9052_bat_read_capacity(struct da9052_battery *bat, int *capacity)
+{
+       int adc_temp;
+       int bat_voltage;
+       int vbat_lower;
+       int vbat_upper;
+       int level_upper;
+       int level_lower;
+       int ret;
+       int flag;
+       int i = 0;
+       int j;
+
+       ret = da9052_bat_read_volt(bat, &bat_voltage);
+       if (ret < 0)
+               return ret;
+
+       adc_temp = da9052_adc_read_temp(bat->da9052);
+       if (adc_temp < 0)
+               return adc_temp;
+
+       i = da9052_determine_vc_tbl_index(adc_temp);
+
+       if (bat_voltage >= vc_tbl[i][0][0]) {
+               *capacity = 100;
+               return 0;
+       }
+       if (bat_voltage <= vc_tbl[i][DA9052_VC_TBL_SZ - 1][0]) {
+               *capacity = 0;
+               return 0;
+       }
+       flag = 0;
+
+       for (j = 0; j < (DA9052_VC_TBL_SZ-1); j++) {
+               if ((bat_voltage <= vc_tbl[i][j][0]) &&
+                   (bat_voltage >= vc_tbl[i][j + 1][0])) {
+                       vbat_upper = vc_tbl[i][j][0];
+                       vbat_lower = vc_tbl[i][j + 1][0];
+                       level_upper = vc_tbl[i][j][1];
+                       level_lower = vc_tbl[i][j + 1][1];
+                       flag = 1;
+                       break;
+               }
+       }
+       if (!flag)
+               return -EIO;
+
+       *capacity = da9052_bat_interpolate(vbat_lower, vbat_upper, level_lower,
+                                          level_upper, bat_voltage);
+
+       return 0;
+}
+
+static int da9052_bat_check_health(struct da9052_battery *bat, int *health)
+{
+       int ret;
+       int bat_illegal;
+       int capacity;
+
+       ret = da9052_bat_check_presence(bat, &bat_illegal);
+       if (ret < 0)
+               return ret;
+
+       if (bat_illegal) {
+               bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               return 0;
+       }
+
+       if (bat->health != POWER_SUPPLY_HEALTH_OVERHEAT) {
+               ret = da9052_bat_read_capacity(bat, &capacity);
+               if (ret < 0)
+                       return ret;
+               if (capacity < DA9052_BAT_LOW_CAP)
+                       bat->health = POWER_SUPPLY_HEALTH_DEAD;
+               else
+                       bat->health = POWER_SUPPLY_HEALTH_GOOD;
+       }
+
+       *health = bat->health;
+
+       return 0;
+}
+
+static irqreturn_t da9052_bat_irq(int irq, void *data)
+{
+       struct da9052_battery *bat = data;
+       int virq;
+
+       virq = regmap_irq_get_virq(bat->da9052->irq_data, irq);
+       irq -= virq;
+
+       if (irq == DA9052_IRQ_CHGEND)
+               bat->status = POWER_SUPPLY_STATUS_FULL;
+       else
+               da9052_bat_check_status(bat, NULL);
+
+       if (irq == DA9052_IRQ_CHGEND || irq == DA9052_IRQ_DCIN ||
+           irq == DA9052_IRQ_VBUS || irq == DA9052_IRQ_TBAT) {
+               power_supply_changed(bat->psy);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int da9052_USB_current_notifier(struct notifier_block *nb,
+                                       unsigned long events, void *data)
+{
+       u8 row;
+       u8 col;
+       int *current_mA = data;
+       int ret;
+       struct da9052_battery *bat = container_of(nb, struct da9052_battery,
+                                                 nb);
+
+       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
+               return -EPERM;
+
+       ret = da9052_reg_read(bat->da9052, DA9052_CHGBUCK_REG);
+       if (ret & DA9052_CHG_USB_ILIM_MASK)
+               return -EPERM;
+
+       if (bat->da9052->chip_id == DA9052)
+               row = 0;
+       else
+               row = 1;
+
+       if (*current_mA < da9052_chg_current_lim[row][0] ||
+           *current_mA > da9052_chg_current_lim[row][DA9052_CHG_LIM_COLS - 1])
+               return -EINVAL;
+
+       for (col = 0; col <= DA9052_CHG_LIM_COLS - 1 ; col++) {
+               if (*current_mA <= da9052_chg_current_lim[row][col])
+                       break;
+       }
+
+       return da9052_reg_update(bat->da9052, DA9052_ISET_REG,
+                                DA9052_ISET_USB_MASK, col);
+}
+
+static int da9052_bat_get_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   union power_supply_propval *val)
+{
+       int ret;
+       int illegal;
+       struct da9052_battery *bat = power_supply_get_drvdata(psy);
+
+       ret = da9052_bat_check_presence(bat, &illegal);
+       if (ret < 0)
+               return ret;
+
+       if (illegal && psp != POWER_SUPPLY_PROP_PRESENT)
+               return -ENODEV;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = da9052_bat_check_status(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval =
+                       (bat->charger_type == DA9052_NOCHARGER) ? 0 : 1;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               ret = da9052_bat_check_presence(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = da9052_bat_check_health(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = DA9052_BAT_CUTOFF_VOLT * 1000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               ret = da9052_bat_read_volt(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               ret = da9052_read_chg_current(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = da9052_bat_read_capacity(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = da9052_adc_read_temp(bat->da9052);
+               ret = val->intval;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return ret;
+}
+
+static enum power_supply_property da9052_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+};
+
+static struct power_supply_desc psy_desc = {
+       .name           = "da9052-bat",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = da9052_bat_props,
+       .num_properties = ARRAY_SIZE(da9052_bat_props),
+       .get_property   = da9052_bat_get_property,
+};
+
+static char *da9052_bat_irqs[] = {
+       "BATT TEMP",
+       "DCIN DET",
+       "DCIN REM",
+       "VBUS DET",
+       "VBUS REM",
+       "CHG END",
+};
+
+static int da9052_bat_irq_bits[] = {
+       DA9052_IRQ_TBAT,
+       DA9052_IRQ_DCIN,
+       DA9052_IRQ_DCINREM,
+       DA9052_IRQ_VBUS,
+       DA9052_IRQ_VBUSREM,
+       DA9052_IRQ_CHGEND,
+};
+
+static s32 da9052_bat_probe(struct platform_device *pdev)
+{
+       struct da9052_pdata *pdata;
+       struct da9052_battery *bat;
+       struct power_supply_config psy_cfg = {};
+       int ret;
+       int i;
+
+       bat = devm_kzalloc(&pdev->dev, sizeof(struct da9052_battery),
+                               GFP_KERNEL);
+       if (!bat)
+               return -ENOMEM;
+
+       psy_cfg.drv_data = bat;
+
+       bat->da9052 = dev_get_drvdata(pdev->dev.parent);
+       bat->charger_type = DA9052_NOCHARGER;
+       bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
+       bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
+       bat->nb.notifier_call = da9052_USB_current_notifier;
+
+       pdata = bat->da9052->dev->platform_data;
+       if (pdata != NULL && pdata->use_for_apm)
+               psy_desc.use_for_apm = pdata->use_for_apm;
+       else
+               psy_desc.use_for_apm = 1;
+
+       for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) {
+               ret = da9052_request_irq(bat->da9052,
+                               da9052_bat_irq_bits[i], da9052_bat_irqs[i],
+                               da9052_bat_irq, bat);
+
+               if (ret != 0) {
+                       dev_err(bat->da9052->dev,
+                               "DA9052 failed to request %s IRQ: %d\n",
+                               da9052_bat_irqs[i], ret);
+                       goto err;
+               }
+       }
+
+       bat->psy = power_supply_register(&pdev->dev, &psy_desc, &psy_cfg);
+       if (IS_ERR(bat->psy)) {
+               ret = PTR_ERR(bat->psy);
+               goto err;
+       }
+
+       platform_set_drvdata(pdev, bat);
+       return 0;
+
+err:
+       while (--i >= 0)
+               da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
+
+       return ret;
+}
+static int da9052_bat_remove(struct platform_device *pdev)
+{
+       int i;
+       struct da9052_battery *bat = platform_get_drvdata(pdev);
+
+       for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++)
+               da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
+
+       power_supply_unregister(bat->psy);
+
+       return 0;
+}
+
+static struct platform_driver da9052_bat_driver = {
+       .probe = da9052_bat_probe,
+       .remove = da9052_bat_remove,
+       .driver = {
+               .name = "da9052-bat",
+       },
+};
+module_platform_driver(da9052_bat_driver);
+
+MODULE_DESCRIPTION("DA9052 BAT Device Driver");
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-bat");
diff --git a/drivers/power/supply/da9150-charger.c b/drivers/power/supply/da9150-charger.c
new file mode 100644 (file)
index 0000000..6009981
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * DA9150 Charger Driver
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ *
+ * 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;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/usb/phy.h>
+#include <linux/iio/consumer.h>
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/registers.h>
+
+/* Private data */
+struct da9150_charger {
+       struct da9150 *da9150;
+       struct device *dev;
+
+       struct power_supply *usb;
+       struct power_supply *battery;
+       struct power_supply *supply_online;
+
+       struct usb_phy *usb_phy;
+       struct notifier_block otg_nb;
+       struct work_struct otg_work;
+       unsigned long usb_event;
+
+       struct iio_channel *ibus_chan;
+       struct iio_channel *vbus_chan;
+       struct iio_channel *tjunc_chan;
+       struct iio_channel *vbat_chan;
+};
+
+static inline int da9150_charger_supply_online(struct da9150_charger *charger,
+                                              struct power_supply *psy,
+                                              union power_supply_propval *val)
+{
+       val->intval = (psy == charger->supply_online) ? 1 : 0;
+
+       return 0;
+}
+
+/* Charger Properties */
+static int da9150_charger_vbus_voltage_now(struct da9150_charger *charger,
+                                          union power_supply_propval *val)
+{
+       int v_val, ret;
+
+       /* Read processed value - mV units */
+       ret = iio_read_channel_processed(charger->vbus_chan, &v_val);
+       if (ret < 0)
+               return ret;
+
+       /* Convert voltage to expected uV units */
+       val->intval = v_val * 1000;
+
+       return 0;
+}
+
+static int da9150_charger_ibus_current_avg(struct da9150_charger *charger,
+                                          union power_supply_propval *val)
+{
+       int i_val, ret;
+
+       /* Read processed value - mA units */
+       ret = iio_read_channel_processed(charger->ibus_chan, &i_val);
+       if (ret < 0)
+               return ret;
+
+       /* Convert current to expected uA units */
+       val->intval = i_val * 1000;
+
+       return 0;
+}
+
+static int da9150_charger_tjunc_temp(struct da9150_charger *charger,
+                                    union power_supply_propval *val)
+{
+       int t_val, ret;
+
+       /* Read processed value - 0.001 degrees C units */
+       ret = iio_read_channel_processed(charger->tjunc_chan, &t_val);
+       if (ret < 0)
+               return ret;
+
+       /* Convert temp to expect 0.1 degrees C units */
+       val->intval = t_val / 100;
+
+       return 0;
+}
+
+static enum power_supply_property da9150_charger_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static int da9150_charger_get_prop(struct power_supply *psy,
+                                  enum power_supply_property psp,
+                                  union power_supply_propval *val)
+{
+       struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent);
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = da9150_charger_supply_online(charger, psy, val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = da9150_charger_vbus_voltage_now(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               ret = da9150_charger_ibus_current_avg(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = da9150_charger_tjunc_temp(charger, val);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/* Battery Properties */
+static int da9150_charger_battery_status(struct da9150_charger *charger,
+                                        union power_supply_propval *val)
+{
+       u8 reg;
+
+       /* Check to see if battery is discharging */
+       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);
+
+       if (((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_OFF) ||
+           ((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_WAIT)) {
+               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+
+               return 0;
+       }
+
+       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
+
+       /* Now check for other states */
+       switch (reg & DA9150_CHG_STAT_MASK) {
+       case DA9150_CHG_STAT_ACT:
+       case DA9150_CHG_STAT_PRE:
+       case DA9150_CHG_STAT_CC:
+       case DA9150_CHG_STAT_CV:
+               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               break;
+       case DA9150_CHG_STAT_OFF:
+       case DA9150_CHG_STAT_SUSP:
+       case DA9150_CHG_STAT_TEMP:
+       case DA9150_CHG_STAT_TIME:
+       case DA9150_CHG_STAT_BAT:
+               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               break;
+       case DA9150_CHG_STAT_FULL:
+               val->intval = POWER_SUPPLY_STATUS_FULL;
+               break;
+       default:
+               val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int da9150_charger_battery_health(struct da9150_charger *charger,
+                                        union power_supply_propval *val)
+{
+       u8 reg;
+
+       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
+
+       /* Check if temperature limit reached */
+       switch (reg & DA9150_CHG_TEMP_MASK) {
+       case DA9150_CHG_TEMP_UNDER:
+               val->intval = POWER_SUPPLY_HEALTH_COLD;
+               return 0;
+       case DA9150_CHG_TEMP_OVER:
+               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               return 0;
+       default:
+               break;
+       }
+
+       /* Check for other health states */
+       switch (reg & DA9150_CHG_STAT_MASK) {
+       case DA9150_CHG_STAT_ACT:
+       case DA9150_CHG_STAT_PRE:
+               val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               break;
+       case DA9150_CHG_STAT_TIME:
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               break;
+       default:
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       }
+
+       return 0;
+}
+
+static int da9150_charger_battery_present(struct da9150_charger *charger,
+                                         union power_supply_propval *val)
+{
+       u8 reg;
+
+       /* Check if battery present or removed */
+       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
+       if ((reg & DA9150_CHG_STAT_MASK) == DA9150_CHG_STAT_BAT)
+               val->intval = 0;
+       else
+               val->intval = 1;
+
+       return 0;
+}
+
+static int da9150_charger_battery_charge_type(struct da9150_charger *charger,
+                                             union power_supply_propval *val)
+{
+       u8 reg;
+
+       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
+
+       switch (reg & DA9150_CHG_STAT_MASK) {
+       case DA9150_CHG_STAT_CC:
+               val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               break;
+       case DA9150_CHG_STAT_ACT:
+       case DA9150_CHG_STAT_PRE:
+       case DA9150_CHG_STAT_CV:
+               val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+               break;
+       default:
+               val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               break;
+       }
+
+       return 0;
+}
+
+static int da9150_charger_battery_voltage_min(struct da9150_charger *charger,
+                                             union power_supply_propval *val)
+{
+       u8 reg;
+
+       reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_C);
+
+       /* Value starts at 2500 mV, 50 mV increments, presented in uV */
+       val->intval = ((reg & DA9150_CHG_VFAULT_MASK) * 50000) + 2500000;
+
+       return 0;
+}
+
+static int da9150_charger_battery_voltage_now(struct da9150_charger *charger,
+                                             union power_supply_propval *val)
+{
+       int v_val, ret;
+
+       /* Read processed value - mV units */
+       ret = iio_read_channel_processed(charger->vbat_chan, &v_val);
+       if (ret < 0)
+               return ret;
+
+       val->intval = v_val * 1000;
+
+       return 0;
+}
+
+static int da9150_charger_battery_current_max(struct da9150_charger *charger,
+                                             union power_supply_propval *val)
+{
+       int reg;
+
+       reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_D);
+
+       /* 25mA increments */
+       val->intval = reg * 25000;
+
+       return 0;
+}
+
+static int da9150_charger_battery_voltage_max(struct da9150_charger *charger,
+                                             union power_supply_propval *val)
+{
+       u8 reg;
+
+       reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_B);
+
+       /* Value starts at 3650 mV, 25 mV increments, presented in uV */
+       val->intval = ((reg & DA9150_CHG_VBAT_MASK) * 25000) + 3650000;
+       return 0;
+}
+
+static enum power_supply_property da9150_charger_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+};
+
+static int da9150_charger_battery_get_prop(struct power_supply *psy,
+                                          enum power_supply_property psp,
+                                          union power_supply_propval *val)
+{
+       struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent);
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = da9150_charger_battery_status(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = da9150_charger_supply_online(charger, psy, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = da9150_charger_battery_health(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               ret = da9150_charger_battery_present(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               ret = da9150_charger_battery_charge_type(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               ret = da9150_charger_battery_voltage_min(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = da9150_charger_battery_voltage_now(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               ret = da9150_charger_battery_current_max(charger, val);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               ret = da9150_charger_battery_voltage_max(charger, val);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static irqreturn_t da9150_charger_chg_irq(int irq, void *data)
+{
+       struct da9150_charger *charger = data;
+
+       power_supply_changed(charger->battery);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t da9150_charger_tjunc_irq(int irq, void *data)
+{
+       struct da9150_charger *charger = data;
+
+       /* Nothing we can really do except report this. */
+       dev_crit(charger->dev, "TJunc over temperature!!!\n");
+       power_supply_changed(charger->usb);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t da9150_charger_vfault_irq(int irq, void *data)
+{
+       struct da9150_charger *charger = data;
+
+       /* Nothing we can really do except report this. */
+       dev_crit(charger->dev, "VSYS under voltage!!!\n");
+       power_supply_changed(charger->usb);
+       power_supply_changed(charger->battery);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t da9150_charger_vbus_irq(int irq, void *data)
+{
+       struct da9150_charger *charger = data;
+       u8 reg;
+
+       reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);
+
+       /* Charger plugged in or battery only */
+       switch (reg & DA9150_VBUS_STAT_MASK) {
+       case DA9150_VBUS_STAT_OFF:
+       case DA9150_VBUS_STAT_WAIT:
+               charger->supply_online = charger->battery;
+               break;
+       case DA9150_VBUS_STAT_CHG:
+               charger->supply_online = charger->usb;
+               break;
+       default:
+               dev_warn(charger->dev, "Unknown VBUS state - reg = 0x%x\n",
+                        reg);
+               charger->supply_online = NULL;
+               break;
+       }
+
+       power_supply_changed(charger->usb);
+       power_supply_changed(charger->battery);
+
+       return IRQ_HANDLED;
+}
+
+static void da9150_charger_otg_work(struct work_struct *data)
+{
+       struct da9150_charger *charger =
+               container_of(data, struct da9150_charger, otg_work);
+
+       switch (charger->usb_event) {
+       case USB_EVENT_ID:
+               /* Enable OTG Boost */
+               da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,
+                               DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_OTG);
+               break;
+       case USB_EVENT_NONE:
+               /* Revert to charge mode */
+               power_supply_changed(charger->usb);
+               power_supply_changed(charger->battery);
+               da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,
+                               DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_CHG);
+               break;
+       }
+}
+
+static int da9150_charger_otg_ncb(struct notifier_block *nb, unsigned long val,
+                                 void *priv)
+{
+       struct da9150_charger *charger =
+               container_of(nb, struct da9150_charger, otg_nb);
+
+       dev_dbg(charger->dev, "DA9150 OTG notify %lu\n", val);
+
+       charger->usb_event = val;
+       schedule_work(&charger->otg_work);
+
+       return NOTIFY_OK;
+}
+
+static int da9150_charger_register_irq(struct platform_device *pdev,
+                                      irq_handler_t handler,
+                                      const char *irq_name)
+{
+       struct device *dev = &pdev->dev;
+       struct da9150_charger *charger = platform_get_drvdata(pdev);
+       int irq, ret;
+
+       irq = platform_get_irq_byname(pdev, irq_name);
+       if (irq < 0) {
+               dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq);
+               return irq;
+       }
+
+       ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, irq_name,
+                                  charger);
+       if (ret)
+               dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
+
+       return ret;
+}
+
+static void da9150_charger_unregister_irq(struct platform_device *pdev,
+                                         const char *irq_name)
+{
+       struct device *dev = &pdev->dev;
+       struct da9150_charger *charger = platform_get_drvdata(pdev);
+       int irq;
+
+       irq = platform_get_irq_byname(pdev, irq_name);
+       if (irq < 0) {
+               dev_err(dev, "Failed to get IRQ CHG_STATUS: %d\n", irq);
+               return;
+       }
+
+       free_irq(irq, charger);
+}
+
+static const struct power_supply_desc usb_desc = {
+       .name           = "da9150-usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties     = da9150_charger_props,
+       .num_properties = ARRAY_SIZE(da9150_charger_props),
+       .get_property   = da9150_charger_get_prop,
+};
+
+static const struct power_supply_desc battery_desc = {
+       .name           = "da9150-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = da9150_charger_bat_props,
+       .num_properties = ARRAY_SIZE(da9150_charger_bat_props),
+       .get_property   = da9150_charger_battery_get_prop,
+};
+
+static int da9150_charger_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct da9150 *da9150 = dev_get_drvdata(dev->parent);
+       struct da9150_charger *charger;
+       u8 reg;
+       int ret;
+
+       charger = devm_kzalloc(dev, sizeof(struct da9150_charger), GFP_KERNEL);
+       if (!charger)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, charger);
+       charger->da9150 = da9150;
+       charger->dev = dev;
+
+       /* Acquire ADC channels */
+       charger->ibus_chan = iio_channel_get(dev, "CHAN_IBUS");
+       if (IS_ERR(charger->ibus_chan)) {
+               ret = PTR_ERR(charger->ibus_chan);
+               goto ibus_chan_fail;
+       }
+
+       charger->vbus_chan = iio_channel_get(dev, "CHAN_VBUS");
+       if (IS_ERR(charger->vbus_chan)) {
+               ret = PTR_ERR(charger->vbus_chan);
+               goto vbus_chan_fail;
+       }
+
+       charger->tjunc_chan = iio_channel_get(dev, "CHAN_TJUNC");
+       if (IS_ERR(charger->tjunc_chan)) {
+               ret = PTR_ERR(charger->tjunc_chan);
+               goto tjunc_chan_fail;
+       }
+
+       charger->vbat_chan = iio_channel_get(dev, "CHAN_VBAT");
+       if (IS_ERR(charger->vbat_chan)) {
+               ret = PTR_ERR(charger->vbat_chan);
+               goto vbat_chan_fail;
+       }
+
+       /* Register power supplies */
+       charger->usb = power_supply_register(dev, &usb_desc, NULL);
+       if (IS_ERR(charger->usb)) {
+               ret = PTR_ERR(charger->usb);
+               goto usb_fail;
+       }
+
+       charger->battery = power_supply_register(dev, &battery_desc, NULL);
+       if (IS_ERR(charger->battery)) {
+               ret = PTR_ERR(charger->battery);
+               goto battery_fail;
+       }
+
+       /* Get initial online supply */
+       reg = da9150_reg_read(da9150, DA9150_STATUS_H);
+
+       switch (reg & DA9150_VBUS_STAT_MASK) {
+       case DA9150_VBUS_STAT_OFF:
+       case DA9150_VBUS_STAT_WAIT:
+               charger->supply_online = charger->battery;
+               break;
+       case DA9150_VBUS_STAT_CHG:
+               charger->supply_online = charger->usb;
+               break;
+       default:
+               dev_warn(dev, "Unknown VBUS state - reg = 0x%x\n", reg);
+               charger->supply_online = NULL;
+               break;
+       }
+
+       /* Setup OTG reporting & configuration */
+       charger->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+       if (!IS_ERR_OR_NULL(charger->usb_phy)) {
+               INIT_WORK(&charger->otg_work, da9150_charger_otg_work);
+               charger->otg_nb.notifier_call = da9150_charger_otg_ncb;
+               usb_register_notifier(charger->usb_phy, &charger->otg_nb);
+       }
+
+       /* Register IRQs */
+       ret = da9150_charger_register_irq(pdev, da9150_charger_chg_irq,
+                                         "CHG_STATUS");
+       if (ret < 0)
+               goto chg_irq_fail;
+
+       ret = da9150_charger_register_irq(pdev, da9150_charger_tjunc_irq,
+                                         "CHG_TJUNC");
+       if (ret < 0)
+               goto tjunc_irq_fail;
+
+       ret = da9150_charger_register_irq(pdev, da9150_charger_vfault_irq,
+                                         "CHG_VFAULT");
+       if (ret < 0)
+               goto vfault_irq_fail;
+
+       ret = da9150_charger_register_irq(pdev, da9150_charger_vbus_irq,
+                                         "CHG_VBUS");
+       if (ret < 0)
+               goto vbus_irq_fail;
+
+       return 0;
+
+
+vbus_irq_fail:
+       da9150_charger_unregister_irq(pdev, "CHG_VFAULT");
+vfault_irq_fail:
+       da9150_charger_unregister_irq(pdev, "CHG_TJUNC");
+tjunc_irq_fail:
+       da9150_charger_unregister_irq(pdev, "CHG_STATUS");
+chg_irq_fail:
+       if (!IS_ERR_OR_NULL(charger->usb_phy))
+               usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);
+battery_fail:
+       power_supply_unregister(charger->usb);
+
+usb_fail:
+       iio_channel_release(charger->vbat_chan);
+
+vbat_chan_fail:
+       iio_channel_release(charger->tjunc_chan);
+
+tjunc_chan_fail:
+       iio_channel_release(charger->vbus_chan);
+
+vbus_chan_fail:
+       iio_channel_release(charger->ibus_chan);
+
+ibus_chan_fail:
+       return ret;
+}
+
+static int da9150_charger_remove(struct platform_device *pdev)
+{
+       struct da9150_charger *charger = platform_get_drvdata(pdev);
+       int irq;
+
+       /* Make sure IRQs are released before unregistering power supplies */
+       irq = platform_get_irq_byname(pdev, "CHG_VBUS");
+       free_irq(irq, charger);
+
+       irq = platform_get_irq_byname(pdev, "CHG_VFAULT");
+       free_irq(irq, charger);
+
+       irq = platform_get_irq_byname(pdev, "CHG_TJUNC");
+       free_irq(irq, charger);
+
+       irq = platform_get_irq_byname(pdev, "CHG_STATUS");
+       free_irq(irq, charger);
+
+       if (!IS_ERR_OR_NULL(charger->usb_phy))
+               usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);
+
+       power_supply_unregister(charger->battery);
+       power_supply_unregister(charger->usb);
+
+       /* Release ADC channels */
+       iio_channel_release(charger->ibus_chan);
+       iio_channel_release(charger->vbus_chan);
+       iio_channel_release(charger->tjunc_chan);
+       iio_channel_release(charger->vbat_chan);
+
+       return 0;
+}
+
+static struct platform_driver da9150_charger_driver = {
+       .driver = {
+               .name = "da9150-charger",
+       },
+       .probe = da9150_charger_probe,
+       .remove = da9150_charger_remove,
+};
+
+module_platform_driver(da9150_charger_driver);
+
+MODULE_DESCRIPTION("Charger Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/da9150-fg.c b/drivers/power/supply/da9150-fg.c
new file mode 100644 (file)
index 0000000..8b8ce97
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * DA9150 Fuel-Gauge Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ *
+ * 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;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/power_supply.h>
+#include <linux/list.h>
+#include <asm/div64.h>
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/registers.h>
+
+/* Core2Wire */
+#define DA9150_QIF_READ                (0x0 << 7)
+#define DA9150_QIF_WRITE       (0x1 << 7)
+#define DA9150_QIF_CODE_MASK   0x7F
+
+#define DA9150_QIF_BYTE_SIZE   8
+#define DA9150_QIF_BYTE_MASK   0xFF
+#define DA9150_QIF_SHORT_SIZE  2
+#define DA9150_QIF_LONG_SIZE   4
+
+/* QIF Codes */
+#define DA9150_QIF_UAVG                        6
+#define DA9150_QIF_UAVG_SIZE           DA9150_QIF_LONG_SIZE
+#define DA9150_QIF_IAVG                        8
+#define DA9150_QIF_IAVG_SIZE           DA9150_QIF_LONG_SIZE
+#define DA9150_QIF_NTCAVG              12
+#define DA9150_QIF_NTCAVG_SIZE         DA9150_QIF_LONG_SIZE
+#define DA9150_QIF_SHUNT_VAL           36
+#define DA9150_QIF_SHUNT_VAL_SIZE      DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_SD_GAIN             38
+#define DA9150_QIF_SD_GAIN_SIZE                DA9150_QIF_LONG_SIZE
+#define DA9150_QIF_FCC_MAH             40
+#define DA9150_QIF_FCC_MAH_SIZE                DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_SOC_PCT             43
+#define DA9150_QIF_SOC_PCT_SIZE                DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_CHARGE_LIMIT                44
+#define DA9150_QIF_CHARGE_LIMIT_SIZE   DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_DISCHARGE_LIMIT     45
+#define DA9150_QIF_DISCHARGE_LIMIT_SIZE        DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_FW_MAIN_VER         118
+#define DA9150_QIF_FW_MAIN_VER_SIZE    DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_E_FG_STATUS         126
+#define DA9150_QIF_E_FG_STATUS_SIZE    DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_SYNC                        127
+#define DA9150_QIF_SYNC_SIZE           DA9150_QIF_SHORT_SIZE
+#define DA9150_QIF_MAX_CODES           128
+
+/* QIF Sync Timeout */
+#define DA9150_QIF_SYNC_TIMEOUT                1000
+#define DA9150_QIF_SYNC_RETRIES                10
+
+/* QIF E_FG_STATUS */
+#define DA9150_FG_IRQ_LOW_SOC_MASK     (1 << 0)
+#define DA9150_FG_IRQ_HIGH_SOC_MASK    (1 << 1)
+#define DA9150_FG_IRQ_SOC_MASK \
+       (DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK)
+
+/* Private data */
+struct da9150_fg {
+       struct da9150 *da9150;
+       struct device *dev;
+
+       struct mutex io_lock;
+
+       struct power_supply *battery;
+       struct delayed_work work;
+       u32 interval;
+
+       int warn_soc;
+       int crit_soc;
+       int soc;
+};
+
+/* Battery Properties */
+static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
+
+{
+       u8 buf[size];
+       u8 read_addr;
+       u32 res = 0;
+       int i;
+
+       /* Set QIF code (READ mode) */
+       read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ;
+
+       da9150_read_qif(fg->da9150, read_addr, size, buf);
+       for (i = 0; i < size; ++i)
+               res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE));
+
+       return res;
+}
+
+static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
+                                u32 val)
+
+{
+       u8 buf[size];
+       u8 write_addr;
+       int i;
+
+       /* Set QIF code (WRITE mode) */
+       write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE;
+
+       for (i = 0; i < size; ++i) {
+               buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) &
+                        DA9150_QIF_BYTE_MASK;
+       }
+       da9150_write_qif(fg->da9150, write_addr, size, buf);
+}
+
+/* Trigger QIF Sync to update QIF readable data */
+static void da9150_fg_read_sync_start(struct da9150_fg *fg)
+{
+       int i = 0;
+       u32 res = 0;
+
+       mutex_lock(&fg->io_lock);
+
+       /* Check if QIF sync already requested, and write to sync if not */
+       res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+                                 DA9150_QIF_SYNC_SIZE);
+       if (res > 0)
+               da9150_fg_write_attr(fg, DA9150_QIF_SYNC,
+                                    DA9150_QIF_SYNC_SIZE, 0);
+
+       /* Wait for sync to complete */
+       res = 0;
+       while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
+               usleep_range(DA9150_QIF_SYNC_TIMEOUT,
+                            DA9150_QIF_SYNC_TIMEOUT * 2);
+               res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+                                         DA9150_QIF_SYNC_SIZE);
+       }
+
+       /* Check if sync completed */
+       if (res == 0)
+               dev_err(fg->dev, "Failed to perform QIF read sync!\n");
+}
+
+/*
+ * Should always be called after QIF sync read has been performed, and all
+ * attributes required have been accessed.
+ */
+static inline void da9150_fg_read_sync_end(struct da9150_fg *fg)
+{
+       mutex_unlock(&fg->io_lock);
+}
+
+/* Sync read of single QIF attribute */
+static u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size)
+{
+       u32 val;
+
+       da9150_fg_read_sync_start(fg);
+       val = da9150_fg_read_attr(fg, code, size);
+       da9150_fg_read_sync_end(fg);
+
+       return val;
+}
+
+/* Wait for QIF Sync, write QIF data and wait for ack */
+static void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size,
+                                     u32 val)
+{
+       int i = 0;
+       u32 res = 0, sync_val;
+
+       mutex_lock(&fg->io_lock);
+
+       /* Check if QIF sync already requested */
+       res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+                                 DA9150_QIF_SYNC_SIZE);
+
+       /* Wait for an existing sync to complete */
+       while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
+               usleep_range(DA9150_QIF_SYNC_TIMEOUT,
+                            DA9150_QIF_SYNC_TIMEOUT * 2);
+               res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+                                         DA9150_QIF_SYNC_SIZE);
+       }
+
+       if (res == 0) {
+               dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n");
+               mutex_unlock(&fg->io_lock);
+               return;
+       }
+
+       /* Write value for QIF code */
+       da9150_fg_write_attr(fg, code, size, val);
+
+       /* Wait for write acknowledgment */
+       i = 0;
+       sync_val = res;
+       while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
+               usleep_range(DA9150_QIF_SYNC_TIMEOUT,
+                            DA9150_QIF_SYNC_TIMEOUT * 2);
+               res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
+                                         DA9150_QIF_SYNC_SIZE);
+       }
+
+       mutex_unlock(&fg->io_lock);
+
+       /* Check write was actually successful */
+       if (res != (sync_val + 1))
+               dev_err(fg->dev, "Error performing QIF sync write for code %d\n",
+                       code);
+}
+
+/* Power Supply attributes */
+static int da9150_fg_capacity(struct da9150_fg *fg,
+                             union power_supply_propval *val)
+{
+       val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
+                                              DA9150_QIF_SOC_PCT_SIZE);
+
+       if (val->intval > 100)
+               val->intval = 100;
+
+       return 0;
+}
+
+static int da9150_fg_current_avg(struct da9150_fg *fg,
+                                union power_supply_propval *val)
+{
+       u32 iavg, sd_gain, shunt_val;
+       u64 div, res;
+
+       da9150_fg_read_sync_start(fg);
+       iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG,
+                                  DA9150_QIF_IAVG_SIZE);
+       shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL,
+                                       DA9150_QIF_SHUNT_VAL_SIZE);
+       sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN,
+                                     DA9150_QIF_SD_GAIN_SIZE);
+       da9150_fg_read_sync_end(fg);
+
+       div = (u64) (sd_gain * shunt_val * 65536ULL);
+       do_div(div, 1000000);
+       res = (u64) (iavg * 1000000ULL);
+       do_div(res, div);
+
+       val->intval = (int) res;
+
+       return 0;
+}
+
+static int da9150_fg_voltage_avg(struct da9150_fg *fg,
+                                union power_supply_propval *val)
+{
+       u64 res;
+
+       val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG,
+                                              DA9150_QIF_UAVG_SIZE);
+
+       res = (u64) (val->intval * 186ULL);
+       do_div(res, 10000);
+       val->intval = (int) res;
+
+       return 0;
+}
+
+static int da9150_fg_charge_full(struct da9150_fg *fg,
+                                union power_supply_propval *val)
+{
+       val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH,
+                                              DA9150_QIF_FCC_MAH_SIZE);
+
+       val->intval = val->intval * 1000;
+
+       return 0;
+}
+
+/*
+ * Temperature reading from device is only valid if battery/system provides
+ * valid NTC to associated pin of DA9150 chip.
+ */
+static int da9150_fg_temp(struct da9150_fg *fg,
+                         union power_supply_propval *val)
+{
+       val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG,
+                                              DA9150_QIF_NTCAVG_SIZE);
+
+       val->intval = (val->intval * 10) / 1048576;
+
+       return 0;
+}
+
+static enum power_supply_property da9150_fg_props[] = {
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static int da9150_fg_get_prop(struct power_supply *psy,
+                             enum power_supply_property psp,
+                             union power_supply_propval *val)
+{
+       struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent);
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = da9150_fg_capacity(fg, val);
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               ret = da9150_fg_current_avg(fg, val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               ret = da9150_fg_voltage_avg(fg, val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               ret = da9150_fg_charge_full(fg, val);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = da9150_fg_temp(fg, val);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/* Repeated SOC check */
+static bool da9150_fg_soc_changed(struct da9150_fg *fg)
+{
+       union power_supply_propval val;
+
+       da9150_fg_capacity(fg, &val);
+       if (val.intval != fg->soc) {
+               fg->soc = val.intval;
+               return true;
+       }
+
+       return false;
+}
+
+static void da9150_fg_work(struct work_struct *work)
+{
+       struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work);
+
+       /* Report if SOC has changed */
+       if (da9150_fg_soc_changed(fg))
+               power_supply_changed(fg->battery);
+
+       schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval));
+}
+
+/* SOC level event configuration */
+static void da9150_fg_soc_event_config(struct da9150_fg *fg)
+{
+       int soc;
+
+       soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
+                                      DA9150_QIF_SOC_PCT_SIZE);
+
+       if (soc > fg->warn_soc) {
+               /* If SOC > warn level, set discharge warn level event */
+               da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
+                                         DA9150_QIF_DISCHARGE_LIMIT_SIZE,
+                                         fg->warn_soc + 1);
+       } else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) {
+               /*
+                * If SOC <= warn level, set discharge crit level event,
+                * and set charge warn level event.
+                */
+               da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
+                                         DA9150_QIF_DISCHARGE_LIMIT_SIZE,
+                                         fg->crit_soc + 1);
+
+               da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
+                                         DA9150_QIF_CHARGE_LIMIT_SIZE,
+                                         fg->warn_soc);
+       } else if (soc <= fg->crit_soc) {
+               /* If SOC <= crit level, set charge crit level event */
+               da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
+                                         DA9150_QIF_CHARGE_LIMIT_SIZE,
+                                         fg->crit_soc);
+       }
+}
+
+static irqreturn_t da9150_fg_irq(int irq, void *data)
+{
+       struct da9150_fg *fg = data;
+       u32 e_fg_status;
+
+       /* Read FG IRQ status info */
+       e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS,
+                                         DA9150_QIF_E_FG_STATUS_SIZE);
+
+       /* Handle warning/critical threhold events */
+       if (e_fg_status & DA9150_FG_IRQ_SOC_MASK)
+               da9150_fg_soc_event_config(fg);
+
+       /* Clear any FG IRQs */
+       da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS,
+                            DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status);
+
+       return IRQ_HANDLED;
+}
+
+static struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev)
+{
+       struct device_node *fg_node = dev->of_node;
+       struct da9150_fg_pdata *pdata;
+
+       pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       of_property_read_u32(fg_node, "dlg,update-interval",
+                            &pdata->update_interval);
+       of_property_read_u8(fg_node, "dlg,warn-soc-level",
+                           &pdata->warn_soc_lvl);
+       of_property_read_u8(fg_node, "dlg,crit-soc-level",
+                           &pdata->crit_soc_lvl);
+
+       return pdata;
+}
+
+static const struct power_supply_desc fg_desc = {
+       .name           = "da9150-fg",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = da9150_fg_props,
+       .num_properties = ARRAY_SIZE(da9150_fg_props),
+       .get_property   = da9150_fg_get_prop,
+};
+
+static int da9150_fg_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct da9150 *da9150 = dev_get_drvdata(dev->parent);
+       struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev);
+       struct da9150_fg *fg;
+       int ver, irq, ret = 0;
+
+       fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL);
+       if (fg == NULL)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, fg);
+       fg->da9150 = da9150;
+       fg->dev = dev;
+
+       mutex_init(&fg->io_lock);
+
+       /* Enable QIF */
+       da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK,
+                       DA9150_FG_QIF_EN_MASK);
+
+       fg->battery = devm_power_supply_register(dev, &fg_desc, NULL);
+       if (IS_ERR(fg->battery)) {
+               ret = PTR_ERR(fg->battery);
+               return ret;
+       }
+
+       ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER,
+                                 DA9150_QIF_FW_MAIN_VER_SIZE);
+       dev_info(dev, "Version: 0x%x\n", ver);
+
+       /* Handle DT data if provided */
+       if (dev->of_node) {
+               fg_pdata = da9150_fg_dt_pdata(dev);
+               dev->platform_data = fg_pdata;
+       }
+
+       /* Handle any pdata provided */
+       if (fg_pdata) {
+               fg->interval = fg_pdata->update_interval;
+
+               if (fg_pdata->warn_soc_lvl > 100)
+                       dev_warn(dev, "Invalid SOC warning level provided, Ignoring");
+               else
+                       fg->warn_soc = fg_pdata->warn_soc_lvl;
+
+               if ((fg_pdata->crit_soc_lvl > 100) ||
+                   (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl))
+                       dev_warn(dev, "Invalid SOC critical level provided, Ignoring");
+               else
+                       fg->crit_soc = fg_pdata->crit_soc_lvl;
+
+
+       }
+
+       /* Configure initial SOC level events */
+       da9150_fg_soc_event_config(fg);
+
+       /*
+        * If an interval period has been provided then setup repeating
+        * work for reporting data updates.
+        */
+       if (fg->interval) {
+               INIT_DELAYED_WORK(&fg->work, da9150_fg_work);
+               schedule_delayed_work(&fg->work,
+                                     msecs_to_jiffies(fg->interval));
+       }
+
+       /* Register IRQ */
+       irq = platform_get_irq_byname(pdev, "FG");
+       if (irq < 0) {
+               dev_err(dev, "Failed to get IRQ FG: %d\n", irq);
+               ret = irq;
+               goto irq_fail;
+       }
+
+       ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,
+                                       IRQF_ONESHOT, "FG", fg);
+       if (ret) {
+               dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
+               goto irq_fail;
+       }
+
+       return 0;
+
+irq_fail:
+       if (fg->interval)
+               cancel_delayed_work(&fg->work);
+
+       return ret;
+}
+
+static int da9150_fg_remove(struct platform_device *pdev)
+{
+       struct da9150_fg *fg = platform_get_drvdata(pdev);
+
+       if (fg->interval)
+               cancel_delayed_work(&fg->work);
+
+       return 0;
+}
+
+static int da9150_fg_resume(struct platform_device *pdev)
+{
+       struct da9150_fg *fg = platform_get_drvdata(pdev);
+
+       /*
+        * Trigger SOC check to happen now so as to indicate any value change
+        * since last check before suspend.
+        */
+       if (fg->interval)
+               flush_delayed_work(&fg->work);
+
+       return 0;
+}
+
+static struct platform_driver da9150_fg_driver = {
+       .driver = {
+               .name = "da9150-fuel-gauge",
+       },
+       .probe = da9150_fg_probe,
+       .remove = da9150_fg_remove,
+       .resume = da9150_fg_resume,
+};
+
+module_platform_driver(da9150_fg_driver);
+
+MODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/ds2760_battery.c b/drivers/power/supply/ds2760_battery.c
new file mode 100644 (file)
index 0000000..369ab00
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * Driver for batteries with DS2760 chips inside.
+ *
+ * Copyright Â© 2007 Anton Vorontsov
+ *            2004-2007 Matt Reimer
+ *            2004 Szabolcs Gyurko
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * Author:  Anton Vorontsov <cbou@mail.ru>
+ *         February 2007
+ *
+ *         Matt Reimer <mreimer@vpop.net>
+ *         April 2004, 2005, 2007
+ *
+ *         Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
+ *         September 2004
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include "../../w1/w1.h"
+#include "../../w1/slaves/w1_ds2760.h"
+
+struct ds2760_device_info {
+       struct device *dev;
+
+       /* DS2760 data, valid after calling ds2760_battery_read_status() */
+       unsigned long update_time;      /* jiffies when data read */
+       char raw[DS2760_DATA_SIZE];     /* raw DS2760 data */
+       int voltage_raw;                /* units of 4.88 mV */
+       int voltage_uV;                 /* units of ÂµV */
+       int current_raw;                /* units of 0.625 mA */
+       int current_uA;                 /* units of ÂµA */
+       int accum_current_raw;          /* units of 0.25 mAh */
+       int accum_current_uAh;          /* units of ÂµAh */
+       int temp_raw;                   /* units of 0.125 Â°C */
+       int temp_C;                     /* units of 0.1 Â°C */
+       int rated_capacity;             /* units of ÂµAh */
+       int rem_capacity;               /* percentage */
+       int full_active_uAh;            /* units of ÂµAh */
+       int empty_uAh;                  /* units of ÂµAh */
+       int life_sec;                   /* units of seconds */
+       int charge_status;              /* POWER_SUPPLY_STATUS_* */
+
+       int full_counter;
+       struct power_supply *bat;
+       struct power_supply_desc bat_desc;
+       struct device *w1_dev;
+       struct workqueue_struct *monitor_wqueue;
+       struct delayed_work monitor_work;
+       struct delayed_work set_charged_work;
+};
+
+static unsigned int cache_time = 1000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+
+static bool pmod_enabled;
+module_param(pmod_enabled, bool, 0644);
+MODULE_PARM_DESC(pmod_enabled, "PMOD enable bit");
+
+static unsigned int rated_capacity;
+module_param(rated_capacity, uint, 0644);
+MODULE_PARM_DESC(rated_capacity, "rated battery capacity, 10*mAh or index");
+
+static unsigned int current_accum;
+module_param(current_accum, uint, 0644);
+MODULE_PARM_DESC(current_accum, "current accumulator value");
+
+/* Some batteries have their rated capacity stored a N * 10 mAh, while
+ * others use an index into this table. */
+static int rated_capacities[] = {
+       0,
+       920,    /* Samsung */
+       920,    /* BYD */
+       920,    /* Lishen */
+       920,    /* NEC */
+       1440,   /* Samsung */
+       1440,   /* BYD */
+#ifdef CONFIG_MACH_H4700
+       1800,   /* HP iPAQ hx4700 3.7V 1800mAh (359113-001) */
+#else
+       1440,   /* Lishen */
+#endif
+       1440,   /* NEC */
+       2880,   /* Samsung */
+       2880,   /* BYD */
+       2880,   /* Lishen */
+       2880,   /* NEC */
+#ifdef CONFIG_MACH_H4700
+       0,
+       3600,   /* HP iPAQ hx4700 3.7V 3600mAh (359114-001) */
+#endif
+};
+
+/* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C
+ * temp is in Celsius */
+static int battery_interpolate(int array[], int temp)
+{
+       int index, dt;
+
+       if (temp <= 0)
+               return array[0];
+       if (temp >= 40)
+               return array[4];
+
+       index = temp / 10;
+       dt    = temp % 10;
+
+       return array[index] + (((array[index + 1] - array[index]) * dt) / 10);
+}
+
+static int ds2760_battery_read_status(struct ds2760_device_info *di)
+{
+       int ret, i, start, count, scale[5];
+
+       if (di->update_time && time_before(jiffies, di->update_time +
+                                          msecs_to_jiffies(cache_time)))
+               return 0;
+
+       /* The first time we read the entire contents of SRAM/EEPROM,
+        * but after that we just read the interesting bits that change. */
+       if (di->update_time == 0) {
+               start = 0;
+               count = DS2760_DATA_SIZE;
+       } else {
+               start = DS2760_VOLTAGE_MSB;
+               count = DS2760_TEMP_LSB - start + 1;
+       }
+
+       ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count);
+       if (ret != count) {
+               dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n",
+                        di->w1_dev);
+               return 1;
+       }
+
+       di->update_time = jiffies;
+
+       /* DS2760 reports voltage in units of 4.88mV, but the battery class
+        * reports in units of uV, so convert by multiplying by 4880. */
+       di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) |
+                         (di->raw[DS2760_VOLTAGE_LSB] >> 5);
+       di->voltage_uV = di->voltage_raw * 4880;
+
+       /* DS2760 reports current in signed units of 0.625mA, but the battery
+        * class reports in units of ÂµA, so convert by multiplying by 625. */
+       di->current_raw =
+           (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) |
+                         (di->raw[DS2760_CURRENT_LSB] >> 3);
+       di->current_uA = di->current_raw * 625;
+
+       /* DS2760 reports accumulated current in signed units of 0.25mAh. */
+       di->accum_current_raw =
+           (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) |
+                          di->raw[DS2760_CURRENT_ACCUM_LSB];
+       di->accum_current_uAh = di->accum_current_raw * 250;
+
+       /* DS2760 reports temperature in signed units of 0.125°C, but the
+        * battery class reports in units of 1/10 Â°C, so we convert by
+        * multiplying by .125 * 10 = 1.25. */
+       di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) |
+                                    (di->raw[DS2760_TEMP_LSB] >> 5);
+       di->temp_C = di->temp_raw + (di->temp_raw / 4);
+
+       /* At least some battery monitors (e.g. HP iPAQ) store the battery's
+        * maximum rated capacity. */
+       if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities))
+               di->rated_capacity = rated_capacities[
+                       (unsigned int)di->raw[DS2760_RATED_CAPACITY]];
+       else
+               di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10;
+
+       di->rated_capacity *= 1000; /* convert to ÂµAh */
+
+       /* Calculate the full level at the present temperature. */
+       di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 |
+                             di->raw[DS2760_ACTIVE_FULL + 1];
+
+       /* If the full_active_uAh value is not given, fall back to the rated
+        * capacity. This is likely to happen when chips are not part of the
+        * battery pack and is therefore not bootstrapped. */
+       if (di->full_active_uAh == 0)
+               di->full_active_uAh = di->rated_capacity / 1000L;
+
+       scale[0] = di->full_active_uAh;
+       for (i = 1; i < 5; i++)
+               scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 1 + i];
+
+       di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10);
+       di->full_active_uAh *= 1000; /* convert to ÂµAh */
+
+       /* Calculate the empty level at the present temperature. */
+       scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4];
+       for (i = 3; i >= 0; i--)
+               scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i];
+
+       di->empty_uAh = battery_interpolate(scale, di->temp_C / 10);
+       di->empty_uAh *= 1000; /* convert to ÂµAh */
+
+       if (di->full_active_uAh == di->empty_uAh)
+               di->rem_capacity = 0;
+       else
+               /* From Maxim Application Note 131: remaining capacity =
+                * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */
+               di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) /
+                                   (di->full_active_uAh - di->empty_uAh);
+
+       if (di->rem_capacity < 0)
+               di->rem_capacity = 0;
+       if (di->rem_capacity > 100)
+               di->rem_capacity = 100;
+
+       if (di->current_uA < -100L)
+               di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * 36L)
+                                       / (di->current_uA / 100L);
+       else
+               di->life_sec = 0;
+
+       return 0;
+}
+
+static void ds2760_battery_set_current_accum(struct ds2760_device_info *di,
+                                            unsigned int acr_val)
+{
+       unsigned char acr[2];
+
+       /* acr is in units of 0.25 mAh */
+       acr_val *= 4L;
+       acr_val /= 1000;
+
+       acr[0] = acr_val >> 8;
+       acr[1] = acr_val & 0xff;
+
+       if (w1_ds2760_write(di->w1_dev, acr, DS2760_CURRENT_ACCUM_MSB, 2) < 2)
+               dev_warn(di->dev, "ACR write failed\n");
+}
+
+static void ds2760_battery_update_status(struct ds2760_device_info *di)
+{
+       int old_charge_status = di->charge_status;
+
+       ds2760_battery_read_status(di);
+
+       if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN)
+               di->full_counter = 0;
+
+       if (power_supply_am_i_supplied(di->bat)) {
+               if (di->current_uA > 10000) {
+                       di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+                       di->full_counter = 0;
+               } else if (di->current_uA < -5000) {
+                       if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING)
+                               dev_notice(di->dev, "not enough power to "
+                                          "charge\n");
+                       di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+                       di->full_counter = 0;
+               } else if (di->current_uA < 10000 &&
+                           di->charge_status != POWER_SUPPLY_STATUS_FULL) {
+
+                       /* Don't consider the battery to be full unless
+                        * we've seen the current < 10 mA at least two
+                        * consecutive times. */
+
+                       di->full_counter++;
+
+                       if (di->full_counter < 2) {
+                               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+                       } else {
+                               di->charge_status = POWER_SUPPLY_STATUS_FULL;
+                               ds2760_battery_set_current_accum(di,
+                                               di->full_active_uAh);
+                       }
+               }
+       } else {
+               di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+               di->full_counter = 0;
+       }
+
+       if (di->charge_status != old_charge_status)
+               power_supply_changed(di->bat);
+}
+
+static void ds2760_battery_write_status(struct ds2760_device_info *di,
+                                       char status)
+{
+       if (status == di->raw[DS2760_STATUS_REG])
+               return;
+
+       w1_ds2760_write(di->w1_dev, &status, DS2760_STATUS_WRITE_REG, 1);
+       w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+       w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+}
+
+static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di,
+                                               unsigned char rated_capacity)
+{
+       if (rated_capacity == di->raw[DS2760_RATED_CAPACITY])
+               return;
+
+       w1_ds2760_write(di->w1_dev, &rated_capacity, DS2760_RATED_CAPACITY, 1);
+       w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+       w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+}
+
+static void ds2760_battery_write_active_full(struct ds2760_device_info *di,
+                                            int active_full)
+{
+       unsigned char tmp[2] = {
+               active_full >> 8,
+               active_full & 0xff
+       };
+
+       if (tmp[0] == di->raw[DS2760_ACTIVE_FULL] &&
+           tmp[1] == di->raw[DS2760_ACTIVE_FULL + 1])
+               return;
+
+       w1_ds2760_write(di->w1_dev, tmp, DS2760_ACTIVE_FULL, sizeof(tmp));
+       w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
+       w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
+
+       /* Write to the di->raw[] buffer directly - the DS2760_ACTIVE_FULL
+        * values won't be read back by ds2760_battery_read_status() */
+       di->raw[DS2760_ACTIVE_FULL] = tmp[0];
+       di->raw[DS2760_ACTIVE_FULL + 1] = tmp[1];
+}
+
+static void ds2760_battery_work(struct work_struct *work)
+{
+       struct ds2760_device_info *di = container_of(work,
+               struct ds2760_device_info, monitor_work.work);
+       const int interval = HZ * 60;
+
+       dev_dbg(di->dev, "%s\n", __func__);
+
+       ds2760_battery_update_status(di);
+       queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+}
+
+static void ds2760_battery_external_power_changed(struct power_supply *psy)
+{
+       struct ds2760_device_info *di = power_supply_get_drvdata(psy);
+
+       dev_dbg(di->dev, "%s\n", __func__);
+
+       mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10);
+}
+
+
+static void ds2760_battery_set_charged_work(struct work_struct *work)
+{
+       char bias;
+       struct ds2760_device_info *di = container_of(work,
+               struct ds2760_device_info, set_charged_work.work);
+
+       dev_dbg(di->dev, "%s\n", __func__);
+
+       ds2760_battery_read_status(di);
+
+       /* When we get notified by external circuitry that the battery is
+        * considered fully charged now, we know that there is no current
+        * flow any more. However, the ds2760's internal current meter is
+        * too inaccurate to rely on - spec say something ~15% failure.
+        * Hence, we use the current offset bias register to compensate
+        * that error.
+        */
+
+       if (!power_supply_am_i_supplied(di->bat))
+               return;
+
+       bias = (signed char) di->current_raw +
+               (signed char) di->raw[DS2760_CURRENT_OFFSET_BIAS];
+
+       dev_dbg(di->dev, "%s: bias = %d\n", __func__, bias);
+
+       w1_ds2760_write(di->w1_dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1);
+       w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+       w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
+
+       /* Write to the di->raw[] buffer directly - the CURRENT_OFFSET_BIAS
+        * value won't be read back by ds2760_battery_read_status() */
+       di->raw[DS2760_CURRENT_OFFSET_BIAS] = bias;
+}
+
+static void ds2760_battery_set_charged(struct power_supply *psy)
+{
+       struct ds2760_device_info *di = power_supply_get_drvdata(psy);
+
+       /* postpone the actual work by 20 secs. This is for debouncing GPIO
+        * signals and to let the current value settle. See AN4188. */
+       mod_delayed_work(di->monitor_wqueue, &di->set_charged_work, HZ * 20);
+}
+
+static int ds2760_battery_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct ds2760_device_info *di = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = di->charge_status;
+               return 0;
+       default:
+               break;
+       }
+
+       ds2760_battery_read_status(di);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = di->voltage_uV;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = di->current_uA;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = di->rated_capacity;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               val->intval = di->full_active_uAh;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_EMPTY:
+               val->intval = di->empty_uAh;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               val->intval = di->accum_current_uAh;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = di->temp_C;
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+               val->intval = di->life_sec;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = di->rem_capacity;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ds2760_battery_set_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      const union power_supply_propval *val)
+{
+       struct ds2760_device_info *di = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               /* the interface counts in uAh, convert the value */
+               ds2760_battery_write_active_full(di, val->intval / 1000L);
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               /* ds2760_battery_set_current_accum() does the conversion */
+               ds2760_battery_set_current_accum(di, val->intval);
+               break;
+
+       default:
+               return -EPERM;
+       }
+
+       return 0;
+}
+
+static int ds2760_battery_property_is_writeable(struct power_supply *psy,
+                                               enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               return 1;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property ds2760_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_EMPTY,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static int ds2760_battery_probe(struct platform_device *pdev)
+{
+       struct power_supply_config psy_cfg = {};
+       char status;
+       int retval = 0;
+       struct ds2760_device_info *di;
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       if (!di) {
+               retval = -ENOMEM;
+               goto di_alloc_failed;
+       }
+
+       platform_set_drvdata(pdev, di);
+
+       di->dev                         = &pdev->dev;
+       di->w1_dev                      = pdev->dev.parent;
+       di->bat_desc.name               = dev_name(&pdev->dev);
+       di->bat_desc.type               = POWER_SUPPLY_TYPE_BATTERY;
+       di->bat_desc.properties         = ds2760_battery_props;
+       di->bat_desc.num_properties     = ARRAY_SIZE(ds2760_battery_props);
+       di->bat_desc.get_property       = ds2760_battery_get_property;
+       di->bat_desc.set_property       = ds2760_battery_set_property;
+       di->bat_desc.property_is_writeable =
+                                 ds2760_battery_property_is_writeable;
+       di->bat_desc.set_charged        = ds2760_battery_set_charged;
+       di->bat_desc.external_power_changed =
+                                 ds2760_battery_external_power_changed;
+
+       psy_cfg.drv_data                = di;
+
+       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       /* enable sleep mode feature */
+       ds2760_battery_read_status(di);
+       status = di->raw[DS2760_STATUS_REG];
+       if (pmod_enabled)
+               status |= DS2760_STATUS_PMOD;
+       else
+               status &= ~DS2760_STATUS_PMOD;
+
+       ds2760_battery_write_status(di, status);
+
+       /* set rated capacity from module param */
+       if (rated_capacity)
+               ds2760_battery_write_rated_capacity(di, rated_capacity);
+
+       /* set current accumulator if given as parameter.
+        * this should only be done for bootstrapping the value */
+       if (current_accum)
+               ds2760_battery_set_current_accum(di, current_accum);
+
+       di->bat = power_supply_register(&pdev->dev, &di->bat_desc, &psy_cfg);
+       if (IS_ERR(di->bat)) {
+               dev_err(di->dev, "failed to register battery\n");
+               retval = PTR_ERR(di->bat);
+               goto batt_failed;
+       }
+
+       INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work);
+       INIT_DELAYED_WORK(&di->set_charged_work,
+                         ds2760_battery_set_charged_work);
+       di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
+       if (!di->monitor_wqueue) {
+               retval = -ESRCH;
+               goto workqueue_failed;
+       }
+       queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1);
+
+       goto success;
+
+workqueue_failed:
+       power_supply_unregister(di->bat);
+batt_failed:
+di_alloc_failed:
+success:
+       return retval;
+}
+
+static int ds2760_battery_remove(struct platform_device *pdev)
+{
+       struct ds2760_device_info *di = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&di->monitor_work);
+       cancel_delayed_work_sync(&di->set_charged_work);
+       destroy_workqueue(di->monitor_wqueue);
+       power_supply_unregister(di->bat);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int ds2760_battery_suspend(struct platform_device *pdev,
+                                 pm_message_t state)
+{
+       struct ds2760_device_info *di = platform_get_drvdata(pdev);
+
+       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       return 0;
+}
+
+static int ds2760_battery_resume(struct platform_device *pdev)
+{
+       struct ds2760_device_info *di = platform_get_drvdata(pdev);
+
+       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+       power_supply_changed(di->bat);
+
+       mod_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ);
+
+       return 0;
+}
+
+#else
+
+#define ds2760_battery_suspend NULL
+#define ds2760_battery_resume NULL
+
+#endif /* CONFIG_PM */
+
+MODULE_ALIAS("platform:ds2760-battery");
+
+static struct platform_driver ds2760_battery_driver = {
+       .driver = {
+               .name = "ds2760-battery",
+       },
+       .probe    = ds2760_battery_probe,
+       .remove   = ds2760_battery_remove,
+       .suspend  = ds2760_battery_suspend,
+       .resume   = ds2760_battery_resume,
+};
+
+module_platform_driver(ds2760_battery_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
+             "Matt Reimer <mreimer@vpop.net>, "
+             "Anton Vorontsov <cbou@mail.ru>");
+MODULE_DESCRIPTION("ds2760 battery driver");
diff --git a/drivers/power/supply/ds2780_battery.c b/drivers/power/supply/ds2780_battery.c
new file mode 100644 (file)
index 0000000..1b3b6fa
--- /dev/null
@@ -0,0 +1,838 @@
+/*
+ * 1-wire client/driver for the Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC
+ *
+ * Copyright (C) 2010 Indesign, LLC
+ *
+ * Author: Clifton Barnes <cabarnes@indesign-llc.com>
+ *
+ * Based on ds2760_battery and ds2782_battery drivers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/param.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+
+#include "../../w1/w1.h"
+#include "../../w1/slaves/w1_ds2780.h"
+
+/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
+#define DS2780_CURRENT_UNITS   1563
+/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */
+#define DS2780_CHARGE_UNITS            6250
+/* Number of bytes in user EEPROM space */
+#define DS2780_USER_EEPROM_SIZE                (DS2780_EEPROM_BLOCK0_END - \
+                                       DS2780_EEPROM_BLOCK0_START + 1)
+/* Number of bytes in parameter EEPROM space */
+#define DS2780_PARAM_EEPROM_SIZE       (DS2780_EEPROM_BLOCK1_END - \
+                                       DS2780_EEPROM_BLOCK1_START + 1)
+
+struct ds2780_device_info {
+       struct device *dev;
+       struct power_supply *bat;
+       struct power_supply_desc bat_desc;
+       struct device *w1_dev;
+};
+
+enum current_types {
+       CURRENT_NOW,
+       CURRENT_AVG,
+};
+
+static const char model[] = "DS2780";
+static const char manufacturer[] = "Maxim/Dallas";
+
+static inline struct ds2780_device_info *
+to_ds2780_device_info(struct power_supply *psy)
+{
+       return power_supply_get_drvdata(psy);
+}
+
+static inline struct power_supply *to_power_supply(struct device *dev)
+{
+       return dev_get_drvdata(dev);
+}
+
+static inline int ds2780_battery_io(struct ds2780_device_info *dev_info,
+       char *buf, int addr, size_t count, int io)
+{
+       return w1_ds2780_io(dev_info->w1_dev, buf, addr, count, io);
+}
+
+static inline int ds2780_read8(struct ds2780_device_info *dev_info, u8 *val,
+       int addr)
+{
+       return ds2780_battery_io(dev_info, val, addr, sizeof(u8), 0);
+}
+
+static int ds2780_read16(struct ds2780_device_info *dev_info, s16 *val,
+       int addr)
+{
+       int ret;
+       u8 raw[2];
+
+       ret = ds2780_battery_io(dev_info, raw, addr, sizeof(raw), 0);
+       if (ret < 0)
+               return ret;
+
+       *val = (raw[0] << 8) | raw[1];
+
+       return 0;
+}
+
+static inline int ds2780_read_block(struct ds2780_device_info *dev_info,
+       u8 *val, int addr, size_t count)
+{
+       return ds2780_battery_io(dev_info, val, addr, count, 0);
+}
+
+static inline int ds2780_write(struct ds2780_device_info *dev_info, u8 *val,
+       int addr, size_t count)
+{
+       return ds2780_battery_io(dev_info, val, addr, count, 1);
+}
+
+static inline int ds2780_store_eeprom(struct device *dev, int addr)
+{
+       return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_COPY_DATA);
+}
+
+static inline int ds2780_recall_eeprom(struct device *dev, int addr)
+{
+       return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_RECALL_DATA);
+}
+
+static int ds2780_save_eeprom(struct ds2780_device_info *dev_info, int reg)
+{
+       int ret;
+
+       ret = ds2780_store_eeprom(dev_info->w1_dev, reg);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2780_recall_eeprom(dev_info->w1_dev, reg);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/* Set sense resistor value in mhos */
+static int ds2780_set_sense_register(struct ds2780_device_info *dev_info,
+       u8 conductance)
+{
+       int ret;
+
+       ret = ds2780_write(dev_info, &conductance,
+                               DS2780_RSNSP_REG, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return ds2780_save_eeprom(dev_info, DS2780_RSNSP_REG);
+}
+
+/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */
+static int ds2780_get_rsgain_register(struct ds2780_device_info *dev_info,
+       u16 *rsgain)
+{
+       return ds2780_read16(dev_info, rsgain, DS2780_RSGAIN_MSB_REG);
+}
+
+/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */
+static int ds2780_set_rsgain_register(struct ds2780_device_info *dev_info,
+       u16 rsgain)
+{
+       int ret;
+       u8 raw[] = {rsgain >> 8, rsgain & 0xFF};
+
+       ret = ds2780_write(dev_info, raw,
+                               DS2780_RSGAIN_MSB_REG, sizeof(raw));
+       if (ret < 0)
+               return ret;
+
+       return ds2780_save_eeprom(dev_info, DS2780_RSGAIN_MSB_REG);
+}
+
+static int ds2780_get_voltage(struct ds2780_device_info *dev_info,
+       int *voltage_uV)
+{
+       int ret;
+       s16 voltage_raw;
+
+       /*
+        * The voltage value is located in 10 bits across the voltage MSB
+        * and LSB registers in two's compliment form
+        * Sign bit of the voltage value is in bit 7 of the voltage MSB register
+        * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the
+        * voltage MSB register
+        * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the
+        * voltage LSB register
+        */
+       ret = ds2780_read16(dev_info, &voltage_raw,
+                               DS2780_VOLT_MSB_REG);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * DS2780 reports voltage in units of 4.88mV, but the battery class
+        * reports in units of uV, so convert by multiplying by 4880.
+        */
+       *voltage_uV = (voltage_raw / 32) * 4880;
+       return 0;
+}
+
+static int ds2780_get_temperature(struct ds2780_device_info *dev_info,
+       int *temperature)
+{
+       int ret;
+       s16 temperature_raw;
+
+       /*
+        * The temperature value is located in 10 bits across the temperature
+        * MSB and LSB registers in two's compliment form
+        * Sign bit of the temperature value is in bit 7 of the temperature
+        * MSB register
+        * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the
+        * temperature MSB register
+        * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the
+        * temperature LSB register
+        */
+       ret = ds2780_read16(dev_info, &temperature_raw,
+                               DS2780_TEMP_MSB_REG);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Temperature is measured in units of 0.125 degrees celcius, the
+        * power_supply class measures temperature in tenths of degrees
+        * celsius. The temperature value is stored as a 10 bit number, plus
+        * sign in the upper bits of a 16 bit register.
+        */
+       *temperature = ((temperature_raw / 32) * 125) / 100;
+       return 0;
+}
+
+static int ds2780_get_current(struct ds2780_device_info *dev_info,
+       enum current_types type, int *current_uA)
+{
+       int ret, sense_res;
+       s16 current_raw;
+       u8 sense_res_raw, reg_msb;
+
+       /*
+        * The units of measurement for current are dependent on the value of
+        * the sense resistor.
+        */
+       ret = ds2780_read8(dev_info, &sense_res_raw, DS2780_RSNSP_REG);
+       if (ret < 0)
+               return ret;
+
+       if (sense_res_raw == 0) {
+               dev_err(dev_info->dev, "sense resistor value is 0\n");
+               return -EINVAL;
+       }
+       sense_res = 1000 / sense_res_raw;
+
+       if (type == CURRENT_NOW)
+               reg_msb = DS2780_CURRENT_MSB_REG;
+       else if (type == CURRENT_AVG)
+               reg_msb = DS2780_IAVG_MSB_REG;
+       else
+               return -EINVAL;
+
+       /*
+        * The current value is located in 16 bits across the current MSB
+        * and LSB registers in two's compliment form
+        * Sign bit of the current value is in bit 7 of the current MSB register
+        * Bits 14 - 8 of the current value are in bits 6 - 0 of the current
+        * MSB register
+        * Bits 7 - 0 of the current value are in bits 7 - 0 of the current
+        * LSB register
+        */
+       ret = ds2780_read16(dev_info, &current_raw, reg_msb);
+       if (ret < 0)
+               return ret;
+
+       *current_uA = current_raw * (DS2780_CURRENT_UNITS / sense_res);
+       return 0;
+}
+
+static int ds2780_get_accumulated_current(struct ds2780_device_info *dev_info,
+       int *accumulated_current)
+{
+       int ret, sense_res;
+       s16 current_raw;
+       u8 sense_res_raw;
+
+       /*
+        * The units of measurement for accumulated current are dependent on
+        * the value of the sense resistor.
+        */
+       ret = ds2780_read8(dev_info, &sense_res_raw, DS2780_RSNSP_REG);
+       if (ret < 0)
+               return ret;
+
+       if (sense_res_raw == 0) {
+               dev_err(dev_info->dev, "sense resistor value is 0\n");
+               return -ENXIO;
+       }
+       sense_res = 1000 / sense_res_raw;
+
+       /*
+        * The ACR value is located in 16 bits across the ACR MSB and
+        * LSB registers
+        * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR
+        * MSB register
+        * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR
+        * LSB register
+        */
+       ret = ds2780_read16(dev_info, &current_raw, DS2780_ACR_MSB_REG);
+       if (ret < 0)
+               return ret;
+
+       *accumulated_current = current_raw * (DS2780_CHARGE_UNITS / sense_res);
+       return 0;
+}
+
+static int ds2780_get_capacity(struct ds2780_device_info *dev_info,
+       int *capacity)
+{
+       int ret;
+       u8 raw;
+
+       ret = ds2780_read8(dev_info, &raw, DS2780_RARC_REG);
+       if (ret < 0)
+               return ret;
+
+       *capacity = raw;
+       return raw;
+}
+
+static int ds2780_get_status(struct ds2780_device_info *dev_info, int *status)
+{
+       int ret, current_uA, capacity;
+
+       ret = ds2780_get_current(dev_info, CURRENT_NOW, &current_uA);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2780_get_capacity(dev_info, &capacity);
+       if (ret < 0)
+               return ret;
+
+       if (capacity == 100)
+               *status = POWER_SUPPLY_STATUS_FULL;
+       else if (current_uA == 0)
+               *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       else if (current_uA < 0)
+               *status = POWER_SUPPLY_STATUS_DISCHARGING;
+       else
+               *status = POWER_SUPPLY_STATUS_CHARGING;
+
+       return 0;
+}
+
+static int ds2780_get_charge_now(struct ds2780_device_info *dev_info,
+       int *charge_now)
+{
+       int ret;
+       u16 charge_raw;
+
+       /*
+        * The RAAC value is located in 16 bits across the RAAC MSB and
+        * LSB registers
+        * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC
+        * MSB register
+        * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC
+        * LSB register
+        */
+       ret = ds2780_read16(dev_info, &charge_raw, DS2780_RAAC_MSB_REG);
+       if (ret < 0)
+               return ret;
+
+       *charge_now = charge_raw * 1600;
+       return 0;
+}
+
+static int ds2780_get_control_register(struct ds2780_device_info *dev_info,
+       u8 *control_reg)
+{
+       return ds2780_read8(dev_info, control_reg, DS2780_CONTROL_REG);
+}
+
+static int ds2780_set_control_register(struct ds2780_device_info *dev_info,
+       u8 control_reg)
+{
+       int ret;
+
+       ret = ds2780_write(dev_info, &control_reg,
+                               DS2780_CONTROL_REG, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return ds2780_save_eeprom(dev_info, DS2780_CONTROL_REG);
+}
+
+static int ds2780_battery_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       int ret = 0;
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = ds2780_get_voltage(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = ds2780_get_temperature(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = model;
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = manufacturer;
+               break;
+
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = ds2780_get_current(dev_info, CURRENT_NOW, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               ret = ds2780_get_current(dev_info, CURRENT_AVG, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = ds2780_get_status(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = ds2780_get_capacity(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+               ret = ds2780_get_accumulated_current(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               ret = ds2780_get_charge_now(dev_info, &val->intval);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property ds2780_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+};
+
+static ssize_t ds2780_get_pmod_enabled(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 control_reg;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       /* Get power mode */
+       ret = ds2780_get_control_register(dev_info, &control_reg);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n",
+                !!(control_reg & DS2780_CONTROL_REG_PMOD));
+}
+
+static ssize_t ds2780_set_pmod_enabled(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 control_reg, new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       /* Set power mode */
+       ret = ds2780_get_control_register(dev_info, &control_reg);
+       if (ret < 0)
+               return ret;
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       if ((new_setting != 0) && (new_setting != 1)) {
+               dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n");
+               return -EINVAL;
+       }
+
+       if (new_setting)
+               control_reg |= DS2780_CONTROL_REG_PMOD;
+       else
+               control_reg &= ~DS2780_CONTROL_REG_PMOD;
+
+       ret = ds2780_set_control_register(dev_info, control_reg);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2780_get_sense_resistor_value(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 sense_resistor;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       ret = ds2780_read8(dev_info, &sense_resistor, DS2780_RSNSP_REG);
+       if (ret < 0)
+               return ret;
+
+       ret = sprintf(buf, "%d\n", sense_resistor);
+       return ret;
+}
+
+static ssize_t ds2780_set_sense_resistor_value(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2780_set_sense_register(dev_info, new_setting);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2780_get_rsgain_setting(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u16 rsgain;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       ret = ds2780_get_rsgain_register(dev_info, &rsgain);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n", rsgain);
+}
+
+static ssize_t ds2780_set_rsgain_setting(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u16 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       ret = kstrtou16(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       /* Gain can only be from 0 to 1.999 in steps of .001 */
+       if (new_setting > 1999) {
+               dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n");
+               return -EINVAL;
+       }
+
+       ret = ds2780_set_rsgain_register(dev_info, new_setting);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2780_get_pio_pin(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 sfr;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       ret = ds2780_read8(dev_info, &sfr, DS2780_SFR_REG);
+       if (ret < 0)
+               return ret;
+
+       ret = sprintf(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC);
+       return ret;
+}
+
+static ssize_t ds2780_set_pio_pin(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       if ((new_setting != 0) && (new_setting != 1)) {
+               dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n");
+               return -EINVAL;
+       }
+
+       ret = ds2780_write(dev_info, &new_setting,
+                               DS2780_SFR_REG, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       return ds2780_read_block(dev_info, buf,
+                               DS2780_EEPROM_BLOCK1_START + off, count);
+}
+
+static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+       int ret;
+
+       ret = ds2780_write(dev_info, buf,
+                               DS2780_EEPROM_BLOCK1_START + off, count);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK1_START);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static struct bin_attribute ds2780_param_eeprom_bin_attr = {
+       .attr = {
+               .name = "param_eeprom",
+               .mode = S_IRUGO | S_IWUSR,
+       },
+       .size = DS2780_PARAM_EEPROM_SIZE,
+       .read = ds2780_read_param_eeprom_bin,
+       .write = ds2780_write_param_eeprom_bin,
+};
+
+static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+       return ds2780_read_block(dev_info, buf,
+                               DS2780_EEPROM_BLOCK0_START + off, count);
+}
+
+static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+       int ret;
+
+       ret = ds2780_write(dev_info, buf,
+                               DS2780_EEPROM_BLOCK0_START + off, count);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK0_START);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static struct bin_attribute ds2780_user_eeprom_bin_attr = {
+       .attr = {
+               .name = "user_eeprom",
+               .mode = S_IRUGO | S_IWUSR,
+       },
+       .size = DS2780_USER_EEPROM_SIZE,
+       .read = ds2780_read_user_eeprom_bin,
+       .write = ds2780_write_user_eeprom_bin,
+};
+
+static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2780_get_pmod_enabled,
+       ds2780_set_pmod_enabled);
+static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR,
+       ds2780_get_sense_resistor_value, ds2780_set_sense_resistor_value);
+static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2780_get_rsgain_setting,
+       ds2780_set_rsgain_setting);
+static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2780_get_pio_pin,
+       ds2780_set_pio_pin);
+
+
+static struct attribute *ds2780_attributes[] = {
+       &dev_attr_pmod_enabled.attr,
+       &dev_attr_sense_resistor_value.attr,
+       &dev_attr_rsgain_setting.attr,
+       &dev_attr_pio_pin.attr,
+       NULL
+};
+
+static const struct attribute_group ds2780_attr_group = {
+       .attrs = ds2780_attributes,
+};
+
+static int ds2780_battery_probe(struct platform_device *pdev)
+{
+       struct power_supply_config psy_cfg = {};
+       int ret = 0;
+       struct ds2780_device_info *dev_info;
+
+       dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
+       if (!dev_info) {
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       platform_set_drvdata(pdev, dev_info);
+
+       dev_info->dev                   = &pdev->dev;
+       dev_info->w1_dev                = pdev->dev.parent;
+       dev_info->bat_desc.name         = dev_name(&pdev->dev);
+       dev_info->bat_desc.type         = POWER_SUPPLY_TYPE_BATTERY;
+       dev_info->bat_desc.properties   = ds2780_battery_props;
+       dev_info->bat_desc.num_properties = ARRAY_SIZE(ds2780_battery_props);
+       dev_info->bat_desc.get_property = ds2780_battery_get_property;
+
+       psy_cfg.drv_data                = dev_info;
+
+       dev_info->bat = power_supply_register(&pdev->dev, &dev_info->bat_desc,
+                                             &psy_cfg);
+       if (IS_ERR(dev_info->bat)) {
+               dev_err(dev_info->dev, "failed to register battery\n");
+               ret = PTR_ERR(dev_info->bat);
+               goto fail;
+       }
+
+       ret = sysfs_create_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
+       if (ret) {
+               dev_err(dev_info->dev, "failed to create sysfs group\n");
+               goto fail_unregister;
+       }
+
+       ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
+                                       &ds2780_param_eeprom_bin_attr);
+       if (ret) {
+               dev_err(dev_info->dev,
+                               "failed to create param eeprom bin file");
+               goto fail_remove_group;
+       }
+
+       ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
+                                       &ds2780_user_eeprom_bin_attr);
+       if (ret) {
+               dev_err(dev_info->dev,
+                               "failed to create user eeprom bin file");
+               goto fail_remove_bin_file;
+       }
+
+       return 0;
+
+fail_remove_bin_file:
+       sysfs_remove_bin_file(&dev_info->bat->dev.kobj,
+                               &ds2780_param_eeprom_bin_attr);
+fail_remove_group:
+       sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
+fail_unregister:
+       power_supply_unregister(dev_info->bat);
+fail:
+       return ret;
+}
+
+static int ds2780_battery_remove(struct platform_device *pdev)
+{
+       struct ds2780_device_info *dev_info = platform_get_drvdata(pdev);
+
+       /*
+        * Remove attributes before unregistering power supply
+        * because 'bat' will be freed on power_supply_unregister() call.
+        */
+       sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2780_attr_group);
+
+       power_supply_unregister(dev_info->bat);
+
+       return 0;
+}
+
+static struct platform_driver ds2780_battery_driver = {
+       .driver = {
+               .name = "ds2780-battery",
+       },
+       .probe    = ds2780_battery_probe,
+       .remove   = ds2780_battery_remove,
+};
+
+module_platform_driver(ds2780_battery_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver");
+MODULE_ALIAS("platform:ds2780-battery");
diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c
new file mode 100644 (file)
index 0000000..cc01491
--- /dev/null
@@ -0,0 +1,839 @@
+/*
+ * 1-wire client/driver for the Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC
+ *
+ * Author: Renata Sayakhova <renata@oktetlabs.ru>
+ *
+ * Based on ds2780_battery drivers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/param.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+
+#include "../../w1/w1.h"
+#include "../../w1/slaves/w1_ds2781.h"
+
+/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
+#define DS2781_CURRENT_UNITS   1563
+/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */
+#define DS2781_CHARGE_UNITS            6250
+/* Number of bytes in user EEPROM space */
+#define DS2781_USER_EEPROM_SIZE                (DS2781_EEPROM_BLOCK0_END - \
+                                       DS2781_EEPROM_BLOCK0_START + 1)
+/* Number of bytes in parameter EEPROM space */
+#define DS2781_PARAM_EEPROM_SIZE       (DS2781_EEPROM_BLOCK1_END - \
+                                       DS2781_EEPROM_BLOCK1_START + 1)
+
+struct ds2781_device_info {
+       struct device *dev;
+       struct power_supply *bat;
+       struct power_supply_desc bat_desc;
+       struct device *w1_dev;
+};
+
+enum current_types {
+       CURRENT_NOW,
+       CURRENT_AVG,
+};
+
+static const char model[] = "DS2781";
+static const char manufacturer[] = "Maxim/Dallas";
+
+static inline struct ds2781_device_info *
+to_ds2781_device_info(struct power_supply *psy)
+{
+       return power_supply_get_drvdata(psy);
+}
+
+static inline struct power_supply *to_power_supply(struct device *dev)
+{
+       return dev_get_drvdata(dev);
+}
+
+static inline int ds2781_battery_io(struct ds2781_device_info *dev_info,
+       char *buf, int addr, size_t count, int io)
+{
+       return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io);
+}
+
+static int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf,
+               int addr, size_t count)
+{
+       return ds2781_battery_io(dev_info, buf, addr, count, 0);
+}
+
+static inline int ds2781_read8(struct ds2781_device_info *dev_info, u8 *val,
+       int addr)
+{
+       return ds2781_battery_io(dev_info, val, addr, sizeof(u8), 0);
+}
+
+static int ds2781_read16(struct ds2781_device_info *dev_info, s16 *val,
+       int addr)
+{
+       int ret;
+       u8 raw[2];
+
+       ret = ds2781_battery_io(dev_info, raw, addr, sizeof(raw), 0);
+       if (ret < 0)
+               return ret;
+
+       *val = (raw[0] << 8) | raw[1];
+
+       return 0;
+}
+
+static inline int ds2781_read_block(struct ds2781_device_info *dev_info,
+       u8 *val, int addr, size_t count)
+{
+       return ds2781_battery_io(dev_info, val, addr, count, 0);
+}
+
+static inline int ds2781_write(struct ds2781_device_info *dev_info, u8 *val,
+       int addr, size_t count)
+{
+       return ds2781_battery_io(dev_info, val, addr, count, 1);
+}
+
+static inline int ds2781_store_eeprom(struct device *dev, int addr)
+{
+       return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_COPY_DATA);
+}
+
+static inline int ds2781_recall_eeprom(struct device *dev, int addr)
+{
+       return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_RECALL_DATA);
+}
+
+static int ds2781_save_eeprom(struct ds2781_device_info *dev_info, int reg)
+{
+       int ret;
+
+       ret = ds2781_store_eeprom(dev_info->w1_dev, reg);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_recall_eeprom(dev_info->w1_dev, reg);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/* Set sense resistor value in mhos */
+static int ds2781_set_sense_register(struct ds2781_device_info *dev_info,
+       u8 conductance)
+{
+       int ret;
+
+       ret = ds2781_write(dev_info, &conductance,
+                               DS2781_RSNSP, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return ds2781_save_eeprom(dev_info, DS2781_RSNSP);
+}
+
+/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */
+static int ds2781_get_rsgain_register(struct ds2781_device_info *dev_info,
+       u16 *rsgain)
+{
+       return ds2781_read16(dev_info, rsgain, DS2781_RSGAIN_MSB);
+}
+
+/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */
+static int ds2781_set_rsgain_register(struct ds2781_device_info *dev_info,
+       u16 rsgain)
+{
+       int ret;
+       u8 raw[] = {rsgain >> 8, rsgain & 0xFF};
+
+       ret = ds2781_write(dev_info, raw,
+                               DS2781_RSGAIN_MSB, sizeof(raw));
+       if (ret < 0)
+               return ret;
+
+       return ds2781_save_eeprom(dev_info, DS2781_RSGAIN_MSB);
+}
+
+static int ds2781_get_voltage(struct ds2781_device_info *dev_info,
+       int *voltage_uV)
+{
+       int ret;
+       char val[2];
+       int voltage_raw;
+
+       ret = w1_ds2781_read(dev_info, val, DS2781_VOLT_MSB, 2 * sizeof(u8));
+       if (ret < 0)
+               return ret;
+       /*
+        * The voltage value is located in 10 bits across the voltage MSB
+        * and LSB registers in two's compliment form
+        * Sign bit of the voltage value is in bit 7 of the voltage MSB register
+        * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the
+        * voltage MSB register
+        * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the
+        * voltage LSB register
+        */
+       voltage_raw = (val[0] << 3) |
+               (val[1] >> 5);
+
+       /* DS2781 reports voltage in units of 9.76mV, but the battery class
+        * reports in units of uV, so convert by multiplying by 9760. */
+       *voltage_uV = voltage_raw * 9760;
+
+       return 0;
+}
+
+static int ds2781_get_temperature(struct ds2781_device_info *dev_info,
+       int *temp)
+{
+       int ret;
+       char val[2];
+       int temp_raw;
+
+       ret = w1_ds2781_read(dev_info, val, DS2781_TEMP_MSB, 2 * sizeof(u8));
+       if (ret < 0)
+               return ret;
+       /*
+        * The temperature value is located in 10 bits across the temperature
+        * MSB and LSB registers in two's compliment form
+        * Sign bit of the temperature value is in bit 7 of the temperature
+        * MSB register
+        * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the
+        * temperature MSB register
+        * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the
+        * temperature LSB register
+        */
+       temp_raw = ((val[0]) << 3) |
+               (val[1] >> 5);
+       *temp = temp_raw + (temp_raw / 4);
+
+       return 0;
+}
+
+static int ds2781_get_current(struct ds2781_device_info *dev_info,
+       enum current_types type, int *current_uA)
+{
+       int ret, sense_res;
+       s16 current_raw;
+       u8 sense_res_raw, reg_msb;
+
+       /*
+        * The units of measurement for current are dependent on the value of
+        * the sense resistor.
+        */
+       ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP);
+       if (ret < 0)
+               return ret;
+
+       if (sense_res_raw == 0) {
+               dev_err(dev_info->dev, "sense resistor value is 0\n");
+               return -EINVAL;
+       }
+       sense_res = 1000 / sense_res_raw;
+
+       if (type == CURRENT_NOW)
+               reg_msb = DS2781_CURRENT_MSB;
+       else if (type == CURRENT_AVG)
+               reg_msb = DS2781_IAVG_MSB;
+       else
+               return -EINVAL;
+
+       /*
+        * The current value is located in 16 bits across the current MSB
+        * and LSB registers in two's compliment form
+        * Sign bit of the current value is in bit 7 of the current MSB register
+        * Bits 14 - 8 of the current value are in bits 6 - 0 of the current
+        * MSB register
+        * Bits 7 - 0 of the current value are in bits 7 - 0 of the current
+        * LSB register
+        */
+       ret = ds2781_read16(dev_info, &current_raw, reg_msb);
+       if (ret < 0)
+               return ret;
+
+       *current_uA = current_raw * (DS2781_CURRENT_UNITS / sense_res);
+       return 0;
+}
+
+static int ds2781_get_accumulated_current(struct ds2781_device_info *dev_info,
+       int *accumulated_current)
+{
+       int ret, sense_res;
+       s16 current_raw;
+       u8 sense_res_raw;
+
+       /*
+        * The units of measurement for accumulated current are dependent on
+        * the value of the sense resistor.
+        */
+       ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP);
+       if (ret < 0)
+               return ret;
+
+       if (sense_res_raw == 0) {
+               dev_err(dev_info->dev, "sense resistor value is 0\n");
+               return -EINVAL;
+       }
+       sense_res = 1000 / sense_res_raw;
+
+       /*
+        * The ACR value is located in 16 bits across the ACR MSB and
+        * LSB registers
+        * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR
+        * MSB register
+        * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR
+        * LSB register
+        */
+       ret = ds2781_read16(dev_info, &current_raw, DS2781_ACR_MSB);
+       if (ret < 0)
+               return ret;
+
+       *accumulated_current = current_raw * (DS2781_CHARGE_UNITS / sense_res);
+       return 0;
+}
+
+static int ds2781_get_capacity(struct ds2781_device_info *dev_info,
+       int *capacity)
+{
+       int ret;
+       u8 raw;
+
+       ret = ds2781_read8(dev_info, &raw, DS2781_RARC);
+       if (ret < 0)
+               return ret;
+
+       *capacity = raw;
+       return 0;
+}
+
+static int ds2781_get_status(struct ds2781_device_info *dev_info, int *status)
+{
+       int ret, current_uA, capacity;
+
+       ret = ds2781_get_current(dev_info, CURRENT_NOW, &current_uA);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_get_capacity(dev_info, &capacity);
+       if (ret < 0)
+               return ret;
+
+       if (power_supply_am_i_supplied(dev_info->bat)) {
+               if (capacity == 100)
+                       *status = POWER_SUPPLY_STATUS_FULL;
+               else if (current_uA > 50000)
+                       *status = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               *status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+       return 0;
+}
+
+static int ds2781_get_charge_now(struct ds2781_device_info *dev_info,
+       int *charge_now)
+{
+       int ret;
+       u16 charge_raw;
+
+       /*
+        * The RAAC value is located in 16 bits across the RAAC MSB and
+        * LSB registers
+        * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC
+        * MSB register
+        * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC
+        * LSB register
+        */
+       ret = ds2781_read16(dev_info, &charge_raw, DS2781_RAAC_MSB);
+       if (ret < 0)
+               return ret;
+
+       *charge_now = charge_raw * 1600;
+       return 0;
+}
+
+static int ds2781_get_control_register(struct ds2781_device_info *dev_info,
+       u8 *control_reg)
+{
+       return ds2781_read8(dev_info, control_reg, DS2781_CONTROL);
+}
+
+static int ds2781_set_control_register(struct ds2781_device_info *dev_info,
+       u8 control_reg)
+{
+       int ret;
+
+       ret = ds2781_write(dev_info, &control_reg,
+                               DS2781_CONTROL, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return ds2781_save_eeprom(dev_info, DS2781_CONTROL);
+}
+
+static int ds2781_battery_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       int ret = 0;
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = ds2781_get_voltage(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = ds2781_get_temperature(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = model;
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = manufacturer;
+               break;
+
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = ds2781_get_current(dev_info, CURRENT_NOW, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               ret = ds2781_get_current(dev_info, CURRENT_AVG, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = ds2781_get_status(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = ds2781_get_capacity(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+               ret = ds2781_get_accumulated_current(dev_info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               ret = ds2781_get_charge_now(dev_info, &val->intval);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property ds2781_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+};
+
+static ssize_t ds2781_get_pmod_enabled(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 control_reg;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       /* Get power mode */
+       ret = ds2781_get_control_register(dev_info, &control_reg);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n",
+                !!(control_reg & DS2781_CONTROL_PMOD));
+}
+
+static ssize_t ds2781_set_pmod_enabled(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 control_reg, new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       /* Set power mode */
+       ret = ds2781_get_control_register(dev_info, &control_reg);
+       if (ret < 0)
+               return ret;
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       if ((new_setting != 0) && (new_setting != 1)) {
+               dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n");
+               return -EINVAL;
+       }
+
+       if (new_setting)
+               control_reg |= DS2781_CONTROL_PMOD;
+       else
+               control_reg &= ~DS2781_CONTROL_PMOD;
+
+       ret = ds2781_set_control_register(dev_info, control_reg);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2781_get_sense_resistor_value(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 sense_resistor;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = ds2781_read8(dev_info, &sense_resistor, DS2781_RSNSP);
+       if (ret < 0)
+               return ret;
+
+       ret = sprintf(buf, "%d\n", sense_resistor);
+       return ret;
+}
+
+static ssize_t ds2781_set_sense_resistor_value(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_set_sense_register(dev_info, new_setting);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2781_get_rsgain_setting(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u16 rsgain;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = ds2781_get_rsgain_register(dev_info, &rsgain);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n", rsgain);
+}
+
+static ssize_t ds2781_set_rsgain_setting(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u16 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = kstrtou16(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       /* Gain can only be from 0 to 1.999 in steps of .001 */
+       if (new_setting > 1999) {
+               dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n");
+               return -EINVAL;
+       }
+
+       ret = ds2781_set_rsgain_register(dev_info, new_setting);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2781_get_pio_pin(struct device *dev,
+       struct device_attribute *attr,
+       char *buf)
+{
+       int ret;
+       u8 sfr;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = ds2781_read8(dev_info, &sfr, DS2781_SFR);
+       if (ret < 0)
+               return ret;
+
+       ret = sprintf(buf, "%d\n", sfr & DS2781_SFR_PIOSC);
+       return ret;
+}
+
+static ssize_t ds2781_set_pio_pin(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf,
+       size_t count)
+{
+       int ret;
+       u8 new_setting;
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       ret = kstrtou8(buf, 0, &new_setting);
+       if (ret < 0)
+               return ret;
+
+       if ((new_setting != 0) && (new_setting != 1)) {
+               dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n");
+               return -EINVAL;
+       }
+
+       ret = ds2781_write(dev_info, &new_setting,
+                               DS2781_SFR, sizeof(u8));
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t ds2781_read_param_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       return ds2781_read_block(dev_info, buf,
+                               DS2781_EEPROM_BLOCK1_START + off, count);
+}
+
+static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+       int ret;
+
+       ret = ds2781_write(dev_info, buf,
+                               DS2781_EEPROM_BLOCK1_START + off, count);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK1_START);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static struct bin_attribute ds2781_param_eeprom_bin_attr = {
+       .attr = {
+               .name = "param_eeprom",
+               .mode = S_IRUGO | S_IWUSR,
+       },
+       .size = DS2781_PARAM_EEPROM_SIZE,
+       .read = ds2781_read_param_eeprom_bin,
+       .write = ds2781_write_param_eeprom_bin,
+};
+
+static ssize_t ds2781_read_user_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+
+       return ds2781_read_block(dev_info, buf,
+                               DS2781_EEPROM_BLOCK0_START + off, count);
+
+}
+
+static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
+                               struct kobject *kobj,
+                               struct bin_attribute *bin_attr,
+                               char *buf, loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = to_power_supply(dev);
+       struct ds2781_device_info *dev_info = to_ds2781_device_info(psy);
+       int ret;
+
+       ret = ds2781_write(dev_info, buf,
+                               DS2781_EEPROM_BLOCK0_START + off, count);
+       if (ret < 0)
+               return ret;
+
+       ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK0_START);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static struct bin_attribute ds2781_user_eeprom_bin_attr = {
+       .attr = {
+               .name = "user_eeprom",
+               .mode = S_IRUGO | S_IWUSR,
+       },
+       .size = DS2781_USER_EEPROM_SIZE,
+       .read = ds2781_read_user_eeprom_bin,
+       .write = ds2781_write_user_eeprom_bin,
+};
+
+static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled,
+       ds2781_set_pmod_enabled);
+static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR,
+       ds2781_get_sense_resistor_value, ds2781_set_sense_resistor_value);
+static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2781_get_rsgain_setting,
+       ds2781_set_rsgain_setting);
+static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2781_get_pio_pin,
+       ds2781_set_pio_pin);
+
+
+static struct attribute *ds2781_attributes[] = {
+       &dev_attr_pmod_enabled.attr,
+       &dev_attr_sense_resistor_value.attr,
+       &dev_attr_rsgain_setting.attr,
+       &dev_attr_pio_pin.attr,
+       NULL
+};
+
+static const struct attribute_group ds2781_attr_group = {
+       .attrs = ds2781_attributes,
+};
+
+static int ds2781_battery_probe(struct platform_device *pdev)
+{
+       struct power_supply_config psy_cfg = {};
+       int ret = 0;
+       struct ds2781_device_info *dev_info;
+
+       dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
+       if (!dev_info)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, dev_info);
+
+       dev_info->dev                   = &pdev->dev;
+       dev_info->w1_dev                = pdev->dev.parent;
+       dev_info->bat_desc.name         = dev_name(&pdev->dev);
+       dev_info->bat_desc.type         = POWER_SUPPLY_TYPE_BATTERY;
+       dev_info->bat_desc.properties   = ds2781_battery_props;
+       dev_info->bat_desc.num_properties = ARRAY_SIZE(ds2781_battery_props);
+       dev_info->bat_desc.get_property = ds2781_battery_get_property;
+
+       psy_cfg.drv_data                = dev_info;
+
+       dev_info->bat = power_supply_register(&pdev->dev, &dev_info->bat_desc,
+                                               &psy_cfg);
+       if (IS_ERR(dev_info->bat)) {
+               dev_err(dev_info->dev, "failed to register battery\n");
+               ret = PTR_ERR(dev_info->bat);
+               goto fail;
+       }
+
+       ret = sysfs_create_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
+       if (ret) {
+               dev_err(dev_info->dev, "failed to create sysfs group\n");
+               goto fail_unregister;
+       }
+
+       ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
+                                       &ds2781_param_eeprom_bin_attr);
+       if (ret) {
+               dev_err(dev_info->dev,
+                               "failed to create param eeprom bin file");
+               goto fail_remove_group;
+       }
+
+       ret = sysfs_create_bin_file(&dev_info->bat->dev.kobj,
+                                       &ds2781_user_eeprom_bin_attr);
+       if (ret) {
+               dev_err(dev_info->dev,
+                               "failed to create user eeprom bin file");
+               goto fail_remove_bin_file;
+       }
+
+       return 0;
+
+fail_remove_bin_file:
+       sysfs_remove_bin_file(&dev_info->bat->dev.kobj,
+                               &ds2781_param_eeprom_bin_attr);
+fail_remove_group:
+       sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
+fail_unregister:
+       power_supply_unregister(dev_info->bat);
+fail:
+       return ret;
+}
+
+static int ds2781_battery_remove(struct platform_device *pdev)
+{
+       struct ds2781_device_info *dev_info = platform_get_drvdata(pdev);
+
+       /*
+        * Remove attributes before unregistering power supply
+        * because 'bat' will be freed on power_supply_unregister() call.
+        */
+       sysfs_remove_group(&dev_info->bat->dev.kobj, &ds2781_attr_group);
+
+       power_supply_unregister(dev_info->bat);
+
+       return 0;
+}
+
+static struct platform_driver ds2781_battery_driver = {
+       .driver = {
+               .name = "ds2781-battery",
+       },
+       .probe    = ds2781_battery_probe,
+       .remove   = ds2781_battery_remove,
+};
+module_platform_driver(ds2781_battery_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Renata Sayakhova <renata@oktetlabs.ru>");
+MODULE_DESCRIPTION("Maxim/Dallas DS2781 Stand-Alone Fuel Gauage IC driver");
+MODULE_ALIAS("platform:ds2781-battery");
+
diff --git a/drivers/power/supply/ds2782_battery.c b/drivers/power/supply/ds2782_battery.c
new file mode 100644 (file)
index 0000000..a1b7e05
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC
+ *
+ * Copyright (C) 2009 Bluewater Systems Ltd
+ *
+ * Author: Ryan Mallon
+ *
+ * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
+ *
+ * UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/swab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/idr.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/ds2782_battery.h>
+
+#define DS2782_REG_RARC                0x06    /* Remaining active relative capacity */
+
+#define DS278x_REG_VOLT_MSB    0x0c
+#define DS278x_REG_TEMP_MSB    0x0a
+#define DS278x_REG_CURRENT_MSB 0x0e
+
+/* EEPROM Block */
+#define DS2782_REG_RSNSP       0x69    /* Sense resistor value */
+
+/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
+#define DS2782_CURRENT_UNITS   1563
+
+#define DS2786_REG_RARC                0x02    /* Remaining active relative capacity */
+
+#define DS2786_CURRENT_UNITS   25
+
+#define DS278x_DELAY           1000
+
+struct ds278x_info;
+
+struct ds278x_battery_ops {
+       int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
+       int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV);
+       int (*get_battery_capacity)(struct ds278x_info *info, int *capacity);
+};
+
+#define to_ds278x_info(x) power_supply_get_drvdata(x)
+
+struct ds278x_info {
+       struct i2c_client       *client;
+       struct power_supply     *battery;
+       struct power_supply_desc        battery_desc;
+       const struct ds278x_battery_ops *ops;
+       struct delayed_work     bat_work;
+       int                     id;
+       int                     rsns;
+       int                     capacity;
+       int                     status;         /* State Of Charge */
+};
+
+static DEFINE_IDR(battery_id);
+static DEFINE_MUTEX(battery_lock);
+
+static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(info->client, reg);
+       if (ret < 0) {
+               dev_err(&info->client->dev, "register read failed\n");
+               return ret;
+       }
+
+       *val = ret;
+       return 0;
+}
+
+static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
+                                   s16 *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_word_data(info->client, reg_msb);
+       if (ret < 0) {
+               dev_err(&info->client->dev, "register read failed\n");
+               return ret;
+       }
+
+       *val = swab16(ret);
+       return 0;
+}
+
+static int ds278x_get_temp(struct ds278x_info *info, int *temp)
+{
+       s16 raw;
+       int err;
+
+       /*
+        * Temperature is measured in units of 0.125 degrees celcius, the
+        * power_supply class measures temperature in tenths of degrees
+        * celsius. The temperature value is stored as a 10 bit number, plus
+        * sign in the upper bits of a 16 bit register.
+        */
+       err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
+       if (err)
+               return err;
+       *temp = ((raw / 32) * 125) / 100;
+       return 0;
+}
+
+static int ds2782_get_current(struct ds278x_info *info, int *current_uA)
+{
+       int sense_res;
+       int err;
+       u8 sense_res_raw;
+       s16 raw;
+
+       /*
+        * The units of measurement for current are dependent on the value of
+        * the sense resistor.
+        */
+       err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
+       if (err)
+               return err;
+       if (sense_res_raw == 0) {
+               dev_err(&info->client->dev, "sense resistor value is 0\n");
+               return -ENXIO;
+       }
+       sense_res = 1000 / sense_res_raw;
+
+       dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n",
+               sense_res);
+       err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
+       if (err)
+               return err;
+       *current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
+       return 0;
+}
+
+static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV)
+{
+       s16 raw;
+       int err;
+
+       /*
+        * Voltage is measured in units of 4.88mV. The voltage is stored as
+        * a 10-bit number plus sign, in the upper bits of a 16-bit register
+        */
+       err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
+       if (err)
+               return err;
+       *voltage_uV = (raw / 32) * 4800;
+       return 0;
+}
+
+static int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
+{
+       int err;
+       u8 raw;
+
+       err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
+       if (err)
+               return err;
+       *capacity = raw;
+       return 0;
+}
+
+static int ds2786_get_current(struct ds278x_info *info, int *current_uA)
+{
+       int err;
+       s16 raw;
+
+       err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
+       if (err)
+               return err;
+       *current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
+       return 0;
+}
+
+static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV)
+{
+       s16 raw;
+       int err;
+
+       /*
+        * Voltage is measured in units of 1.22mV. The voltage is stored as
+        * a 12-bit number plus sign, in the upper bits of a 16-bit register
+        */
+       err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
+       if (err)
+               return err;
+       *voltage_uV = (raw / 8) * 1220;
+       return 0;
+}
+
+static int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
+{
+       int err;
+       u8 raw;
+
+       err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
+       if (err)
+               return err;
+       /* Relative capacity is displayed with resolution 0.5 % */
+       *capacity = raw/2 ;
+       return 0;
+}
+
+static int ds278x_get_status(struct ds278x_info *info, int *status)
+{
+       int err;
+       int current_uA;
+       int capacity;
+
+       err = info->ops->get_battery_current(info, &current_uA);
+       if (err)
+               return err;
+
+       err = info->ops->get_battery_capacity(info, &capacity);
+       if (err)
+               return err;
+
+       info->capacity = capacity;
+
+       if (capacity == 100)
+               *status = POWER_SUPPLY_STATUS_FULL;
+       else if (current_uA == 0)
+               *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       else if (current_uA < 0)
+               *status = POWER_SUPPLY_STATUS_DISCHARGING;
+       else
+               *status = POWER_SUPPLY_STATUS_CHARGING;
+
+       return 0;
+}
+
+static int ds278x_battery_get_property(struct power_supply *psy,
+                                      enum power_supply_property prop,
+                                      union power_supply_propval *val)
+{
+       struct ds278x_info *info = to_ds278x_info(psy);
+       int ret;
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = ds278x_get_status(info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = info->ops->get_battery_capacity(info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = info->ops->get_battery_voltage(info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = info->ops->get_battery_current(info, &val->intval);
+               break;
+
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = ds278x_get_temp(info, &val->intval);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static void ds278x_bat_update(struct ds278x_info *info)
+{
+       int old_status = info->status;
+       int old_capacity = info->capacity;
+
+       ds278x_get_status(info, &info->status);
+
+       if ((old_status != info->status) || (old_capacity != info->capacity))
+               power_supply_changed(info->battery);
+}
+
+static void ds278x_bat_work(struct work_struct *work)
+{
+       struct ds278x_info *info;
+
+       info = container_of(work, struct ds278x_info, bat_work.work);
+       ds278x_bat_update(info);
+
+       schedule_delayed_work(&info->bat_work, DS278x_DELAY);
+}
+
+static enum power_supply_property ds278x_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static void ds278x_power_supply_init(struct power_supply_desc *battery)
+{
+       battery->type                   = POWER_SUPPLY_TYPE_BATTERY;
+       battery->properties             = ds278x_battery_props;
+       battery->num_properties         = ARRAY_SIZE(ds278x_battery_props);
+       battery->get_property           = ds278x_battery_get_property;
+       battery->external_power_changed = NULL;
+}
+
+static int ds278x_battery_remove(struct i2c_client *client)
+{
+       struct ds278x_info *info = i2c_get_clientdata(client);
+
+       power_supply_unregister(info->battery);
+       kfree(info->battery_desc.name);
+
+       mutex_lock(&battery_lock);
+       idr_remove(&battery_id, info->id);
+       mutex_unlock(&battery_lock);
+
+       cancel_delayed_work(&info->bat_work);
+
+       kfree(info);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int ds278x_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ds278x_info *info = i2c_get_clientdata(client);
+
+       cancel_delayed_work(&info->bat_work);
+       return 0;
+}
+
+static int ds278x_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ds278x_info *info = i2c_get_clientdata(client);
+
+       schedule_delayed_work(&info->bat_work, DS278x_DELAY);
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(ds278x_battery_pm_ops, ds278x_suspend, ds278x_resume);
+
+enum ds278x_num_id {
+       DS2782 = 0,
+       DS2786,
+};
+
+static const struct ds278x_battery_ops ds278x_ops[] = {
+       [DS2782] = {
+               .get_battery_current  = ds2782_get_current,
+               .get_battery_voltage  = ds2782_get_voltage,
+               .get_battery_capacity = ds2782_get_capacity,
+       },
+       [DS2786] = {
+               .get_battery_current  = ds2786_get_current,
+               .get_battery_voltage  = ds2786_get_voltage,
+               .get_battery_capacity = ds2786_get_capacity,
+       }
+};
+
+static int ds278x_battery_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct ds278x_platform_data *pdata = client->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       struct ds278x_info *info;
+       int ret;
+       int num;
+
+       /*
+        * ds2786 should have the sense resistor value set
+        * in the platform data
+        */
+       if (id->driver_data == DS2786 && !pdata) {
+               dev_err(&client->dev, "missing platform data for ds2786\n");
+               return -EINVAL;
+       }
+
+       /* Get an ID for this battery */
+       mutex_lock(&battery_lock);
+       ret = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
+       mutex_unlock(&battery_lock);
+       if (ret < 0)
+               goto fail_id;
+       num = ret;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               ret = -ENOMEM;
+               goto fail_info;
+       }
+
+       info->battery_desc.name = kasprintf(GFP_KERNEL, "%s-%d",
+                                           client->name, num);
+       if (!info->battery_desc.name) {
+               ret = -ENOMEM;
+               goto fail_name;
+       }
+
+       if (id->driver_data == DS2786)
+               info->rsns = pdata->rsns;
+
+       i2c_set_clientdata(client, info);
+       info->client = client;
+       info->id = num;
+       info->ops  = &ds278x_ops[id->driver_data];
+       ds278x_power_supply_init(&info->battery_desc);
+       psy_cfg.drv_data = info;
+
+       info->capacity = 100;
+       info->status = POWER_SUPPLY_STATUS_FULL;
+
+       INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work);
+
+       info->battery = power_supply_register(&client->dev,
+                                             &info->battery_desc, &psy_cfg);
+       if (IS_ERR(info->battery)) {
+               dev_err(&client->dev, "failed to register battery\n");
+               ret = PTR_ERR(info->battery);
+               goto fail_register;
+       } else {
+               schedule_delayed_work(&info->bat_work, DS278x_DELAY);
+       }
+
+       return 0;
+
+fail_register:
+       kfree(info->battery_desc.name);
+fail_name:
+       kfree(info);
+fail_info:
+       mutex_lock(&battery_lock);
+       idr_remove(&battery_id, num);
+       mutex_unlock(&battery_lock);
+fail_id:
+       return ret;
+}
+
+static const struct i2c_device_id ds278x_id[] = {
+       {"ds2782", DS2782},
+       {"ds2786", DS2786},
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, ds278x_id);
+
+static struct i2c_driver ds278x_battery_driver = {
+       .driver         = {
+               .name   = "ds2782-battery",
+               .pm     = &ds278x_battery_pm_ops,
+       },
+       .probe          = ds278x_battery_probe,
+       .remove         = ds278x_battery_remove,
+       .id_table       = ds278x_id,
+};
+module_i2c_driver(ds278x_battery_driver);
+
+MODULE_AUTHOR("Ryan Mallon");
+MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c
new file mode 100644 (file)
index 0000000..edb36bf
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * Generic battery driver code using IIO
+ * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com>
+ * based on jz4740-battery.c
+ * based on s3c_adc_battery.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/power/generic-adc-battery.h>
+
+#define JITTER_DEFAULT 10 /* hope 10ms is enough */
+
+enum gab_chan_type {
+       GAB_VOLTAGE = 0,
+       GAB_CURRENT,
+       GAB_POWER,
+       GAB_MAX_CHAN_TYPE
+};
+
+/*
+ * gab_chan_name suggests the standard channel names for commonly used
+ * channel types.
+ */
+static const char *const gab_chan_name[] = {
+       [GAB_VOLTAGE]   = "voltage",
+       [GAB_CURRENT]   = "current",
+       [GAB_POWER]             = "power",
+};
+
+struct gab {
+       struct power_supply             *psy;
+       struct power_supply_desc        psy_desc;
+       struct iio_channel      *channel[GAB_MAX_CHAN_TYPE];
+       struct gab_platform_data        *pdata;
+       struct delayed_work bat_work;
+       int     level;
+       int     status;
+       bool cable_plugged;
+};
+
+static struct gab *to_generic_bat(struct power_supply *psy)
+{
+       return power_supply_get_drvdata(psy);
+}
+
+static void gab_ext_power_changed(struct power_supply *psy)
+{
+       struct gab *adc_bat = to_generic_bat(psy);
+
+       schedule_delayed_work(&adc_bat->bat_work, msecs_to_jiffies(0));
+}
+
+static const enum power_supply_property gab_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+/*
+ * This properties are set based on the received platform data and this
+ * should correspond one-to-one with enum chan_type.
+ */
+static const enum power_supply_property gab_dyn_props[] = {
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_POWER_NOW,
+};
+
+static bool gab_charge_finished(struct gab *adc_bat)
+{
+       struct gab_platform_data *pdata = adc_bat->pdata;
+       bool ret = gpio_get_value(pdata->gpio_charge_finished);
+       bool inv = pdata->gpio_inverted;
+
+       if (!gpio_is_valid(pdata->gpio_charge_finished))
+               return false;
+       return ret ^ inv;
+}
+
+static int gab_get_status(struct gab *adc_bat)
+{
+       struct gab_platform_data *pdata = adc_bat->pdata;
+       struct power_supply_info *bat_info;
+
+       bat_info = &pdata->battery_info;
+       if (adc_bat->level == bat_info->charge_full_design)
+               return POWER_SUPPLY_STATUS_FULL;
+       return adc_bat->status;
+}
+
+static enum gab_chan_type gab_prop_to_chan(enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_POWER_NOW:
+               return GAB_POWER;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               return GAB_VOLTAGE;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               return GAB_CURRENT;
+       default:
+               WARN_ON(1);
+               break;
+       }
+       return GAB_POWER;
+}
+
+static int read_channel(struct gab *adc_bat, enum power_supply_property psp,
+               int *result)
+{
+       int ret;
+       int chan_index;
+
+       chan_index = gab_prop_to_chan(psp);
+       ret = iio_read_channel_processed(adc_bat->channel[chan_index],
+                       result);
+       if (ret < 0)
+               pr_err("read channel error\n");
+       return ret;
+}
+
+static int gab_get_property(struct power_supply *psy,
+               enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct gab *adc_bat;
+       struct gab_platform_data *pdata;
+       struct power_supply_info *bat_info;
+       int result = 0;
+       int ret = 0;
+
+       adc_bat = to_generic_bat(psy);
+       if (!adc_bat) {
+               dev_err(&psy->dev, "no battery infos ?!\n");
+               return -EINVAL;
+       }
+       pdata = adc_bat->pdata;
+       bat_info = &pdata->battery_info;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = gab_get_status(adc_bat);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
+               val->intval = 0;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               val->intval = pdata->cal_charge(result);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+       case POWER_SUPPLY_PROP_POWER_NOW:
+               ret = read_channel(adc_bat, psp, &result);
+               if (ret < 0)
+                       goto err;
+               val->intval = result;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = bat_info->technology;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = bat_info->voltage_min_design;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = bat_info->voltage_max_design;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = bat_info->charge_full_design;
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = bat_info->name;
+               break;
+       default:
+               return -EINVAL;
+       }
+err:
+       return ret;
+}
+
+static void gab_work(struct work_struct *work)
+{
+       struct gab *adc_bat;
+       struct gab_platform_data *pdata;
+       struct delayed_work *delayed_work;
+       bool is_plugged;
+       int status;
+
+       delayed_work = to_delayed_work(work);
+       adc_bat = container_of(delayed_work, struct gab, bat_work);
+       pdata = adc_bat->pdata;
+       status = adc_bat->status;
+
+       is_plugged = power_supply_am_i_supplied(adc_bat->psy);
+       adc_bat->cable_plugged = is_plugged;
+
+       if (!is_plugged)
+               adc_bat->status =  POWER_SUPPLY_STATUS_DISCHARGING;
+       else if (gab_charge_finished(adc_bat))
+               adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       else
+               adc_bat->status = POWER_SUPPLY_STATUS_CHARGING;
+
+       if (status != adc_bat->status)
+               power_supply_changed(adc_bat->psy);
+}
+
+static irqreturn_t gab_charged(int irq, void *dev_id)
+{
+       struct gab *adc_bat = dev_id;
+       struct gab_platform_data *pdata = adc_bat->pdata;
+       int delay;
+
+       delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
+       schedule_delayed_work(&adc_bat->bat_work,
+                       msecs_to_jiffies(delay));
+       return IRQ_HANDLED;
+}
+
+static int gab_probe(struct platform_device *pdev)
+{
+       struct gab *adc_bat;
+       struct power_supply_desc *psy_desc;
+       struct power_supply_config psy_cfg = {};
+       struct gab_platform_data *pdata = pdev->dev.platform_data;
+       enum power_supply_property *properties;
+       int ret = 0;
+       int chan;
+       int index = 0;
+
+       adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL);
+       if (!adc_bat) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       psy_cfg.drv_data = adc_bat;
+       psy_desc = &adc_bat->psy_desc;
+       psy_desc->name = pdata->battery_info.name;
+
+       /* bootup default values for the battery */
+       adc_bat->cable_plugged = false;
+       adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
+       psy_desc->get_property = gab_get_property;
+       psy_desc->external_power_changed = gab_ext_power_changed;
+       adc_bat->pdata = pdata;
+
+       /*
+        * copying the static properties and allocating extra memory for holding
+        * the extra configurable properties received from platform data.
+        */
+       psy_desc->properties = kcalloc(ARRAY_SIZE(gab_props) +
+                                       ARRAY_SIZE(gab_chan_name),
+                                       sizeof(*psy_desc->properties),
+                                       GFP_KERNEL);
+       if (!psy_desc->properties) {
+               ret = -ENOMEM;
+               goto first_mem_fail;
+       }
+
+       memcpy(psy_desc->properties, gab_props, sizeof(gab_props));
+       properties = (enum power_supply_property *)
+                       ((char *)psy_desc->properties + sizeof(gab_props));
+
+       /*
+        * getting channel from iio and copying the battery properties
+        * based on the channel supported by consumer device.
+        */
+       for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
+               adc_bat->channel[chan] = iio_channel_get(&pdev->dev,
+                                                        gab_chan_name[chan]);
+               if (IS_ERR(adc_bat->channel[chan])) {
+                       ret = PTR_ERR(adc_bat->channel[chan]);
+                       adc_bat->channel[chan] = NULL;
+               } else {
+                       /* copying properties for supported channels only */
+                       memcpy(properties + sizeof(*(psy_desc->properties)) * index,
+                                       &gab_dyn_props[chan],
+                                       sizeof(gab_dyn_props[chan]));
+                       index++;
+               }
+       }
+
+       /* none of the channels are supported so let's bail out */
+       if (index == 0) {
+               ret = -ENODEV;
+               goto second_mem_fail;
+       }
+
+       /*
+        * Total number of properties is equal to static properties
+        * plus the dynamic properties.Some properties may not be set
+        * as come channels may be not be supported by the device.So
+        * we need to take care of that.
+        */
+       psy_desc->num_properties = ARRAY_SIZE(gab_props) + index;
+
+       adc_bat->psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
+       if (IS_ERR(adc_bat->psy)) {
+               ret = PTR_ERR(adc_bat->psy);
+               goto err_reg_fail;
+       }
+
+       INIT_DELAYED_WORK(&adc_bat->bat_work, gab_work);
+
+       if (gpio_is_valid(pdata->gpio_charge_finished)) {
+               int irq;
+               ret = gpio_request(pdata->gpio_charge_finished, "charged");
+               if (ret)
+                       goto gpio_req_fail;
+
+               irq = gpio_to_irq(pdata->gpio_charge_finished);
+               ret = request_any_context_irq(irq, gab_charged,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               "battery charged", adc_bat);
+               if (ret < 0)
+                       goto err_gpio;
+       }
+
+       platform_set_drvdata(pdev, adc_bat);
+
+       /* Schedule timer to check current status */
+       schedule_delayed_work(&adc_bat->bat_work,
+                       msecs_to_jiffies(0));
+       return 0;
+
+err_gpio:
+       gpio_free(pdata->gpio_charge_finished);
+gpio_req_fail:
+       power_supply_unregister(adc_bat->psy);
+err_reg_fail:
+       for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
+               if (adc_bat->channel[chan])
+                       iio_channel_release(adc_bat->channel[chan]);
+       }
+second_mem_fail:
+       kfree(psy_desc->properties);
+first_mem_fail:
+       return ret;
+}
+
+static int gab_remove(struct platform_device *pdev)
+{
+       int chan;
+       struct gab *adc_bat = platform_get_drvdata(pdev);
+       struct gab_platform_data *pdata = adc_bat->pdata;
+
+       power_supply_unregister(adc_bat->psy);
+
+       if (gpio_is_valid(pdata->gpio_charge_finished)) {
+               free_irq(gpio_to_irq(pdata->gpio_charge_finished), adc_bat);
+               gpio_free(pdata->gpio_charge_finished);
+       }
+
+       for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
+               if (adc_bat->channel[chan])
+                       iio_channel_release(adc_bat->channel[chan]);
+       }
+
+       kfree(adc_bat->psy_desc.properties);
+       cancel_delayed_work(&adc_bat->bat_work);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int gab_suspend(struct device *dev)
+{
+       struct gab *adc_bat = dev_get_drvdata(dev);
+
+       cancel_delayed_work_sync(&adc_bat->bat_work);
+       adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
+       return 0;
+}
+
+static int gab_resume(struct device *dev)
+{
+       struct gab *adc_bat = dev_get_drvdata(dev);
+       struct gab_platform_data *pdata = adc_bat->pdata;
+       int delay;
+
+       delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
+
+       /* Schedule timer to check current status */
+       schedule_delayed_work(&adc_bat->bat_work,
+                       msecs_to_jiffies(delay));
+       return 0;
+}
+
+static const struct dev_pm_ops gab_pm_ops = {
+       .suspend        = gab_suspend,
+       .resume         = gab_resume,
+};
+
+#define GAB_PM_OPS       (&gab_pm_ops)
+#else
+#define GAB_PM_OPS       (NULL)
+#endif
+
+static struct platform_driver gab_driver = {
+       .driver         = {
+               .name   = "generic-adc-battery",
+               .pm     = GAB_PM_OPS
+       },
+       .probe          = gab_probe,
+       .remove         = gab_remove,
+};
+module_platform_driver(gab_driver);
+
+MODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>");
+MODULE_DESCRIPTION("generic battery driver using IIO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c
new file mode 100644 (file)
index 0000000..f5c525e
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Power supply driver for the goldfish emulator
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ * Copyright (C) 2013 Intel, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+
+struct goldfish_battery_data {
+       void __iomem *reg_base;
+       int irq;
+       spinlock_t lock;
+
+       struct power_supply *battery;
+       struct power_supply *ac;
+};
+
+#define GOLDFISH_BATTERY_READ(data, addr) \
+       (readl(data->reg_base + addr))
+#define GOLDFISH_BATTERY_WRITE(data, addr, x) \
+       (writel(x, data->reg_base + addr))
+
+/*
+ * Temporary variable used between goldfish_battery_probe() and
+ * goldfish_battery_open().
+ */
+static struct goldfish_battery_data *battery_data;
+
+enum {
+       /* status register */
+       BATTERY_INT_STATUS          = 0x00,
+       /* set this to enable IRQ */
+       BATTERY_INT_ENABLE          = 0x04,
+
+       BATTERY_AC_ONLINE       = 0x08,
+       BATTERY_STATUS          = 0x0C,
+       BATTERY_HEALTH          = 0x10,
+       BATTERY_PRESENT         = 0x14,
+       BATTERY_CAPACITY        = 0x18,
+
+       BATTERY_STATUS_CHANGED  = 1U << 0,
+       AC_STATUS_CHANGED       = 1U << 1,
+       BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
+};
+
+
+static int goldfish_ac_get_property(struct power_supply *psy,
+                       enum power_supply_property psp,
+                       union power_supply_propval *val)
+{
+       struct goldfish_battery_data *data = power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int goldfish_battery_get_property(struct power_supply *psy,
+                                enum power_supply_property psp,
+                                union power_supply_propval *val)
+{
+       struct goldfish_battery_data *data = power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property goldfish_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property goldfish_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
+{
+       unsigned long irq_flags;
+       struct goldfish_battery_data *data = dev_id;
+       uint32_t status;
+
+       spin_lock_irqsave(&data->lock, irq_flags);
+
+       /* read status flags, which will clear the interrupt */
+       status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
+       status &= BATTERY_INT_MASK;
+
+       if (status & BATTERY_STATUS_CHANGED)
+               power_supply_changed(data->battery);
+       if (status & AC_STATUS_CHANGED)
+               power_supply_changed(data->ac);
+
+       spin_unlock_irqrestore(&data->lock, irq_flags);
+       return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static const struct power_supply_desc battery_desc = {
+       .properties     = goldfish_battery_props,
+       .num_properties = ARRAY_SIZE(goldfish_battery_props),
+       .get_property   = goldfish_battery_get_property,
+       .name           = "battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+};
+
+static const struct power_supply_desc ac_desc = {
+       .properties     = goldfish_ac_props,
+       .num_properties = ARRAY_SIZE(goldfish_ac_props),
+       .get_property   = goldfish_ac_get_property,
+       .name           = "ac",
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+};
+
+static int goldfish_battery_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct resource *r;
+       struct goldfish_battery_data *data;
+       struct power_supply_config psy_cfg = {};
+
+       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       spin_lock_init(&data->lock);
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (r == NULL) {
+               dev_err(&pdev->dev, "platform_get_resource failed\n");
+               return -ENODEV;
+       }
+
+       data->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+       if (data->reg_base == NULL) {
+               dev_err(&pdev->dev, "unable to remap MMIO\n");
+               return -ENOMEM;
+       }
+
+       data->irq = platform_get_irq(pdev, 0);
+       if (data->irq < 0) {
+               dev_err(&pdev->dev, "platform_get_irq failed\n");
+               return -ENODEV;
+       }
+
+       ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt,
+                                               IRQF_SHARED, pdev->name, data);
+       if (ret)
+               return ret;
+
+       psy_cfg.drv_data = data;
+
+       data->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
+       if (IS_ERR(data->ac))
+               return PTR_ERR(data->ac);
+
+       data->battery = power_supply_register(&pdev->dev, &battery_desc,
+                                               &psy_cfg);
+       if (IS_ERR(data->battery)) {
+               power_supply_unregister(data->ac);
+               return PTR_ERR(data->battery);
+       }
+
+       platform_set_drvdata(pdev, data);
+       battery_data = data;
+
+       GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
+       return 0;
+}
+
+static int goldfish_battery_remove(struct platform_device *pdev)
+{
+       struct goldfish_battery_data *data = platform_get_drvdata(pdev);
+
+       power_supply_unregister(data->battery);
+       power_supply_unregister(data->ac);
+       battery_data = NULL;
+       return 0;
+}
+
+static const struct of_device_id goldfish_battery_of_match[] = {
+       { .compatible = "google,goldfish-battery", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_battery_of_match);
+
+static const struct acpi_device_id goldfish_battery_acpi_match[] = {
+       { "GFSH0001", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match);
+
+static struct platform_driver goldfish_battery_device = {
+       .probe          = goldfish_battery_probe,
+       .remove         = goldfish_battery_remove,
+       .driver = {
+               .name = "goldfish-battery",
+               .of_match_table = goldfish_battery_of_match,
+               .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match),
+       }
+};
+module_platform_driver(goldfish_battery_device);
+
+MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");
diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c
new file mode 100644 (file)
index 0000000..c5869b1
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ *  Driver for chargers which report their online status through a GPIO pin
+ *
+ *  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;  either version 2 of the License, or (at your
+ *  option) any later version.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include <linux/power/gpio-charger.h>
+
+struct gpio_charger {
+       const struct gpio_charger_platform_data *pdata;
+       unsigned int irq;
+       bool wakeup_enabled;
+
+       struct power_supply *charger;
+       struct power_supply_desc charger_desc;
+};
+
+static irqreturn_t gpio_charger_irq(int irq, void *devid)
+{
+       struct power_supply *charger = devid;
+
+       power_supply_changed(charger);
+
+       return IRQ_HANDLED;
+}
+
+static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy)
+{
+       return power_supply_get_drvdata(psy);
+}
+
+static int gpio_charger_get_property(struct power_supply *psy,
+               enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
+       const struct gpio_charger_platform_data *pdata = gpio_charger->pdata;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!gpio_get_value_cansleep(pdata->gpio);
+               val->intval ^= pdata->gpio_active_low;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property gpio_charger_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static
+struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct gpio_charger_platform_data *pdata;
+       const char *chargetype;
+       enum of_gpio_flags flags;
+       int ret;
+
+       if (!np)
+               return ERR_PTR(-ENOENT);
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       pdata->name = np->name;
+
+       pdata->gpio = of_get_gpio_flags(np, 0, &flags);
+       if (pdata->gpio < 0) {
+               if (pdata->gpio != -EPROBE_DEFER)
+                       dev_err(dev, "could not get charger gpio\n");
+               return ERR_PTR(pdata->gpio);
+       }
+
+       pdata->gpio_active_low = !!(flags & OF_GPIO_ACTIVE_LOW);
+
+       pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
+       ret = of_property_read_string(np, "charger-type", &chargetype);
+       if (ret >= 0) {
+               if (!strncmp("unknown", chargetype, 7))
+                       pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
+               else if (!strncmp("battery", chargetype, 7))
+                       pdata->type = POWER_SUPPLY_TYPE_BATTERY;
+               else if (!strncmp("ups", chargetype, 3))
+                       pdata->type = POWER_SUPPLY_TYPE_UPS;
+               else if (!strncmp("mains", chargetype, 5))
+                       pdata->type = POWER_SUPPLY_TYPE_MAINS;
+               else if (!strncmp("usb-sdp", chargetype, 7))
+                       pdata->type = POWER_SUPPLY_TYPE_USB;
+               else if (!strncmp("usb-dcp", chargetype, 7))
+                       pdata->type = POWER_SUPPLY_TYPE_USB_DCP;
+               else if (!strncmp("usb-cdp", chargetype, 7))
+                       pdata->type = POWER_SUPPLY_TYPE_USB_CDP;
+               else if (!strncmp("usb-aca", chargetype, 7))
+                       pdata->type = POWER_SUPPLY_TYPE_USB_ACA;
+               else
+                       dev_warn(dev, "unknown charger type %s\n", chargetype);
+       }
+
+       return pdata;
+}
+
+static int gpio_charger_probe(struct platform_device *pdev)
+{
+       const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       struct gpio_charger *gpio_charger;
+       struct power_supply_desc *charger_desc;
+       int ret;
+       int irq;
+
+       if (!pdata) {
+               pdata = gpio_charger_parse_dt(&pdev->dev);
+               if (IS_ERR(pdata)) {
+                       ret = PTR_ERR(pdata);
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(&pdev->dev, "No platform data\n");
+                       return ret;
+               }
+       }
+
+       if (!gpio_is_valid(pdata->gpio)) {
+               dev_err(&pdev->dev, "Invalid gpio pin\n");
+               return -EINVAL;
+       }
+
+       gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
+                                       GFP_KERNEL);
+       if (!gpio_charger) {
+               dev_err(&pdev->dev, "Failed to alloc driver structure\n");
+               return -ENOMEM;
+       }
+
+       charger_desc = &gpio_charger->charger_desc;
+
+       charger_desc->name = pdata->name ? pdata->name : "gpio-charger";
+       charger_desc->type = pdata->type;
+       charger_desc->properties = gpio_charger_properties;
+       charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
+       charger_desc->get_property = gpio_charger_get_property;
+
+       psy_cfg.supplied_to = pdata->supplied_to;
+       psy_cfg.num_supplicants = pdata->num_supplicants;
+       psy_cfg.of_node = pdev->dev.of_node;
+       psy_cfg.drv_data = gpio_charger;
+
+       ret = gpio_request(pdata->gpio, dev_name(&pdev->dev));
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret);
+               goto err_free;
+       }
+       ret = gpio_direction_input(pdata->gpio);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret);
+               goto err_gpio_free;
+       }
+
+       gpio_charger->pdata = pdata;
+
+       gpio_charger->charger = power_supply_register(&pdev->dev,
+                                                     charger_desc, &psy_cfg);
+       if (IS_ERR(gpio_charger->charger)) {
+               ret = PTR_ERR(gpio_charger->charger);
+               dev_err(&pdev->dev, "Failed to register power supply: %d\n",
+                       ret);
+               goto err_gpio_free;
+       }
+
+       irq = gpio_to_irq(pdata->gpio);
+       if (irq > 0) {
+               ret = request_any_context_irq(irq, gpio_charger_irq,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               dev_name(&pdev->dev), gpio_charger->charger);
+               if (ret < 0)
+                       dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret);
+               else
+                       gpio_charger->irq = irq;
+       }
+
+       platform_set_drvdata(pdev, gpio_charger);
+
+       device_init_wakeup(&pdev->dev, 1);
+
+       return 0;
+
+err_gpio_free:
+       gpio_free(pdata->gpio);
+err_free:
+       return ret;
+}
+
+static int gpio_charger_remove(struct platform_device *pdev)
+{
+       struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
+
+       if (gpio_charger->irq)
+               free_irq(gpio_charger->irq, gpio_charger->charger);
+
+       power_supply_unregister(gpio_charger->charger);
+
+       gpio_free(gpio_charger->pdata->gpio);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int gpio_charger_suspend(struct device *dev)
+{
+       struct gpio_charger *gpio_charger = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               gpio_charger->wakeup_enabled =
+                       !enable_irq_wake(gpio_charger->irq);
+
+       return 0;
+}
+
+static int gpio_charger_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
+
+       if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled)
+               disable_irq_wake(gpio_charger->irq);
+       power_supply_changed(gpio_charger->charger);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops,
+               gpio_charger_suspend, gpio_charger_resume);
+
+static const struct of_device_id gpio_charger_match[] = {
+       { .compatible = "gpio-charger" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, gpio_charger_match);
+
+static struct platform_driver gpio_charger_driver = {
+       .probe = gpio_charger_probe,
+       .remove = gpio_charger_remove,
+       .driver = {
+               .name = "gpio-charger",
+               .pm = &gpio_charger_pm_ops,
+               .of_match_table = gpio_charger_match,
+       },
+};
+
+module_platform_driver(gpio_charger_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-charger");
diff --git a/drivers/power/supply/intel_mid_battery.c b/drivers/power/supply/intel_mid_battery.c
new file mode 100644 (file)
index 0000000..9fa4acc
--- /dev/null
@@ -0,0 +1,796 @@
+/*
+ * intel_mid_battery.c - Intel MID PMIC Battery Driver
+ *
+ * Copyright (C) 2009 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Nithish Mahalingam <nithish.mahalingam@intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/param.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define DRIVER_NAME "pmic_battery"
+
+/*********************************************************************
+ *             Generic defines
+ *********************************************************************/
+
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages.");
+
+#define PMIC_BATT_DRV_INFO_UPDATED     1
+#define PMIC_BATT_PRESENT              1
+#define PMIC_BATT_NOT_PRESENT          0
+#define PMIC_USB_PRESENT               PMIC_BATT_PRESENT
+#define PMIC_USB_NOT_PRESENT           PMIC_BATT_NOT_PRESENT
+
+/* pmic battery register related */
+#define PMIC_BATT_CHR_SCHRGINT_ADDR    0xD2
+#define PMIC_BATT_CHR_SBATOVP_MASK     (1 << 1)
+#define PMIC_BATT_CHR_STEMP_MASK       (1 << 2)
+#define PMIC_BATT_CHR_SCOMP_MASK       (1 << 3)
+#define PMIC_BATT_CHR_SUSBDET_MASK     (1 << 4)
+#define PMIC_BATT_CHR_SBATDET_MASK     (1 << 5)
+#define PMIC_BATT_CHR_SDCLMT_MASK      (1 << 6)
+#define PMIC_BATT_CHR_SUSBOVP_MASK     (1 << 7)
+#define PMIC_BATT_CHR_EXCPT_MASK       0x86
+
+#define PMIC_BATT_ADC_ACCCHRG_MASK     (1 << 31)
+#define PMIC_BATT_ADC_ACCCHRGVAL_MASK  0x7FFFFFFF
+
+/* pmic ipc related */
+#define PMIC_BATT_CHR_IPC_FCHRG_SUBID  0x4
+#define PMIC_BATT_CHR_IPC_TCHRG_SUBID  0x6
+
+/* types of battery charging */
+enum batt_charge_type {
+       BATT_USBOTG_500MA_CHARGE,
+       BATT_USBOTG_TRICKLE_CHARGE,
+};
+
+/* valid battery events */
+enum batt_event {
+       BATT_EVENT_BATOVP_EXCPT,
+       BATT_EVENT_USBOVP_EXCPT,
+       BATT_EVENT_TEMP_EXCPT,
+       BATT_EVENT_DCLMT_EXCPT,
+       BATT_EVENT_EXCPT
+};
+
+
+/*********************************************************************
+ *             Battery properties
+ *********************************************************************/
+
+/*
+ * pmic battery info
+ */
+struct pmic_power_module_info {
+       bool is_dev_info_updated;
+       struct device *dev;
+       /* pmic battery data */
+       unsigned long update_time;              /* jiffies when data read */
+       unsigned int usb_is_present;
+       unsigned int batt_is_present;
+       unsigned int batt_health;
+       unsigned int usb_health;
+       unsigned int batt_status;
+       unsigned int batt_charge_now;           /* in mAS */
+       unsigned int batt_prev_charge_full;     /* in mAS */
+       unsigned int batt_charge_rate;          /* in units per second */
+
+       struct power_supply *usb;
+       struct power_supply *batt;
+       int irq;                                /* GPE_ID or IRQ# */
+       struct workqueue_struct *monitor_wqueue;
+       struct delayed_work monitor_battery;
+       struct work_struct handler;
+};
+
+static unsigned int delay_time = 2000; /* in ms */
+
+/*
+ * pmic ac properties
+ */
+static enum power_supply_property pmic_usb_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_HEALTH,
+};
+
+/*
+ * pmic battery properties
+ */
+static enum power_supply_property pmic_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+};
+
+
+/*
+ * Glue functions for talking to the IPC
+ */
+
+struct battery_property {
+       u32 capacity;   /* Charger capacity */
+       u8  crnt;       /* Quick charge current value*/
+       u8  volt;       /* Fine adjustment of constant charge voltage */
+       u8  prot;       /* CHRGPROT register value */
+       u8  prot2;      /* CHRGPROT1 register value */
+       u8  timer;      /* Charging timer */
+};
+
+#define IPCMSG_BATTERY         0xEF
+
+/* Battery coulomb counter accumulator commands */
+#define IPC_CMD_CC_WR            0 /* Update coulomb counter value */
+#define IPC_CMD_CC_RD            1 /* Read coulomb counter value */
+#define IPC_CMD_BATTERY_PROPERTY  2 /* Read Battery property */
+
+/**
+ *     pmic_scu_ipc_battery_cc_read    -       read battery cc
+ *     @value: battery coulomb counter read
+ *
+ *     Reads the battery couloumb counter value, returns 0 on success, or
+ *     an error code
+ *
+ *     This function may sleep. Locking for SCU accesses is handled for
+ *     the caller.
+ */
+static int pmic_scu_ipc_battery_cc_read(u32 *value)
+{
+       return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD,
+                                       NULL, 0, value, 1);
+}
+
+/**
+ *     pmic_scu_ipc_battery_property_get       -       fetch properties
+ *     @prop: battery properties
+ *
+ *     Retrieve the battery properties from the power management
+ *
+ *     This function may sleep. Locking for SCU accesses is handled for
+ *     the caller.
+ */
+static int pmic_scu_ipc_battery_property_get(struct battery_property *prop)
+{
+       u32 data[3];
+       u8 *p = (u8 *)&data[1];
+       int err = intel_scu_ipc_command(IPCMSG_BATTERY,
+                               IPC_CMD_BATTERY_PROPERTY, NULL, 0, data, 3);
+
+       prop->capacity = data[0];
+       prop->crnt = *p++;
+       prop->volt = *p++;
+       prop->prot = *p++;
+       prop->prot2 = *p++;
+       prop->timer = *p++;
+
+       return err;
+}
+
+/**
+ *     pmic_scu_ipc_set_charger        -       set charger
+ *     @charger: charger to select
+ *
+ *     Switch the charging mode for the SCU
+ */
+
+static int pmic_scu_ipc_set_charger(int charger)
+{
+       return intel_scu_ipc_simple_command(IPCMSG_BATTERY, charger);
+}
+
+/**
+ * pmic_battery_log_event - log battery events
+ * @event: battery event to be logged
+ * Context: can sleep
+ *
+ * There are multiple battery events which may be of interest to users;
+ * this battery function logs the different battery events onto the
+ * kernel log messages.
+ */
+static void pmic_battery_log_event(enum batt_event event)
+{
+       printk(KERN_WARNING "pmic-battery: ");
+       switch (event) {
+       case BATT_EVENT_BATOVP_EXCPT:
+               printk(KERN_CONT "battery overvoltage condition\n");
+               break;
+       case BATT_EVENT_USBOVP_EXCPT:
+               printk(KERN_CONT "usb charger overvoltage condition\n");
+               break;
+       case BATT_EVENT_TEMP_EXCPT:
+               printk(KERN_CONT "high battery temperature condition\n");
+               break;
+       case BATT_EVENT_DCLMT_EXCPT:
+               printk(KERN_CONT "over battery charge current condition\n");
+               break;
+       default:
+               printk(KERN_CONT "charger/battery exception %d\n", event);
+               break;
+       }
+}
+
+/**
+ * pmic_battery_read_status - read battery status information
+ * @pbi: device info structure to update the read information
+ * Context: can sleep
+ *
+ * PMIC power source information need to be updated based on the data read
+ * from the PMIC battery registers.
+ *
+ */
+static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
+{
+       unsigned int update_time_intrvl;
+       unsigned int chrg_val;
+       u32 ccval;
+       u8 r8;
+       struct battery_property batt_prop;
+       int batt_present = 0;
+       int usb_present = 0;
+       int batt_exception = 0;
+
+       /* make sure the last batt_status read happened delay_time before */
+       if (pbi->update_time && time_before(jiffies, pbi->update_time +
+                                               msecs_to_jiffies(delay_time)))
+               return;
+
+       update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time);
+       pbi->update_time = jiffies;
+
+       /* read coulomb counter registers and schrgint register */
+       if (pmic_scu_ipc_battery_cc_read(&ccval)) {
+               dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
+                                                               __func__);
+               return;
+       }
+
+       if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
+               dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+                                                               __func__);
+               return;
+       }
+
+       /*
+        * set pmic_power_module_info members based on pmic register values
+        * read.
+        */
+
+       /* set batt_is_present */
+       if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
+               pbi->batt_is_present = PMIC_BATT_PRESENT;
+               batt_present = 1;
+       } else {
+               pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
+               pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
+       }
+
+       /* set batt_health */
+       if (batt_present) {
+               if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) {
+                       pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+                       pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+                       pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT);
+                       batt_exception = 1;
+               } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) {
+                       pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
+                       pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+                       pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT);
+                       batt_exception = 1;
+               } else {
+                       pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
+                       if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
+                               /* PMIC will change charging current automatically */
+                               pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
+                       }
+               }
+       }
+
+       /* set usb_is_present */
+       if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
+               pbi->usb_is_present = PMIC_USB_PRESENT;
+               usb_present = 1;
+       } else {
+               pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
+               pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+       }
+
+       if (usb_present) {
+               if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) {
+                       pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+                       pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT);
+               } else {
+                       pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
+               }
+       }
+
+       chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK;
+
+       /* set batt_prev_charge_full to battery capacity the first time */
+       if (!pbi->is_dev_info_updated) {
+               if (pmic_scu_ipc_battery_property_get(&batt_prop)) {
+                       dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
+                                                               __func__);
+                       return;
+               }
+               pbi->batt_prev_charge_full = batt_prop.capacity;
+       }
+
+       /* set batt_status */
+       if (batt_present && !batt_exception) {
+               if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
+                       pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
+                       pbi->batt_prev_charge_full = chrg_val;
+               } else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) {
+                       pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
+               } else {
+                       pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING;
+               }
+       }
+
+       /* set batt_charge_rate */
+       if (pbi->is_dev_info_updated && batt_present && !batt_exception) {
+               if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+                       if (pbi->batt_charge_now - chrg_val) {
+                               pbi->batt_charge_rate = ((pbi->batt_charge_now -
+                                       chrg_val) * 1000 * 60) /
+                                       update_time_intrvl;
+                       }
+               } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) {
+                       if (chrg_val - pbi->batt_charge_now) {
+                               pbi->batt_charge_rate = ((chrg_val -
+                                       pbi->batt_charge_now) * 1000 * 60) /
+                                       update_time_intrvl;
+                       }
+               } else
+                       pbi->batt_charge_rate = 0;
+       } else {
+               pbi->batt_charge_rate = -1;
+       }
+
+       /* batt_charge_now */
+       if (batt_present && !batt_exception)
+               pbi->batt_charge_now = chrg_val;
+       else
+               pbi->batt_charge_now = -1;
+
+       pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED;
+}
+
+/**
+ * pmic_usb_get_property - usb power source get property
+ * @psy: usb power supply context
+ * @psp: usb power source property
+ * @val: usb power source property value
+ * Context: can sleep
+ *
+ * PMIC usb power source property needs to be provided to power_supply
+ * subsytem for it to provide the information to users.
+ */
+static int pmic_usb_get_property(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy);
+
+       /* update pmic_power_module_info members */
+       pmic_battery_read_status(pbi);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = pbi->usb_is_present;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = pbi->usb_health;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static inline unsigned long mAStouAh(unsigned long v)
+{
+       /* seconds to hours, mA to ÂµA */
+       return (v * 1000) / 3600;
+}
+
+/**
+ * pmic_battery_get_property - battery power source get property
+ * @psy: battery power supply context
+ * @psp: battery power source property
+ * @val: battery power source property value
+ * Context: can sleep
+ *
+ * PMIC battery power source property needs to be provided to power_supply
+ * subsytem for it to provide the information to users.
+ */
+static int pmic_battery_get_property(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct pmic_power_module_info *pbi = power_supply_get_drvdata(psy);
+
+       /* update pmic_power_module_info members */
+       pmic_battery_read_status(pbi);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = pbi->batt_status;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = pbi->batt_health;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = pbi->batt_is_present;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               val->intval = mAStouAh(pbi->batt_charge_now);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               val->intval = mAStouAh(pbi->batt_prev_charge_full);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * pmic_battery_monitor - monitor battery status
+ * @work: work structure
+ * Context: can sleep
+ *
+ * PMIC battery status needs to be monitored for any change
+ * and information needs to be frequently updated.
+ */
+static void pmic_battery_monitor(struct work_struct *work)
+{
+       struct pmic_power_module_info *pbi = container_of(work,
+                       struct pmic_power_module_info, monitor_battery.work);
+
+       /* update pmic_power_module_info members */
+       pmic_battery_read_status(pbi);
+       queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10);
+}
+
+/**
+ * pmic_battery_set_charger - set battery charger
+ * @pbi: device info structure
+ * @chrg: charge mode to set battery charger in
+ * Context: can sleep
+ *
+ * PMIC battery charger needs to be enabled based on the usb charge
+ * capabilities connected to the platform.
+ */
+static int pmic_battery_set_charger(struct pmic_power_module_info *pbi,
+                                               enum batt_charge_type chrg)
+{
+       int retval;
+
+       /* set usblmt bits and chrgcntl register bits appropriately */
+       switch (chrg) {
+       case BATT_USBOTG_500MA_CHARGE:
+               retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID);
+               break;
+       case BATT_USBOTG_TRICKLE_CHARGE:
+               retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID);
+               break;
+       default:
+               dev_warn(pbi->dev, "%s(): out of range usb charger "
+                                               "charge detected\n", __func__);
+               return -EINVAL;
+       }
+
+       if (retval) {
+               dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+                                                               __func__);
+               return retval;
+       }
+
+       return 0;
+}
+
+/**
+ * pmic_battery_interrupt_handler - pmic battery interrupt handler
+ * Context: interrupt context
+ *
+ * PMIC battery interrupt handler which will be called with either
+ * battery full condition occurs or usb otg & battery connect
+ * condition occurs.
+ */
+static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev)
+{
+       struct pmic_power_module_info *pbi = dev;
+
+       schedule_work(&pbi->handler);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * pmic_battery_handle_intrpt - pmic battery service interrupt
+ * @work: work structure
+ * Context: can sleep
+ *
+ * PMIC battery needs to either update the battery status as full
+ * if it detects battery full condition caused the interrupt or needs
+ * to enable battery charger if it detects usb and battery detect
+ * caused the source of interrupt.
+ */
+static void pmic_battery_handle_intrpt(struct work_struct *work)
+{
+       struct pmic_power_module_info *pbi = container_of(work,
+                               struct pmic_power_module_info, handler);
+       enum batt_charge_type chrg;
+       u8 r8;
+
+       if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
+               dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+                                                               __func__);
+               return;
+       }
+       /* find the cause of the interrupt */
+       if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
+               pbi->batt_is_present = PMIC_BATT_PRESENT;
+       } else {
+               pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
+               pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
+               return;
+       }
+
+       if (r8 & PMIC_BATT_CHR_EXCPT_MASK) {
+               pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               pmic_battery_log_event(BATT_EVENT_EXCPT);
+               return;
+       } else {
+               pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
+               pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
+       }
+
+       if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
+               u32 ccval;
+               pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
+
+               if (pmic_scu_ipc_battery_cc_read(&ccval)) {
+                       dev_warn(pbi->dev, "%s(): ipc config cmd "
+                                                       "failed\n", __func__);
+                       return;
+               }
+               pbi->batt_prev_charge_full = ccval &
+                                               PMIC_BATT_ADC_ACCCHRGVAL_MASK;
+               return;
+       }
+
+       if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
+               pbi->usb_is_present = PMIC_USB_PRESENT;
+       } else {
+               pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
+               pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               return;
+       }
+
+       /* setup battery charging */
+
+#if 0
+       /* check usb otg power capability and set charger accordingly */
+       retval = langwell_udc_maxpower(&power);
+       if (retval) {
+               dev_warn(pbi->dev,
+                   "%s(): usb otg power query failed with error code %d\n",
+                       __func__, retval);
+               return;
+       }
+
+       if (power >= 500)
+               chrg = BATT_USBOTG_500MA_CHARGE;
+       else
+#endif
+               chrg = BATT_USBOTG_TRICKLE_CHARGE;
+
+       /* enable battery charging */
+       if (pmic_battery_set_charger(pbi, chrg)) {
+               dev_warn(pbi->dev,
+                       "%s(): failed to set up battery charging\n", __func__);
+               return;
+       }
+
+       dev_dbg(pbi->dev,
+               "pmic-battery: %s() - setting up battery charger successful\n",
+                       __func__);
+}
+
+/*
+ * Description of power supplies
+ */
+static const struct power_supply_desc pmic_usb_desc = {
+       .name           = "pmic-usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties     = pmic_usb_props,
+       .num_properties = ARRAY_SIZE(pmic_usb_props),
+       .get_property   = pmic_usb_get_property,
+};
+
+static const struct power_supply_desc pmic_batt_desc = {
+       .name           = "pmic-batt",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = pmic_battery_props,
+       .num_properties = ARRAY_SIZE(pmic_battery_props),
+       .get_property   = pmic_battery_get_property,
+};
+
+/**
+ * pmic_battery_probe - pmic battery initialize
+ * @irq: pmic battery device irq
+ * @dev: pmic battery device structure
+ * Context: can sleep
+ *
+ * PMIC battery initializes its internal data structue and other
+ * infrastructure components for it to work as expected.
+ */
+static int probe(int irq, struct device *dev)
+{
+       int retval = 0;
+       struct pmic_power_module_info *pbi;
+       struct power_supply_config psy_cfg = {};
+
+       dev_dbg(dev, "pmic-battery: found pmic battery device\n");
+
+       pbi = kzalloc(sizeof(*pbi), GFP_KERNEL);
+       if (!pbi) {
+               dev_err(dev, "%s(): memory allocation failed\n",
+                                                               __func__);
+               return -ENOMEM;
+       }
+
+       pbi->dev = dev;
+       pbi->irq = irq;
+       dev_set_drvdata(dev, pbi);
+       psy_cfg.drv_data = pbi;
+
+       /* initialize all required framework before enabling interrupts */
+       INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt);
+       INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor);
+       pbi->monitor_wqueue =
+                       create_singlethread_workqueue(dev_name(dev));
+       if (!pbi->monitor_wqueue) {
+               dev_err(dev, "%s(): wqueue init failed\n", __func__);
+               retval = -ESRCH;
+               goto wqueue_failed;
+       }
+
+       /* register interrupt */
+       retval = request_irq(pbi->irq, pmic_battery_interrupt_handler,
+                                                       0, DRIVER_NAME, pbi);
+       if (retval) {
+               dev_err(dev, "%s(): cannot get IRQ\n", __func__);
+               goto requestirq_failed;
+       }
+
+       /* register pmic-batt with power supply subsystem */
+       pbi->batt = power_supply_register(dev, &pmic_usb_desc, &psy_cfg);
+       if (IS_ERR(pbi->batt)) {
+               dev_err(dev,
+                       "%s(): failed to register pmic battery device with power supply subsystem\n",
+                               __func__);
+               retval = PTR_ERR(pbi->batt);
+               goto power_reg_failed;
+       }
+
+       dev_dbg(dev, "pmic-battery: %s() - pmic battery device "
+               "registration with power supply subsystem successful\n",
+               __func__);
+
+       queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1);
+
+       /* register pmic-usb with power supply subsystem */
+       pbi->usb = power_supply_register(dev, &pmic_batt_desc, &psy_cfg);
+       if (IS_ERR(pbi->usb)) {
+               dev_err(dev,
+                       "%s(): failed to register pmic usb device with power supply subsystem\n",
+                               __func__);
+               retval = PTR_ERR(pbi->usb);
+               goto power_reg_failed_1;
+       }
+
+       if (debug)
+               printk(KERN_INFO "pmic-battery: %s() - pmic usb device "
+                       "registration with power supply subsystem successful\n",
+                       __func__);
+
+       return retval;
+
+power_reg_failed_1:
+       power_supply_unregister(pbi->batt);
+power_reg_failed:
+       cancel_delayed_work_sync(&pbi->monitor_battery);
+requestirq_failed:
+       destroy_workqueue(pbi->monitor_wqueue);
+wqueue_failed:
+       kfree(pbi);
+
+       return retval;
+}
+
+static int platform_pmic_battery_probe(struct platform_device *pdev)
+{
+       return probe(pdev->id, &pdev->dev);
+}
+
+/**
+ * pmic_battery_remove - pmic battery finalize
+ * @dev: pmic battery device structure
+ * Context: can sleep
+ *
+ * PMIC battery finalizes its internal data structue and other
+ * infrastructure components that it initialized in
+ * pmic_battery_probe.
+ */
+
+static int platform_pmic_battery_remove(struct platform_device *pdev)
+{
+       struct pmic_power_module_info *pbi = platform_get_drvdata(pdev);
+
+       free_irq(pbi->irq, pbi);
+       cancel_delayed_work_sync(&pbi->monitor_battery);
+       destroy_workqueue(pbi->monitor_wqueue);
+
+       power_supply_unregister(pbi->usb);
+       power_supply_unregister(pbi->batt);
+
+       cancel_work_sync(&pbi->handler);
+       kfree(pbi);
+       return 0;
+}
+
+static struct platform_driver platform_pmic_battery_driver = {
+       .driver = {
+               .name = DRIVER_NAME,
+       },
+       .probe = platform_pmic_battery_probe,
+       .remove = platform_pmic_battery_remove,
+};
+
+module_platform_driver(platform_pmic_battery_driver);
+
+MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
+MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/ipaq_micro_battery.c b/drivers/power/supply/ipaq_micro_battery.c
new file mode 100644 (file)
index 0000000..35b01c7
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * h3xxx atmel micro companion support, battery subdevice
+ * based on previous kernel 2.4 version
+ * Author : Alessandro Gardich <gremlin@gremlin.it>
+ * Author : Linus Walleij <linus.walleij@linaro.org>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ipaq-micro.h>
+#include <linux/power_supply.h>
+#include <linux/workqueue.h>
+
+#define BATT_PERIOD 100000 /* 100 seconds in milliseconds */
+
+#define MICRO_BATT_CHEM_ALKALINE       0x01
+#define MICRO_BATT_CHEM_NICD           0x02
+#define MICRO_BATT_CHEM_NIMH           0x03
+#define MICRO_BATT_CHEM_LION           0x04
+#define MICRO_BATT_CHEM_LIPOLY         0x05
+#define MICRO_BATT_CHEM_NOT_INSTALLED  0x06
+#define MICRO_BATT_CHEM_UNKNOWN                0xff
+
+#define MICRO_BATT_STATUS_HIGH         0x01
+#define MICRO_BATT_STATUS_LOW          0x02
+#define MICRO_BATT_STATUS_CRITICAL     0x04
+#define MICRO_BATT_STATUS_CHARGING     0x08
+#define MICRO_BATT_STATUS_CHARGEMAIN   0x10
+#define MICRO_BATT_STATUS_DEAD         0x20 /* Battery will not charge */
+#define MICRO_BATT_STATUS_NOTINSTALLED 0x20 /* For expansion pack batteries */
+#define MICRO_BATT_STATUS_FULL         0x40 /* Battery fully charged */
+#define MICRO_BATT_STATUS_NOBATTERY    0x80
+#define MICRO_BATT_STATUS_UNKNOWN      0xff
+
+struct micro_battery {
+       struct ipaq_micro *micro;
+       struct workqueue_struct *wq;
+       struct delayed_work update;
+       u8 ac;
+       u8 chemistry;
+       unsigned int voltage;
+       u16 temperature;
+       u8 flag;
+};
+
+static void micro_battery_work(struct work_struct *work)
+{
+       struct micro_battery *mb = container_of(work,
+                               struct micro_battery, update.work);
+       struct ipaq_micro_msg msg_battery = {
+               .id = MSG_BATTERY,
+       };
+       struct ipaq_micro_msg msg_sensor = {
+               .id = MSG_THERMAL_SENSOR,
+       };
+
+       /* First send battery message */
+       ipaq_micro_tx_msg_sync(mb->micro, &msg_battery);
+       if (msg_battery.rx_len < 4)
+               pr_info("ERROR");
+
+       /*
+        * Returned message format:
+        * byte 0:   0x00 = Not plugged in
+        *           0x01 = AC adapter plugged in
+        * byte 1:   chemistry
+        * byte 2:   voltage LSB
+        * byte 3:   voltage MSB
+        * byte 4:   flags
+        * byte 5-9: same for battery 2
+        */
+       mb->ac = msg_battery.rx_data[0];
+       mb->chemistry = msg_battery.rx_data[1];
+       mb->voltage = ((((unsigned short)msg_battery.rx_data[3] << 8) +
+                       msg_battery.rx_data[2]) * 5000L) * 1000 / 1024;
+       mb->flag = msg_battery.rx_data[4];
+
+       if (msg_battery.rx_len == 9)
+               pr_debug("second battery ignored\n");
+
+       /* Then read the sensor */
+       ipaq_micro_tx_msg_sync(mb->micro, &msg_sensor);
+       mb->temperature = msg_sensor.rx_data[1] << 8 | msg_sensor.rx_data[0];
+
+       queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
+}
+
+static int get_capacity(struct power_supply *b)
+{
+       struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
+
+       switch (mb->flag & 0x07) {
+       case MICRO_BATT_STATUS_HIGH:
+               return 100;
+               break;
+       case MICRO_BATT_STATUS_LOW:
+               return 50;
+               break;
+       case MICRO_BATT_STATUS_CRITICAL:
+               return 5;
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int get_status(struct power_supply *b)
+{
+       struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
+
+       if (mb->flag == MICRO_BATT_STATUS_UNKNOWN)
+               return POWER_SUPPLY_STATUS_UNKNOWN;
+
+       if (mb->flag & MICRO_BATT_STATUS_FULL)
+               return POWER_SUPPLY_STATUS_FULL;
+
+       if ((mb->flag & MICRO_BATT_STATUS_CHARGING) ||
+               (mb->flag & MICRO_BATT_STATUS_CHARGEMAIN))
+               return POWER_SUPPLY_STATUS_CHARGING;
+
+       return POWER_SUPPLY_STATUS_DISCHARGING;
+}
+
+static int micro_batt_get_property(struct power_supply *b,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               switch (mb->chemistry) {
+               case MICRO_BATT_CHEM_NICD:
+                       val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd;
+                       break;
+               case MICRO_BATT_CHEM_NIMH:
+                       val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
+                       break;
+               case MICRO_BATT_CHEM_LION:
+                       val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+                       break;
+               case MICRO_BATT_CHEM_LIPOLY:
+                       val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
+                       break;
+               default:
+                       val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+                       break;
+               };
+               break;
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = get_status(b);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = 4700000;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = get_capacity(b);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = mb->temperature;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = mb->voltage;
+               break;
+       default:
+               return -EINVAL;
+       };
+
+       return 0;
+}
+
+static int micro_ac_get_property(struct power_supply *b,
+                                enum power_supply_property psp,
+                                union power_supply_propval *val)
+{
+       struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = mb->ac;
+               break;
+       default:
+               return -EINVAL;
+       };
+
+       return 0;
+}
+
+static enum power_supply_property micro_batt_power_props[] = {
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static const struct power_supply_desc micro_batt_power_desc = {
+       .name                   = "main-battery",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = micro_batt_power_props,
+       .num_properties         = ARRAY_SIZE(micro_batt_power_props),
+       .get_property           = micro_batt_get_property,
+       .use_for_apm            = 1,
+};
+
+static enum power_supply_property micro_ac_power_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc micro_ac_power_desc = {
+       .name                   = "ac",
+       .type                   = POWER_SUPPLY_TYPE_MAINS,
+       .properties             = micro_ac_power_props,
+       .num_properties         = ARRAY_SIZE(micro_ac_power_props),
+       .get_property           = micro_ac_get_property,
+};
+
+static struct power_supply *micro_batt_power, *micro_ac_power;
+
+static int micro_batt_probe(struct platform_device *pdev)
+{
+       struct micro_battery *mb;
+       int ret;
+
+       mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
+       if (!mb)
+               return -ENOMEM;
+
+       mb->micro = dev_get_drvdata(pdev->dev.parent);
+       mb->wq = create_singlethread_workqueue("ipaq-battery-wq");
+       if (!mb->wq)
+               return -ENOMEM;
+
+       INIT_DELAYED_WORK(&mb->update, micro_battery_work);
+       platform_set_drvdata(pdev, mb);
+       queue_delayed_work(mb->wq, &mb->update, 1);
+
+       micro_batt_power = power_supply_register(&pdev->dev,
+                                                &micro_batt_power_desc, NULL);
+       if (IS_ERR(micro_batt_power)) {
+               ret = PTR_ERR(micro_batt_power);
+               goto batt_err;
+       }
+
+       micro_ac_power = power_supply_register(&pdev->dev,
+                                              &micro_ac_power_desc, NULL);
+       if (IS_ERR(micro_ac_power)) {
+               ret = PTR_ERR(micro_ac_power);
+               goto ac_err;
+       }
+
+       dev_info(&pdev->dev, "iPAQ micro battery driver\n");
+       return 0;
+
+ac_err:
+       power_supply_unregister(micro_batt_power);
+batt_err:
+       cancel_delayed_work_sync(&mb->update);
+       destroy_workqueue(mb->wq);
+       return ret;
+}
+
+static int micro_batt_remove(struct platform_device *pdev)
+
+{
+       struct micro_battery *mb = platform_get_drvdata(pdev);
+
+       power_supply_unregister(micro_ac_power);
+       power_supply_unregister(micro_batt_power);
+       cancel_delayed_work_sync(&mb->update);
+       destroy_workqueue(mb->wq);
+
+       return 0;
+}
+
+static int __maybe_unused micro_batt_suspend(struct device *dev)
+{
+       struct micro_battery *mb = dev_get_drvdata(dev);
+
+       cancel_delayed_work_sync(&mb->update);
+       return 0;
+}
+
+static int __maybe_unused micro_batt_resume(struct device *dev)
+{
+       struct micro_battery *mb = dev_get_drvdata(dev);
+
+       queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
+       return 0;
+}
+
+static const struct dev_pm_ops micro_batt_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(micro_batt_suspend, micro_batt_resume)
+};
+
+static struct platform_driver micro_batt_device_driver = {
+       .driver         = {
+               .name   = "ipaq-micro-battery",
+               .pm     = &micro_batt_dev_pm_ops,
+       },
+       .probe          = micro_batt_probe,
+       .remove         = micro_batt_remove,
+};
+module_platform_driver(micro_batt_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery");
+MODULE_ALIAS("platform:battery-ipaq-micro");
diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c
new file mode 100644 (file)
index 0000000..4cd6899
--- /dev/null
@@ -0,0 +1,559 @@
+/*
+ * ISP1704 USB Charger Detection driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2012 - 2013 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/power/isp1704_charger.h>
+
+/* Vendor specific Power Control register */
+#define ISP1704_PWR_CTRL               0x3d
+#define ISP1704_PWR_CTRL_SWCTRL                (1 << 0)
+#define ISP1704_PWR_CTRL_DET_COMP      (1 << 1)
+#define ISP1704_PWR_CTRL_BVALID_RISE   (1 << 2)
+#define ISP1704_PWR_CTRL_BVALID_FALL   (1 << 3)
+#define ISP1704_PWR_CTRL_DP_WKPU_EN    (1 << 4)
+#define ISP1704_PWR_CTRL_VDAT_DET      (1 << 5)
+#define ISP1704_PWR_CTRL_DPVSRC_EN     (1 << 6)
+#define ISP1704_PWR_CTRL_HWDETECT      (1 << 7)
+
+#define NXP_VENDOR_ID                  0x04cc
+
+static u16 isp170x_id[] = {
+       0x1704,
+       0x1707,
+};
+
+struct isp1704_charger {
+       struct device                   *dev;
+       struct power_supply             *psy;
+       struct power_supply_desc        psy_desc;
+       struct usb_phy                  *phy;
+       struct notifier_block           nb;
+       struct work_struct              work;
+
+       /* properties */
+       char                    model[8];
+       unsigned                present:1;
+       unsigned                online:1;
+       unsigned                current_max;
+};
+
+static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
+{
+       return usb_phy_io_read(isp->phy, reg);
+}
+
+static inline int isp1704_write(struct isp1704_charger *isp, u32 reg, u32 val)
+{
+       return usb_phy_io_write(isp->phy, val, reg);
+}
+
+/*
+ * Disable/enable the power from the isp1704 if a function for it
+ * has been provided with platform data.
+ */
+static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
+{
+       struct isp1704_charger_data     *board = isp->dev->platform_data;
+
+       if (board && board->set_power)
+               board->set_power(on);
+       else if (board)
+               gpio_set_value(board->enable_gpio, on);
+}
+
+/*
+ * Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB
+ * chargers).
+ *
+ * REVISIT: The method is defined in Battery Charging Specification and is
+ * applicable to any ULPI transceiver. Nothing isp170x specific here.
+ */
+static inline int isp1704_charger_type(struct isp1704_charger *isp)
+{
+       u8 reg;
+       u8 func_ctrl;
+       u8 otg_ctrl;
+       int type = POWER_SUPPLY_TYPE_USB_DCP;
+
+       func_ctrl = isp1704_read(isp, ULPI_FUNC_CTRL);
+       otg_ctrl = isp1704_read(isp, ULPI_OTG_CTRL);
+
+       /* disable pulldowns */
+       reg = ULPI_OTG_CTRL_DM_PULLDOWN | ULPI_OTG_CTRL_DP_PULLDOWN;
+       isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), reg);
+
+       /* full speed */
+       isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
+                       ULPI_FUNC_CTRL_XCVRSEL_MASK);
+       isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL),
+                       ULPI_FUNC_CTRL_FULL_SPEED);
+
+       /* Enable strong pull-up on DP (1.5K) and reset */
+       reg = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
+       isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), reg);
+       usleep_range(1000, 2000);
+
+       reg = isp1704_read(isp, ULPI_DEBUG);
+       if ((reg & 3) != 3)
+               type = POWER_SUPPLY_TYPE_USB_CDP;
+
+       /* recover original state */
+       isp1704_write(isp, ULPI_FUNC_CTRL, func_ctrl);
+       isp1704_write(isp, ULPI_OTG_CTRL, otg_ctrl);
+
+       return type;
+}
+
+/*
+ * ISP1704 detects PS/2 adapters as charger. To make sure the detected charger
+ * is actually a dedicated charger, the following steps need to be taken.
+ */
+static inline int isp1704_charger_verify(struct isp1704_charger *isp)
+{
+       int     ret = 0;
+       u8      r;
+
+       /* Reset the transceiver */
+       r = isp1704_read(isp, ULPI_FUNC_CTRL);
+       r |= ULPI_FUNC_CTRL_RESET;
+       isp1704_write(isp, ULPI_FUNC_CTRL, r);
+       usleep_range(1000, 2000);
+
+       /* Set normal mode */
+       r &= ~(ULPI_FUNC_CTRL_RESET | ULPI_FUNC_CTRL_OPMODE_MASK);
+       isp1704_write(isp, ULPI_FUNC_CTRL, r);
+
+       /* Clear the DP and DM pull-down bits */
+       r = ULPI_OTG_CTRL_DP_PULLDOWN | ULPI_OTG_CTRL_DM_PULLDOWN;
+       isp1704_write(isp, ULPI_CLR(ULPI_OTG_CTRL), r);
+
+       /* Enable strong pull-up on DP (1.5K) and reset */
+       r = ULPI_FUNC_CTRL_TERMSELECT | ULPI_FUNC_CTRL_RESET;
+       isp1704_write(isp, ULPI_SET(ULPI_FUNC_CTRL), r);
+       usleep_range(1000, 2000);
+
+       /* Read the line state */
+       if (!isp1704_read(isp, ULPI_DEBUG)) {
+               /* Disable strong pull-up on DP (1.5K) */
+               isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
+                               ULPI_FUNC_CTRL_TERMSELECT);
+               return 1;
+       }
+
+       /* Is it a charger or PS/2 connection */
+
+       /* Enable weak pull-up resistor on DP */
+       isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
+                       ISP1704_PWR_CTRL_DP_WKPU_EN);
+
+       /* Disable strong pull-up on DP (1.5K) */
+       isp1704_write(isp, ULPI_CLR(ULPI_FUNC_CTRL),
+                       ULPI_FUNC_CTRL_TERMSELECT);
+
+       /* Enable weak pull-down resistor on DM */
+       isp1704_write(isp, ULPI_SET(ULPI_OTG_CTRL),
+                       ULPI_OTG_CTRL_DM_PULLDOWN);
+
+       /* It's a charger if the line states are clear */
+       if (!(isp1704_read(isp, ULPI_DEBUG)))
+               ret = 1;
+
+       /* Disable weak pull-up resistor on DP */
+       isp1704_write(isp, ULPI_CLR(ISP1704_PWR_CTRL),
+                       ISP1704_PWR_CTRL_DP_WKPU_EN);
+
+       return ret;
+}
+
+static inline int isp1704_charger_detect(struct isp1704_charger *isp)
+{
+       unsigned long   timeout;
+       u8              pwr_ctrl;
+       int             ret = 0;
+
+       pwr_ctrl = isp1704_read(isp, ISP1704_PWR_CTRL);
+
+       /* set SW control bit in PWR_CTRL register */
+       isp1704_write(isp, ISP1704_PWR_CTRL,
+                       ISP1704_PWR_CTRL_SWCTRL);
+
+       /* enable manual charger detection */
+       isp1704_write(isp, ULPI_SET(ISP1704_PWR_CTRL),
+                       ISP1704_PWR_CTRL_SWCTRL
+                       | ISP1704_PWR_CTRL_DPVSRC_EN);
+       usleep_range(1000, 2000);
+
+       timeout = jiffies + msecs_to_jiffies(300);
+       do {
+               /* Check if there is a charger */
+               if (isp1704_read(isp, ISP1704_PWR_CTRL)
+                               & ISP1704_PWR_CTRL_VDAT_DET) {
+                       ret = isp1704_charger_verify(isp);
+                       break;
+               }
+       } while (!time_after(jiffies, timeout) && isp->online);
+
+       /* recover original state */
+       isp1704_write(isp, ISP1704_PWR_CTRL, pwr_ctrl);
+
+       return ret;
+}
+
+static inline int isp1704_charger_detect_dcp(struct isp1704_charger *isp)
+{
+       if (isp1704_charger_detect(isp) &&
+                       isp1704_charger_type(isp) == POWER_SUPPLY_TYPE_USB_DCP)
+               return true;
+       else
+               return false;
+}
+
+static void isp1704_charger_work(struct work_struct *data)
+{
+       struct isp1704_charger  *isp =
+               container_of(data, struct isp1704_charger, work);
+       static DEFINE_MUTEX(lock);
+
+       mutex_lock(&lock);
+
+       switch (isp->phy->last_event) {
+       case USB_EVENT_VBUS:
+               /* do not call wall charger detection more times */
+               if (!isp->present) {
+                       isp->online = true;
+                       isp->present = 1;
+                       isp1704_charger_set_power(isp, 1);
+
+                       /* detect wall charger */
+                       if (isp1704_charger_detect_dcp(isp)) {
+                               isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
+                               isp->current_max = 1800;
+                       } else {
+                               isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+                               isp->current_max = 500;
+                       }
+
+                       /* enable data pullups */
+                       if (isp->phy->otg->gadget)
+                               usb_gadget_connect(isp->phy->otg->gadget);
+               }
+
+               if (isp->psy_desc.type != POWER_SUPPLY_TYPE_USB_DCP) {
+                       /*
+                        * Only 500mA here or high speed chirp
+                        * handshaking may break
+                        */
+                       if (isp->current_max > 500)
+                               isp->current_max = 500;
+
+                       if (isp->current_max > 100)
+                               isp->psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP;
+               }
+               break;
+       case USB_EVENT_NONE:
+               isp->online = false;
+               isp->present = 0;
+               isp->current_max = 0;
+               isp->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+
+               /*
+                * Disable data pullups. We need to prevent the controller from
+                * enumerating.
+                *
+                * FIXME: This is here to allow charger detection with Host/HUB
+                * chargers. The pullups may be enabled elsewhere, so this can
+                * not be the final solution.
+                */
+               if (isp->phy->otg->gadget)
+                       usb_gadget_disconnect(isp->phy->otg->gadget);
+
+               isp1704_charger_set_power(isp, 0);
+               break;
+       default:
+               goto out;
+       }
+
+       power_supply_changed(isp->psy);
+out:
+       mutex_unlock(&lock);
+}
+
+static int isp1704_notifier_call(struct notifier_block *nb,
+               unsigned long val, void *v)
+{
+       struct isp1704_charger *isp =
+               container_of(nb, struct isp1704_charger, nb);
+
+       schedule_work(&isp->work);
+
+       return NOTIFY_OK;
+}
+
+static int isp1704_charger_get_property(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct isp1704_charger *isp = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = isp->present;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = isp->online;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               val->intval = isp->current_max;
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = isp->model;
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = "NXP";
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static enum power_supply_property power_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static inline int isp1704_test_ulpi(struct isp1704_charger *isp)
+{
+       int vendor;
+       int product;
+       int i;
+       int ret = -ENODEV;
+
+       /* Test ULPI interface */
+       ret = isp1704_write(isp, ULPI_SCRATCH, 0xaa);
+       if (ret < 0)
+               return ret;
+
+       ret = isp1704_read(isp, ULPI_SCRATCH);
+       if (ret < 0)
+               return ret;
+
+       if (ret != 0xaa)
+               return -ENODEV;
+
+       /* Verify the product and vendor id matches */
+       vendor = isp1704_read(isp, ULPI_VENDOR_ID_LOW);
+       vendor |= isp1704_read(isp, ULPI_VENDOR_ID_HIGH) << 8;
+       if (vendor != NXP_VENDOR_ID)
+               return -ENODEV;
+
+       product = isp1704_read(isp, ULPI_PRODUCT_ID_LOW);
+       product |= isp1704_read(isp, ULPI_PRODUCT_ID_HIGH) << 8;
+
+       for (i = 0; i < ARRAY_SIZE(isp170x_id); i++) {
+               if (product == isp170x_id[i]) {
+                       sprintf(isp->model, "isp%x", product);
+                       return product;
+               }
+       }
+
+       dev_err(isp->dev, "product id %x not matching known ids", product);
+
+       return -ENODEV;
+}
+
+static int isp1704_charger_probe(struct platform_device *pdev)
+{
+       struct isp1704_charger  *isp;
+       int                     ret = -ENODEV;
+       struct power_supply_config psy_cfg = {};
+
+       struct isp1704_charger_data *pdata = dev_get_platdata(&pdev->dev);
+       struct device_node *np = pdev->dev.of_node;
+
+       if (np) {
+               int gpio = of_get_named_gpio(np, "nxp,enable-gpio", 0);
+
+               if (gpio < 0) {
+                       dev_err(&pdev->dev, "missing DT GPIO nxp,enable-gpio\n");
+                       return gpio;
+               }
+
+               pdata = devm_kzalloc(&pdev->dev,
+                       sizeof(struct isp1704_charger_data), GFP_KERNEL);
+               pdata->enable_gpio = gpio;
+
+               dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio);
+
+               ret = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio,
+                                       GPIOF_OUT_INIT_HIGH, "isp1704_reset");
+               if (ret) {
+                       dev_err(&pdev->dev, "gpio request failed\n");
+                       goto fail0;
+               }
+       }
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "missing platform data!\n");
+               return -ENODEV;
+       }
+
+
+       isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
+       if (!isp)
+               return -ENOMEM;
+
+       if (np)
+               isp->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
+       else
+               isp->phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+
+       if (IS_ERR(isp->phy)) {
+               ret = PTR_ERR(isp->phy);
+               dev_err(&pdev->dev, "usb_get_phy failed\n");
+               goto fail0;
+       }
+
+       isp->dev = &pdev->dev;
+       platform_set_drvdata(pdev, isp);
+
+       isp1704_charger_set_power(isp, 1);
+
+       ret = isp1704_test_ulpi(isp);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "isp1704_test_ulpi failed\n");
+               goto fail1;
+       }
+
+       isp->psy_desc.name              = "isp1704";
+       isp->psy_desc.type              = POWER_SUPPLY_TYPE_USB;
+       isp->psy_desc.properties        = power_props;
+       isp->psy_desc.num_properties    = ARRAY_SIZE(power_props);
+       isp->psy_desc.get_property      = isp1704_charger_get_property;
+
+       psy_cfg.drv_data                = isp;
+
+       isp->psy = power_supply_register(isp->dev, &isp->psy_desc, &psy_cfg);
+       if (IS_ERR(isp->psy)) {
+               ret = PTR_ERR(isp->psy);
+               dev_err(&pdev->dev, "power_supply_register failed\n");
+               goto fail1;
+       }
+
+       /*
+        * REVISIT: using work in order to allow the usb notifications to be
+        * made atomically in the future.
+        */
+       INIT_WORK(&isp->work, isp1704_charger_work);
+
+       isp->nb.notifier_call = isp1704_notifier_call;
+
+       ret = usb_register_notifier(isp->phy, &isp->nb);
+       if (ret) {
+               dev_err(&pdev->dev, "usb_register_notifier failed\n");
+               goto fail2;
+       }
+
+       dev_info(isp->dev, "registered with product id %s\n", isp->model);
+
+       /*
+        * Taking over the D+ pullup.
+        *
+        * FIXME: The device will be disconnected if it was already
+        * enumerated. The charger driver should be always loaded before any
+        * gadget is loaded.
+        */
+       if (isp->phy->otg->gadget)
+               usb_gadget_disconnect(isp->phy->otg->gadget);
+
+       if (isp->phy->last_event == USB_EVENT_NONE)
+               isp1704_charger_set_power(isp, 0);
+
+       /* Detect charger if VBUS is valid (the cable was already plugged). */
+       if (isp->phy->last_event == USB_EVENT_VBUS &&
+                       !isp->phy->otg->default_a)
+               schedule_work(&isp->work);
+
+       return 0;
+fail2:
+       power_supply_unregister(isp->psy);
+fail1:
+       isp1704_charger_set_power(isp, 0);
+fail0:
+       dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
+
+       return ret;
+}
+
+static int isp1704_charger_remove(struct platform_device *pdev)
+{
+       struct isp1704_charger *isp = platform_get_drvdata(pdev);
+
+       usb_unregister_notifier(isp->phy, &isp->nb);
+       power_supply_unregister(isp->psy);
+       isp1704_charger_set_power(isp, 0);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_isp1704_of_match[] = {
+       { .compatible = "nxp,isp1704", },
+       { .compatible = "nxp,isp1707", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_isp1704_of_match);
+#endif
+
+static struct platform_driver isp1704_charger_driver = {
+       .driver = {
+               .name = "isp1704_charger",
+               .of_match_table = of_match_ptr(omap_isp1704_of_match),
+       },
+       .probe = isp1704_charger_probe,
+       .remove = isp1704_charger_remove,
+};
+
+module_platform_driver(isp1704_charger_driver);
+
+MODULE_ALIAS("platform:isp1704_charger");
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("ISP170x USB Charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/jz4740-battery.c b/drivers/power/supply/jz4740-battery.c
new file mode 100644 (file)
index 0000000..88f04f4
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * Battery measurement code for Ingenic JZ SOC.
+ *
+ * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com>
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * based on tosa_battery.c
+ *
+ * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
+*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/power_supply.h>
+
+#include <linux/power/jz4740-battery.h>
+#include <linux/jz4740-adc.h>
+
+struct jz_battery {
+       struct jz_battery_platform_data *pdata;
+       struct platform_device *pdev;
+
+       void __iomem *base;
+
+       int irq;
+       int charge_irq;
+
+       const struct mfd_cell *cell;
+
+       int status;
+       long voltage;
+
+       struct completion read_completion;
+
+       struct power_supply *battery;
+       struct power_supply_desc battery_desc;
+       struct delayed_work work;
+
+       struct mutex lock;
+};
+
+static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy)
+{
+       return power_supply_get_drvdata(psy);
+}
+
+static irqreturn_t jz_battery_irq_handler(int irq, void *devid)
+{
+       struct jz_battery *battery = devid;
+
+       complete(&battery->read_completion);
+       return IRQ_HANDLED;
+}
+
+static long jz_battery_read_voltage(struct jz_battery *battery)
+{
+       long t;
+       unsigned long val;
+       long voltage;
+
+       mutex_lock(&battery->lock);
+
+       reinit_completion(&battery->read_completion);
+
+       enable_irq(battery->irq);
+       battery->cell->enable(battery->pdev);
+
+       t = wait_for_completion_interruptible_timeout(&battery->read_completion,
+               HZ);
+
+       if (t > 0) {
+               val = readw(battery->base) & 0xfff;
+
+               if (battery->pdata->info.voltage_max_design <= 2500000)
+                       val = (val * 78125UL) >> 7UL;
+               else
+                       val = ((val * 924375UL) >> 9UL) + 33000;
+               voltage = (long)val;
+       } else {
+               voltage = t ? t : -ETIMEDOUT;
+       }
+
+       battery->cell->disable(battery->pdev);
+       disable_irq(battery->irq);
+
+       mutex_unlock(&battery->lock);
+
+       return voltage;
+}
+
+static int jz_battery_get_capacity(struct power_supply *psy)
+{
+       struct jz_battery *jz_battery = psy_to_jz_battery(psy);
+       struct power_supply_info *info = &jz_battery->pdata->info;
+       long voltage;
+       int ret;
+       int voltage_span;
+
+       voltage = jz_battery_read_voltage(jz_battery);
+
+       if (voltage < 0)
+               return voltage;
+
+       voltage_span = info->voltage_max_design - info->voltage_min_design;
+       ret = ((voltage - info->voltage_min_design) * 100) / voltage_span;
+
+       if (ret > 100)
+               ret = 100;
+       else if (ret < 0)
+               ret = 0;
+
+       return ret;
+}
+
+static int jz_battery_get_property(struct power_supply *psy,
+       enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct jz_battery *jz_battery = psy_to_jz_battery(psy);
+       struct power_supply_info *info = &jz_battery->pdata->info;
+       long voltage;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = jz_battery->status;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = jz_battery->pdata->info.technology;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               voltage = jz_battery_read_voltage(jz_battery);
+               if (voltage < info->voltage_min_design)
+                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = jz_battery_get_capacity(psy);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = jz_battery_read_voltage(jz_battery);
+               if (val->intval < 0)
+                       return val->intval;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = info->voltage_max_design;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = info->voltage_min_design;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void jz_battery_external_power_changed(struct power_supply *psy)
+{
+       struct jz_battery *jz_battery = psy_to_jz_battery(psy);
+
+       mod_delayed_work(system_wq, &jz_battery->work, 0);
+}
+
+static irqreturn_t jz_battery_charge_irq(int irq, void *data)
+{
+       struct jz_battery *jz_battery = data;
+
+       mod_delayed_work(system_wq, &jz_battery->work, 0);
+
+       return IRQ_HANDLED;
+}
+
+static void jz_battery_update(struct jz_battery *jz_battery)
+{
+       int status;
+       long voltage;
+       bool has_changed = false;
+       int is_charging;
+
+       if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
+               is_charging = gpio_get_value(jz_battery->pdata->gpio_charge);
+               is_charging ^= jz_battery->pdata->gpio_charge_active_low;
+               if (is_charging)
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+               if (status != jz_battery->status) {
+                       jz_battery->status = status;
+                       has_changed = true;
+               }
+       }
+
+       voltage = jz_battery_read_voltage(jz_battery);
+       if (voltage >= 0 && abs(voltage - jz_battery->voltage) > 50000) {
+               jz_battery->voltage = voltage;
+               has_changed = true;
+       }
+
+       if (has_changed)
+               power_supply_changed(jz_battery->battery);
+}
+
+static enum power_supply_property jz_battery_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_PRESENT,
+};
+
+static void jz_battery_work(struct work_struct *work)
+{
+       /* Too small interval will increase system workload */
+       const int interval = HZ * 30;
+       struct jz_battery *jz_battery = container_of(work, struct jz_battery,
+                                           work.work);
+
+       jz_battery_update(jz_battery);
+       schedule_delayed_work(&jz_battery->work, interval);
+}
+
+static int jz_battery_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data;
+       struct power_supply_config psy_cfg = {};
+       struct jz_battery *jz_battery;
+       struct power_supply_desc *battery_desc;
+       struct resource *mem;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "No platform_data supplied\n");
+               return -ENXIO;
+       }
+
+       jz_battery = devm_kzalloc(&pdev->dev, sizeof(*jz_battery), GFP_KERNEL);
+       if (!jz_battery) {
+               dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+               return -ENOMEM;
+       }
+
+       jz_battery->cell = mfd_get_cell(pdev);
+
+       jz_battery->irq = platform_get_irq(pdev, 0);
+       if (jz_battery->irq < 0) {
+               dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
+               return jz_battery->irq;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       jz_battery->base = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(jz_battery->base))
+               return PTR_ERR(jz_battery->base);
+
+       battery_desc = &jz_battery->battery_desc;
+       battery_desc->name = pdata->info.name;
+       battery_desc->type = POWER_SUPPLY_TYPE_BATTERY;
+       battery_desc->properties        = jz_battery_properties;
+       battery_desc->num_properties    = ARRAY_SIZE(jz_battery_properties);
+       battery_desc->get_property      = jz_battery_get_property;
+       battery_desc->external_power_changed =
+                                       jz_battery_external_power_changed;
+       battery_desc->use_for_apm       = 1;
+
+       psy_cfg.drv_data = jz_battery;
+
+       jz_battery->pdata = pdata;
+       jz_battery->pdev = pdev;
+
+       init_completion(&jz_battery->read_completion);
+       mutex_init(&jz_battery->lock);
+
+       INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work);
+
+       ret = request_irq(jz_battery->irq, jz_battery_irq_handler, 0, pdev->name,
+                       jz_battery);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to request irq %d\n", ret);
+               return ret;
+       }
+       disable_irq(jz_battery->irq);
+
+       if (gpio_is_valid(pdata->gpio_charge)) {
+               ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev));
+               if (ret) {
+                       dev_err(&pdev->dev, "charger state gpio request failed.\n");
+                       goto err_free_irq;
+               }
+               ret = gpio_direction_input(pdata->gpio_charge);
+               if (ret) {
+                       dev_err(&pdev->dev, "charger state gpio set direction failed.\n");
+                       goto err_free_gpio;
+               }
+
+               jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge);
+
+               if (jz_battery->charge_irq >= 0) {
+                       ret = request_irq(jz_battery->charge_irq,
+                                   jz_battery_charge_irq,
+                                   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                                   dev_name(&pdev->dev), jz_battery);
+                       if (ret) {
+                               dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret);
+                               goto err_free_gpio;
+                       }
+               }
+       } else {
+               jz_battery->charge_irq = -1;
+       }
+
+       if (jz_battery->pdata->info.voltage_max_design <= 2500000)
+               jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB,
+                       JZ_ADC_CONFIG_BAT_MB);
+       else
+               jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB, 0);
+
+       jz_battery->battery = power_supply_register(&pdev->dev, battery_desc,
+                                                       &psy_cfg);
+       if (IS_ERR(jz_battery->battery)) {
+               dev_err(&pdev->dev, "power supply battery register failed.\n");
+               ret = PTR_ERR(jz_battery->battery);
+               goto err_free_charge_irq;
+       }
+
+       platform_set_drvdata(pdev, jz_battery);
+       schedule_delayed_work(&jz_battery->work, 0);
+
+       return 0;
+
+err_free_charge_irq:
+       if (jz_battery->charge_irq >= 0)
+               free_irq(jz_battery->charge_irq, jz_battery);
+err_free_gpio:
+       if (gpio_is_valid(pdata->gpio_charge))
+               gpio_free(jz_battery->pdata->gpio_charge);
+err_free_irq:
+       free_irq(jz_battery->irq, jz_battery);
+       return ret;
+}
+
+static int jz_battery_remove(struct platform_device *pdev)
+{
+       struct jz_battery *jz_battery = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&jz_battery->work);
+
+       if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
+               if (jz_battery->charge_irq >= 0)
+                       free_irq(jz_battery->charge_irq, jz_battery);
+               gpio_free(jz_battery->pdata->gpio_charge);
+       }
+
+       power_supply_unregister(jz_battery->battery);
+
+       free_irq(jz_battery->irq, jz_battery);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int jz_battery_suspend(struct device *dev)
+{
+       struct jz_battery *jz_battery = dev_get_drvdata(dev);
+
+       cancel_delayed_work_sync(&jz_battery->work);
+       jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       return 0;
+}
+
+static int jz_battery_resume(struct device *dev)
+{
+       struct jz_battery *jz_battery = dev_get_drvdata(dev);
+
+       schedule_delayed_work(&jz_battery->work, 0);
+
+       return 0;
+}
+
+static const struct dev_pm_ops jz_battery_pm_ops = {
+       .suspend        = jz_battery_suspend,
+       .resume         = jz_battery_resume,
+};
+
+#define JZ_BATTERY_PM_OPS (&jz_battery_pm_ops)
+#else
+#define JZ_BATTERY_PM_OPS NULL
+#endif
+
+static struct platform_driver jz_battery_driver = {
+       .probe          = jz_battery_probe,
+       .remove         = jz_battery_remove,
+       .driver = {
+               .name = "jz4740-battery",
+               .pm = JZ_BATTERY_PM_OPS,
+       },
+};
+
+module_platform_driver(jz_battery_driver);
+
+MODULE_ALIAS("platform:jz4740-battery");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("JZ4740 SoC battery driver");
diff --git a/drivers/power/supply/lp8727_charger.c b/drivers/power/supply/lp8727_charger.c
new file mode 100644 (file)
index 0000000..042fb3d
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Driver for LP8727 Micro/Mini USB IC with integrated charger
+ *
+ *                     Copyright (C) 2011 Texas Instruments
+ *                     Copyright (C) 2011 National Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/platform_data/lp8727.h>
+#include <linux/of.h>
+
+#define LP8788_NUM_INTREGS     2
+#define DEFAULT_DEBOUNCE_MSEC  270
+
+/* Registers */
+#define LP8727_CTRL1           0x1
+#define LP8727_CTRL2           0x2
+#define LP8727_SWCTRL          0x3
+#define LP8727_INT1            0x4
+#define LP8727_INT2            0x5
+#define LP8727_STATUS1         0x6
+#define LP8727_STATUS2         0x7
+#define LP8727_CHGCTRL2                0x9
+
+/* CTRL1 register */
+#define LP8727_CP_EN           BIT(0)
+#define LP8727_ADC_EN          BIT(1)
+#define LP8727_ID200_EN                BIT(4)
+
+/* CTRL2 register */
+#define LP8727_CHGDET_EN       BIT(1)
+#define LP8727_INT_EN          BIT(6)
+
+/* SWCTRL register */
+#define LP8727_SW_DM1_DM       (0x0 << 0)
+#define LP8727_SW_DM1_HiZ      (0x7 << 0)
+#define LP8727_SW_DP2_DP       (0x0 << 3)
+#define LP8727_SW_DP2_HiZ      (0x7 << 3)
+
+/* INT1 register */
+#define LP8727_IDNO            (0xF << 0)
+#define LP8727_VBUS            BIT(4)
+
+/* STATUS1 register */
+#define LP8727_CHGSTAT         (3 << 4)
+#define LP8727_CHPORT          BIT(6)
+#define LP8727_DCPORT          BIT(7)
+#define LP8727_STAT_EOC                0x30
+
+/* STATUS2 register */
+#define LP8727_TEMP_STAT       (3 << 5)
+#define LP8727_TEMP_SHIFT      5
+
+/* CHGCTRL2 register */
+#define LP8727_ICHG_SHIFT      4
+
+enum lp8727_dev_id {
+       LP8727_ID_NONE,
+       LP8727_ID_TA,
+       LP8727_ID_DEDICATED_CHG,
+       LP8727_ID_USB_CHG,
+       LP8727_ID_USB_DS,
+       LP8727_ID_MAX,
+};
+
+enum lp8727_die_temp {
+       LP8788_TEMP_75C,
+       LP8788_TEMP_95C,
+       LP8788_TEMP_115C,
+       LP8788_TEMP_135C,
+};
+
+struct lp8727_psy {
+       struct power_supply *ac;
+       struct power_supply *usb;
+       struct power_supply *batt;
+};
+
+struct lp8727_chg {
+       struct device *dev;
+       struct i2c_client *client;
+       struct mutex xfer_lock;
+       struct lp8727_psy *psy;
+       struct lp8727_platform_data *pdata;
+
+       /* Charger Data */
+       enum lp8727_dev_id devid;
+       struct lp8727_chg_param *chg_param;
+
+       /* Interrupt Handling */
+       int irq;
+       struct delayed_work work;
+       unsigned long debounce_jiffies;
+};
+
+static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
+{
+       s32 ret;
+
+       mutex_lock(&pchg->xfer_lock);
+       ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data);
+       mutex_unlock(&pchg->xfer_lock);
+
+       return (ret != len) ? -EIO : 0;
+}
+
+static inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data)
+{
+       return lp8727_read_bytes(pchg, reg, data, 1);
+}
+
+static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data)
+{
+       int ret;
+
+       mutex_lock(&pchg->xfer_lock);
+       ret = i2c_smbus_write_byte_data(pchg->client, reg, data);
+       mutex_unlock(&pchg->xfer_lock);
+
+       return ret;
+}
+
+static bool lp8727_is_charger_attached(const char *name, int id)
+{
+       if (!strcmp(name, "ac"))
+               return id == LP8727_ID_TA || id == LP8727_ID_DEDICATED_CHG;
+       else if (!strcmp(name, "usb"))
+               return id == LP8727_ID_USB_CHG;
+
+       return id >= LP8727_ID_TA && id <= LP8727_ID_USB_CHG;
+}
+
+static int lp8727_init_device(struct lp8727_chg *pchg)
+{
+       u8 val;
+       int ret;
+       u8 intstat[LP8788_NUM_INTREGS];
+
+       /* clear interrupts */
+       ret = lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS);
+       if (ret)
+               return ret;
+
+       val = LP8727_ID200_EN | LP8727_ADC_EN | LP8727_CP_EN;
+       ret = lp8727_write_byte(pchg, LP8727_CTRL1, val);
+       if (ret)
+               return ret;
+
+       val = LP8727_INT_EN | LP8727_CHGDET_EN;
+       return lp8727_write_byte(pchg, LP8727_CTRL2, val);
+}
+
+static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
+{
+       u8 val;
+
+       lp8727_read_byte(pchg, LP8727_STATUS1, &val);
+       return val & LP8727_DCPORT;
+}
+
+static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
+{
+       u8 val;
+
+       lp8727_read_byte(pchg, LP8727_STATUS1, &val);
+       return val & LP8727_CHPORT;
+}
+
+static inline void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
+{
+       lp8727_write_byte(pchg, LP8727_SWCTRL, sw);
+}
+
+static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
+{
+       struct lp8727_platform_data *pdata = pchg->pdata;
+       u8 devid = LP8727_ID_NONE;
+       u8 swctrl = LP8727_SW_DM1_HiZ | LP8727_SW_DP2_HiZ;
+
+       switch (id) {
+       case 0x5:
+               devid = LP8727_ID_TA;
+               pchg->chg_param = pdata ? pdata->ac : NULL;
+               break;
+       case 0xB:
+               if (lp8727_is_dedicated_charger(pchg)) {
+                       pchg->chg_param = pdata ? pdata->ac : NULL;
+                       devid = LP8727_ID_DEDICATED_CHG;
+               } else if (lp8727_is_usb_charger(pchg)) {
+                       pchg->chg_param = pdata ? pdata->usb : NULL;
+                       devid = LP8727_ID_USB_CHG;
+                       swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;
+               } else if (vbusin) {
+                       devid = LP8727_ID_USB_DS;
+                       swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;
+               }
+               break;
+       default:
+               devid = LP8727_ID_NONE;
+               pchg->chg_param = NULL;
+               break;
+       }
+
+       pchg->devid = devid;
+       lp8727_ctrl_switch(pchg, swctrl);
+}
+
+static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
+{
+       u8 val;
+
+       lp8727_read_byte(pchg, LP8727_CTRL2, &val);
+       val |= LP8727_CHGDET_EN;
+       lp8727_write_byte(pchg, LP8727_CTRL2, val);
+}
+
+static void lp8727_delayed_func(struct work_struct *_work)
+{
+       struct lp8727_chg *pchg = container_of(_work, struct lp8727_chg,
+                                               work.work);
+       u8 intstat[LP8788_NUM_INTREGS];
+       u8 idno;
+       u8 vbus;
+
+       if (lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS)) {
+               dev_err(pchg->dev, "can not read INT registers\n");
+               return;
+       }
+
+       idno = intstat[0] & LP8727_IDNO;
+       vbus = intstat[0] & LP8727_VBUS;
+
+       lp8727_id_detection(pchg, idno, vbus);
+       lp8727_enable_chgdet(pchg);
+
+       power_supply_changed(pchg->psy->ac);
+       power_supply_changed(pchg->psy->usb);
+       power_supply_changed(pchg->psy->batt);
+}
+
+static irqreturn_t lp8727_isr_func(int irq, void *ptr)
+{
+       struct lp8727_chg *pchg = ptr;
+
+       schedule_delayed_work(&pchg->work, pchg->debounce_jiffies);
+       return IRQ_HANDLED;
+}
+
+static int lp8727_setup_irq(struct lp8727_chg *pchg)
+{
+       int ret;
+       int irq = pchg->client->irq;
+       unsigned delay_msec = pchg->pdata ? pchg->pdata->debounce_msec :
+                                               DEFAULT_DEBOUNCE_MSEC;
+
+       INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
+
+       if (irq <= 0) {
+               dev_warn(pchg->dev, "invalid irq number: %d\n", irq);
+               return 0;
+       }
+
+       ret = request_threaded_irq(irq, NULL, lp8727_isr_func,
+                               IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                               "lp8727_irq", pchg);
+
+       if (ret)
+               return ret;
+
+       pchg->irq = irq;
+       pchg->debounce_jiffies = msecs_to_jiffies(delay_msec);
+
+       return 0;
+}
+
+static void lp8727_release_irq(struct lp8727_chg *pchg)
+{
+       cancel_delayed_work_sync(&pchg->work);
+
+       if (pchg->irq)
+               free_irq(pchg->irq, pchg);
+}
+
+static enum power_supply_property lp8727_charger_prop[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property lp8727_battery_prop[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static char *battery_supplied_to[] = {
+       "main_batt",
+};
+
+static int lp8727_charger_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);
+
+       if (psp != POWER_SUPPLY_PROP_ONLINE)
+               return -EINVAL;
+
+       val->intval = lp8727_is_charger_attached(psy->desc->name, pchg->devid);
+
+       return 0;
+}
+
+static bool lp8727_is_high_temperature(enum lp8727_die_temp temp)
+{
+       switch (temp) {
+       case LP8788_TEMP_95C:
+       case LP8788_TEMP_115C:
+       case LP8788_TEMP_135C:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static int lp8727_battery_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);
+       struct lp8727_platform_data *pdata = pchg->pdata;
+       enum lp8727_die_temp temp;
+       u8 read;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid)) {
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+                       return 0;
+               }
+
+               lp8727_read_byte(pchg, LP8727_STATUS1, &read);
+
+               val->intval = (read & LP8727_CHGSTAT) == LP8727_STAT_EOC ?
+                               POWER_SUPPLY_STATUS_FULL :
+                               POWER_SUPPLY_STATUS_CHARGING;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               lp8727_read_byte(pchg, LP8727_STATUS2, &read);
+               temp = (read & LP8727_TEMP_STAT) >> LP8727_TEMP_SHIFT;
+
+               val->intval = lp8727_is_high_temperature(temp) ?
+                       POWER_SUPPLY_HEALTH_OVERHEAT :
+                       POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               if (!pdata)
+                       return -EINVAL;
+
+               if (pdata->get_batt_present)
+                       val->intval = pdata->get_batt_present();
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (!pdata)
+                       return -EINVAL;
+
+               if (pdata->get_batt_level)
+                       val->intval = pdata->get_batt_level();
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (!pdata)
+                       return -EINVAL;
+
+               if (pdata->get_batt_capacity)
+                       val->intval = pdata->get_batt_capacity();
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               if (!pdata)
+                       return -EINVAL;
+
+               if (pdata->get_batt_temp)
+                       val->intval = pdata->get_batt_temp();
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static void lp8727_charger_changed(struct power_supply *psy)
+{
+       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev.parent);
+       u8 eoc_level;
+       u8 ichg;
+       u8 val;
+
+       /* skip if no charger exists */
+       if (!lp8727_is_charger_attached(psy->desc->name, pchg->devid))
+               return;
+
+       /* update charging parameters */
+       if (pchg->chg_param) {
+               eoc_level = pchg->chg_param->eoc_level;
+               ichg = pchg->chg_param->ichg;
+               val = (ichg << LP8727_ICHG_SHIFT) | eoc_level;
+               lp8727_write_byte(pchg, LP8727_CHGCTRL2, val);
+       }
+}
+
+static const struct power_supply_desc lp8727_ac_desc = {
+       .name                   = "ac",
+       .type                   = POWER_SUPPLY_TYPE_MAINS,
+       .properties             = lp8727_charger_prop,
+       .num_properties         = ARRAY_SIZE(lp8727_charger_prop),
+       .get_property           = lp8727_charger_get_property,
+};
+
+static const struct power_supply_desc lp8727_usb_desc = {
+       .name                   = "usb",
+       .type                   = POWER_SUPPLY_TYPE_USB,
+       .properties             = lp8727_charger_prop,
+       .num_properties         = ARRAY_SIZE(lp8727_charger_prop),
+       .get_property           = lp8727_charger_get_property,
+};
+
+static const struct power_supply_desc lp8727_batt_desc = {
+       .name                   = "main_batt",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = lp8727_battery_prop,
+       .num_properties         = ARRAY_SIZE(lp8727_battery_prop),
+       .get_property           = lp8727_battery_get_property,
+       .external_power_changed = lp8727_charger_changed,
+};
+
+static int lp8727_register_psy(struct lp8727_chg *pchg)
+{
+       struct power_supply_config psy_cfg = {}; /* Only for ac and usb */
+       struct lp8727_psy *psy;
+
+       psy = devm_kzalloc(pchg->dev, sizeof(*psy), GFP_KERNEL);
+       if (!psy)
+               return -ENOMEM;
+
+       pchg->psy = psy;
+
+       psy_cfg.supplied_to = battery_supplied_to;
+       psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
+
+       psy->ac = power_supply_register(pchg->dev, &lp8727_ac_desc, &psy_cfg);
+       if (IS_ERR(psy->ac))
+               goto err_psy_ac;
+
+       psy->usb = power_supply_register(pchg->dev, &lp8727_usb_desc,
+                                        &psy_cfg);
+       if (IS_ERR(psy->usb))
+               goto err_psy_usb;
+
+       psy->batt = power_supply_register(pchg->dev, &lp8727_batt_desc, NULL);
+       if (IS_ERR(psy->batt))
+               goto err_psy_batt;
+
+       return 0;
+
+err_psy_batt:
+       power_supply_unregister(psy->usb);
+err_psy_usb:
+       power_supply_unregister(psy->ac);
+err_psy_ac:
+       return -EPERM;
+}
+
+static void lp8727_unregister_psy(struct lp8727_chg *pchg)
+{
+       struct lp8727_psy *psy = pchg->psy;
+
+       if (!psy)
+               return;
+
+       power_supply_unregister(psy->ac);
+       power_supply_unregister(psy->usb);
+       power_supply_unregister(psy->batt);
+}
+
+#ifdef CONFIG_OF
+static struct lp8727_chg_param
+*lp8727_parse_charge_pdata(struct device *dev, struct device_node *np)
+{
+       struct lp8727_chg_param *param;
+
+       param = devm_kzalloc(dev, sizeof(*param), GFP_KERNEL);
+       if (!param)
+               goto out;
+
+       of_property_read_u8(np, "eoc-level", (u8 *)&param->eoc_level);
+       of_property_read_u8(np, "charging-current", (u8 *)&param->ichg);
+out:
+       return param;
+}
+
+static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct device_node *child;
+       struct lp8727_platform_data *pdata;
+       const char *type;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       of_property_read_u32(np, "debounce-ms", &pdata->debounce_msec);
+
+       /* If charging parameter is not defined, just skip parsing the dt */
+       if (of_get_child_count(np) == 0)
+               return pdata;
+
+       for_each_child_of_node(np, child) {
+               of_property_read_string(child, "charger-type", &type);
+
+               if (!strcmp(type, "ac"))
+                       pdata->ac = lp8727_parse_charge_pdata(dev, child);
+
+               if (!strcmp(type, "usb"))
+                       pdata->usb = lp8727_parse_charge_pdata(dev, child);
+       }
+
+       return pdata;
+}
+#else
+static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)
+{
+       return NULL;
+}
+#endif
+
+static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
+{
+       struct lp8727_chg *pchg;
+       struct lp8727_platform_data *pdata;
+       int ret;
+
+       if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+               return -EIO;
+
+       if (cl->dev.of_node) {
+               pdata = lp8727_parse_dt(&cl->dev);
+               if (IS_ERR(pdata))
+                       return PTR_ERR(pdata);
+       } else {
+               pdata = dev_get_platdata(&cl->dev);
+       }
+
+       pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL);
+       if (!pchg)
+               return -ENOMEM;
+
+       pchg->client = cl;
+       pchg->dev = &cl->dev;
+       pchg->pdata = pdata;
+       i2c_set_clientdata(cl, pchg);
+
+       mutex_init(&pchg->xfer_lock);
+
+       ret = lp8727_init_device(pchg);
+       if (ret) {
+               dev_err(pchg->dev, "i2c communication err: %d", ret);
+               return ret;
+       }
+
+       ret = lp8727_register_psy(pchg);
+       if (ret) {
+               dev_err(pchg->dev, "power supplies register err: %d", ret);
+               return ret;
+       }
+
+       ret = lp8727_setup_irq(pchg);
+       if (ret) {
+               dev_err(pchg->dev, "irq handler err: %d", ret);
+               lp8727_unregister_psy(pchg);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int lp8727_remove(struct i2c_client *cl)
+{
+       struct lp8727_chg *pchg = i2c_get_clientdata(cl);
+
+       lp8727_release_irq(pchg);
+       lp8727_unregister_psy(pchg);
+       return 0;
+}
+
+static const struct of_device_id lp8727_dt_ids[] = {
+       { .compatible = "ti,lp8727", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lp8727_dt_ids);
+
+static const struct i2c_device_id lp8727_ids[] = {
+       {"lp8727", 0},
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, lp8727_ids);
+
+static struct i2c_driver lp8727_driver = {
+       .driver = {
+                  .name = "lp8727",
+                  .of_match_table = of_match_ptr(lp8727_dt_ids),
+                  },
+       .probe = lp8727_probe,
+       .remove = lp8727_remove,
+       .id_table = lp8727_ids,
+};
+module_i2c_driver(lp8727_driver);
+
+MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver");
+MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c
new file mode 100644 (file)
index 0000000..7321b72
--- /dev/null
@@ -0,0 +1,764 @@
+/*
+ * TI LP8788 MFD - battery charger driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/iio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+/* register address */
+#define LP8788_CHG_STATUS              0x07
+#define LP8788_CHG_IDCIN               0x13
+#define LP8788_CHG_IBATT               0x14
+#define LP8788_CHG_VTERM               0x15
+#define LP8788_CHG_EOC                 0x16
+
+/* mask/shift bits */
+#define LP8788_CHG_INPUT_STATE_M       0x03    /* Addr 07h */
+#define LP8788_CHG_STATE_M             0x3C
+#define LP8788_CHG_STATE_S             2
+#define LP8788_NO_BATT_M               BIT(6)
+#define LP8788_BAD_BATT_M              BIT(7)
+#define LP8788_CHG_IBATT_M             0x1F    /* Addr 14h */
+#define LP8788_CHG_VTERM_M             0x0F    /* Addr 15h */
+#define LP8788_CHG_EOC_LEVEL_M         0x30    /* Addr 16h */
+#define LP8788_CHG_EOC_LEVEL_S         4
+#define LP8788_CHG_EOC_TIME_M          0x0E
+#define LP8788_CHG_EOC_TIME_S          1
+#define LP8788_CHG_EOC_MODE_M          BIT(0)
+
+#define LP8788_CHARGER_NAME            "charger"
+#define LP8788_BATTERY_NAME            "main_batt"
+
+#define LP8788_CHG_START               0x11
+#define LP8788_CHG_END                 0x1C
+
+#define LP8788_ISEL_MAX                        23
+#define LP8788_ISEL_STEP               50
+#define LP8788_VTERM_MIN               4100
+#define LP8788_VTERM_STEP              25
+#define LP8788_MAX_BATT_CAPACITY       100
+#define LP8788_MAX_CHG_IRQS            11
+
+enum lp8788_charging_state {
+       LP8788_OFF,
+       LP8788_WARM_UP,
+       LP8788_LOW_INPUT = 0x3,
+       LP8788_PRECHARGE,
+       LP8788_CC,
+       LP8788_CV,
+       LP8788_MAINTENANCE,
+       LP8788_BATTERY_FAULT,
+       LP8788_SYSTEM_SUPPORT = 0xC,
+       LP8788_HIGH_CURRENT = 0xF,
+       LP8788_MAX_CHG_STATE,
+};
+
+enum lp8788_charger_adc_sel {
+       LP8788_VBATT,
+       LP8788_BATT_TEMP,
+       LP8788_NUM_CHG_ADC,
+};
+
+enum lp8788_charger_input_state {
+       LP8788_SYSTEM_SUPPLY = 1,
+       LP8788_FULL_FUNCTION,
+};
+
+/*
+ * struct lp8788_chg_irq
+ * @which        : lp8788 interrupt id
+ * @virq         : Linux IRQ number from irq_domain
+ */
+struct lp8788_chg_irq {
+       enum lp8788_int_id which;
+       int virq;
+};
+
+/*
+ * struct lp8788_charger
+ * @lp           : used for accessing the registers of mfd lp8788 device
+ * @charger      : power supply driver for the battery charger
+ * @battery      : power supply driver for the battery
+ * @charger_work : work queue for charger input interrupts
+ * @chan         : iio channels for getting adc values
+ *                 eg) battery voltage, capacity and temperature
+ * @irqs         : charger dedicated interrupts
+ * @num_irqs     : total numbers of charger interrupts
+ * @pdata        : charger platform specific data
+ */
+struct lp8788_charger {
+       struct lp8788 *lp;
+       struct power_supply *charger;
+       struct power_supply *battery;
+       struct work_struct charger_work;
+       struct iio_channel *chan[LP8788_NUM_CHG_ADC];
+       struct lp8788_chg_irq irqs[LP8788_MAX_CHG_IRQS];
+       int num_irqs;
+       struct lp8788_charger_platform_data *pdata;
+};
+
+static char *battery_supplied_to[] = {
+       LP8788_BATTERY_NAME,
+};
+
+static enum power_supply_property lp8788_charger_prop[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static enum power_supply_property lp8788_battery_prop[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static bool lp8788_is_charger_detected(struct lp8788_charger *pchg)
+{
+       u8 data;
+
+       lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
+       data &= LP8788_CHG_INPUT_STATE_M;
+
+       return data == LP8788_SYSTEM_SUPPLY || data == LP8788_FULL_FUNCTION;
+}
+
+static int lp8788_charger_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct lp8788_charger *pchg = dev_get_drvdata(psy->dev.parent);
+       u8 read;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = lp8788_is_charger_detected(pchg);
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               lp8788_read_byte(pchg->lp, LP8788_CHG_IDCIN, &read);
+               val->intval = LP8788_ISEL_STEP *
+                               (min_t(int, read, LP8788_ISEL_MAX) + 1);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int lp8788_get_battery_status(struct lp8788_charger *pchg,
+                               union power_supply_propval *val)
+{
+       enum lp8788_charging_state state;
+       u8 data;
+       int ret;
+
+       ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
+       if (ret)
+               return ret;
+
+       state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
+       switch (state) {
+       case LP8788_OFF:
+               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       case LP8788_PRECHARGE:
+       case LP8788_CC:
+       case LP8788_CV:
+       case LP8788_HIGH_CURRENT:
+               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               break;
+       case LP8788_MAINTENANCE:
+               val->intval = POWER_SUPPLY_STATUS_FULL;
+               break;
+       default:
+               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               break;
+       }
+
+       return 0;
+}
+
+static int lp8788_get_battery_health(struct lp8788_charger *pchg,
+                               union power_supply_propval *val)
+{
+       u8 data;
+       int ret;
+
+       ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
+       if (ret)
+               return ret;
+
+       if (data & LP8788_NO_BATT_M)
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+       else if (data & LP8788_BAD_BATT_M)
+               val->intval = POWER_SUPPLY_HEALTH_DEAD;
+       else
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+       return 0;
+}
+
+static int lp8788_get_battery_present(struct lp8788_charger *pchg,
+                               union power_supply_propval *val)
+{
+       u8 data;
+       int ret;
+
+       ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
+       if (ret)
+               return ret;
+
+       val->intval = !(data & LP8788_NO_BATT_M);
+       return 0;
+}
+
+static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, int *result)
+{
+       struct iio_channel *channel = pchg->chan[LP8788_VBATT];
+
+       if (!channel)
+               return -EINVAL;
+
+       return iio_read_channel_processed(channel, result);
+}
+
+static int lp8788_get_battery_voltage(struct lp8788_charger *pchg,
+                               union power_supply_propval *val)
+{
+       return lp8788_get_vbatt_adc(pchg, &val->intval);
+}
+
+static int lp8788_get_battery_capacity(struct lp8788_charger *pchg,
+                               union power_supply_propval *val)
+{
+       struct lp8788 *lp = pchg->lp;
+       struct lp8788_charger_platform_data *pdata = pchg->pdata;
+       unsigned int max_vbatt;
+       int vbatt;
+       enum lp8788_charging_state state;
+       u8 data;
+       int ret;
+
+       if (!pdata)
+               return -EINVAL;
+
+       max_vbatt = pdata->max_vbatt_mv;
+       if (max_vbatt == 0)
+               return -EINVAL;
+
+       ret = lp8788_read_byte(lp, LP8788_CHG_STATUS, &data);
+       if (ret)
+               return ret;
+
+       state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
+
+       if (state == LP8788_MAINTENANCE) {
+               val->intval = LP8788_MAX_BATT_CAPACITY;
+       } else {
+               ret = lp8788_get_vbatt_adc(pchg, &vbatt);
+               if (ret)
+                       return ret;
+
+               val->intval = (vbatt * LP8788_MAX_BATT_CAPACITY) / max_vbatt;
+               val->intval = min(val->intval, LP8788_MAX_BATT_CAPACITY);
+       }
+
+       return 0;
+}
+
+static int lp8788_get_battery_temperature(struct lp8788_charger *pchg,
+                               union power_supply_propval *val)
+{
+       struct iio_channel *channel = pchg->chan[LP8788_BATT_TEMP];
+       int result;
+       int ret;
+
+       if (!channel)
+               return -EINVAL;
+
+       ret = iio_read_channel_processed(channel, &result);
+       if (ret < 0)
+               return -EINVAL;
+
+       /* unit: 0.1 'C */
+       val->intval = result * 10;
+
+       return 0;
+}
+
+static int lp8788_get_battery_charging_current(struct lp8788_charger *pchg,
+                               union power_supply_propval *val)
+{
+       u8 read;
+
+       lp8788_read_byte(pchg->lp, LP8788_CHG_IBATT, &read);
+       read &= LP8788_CHG_IBATT_M;
+       val->intval = LP8788_ISEL_STEP *
+                       (min_t(int, read, LP8788_ISEL_MAX) + 1);
+
+       return 0;
+}
+
+static int lp8788_get_charging_termination_voltage(struct lp8788_charger *pchg,
+                               union power_supply_propval *val)
+{
+       u8 read;
+
+       lp8788_read_byte(pchg->lp, LP8788_CHG_VTERM, &read);
+       read &= LP8788_CHG_VTERM_M;
+       val->intval = LP8788_VTERM_MIN + LP8788_VTERM_STEP * read;
+
+       return 0;
+}
+
+static int lp8788_battery_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct lp8788_charger *pchg = dev_get_drvdata(psy->dev.parent);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               return lp8788_get_battery_status(pchg, val);
+       case POWER_SUPPLY_PROP_HEALTH:
+               return lp8788_get_battery_health(pchg, val);
+       case POWER_SUPPLY_PROP_PRESENT:
+               return lp8788_get_battery_present(pchg, val);
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               return lp8788_get_battery_voltage(pchg, val);
+       case POWER_SUPPLY_PROP_CAPACITY:
+               return lp8788_get_battery_capacity(pchg, val);
+       case POWER_SUPPLY_PROP_TEMP:
+               return lp8788_get_battery_temperature(pchg, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               return lp8788_get_battery_charging_current(pchg, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               return lp8788_get_charging_termination_voltage(pchg, val);
+       default:
+               return -EINVAL;
+       }
+}
+
+static inline bool lp8788_is_valid_charger_register(u8 addr)
+{
+       return addr >= LP8788_CHG_START && addr <= LP8788_CHG_END;
+}
+
+static int lp8788_update_charger_params(struct platform_device *pdev,
+                                       struct lp8788_charger *pchg)
+{
+       struct lp8788 *lp = pchg->lp;
+       struct lp8788_charger_platform_data *pdata = pchg->pdata;
+       struct lp8788_chg_param *param;
+       int i;
+       int ret;
+
+       if (!pdata || !pdata->chg_params) {
+               dev_info(&pdev->dev, "skip updating charger parameters\n");
+               return 0;
+       }
+
+       /* settting charging parameters */
+       for (i = 0; i < pdata->num_chg_params; i++) {
+               param = pdata->chg_params + i;
+
+               if (!param)
+                       continue;
+
+               if (lp8788_is_valid_charger_register(param->addr)) {
+                       ret = lp8788_write_byte(lp, param->addr, param->val);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct power_supply_desc lp8788_psy_charger_desc = {
+       .name           = LP8788_CHARGER_NAME,
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+       .properties     = lp8788_charger_prop,
+       .num_properties = ARRAY_SIZE(lp8788_charger_prop),
+       .get_property   = lp8788_charger_get_property,
+};
+
+static const struct power_supply_desc lp8788_psy_battery_desc = {
+       .name           = LP8788_BATTERY_NAME,
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = lp8788_battery_prop,
+       .num_properties = ARRAY_SIZE(lp8788_battery_prop),
+       .get_property   = lp8788_battery_get_property,
+};
+
+static int lp8788_psy_register(struct platform_device *pdev,
+                               struct lp8788_charger *pchg)
+{
+       struct power_supply_config charger_cfg = {};
+
+       charger_cfg.supplied_to = battery_supplied_to;
+       charger_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to);
+
+       pchg->charger = power_supply_register(&pdev->dev,
+                                             &lp8788_psy_charger_desc,
+                                             &charger_cfg);
+       if (IS_ERR(pchg->charger))
+               return -EPERM;
+
+       pchg->battery = power_supply_register(&pdev->dev,
+                                             &lp8788_psy_battery_desc, NULL);
+       if (IS_ERR(pchg->battery)) {
+               power_supply_unregister(pchg->charger);
+               return -EPERM;
+       }
+
+       return 0;
+}
+
+static void lp8788_psy_unregister(struct lp8788_charger *pchg)
+{
+       power_supply_unregister(pchg->battery);
+       power_supply_unregister(pchg->charger);
+}
+
+static void lp8788_charger_event(struct work_struct *work)
+{
+       struct lp8788_charger *pchg =
+               container_of(work, struct lp8788_charger, charger_work);
+       struct lp8788_charger_platform_data *pdata = pchg->pdata;
+       enum lp8788_charger_event event = lp8788_is_charger_detected(pchg);
+
+       pdata->charger_event(pchg->lp, event);
+}
+
+static bool lp8788_find_irq_id(struct lp8788_charger *pchg, int virq, int *id)
+{
+       bool found = false;
+       int i;
+
+       for (i = 0; i < pchg->num_irqs; i++) {
+               if (pchg->irqs[i].virq == virq) {
+                       *id = pchg->irqs[i].which;
+                       found = true;
+                       break;
+               }
+       }
+
+       return found;
+}
+
+static irqreturn_t lp8788_charger_irq_thread(int virq, void *ptr)
+{
+       struct lp8788_charger *pchg = ptr;
+       struct lp8788_charger_platform_data *pdata = pchg->pdata;
+       int id = -1;
+
+       if (!lp8788_find_irq_id(pchg, virq, &id))
+               return IRQ_NONE;
+
+       switch (id) {
+       case LP8788_INT_CHG_INPUT_STATE:
+       case LP8788_INT_CHG_STATE:
+       case LP8788_INT_EOC:
+       case LP8788_INT_BATT_LOW:
+       case LP8788_INT_NO_BATT:
+               power_supply_changed(pchg->charger);
+               power_supply_changed(pchg->battery);
+               break;
+       default:
+               break;
+       }
+
+       /* report charger dectection event if used */
+       if (!pdata)
+               goto irq_handled;
+
+       if (pdata->charger_event && id == LP8788_INT_CHG_INPUT_STATE)
+               schedule_work(&pchg->charger_work);
+
+irq_handled:
+       return IRQ_HANDLED;
+}
+
+static int lp8788_set_irqs(struct platform_device *pdev,
+                       struct lp8788_charger *pchg, const char *name)
+{
+       struct resource *r;
+       struct irq_domain *irqdm = pchg->lp->irqdm;
+       int irq_start;
+       int irq_end;
+       int virq;
+       int nr_irq;
+       int i;
+       int ret;
+
+       /* no error even if no irq resource */
+       r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, name);
+       if (!r)
+               return 0;
+
+       irq_start = r->start;
+       irq_end = r->end;
+
+       for (i = irq_start; i <= irq_end; i++) {
+               nr_irq = pchg->num_irqs;
+
+               virq = irq_create_mapping(irqdm, i);
+               pchg->irqs[nr_irq].virq = virq;
+               pchg->irqs[nr_irq].which = i;
+               pchg->num_irqs++;
+
+               ret = request_threaded_irq(virq, NULL,
+                                       lp8788_charger_irq_thread,
+                                       0, name, pchg);
+               if (ret)
+                       break;
+       }
+
+       if (i <= irq_end)
+               goto err_free_irq;
+
+       return 0;
+
+err_free_irq:
+       for (i = 0; i < pchg->num_irqs; i++)
+               free_irq(pchg->irqs[i].virq, pchg);
+       return ret;
+}
+
+static int lp8788_irq_register(struct platform_device *pdev,
+                               struct lp8788_charger *pchg)
+{
+       const char *name[] = {
+               LP8788_CHG_IRQ, LP8788_PRSW_IRQ, LP8788_BATT_IRQ
+       };
+       int i;
+       int ret;
+
+       INIT_WORK(&pchg->charger_work, lp8788_charger_event);
+       pchg->num_irqs = 0;
+
+       for (i = 0; i < ARRAY_SIZE(name); i++) {
+               ret = lp8788_set_irqs(pdev, pchg, name[i]);
+               if (ret) {
+                       dev_warn(&pdev->dev, "irq setup failed: %s\n", name[i]);
+                       return ret;
+               }
+       }
+
+       if (pchg->num_irqs > LP8788_MAX_CHG_IRQS) {
+               dev_err(&pdev->dev, "invalid total number of irqs: %d\n",
+                       pchg->num_irqs);
+               return -EINVAL;
+       }
+
+
+       return 0;
+}
+
+static void lp8788_irq_unregister(struct platform_device *pdev,
+                                 struct lp8788_charger *pchg)
+{
+       int i;
+       int irq;
+
+       for (i = 0; i < pchg->num_irqs; i++) {
+               irq = pchg->irqs[i].virq;
+               if (!irq)
+                       continue;
+
+               free_irq(irq, pchg);
+       }
+}
+
+static void lp8788_setup_adc_channel(struct device *dev,
+                               struct lp8788_charger *pchg)
+{
+       struct lp8788_charger_platform_data *pdata = pchg->pdata;
+       struct iio_channel *chan;
+
+       if (!pdata)
+               return;
+
+       /* ADC channel for battery voltage */
+       chan = iio_channel_get(dev, pdata->adc_vbatt);
+       pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
+
+       /* ADC channel for battery temperature */
+       chan = iio_channel_get(dev, pdata->adc_batt_temp);
+       pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
+}
+
+static void lp8788_release_adc_channel(struct lp8788_charger *pchg)
+{
+       int i;
+
+       for (i = 0; i < LP8788_NUM_CHG_ADC; i++) {
+               if (!pchg->chan[i])
+                       continue;
+
+               iio_channel_release(pchg->chan[i]);
+               pchg->chan[i] = NULL;
+       }
+}
+
+static ssize_t lp8788_show_charger_status(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lp8788_charger *pchg = dev_get_drvdata(dev);
+       enum lp8788_charging_state state;
+       char *desc[LP8788_MAX_CHG_STATE] = {
+               [LP8788_OFF] = "CHARGER OFF",
+               [LP8788_WARM_UP] = "WARM UP",
+               [LP8788_LOW_INPUT] = "LOW INPUT STATE",
+               [LP8788_PRECHARGE] = "CHARGING - PRECHARGE",
+               [LP8788_CC] = "CHARGING - CC",
+               [LP8788_CV] = "CHARGING - CV",
+               [LP8788_MAINTENANCE] = "NO CHARGING - MAINTENANCE",
+               [LP8788_BATTERY_FAULT] = "BATTERY FAULT",
+               [LP8788_SYSTEM_SUPPORT] = "SYSTEM SUPPORT",
+               [LP8788_HIGH_CURRENT] = "HIGH CURRENT",
+       };
+       u8 data;
+
+       lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
+       state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n", desc[state]);
+}
+
+static ssize_t lp8788_show_eoc_time(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lp8788_charger *pchg = dev_get_drvdata(dev);
+       char *stime[] = { "400ms", "5min", "10min", "15min",
+                       "20min", "25min", "30min" "No timeout" };
+       u8 val;
+
+       lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
+       val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S;
+
+       return scnprintf(buf, PAGE_SIZE, "End Of Charge Time: %s\n",
+                       stime[val]);
+}
+
+static ssize_t lp8788_show_eoc_level(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct lp8788_charger *pchg = dev_get_drvdata(dev);
+       char *abs_level[] = { "25mA", "49mA", "75mA", "98mA" };
+       char *relative_level[] = { "5%", "10%", "15%", "20%" };
+       char *level;
+       u8 val;
+       u8 mode;
+
+       lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
+
+       mode = val & LP8788_CHG_EOC_MODE_M;
+       val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S;
+       level = mode ? abs_level[val] : relative_level[val];
+
+       return scnprintf(buf, PAGE_SIZE, "End Of Charge Level: %s\n", level);
+}
+
+static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL);
+static DEVICE_ATTR(eoc_time, S_IRUSR, lp8788_show_eoc_time, NULL);
+static DEVICE_ATTR(eoc_level, S_IRUSR, lp8788_show_eoc_level, NULL);
+
+static struct attribute *lp8788_charger_attr[] = {
+       &dev_attr_charger_status.attr,
+       &dev_attr_eoc_time.attr,
+       &dev_attr_eoc_level.attr,
+       NULL,
+};
+
+static const struct attribute_group lp8788_attr_group = {
+       .attrs = lp8788_charger_attr,
+};
+
+static int lp8788_charger_probe(struct platform_device *pdev)
+{
+       struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
+       struct lp8788_charger *pchg;
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       pchg = devm_kzalloc(dev, sizeof(struct lp8788_charger), GFP_KERNEL);
+       if (!pchg)
+               return -ENOMEM;
+
+       pchg->lp = lp;
+       pchg->pdata = lp->pdata ? lp->pdata->chg_pdata : NULL;
+       platform_set_drvdata(pdev, pchg);
+
+       ret = lp8788_update_charger_params(pdev, pchg);
+       if (ret)
+               return ret;
+
+       lp8788_setup_adc_channel(&pdev->dev, pchg);
+
+       ret = lp8788_psy_register(pdev, pchg);
+       if (ret)
+               return ret;
+
+       ret = sysfs_create_group(&pdev->dev.kobj, &lp8788_attr_group);
+       if (ret) {
+               lp8788_psy_unregister(pchg);
+               return ret;
+       }
+
+       ret = lp8788_irq_register(pdev, pchg);
+       if (ret)
+               dev_warn(dev, "failed to register charger irq: %d\n", ret);
+
+       return 0;
+}
+
+static int lp8788_charger_remove(struct platform_device *pdev)
+{
+       struct lp8788_charger *pchg = platform_get_drvdata(pdev);
+
+       flush_work(&pchg->charger_work);
+       lp8788_irq_unregister(pdev, pchg);
+       sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group);
+       lp8788_psy_unregister(pchg);
+       lp8788_release_adc_channel(pchg);
+
+       return 0;
+}
+
+static struct platform_driver lp8788_charger_driver = {
+       .probe = lp8788_charger_probe,
+       .remove = lp8788_charger_remove,
+       .driver = {
+               .name = LP8788_DEV_CHARGER,
+       },
+};
+module_platform_driver(lp8788_charger_driver);
+
+MODULE_DESCRIPTION("TI LP8788 Charger Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp8788-charger");
diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c
new file mode 100644 (file)
index 0000000..4adf2ba
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * I2C client/driver for the Linear Technology LTC2941 and LTC2943
+ * Battery Gas Gauge IC
+ *
+ * Copyright (C) 2014 Topic Embedded Systems
+ *
+ * Author: Auryn Verwegen
+ * Author: Mike Looijmans
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/swab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#define I16_MSB(x)                     ((x >> 8) & 0xFF)
+#define I16_LSB(x)                     (x & 0xFF)
+
+#define LTC294X_WORK_DELAY             10      /* Update delay in seconds */
+
+#define LTC294X_MAX_VALUE              0xFFFF
+#define LTC294X_MID_SUPPLY             0x7FFF
+
+#define LTC2941_MAX_PRESCALER_EXP      7
+#define LTC2943_MAX_PRESCALER_EXP      6
+
+enum ltc294x_reg {
+       LTC294X_REG_STATUS              = 0x00,
+       LTC294X_REG_CONTROL             = 0x01,
+       LTC294X_REG_ACC_CHARGE_MSB      = 0x02,
+       LTC294X_REG_ACC_CHARGE_LSB      = 0x03,
+       LTC294X_REG_THRESH_HIGH_MSB     = 0x04,
+       LTC294X_REG_THRESH_HIGH_LSB     = 0x05,
+       LTC294X_REG_THRESH_LOW_MSB      = 0x06,
+       LTC294X_REG_THRESH_LOW_LSB      = 0x07,
+       LTC294X_REG_VOLTAGE_MSB = 0x08,
+       LTC294X_REG_VOLTAGE_LSB = 0x09,
+       LTC294X_REG_CURRENT_MSB = 0x0E,
+       LTC294X_REG_CURRENT_LSB = 0x0F,
+       LTC294X_REG_TEMPERATURE_MSB     = 0x14,
+       LTC294X_REG_TEMPERATURE_LSB     = 0x15,
+};
+
+#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6))
+#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)
+#define LTC294X_REG_CONTROL_PRESCALER_MASK     (BIT(5) | BIT(4) | BIT(3))
+#define LTC294X_REG_CONTROL_SHUTDOWN_MASK      (BIT(0))
+#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \
+       ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)
+#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED       0
+
+#define LTC2941_NUM_REGS       0x08
+#define LTC2943_NUM_REGS       0x18
+
+struct ltc294x_info {
+       struct i2c_client *client;      /* I2C Client pointer */
+       struct power_supply *supply;    /* Supply pointer */
+       struct power_supply_desc supply_desc;   /* Supply description */
+       struct delayed_work work;       /* Work scheduler */
+       int num_regs;   /* Number of registers (chip type) */
+       int charge;     /* Last charge register content */
+       int r_sense;    /* mOhm */
+       int Qlsb;       /* nAh */
+};
+
+static inline int convert_bin_to_uAh(
+       const struct ltc294x_info *info, int Q)
+{
+       return ((Q * (info->Qlsb / 10))) / 100;
+}
+
+static inline int convert_uAh_to_bin(
+       const struct ltc294x_info *info, int uAh)
+{
+       int Q;
+
+       Q = (uAh * 100) / (info->Qlsb/10);
+       return (Q < LTC294X_MAX_VALUE) ? Q : LTC294X_MAX_VALUE;
+}
+
+static int ltc294x_read_regs(struct i2c_client *client,
+       enum ltc294x_reg reg, u8 *buf, int num_regs)
+{
+       int ret;
+       struct i2c_msg msgs[2] = { };
+       u8 reg_start = reg;
+
+       msgs[0].addr    = client->addr;
+       msgs[0].len     = 1;
+       msgs[0].buf     = &reg_start;
+
+       msgs[1].addr    = client->addr;
+       msgs[1].len     = num_regs;
+       msgs[1].buf     = buf;
+       msgs[1].flags   = I2C_M_RD;
+
+       ret = i2c_transfer(client->adapter, &msgs[0], 2);
+       if (ret < 0) {
+               dev_err(&client->dev, "ltc2941 read_reg failed!\n");
+               return ret;
+       }
+
+       dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",
+               __func__, reg, num_regs, *buf);
+
+       return 0;
+}
+
+static int ltc294x_write_regs(struct i2c_client *client,
+       enum ltc294x_reg reg, const u8 *buf, int num_regs)
+{
+       int ret;
+       u8 reg_start = reg;
+
+       ret = i2c_smbus_write_i2c_block_data(client, reg_start, num_regs, buf);
+       if (ret < 0) {
+               dev_err(&client->dev, "ltc2941 write_reg failed!\n");
+               return ret;
+       }
+
+       dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",
+               __func__, reg, num_regs, *buf);
+
+       return 0;
+}
+
+static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)
+{
+       int ret;
+       u8 value;
+       u8 control;
+
+       /* Read status and control registers */
+       ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1);
+       if (ret < 0) {
+               dev_err(&info->client->dev,
+                       "Could not read registers from device\n");
+               goto error_exit;
+       }
+
+       control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) |
+                               LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED;
+       /* Put the 2943 into "monitor" mode, so it measures every 10 sec */
+       if (info->num_regs == LTC2943_NUM_REGS)
+               control |= LTC2943_REG_CONTROL_MODE_SCAN;
+
+       if (value != control) {
+               ret = ltc294x_write_regs(info->client,
+                       LTC294X_REG_CONTROL, &control, 1);
+               if (ret < 0) {
+                       dev_err(&info->client->dev,
+                               "Could not write register\n");
+                       goto error_exit;
+               }
+       }
+
+       return 0;
+
+error_exit:
+       return ret;
+}
+
+static int ltc294x_read_charge_register(const struct ltc294x_info *info)
+{
+       int ret;
+       u8 datar[2];
+
+       ret = ltc294x_read_regs(info->client,
+               LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2);
+       if (ret < 0)
+               return ret;
+       return (datar[0] << 8) + datar[1];
+}
+
+static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val)
+{
+       int value = ltc294x_read_charge_register(info);
+
+       if (value < 0)
+               return value;
+       /* When r_sense < 0, this counts up when the battery discharges */
+       if (info->Qlsb < 0)
+               value -= 0xFFFF;
+       *val = convert_bin_to_uAh(info, value);
+       return 0;
+}
+
+static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val)
+{
+       int ret;
+       u8 dataw[2];
+       u8 ctrl_reg;
+       s32 value;
+
+       value = convert_uAh_to_bin(info, val);
+       /* Direction depends on how sense+/- were connected */
+       if (info->Qlsb < 0)
+               value += 0xFFFF;
+       if ((value < 0) || (value > 0xFFFF)) /* input validation */
+               return -EINVAL;
+
+       /* Read control register */
+       ret = ltc294x_read_regs(info->client,
+               LTC294X_REG_CONTROL, &ctrl_reg, 1);
+       if (ret < 0)
+               return ret;
+       /* Disable analog section */
+       ctrl_reg |= LTC294X_REG_CONTROL_SHUTDOWN_MASK;
+       ret = ltc294x_write_regs(info->client,
+               LTC294X_REG_CONTROL, &ctrl_reg, 1);
+       if (ret < 0)
+               return ret;
+       /* Set new charge value */
+       dataw[0] = I16_MSB(value);
+       dataw[1] = I16_LSB(value);
+       ret = ltc294x_write_regs(info->client,
+               LTC294X_REG_ACC_CHARGE_MSB, &dataw[0], 2);
+       if (ret < 0)
+               goto error_exit;
+       /* Enable analog section */
+error_exit:
+       ctrl_reg &= ~LTC294X_REG_CONTROL_SHUTDOWN_MASK;
+       ret = ltc294x_write_regs(info->client,
+               LTC294X_REG_CONTROL, &ctrl_reg, 1);
+
+       return ret < 0 ? ret : 0;
+}
+
+static int ltc294x_get_charge_counter(
+       const struct ltc294x_info *info, int *val)
+{
+       int value = ltc294x_read_charge_register(info);
+
+       if (value < 0)
+               return value;
+       value -= LTC294X_MID_SUPPLY;
+       *val = convert_bin_to_uAh(info, value);
+       return 0;
+}
+
+static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val)
+{
+       int ret;
+       u8 datar[2];
+       u32 value;
+
+       ret = ltc294x_read_regs(info->client,
+               LTC294X_REG_VOLTAGE_MSB, &datar[0], 2);
+       value = (datar[0] << 8) | datar[1];
+       *val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */
+       return ret;
+}
+
+static int ltc294x_get_current(const struct ltc294x_info *info, int *val)
+{
+       int ret;
+       u8 datar[2];
+       s32 value;
+
+       ret = ltc294x_read_regs(info->client,
+               LTC294X_REG_CURRENT_MSB, &datar[0], 2);
+       value = (datar[0] << 8) | datar[1];
+       value -= 0x7FFF;
+       /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm,
+        * the formula below keeps everything in s32 range while preserving
+        * enough digits */
+       *val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */
+       return ret;
+}
+
+static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
+{
+       int ret;
+       u8 datar[2];
+       u32 value;
+
+       ret = ltc294x_read_regs(info->client,
+               LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2);
+       value = (datar[0] << 8) | datar[1];
+       /* Full-scale is 510 Kelvin, convert to centidegrees  */
+       *val = (((51000 * value) / 0xFFFF) - 27215);
+       return ret;
+}
+
+static int ltc294x_get_property(struct power_supply *psy,
+                               enum power_supply_property prop,
+                               union power_supply_propval *val)
+{
+       struct ltc294x_info *info = power_supply_get_drvdata(psy);
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               return ltc294x_get_charge_now(info, &val->intval);
+       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+               return ltc294x_get_charge_counter(info, &val->intval);
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               return ltc294x_get_voltage(info, &val->intval);
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               return ltc294x_get_current(info, &val->intval);
+       case POWER_SUPPLY_PROP_TEMP:
+               return ltc294x_get_temperature(info, &val->intval);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int ltc294x_set_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       const union power_supply_propval *val)
+{
+       struct ltc294x_info *info = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               return ltc294x_set_charge_now(info, val->intval);
+       default:
+               return -EPERM;
+       }
+}
+
+static int ltc294x_property_is_writeable(
+       struct power_supply *psy, enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static void ltc294x_update(struct ltc294x_info *info)
+{
+       int charge = ltc294x_read_charge_register(info);
+
+       if (charge != info->charge) {
+               info->charge = charge;
+               power_supply_changed(info->supply);
+       }
+}
+
+static void ltc294x_work(struct work_struct *work)
+{
+       struct ltc294x_info *info;
+
+       info = container_of(work, struct ltc294x_info, work.work);
+       ltc294x_update(info);
+       schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
+}
+
+static enum power_supply_property ltc294x_properties[] = {
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static int ltc294x_i2c_remove(struct i2c_client *client)
+{
+       struct ltc294x_info *info = i2c_get_clientdata(client);
+
+       cancel_delayed_work(&info->work);
+       power_supply_unregister(info->supply);
+       return 0;
+}
+
+static int ltc294x_i2c_probe(struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       struct power_supply_config psy_cfg = {};
+       struct ltc294x_info *info;
+       int ret;
+       u32 prescaler_exp;
+       s32 r_sense;
+       struct device_node *np;
+
+       info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
+       if (info == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, info);
+
+       np = of_node_get(client->dev.of_node);
+
+       info->num_regs = id->driver_data;
+       info->supply_desc.name = np->name;
+
+       /* r_sense can be negative, when sense+ is connected to the battery
+        * instead of the sense-. This results in reversed measurements. */
+       ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);
+       if (ret < 0) {
+               dev_err(&client->dev,
+                       "Could not find lltc,resistor-sense in devicetree\n");
+               return ret;
+       }
+       info->r_sense = r_sense;
+
+       ret = of_property_read_u32(np, "lltc,prescaler-exponent",
+               &prescaler_exp);
+       if (ret < 0) {
+               dev_warn(&client->dev,
+                       "lltc,prescaler-exponent not in devicetree\n");
+               prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
+       }
+
+       if (info->num_regs == LTC2943_NUM_REGS) {
+               if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)
+                       prescaler_exp = LTC2943_MAX_PRESCALER_EXP;
+               info->Qlsb = ((340 * 50000) / r_sense) /
+                               (4096 / (1 << (2*prescaler_exp)));
+       } else {
+               if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP)
+                       prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
+               info->Qlsb = ((85 * 50000) / r_sense) /
+                               (128 / (1 << prescaler_exp));
+       }
+
+       info->client = client;
+       info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+       info->supply_desc.properties = ltc294x_properties;
+       if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
+               info->supply_desc.num_properties =
+                       ARRAY_SIZE(ltc294x_properties);
+       else if (info->num_regs >= LTC294X_REG_CURRENT_LSB)
+               info->supply_desc.num_properties =
+                       ARRAY_SIZE(ltc294x_properties) - 1;
+       else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB)
+               info->supply_desc.num_properties =
+                       ARRAY_SIZE(ltc294x_properties) - 2;
+       else
+               info->supply_desc.num_properties =
+                       ARRAY_SIZE(ltc294x_properties) - 3;
+       info->supply_desc.get_property = ltc294x_get_property;
+       info->supply_desc.set_property = ltc294x_set_property;
+       info->supply_desc.property_is_writeable = ltc294x_property_is_writeable;
+       info->supply_desc.external_power_changed        = NULL;
+
+       psy_cfg.drv_data = info;
+
+       INIT_DELAYED_WORK(&info->work, ltc294x_work);
+
+       ret = ltc294x_reset(info, prescaler_exp);
+       if (ret < 0) {
+               dev_err(&client->dev, "Communication with chip failed\n");
+               return ret;
+       }
+
+       info->supply = power_supply_register(&client->dev, &info->supply_desc,
+                                            &psy_cfg);
+       if (IS_ERR(info->supply)) {
+               dev_err(&client->dev, "failed to register ltc2941\n");
+               return PTR_ERR(info->supply);
+       } else {
+               schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int ltc294x_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ltc294x_info *info = i2c_get_clientdata(client);
+
+       cancel_delayed_work(&info->work);
+       return 0;
+}
+
+static int ltc294x_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ltc294x_info *info = i2c_get_clientdata(client);
+
+       schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume);
+#define LTC294X_PM_OPS (&ltc294x_pm_ops)
+
+#else
+#define LTC294X_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+
+static const struct i2c_device_id ltc294x_i2c_id[] = {
+       {"ltc2941", LTC2941_NUM_REGS},
+       {"ltc2943", LTC2943_NUM_REGS},
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);
+
+static struct i2c_driver ltc294x_driver = {
+       .driver = {
+               .name   = "LTC2941",
+               .pm     = LTC294X_PM_OPS,
+       },
+       .probe          = ltc294x_i2c_probe,
+       .remove         = ltc294x_i2c_remove,
+       .id_table       = ltc294x_i2c_id,
+};
+module_i2c_driver(ltc294x_driver);
+
+MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems");
+MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products");
+MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/max14577_charger.c b/drivers/power/supply/max14577_charger.c
new file mode 100644 (file)
index 0000000..a36bcaf
--- /dev/null
@@ -0,0 +1,648 @@
+/*
+ * max14577_charger.c - Battery charger driver for the Maxim 14577/77836
+ *
+ * Copyright (C) 2013,2014 Samsung Electronics
+ * Krzysztof Kozlowski <k.kozlowski@samsung.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/max14577-private.h>
+#include <linux/mfd/max14577.h>
+
+struct max14577_charger {
+       struct device           *dev;
+       struct max14577         *max14577;
+       struct power_supply     *charger;
+
+       struct max14577_charger_platform_data   *pdata;
+};
+
+/*
+ * Helper function for mapping values of STATUS2/CHGTYP register on max14577
+ * and max77836 chipsets to enum maxim_muic_charger_type.
+ */
+static enum max14577_muic_charger_type maxim_get_charger_type(
+               enum maxim_device_type dev_type, u8 val) {
+       switch (val) {
+       case MAX14577_CHARGER_TYPE_NONE:
+       case MAX14577_CHARGER_TYPE_USB:
+       case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
+       case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
+       case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
+       case MAX14577_CHARGER_TYPE_SPECIAL_1A:
+               return val;
+       case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
+       case MAX14577_CHARGER_TYPE_RESERVED:
+               if (dev_type == MAXIM_DEVICE_TYPE_MAX77836)
+                       val |= 0x8;
+               return val;
+       default:
+               WARN_ONCE(1, "max14577: Unsupported chgtyp register value 0x%02x", val);
+               return val;
+       }
+}
+
+static int max14577_get_charger_state(struct max14577_charger *chg, int *val)
+{
+       struct regmap *rmap = chg->max14577->regmap;
+       int ret;
+       u8 reg_data;
+
+       /*
+        * Charging occurs only if:
+        *  - CHGCTRL2/MBCHOSTEN == 1
+        *  - STATUS2/CGMBC == 1
+        *
+        * TODO:
+        *  - handle FULL after Top-off timer (EOC register may be off
+        *    and the charger won't be charging although MBCHOSTEN is on)
+        *  - handle properly dead-battery charging (respect timer)
+        *  - handle timers (fast-charge and prequal) /MBCCHGERR/
+        */
+       ret = max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL2, &reg_data);
+       if (ret < 0)
+               goto out;
+
+       if ((reg_data & CHGCTRL2_MBCHOSTEN_MASK) == 0) {
+               *val = POWER_SUPPLY_STATUS_DISCHARGING;
+               goto out;
+       }
+
+       ret = max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, &reg_data);
+       if (ret < 0)
+               goto out;
+
+       if (reg_data & STATUS3_CGMBC_MASK) {
+               /* Charger or USB-cable is connected */
+               if (reg_data & STATUS3_EOC_MASK)
+                       *val = POWER_SUPPLY_STATUS_FULL;
+               else
+                       *val = POWER_SUPPLY_STATUS_CHARGING;
+               goto out;
+       }
+
+       *val = POWER_SUPPLY_STATUS_DISCHARGING;
+
+out:
+       return ret;
+}
+
+/*
+ * Supported charge types:
+ *  - POWER_SUPPLY_CHARGE_TYPE_NONE
+ *  - POWER_SUPPLY_CHARGE_TYPE_FAST
+ */
+static int max14577_get_charge_type(struct max14577_charger *chg, int *val)
+{
+       int ret, charging;
+
+       /*
+        * TODO: CHARGE_TYPE_TRICKLE (VCHGR_RC or EOC)?
+        * As spec says:
+        * [after reaching EOC interrupt]
+        * "When the battery is fully charged, the 30-minute (typ)
+        *  top-off timer starts. The device continues to trickle
+        *  charge the battery until the top-off timer runs out."
+        */
+       ret = max14577_get_charger_state(chg, &charging);
+       if (ret < 0)
+               return ret;
+
+       if (charging == POWER_SUPPLY_STATUS_CHARGING)
+               *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
+       else
+               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+
+       return 0;
+}
+
+static int max14577_get_online(struct max14577_charger *chg, int *val)
+{
+       struct regmap *rmap = chg->max14577->regmap;
+       u8 reg_data;
+       int ret;
+       enum max14577_muic_charger_type chg_type;
+
+       ret = max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, &reg_data);
+       if (ret < 0)
+               return ret;
+
+       reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
+       chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
+       switch (chg_type) {
+       case MAX14577_CHARGER_TYPE_USB:
+       case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
+       case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
+       case MAX14577_CHARGER_TYPE_SPECIAL_1A:
+       case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
+       case MAX77836_CHARGER_TYPE_SPECIAL_BIAS:
+               *val = 1;
+               break;
+       case MAX14577_CHARGER_TYPE_NONE:
+       case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
+       case MAX14577_CHARGER_TYPE_RESERVED:
+       case MAX77836_CHARGER_TYPE_RESERVED:
+       default:
+               *val = 0;
+       }
+
+       return 0;
+}
+
+/*
+ * Supported health statuses:
+ *  - POWER_SUPPLY_HEALTH_DEAD
+ *  - POWER_SUPPLY_HEALTH_OVERVOLTAGE
+ *  - POWER_SUPPLY_HEALTH_GOOD
+ */
+static int max14577_get_battery_health(struct max14577_charger *chg, int *val)
+{
+       struct regmap *rmap = chg->max14577->regmap;
+       int ret;
+       u8 reg_data;
+       enum max14577_muic_charger_type chg_type;
+
+       ret = max14577_read_reg(rmap, MAX14577_MUIC_REG_STATUS2, &reg_data);
+       if (ret < 0)
+               goto out;
+
+       reg_data = ((reg_data & STATUS2_CHGTYP_MASK) >> STATUS2_CHGTYP_SHIFT);
+       chg_type = maxim_get_charger_type(chg->max14577->dev_type, reg_data);
+       if (chg_type == MAX14577_CHARGER_TYPE_DEAD_BATTERY) {
+               *val = POWER_SUPPLY_HEALTH_DEAD;
+               goto out;
+       }
+
+       ret = max14577_read_reg(rmap, MAX14577_CHG_REG_STATUS3, &reg_data);
+       if (ret < 0)
+               goto out;
+
+       if (reg_data & STATUS3_OVP_MASK) {
+               *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               goto out;
+       }
+
+       /* Not dead, not overvoltage */
+       *val = POWER_SUPPLY_HEALTH_GOOD;
+
+out:
+       return ret;
+}
+
+/*
+ * Always returns 1.
+ * The max14577 chip doesn't report any status of battery presence.
+ * Lets assume that it will always be used with some battery.
+ */
+static int max14577_get_present(struct max14577_charger *chg, int *val)
+{
+       *val = 1;
+
+       return 0;
+}
+
+static int max14577_set_fast_charge_timer(struct max14577_charger *chg,
+               unsigned long hours)
+{
+       u8 reg_data;
+
+       switch (hours) {
+       case 5 ... 7:
+               reg_data = hours - 3;
+               break;
+       case 0:
+               /* Disable */
+               reg_data = 0x7;
+               break;
+       default:
+               dev_err(chg->dev, "Wrong value for Fast-Charge Timer: %lu\n",
+                               hours);
+               return -EINVAL;
+       }
+       reg_data <<= CHGCTRL1_TCHW_SHIFT;
+
+       return max14577_update_reg(chg->max14577->regmap,
+                       MAX14577_REG_CHGCTRL1, CHGCTRL1_TCHW_MASK, reg_data);
+}
+
+static int max14577_init_constant_voltage(struct max14577_charger *chg,
+               unsigned int uvolt)
+{
+       u8 reg_data;
+
+       if (uvolt < MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN ||
+                       uvolt > MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
+               return -EINVAL;
+
+       if (uvolt == 4200000)
+               reg_data = 0x0;
+       else if (uvolt == MAXIM_CHARGER_CONSTANT_VOLTAGE_MAX)
+               reg_data = 0x1f;
+       else if (uvolt <= 4280000) {
+               unsigned int val = uvolt;
+
+               val -= MAXIM_CHARGER_CONSTANT_VOLTAGE_MIN;
+               val /= MAXIM_CHARGER_CONSTANT_VOLTAGE_STEP;
+               if (uvolt <= 4180000)
+                       reg_data = 0x1 + val;
+               else
+                       reg_data = val; /* Fix for gap between 4.18V and 4.22V */
+       } else
+               return -EINVAL;
+
+       reg_data <<= CHGCTRL3_MBCCVWRC_SHIFT;
+
+       return max14577_write_reg(chg->max14577->regmap,
+                       MAX14577_CHG_REG_CHG_CTRL3, reg_data);
+}
+
+static int max14577_init_eoc(struct max14577_charger *chg,
+               unsigned int uamp)
+{
+       unsigned int current_bits = 0xf;
+       u8 reg_data;
+
+       switch (chg->max14577->dev_type) {
+       case MAXIM_DEVICE_TYPE_MAX77836:
+               if (uamp < 5000)
+                       return -EINVAL; /* Requested current is too low */
+
+               if (uamp >= 7500 && uamp < 10000)
+                       current_bits = 0x0;
+               else if (uamp <= 50000) {
+                       /* <5000, 7499> and <10000, 50000> */
+                       current_bits = uamp / 5000;
+               } else {
+                       uamp = min(uamp, 100000U) - 50000U;
+                       current_bits = 0xa + uamp / 10000;
+               }
+               break;
+
+       case MAXIM_DEVICE_TYPE_MAX14577:
+       default:
+               if (uamp < MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN)
+                       return -EINVAL; /* Requested current is too low */
+
+               uamp = min(uamp, MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
+               uamp -= MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN;
+               current_bits = uamp / MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP;
+               break;
+       }
+
+       reg_data = current_bits << CHGCTRL5_EOCS_SHIFT;
+
+       return max14577_update_reg(chg->max14577->regmap,
+                       MAX14577_CHG_REG_CHG_CTRL5, CHGCTRL5_EOCS_MASK,
+                       reg_data);
+}
+
+static int max14577_init_fast_charge(struct max14577_charger *chg,
+               unsigned int uamp)
+{
+       u8 reg_data;
+       int ret;
+       const struct maxim_charger_current *limits =
+               &maxim_charger_currents[chg->max14577->dev_type];
+
+       ret = maxim_charger_calc_reg_current(limits, uamp, uamp, &reg_data);
+       if (ret) {
+               dev_err(chg->dev, "Wrong value for fast charge: %u\n", uamp);
+               return ret;
+       }
+
+       return max14577_update_reg(chg->max14577->regmap,
+                       MAX14577_CHG_REG_CHG_CTRL4,
+                       CHGCTRL4_MBCICHWRCL_MASK | CHGCTRL4_MBCICHWRCH_MASK,
+                       reg_data);
+}
+
+/*
+ * Sets charger registers to proper and safe default values.
+ * Some of these values are equal to defaults in MAX14577E
+ * data sheet but there are minor differences.
+ */
+static int max14577_charger_reg_init(struct max14577_charger *chg)
+{
+       struct regmap *rmap = chg->max14577->regmap;
+       u8 reg_data;
+       int ret;
+
+       /*
+        * Charger-Type Manual Detection, default off (set CHGTYPMAN to 0)
+        * Charger-Detection Enable, default on (set CHGDETEN to 1)
+        * Combined mask of CHGDETEN and CHGTYPMAN will zero the CHGTYPMAN bit
+        */
+       reg_data = 0x1 << CDETCTRL1_CHGDETEN_SHIFT;
+       max14577_update_reg(rmap, MAX14577_REG_CDETCTRL1,
+                       CDETCTRL1_CHGDETEN_MASK | CDETCTRL1_CHGTYPMAN_MASK,
+                       reg_data);
+
+       /*
+        * Wall-Adapter Rapid Charge, default on
+        * Battery-Charger, default on
+        */
+       reg_data = 0x1 << CHGCTRL2_VCHGR_RC_SHIFT;
+       reg_data |= 0x1 << CHGCTRL2_MBCHOSTEN_SHIFT;
+       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL2, reg_data);
+
+       /* Auto Charging Stop, default off */
+       reg_data = 0x0 << CHGCTRL6_AUTOSTOP_SHIFT;
+       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL6, reg_data);
+
+       ret = max14577_init_constant_voltage(chg, chg->pdata->constant_uvolt);
+       if (ret)
+               return ret;
+
+       ret = max14577_init_eoc(chg, chg->pdata->eoc_uamp);
+       if (ret)
+               return ret;
+
+       ret = max14577_init_fast_charge(chg, chg->pdata->fast_charge_uamp);
+       if (ret)
+               return ret;
+
+       ret = max14577_set_fast_charge_timer(chg,
+                       MAXIM_CHARGER_FAST_CHARGE_TIMER_DEFAULT);
+       if (ret)
+               return ret;
+
+       /* Initialize Overvoltage-Protection Threshold */
+       switch (chg->pdata->ovp_uvolt) {
+       case 7500000:
+               reg_data = 0x0;
+               break;
+       case 6000000:
+       case 6500000:
+       case 7000000:
+               reg_data = 0x1 + (chg->pdata->ovp_uvolt - 6000000) / 500000;
+               break;
+       default:
+               dev_err(chg->dev, "Wrong value for OVP: %u\n",
+                               chg->pdata->ovp_uvolt);
+               return -EINVAL;
+       }
+       reg_data <<= CHGCTRL7_OTPCGHCVS_SHIFT;
+       max14577_write_reg(rmap, MAX14577_REG_CHGCTRL7, reg_data);
+
+       return 0;
+}
+
+/* Support property from charger */
+static enum power_supply_property max14577_charger_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static const char * const model_names[] = {
+       [MAXIM_DEVICE_TYPE_UNKNOWN]     = "MAX14577-like",
+       [MAXIM_DEVICE_TYPE_MAX14577]    = "MAX14577",
+       [MAXIM_DEVICE_TYPE_MAX77836]    = "MAX77836",
+};
+static const char *manufacturer = "Maxim Integrated";
+
+static int max14577_charger_get_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       struct max14577_charger *chg = power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = max14577_get_charger_state(chg, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               ret = max14577_get_charge_type(chg, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = max14577_get_battery_health(chg, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               ret = max14577_get_present(chg, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = max14577_get_online(chg, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               BUILD_BUG_ON(ARRAY_SIZE(model_names) != MAXIM_DEVICE_TYPE_NUM);
+               val->strval = model_names[chg->max14577->dev_type];
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = manufacturer;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+static const struct power_supply_desc max14577_charger_desc = {
+       .name = "max14577-charger",
+       .type = POWER_SUPPLY_TYPE_BATTERY,
+       .properties = max14577_charger_props,
+       .num_properties = ARRAY_SIZE(max14577_charger_props),
+       .get_property = max14577_charger_get_property,
+};
+
+#ifdef CONFIG_OF
+static struct max14577_charger_platform_data *max14577_charger_dt_init(
+               struct platform_device *pdev)
+{
+       struct max14577_charger_platform_data *pdata;
+       struct device_node *np = pdev->dev.of_node;
+       int ret;
+
+       if (!np) {
+               dev_err(&pdev->dev, "No charger OF node\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       ret = of_property_read_u32(np, "maxim,constant-uvolt",
+                       &pdata->constant_uvolt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot parse maxim,constant-uvolt field from DT\n");
+               return ERR_PTR(ret);
+       }
+
+       ret = of_property_read_u32(np, "maxim,fast-charge-uamp",
+                       &pdata->fast_charge_uamp);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot parse maxim,fast-charge-uamp field from DT\n");
+               return ERR_PTR(ret);
+       }
+
+       ret = of_property_read_u32(np, "maxim,eoc-uamp", &pdata->eoc_uamp);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot parse maxim,eoc-uamp field from DT\n");
+               return ERR_PTR(ret);
+       }
+
+       ret = of_property_read_u32(np, "maxim,ovp-uvolt", &pdata->ovp_uvolt);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot parse maxim,ovp-uvolt field from DT\n");
+               return ERR_PTR(ret);
+       }
+
+       return pdata;
+}
+#else /* CONFIG_OF */
+static struct max14577_charger_platform_data *max14577_charger_dt_init(
+               struct platform_device *pdev)
+{
+       return NULL;
+}
+#endif /* CONFIG_OF */
+
+static ssize_t show_fast_charge_timer(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct max14577_charger *chg = dev_get_drvdata(dev);
+       u8 reg_data;
+       int ret;
+       unsigned int val;
+
+       ret = max14577_read_reg(chg->max14577->regmap, MAX14577_REG_CHGCTRL1,
+                       &reg_data);
+       if (ret)
+               return ret;
+
+       reg_data &= CHGCTRL1_TCHW_MASK;
+       reg_data >>= CHGCTRL1_TCHW_SHIFT;
+       switch (reg_data) {
+       case 0x2 ... 0x4:
+               val = reg_data + 3;
+               break;
+       case 0x7:
+               val = 0;
+               break;
+       default:
+               val = 5;
+               break;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t store_fast_charge_timer(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct max14577_charger *chg = dev_get_drvdata(dev);
+       unsigned long val;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &val);
+       if (ret)
+               return ret;
+
+       ret = max14577_set_fast_charge_timer(chg, val);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(fast_charge_timer, S_IRUGO | S_IWUSR,
+               show_fast_charge_timer, store_fast_charge_timer);
+
+static int max14577_charger_probe(struct platform_device *pdev)
+{
+       struct max14577_charger *chg;
+       struct power_supply_config psy_cfg = {};
+       struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent);
+       int ret;
+
+       chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
+       if (!chg)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, chg);
+       chg->dev = &pdev->dev;
+       chg->max14577 = max14577;
+
+       chg->pdata = max14577_charger_dt_init(pdev);
+       if (IS_ERR_OR_NULL(chg->pdata))
+               return PTR_ERR(chg->pdata);
+
+       ret = max14577_charger_reg_init(chg);
+       if (ret)
+               return ret;
+
+       ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
+       if (ret) {
+               dev_err(&pdev->dev, "failed: create sysfs entry\n");
+               return ret;
+       }
+
+       psy_cfg.drv_data = chg;
+       chg->charger = power_supply_register(&pdev->dev, &max14577_charger_desc,
+                                               &psy_cfg);
+       if (IS_ERR(chg->charger)) {
+               dev_err(&pdev->dev, "failed: power supply register\n");
+               ret = PTR_ERR(chg->charger);
+               goto err;
+       }
+
+       /* Check for valid values for charger */
+       BUILD_BUG_ON(MAX14577_CHARGER_EOC_CURRENT_LIMIT_MIN +
+                       MAX14577_CHARGER_EOC_CURRENT_LIMIT_STEP * 0xf !=
+                       MAX14577_CHARGER_EOC_CURRENT_LIMIT_MAX);
+       return 0;
+
+err:
+       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
+
+       return ret;
+}
+
+static int max14577_charger_remove(struct platform_device *pdev)
+{
+       struct max14577_charger *chg = platform_get_drvdata(pdev);
+
+       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
+       power_supply_unregister(chg->charger);
+
+       return 0;
+}
+
+static const struct platform_device_id max14577_charger_id[] = {
+       { "max14577-charger", MAXIM_DEVICE_TYPE_MAX14577, },
+       { "max77836-charger", MAXIM_DEVICE_TYPE_MAX77836, },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, max14577_charger_id);
+
+static struct platform_driver max14577_charger_driver = {
+       .driver = {
+               .name   = "max14577-charger",
+       },
+       .probe          = max14577_charger_probe,
+       .remove         = max14577_charger_remove,
+       .id_table       = max14577_charger_id,
+};
+module_platform_driver(max14577_charger_driver);
+
+MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
+MODULE_DESCRIPTION("Maxim 14577/77836 charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c
new file mode 100644 (file)
index 0000000..8689c80
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ *  max17040_battery.c
+ *  fuel-gauge systems for lithium-ion (Li+) batteries
+ *
+ *  Copyright (C) 2009 Samsung Electronics
+ *  Minkyu Kang <mk7.kang@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/power_supply.h>
+#include <linux/max17040_battery.h>
+#include <linux/slab.h>
+
+#define MAX17040_VCELL_MSB     0x02
+#define MAX17040_VCELL_LSB     0x03
+#define MAX17040_SOC_MSB       0x04
+#define MAX17040_SOC_LSB       0x05
+#define MAX17040_MODE_MSB      0x06
+#define MAX17040_MODE_LSB      0x07
+#define MAX17040_VER_MSB       0x08
+#define MAX17040_VER_LSB       0x09
+#define MAX17040_RCOMP_MSB     0x0C
+#define MAX17040_RCOMP_LSB     0x0D
+#define MAX17040_CMD_MSB       0xFE
+#define MAX17040_CMD_LSB       0xFF
+
+#define MAX17040_DELAY         1000
+#define MAX17040_BATTERY_FULL  95
+
+struct max17040_chip {
+       struct i2c_client               *client;
+       struct delayed_work             work;
+       struct power_supply             *battery;
+       struct max17040_platform_data   *pdata;
+
+       /* State Of Connect */
+       int online;
+       /* battery voltage */
+       int vcell;
+       /* battery capacity */
+       int soc;
+       /* State Of Charge */
+       int status;
+};
+
+static int max17040_get_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       struct max17040_chip *chip = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = chip->status;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = chip->online;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = chip->vcell;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = chip->soc;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int max17040_write_reg(struct i2c_client *client, int reg, u8 value)
+{
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(client, reg, value);
+
+       if (ret < 0)
+               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+       return ret;
+}
+
+static int max17040_read_reg(struct i2c_client *client, int reg)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(client, reg);
+
+       if (ret < 0)
+               dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+
+       return ret;
+}
+
+static void max17040_reset(struct i2c_client *client)
+{
+       max17040_write_reg(client, MAX17040_CMD_MSB, 0x54);
+       max17040_write_reg(client, MAX17040_CMD_LSB, 0x00);
+}
+
+static void max17040_get_vcell(struct i2c_client *client)
+{
+       struct max17040_chip *chip = i2c_get_clientdata(client);
+       u8 msb;
+       u8 lsb;
+
+       msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
+       lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
+
+       chip->vcell = (msb << 4) + (lsb >> 4);
+}
+
+static void max17040_get_soc(struct i2c_client *client)
+{
+       struct max17040_chip *chip = i2c_get_clientdata(client);
+       u8 msb;
+       u8 lsb;
+
+       msb = max17040_read_reg(client, MAX17040_SOC_MSB);
+       lsb = max17040_read_reg(client, MAX17040_SOC_LSB);
+
+       chip->soc = msb;
+}
+
+static void max17040_get_version(struct i2c_client *client)
+{
+       u8 msb;
+       u8 lsb;
+
+       msb = max17040_read_reg(client, MAX17040_VER_MSB);
+       lsb = max17040_read_reg(client, MAX17040_VER_LSB);
+
+       dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver %d%d\n", msb, lsb);
+}
+
+static void max17040_get_online(struct i2c_client *client)
+{
+       struct max17040_chip *chip = i2c_get_clientdata(client);
+
+       if (chip->pdata && chip->pdata->battery_online)
+               chip->online = chip->pdata->battery_online();
+       else
+               chip->online = 1;
+}
+
+static void max17040_get_status(struct i2c_client *client)
+{
+       struct max17040_chip *chip = i2c_get_clientdata(client);
+
+       if (!chip->pdata || !chip->pdata->charger_online
+                       || !chip->pdata->charger_enable) {
+               chip->status = POWER_SUPPLY_STATUS_UNKNOWN;
+               return;
+       }
+
+       if (chip->pdata->charger_online()) {
+               if (chip->pdata->charger_enable())
+                       chip->status = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+
+       if (chip->soc > MAX17040_BATTERY_FULL)
+               chip->status = POWER_SUPPLY_STATUS_FULL;
+}
+
+static void max17040_work(struct work_struct *work)
+{
+       struct max17040_chip *chip;
+
+       chip = container_of(work, struct max17040_chip, work.work);
+
+       max17040_get_vcell(chip->client);
+       max17040_get_soc(chip->client);
+       max17040_get_online(chip->client);
+       max17040_get_status(chip->client);
+
+       queue_delayed_work(system_power_efficient_wq, &chip->work,
+                          MAX17040_DELAY);
+}
+
+static enum power_supply_property max17040_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static const struct power_supply_desc max17040_battery_desc = {
+       .name           = "battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property   = max17040_get_property,
+       .properties     = max17040_battery_props,
+       .num_properties = ARRAY_SIZE(max17040_battery_props),
+};
+
+static int max17040_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct power_supply_config psy_cfg = {};
+       struct max17040_chip *chip;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+               return -EIO;
+
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->client = client;
+       chip->pdata = client->dev.platform_data;
+
+       i2c_set_clientdata(client, chip);
+       psy_cfg.drv_data = chip;
+
+       chip->battery = power_supply_register(&client->dev,
+                               &max17040_battery_desc, &psy_cfg);
+       if (IS_ERR(chip->battery)) {
+               dev_err(&client->dev, "failed: power supply register\n");
+               return PTR_ERR(chip->battery);
+       }
+
+       max17040_reset(client);
+       max17040_get_version(client);
+
+       INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
+       queue_delayed_work(system_power_efficient_wq, &chip->work,
+                          MAX17040_DELAY);
+
+       return 0;
+}
+
+static int max17040_remove(struct i2c_client *client)
+{
+       struct max17040_chip *chip = i2c_get_clientdata(client);
+
+       power_supply_unregister(chip->battery);
+       cancel_delayed_work(&chip->work);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int max17040_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct max17040_chip *chip = i2c_get_clientdata(client);
+
+       cancel_delayed_work(&chip->work);
+       return 0;
+}
+
+static int max17040_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct max17040_chip *chip = i2c_get_clientdata(client);
+
+       queue_delayed_work(system_power_efficient_wq, &chip->work,
+                          MAX17040_DELAY);
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
+#define MAX17040_PM_OPS (&max17040_pm_ops)
+
+#else
+
+#define MAX17040_PM_OPS NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct i2c_device_id max17040_id[] = {
+       { "max17040" },
+       { "max77836-battery" },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max17040_id);
+
+static struct i2c_driver max17040_i2c_driver = {
+       .driver = {
+               .name   = "max17040",
+               .pm     = MAX17040_PM_OPS,
+       },
+       .probe          = max17040_probe,
+       .remove         = max17040_remove,
+       .id_table       = max17040_id,
+};
+module_i2c_driver(max17040_i2c_driver);
+
+MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
+MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c
new file mode 100644 (file)
index 0000000..9c65f13
--- /dev/null
@@ -0,0 +1,1016 @@
+/*
+ * Fuel gauge driver for Maxim 17042 / 8966 / 8997
+ *  Note that Maxim 8966 and 8997 are mfd and this is its subdevice.
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is based on max17040_battery.c
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/mod_devicetable.h>
+#include <linux/power_supply.h>
+#include <linux/power/max17042_battery.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+/* Status register bits */
+#define STATUS_POR_BIT         (1 << 1)
+#define STATUS_BST_BIT         (1 << 3)
+#define STATUS_VMN_BIT         (1 << 8)
+#define STATUS_TMN_BIT         (1 << 9)
+#define STATUS_SMN_BIT         (1 << 10)
+#define STATUS_BI_BIT          (1 << 11)
+#define STATUS_VMX_BIT         (1 << 12)
+#define STATUS_TMX_BIT         (1 << 13)
+#define STATUS_SMX_BIT         (1 << 14)
+#define STATUS_BR_BIT          (1 << 15)
+
+/* Interrupt mask bits */
+#define CONFIG_ALRT_BIT_ENBL   (1 << 2)
+#define STATUS_INTR_SOCMIN_BIT (1 << 10)
+#define STATUS_INTR_SOCMAX_BIT (1 << 14)
+
+#define VFSOC0_LOCK            0x0000
+#define VFSOC0_UNLOCK          0x0080
+#define MODEL_UNLOCK1  0X0059
+#define MODEL_UNLOCK2  0X00C4
+#define MODEL_LOCK1            0X0000
+#define MODEL_LOCK2            0X0000
+
+#define dQ_ACC_DIV     0x4
+#define dP_ACC_100     0x1900
+#define dP_ACC_200     0x3200
+
+#define MAX17042_VMAX_TOLERANCE                50 /* 50 mV */
+
+struct max17042_chip {
+       struct i2c_client *client;
+       struct regmap *regmap;
+       struct power_supply *battery;
+       enum max170xx_chip_type chip_type;
+       struct max17042_platform_data *pdata;
+       struct work_struct work;
+       int    init_complete;
+};
+
+static enum power_supply_property max17042_battery_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_OCV,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
+       POWER_SUPPLY_PROP_TEMP_MIN,
+       POWER_SUPPLY_PROP_TEMP_MAX,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+};
+
+static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
+{
+       int ret;
+       u32 data;
+       struct regmap *map = chip->regmap;
+
+       ret = regmap_read(map, MAX17042_TEMP, &data);
+       if (ret < 0)
+               return ret;
+
+       *temp = data;
+       /* The value is signed. */
+       if (*temp & 0x8000) {
+               *temp = (0x7fff & ~*temp) + 1;
+               *temp *= -1;
+       }
+
+       /* The value is converted into deci-centigrade scale */
+       /* Units of LSB = 1 / 256 degree Celsius */
+       *temp = *temp * 10 / 256;
+       return 0;
+}
+
+static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
+{
+       int temp, vavg, vbatt, ret;
+       u32 val;
+
+       ret = regmap_read(chip->regmap, MAX17042_AvgVCELL, &val);
+       if (ret < 0)
+               goto health_error;
+
+       /* bits [0-3] unused */
+       vavg = val * 625 / 8;
+       /* Convert to millivolts */
+       vavg /= 1000;
+
+       ret = regmap_read(chip->regmap, MAX17042_VCELL, &val);
+       if (ret < 0)
+               goto health_error;
+
+       /* bits [0-3] unused */
+       vbatt = val * 625 / 8;
+       /* Convert to millivolts */
+       vbatt /= 1000;
+
+       if (vavg < chip->pdata->vmin) {
+               *health = POWER_SUPPLY_HEALTH_DEAD;
+               goto out;
+       }
+
+       if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) {
+               *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               goto out;
+       }
+
+       ret = max17042_get_temperature(chip, &temp);
+       if (ret < 0)
+               goto health_error;
+
+       if (temp <= chip->pdata->temp_min) {
+               *health = POWER_SUPPLY_HEALTH_COLD;
+               goto out;
+       }
+
+       if (temp >= chip->pdata->temp_max) {
+               *health = POWER_SUPPLY_HEALTH_OVERHEAT;
+               goto out;
+       }
+
+       *health = POWER_SUPPLY_HEALTH_GOOD;
+
+out:
+       return 0;
+
+health_error:
+       return ret;
+}
+
+static int max17042_get_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       struct max17042_chip *chip = power_supply_get_drvdata(psy);
+       struct regmap *map = chip->regmap;
+       int ret;
+       u32 data;
+
+       if (!chip->init_complete)
+               return -EAGAIN;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+               ret = regmap_read(map, MAX17042_STATUS, &data);
+               if (ret < 0)
+                       return ret;
+
+               if (data & MAX17042_STATUS_BattAbsent)
+                       val->intval = 0;
+               else
+                       val->intval = 1;
+               break;
+       case POWER_SUPPLY_PROP_CYCLE_COUNT:
+               ret = regmap_read(map, MAX17042_Cycles, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               ret = regmap_read(map, MAX17042_MinMaxVolt, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data >> 8;
+               val->intval *= 20000; /* Units of LSB = 20mV */
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
+                       ret = regmap_read(map, MAX17042_V_empty, &data);
+               else
+                       ret = regmap_read(map, MAX17047_V_empty, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data >> 7;
+               val->intval *= 10000; /* Units of LSB = 10mV */
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = regmap_read(map, MAX17042_VCELL, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data * 625 / 8;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               ret = regmap_read(map, MAX17042_AvgVCELL, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data * 625 / 8;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+               ret = regmap_read(map, MAX17042_OCVInternal, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data * 625 / 8;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = regmap_read(map, MAX17042_RepSOC, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data >> 8;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               ret = regmap_read(map, MAX17042_FullCAP, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data * 1000 / 2;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+               ret = regmap_read(map, MAX17042_QH, &data);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = data * 1000 / 2;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = max17042_get_temperature(chip, &val->intval);
+               if (ret < 0)
+                       return ret;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+               if (ret < 0)
+                       return ret;
+               /* LSB is Alert Minimum. In deci-centigrade */
+               val->intval = (data & 0xff) * 10;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+               if (ret < 0)
+                       return ret;
+               /* MSB is Alert Maximum. In deci-centigrade */
+               val->intval = (data >> 8) * 10;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_MIN:
+               val->intval = chip->pdata->temp_min;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_MAX:
+               val->intval = chip->pdata->temp_max;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = max17042_get_battery_health(chip, &val->intval);
+               if (ret < 0)
+                       return ret;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               if (chip->pdata->enable_current_sense) {
+                       ret = regmap_read(map, MAX17042_Current, &data);
+                       if (ret < 0)
+                               return ret;
+
+                       val->intval = data;
+                       if (val->intval & 0x8000) {
+                               /* Negative */
+                               val->intval = ~val->intval & 0x7fff;
+                               val->intval++;
+                               val->intval *= -1;
+                       }
+                       val->intval *= 1562500 / chip->pdata->r_sns;
+               } else {
+                       return -EINVAL;
+               }
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               if (chip->pdata->enable_current_sense) {
+                       ret = regmap_read(map, MAX17042_AvgCurrent, &data);
+                       if (ret < 0)
+                               return ret;
+
+                       val->intval = data;
+                       if (val->intval & 0x8000) {
+                               /* Negative */
+                               val->intval = ~val->intval & 0x7fff;
+                               val->intval++;
+                               val->intval *= -1;
+                       }
+                       val->intval *= 1562500 / chip->pdata->r_sns;
+               } else {
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int max17042_set_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           const union power_supply_propval *val)
+{
+       struct max17042_chip *chip = power_supply_get_drvdata(psy);
+       struct regmap *map = chip->regmap;
+       int ret = 0;
+       u32 data;
+       int8_t temp;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+               if (ret < 0)
+                       return ret;
+
+               /* Input in deci-centigrade, convert to centigrade */
+               temp = val->intval / 10;
+               /* force min < max */
+               if (temp >= (int8_t)(data >> 8))
+                       temp = (int8_t)(data >> 8) - 1;
+               /* Write both MAX and MIN ALERT */
+               data = (data & 0xff00) + temp;
+               ret = regmap_write(map, MAX17042_TALRT_Th, data);
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+               if (ret < 0)
+                       return ret;
+
+               /* Input in Deci-Centigrade, convert to centigrade */
+               temp = val->intval / 10;
+               /* force max > min */
+               if (temp <= (int8_t)(data & 0xff))
+                       temp = (int8_t)(data & 0xff) + 1;
+               /* Write both MAX and MIN ALERT */
+               data = (data & 0xff) + (temp << 8);
+               ret = regmap_write(map, MAX17042_TALRT_Th, data);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int max17042_property_is_writeable(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = 1;
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
+{
+       int retries = 8;
+       int ret;
+       u32 read_value;
+
+       do {
+               ret = regmap_write(map, reg, value);
+               regmap_read(map, reg, &read_value);
+               if (read_value != value) {
+                       ret = -EIO;
+                       retries--;
+               }
+       } while (retries && read_value != value);
+
+       if (ret < 0)
+               pr_err("%s: err %d\n", __func__, ret);
+
+       return ret;
+}
+
+static inline void max17042_override_por(struct regmap *map,
+                                        u8 reg, u16 value)
+{
+       if (value)
+               regmap_write(map, reg, value);
+}
+
+static inline void max10742_unlock_model(struct max17042_chip *chip)
+{
+       struct regmap *map = chip->regmap;
+
+       regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
+       regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
+}
+
+static inline void max10742_lock_model(struct max17042_chip *chip)
+{
+       struct regmap *map = chip->regmap;
+
+       regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1);
+       regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2);
+}
+
+static inline void max17042_write_model_data(struct max17042_chip *chip,
+                                       u8 addr, int size)
+{
+       struct regmap *map = chip->regmap;
+       int i;
+
+       for (i = 0; i < size; i++)
+               regmap_write(map, addr + i,
+                       chip->pdata->config_data->cell_char_tbl[i]);
+}
+
+static inline void max17042_read_model_data(struct max17042_chip *chip,
+                                       u8 addr, u32 *data, int size)
+{
+       struct regmap *map = chip->regmap;
+       int i;
+
+       for (i = 0; i < size; i++)
+               regmap_read(map, addr + i, &data[i]);
+}
+
+static inline int max17042_model_data_compare(struct max17042_chip *chip,
+                                       u16 *data1, u16 *data2, int size)
+{
+       int i;
+
+       if (memcmp(data1, data2, size)) {
+               dev_err(&chip->client->dev, "%s compare failed\n", __func__);
+               for (i = 0; i < size; i++)
+                       dev_info(&chip->client->dev, "0x%x, 0x%x",
+                               data1[i], data2[i]);
+               dev_info(&chip->client->dev, "\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int max17042_init_model(struct max17042_chip *chip)
+{
+       int ret;
+       int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
+       u32 *temp_data;
+
+       temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
+       if (!temp_data)
+               return -ENOMEM;
+
+       max10742_unlock_model(chip);
+       max17042_write_model_data(chip, MAX17042_MODELChrTbl,
+                               table_size);
+       max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
+                               table_size);
+
+       ret = max17042_model_data_compare(
+               chip,
+               chip->pdata->config_data->cell_char_tbl,
+               (u16 *)temp_data,
+               table_size);
+
+       max10742_lock_model(chip);
+       kfree(temp_data);
+
+       return ret;
+}
+
+static int max17042_verify_model_lock(struct max17042_chip *chip)
+{
+       int i;
+       int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
+       u32 *temp_data;
+       int ret = 0;
+
+       temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
+       if (!temp_data)
+               return -ENOMEM;
+
+       max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
+                               table_size);
+       for (i = 0; i < table_size; i++)
+               if (temp_data[i])
+                       ret = -EINVAL;
+
+       kfree(temp_data);
+       return ret;
+}
+
+static void max17042_write_config_regs(struct max17042_chip *chip)
+{
+       struct max17042_config_data *config = chip->pdata->config_data;
+       struct regmap *map = chip->regmap;
+
+       regmap_write(map, MAX17042_CONFIG, config->config);
+       regmap_write(map, MAX17042_LearnCFG, config->learn_cfg);
+       regmap_write(map, MAX17042_FilterCFG,
+                       config->filter_cfg);
+       regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg);
+       if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 ||
+                       chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)
+               regmap_write(map, MAX17047_FullSOCThr,
+                                               config->full_soc_thresh);
+}
+
+static void  max17042_write_custom_regs(struct max17042_chip *chip)
+{
+       struct max17042_config_data *config = chip->pdata->config_data;
+       struct regmap *map = chip->regmap;
+
+       max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0);
+       max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0);
+       max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term);
+       if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) {
+               regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco);
+               max17042_write_verify_reg(map, MAX17042_K_empty0,
+                                       config->kempty0);
+       } else {
+               max17042_write_verify_reg(map, MAX17047_QRTbl00,
+                                               config->qrtbl00);
+               max17042_write_verify_reg(map, MAX17047_QRTbl10,
+                                               config->qrtbl10);
+               max17042_write_verify_reg(map, MAX17047_QRTbl20,
+                                               config->qrtbl20);
+               max17042_write_verify_reg(map, MAX17047_QRTbl30,
+                                               config->qrtbl30);
+       }
+}
+
+static void max17042_update_capacity_regs(struct max17042_chip *chip)
+{
+       struct max17042_config_data *config = chip->pdata->config_data;
+       struct regmap *map = chip->regmap;
+
+       max17042_write_verify_reg(map, MAX17042_FullCAP,
+                               config->fullcap);
+       regmap_write(map, MAX17042_DesignCap, config->design_cap);
+       max17042_write_verify_reg(map, MAX17042_FullCAPNom,
+                               config->fullcapnom);
+}
+
+static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
+{
+       unsigned int vfSoc;
+       struct regmap *map = chip->regmap;
+
+       regmap_read(map, MAX17042_VFSOC, &vfSoc);
+       regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
+       max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc);
+       regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
+}
+
+static void max17042_load_new_capacity_params(struct max17042_chip *chip)
+{
+       u32 full_cap0, rep_cap, dq_acc, vfSoc;
+       u32 rem_cap;
+
+       struct max17042_config_data *config = chip->pdata->config_data;
+       struct regmap *map = chip->regmap;
+
+       regmap_read(map, MAX17042_FullCAP0, &full_cap0);
+       regmap_read(map, MAX17042_VFSOC, &vfSoc);
+
+       /* fg_vfSoc needs to shifted by 8 bits to get the
+        * perc in 1% accuracy, to get the right rem_cap multiply
+        * full_cap0, fg_vfSoc and devide by 100
+        */
+       rem_cap = ((vfSoc >> 8) * full_cap0) / 100;
+       max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap);
+
+       rep_cap = rem_cap;
+       max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap);
+
+       /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */
+       dq_acc = config->fullcap / dQ_ACC_DIV;
+       max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc);
+       max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200);
+
+       max17042_write_verify_reg(map, MAX17042_FullCAP,
+                       config->fullcap);
+       regmap_write(map, MAX17042_DesignCap,
+                       config->design_cap);
+       max17042_write_verify_reg(map, MAX17042_FullCAPNom,
+                       config->fullcapnom);
+       /* Update SOC register with new SOC */
+       regmap_write(map, MAX17042_RepSOC, vfSoc);
+}
+
+/*
+ * Block write all the override values coming from platform data.
+ * This function MUST be called before the POR initialization proceedure
+ * specified by maxim.
+ */
+static inline void max17042_override_por_values(struct max17042_chip *chip)
+{
+       struct regmap *map = chip->regmap;
+       struct max17042_config_data *config = chip->pdata->config_data;
+
+       max17042_override_por(map, MAX17042_TGAIN, config->tgain);
+       max17042_override_por(map, MAx17042_TOFF, config->toff);
+       max17042_override_por(map, MAX17042_CGAIN, config->cgain);
+       max17042_override_por(map, MAX17042_COFF, config->coff);
+
+       max17042_override_por(map, MAX17042_VALRT_Th, config->valrt_thresh);
+       max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh);
+       max17042_override_por(map, MAX17042_SALRT_Th,
+                                               config->soc_alrt_thresh);
+       max17042_override_por(map, MAX17042_CONFIG, config->config);
+       max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer);
+
+       max17042_override_por(map, MAX17042_DesignCap, config->design_cap);
+       max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term);
+
+       max17042_override_por(map, MAX17042_AtRate, config->at_rate);
+       max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg);
+       max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg);
+       max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg);
+       max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg);
+       max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
+
+       max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
+       max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
+       if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
+               max17042_override_por(map, MAX17042_SOC_empty,
+                                               config->socempty);
+       max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
+       max17042_override_por(map, MAX17042_dQacc, config->dqacc);
+       max17042_override_por(map, MAX17042_dPacc, config->dpacc);
+
+       if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
+               max17042_override_por(map, MAX17042_V_empty, config->vempty);
+       else
+               max17042_override_por(map, MAX17047_V_empty, config->vempty);
+       max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
+       max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
+       max17042_override_por(map, MAX17042_FCTC, config->fctc);
+       max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
+       max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
+       if (chip->chip_type) {
+               max17042_override_por(map, MAX17042_EmptyTempCo,
+                                               config->empty_tempco);
+               max17042_override_por(map, MAX17042_K_empty0,
+                                               config->kempty0);
+       }
+}
+
+static int max17042_init_chip(struct max17042_chip *chip)
+{
+       struct regmap *map = chip->regmap;
+       int ret;
+
+       max17042_override_por_values(chip);
+       /* After Power up, the MAX17042 requires 500mS in order
+        * to perform signal debouncing and initial SOC reporting
+        */
+       msleep(500);
+
+       /* Initialize configaration */
+       max17042_write_config_regs(chip);
+
+       /* write cell characterization data */
+       ret = max17042_init_model(chip);
+       if (ret) {
+               dev_err(&chip->client->dev, "%s init failed\n",
+                       __func__);
+               return -EIO;
+       }
+
+       ret = max17042_verify_model_lock(chip);
+       if (ret) {
+               dev_err(&chip->client->dev, "%s lock verify failed\n",
+                       __func__);
+               return -EIO;
+       }
+       /* write custom parameters */
+       max17042_write_custom_regs(chip);
+
+       /* update capacity params */
+       max17042_update_capacity_regs(chip);
+
+       /* delay must be atleast 350mS to allow VFSOC
+        * to be calculated from the new configuration
+        */
+       msleep(350);
+
+       /* reset vfsoc0 reg */
+       max17042_reset_vfsoc0_reg(chip);
+
+       /* load new capacity params */
+       max17042_load_new_capacity_params(chip);
+
+       /* Init complete, Clear the POR bit */
+       regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0);
+       return 0;
+}
+
+static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
+{
+       struct regmap *map = chip->regmap;
+       u32 soc, soc_tr;
+
+       /* program interrupt thesholds such that we should
+        * get interrupt for every 'off' perc change in the soc
+        */
+       regmap_read(map, MAX17042_RepSOC, &soc);
+       soc >>= 8;
+       soc_tr = (soc + off) << 8;
+       soc_tr |= (soc - off);
+       regmap_write(map, MAX17042_SALRT_Th, soc_tr);
+}
+
+static irqreturn_t max17042_thread_handler(int id, void *dev)
+{
+       struct max17042_chip *chip = dev;
+       u32 val;
+
+       regmap_read(chip->regmap, MAX17042_STATUS, &val);
+       if ((val & STATUS_INTR_SOCMIN_BIT) ||
+               (val & STATUS_INTR_SOCMAX_BIT)) {
+               dev_info(&chip->client->dev, "SOC threshold INTR\n");
+               max17042_set_soc_threshold(chip, 1);
+       }
+
+       power_supply_changed(chip->battery);
+       return IRQ_HANDLED;
+}
+
+static void max17042_init_worker(struct work_struct *work)
+{
+       struct max17042_chip *chip = container_of(work,
+                               struct max17042_chip, work);
+       int ret;
+
+       /* Initialize registers according to values from the platform data */
+       if (chip->pdata->enable_por_init && chip->pdata->config_data) {
+               ret = max17042_init_chip(chip);
+               if (ret)
+                       return;
+       }
+
+       chip->init_complete = 1;
+}
+
+#ifdef CONFIG_OF
+static struct max17042_platform_data *
+max17042_get_pdata(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       u32 prop;
+       struct max17042_platform_data *pdata;
+
+       if (!np)
+               return dev->platform_data;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       /*
+        * Require current sense resistor value to be specified for
+        * current-sense functionality to be enabled at all.
+        */
+       if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) {
+               pdata->r_sns = prop;
+               pdata->enable_current_sense = true;
+       }
+
+       if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min))
+               pdata->temp_min = INT_MIN;
+       if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max))
+               pdata->temp_max = INT_MAX;
+       if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin))
+               pdata->vmin = INT_MIN;
+       if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
+               pdata->vmax = INT_MAX;
+
+       return pdata;
+}
+#else
+static struct max17042_platform_data *
+max17042_get_pdata(struct device *dev)
+{
+       return dev->platform_data;
+}
+#endif
+
+static const struct regmap_config max17042_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+       .val_format_endian = REGMAP_ENDIAN_NATIVE,
+};
+
+static const struct power_supply_desc max17042_psy_desc = {
+       .name           = "max170xx_battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property   = max17042_get_property,
+       .set_property   = max17042_set_property,
+       .property_is_writeable  = max17042_property_is_writeable,
+       .properties     = max17042_battery_props,
+       .num_properties = ARRAY_SIZE(max17042_battery_props),
+};
+
+static const struct power_supply_desc max17042_no_current_sense_psy_desc = {
+       .name           = "max170xx_battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property   = max17042_get_property,
+       .set_property   = max17042_set_property,
+       .property_is_writeable  = max17042_property_is_writeable,
+       .properties     = max17042_battery_props,
+       .num_properties = ARRAY_SIZE(max17042_battery_props) - 2,
+};
+
+static int max17042_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
+       struct power_supply_config psy_cfg = {};
+       struct max17042_chip *chip;
+       int ret;
+       int i;
+       u32 val;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+               return -EIO;
+
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->client = client;
+       chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config);
+       if (IS_ERR(chip->regmap)) {
+               dev_err(&client->dev, "Failed to initialize regmap\n");
+               return -EINVAL;
+       }
+
+       chip->pdata = max17042_get_pdata(&client->dev);
+       if (!chip->pdata) {
+               dev_err(&client->dev, "no platform data provided\n");
+               return -EINVAL;
+       }
+
+       i2c_set_clientdata(client, chip);
+       chip->chip_type = id->driver_data;
+       psy_cfg.drv_data = chip;
+
+       /* When current is not measured,
+        * CURRENT_NOW and CURRENT_AVG properties should be invisible. */
+       if (!chip->pdata->enable_current_sense)
+               max17042_desc = &max17042_no_current_sense_psy_desc;
+
+       if (chip->pdata->r_sns == 0)
+               chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
+
+       if (chip->pdata->init_data)
+               for (i = 0; i < chip->pdata->num_init_data; i++)
+                       regmap_write(chip->regmap,
+                                       chip->pdata->init_data[i].addr,
+                                       chip->pdata->init_data[i].data);
+
+       if (!chip->pdata->enable_current_sense) {
+               regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
+               regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
+               regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
+       }
+
+       chip->battery = devm_power_supply_register(&client->dev, max17042_desc,
+                                                  &psy_cfg);
+       if (IS_ERR(chip->battery)) {
+               dev_err(&client->dev, "failed: power supply register\n");
+               return PTR_ERR(chip->battery);
+       }
+
+       if (client->irq) {
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                               NULL,
+                                               max17042_thread_handler,
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_ONESHOT,
+                                               chip->battery->desc->name,
+                                               chip);
+               if (!ret) {
+                       regmap_update_bits(chip->regmap, MAX17042_CONFIG,
+                                       CONFIG_ALRT_BIT_ENBL,
+                                       CONFIG_ALRT_BIT_ENBL);
+                       max17042_set_soc_threshold(chip, 1);
+               } else {
+                       client->irq = 0;
+                       dev_err(&client->dev, "%s(): cannot get IRQ\n",
+                               __func__);
+               }
+       }
+
+       regmap_read(chip->regmap, MAX17042_STATUS, &val);
+       if (val & STATUS_POR_BIT) {
+               INIT_WORK(&chip->work, max17042_init_worker);
+               schedule_work(&chip->work);
+       } else {
+               chip->init_complete = 1;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max17042_suspend(struct device *dev)
+{
+       struct max17042_chip *chip = dev_get_drvdata(dev);
+
+       /*
+        * disable the irq and enable irq_wake
+        * capability to the interrupt line.
+        */
+       if (chip->client->irq) {
+               disable_irq(chip->client->irq);
+               enable_irq_wake(chip->client->irq);
+       }
+
+       return 0;
+}
+
+static int max17042_resume(struct device *dev)
+{
+       struct max17042_chip *chip = dev_get_drvdata(dev);
+
+       if (chip->client->irq) {
+               disable_irq_wake(chip->client->irq);
+               enable_irq(chip->client->irq);
+               /* re-program the SOC thresholds to 1% change */
+               max17042_set_soc_threshold(chip, 1);
+       }
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
+                       max17042_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max17042_dt_match[] = {
+       { .compatible = "maxim,max17042" },
+       { .compatible = "maxim,max17047" },
+       { .compatible = "maxim,max17050" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, max17042_dt_match);
+#endif
+
+static const struct i2c_device_id max17042_id[] = {
+       { "max17042", MAXIM_DEVICE_TYPE_MAX17042 },
+       { "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
+       { "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max17042_id);
+
+static struct i2c_driver max17042_i2c_driver = {
+       .driver = {
+               .name   = "max17042",
+               .of_match_table = of_match_ptr(max17042_dt_match),
+               .pm     = &max17042_pm_ops,
+       },
+       .probe          = max17042_probe,
+       .id_table       = max17042_id,
+};
+module_i2c_driver(max17042_i2c_driver);
+
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_DESCRIPTION("MAX17042 Fuel Gauge");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/max77693_charger.c b/drivers/power/supply/max77693_charger.c
new file mode 100644 (file)
index 0000000..060cab5
--- /dev/null
@@ -0,0 +1,771 @@
+/*
+ * max77693_charger.c - Battery charger driver for the Maxim 77693
+ *
+ * Copyright (C) 2014 Samsung Electronics
+ * Krzysztof Kozlowski <k.kozlowski@samsung.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-common.h>
+#include <linux/mfd/max77693-private.h>
+
+#define MAX77693_CHARGER_NAME                          "max77693-charger"
+static const char *max77693_charger_model              = "MAX77693";
+static const char *max77693_charger_manufacturer       = "Maxim Integrated";
+
+struct max77693_charger {
+       struct device           *dev;
+       struct max77693_dev     *max77693;
+       struct power_supply     *charger;
+
+       u32 constant_volt;
+       u32 min_system_volt;
+       u32 thermal_regulation_temp;
+       u32 batttery_overcurrent;
+       u32 charge_input_threshold_volt;
+};
+
+static int max77693_get_charger_state(struct regmap *regmap, int *val)
+{
+       int ret;
+       unsigned int data;
+
+       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
+       if (ret < 0)
+               return ret;
+
+       data &= CHG_DETAILS_01_CHG_MASK;
+       data >>= CHG_DETAILS_01_CHG_SHIFT;
+
+       switch (data) {
+       case MAX77693_CHARGING_PREQUALIFICATION:
+       case MAX77693_CHARGING_FAST_CONST_CURRENT:
+       case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
+       case MAX77693_CHARGING_TOP_OFF:
+       /* In high temp the charging current is reduced, but still charging */
+       case MAX77693_CHARGING_HIGH_TEMP:
+               *val = POWER_SUPPLY_STATUS_CHARGING;
+               break;
+       case MAX77693_CHARGING_DONE:
+               *val = POWER_SUPPLY_STATUS_FULL;
+               break;
+       case MAX77693_CHARGING_TIMER_EXPIRED:
+       case MAX77693_CHARGING_THERMISTOR_SUSPEND:
+               *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               break;
+       case MAX77693_CHARGING_OFF:
+       case MAX77693_CHARGING_OVER_TEMP:
+       case MAX77693_CHARGING_WATCHDOG_EXPIRED:
+               *val = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       case MAX77693_CHARGING_RESERVED:
+       default:
+               *val = POWER_SUPPLY_STATUS_UNKNOWN;
+       }
+
+       return 0;
+}
+
+static int max77693_get_charge_type(struct regmap *regmap, int *val)
+{
+       int ret;
+       unsigned int data;
+
+       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
+       if (ret < 0)
+               return ret;
+
+       data &= CHG_DETAILS_01_CHG_MASK;
+       data >>= CHG_DETAILS_01_CHG_SHIFT;
+
+       switch (data) {
+       case MAX77693_CHARGING_PREQUALIFICATION:
+       /*
+        * Top-off: trickle or fast? In top-off the current varies between
+        * 100 and 250 mA. It is higher than prequalification current.
+        */
+       case MAX77693_CHARGING_TOP_OFF:
+               *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+               break;
+       case MAX77693_CHARGING_FAST_CONST_CURRENT:
+       case MAX77693_CHARGING_FAST_CONST_VOLTAGE:
+       /* In high temp the charging current is reduced, but still charging */
+       case MAX77693_CHARGING_HIGH_TEMP:
+               *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               break;
+       case MAX77693_CHARGING_DONE:
+       case MAX77693_CHARGING_TIMER_EXPIRED:
+       case MAX77693_CHARGING_THERMISTOR_SUSPEND:
+       case MAX77693_CHARGING_OFF:
+       case MAX77693_CHARGING_OVER_TEMP:
+       case MAX77693_CHARGING_WATCHDOG_EXPIRED:
+               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               break;
+       case MAX77693_CHARGING_RESERVED:
+       default:
+               *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+       }
+
+       return 0;
+}
+
+/*
+ * Supported health statuses:
+ *  - POWER_SUPPLY_HEALTH_DEAD
+ *  - POWER_SUPPLY_HEALTH_GOOD
+ *  - POWER_SUPPLY_HEALTH_OVERVOLTAGE
+ *  - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE
+ *  - POWER_SUPPLY_HEALTH_UNKNOWN
+ *  - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
+ */
+static int max77693_get_battery_health(struct regmap *regmap, int *val)
+{
+       int ret;
+       unsigned int data;
+
+       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data);
+       if (ret < 0)
+               return ret;
+
+       data &= CHG_DETAILS_01_BAT_MASK;
+       data >>= CHG_DETAILS_01_BAT_SHIFT;
+
+       switch (data) {
+       case MAX77693_BATTERY_NOBAT:
+               *val = POWER_SUPPLY_HEALTH_DEAD;
+               break;
+       case MAX77693_BATTERY_PREQUALIFICATION:
+       case MAX77693_BATTERY_GOOD:
+       case MAX77693_BATTERY_LOWVOLTAGE:
+               *val = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case MAX77693_BATTERY_TIMER_EXPIRED:
+               /*
+                * Took longer to charge than expected, charging suspended.
+                * Damaged battery?
+                */
+               *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+               break;
+       case MAX77693_BATTERY_OVERVOLTAGE:
+               *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               break;
+       case MAX77693_BATTERY_OVERCURRENT:
+               *val = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               break;
+       case MAX77693_BATTERY_RESERVED:
+       default:
+               *val = POWER_SUPPLY_HEALTH_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int max77693_get_present(struct regmap *regmap, int *val)
+{
+       unsigned int data;
+       int ret;
+
+       /*
+        * Read CHG_INT_OK register. High DETBAT bit here should be
+        * equal to value 0x0 in CHG_DETAILS_01/BAT field.
+        */
+       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
+       if (ret < 0)
+               return ret;
+
+       *val = (data & CHG_INT_OK_DETBAT_MASK) ? 0 : 1;
+
+       return 0;
+}
+
+static int max77693_get_online(struct regmap *regmap, int *val)
+{
+       unsigned int data;
+       int ret;
+
+       ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data);
+       if (ret < 0)
+               return ret;
+
+       *val = (data & CHG_INT_OK_CHGIN_MASK) ? 1 : 0;
+
+       return 0;
+}
+
+static enum power_supply_property max77693_charger_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static int max77693_charger_get_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       struct max77693_charger *chg = power_supply_get_drvdata(psy);
+       struct regmap *regmap = chg->max77693->regmap;
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = max77693_get_charger_state(regmap, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               ret = max77693_get_charge_type(regmap, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = max77693_get_battery_health(regmap, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               ret = max77693_get_present(regmap, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = max77693_get_online(regmap, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = max77693_charger_model;
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = max77693_charger_manufacturer;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+static const struct power_supply_desc max77693_charger_desc = {
+       .name           = MAX77693_CHARGER_NAME,
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = max77693_charger_props,
+       .num_properties = ARRAY_SIZE(max77693_charger_props),
+       .get_property   = max77693_charger_get_property,
+};
+
+static ssize_t device_attr_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count,
+               int (*fn)(struct max77693_charger *, unsigned long))
+{
+       struct max77693_charger *chg = dev_get_drvdata(dev);
+       unsigned long val;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &val);
+       if (ret)
+               return ret;
+
+       ret = fn(chg, val);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static ssize_t fast_charge_timer_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct max77693_charger *chg = dev_get_drvdata(dev);
+       unsigned int data, val;
+       int ret;
+
+       ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01,
+                       &data);
+       if (ret < 0)
+               return ret;
+
+       data &= CHG_CNFG_01_FCHGTIME_MASK;
+       data >>= CHG_CNFG_01_FCHGTIME_SHIFT;
+       switch (data) {
+       case 0x1 ... 0x7:
+               /* Starting from 4 hours, step by 2 hours */
+               val = 4 + (data - 1) * 2;
+               break;
+       case 0x0:
+       default:
+               val = 0;
+               break;
+       }
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static int max77693_set_fast_charge_timer(struct max77693_charger *chg,
+               unsigned long hours)
+{
+       unsigned int data;
+
+       /*
+        * 0x00 - disable
+        * 0x01 - 4h
+        * 0x02 - 6h
+        * ...
+        * 0x07 - 16h
+        * Round down odd values.
+        */
+       switch (hours) {
+       case 4 ... 16:
+               data = (hours - 4) / 2 + 1;
+               break;
+       case 0:
+               /* Disable */
+               data = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+       data <<= CHG_CNFG_01_FCHGTIME_SHIFT;
+
+       return regmap_update_bits(chg->max77693->regmap,
+                       MAX77693_CHG_REG_CHG_CNFG_01,
+                       CHG_CNFG_01_FCHGTIME_MASK, data);
+}
+
+static ssize_t fast_charge_timer_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       return device_attr_store(dev, attr, buf, count,
+                       max77693_set_fast_charge_timer);
+}
+
+static ssize_t top_off_threshold_current_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct max77693_charger *chg = dev_get_drvdata(dev);
+       unsigned int data, val;
+       int ret;
+
+       ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
+                       &data);
+       if (ret < 0)
+               return ret;
+
+       data &= CHG_CNFG_03_TOITH_MASK;
+       data >>= CHG_CNFG_03_TOITH_SHIFT;
+
+       if (data <= 0x04)
+               val = 100000 + data * 25000;
+       else
+               val = data * 50000;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static int max77693_set_top_off_threshold_current(struct max77693_charger *chg,
+               unsigned long uamp)
+{
+       unsigned int data;
+
+       if (uamp < 100000 || uamp > 350000)
+               return -EINVAL;
+
+       if (uamp <= 200000)
+               data = (uamp - 100000) / 25000;
+       else
+               /* (200000, 350000> */
+               data = uamp / 50000;
+
+       data <<= CHG_CNFG_03_TOITH_SHIFT;
+
+       return regmap_update_bits(chg->max77693->regmap,
+                       MAX77693_CHG_REG_CHG_CNFG_03,
+                       CHG_CNFG_03_TOITH_MASK, data);
+}
+
+static ssize_t top_off_threshold_current_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       return device_attr_store(dev, attr, buf, count,
+                       max77693_set_top_off_threshold_current);
+}
+
+static ssize_t top_off_timer_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct max77693_charger *chg = dev_get_drvdata(dev);
+       unsigned int data, val;
+       int ret;
+
+       ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03,
+                       &data);
+       if (ret < 0)
+               return ret;
+
+       data &= CHG_CNFG_03_TOTIME_MASK;
+       data >>= CHG_CNFG_03_TOTIME_SHIFT;
+
+       val = data * 10;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static int max77693_set_top_off_timer(struct max77693_charger *chg,
+               unsigned long minutes)
+{
+       unsigned int data;
+
+       if (minutes > 70)
+               return -EINVAL;
+
+       data = minutes / 10;
+       data <<= CHG_CNFG_03_TOTIME_SHIFT;
+
+       return regmap_update_bits(chg->max77693->regmap,
+                       MAX77693_CHG_REG_CHG_CNFG_03,
+                       CHG_CNFG_03_TOTIME_MASK, data);
+}
+
+static ssize_t top_off_timer_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       return device_attr_store(dev, attr, buf, count,
+                       max77693_set_top_off_timer);
+}
+
+static DEVICE_ATTR_RW(fast_charge_timer);
+static DEVICE_ATTR_RW(top_off_threshold_current);
+static DEVICE_ATTR_RW(top_off_timer);
+
+static int max77693_set_constant_volt(struct max77693_charger *chg,
+               unsigned int uvolt)
+{
+       unsigned int data;
+
+       /*
+        * 0x00 - 3.650 V
+        * 0x01 - 3.675 V
+        * ...
+        * 0x1b - 4.325 V
+        * 0x1c - 4.340 V
+        * 0x1d - 4.350 V
+        * 0x1e - 4.375 V
+        * 0x1f - 4.400 V
+        */
+       if (uvolt >= 3650000 && uvolt < 4340000)
+               data = (uvolt - 3650000) / 25000;
+       else if (uvolt >= 4340000 && uvolt < 4350000)
+               data = 0x1c;
+       else if (uvolt >= 4350000 && uvolt <= 4400000)
+               data = 0x1d + (uvolt - 4350000) / 25000;
+       else {
+               dev_err(chg->dev, "Wrong value for charging constant voltage\n");
+               return -EINVAL;
+       }
+
+       data <<= CHG_CNFG_04_CHGCVPRM_SHIFT;
+
+       dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt,
+                       data);
+
+       return regmap_update_bits(chg->max77693->regmap,
+                       MAX77693_CHG_REG_CHG_CNFG_04,
+                       CHG_CNFG_04_CHGCVPRM_MASK, data);
+}
+
+static int max77693_set_min_system_volt(struct max77693_charger *chg,
+               unsigned int uvolt)
+{
+       unsigned int data;
+
+       if (uvolt < 3000000 || uvolt > 3700000) {
+               dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n");
+               return -EINVAL;
+       }
+
+       data = (uvolt - 3000000) / 100000;
+
+       data <<= CHG_CNFG_04_MINVSYS_SHIFT;
+
+       dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n",
+                       uvolt, data);
+
+       return regmap_update_bits(chg->max77693->regmap,
+                       MAX77693_CHG_REG_CHG_CNFG_04,
+                       CHG_CNFG_04_MINVSYS_MASK, data);
+}
+
+static int max77693_set_thermal_regulation_temp(struct max77693_charger *chg,
+               unsigned int cels)
+{
+       unsigned int data;
+
+       switch (cels) {
+       case 70:
+       case 85:
+       case 100:
+       case 115:
+               data = (cels - 70) / 15;
+               break;
+       default:
+               dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n");
+               return -EINVAL;
+       }
+
+       data <<= CHG_CNFG_07_REGTEMP_SHIFT;
+
+       dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n",
+                       cels, data);
+
+       return regmap_update_bits(chg->max77693->regmap,
+                       MAX77693_CHG_REG_CHG_CNFG_07,
+                       CHG_CNFG_07_REGTEMP_MASK, data);
+}
+
+static int max77693_set_batttery_overcurrent(struct max77693_charger *chg,
+               unsigned int uamp)
+{
+       unsigned int data;
+
+       if (uamp && (uamp < 2000000 || uamp > 3500000)) {
+               dev_err(chg->dev, "Wrong value for battery overcurrent\n");
+               return -EINVAL;
+       }
+
+       if (uamp)
+               data = ((uamp - 2000000) / 250000) + 1;
+       else
+               data = 0; /* disable */
+
+       data <<= CHG_CNFG_12_B2SOVRC_SHIFT;
+
+       dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data);
+
+       return regmap_update_bits(chg->max77693->regmap,
+                       MAX77693_CHG_REG_CHG_CNFG_12,
+                       CHG_CNFG_12_B2SOVRC_MASK, data);
+}
+
+static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg,
+               unsigned int uvolt)
+{
+       unsigned int data;
+
+       switch (uvolt) {
+       case 4300000:
+               data = 0x0;
+               break;
+       case 4700000:
+       case 4800000:
+       case 4900000:
+               data = (uvolt - 4700000) / 100000;
+       default:
+               dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n");
+               return -EINVAL;
+       }
+
+       data <<= CHG_CNFG_12_VCHGINREG_SHIFT;
+
+       dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n",
+                       uvolt, data);
+
+       return regmap_update_bits(chg->max77693->regmap,
+                       MAX77693_CHG_REG_CHG_CNFG_12,
+                       CHG_CNFG_12_VCHGINREG_MASK, data);
+}
+
+/*
+ * Sets charger registers to proper and safe default values.
+ */
+static int max77693_reg_init(struct max77693_charger *chg)
+{
+       int ret;
+       unsigned int data;
+
+       /* Unlock charger register protection */
+       data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT);
+       ret = regmap_update_bits(chg->max77693->regmap,
+                               MAX77693_CHG_REG_CHG_CNFG_06,
+                               CHG_CNFG_06_CHGPROT_MASK, data);
+       if (ret) {
+               dev_err(chg->dev, "Error unlocking registers: %d\n", ret);
+               return ret;
+       }
+
+       ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER);
+       if (ret)
+               return ret;
+
+       ret = max77693_set_top_off_threshold_current(chg,
+                       DEFAULT_TOP_OFF_THRESHOLD_CURRENT);
+       if (ret)
+               return ret;
+
+       ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER);
+       if (ret)
+               return ret;
+
+       ret = max77693_set_constant_volt(chg, chg->constant_volt);
+       if (ret)
+               return ret;
+
+       ret = max77693_set_min_system_volt(chg, chg->min_system_volt);
+       if (ret)
+               return ret;
+
+       ret = max77693_set_thermal_regulation_temp(chg,
+                       chg->thermal_regulation_temp);
+       if (ret)
+               return ret;
+
+       ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent);
+       if (ret)
+               return ret;
+
+       return max77693_set_charge_input_threshold_volt(chg,
+                       chg->charge_input_threshold_volt);
+}
+
+#ifdef CONFIG_OF
+static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
+{
+       struct device_node *np = dev->of_node;
+
+       if (!np) {
+               dev_err(dev, "no charger OF node\n");
+               return -EINVAL;
+       }
+
+       if (of_property_read_u32(np, "maxim,constant-microvolt",
+                       &chg->constant_volt))
+               chg->constant_volt = DEFAULT_CONSTANT_VOLT;
+
+       if (of_property_read_u32(np, "maxim,min-system-microvolt",
+                       &chg->min_system_volt))
+               chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT;
+
+       if (of_property_read_u32(np, "maxim,thermal-regulation-celsius",
+                       &chg->thermal_regulation_temp))
+               chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP;
+
+       if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp",
+                       &chg->batttery_overcurrent))
+               chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT;
+
+       if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt",
+                       &chg->charge_input_threshold_volt))
+               chg->charge_input_threshold_volt =
+                       DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT;
+
+       return 0;
+}
+#else /* CONFIG_OF */
+static int max77693_dt_init(struct device *dev, struct max77693_charger *chg)
+{
+       return 0;
+}
+#endif /* CONFIG_OF */
+
+static int max77693_charger_probe(struct platform_device *pdev)
+{
+       struct max77693_charger *chg;
+       struct power_supply_config psy_cfg = {};
+       struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
+       int ret;
+
+       chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
+       if (!chg)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, chg);
+       chg->dev = &pdev->dev;
+       chg->max77693 = max77693;
+
+       ret = max77693_dt_init(&pdev->dev, chg);
+       if (ret)
+               return ret;
+
+       ret = max77693_reg_init(chg);
+       if (ret)
+               return ret;
+
+       psy_cfg.drv_data = chg;
+
+       ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer);
+       if (ret) {
+               dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n");
+               goto err;
+       }
+
+       ret = device_create_file(&pdev->dev,
+                       &dev_attr_top_off_threshold_current);
+       if (ret) {
+               dev_err(&pdev->dev, "failed: create top off current sysfs entry\n");
+               goto err;
+       }
+
+       ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer);
+       if (ret) {
+               dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n");
+               goto err;
+       }
+
+       chg->charger = power_supply_register(&pdev->dev,
+                                               &max77693_charger_desc,
+                                               &psy_cfg);
+       if (IS_ERR(chg->charger)) {
+               dev_err(&pdev->dev, "failed: power supply register\n");
+               ret = PTR_ERR(chg->charger);
+               goto err;
+       }
+
+       return 0;
+
+err:
+       device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
+       device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
+       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
+
+       return ret;
+}
+
+static int max77693_charger_remove(struct platform_device *pdev)
+{
+       struct max77693_charger *chg = platform_get_drvdata(pdev);
+
+       device_remove_file(&pdev->dev, &dev_attr_top_off_timer);
+       device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current);
+       device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer);
+
+       power_supply_unregister(chg->charger);
+
+       return 0;
+}
+
+static const struct platform_device_id max77693_charger_id[] = {
+       { "max77693-charger", 0, },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, max77693_charger_id);
+
+static struct platform_driver max77693_charger_driver = {
+       .driver = {
+               .name   = "max77693-charger",
+       },
+       .probe          = max77693_charger_probe,
+       .remove         = max77693_charger_remove,
+       .id_table       = max77693_charger_id,
+};
+module_platform_driver(max77693_charger_driver);
+
+MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
+MODULE_DESCRIPTION("Maxim 77693 charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/max8903_charger.c b/drivers/power/supply/max8903_charger.c
new file mode 100644 (file)
index 0000000..fdc73d6
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/power/max8903_charger.h>
+
+struct max8903_data {
+       struct max8903_pdata *pdata;
+       struct device *dev;
+       struct power_supply *psy;
+       struct power_supply_desc psy_desc;
+       bool fault;
+       bool usb_in;
+       bool ta_in;
+};
+
+static enum power_supply_property max8903_charger_props[] = {
+       POWER_SUPPLY_PROP_STATUS, /* Charger status output */
+       POWER_SUPPLY_PROP_ONLINE, /* External power source */
+       POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
+};
+
+static int max8903_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct max8903_data *data = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+               if (gpio_is_valid(data->pdata->chg)) {
+                       if (gpio_get_value(data->pdata->chg) == 0)
+                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+                       else if (data->usb_in || data->ta_in)
+                               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+                       else
+                               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               }
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = 0;
+               if (data->usb_in || data->ta_in)
+                       val->intval = 1;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               if (data->fault)
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static irqreturn_t max8903_dcin(int irq, void *_data)
+{
+       struct max8903_data *data = _data;
+       struct max8903_pdata *pdata = data->pdata;
+       bool ta_in;
+       enum power_supply_type old_type;
+
+       ta_in = gpio_get_value(pdata->dok) ? false : true;
+
+       if (ta_in == data->ta_in)
+               return IRQ_HANDLED;
+
+       data->ta_in = ta_in;
+
+       /* Set Current-Limit-Mode 1:DC 0:USB */
+       if (gpio_is_valid(pdata->dcm))
+               gpio_set_value(pdata->dcm, ta_in ? 1 : 0);
+
+       /* Charger Enable / Disable (cen is negated) */
+       if (gpio_is_valid(pdata->cen))
+               gpio_set_value(pdata->cen, ta_in ? 0 :
+                               (data->usb_in ? 0 : 1));
+
+       dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
+                       "Connected" : "Disconnected");
+
+       old_type = data->psy_desc.type;
+
+       if (data->ta_in)
+               data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
+       else if (data->usb_in)
+               data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+       else
+               data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+
+       if (old_type != data->psy_desc.type)
+               power_supply_changed(data->psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_usbin(int irq, void *_data)
+{
+       struct max8903_data *data = _data;
+       struct max8903_pdata *pdata = data->pdata;
+       bool usb_in;
+       enum power_supply_type old_type;
+
+       usb_in = gpio_get_value(pdata->uok) ? false : true;
+
+       if (usb_in == data->usb_in)
+               return IRQ_HANDLED;
+
+       data->usb_in = usb_in;
+
+       /* Do not touch Current-Limit-Mode */
+
+       /* Charger Enable / Disable (cen is negated) */
+       if (gpio_is_valid(pdata->cen))
+               gpio_set_value(pdata->cen, usb_in ? 0 :
+                               (data->ta_in ? 0 : 1));
+
+       dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
+                       "Connected" : "Disconnected");
+
+       old_type = data->psy_desc.type;
+
+       if (data->ta_in)
+               data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
+       else if (data->usb_in)
+               data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+       else
+               data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+
+       if (old_type != data->psy_desc.type)
+               power_supply_changed(data->psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_fault(int irq, void *_data)
+{
+       struct max8903_data *data = _data;
+       struct max8903_pdata *pdata = data->pdata;
+       bool fault;
+
+       fault = gpio_get_value(pdata->flt) ? false : true;
+
+       if (fault == data->fault)
+               return IRQ_HANDLED;
+
+       data->fault = fault;
+
+       if (fault)
+               dev_err(data->dev, "Charger suffers a fault and stops.\n");
+       else
+               dev_err(data->dev, "Charger recovered from a fault.\n");
+
+       return IRQ_HANDLED;
+}
+
+static struct max8903_pdata *max8903_parse_dt_data(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct max8903_pdata *pdata = NULL;
+
+       if (!np)
+               return NULL;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       pdata->dc_valid = false;
+       pdata->usb_valid = false;
+
+       pdata->cen = of_get_named_gpio(np, "cen-gpios", 0);
+       if (!gpio_is_valid(pdata->cen))
+               pdata->cen = -EINVAL;
+
+       pdata->chg = of_get_named_gpio(np, "chg-gpios", 0);
+       if (!gpio_is_valid(pdata->chg))
+               pdata->chg = -EINVAL;
+
+       pdata->flt = of_get_named_gpio(np, "flt-gpios", 0);
+       if (!gpio_is_valid(pdata->flt))
+               pdata->flt = -EINVAL;
+
+       pdata->usus = of_get_named_gpio(np, "usus-gpios", 0);
+       if (!gpio_is_valid(pdata->usus))
+               pdata->usus = -EINVAL;
+
+       pdata->dcm = of_get_named_gpio(np, "dcm-gpios", 0);
+       if (!gpio_is_valid(pdata->dcm))
+               pdata->dcm = -EINVAL;
+
+       pdata->dok = of_get_named_gpio(np, "dok-gpios", 0);
+       if (!gpio_is_valid(pdata->dok))
+               pdata->dok = -EINVAL;
+       else
+               pdata->dc_valid = true;
+
+       pdata->uok = of_get_named_gpio(np, "uok-gpios", 0);
+       if (!gpio_is_valid(pdata->uok))
+               pdata->uok = -EINVAL;
+       else
+               pdata->usb_valid = true;
+
+       return pdata;
+}
+
+static int max8903_setup_gpios(struct platform_device *pdev)
+{
+       struct max8903_data *data = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       struct max8903_pdata *pdata = pdev->dev.platform_data;
+       int ret = 0;
+       int gpio;
+       int ta_in = 0;
+       int usb_in = 0;
+
+       if (pdata->dc_valid) {
+               if (gpio_is_valid(pdata->dok)) {
+                       ret = devm_gpio_request(dev, pdata->dok,
+                                               data->psy_desc.name);
+                       if (ret) {
+                               dev_err(dev,
+                                       "Failed GPIO request for dok: %d err %d\n",
+                                       pdata->dok, ret);
+                               return ret;
+                       }
+
+                       gpio = pdata->dok; /* PULL_UPed Interrupt */
+                       ta_in = gpio_get_value(gpio) ? 0 : 1;
+               } else {
+                       dev_err(dev, "When DC is wired, DOK should be wired as well.\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (gpio_is_valid(pdata->dcm)) {
+               ret = devm_gpio_request(dev, pdata->dcm, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for dcm: %d err %d\n",
+                               pdata->dcm, ret);
+                       return ret;
+               }
+
+               gpio = pdata->dcm; /* Output */
+               gpio_set_value(gpio, ta_in);
+       }
+
+       if (pdata->usb_valid) {
+               if (gpio_is_valid(pdata->uok)) {
+                       ret = devm_gpio_request(dev, pdata->uok,
+                                               data->psy_desc.name);
+                       if (ret) {
+                               dev_err(dev,
+                                       "Failed GPIO request for uok: %d err %d\n",
+                                       pdata->uok, ret);
+                               return ret;
+                       }
+
+                       gpio = pdata->uok;
+                       usb_in = gpio_get_value(gpio) ? 0 : 1;
+               } else {
+                       dev_err(dev, "When USB is wired, UOK should be wired."
+                                       "as well.\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (gpio_is_valid(pdata->cen)) {
+               ret = devm_gpio_request(dev, pdata->cen, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for cen: %d err %d\n",
+                               pdata->cen, ret);
+                       return ret;
+               }
+
+               gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
+       }
+
+       if (gpio_is_valid(pdata->chg)) {
+               ret = devm_gpio_request(dev, pdata->chg, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for chg: %d err %d\n",
+                               pdata->chg, ret);
+                       return ret;
+               }
+       }
+
+       if (gpio_is_valid(pdata->flt)) {
+               ret = devm_gpio_request(dev, pdata->flt, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for flt: %d err %d\n",
+                               pdata->flt, ret);
+                       return ret;
+               }
+       }
+
+       if (gpio_is_valid(pdata->usus)) {
+               ret = devm_gpio_request(dev, pdata->usus, data->psy_desc.name);
+               if (ret) {
+                       dev_err(dev,
+                               "Failed GPIO request for usus: %d err %d\n",
+                               pdata->usus, ret);
+                       return ret;
+               }
+       }
+
+       data->fault = false;
+       data->ta_in = ta_in;
+       data->usb_in = usb_in;
+
+       return 0;
+}
+
+static int max8903_probe(struct platform_device *pdev)
+{
+       struct max8903_data *data;
+       struct device *dev = &pdev->dev;
+       struct max8903_pdata *pdata = pdev->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       int ret = 0;
+
+       data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       if (IS_ENABLED(CONFIG_OF) && !pdata && dev->of_node)
+               pdata = max8903_parse_dt_data(dev);
+
+       if (!pdata) {
+               dev_err(dev, "No platform data.\n");
+               return -EINVAL;
+       }
+
+       pdev->dev.platform_data = pdata;
+       data->pdata = pdata;
+       data->dev = dev;
+       platform_set_drvdata(pdev, data);
+
+       if (pdata->dc_valid == false && pdata->usb_valid == false) {
+               dev_err(dev, "No valid power sources.\n");
+               return -EINVAL;
+       }
+
+       ret = max8903_setup_gpios(pdev);
+       if (ret)
+               return ret;
+
+       data->psy_desc.name = "max8903_charger";
+       data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
+                       ((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
+                        POWER_SUPPLY_TYPE_BATTERY);
+       data->psy_desc.get_property = max8903_get_property;
+       data->psy_desc.properties = max8903_charger_props;
+       data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
+
+       psy_cfg.of_node = dev->of_node;
+       psy_cfg.drv_data = data;
+
+       data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
+       if (IS_ERR(data->psy)) {
+               dev_err(dev, "failed: power supply register.\n");
+               return PTR_ERR(data->psy);
+       }
+
+       if (pdata->dc_valid) {
+               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok),
+                                       NULL, max8903_dcin,
+                                       IRQF_TRIGGER_FALLING |
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       "MAX8903 DC IN", data);
+               if (ret) {
+                       dev_err(dev, "Cannot request irq %d for DC (%d)\n",
+                                       gpio_to_irq(pdata->dok), ret);
+                       return ret;
+               }
+       }
+
+       if (pdata->usb_valid) {
+               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok),
+                                       NULL, max8903_usbin,
+                                       IRQF_TRIGGER_FALLING |
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       "MAX8903 USB IN", data);
+               if (ret) {
+                       dev_err(dev, "Cannot request irq %d for USB (%d)\n",
+                                       gpio_to_irq(pdata->uok), ret);
+                       return ret;
+               }
+       }
+
+       if (gpio_is_valid(pdata->flt)) {
+               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
+                                       NULL, max8903_fault,
+                                       IRQF_TRIGGER_FALLING |
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       "MAX8903 Fault", data);
+               if (ret) {
+                       dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
+                                       gpio_to_irq(pdata->flt), ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct of_device_id max8903_match_ids[] = {
+       { .compatible = "maxim,max8903", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max8903_match_ids);
+
+static struct platform_driver max8903_driver = {
+       .probe  = max8903_probe,
+       .driver = {
+               .name   = "max8903-charger",
+               .of_match_table = max8903_match_ids
+       },
+};
+
+module_platform_driver(max8903_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MAX8903 Charger Driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_ALIAS("platform:max8903-charger");
diff --git a/drivers/power/supply/max8925_power.c b/drivers/power/supply/max8925_power.c
new file mode 100644 (file)
index 0000000..3b94620
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Battery driver for Maxim MAX8925
+ *
+ * Copyright (c) 2009-2010 Marvell International Ltd.
+ *     Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/max8925.h>
+
+/* registers in GPM */
+#define MAX8925_OUT5VEN                        0x54
+#define MAX8925_OUT3VEN                        0x58
+#define MAX8925_CHG_CNTL1              0x7c
+
+/* bits definition */
+#define MAX8925_CHG_STAT_VSYSLOW       (1 << 0)
+#define MAX8925_CHG_STAT_MODE_MASK     (3 << 2)
+#define MAX8925_CHG_STAT_EN_MASK       (1 << 4)
+#define MAX8925_CHG_MBDET              (1 << 1)
+#define MAX8925_CHG_AC_RANGE_MASK      (3 << 6)
+
+/* registers in ADC */
+#define MAX8925_ADC_RES_CNFG1          0x06
+#define MAX8925_ADC_AVG_CNFG1          0x07
+#define MAX8925_ADC_ACQ_CNFG1          0x08
+#define MAX8925_ADC_ACQ_CNFG2          0x09
+/* 2 bytes registers in below. MSB is 1st, LSB is 2nd. */
+#define MAX8925_ADC_AUX2               0x62
+#define MAX8925_ADC_VCHG               0x64
+#define MAX8925_ADC_VBBATT             0x66
+#define MAX8925_ADC_VMBATT             0x68
+#define MAX8925_ADC_ISNS               0x6a
+#define MAX8925_ADC_THM                        0x6c
+#define MAX8925_ADC_TDIE               0x6e
+#define MAX8925_CMD_AUX2               0xc8
+#define MAX8925_CMD_VCHG               0xd0
+#define MAX8925_CMD_VBBATT             0xd8
+#define MAX8925_CMD_VMBATT             0xe0
+#define MAX8925_CMD_ISNS               0xe8
+#define MAX8925_CMD_THM                        0xf0
+#define MAX8925_CMD_TDIE               0xf8
+
+enum {
+       MEASURE_AUX2,
+       MEASURE_VCHG,
+       MEASURE_VBBATT,
+       MEASURE_VMBATT,
+       MEASURE_ISNS,
+       MEASURE_THM,
+       MEASURE_TDIE,
+       MEASURE_MAX,
+};
+
+struct max8925_power_info {
+       struct max8925_chip     *chip;
+       struct i2c_client       *gpm;
+       struct i2c_client       *adc;
+
+       struct power_supply     *ac;
+       struct power_supply     *usb;
+       struct power_supply     *battery;
+       int                     irq_base;
+       unsigned                ac_online:1;
+       unsigned                usb_online:1;
+       unsigned                bat_online:1;
+       unsigned                chg_mode:2;
+       unsigned                batt_detect:1;  /* detecing MB by ID pin */
+       unsigned                topoff_threshold:2;
+       unsigned                fast_charge:3;
+       unsigned                no_temp_support:1;
+       unsigned                no_insert_detect:1;
+
+       int (*set_charger) (int);
+};
+
+static int __set_charger(struct max8925_power_info *info, int enable)
+{
+       struct max8925_chip *chip = info->chip;
+       if (enable) {
+               /* enable charger in platform */
+               if (info->set_charger)
+                       info->set_charger(1);
+               /* enable charger */
+               max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 0);
+       } else {
+               /* disable charge */
+               max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
+               if (info->set_charger)
+                       info->set_charger(0);
+       }
+       dev_dbg(chip->dev, "%s\n", (enable) ? "Enable charger"
+               : "Disable charger");
+       return 0;
+}
+
+static irqreturn_t max8925_charger_handler(int irq, void *data)
+{
+       struct max8925_power_info *info = (struct max8925_power_info *)data;
+       struct max8925_chip *chip = info->chip;
+
+       switch (irq - chip->irq_base) {
+       case MAX8925_IRQ_VCHG_DC_R:
+               info->ac_online = 1;
+               __set_charger(info, 1);
+               dev_dbg(chip->dev, "Adapter inserted\n");
+               break;
+       case MAX8925_IRQ_VCHG_DC_F:
+               info->ac_online = 0;
+               __set_charger(info, 0);
+               dev_dbg(chip->dev, "Adapter removed\n");
+               break;
+       case MAX8925_IRQ_VCHG_THM_OK_F:
+               /* Battery is not ready yet */
+               dev_dbg(chip->dev, "Battery temperature is out of range\n");
+       case MAX8925_IRQ_VCHG_DC_OVP:
+               dev_dbg(chip->dev, "Error detection\n");
+               __set_charger(info, 0);
+               break;
+       case MAX8925_IRQ_VCHG_THM_OK_R:
+               /* Battery is ready now */
+               dev_dbg(chip->dev, "Battery temperature is in range\n");
+               break;
+       case MAX8925_IRQ_VCHG_SYSLOW_R:
+               /* VSYS is low */
+               dev_info(chip->dev, "Sys power is too low\n");
+               break;
+       case MAX8925_IRQ_VCHG_SYSLOW_F:
+               dev_dbg(chip->dev, "Sys power is above low threshold\n");
+               break;
+       case MAX8925_IRQ_VCHG_DONE:
+               __set_charger(info, 0);
+               dev_dbg(chip->dev, "Charging is done\n");
+               break;
+       case MAX8925_IRQ_VCHG_TOPOFF:
+               dev_dbg(chip->dev, "Charging in top-off mode\n");
+               break;
+       case MAX8925_IRQ_VCHG_TMR_FAULT:
+               __set_charger(info, 0);
+               dev_dbg(chip->dev, "Safe timer is expired\n");
+               break;
+       case MAX8925_IRQ_VCHG_RST:
+               __set_charger(info, 0);
+               dev_dbg(chip->dev, "Charger is reset\n");
+               break;
+       }
+       return IRQ_HANDLED;
+}
+
+static int start_measure(struct max8925_power_info *info, int type)
+{
+       unsigned char buf[2] = {0, 0};
+       int meas_cmd;
+       int meas_reg = 0, ret;
+
+       switch (type) {
+       case MEASURE_VCHG:
+               meas_cmd = MAX8925_CMD_VCHG;
+               meas_reg = MAX8925_ADC_VCHG;
+               break;
+       case MEASURE_VBBATT:
+               meas_cmd = MAX8925_CMD_VBBATT;
+               meas_reg = MAX8925_ADC_VBBATT;
+               break;
+       case MEASURE_VMBATT:
+               meas_cmd = MAX8925_CMD_VMBATT;
+               meas_reg = MAX8925_ADC_VMBATT;
+               break;
+       case MEASURE_ISNS:
+               meas_cmd = MAX8925_CMD_ISNS;
+               meas_reg = MAX8925_ADC_ISNS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       max8925_reg_write(info->adc, meas_cmd, 0);
+       max8925_bulk_read(info->adc, meas_reg, 2, buf);
+       ret = ((buf[0]<<8) | buf[1]) >> 4;
+
+       return ret;
+}
+
+static int max8925_ac_get_prop(struct power_supply *psy,
+                              enum power_supply_property psp,
+                              union power_supply_propval *val)
+{
+       struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = info->ac_online;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (info->ac_online) {
+                       ret = start_measure(info, MEASURE_VCHG);
+                       if (ret >= 0) {
+                               val->intval = ret * 2000;       /* unit is uV */
+                               goto out;
+                       }
+               }
+               ret = -ENODATA;
+               break;
+       default:
+               ret = -ENODEV;
+               break;
+       }
+out:
+       return ret;
+}
+
+static enum power_supply_property max8925_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static int max8925_usb_get_prop(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = info->usb_online;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (info->usb_online) {
+                       ret = start_measure(info, MEASURE_VCHG);
+                       if (ret >= 0) {
+                               val->intval = ret * 2000;       /* unit is uV */
+                               goto out;
+                       }
+               }
+               ret = -ENODATA;
+               break;
+       default:
+               ret = -ENODEV;
+               break;
+       }
+out:
+       return ret;
+}
+
+static enum power_supply_property max8925_usb_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static int max8925_bat_get_prop(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = info->bat_online;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (info->bat_online) {
+                       ret = start_measure(info, MEASURE_VMBATT);
+                       if (ret >= 0) {
+                               val->intval = ret * 2000;       /* unit is uV */
+                               ret = 0;
+                               break;
+                       }
+               }
+               ret = -ENODATA;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               if (info->bat_online) {
+                       ret = start_measure(info, MEASURE_ISNS);
+                       if (ret >= 0) {
+                               /* assume r_sns is 0.02 */
+                               ret = ((ret * 6250) - 3125) /* uA */;
+                               val->intval = 0;
+                               if (ret > 0)
+                                       val->intval = ret; /* unit is mA */
+                               ret = 0;
+                               break;
+                       }
+               }
+               ret = -ENODATA;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               if (!info->bat_online) {
+                       ret = -ENODATA;
+                       break;
+               }
+               ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
+               ret = (ret & MAX8925_CHG_STAT_MODE_MASK) >> 2;
+               switch (ret) {
+               case 1:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+                       break;
+               case 0:
+               case 2:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+                       break;
+               case 3:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+                       break;
+               }
+               ret = 0;
+               break;
+       case POWER_SUPPLY_PROP_STATUS:
+               if (!info->bat_online) {
+                       ret = -ENODATA;
+                       break;
+               }
+               ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
+               if (info->usb_online || info->ac_online) {
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+                       if (ret & MAX8925_CHG_STAT_EN_MASK)
+                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               } else
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               ret = 0;
+               break;
+       default:
+               ret = -ENODEV;
+               break;
+       }
+       return ret;
+}
+
+static enum power_supply_property max8925_battery_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_STATUS,
+};
+
+static const struct power_supply_desc ac_desc = {
+       .name           = "max8925-ac",
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+       .properties     = max8925_ac_props,
+       .num_properties = ARRAY_SIZE(max8925_ac_props),
+       .get_property   = max8925_ac_get_prop,
+};
+
+static const struct power_supply_desc usb_desc = {
+       .name           = "max8925-usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties     = max8925_usb_props,
+       .num_properties = ARRAY_SIZE(max8925_usb_props),
+       .get_property   = max8925_usb_get_prop,
+};
+
+static const struct power_supply_desc battery_desc = {
+       .name           = "max8925-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = max8925_battery_props,
+       .num_properties = ARRAY_SIZE(max8925_battery_props),
+       .get_property   = max8925_bat_get_prop,
+};
+
+#define REQUEST_IRQ(_irq, _name)                                       \
+do {                                                                   \
+       ret = request_threaded_irq(chip->irq_base + _irq, NULL,         \
+                                   max8925_charger_handler,            \
+                                   IRQF_ONESHOT, _name, info);         \
+       if (ret)                                                        \
+               dev_err(chip->dev, "Failed to request IRQ #%d: %d\n",   \
+                       _irq, ret);                                     \
+} while (0)
+
+static int max8925_init_charger(struct max8925_chip *chip,
+                                         struct max8925_power_info *info)
+{
+       int ret;
+
+       REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
+       if (!info->no_insert_detect) {
+               REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
+               REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
+       }
+       if (!info->no_temp_support) {
+               REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
+               REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
+       }
+       REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
+       REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
+       REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
+       REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done");
+       REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
+       REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");
+
+       info->usb_online = 0;
+       info->bat_online = 0;
+
+       /* check for power - can miss interrupt at boot time */
+       if (start_measure(info, MEASURE_VCHG) * 2000 > 500000)
+               info->ac_online = 1;
+       else
+               info->ac_online = 0;
+
+       ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
+       if (ret >= 0) {
+               /*
+                * If battery detection is enabled, ID pin of battery is
+                * connected to MBDET pin of MAX8925. It could be used to
+                * detect battery presence.
+                * Otherwise, we have to assume that battery is always on.
+                */
+               if (info->batt_detect)
+                       info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1;
+               else
+                       info->bat_online = 1;
+               if (ret & MAX8925_CHG_AC_RANGE_MASK)
+                       info->ac_online = 1;
+               else
+                       info->ac_online = 0;
+       }
+       /* disable charge */
+       max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
+       /* set charging current in charge topoff mode */
+       max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5,
+                        info->topoff_threshold << 5);
+       /* set charing current in fast charge mode */
+       max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge);
+
+       return 0;
+}
+
+static int max8925_deinit_charger(struct max8925_power_info *info)
+{
+       struct max8925_chip *chip = info->chip;
+       int irq;
+
+       irq = chip->irq_base + MAX8925_IRQ_VCHG_DC_OVP;
+       for (; irq <= chip->irq_base + MAX8925_IRQ_VCHG_TMR_FAULT; irq++)
+               free_irq(irq, info);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static struct max8925_power_pdata *
+max8925_power_dt_init(struct platform_device *pdev)
+{
+       struct device_node *nproot = pdev->dev.parent->of_node;
+       struct device_node *np;
+       int batt_detect;
+       int topoff_threshold;
+       int fast_charge;
+       int no_temp_support;
+       int no_insert_detect;
+       struct max8925_power_pdata *pdata;
+
+       if (!nproot)
+               return pdev->dev.platform_data;
+
+       np = of_get_child_by_name(nproot, "charger");
+       if (!np) {
+               dev_err(&pdev->dev, "failed to find charger node\n");
+               return NULL;
+       }
+
+       pdata = devm_kzalloc(&pdev->dev,
+                       sizeof(struct max8925_power_pdata),
+                       GFP_KERNEL);
+       if (!pdata)
+               goto ret;
+
+       of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
+       of_property_read_u32(np, "batt-detect", &batt_detect);
+       of_property_read_u32(np, "fast-charge", &fast_charge);
+       of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
+       of_property_read_u32(np, "no-temp-support", &no_temp_support);
+
+       pdata->batt_detect = batt_detect;
+       pdata->fast_charge = fast_charge;
+       pdata->topoff_threshold = topoff_threshold;
+       pdata->no_insert_detect = no_insert_detect;
+       pdata->no_temp_support = no_temp_support;
+
+ret:
+       of_node_put(np);
+       return pdata;
+}
+#else
+static struct max8925_power_pdata *
+max8925_power_dt_init(struct platform_device *pdev)
+{
+       return pdev->dev.platform_data;
+}
+#endif
+
+static int max8925_power_probe(struct platform_device *pdev)
+{
+       struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       struct power_supply_config psy_cfg = {}; /* Only for ac and usb */
+       struct max8925_power_pdata *pdata = NULL;
+       struct max8925_power_info *info;
+       int ret;
+
+       pdata = max8925_power_dt_init(pdev);
+       if (!pdata) {
+               dev_err(&pdev->dev, "platform data isn't assigned to "
+                       "power supply\n");
+               return -EINVAL;
+       }
+
+       info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
+                               GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+       info->chip = chip;
+       info->gpm = chip->i2c;
+       info->adc = chip->adc;
+       platform_set_drvdata(pdev, info);
+
+       psy_cfg.supplied_to = pdata->supplied_to;
+       psy_cfg.num_supplicants = pdata->num_supplicants;
+
+       info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
+       if (IS_ERR(info->ac)) {
+               ret = PTR_ERR(info->ac);
+               goto out;
+       }
+       info->ac->dev.parent = &pdev->dev;
+
+       info->usb = power_supply_register(&pdev->dev, &usb_desc, &psy_cfg);
+       if (IS_ERR(info->usb)) {
+               ret = PTR_ERR(info->usb);
+               goto out_unregister_ac;
+       }
+       info->usb->dev.parent = &pdev->dev;
+
+       info->battery = power_supply_register(&pdev->dev, &battery_desc, NULL);
+       if (IS_ERR(info->battery)) {
+               ret = PTR_ERR(info->battery);
+               goto out_unregister_usb;
+       }
+       info->battery->dev.parent = &pdev->dev;
+
+       info->batt_detect = pdata->batt_detect;
+       info->topoff_threshold = pdata->topoff_threshold;
+       info->fast_charge = pdata->fast_charge;
+       info->set_charger = pdata->set_charger;
+       info->no_temp_support = pdata->no_temp_support;
+       info->no_insert_detect = pdata->no_insert_detect;
+
+       max8925_init_charger(chip, info);
+       return 0;
+out_unregister_usb:
+       power_supply_unregister(info->usb);
+out_unregister_ac:
+       power_supply_unregister(info->ac);
+out:
+       return ret;
+}
+
+static int max8925_power_remove(struct platform_device *pdev)
+{
+       struct max8925_power_info *info = platform_get_drvdata(pdev);
+
+       if (info) {
+               power_supply_unregister(info->ac);
+               power_supply_unregister(info->usb);
+               power_supply_unregister(info->battery);
+               max8925_deinit_charger(info);
+       }
+       return 0;
+}
+
+static struct platform_driver max8925_power_driver = {
+       .probe  = max8925_power_probe,
+       .remove = max8925_power_remove,
+       .driver = {
+               .name   = "max8925-power",
+       },
+};
+
+module_platform_driver(max8925_power_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Power supply driver for MAX8925");
+MODULE_ALIAS("platform:max8925-power");
diff --git a/drivers/power/supply/max8997_charger.c b/drivers/power/supply/max8997_charger.c
new file mode 100644 (file)
index 0000000..0b2eab5
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * max8997_charger.c - Power supply consumer driver for the Maxim 8997/8966
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *  MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+
+struct charger_data {
+       struct device *dev;
+       struct max8997_dev *iodev;
+       struct power_supply *battery;
+};
+
+static enum power_supply_property max8997_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS, /* "FULL" or "NOT FULL" only. */
+       POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */
+       POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */
+};
+
+/* Note that the charger control is done by a current regulator "CHARGER" */
+static int max8997_battery_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct charger_data *charger = power_supply_get_drvdata(psy);
+       struct i2c_client *i2c = charger->iodev->i2c;
+       int ret;
+       u8 reg;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = 0;
+               ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+               if (ret)
+                       return ret;
+               if ((reg & (1 << 0)) == 0x1)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = 0;
+               ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+               if (ret)
+                       return ret;
+               if ((reg & (1 << 2)) == 0x0)
+                       val->intval = 1;
+
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = 0;
+               ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+               if (ret)
+                       return ret;
+               /* DCINOK */
+               if (reg & (1 << 1))
+                       val->intval = 1;
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct power_supply_desc max8997_battery_desc = {
+       .name           = "max8997_pmic",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property   = max8997_battery_get_property,
+       .properties     = max8997_battery_props,
+       .num_properties = ARRAY_SIZE(max8997_battery_props),
+};
+
+static int max8997_battery_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct charger_data *charger;
+       struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+       struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev);
+       struct power_supply_config psy_cfg = {};
+
+       if (!pdata)
+               return -EINVAL;
+
+       if (pdata->eoc_mA) {
+               int val = (pdata->eoc_mA - 50) / 10;
+               if (val < 0)
+                       val = 0;
+               if (val > 0xf)
+                       val = 0xf;
+
+               ret = max8997_update_reg(iodev->i2c,
+                               MAX8997_REG_MBCCTRL5, val, 0xf);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Cannot use i2c bus.\n");
+                       return ret;
+               }
+       }
+
+       switch (pdata->timeout) {
+       case 5:
+               ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
+                               0x2 << 4, 0x7 << 4);
+               break;
+       case 6:
+               ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
+                               0x3 << 4, 0x7 << 4);
+               break;
+       case 7:
+               ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
+                               0x4 << 4, 0x7 << 4);
+               break;
+       case 0:
+               ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
+                               0x7 << 4, 0x7 << 4);
+               break;
+       default:
+               dev_err(&pdev->dev, "incorrect timeout value (%d)\n",
+                               pdata->timeout);
+               return -EINVAL;
+       }
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Cannot use i2c bus.\n");
+               return ret;
+       }
+
+       charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),
+                               GFP_KERNEL);
+       if (charger == NULL) {
+               dev_err(&pdev->dev, "Cannot allocate memory.\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, charger);
+
+
+       charger->dev = &pdev->dev;
+       charger->iodev = iodev;
+
+       psy_cfg.drv_data = charger;
+
+       charger->battery = power_supply_register(&pdev->dev,
+                                                &max8997_battery_desc,
+                                                &psy_cfg);
+       if (IS_ERR(charger->battery)) {
+               dev_err(&pdev->dev, "failed: power supply register\n");
+               return PTR_ERR(charger->battery);
+       }
+
+       return 0;
+}
+
+static int max8997_battery_remove(struct platform_device *pdev)
+{
+       struct charger_data *charger = platform_get_drvdata(pdev);
+
+       power_supply_unregister(charger->battery);
+       return 0;
+}
+
+static const struct platform_device_id max8997_battery_id[] = {
+       { "max8997-battery", 0 },
+       { }
+};
+
+static struct platform_driver max8997_battery_driver = {
+       .driver = {
+               .name = "max8997-battery",
+       },
+       .probe = max8997_battery_probe,
+       .remove = max8997_battery_remove,
+       .id_table = max8997_battery_id,
+};
+
+static int __init max8997_battery_init(void)
+{
+       return platform_driver_register(&max8997_battery_driver);
+}
+subsys_initcall(max8997_battery_init);
+
+static void __exit max8997_battery_cleanup(void)
+{
+       platform_driver_unregister(&max8997_battery_driver);
+}
+module_exit(max8997_battery_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 8997/8966 battery control driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/max8998_charger.c b/drivers/power/supply/max8998_charger.c
new file mode 100644 (file)
index 0000000..b64cf0f
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * max8998_charger.c - Power supply consumer driver for the Maxim 8998/LP3974
+ *
+ *  Copyright (C) 2009-2010 Samsung Electronics
+ *  MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+struct max8998_battery_data {
+       struct device *dev;
+       struct max8998_dev *iodev;
+       struct power_supply *battery;
+};
+
+static enum power_supply_property max8998_battery_props[] = {
+       POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */
+       POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */
+};
+
+/* Note that the charger control is done by a current regulator "CHARGER" */
+static int max8998_battery_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct max8998_battery_data *max8998 = power_supply_get_drvdata(psy);
+       struct i2c_client *i2c = max8998->iodev->i2c;
+       int ret;
+       u8 reg;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+               ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, &reg);
+               if (ret)
+                       return ret;
+               if (reg & (1 << 4))
+                       val->intval = 0;
+               else
+                       val->intval = 1;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, &reg);
+               if (ret)
+                       return ret;
+               if (reg & (1 << 3))
+                       val->intval = 0;
+               else
+                       val->intval = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct power_supply_desc max8998_battery_desc = {
+       .name           = "max8998_pmic",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property   = max8998_battery_get_property,
+       .properties     = max8998_battery_props,
+       .num_properties = ARRAY_SIZE(max8998_battery_props),
+};
+
+static int max8998_battery_probe(struct platform_device *pdev)
+{
+       struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+       struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev);
+       struct power_supply_config psy_cfg = {};
+       struct max8998_battery_data *max8998;
+       struct i2c_client *i2c;
+       int ret = 0;
+
+       if (!pdata) {
+               dev_err(pdev->dev.parent, "No platform init data supplied\n");
+               return -ENODEV;
+       }
+
+       max8998 = devm_kzalloc(&pdev->dev, sizeof(struct max8998_battery_data),
+                               GFP_KERNEL);
+       if (!max8998)
+               return -ENOMEM;
+
+       max8998->dev = &pdev->dev;
+       max8998->iodev = iodev;
+       platform_set_drvdata(pdev, max8998);
+       i2c = max8998->iodev->i2c;
+
+       /* Setup "End of Charge" */
+       /* If EOC value equals 0,
+        * remain value set from bootloader or default value */
+       if (pdata->eoc >= 10 && pdata->eoc <= 45) {
+               max8998_update_reg(i2c, MAX8998_REG_CHGR1,
+                               (pdata->eoc / 5 - 2) << 5, 0x7 << 5);
+       } else if (pdata->eoc == 0) {
+               dev_dbg(max8998->dev,
+                       "EOC value not set: leave it unchanged.\n");
+       } else {
+               dev_err(max8998->dev, "Invalid EOC value\n");
+               return -EINVAL;
+       }
+
+       /* Setup Charge Restart Level */
+       switch (pdata->restart) {
+       case 100:
+               max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x1 << 3, 0x3 << 3);
+               break;
+       case 150:
+               max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x0 << 3, 0x3 << 3);
+               break;
+       case 200:
+               max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x2 << 3, 0x3 << 3);
+               break;
+       case -1:
+               max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x3 << 3, 0x3 << 3);
+               break;
+       case 0:
+               dev_dbg(max8998->dev,
+                       "Restart Level not set: leave it unchanged.\n");
+               break;
+       default:
+               dev_err(max8998->dev, "Invalid Restart Level\n");
+               return -EINVAL;
+       }
+
+       /* Setup Charge Full Timeout */
+       switch (pdata->timeout) {
+       case 5:
+               max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x0 << 4, 0x3 << 4);
+               break;
+       case 6:
+               max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x1 << 4, 0x3 << 4);
+               break;
+       case 7:
+               max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x2 << 4, 0x3 << 4);
+               break;
+       case -1:
+               max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x3 << 4, 0x3 << 4);
+               break;
+       case 0:
+               dev_dbg(max8998->dev,
+                       "Full Timeout not set: leave it unchanged.\n");
+               break;
+       default:
+               dev_err(max8998->dev, "Invalid Full Timeout value\n");
+               return -EINVAL;
+       }
+
+       psy_cfg.drv_data = max8998;
+
+       max8998->battery = devm_power_supply_register(max8998->dev,
+                                                     &max8998_battery_desc,
+                                                     &psy_cfg);
+       if (IS_ERR(max8998->battery)) {
+               ret = PTR_ERR(max8998->battery);
+               dev_err(max8998->dev, "failed: power supply register: %d\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct platform_device_id max8998_battery_id[] = {
+       { "max8998-battery", TYPE_MAX8998 },
+       { }
+};
+
+static struct platform_driver max8998_battery_driver = {
+       .driver = {
+               .name = "max8998-battery",
+       },
+       .probe = max8998_battery_probe,
+       .id_table = max8998_battery_id,
+};
+
+module_platform_driver(max8998_battery_driver);
+
+MODULE_DESCRIPTION("MAXIM 8998 battery control driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:max8998-battery");
diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c
new file mode 100644 (file)
index 0000000..9e29b13
--- /dev/null
@@ -0,0 +1,692 @@
+/*
+ * Battery driver for One Laptop Per Child board.
+ *
+ *     Copyright Â© 2006-2010  David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/olpc-ec.h>
+#include <asm/olpc.h>
+
+
+#define EC_BAT_VOLTAGE 0x10    /* uint16_t,    *9.76/32,    mV   */
+#define EC_BAT_CURRENT 0x11    /* int16_t,     *15.625/120, mA   */
+#define EC_BAT_ACR     0x12    /* int16_t,     *6250/15,    ÂµAh  */
+#define EC_BAT_TEMP    0x13    /* uint16_t,    *100/256,   Â°C  */
+#define EC_AMB_TEMP    0x14    /* uint16_t,    *100/256,   Â°C  */
+#define EC_BAT_STATUS  0x15    /* uint8_t,     bitmask */
+#define EC_BAT_SOC     0x16    /* uint8_t,     percentage */
+#define EC_BAT_SERIAL  0x17    /* uint8_t[6] */
+#define EC_BAT_EEPROM  0x18    /* uint8_t adr as input, uint8_t output */
+#define EC_BAT_ERRCODE 0x1f    /* uint8_t,     bitmask */
+
+#define BAT_STAT_PRESENT       0x01
+#define BAT_STAT_FULL          0x02
+#define BAT_STAT_LOW           0x04
+#define BAT_STAT_DESTROY       0x08
+#define BAT_STAT_AC            0x10
+#define BAT_STAT_CHARGING      0x20
+#define BAT_STAT_DISCHARGING   0x40
+#define BAT_STAT_TRICKLE       0x80
+
+#define BAT_ERR_INFOFAIL       0x02
+#define BAT_ERR_OVERVOLTAGE    0x04
+#define BAT_ERR_OVERTEMP       0x05
+#define BAT_ERR_GAUGESTOP      0x06
+#define BAT_ERR_OUT_OF_CONTROL 0x07
+#define BAT_ERR_ID_FAIL                0x09
+#define BAT_ERR_ACR_FAIL       0x10
+
+#define BAT_ADDR_MFR_TYPE      0x5F
+
+/*********************************************************************
+ *             Power
+ *********************************************************************/
+
+static int olpc_ac_get_prop(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       int ret = 0;
+       uint8_t status;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
+               if (ret)
+                       return ret;
+
+               val->intval = !!(status & BAT_STAT_AC);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static enum power_supply_property olpc_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc olpc_ac_desc = {
+       .name = "olpc-ac",
+       .type = POWER_SUPPLY_TYPE_MAINS,
+       .properties = olpc_ac_props,
+       .num_properties = ARRAY_SIZE(olpc_ac_props),
+       .get_property = olpc_ac_get_prop,
+};
+
+static struct power_supply *olpc_ac;
+
+static char bat_serial[17]; /* Ick */
+
+static int olpc_bat_get_status(union power_supply_propval *val, uint8_t ec_byte)
+{
+       if (olpc_platform_info.ecver > 0x44) {
+               if (ec_byte & (BAT_STAT_CHARGING | BAT_STAT_TRICKLE))
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (ec_byte & BAT_STAT_DISCHARGING)
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (ec_byte & BAT_STAT_FULL)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else /* er,... */
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               /* Older EC didn't report charge/discharge bits */
+               if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (ec_byte & BAT_STAT_FULL)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else /* Not _necessarily_ true but EC doesn't tell all yet */
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+       }
+
+       return 0;
+}
+
+static int olpc_bat_get_health(union power_supply_propval *val)
+{
+       uint8_t ec_byte;
+       int ret;
+
+       ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
+       if (ret)
+               return ret;
+
+       switch (ec_byte) {
+       case 0:
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+
+       case BAT_ERR_OVERTEMP:
+               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               break;
+
+       case BAT_ERR_OVERVOLTAGE:
+               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               break;
+
+       case BAT_ERR_INFOFAIL:
+       case BAT_ERR_OUT_OF_CONTROL:
+       case BAT_ERR_ID_FAIL:
+       case BAT_ERR_ACR_FAIL:
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               break;
+
+       default:
+               /* Eep. We don't know this failure code */
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+static int olpc_bat_get_mfr(union power_supply_propval *val)
+{
+       uint8_t ec_byte;
+       int ret;
+
+       ec_byte = BAT_ADDR_MFR_TYPE;
+       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
+       if (ret)
+               return ret;
+
+       switch (ec_byte >> 4) {
+       case 1:
+               val->strval = "Gold Peak";
+               break;
+       case 2:
+               val->strval = "BYD";
+               break;
+       default:
+               val->strval = "Unknown";
+               break;
+       }
+
+       return ret;
+}
+
+static int olpc_bat_get_tech(union power_supply_propval *val)
+{
+       uint8_t ec_byte;
+       int ret;
+
+       ec_byte = BAT_ADDR_MFR_TYPE;
+       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
+       if (ret)
+               return ret;
+
+       switch (ec_byte & 0xf) {
+       case 1:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
+               break;
+       case 2:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe;
+               break;
+       default:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+               break;
+       }
+
+       return ret;
+}
+
+static int olpc_bat_get_charge_full_design(union power_supply_propval *val)
+{
+       uint8_t ec_byte;
+       union power_supply_propval tech;
+       int ret, mfr;
+
+       ret = olpc_bat_get_tech(&tech);
+       if (ret)
+               return ret;
+
+       ec_byte = BAT_ADDR_MFR_TYPE;
+       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
+       if (ret)
+               return ret;
+
+       mfr = ec_byte >> 4;
+
+       switch (tech.intval) {
+       case POWER_SUPPLY_TECHNOLOGY_NiMH:
+               switch (mfr) {
+               case 1: /* Gold Peak */
+                       val->intval = 3000000*.8;
+                       break;
+               default:
+                       return -EIO;
+               }
+               break;
+
+       case POWER_SUPPLY_TECHNOLOGY_LiFe:
+               switch (mfr) {
+               case 1: /* Gold Peak, fall through */
+               case 2: /* BYD */
+                       val->intval = 2800000;
+                       break;
+               default:
+                       return -EIO;
+               }
+               break;
+
+       default:
+               return -EIO;
+       }
+
+       return ret;
+}
+
+static int olpc_bat_get_charge_now(union power_supply_propval *val)
+{
+       uint8_t soc;
+       union power_supply_propval full;
+       int ret;
+
+       ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &soc, 1);
+       if (ret)
+               return ret;
+
+       ret = olpc_bat_get_charge_full_design(&full);
+       if (ret)
+               return ret;
+
+       val->intval = soc * (full.intval / 100);
+       return 0;
+}
+
+static int olpc_bat_get_voltage_max_design(union power_supply_propval *val)
+{
+       uint8_t ec_byte;
+       union power_supply_propval tech;
+       int mfr;
+       int ret;
+
+       ret = olpc_bat_get_tech(&tech);
+       if (ret)
+               return ret;
+
+       ec_byte = BAT_ADDR_MFR_TYPE;
+       ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
+       if (ret)
+               return ret;
+
+       mfr = ec_byte >> 4;
+
+       switch (tech.intval) {
+       case POWER_SUPPLY_TECHNOLOGY_NiMH:
+               switch (mfr) {
+               case 1: /* Gold Peak */
+                       val->intval = 6000000;
+                       break;
+               default:
+                       return -EIO;
+               }
+               break;
+
+       case POWER_SUPPLY_TECHNOLOGY_LiFe:
+               switch (mfr) {
+               case 1: /* Gold Peak */
+                       val->intval = 6400000;
+                       break;
+               case 2: /* BYD */
+                       val->intval = 6500000;
+                       break;
+               default:
+                       return -EIO;
+               }
+               break;
+
+       default:
+               return -EIO;
+       }
+
+       return ret;
+}
+
+/*********************************************************************
+ *             Battery properties
+ *********************************************************************/
+static int olpc_bat_get_property(struct power_supply *psy,
+                                enum power_supply_property psp,
+                                union power_supply_propval *val)
+{
+       int ret = 0;
+       __be16 ec_word;
+       uint8_t ec_byte;
+       __be64 ser_buf;
+
+       ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1);
+       if (ret)
+               return ret;
+
+       /* Theoretically there's a race here -- the battery could be
+          removed immediately after we check whether it's present, and
+          then we query for some other property of the now-absent battery.
+          It doesn't matter though -- the EC will return the last-known
+          information, and it's as if we just ran that _little_ bit faster
+          and managed to read it out before the battery went away. */
+       if (!(ec_byte & (BAT_STAT_PRESENT | BAT_STAT_TRICKLE)) &&
+                       psp != POWER_SUPPLY_PROP_PRESENT)
+               return -ENODEV;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = olpc_bat_get_status(val, ec_byte);
+               if (ret)
+                       return ret;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               if (ec_byte & BAT_STAT_TRICKLE)
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+               else if (ec_byte & BAT_STAT_CHARGING)
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               else
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = !!(ec_byte & (BAT_STAT_PRESENT |
+                                           BAT_STAT_TRICKLE));
+               break;
+
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (ec_byte & BAT_STAT_DESTROY)
+                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               else {
+                       ret = olpc_bat_get_health(val);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               ret = olpc_bat_get_mfr(val);
+               if (ret)
+                       return ret;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               ret = olpc_bat_get_tech(val);
+               if (ret)
+                       return ret;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2);
+               if (ret)
+                       return ret;
+
+               val->intval = (s16)be16_to_cpu(ec_word) * 9760L / 32;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2);
+               if (ret)
+                       return ret;
+
+               val->intval = (s16)be16_to_cpu(ec_word) * 15625L / 120;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1);
+               if (ret)
+                       return ret;
+               val->intval = ec_byte;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+               if (ec_byte & BAT_STAT_FULL)
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+               else if (ec_byte & BAT_STAT_LOW)
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+               else
+                       val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               ret = olpc_bat_get_charge_full_design(val);
+               if (ret)
+                       return ret;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               ret = olpc_bat_get_charge_now(val);
+               if (ret)
+                       return ret;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2);
+               if (ret)
+                       return ret;
+
+               val->intval = (s16)be16_to_cpu(ec_word) * 100 / 256;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_AMBIENT:
+               ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
+               if (ret)
+                       return ret;
+
+               val->intval = (int)be16_to_cpu(ec_word) * 100 / 256;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+               ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2);
+               if (ret)
+                       return ret;
+
+               val->intval = (s16)be16_to_cpu(ec_word) * 6250 / 15;
+               break;
+       case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+               ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8);
+               if (ret)
+                       return ret;
+
+               sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf));
+               val->strval = bat_serial;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               ret = olpc_bat_get_voltage_max_design(val);
+               if (ret)
+                       return ret;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property olpc_xo1_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TEMP_AMBIENT,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_SERIAL_NUMBER,
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+};
+
+/* XO-1.5 does not have ambient temperature property */
+static enum power_supply_property olpc_xo15_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_SERIAL_NUMBER,
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+};
+
+/* EEPROM reading goes completely around the power_supply API, sadly */
+
+#define EEPROM_START   0x20
+#define EEPROM_END     0x80
+#define EEPROM_SIZE    (EEPROM_END - EEPROM_START)
+
+static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr, char *buf, loff_t off, size_t count)
+{
+       uint8_t ec_byte;
+       int ret;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               ec_byte = EEPROM_START + off + i;
+               ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1);
+               if (ret) {
+                       pr_err("olpc-battery: "
+                              "EC_BAT_EEPROM cmd @ 0x%x failed - %d!\n",
+                              ec_byte, ret);
+                       return -EIO;
+               }
+       }
+
+       return count;
+}
+
+static struct bin_attribute olpc_bat_eeprom = {
+       .attr = {
+               .name = "eeprom",
+               .mode = S_IRUGO,
+       },
+       .size = EEPROM_SIZE,
+       .read = olpc_bat_eeprom_read,
+};
+
+/* Allow userspace to see the specific error value pulled from the EC */
+
+static ssize_t olpc_bat_error_read(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       uint8_t ec_byte;
+       ssize_t ret;
+
+       ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n", ec_byte);
+}
+
+static struct device_attribute olpc_bat_error = {
+       .attr = {
+               .name = "error",
+               .mode = S_IRUGO,
+       },
+       .show = olpc_bat_error_read,
+};
+
+/*********************************************************************
+ *             Initialisation
+ *********************************************************************/
+
+static struct power_supply_desc olpc_bat_desc = {
+       .name = "olpc-battery",
+       .get_property = olpc_bat_get_property,
+       .use_for_apm = 1,
+};
+
+static struct power_supply *olpc_bat;
+
+static int olpc_battery_suspend(struct platform_device *pdev,
+                               pm_message_t state)
+{
+       if (device_may_wakeup(&olpc_ac->dev))
+               olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR);
+       else
+               olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR);
+
+       if (device_may_wakeup(&olpc_bat->dev))
+               olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
+                                  | EC_SCI_SRC_BATERR);
+       else
+               olpc_ec_wakeup_clear(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
+                                    | EC_SCI_SRC_BATERR);
+
+       return 0;
+}
+
+static int olpc_battery_probe(struct platform_device *pdev)
+{
+       int ret;
+       uint8_t status;
+
+       /*
+        * We've seen a number of EC protocol changes; this driver requires
+        * the latest EC protocol, supported by 0x44 and above.
+        */
+       if (olpc_platform_info.ecver < 0x44) {
+               printk(KERN_NOTICE "OLPC EC version 0x%02x too old for "
+                       "battery driver.\n", olpc_platform_info.ecver);
+               return -ENXIO;
+       }
+
+       ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
+       if (ret)
+               return ret;
+
+       /* Ignore the status. It doesn't actually matter */
+
+       olpc_ac = power_supply_register(&pdev->dev, &olpc_ac_desc, NULL);
+       if (IS_ERR(olpc_ac))
+               return PTR_ERR(olpc_ac);
+
+       if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */
+               olpc_bat_desc.properties = olpc_xo15_bat_props;
+               olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo15_bat_props);
+       } else { /* XO-1 */
+               olpc_bat_desc.properties = olpc_xo1_bat_props;
+               olpc_bat_desc.num_properties = ARRAY_SIZE(olpc_xo1_bat_props);
+       }
+
+       olpc_bat = power_supply_register(&pdev->dev, &olpc_bat_desc, NULL);
+       if (IS_ERR(olpc_bat)) {
+               ret = PTR_ERR(olpc_bat);
+               goto battery_failed;
+       }
+
+       ret = device_create_bin_file(&olpc_bat->dev, &olpc_bat_eeprom);
+       if (ret)
+               goto eeprom_failed;
+
+       ret = device_create_file(&olpc_bat->dev, &olpc_bat_error);
+       if (ret)
+               goto error_failed;
+
+       if (olpc_ec_wakeup_available()) {
+               device_set_wakeup_capable(&olpc_ac->dev, true);
+               device_set_wakeup_capable(&olpc_bat->dev, true);
+       }
+
+       return 0;
+
+error_failed:
+       device_remove_bin_file(&olpc_bat->dev, &olpc_bat_eeprom);
+eeprom_failed:
+       power_supply_unregister(olpc_bat);
+battery_failed:
+       power_supply_unregister(olpc_ac);
+       return ret;
+}
+
+static int olpc_battery_remove(struct platform_device *pdev)
+{
+       device_remove_file(&olpc_bat->dev, &olpc_bat_error);
+       device_remove_bin_file(&olpc_bat->dev, &olpc_bat_eeprom);
+       power_supply_unregister(olpc_bat);
+       power_supply_unregister(olpc_ac);
+       return 0;
+}
+
+static const struct of_device_id olpc_battery_ids[] = {
+       { .compatible = "olpc,xo1-battery" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, olpc_battery_ids);
+
+static struct platform_driver olpc_battery_driver = {
+       .driver = {
+               .name = "olpc-battery",
+               .of_match_table = olpc_battery_ids,
+       },
+       .probe = olpc_battery_probe,
+       .remove = olpc_battery_remove,
+       .suspend = olpc_battery_suspend,
+};
+
+module_platform_driver(olpc_battery_driver);
+
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine");
diff --git a/drivers/power/supply/pcf50633-charger.c b/drivers/power/supply/pcf50633-charger.c
new file mode 100644 (file)
index 0000000..d05597b
--- /dev/null
@@ -0,0 +1,488 @@
+/* NXP PCF50633 Main Battery Charger Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ *  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;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/mbc.h>
+
+struct pcf50633_mbc {
+       struct pcf50633 *pcf;
+
+       int adapter_online;
+       int usb_online;
+
+       struct power_supply *usb;
+       struct power_supply *adapter;
+       struct power_supply *ac;
+};
+
+int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
+{
+       struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
+       int ret = 0;
+       u8 bits;
+       int charging_start = 1;
+       u8 mbcs2, chgmod;
+       unsigned int mbcc5;
+
+       if (ma >= 1000) {
+               bits = PCF50633_MBCC7_USB_1000mA;
+               ma = 1000;
+       } else if (ma >= 500) {
+               bits = PCF50633_MBCC7_USB_500mA;
+               ma = 500;
+       } else if (ma >= 100) {
+               bits = PCF50633_MBCC7_USB_100mA;
+               ma = 100;
+       } else {
+               bits = PCF50633_MBCC7_USB_SUSPEND;
+               charging_start = 0;
+               ma = 0;
+       }
+
+       ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
+                                       PCF50633_MBCC7_USB_MASK, bits);
+       if (ret)
+               dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
+       else
+               dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
+
+       /*
+        * We limit the charging current to be the USB current limit.
+        * The reason is that on pcf50633, when it enters PMU Standby mode,
+        * which it does when the device goes "off", the USB current limit
+        * reverts to the variant default.  In at least one common case, that
+        * default is 500mA.  By setting the charging current to be the same
+        * as the USB limit we set here before PMU standby, we enforce it only
+        * using the correct amount of current even when the USB current limit
+        * gets reset to the wrong thing
+        */
+
+       if (mbc->pcf->pdata->charger_reference_current_ma) {
+               mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
+               if (mbcc5 > 255)
+                       mbcc5 = 255;
+               pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
+       }
+
+       mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
+       chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
+
+       /* If chgmod == BATFULL, setting chgena has no effect.
+        * Datasheet says we need to set resume instead but when autoresume is
+        * used resume doesn't work. Clear and set chgena instead.
+        */
+       if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
+               pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
+                               PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
+       else {
+               pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
+                               PCF50633_MBCC1_CHGENA);
+               pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
+                               PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
+       }
+
+       power_supply_changed(mbc->usb);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
+
+int pcf50633_mbc_get_status(struct pcf50633 *pcf)
+{
+       struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
+       int status = 0;
+       u8 chgmod;
+
+       if (!mbc)
+               return 0;
+
+       chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
+               & PCF50633_MBCS2_MBC_MASK;
+
+       if (mbc->usb_online)
+               status |= PCF50633_MBC_USB_ONLINE;
+       if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
+           chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
+           chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
+           chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
+               status |= PCF50633_MBC_USB_ACTIVE;
+       if (mbc->adapter_online)
+               status |= PCF50633_MBC_ADAPTER_ONLINE;
+       if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
+           chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
+           chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
+           chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
+               status |= PCF50633_MBC_ADAPTER_ACTIVE;
+
+       return status;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
+
+int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
+{
+       struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
+
+       if (!mbc)
+               return 0;
+
+       return mbc->usb_online;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
+
+static ssize_t
+show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+
+       u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
+       u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
+
+       return sprintf(buf, "%d\n", chgmod);
+}
+static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
+
+static ssize_t
+show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+       u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+                                               PCF50633_MBCC7_USB_MASK;
+       unsigned int ma;
+
+       if (usblim == PCF50633_MBCC7_USB_1000mA)
+               ma = 1000;
+       else if (usblim == PCF50633_MBCC7_USB_500mA)
+               ma = 500;
+       else if (usblim == PCF50633_MBCC7_USB_100mA)
+               ma = 100;
+       else
+               ma = 0;
+
+       return sprintf(buf, "%u\n", ma);
+}
+
+static ssize_t set_usblim(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+       unsigned long ma;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &ma);
+       if (ret)
+               return ret;
+
+       pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
+
+       return count;
+}
+
+static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
+
+static ssize_t
+show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+       u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
+       unsigned int ma;
+
+       if (!mbc->pcf->pdata->charger_reference_current_ma)
+               return -ENODEV;
+
+       ma = (mbc->pcf->pdata->charger_reference_current_ma *  mbcc5) >> 8;
+
+       return sprintf(buf, "%u\n", ma);
+}
+
+static ssize_t set_chglim(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+       unsigned long ma;
+       unsigned int mbcc5;
+       int ret;
+
+       if (!mbc->pcf->pdata->charger_reference_current_ma)
+               return -ENODEV;
+
+       ret = kstrtoul(buf, 10, &ma);
+       if (ret)
+               return ret;
+
+       mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
+       if (mbcc5 > 255)
+               mbcc5 = 255;
+       pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
+
+       return count;
+}
+
+/*
+ * This attribute allows to change MBC charging limit on the fly
+ * independently of usb current limit. It also gets set automatically every
+ * time usb current limit is changed.
+ */
+static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
+
+static struct attribute *pcf50633_mbc_sysfs_entries[] = {
+       &dev_attr_chgmode.attr,
+       &dev_attr_usb_curlim.attr,
+       &dev_attr_chg_curlim.attr,
+       NULL,
+};
+
+static struct attribute_group mbc_attr_group = {
+       .name   = NULL,                 /* put in device directory */
+       .attrs  = pcf50633_mbc_sysfs_entries,
+};
+
+static void
+pcf50633_mbc_irq_handler(int irq, void *data)
+{
+       struct pcf50633_mbc *mbc = data;
+
+       /* USB */
+       if (irq == PCF50633_IRQ_USBINS) {
+               mbc->usb_online = 1;
+       } else if (irq == PCF50633_IRQ_USBREM) {
+               mbc->usb_online = 0;
+               pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
+       }
+
+       /* Adapter */
+       if (irq == PCF50633_IRQ_ADPINS)
+               mbc->adapter_online = 1;
+       else if (irq == PCF50633_IRQ_ADPREM)
+               mbc->adapter_online = 0;
+
+       power_supply_changed(mbc->ac);
+       power_supply_changed(mbc->usb);
+       power_supply_changed(mbc->adapter);
+
+       if (mbc->pcf->pdata->mbc_event_callback)
+               mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
+}
+
+static int adapter_get_property(struct power_supply *psy,
+                       enum power_supply_property psp,
+                       union power_supply_propval *val)
+{
+       struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval =  mbc->adapter_online;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int usb_get_property(struct power_supply *psy,
+                       enum power_supply_property psp,
+                       union power_supply_propval *val)
+{
+       struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
+       int ret = 0;
+       u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+                                               PCF50633_MBCC7_USB_MASK;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = mbc->usb_online &&
+                               (usblim <= PCF50633_MBCC7_USB_500mA);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int ac_get_property(struct power_supply *psy,
+                       enum power_supply_property psp,
+                       union power_supply_propval *val)
+{
+       struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
+       int ret = 0;
+       u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+                                               PCF50633_MBCC7_USB_MASK;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = mbc->usb_online &&
+                               (usblim == PCF50633_MBCC7_USB_1000mA);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static enum power_supply_property power_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const u8 mbc_irq_handlers[] = {
+       PCF50633_IRQ_ADPINS,
+       PCF50633_IRQ_ADPREM,
+       PCF50633_IRQ_USBINS,
+       PCF50633_IRQ_USBREM,
+       PCF50633_IRQ_BATFULL,
+       PCF50633_IRQ_CHGHALT,
+       PCF50633_IRQ_THLIMON,
+       PCF50633_IRQ_THLIMOFF,
+       PCF50633_IRQ_USBLIMON,
+       PCF50633_IRQ_USBLIMOFF,
+       PCF50633_IRQ_LOWSYS,
+       PCF50633_IRQ_LOWBAT,
+};
+
+static const struct power_supply_desc pcf50633_mbc_adapter_desc = {
+       .name           = "adapter",
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+       .properties     = power_props,
+       .num_properties = ARRAY_SIZE(power_props),
+       .get_property   = &adapter_get_property,
+};
+
+static const struct power_supply_desc pcf50633_mbc_usb_desc = {
+       .name           = "usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties     = power_props,
+       .num_properties = ARRAY_SIZE(power_props),
+       .get_property   = usb_get_property,
+};
+
+static const struct power_supply_desc pcf50633_mbc_ac_desc = {
+       .name           = "ac",
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+       .properties     = power_props,
+       .num_properties = ARRAY_SIZE(power_props),
+       .get_property   = ac_get_property,
+};
+
+static int pcf50633_mbc_probe(struct platform_device *pdev)
+{
+       struct power_supply_config psy_cfg = {};
+       struct pcf50633_mbc *mbc;
+       int ret;
+       int i;
+       u8 mbcs1;
+
+       mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
+       if (!mbc)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, mbc);
+       mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
+
+       /* Set up IRQ handlers */
+       for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
+               pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
+                                       pcf50633_mbc_irq_handler, mbc);
+
+       psy_cfg.supplied_to             = mbc->pcf->pdata->batteries;
+       psy_cfg.num_supplicants         = mbc->pcf->pdata->num_batteries;
+       psy_cfg.drv_data                = mbc;
+
+       /* Create power supplies */
+       mbc->adapter = power_supply_register(&pdev->dev,
+                                            &pcf50633_mbc_adapter_desc,
+                                            &psy_cfg);
+       if (IS_ERR(mbc->adapter)) {
+               dev_err(mbc->pcf->dev, "failed to register adapter\n");
+               ret = PTR_ERR(mbc->adapter);
+               return ret;
+       }
+
+       mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
+                                        &psy_cfg);
+       if (IS_ERR(mbc->usb)) {
+               dev_err(mbc->pcf->dev, "failed to register usb\n");
+               power_supply_unregister(mbc->adapter);
+               ret = PTR_ERR(mbc->usb);
+               return ret;
+       }
+
+       mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc,
+                                       &psy_cfg);
+       if (IS_ERR(mbc->ac)) {
+               dev_err(mbc->pcf->dev, "failed to register ac\n");
+               power_supply_unregister(mbc->adapter);
+               power_supply_unregister(mbc->usb);
+               ret = PTR_ERR(mbc->ac);
+               return ret;
+       }
+
+       ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
+       if (ret)
+               dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
+
+       mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
+       if (mbcs1 & PCF50633_MBCS1_USBPRES)
+               pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
+       if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
+               pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
+
+       return 0;
+}
+
+static int pcf50633_mbc_remove(struct platform_device *pdev)
+{
+       struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
+       int i;
+
+       /* Remove IRQ handlers */
+       for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
+               pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
+
+       sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group);
+       power_supply_unregister(mbc->usb);
+       power_supply_unregister(mbc->adapter);
+       power_supply_unregister(mbc->ac);
+
+       return 0;
+}
+
+static struct platform_driver pcf50633_mbc_driver = {
+       .driver = {
+               .name = "pcf50633-mbc",
+       },
+       .probe = pcf50633_mbc_probe,
+       .remove = pcf50633_mbc_remove,
+};
+
+module_platform_driver(pcf50633_mbc_driver);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 mbc driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-mbc");
diff --git a/drivers/power/supply/pda_power.c b/drivers/power/supply/pda_power.c
new file mode 100644 (file)
index 0000000..dfe1ee8
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * Common power driver for PDAs and phones with one or two external
+ * power supplies (AC/USB) connected to main and backup batteries,
+ * and optional builtin charger.
+ *
+ * Copyright Â© 2007 Anton Vorontsov <cbou@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/power_supply.h>
+#include <linux/pda_power.h>
+#include <linux/regulator/consumer.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/usb/otg.h>
+
+static inline unsigned int get_irq_flags(struct resource *res)
+{
+       return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK);
+}
+
+static struct device *dev;
+static struct pda_power_pdata *pdata;
+static struct resource *ac_irq, *usb_irq;
+static struct timer_list charger_timer;
+static struct timer_list supply_timer;
+static struct timer_list polling_timer;
+static int polling;
+static struct power_supply *pda_psy_ac, *pda_psy_usb;
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+static struct usb_phy *transceiver;
+static struct notifier_block otg_nb;
+#endif
+
+static struct regulator *ac_draw;
+
+enum {
+       PDA_PSY_OFFLINE = 0,
+       PDA_PSY_ONLINE = 1,
+       PDA_PSY_TO_CHANGE,
+};
+static int new_ac_status = -1;
+static int new_usb_status = -1;
+static int ac_status = -1;
+static int usb_status = -1;
+
+static int pda_power_get_property(struct power_supply *psy,
+                                 enum power_supply_property psp,
+                                 union power_supply_propval *val)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               if (psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
+                       val->intval = pdata->is_ac_online ?
+                                     pdata->is_ac_online() : 0;
+               else
+                       val->intval = pdata->is_usb_online ?
+                                     pdata->is_usb_online() : 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static enum power_supply_property pda_power_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *pda_power_supplied_to[] = {
+       "main-battery",
+       "backup-battery",
+};
+
+static const struct power_supply_desc pda_psy_ac_desc = {
+       .name = "ac",
+       .type = POWER_SUPPLY_TYPE_MAINS,
+       .properties = pda_power_props,
+       .num_properties = ARRAY_SIZE(pda_power_props),
+       .get_property = pda_power_get_property,
+};
+
+static const struct power_supply_desc pda_psy_usb_desc = {
+       .name = "usb",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = pda_power_props,
+       .num_properties = ARRAY_SIZE(pda_power_props),
+       .get_property = pda_power_get_property,
+};
+
+static void update_status(void)
+{
+       if (pdata->is_ac_online)
+               new_ac_status = !!pdata->is_ac_online();
+
+       if (pdata->is_usb_online)
+               new_usb_status = !!pdata->is_usb_online();
+}
+
+static void update_charger(void)
+{
+       static int regulator_enabled;
+       int max_uA = pdata->ac_max_uA;
+
+       if (pdata->set_charge) {
+               if (new_ac_status > 0) {
+                       dev_dbg(dev, "charger on (AC)\n");
+                       pdata->set_charge(PDA_POWER_CHARGE_AC);
+               } else if (new_usb_status > 0) {
+                       dev_dbg(dev, "charger on (USB)\n");
+                       pdata->set_charge(PDA_POWER_CHARGE_USB);
+               } else {
+                       dev_dbg(dev, "charger off\n");
+                       pdata->set_charge(0);
+               }
+       } else if (ac_draw) {
+               if (new_ac_status > 0) {
+                       regulator_set_current_limit(ac_draw, max_uA, max_uA);
+                       if (!regulator_enabled) {
+                               dev_dbg(dev, "charger on (AC)\n");
+                               WARN_ON(regulator_enable(ac_draw));
+                               regulator_enabled = 1;
+                       }
+               } else {
+                       if (regulator_enabled) {
+                               dev_dbg(dev, "charger off\n");
+                               WARN_ON(regulator_disable(ac_draw));
+                               regulator_enabled = 0;
+                       }
+               }
+       }
+}
+
+static void supply_timer_func(unsigned long unused)
+{
+       if (ac_status == PDA_PSY_TO_CHANGE) {
+               ac_status = new_ac_status;
+               power_supply_changed(pda_psy_ac);
+       }
+
+       if (usb_status == PDA_PSY_TO_CHANGE) {
+               usb_status = new_usb_status;
+               power_supply_changed(pda_psy_usb);
+       }
+}
+
+static void psy_changed(void)
+{
+       update_charger();
+
+       /*
+        * Okay, charger set. Now wait a bit before notifying supplicants,
+        * charge power should stabilize.
+        */
+       mod_timer(&supply_timer,
+                 jiffies + msecs_to_jiffies(pdata->wait_for_charger));
+}
+
+static void charger_timer_func(unsigned long unused)
+{
+       update_status();
+       psy_changed();
+}
+
+static irqreturn_t power_changed_isr(int irq, void *power_supply)
+{
+       if (power_supply == pda_psy_ac)
+               ac_status = PDA_PSY_TO_CHANGE;
+       else if (power_supply == pda_psy_usb)
+               usb_status = PDA_PSY_TO_CHANGE;
+       else
+               return IRQ_NONE;
+
+       /*
+        * Wait a bit before reading ac/usb line status and setting charger,
+        * because ac/usb status readings may lag from irq.
+        */
+       mod_timer(&charger_timer,
+                 jiffies + msecs_to_jiffies(pdata->wait_for_status));
+
+       return IRQ_HANDLED;
+}
+
+static void polling_timer_func(unsigned long unused)
+{
+       int changed = 0;
+
+       dev_dbg(dev, "polling...\n");
+
+       update_status();
+
+       if (!ac_irq && new_ac_status != ac_status) {
+               ac_status = PDA_PSY_TO_CHANGE;
+               changed = 1;
+       }
+
+       if (!usb_irq && new_usb_status != usb_status) {
+               usb_status = PDA_PSY_TO_CHANGE;
+               changed = 1;
+       }
+
+       if (changed)
+               psy_changed();
+
+       mod_timer(&polling_timer,
+                 jiffies + msecs_to_jiffies(pdata->polling_interval));
+}
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+static int otg_is_usb_online(void)
+{
+       return (transceiver->last_event == USB_EVENT_VBUS ||
+               transceiver->last_event == USB_EVENT_ENUMERATED);
+}
+
+static int otg_is_ac_online(void)
+{
+       return (transceiver->last_event == USB_EVENT_CHARGER);
+}
+
+static int otg_handle_notification(struct notifier_block *nb,
+               unsigned long event, void *unused)
+{
+       switch (event) {
+       case USB_EVENT_CHARGER:
+               ac_status = PDA_PSY_TO_CHANGE;
+               break;
+       case USB_EVENT_VBUS:
+       case USB_EVENT_ENUMERATED:
+               usb_status = PDA_PSY_TO_CHANGE;
+               break;
+       case USB_EVENT_NONE:
+               ac_status = PDA_PSY_TO_CHANGE;
+               usb_status = PDA_PSY_TO_CHANGE;
+               break;
+       default:
+               return NOTIFY_OK;
+       }
+
+       /*
+        * Wait a bit before reading ac/usb line status and setting charger,
+        * because ac/usb status readings may lag from irq.
+        */
+       mod_timer(&charger_timer,
+                 jiffies + msecs_to_jiffies(pdata->wait_for_status));
+
+       return NOTIFY_OK;
+}
+#endif
+
+static int pda_power_probe(struct platform_device *pdev)
+{
+       struct power_supply_config psy_cfg = {};
+       int ret = 0;
+
+       dev = &pdev->dev;
+
+       if (pdev->id != -1) {
+               dev_err(dev, "it's meaningless to register several "
+                       "pda_powers; use id = -1\n");
+               ret = -EINVAL;
+               goto wrongid;
+       }
+
+       pdata = pdev->dev.platform_data;
+
+       if (pdata->init) {
+               ret = pdata->init(dev);
+               if (ret < 0)
+                       goto init_failed;
+       }
+
+       ac_draw = regulator_get(dev, "ac_draw");
+       if (IS_ERR(ac_draw)) {
+               dev_dbg(dev, "couldn't get ac_draw regulator\n");
+               ac_draw = NULL;
+       }
+
+       update_status();
+       update_charger();
+
+       if (!pdata->wait_for_status)
+               pdata->wait_for_status = 500;
+
+       if (!pdata->wait_for_charger)
+               pdata->wait_for_charger = 500;
+
+       if (!pdata->polling_interval)
+               pdata->polling_interval = 2000;
+
+       if (!pdata->ac_max_uA)
+               pdata->ac_max_uA = 500000;
+
+       setup_timer(&charger_timer, charger_timer_func, 0);
+       setup_timer(&supply_timer, supply_timer_func, 0);
+
+       ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
+       usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
+
+       if (pdata->supplied_to) {
+               psy_cfg.supplied_to = pdata->supplied_to;
+               psy_cfg.num_supplicants = pdata->num_supplicants;
+       } else {
+               psy_cfg.supplied_to = pda_power_supplied_to;
+               psy_cfg.num_supplicants = ARRAY_SIZE(pda_power_supplied_to);
+       }
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+       transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+       if (!IS_ERR_OR_NULL(transceiver)) {
+               if (!pdata->is_usb_online)
+                       pdata->is_usb_online = otg_is_usb_online;
+               if (!pdata->is_ac_online)
+                       pdata->is_ac_online = otg_is_ac_online;
+       }
+#endif
+
+       if (pdata->is_ac_online) {
+               pda_psy_ac = power_supply_register(&pdev->dev,
+                                                  &pda_psy_ac_desc, &psy_cfg);
+               if (IS_ERR(pda_psy_ac)) {
+                       dev_err(dev, "failed to register %s power supply\n",
+                               pda_psy_ac_desc.name);
+                       ret = PTR_ERR(pda_psy_ac);
+                       goto ac_supply_failed;
+               }
+
+               if (ac_irq) {
+                       ret = request_irq(ac_irq->start, power_changed_isr,
+                                         get_irq_flags(ac_irq), ac_irq->name,
+                                         pda_psy_ac);
+                       if (ret) {
+                               dev_err(dev, "request ac irq failed\n");
+                               goto ac_irq_failed;
+                       }
+               } else {
+                       polling = 1;
+               }
+       }
+
+       if (pdata->is_usb_online) {
+               pda_psy_usb = power_supply_register(&pdev->dev,
+                                                   &pda_psy_usb_desc,
+                                                   &psy_cfg);
+               if (IS_ERR(pda_psy_usb)) {
+                       dev_err(dev, "failed to register %s power supply\n",
+                               pda_psy_usb_desc.name);
+                       ret = PTR_ERR(pda_psy_usb);
+                       goto usb_supply_failed;
+               }
+
+               if (usb_irq) {
+                       ret = request_irq(usb_irq->start, power_changed_isr,
+                                         get_irq_flags(usb_irq),
+                                         usb_irq->name, pda_psy_usb);
+                       if (ret) {
+                               dev_err(dev, "request usb irq failed\n");
+                               goto usb_irq_failed;
+                       }
+               } else {
+                       polling = 1;
+               }
+       }
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+       if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) {
+               otg_nb.notifier_call = otg_handle_notification;
+               ret = usb_register_notifier(transceiver, &otg_nb);
+               if (ret) {
+                       dev_err(dev, "failure to register otg notifier\n");
+                       goto otg_reg_notifier_failed;
+               }
+               polling = 0;
+       }
+#endif
+
+       if (polling) {
+               dev_dbg(dev, "will poll for status\n");
+               setup_timer(&polling_timer, polling_timer_func, 0);
+               mod_timer(&polling_timer,
+                         jiffies + msecs_to_jiffies(pdata->polling_interval));
+       }
+
+       if (ac_irq || usb_irq)
+               device_init_wakeup(&pdev->dev, 1);
+
+       return 0;
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+otg_reg_notifier_failed:
+       if (pdata->is_usb_online && usb_irq)
+               free_irq(usb_irq->start, pda_psy_usb);
+#endif
+usb_irq_failed:
+       if (pdata->is_usb_online)
+               power_supply_unregister(pda_psy_usb);
+usb_supply_failed:
+       if (pdata->is_ac_online && ac_irq)
+               free_irq(ac_irq->start, pda_psy_ac);
+#if IS_ENABLED(CONFIG_USB_PHY)
+       if (!IS_ERR_OR_NULL(transceiver))
+               usb_put_phy(transceiver);
+#endif
+ac_irq_failed:
+       if (pdata->is_ac_online)
+               power_supply_unregister(pda_psy_ac);
+ac_supply_failed:
+       if (ac_draw) {
+               regulator_put(ac_draw);
+               ac_draw = NULL;
+       }
+       if (pdata->exit)
+               pdata->exit(dev);
+init_failed:
+wrongid:
+       return ret;
+}
+
+static int pda_power_remove(struct platform_device *pdev)
+{
+       if (pdata->is_usb_online && usb_irq)
+               free_irq(usb_irq->start, pda_psy_usb);
+       if (pdata->is_ac_online && ac_irq)
+               free_irq(ac_irq->start, pda_psy_ac);
+
+       if (polling)
+               del_timer_sync(&polling_timer);
+       del_timer_sync(&charger_timer);
+       del_timer_sync(&supply_timer);
+
+       if (pdata->is_usb_online)
+               power_supply_unregister(pda_psy_usb);
+       if (pdata->is_ac_online)
+               power_supply_unregister(pda_psy_ac);
+#if IS_ENABLED(CONFIG_USB_PHY)
+       if (!IS_ERR_OR_NULL(transceiver))
+               usb_put_phy(transceiver);
+#endif
+       if (ac_draw) {
+               regulator_put(ac_draw);
+               ac_draw = NULL;
+       }
+       if (pdata->exit)
+               pdata->exit(dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ac_wakeup_enabled;
+static int usb_wakeup_enabled;
+
+static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       if (pdata->suspend) {
+               int ret = pdata->suspend(state);
+
+               if (ret)
+                       return ret;
+       }
+
+       if (device_may_wakeup(&pdev->dev)) {
+               if (ac_irq)
+                       ac_wakeup_enabled = !enable_irq_wake(ac_irq->start);
+               if (usb_irq)
+                       usb_wakeup_enabled = !enable_irq_wake(usb_irq->start);
+       }
+
+       return 0;
+}
+
+static int pda_power_resume(struct platform_device *pdev)
+{
+       if (device_may_wakeup(&pdev->dev)) {
+               if (usb_irq && usb_wakeup_enabled)
+                       disable_irq_wake(usb_irq->start);
+               if (ac_irq && ac_wakeup_enabled)
+                       disable_irq_wake(ac_irq->start);
+       }
+
+       if (pdata->resume)
+               return pdata->resume();
+
+       return 0;
+}
+#else
+#define pda_power_suspend NULL
+#define pda_power_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver pda_power_pdrv = {
+       .driver = {
+               .name = "pda-power",
+       },
+       .probe = pda_power_probe,
+       .remove = pda_power_remove,
+       .suspend = pda_power_suspend,
+       .resume = pda_power_resume,
+};
+
+module_platform_driver(pda_power_pdrv);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
+MODULE_ALIAS("platform:pda-power");
diff --git a/drivers/power/supply/pm2301_charger.c b/drivers/power/supply/pm2301_charger.c
new file mode 100644 (file)
index 0000000..fb62ed3
--- /dev/null
@@ -0,0 +1,1257 @@
+/*
+ * Copyright 2012 ST Ericsson.
+ *
+ * Power supply driver for ST Ericsson pm2xxx_charger charger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/pm2301_charger.h>
+#include <linux/gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+
+#include "pm2301_charger.h"
+
+#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
+               struct pm2xxx_charger, ac_chg)
+#define SLEEP_MIN              50
+#define SLEEP_MAX              100
+#define PM2XXX_AUTOSUSPEND_DELAY 500
+
+static int pm2xxx_interrupt_registers[] = {
+       PM2XXX_REG_INT1,
+       PM2XXX_REG_INT2,
+       PM2XXX_REG_INT3,
+       PM2XXX_REG_INT4,
+       PM2XXX_REG_INT5,
+       PM2XXX_REG_INT6,
+};
+
+static enum power_supply_property pm2xxx_charger_ac_props[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+};
+
+static int pm2xxx_charger_voltage_map[] = {
+       3500,
+       3525,
+       3550,
+       3575,
+       3600,
+       3625,
+       3650,
+       3675,
+       3700,
+       3725,
+       3750,
+       3775,
+       3800,
+       3825,
+       3850,
+       3875,
+       3900,
+       3925,
+       3950,
+       3975,
+       4000,
+       4025,
+       4050,
+       4075,
+       4100,
+       4125,
+       4150,
+       4175,
+       4200,
+       4225,
+       4250,
+       4275,
+       4300,
+};
+
+static int pm2xxx_charger_current_map[] = {
+       200,
+       200,
+       400,
+       600,
+       800,
+       1000,
+       1200,
+       1400,
+       1600,
+       1800,
+       2000,
+       2200,
+       2400,
+       2600,
+       2800,
+       3000,
+};
+
+static const struct i2c_device_id pm2xxx_ident[] = {
+       { "pm2301", 0 },
+       { }
+};
+
+static void set_lpn_pin(struct pm2xxx_charger *pm2)
+{
+       if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) {
+               gpio_set_value(pm2->lpn_pin, 1);
+               usleep_range(SLEEP_MIN, SLEEP_MAX);
+       }
+}
+
+static void clear_lpn_pin(struct pm2xxx_charger *pm2)
+{
+       if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin))
+               gpio_set_value(pm2->lpn_pin, 0);
+}
+
+static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
+{
+       int ret;
+
+       /* wake up the device */
+       pm_runtime_get_sync(pm2->dev);
+
+       ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
+                               1, val);
+       if (ret < 0)
+               dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
+       else
+               ret = 0;
+
+       pm_runtime_put_sync(pm2->dev);
+
+       return ret;
+}
+
+static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
+{
+       int ret;
+
+       /* wake up the device */
+       pm_runtime_get_sync(pm2->dev);
+
+       ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
+                               1, &val);
+       if (ret < 0)
+               dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
+       else
+               ret = 0;
+
+       pm_runtime_put_sync(pm2->dev);
+
+       return ret;
+}
+
+static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2)
+{
+       int ret;
+
+       /* Enable charging */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
+                       (PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA));
+
+       return ret;
+}
+
+static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2)
+{
+       int ret;
+
+       /* Disable SW EOC ctrl */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, PM2XXX_SWCTRL_HW);
+       if (ret < 0) {
+               dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
+               return ret;
+       }
+
+       /* Disable charging */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
+                       (PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS));
+       if (ret < 0) {
+               dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
+{
+       queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
+
+       return 0;
+}
+
+
+static int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
+{
+       queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
+
+       return 0;
+}
+
+static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val)
+{
+       dev_err(pm2->dev, "Overvoltage detected\n");
+       pm2->flags.ovv = true;
+       power_supply_changed(pm2->ac_chg.psy);
+
+       /* Schedule a new HW failure check */
+       queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0);
+
+       return 0;
+}
+
+static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
+{
+       dev_dbg(pm2->dev , "20 minutes watchdog expired\n");
+
+       pm2->ac.wd_expired = true;
+       power_supply_changed(pm2->ac_chg.psy);
+
+       return 0;
+}
+
+static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val)
+{
+       int ret;
+
+       switch (val) {
+       case PM2XXX_INT1_ITVBATLOWR:
+               dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n");
+               /* Enable SW EOC ctrl */
+               ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
+                                                       PM2XXX_SWCTRL_SW);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
+                       return ret;
+               }
+               break;
+
+       case PM2XXX_INT1_ITVBATLOWF:
+               dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n");
+               /* Disable SW EOC ctrl */
+               ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
+                                                       PM2XXX_SWCTRL_HW);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
+                       return ret;
+               }
+               break;
+
+       default:
+               dev_err(pm2->dev, "Unknown VBAT level\n");
+       }
+
+       return 0;
+}
+
+static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val)
+{
+       dev_dbg(pm2->dev, "battery disconnected\n");
+
+       return 0;
+}
+
+static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
+{
+       int ret;
+
+       ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val);
+
+       if (ret < 0) {
+               dev_err(pm2->dev, "Charger detection failed\n");
+               goto out;
+       }
+
+       *val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG);
+
+out:
+       return ret;
+}
+
+static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val)
+{
+
+       int ret;
+       u8 read_val;
+
+       /*
+        * Since we can't be sure that the events are received
+        * synchronously, we have the check if the main charger is
+        * connected by reading the interrupt source register.
+        */
+       ret = pm2xxx_charger_detection(pm2, &read_val);
+
+       if ((ret == 0) && read_val) {
+               pm2->ac.charger_connected = 1;
+               pm2->ac_conn = true;
+               queue_work(pm2->charger_wq, &pm2->ac_work);
+       }
+
+
+       return ret;
+}
+
+static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2,
+                                                               int val)
+{
+       pm2->ac.charger_connected = 0;
+       queue_work(pm2->charger_wq, &pm2->ac_work);
+
+       return 0;
+}
+
+static int pm2_int_reg0(void *pm2_data, int val)
+{
+       struct pm2xxx_charger *pm2 = pm2_data;
+       int ret = 0;
+
+       if (val & PM2XXX_INT1_ITVBATLOWR) {
+               ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
+                                               PM2XXX_INT1_ITVBATLOWR);
+               if (ret < 0)
+                       goto out;
+       }
+
+       if (val & PM2XXX_INT1_ITVBATLOWF) {
+               ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
+                                               PM2XXX_INT1_ITVBATLOWF);
+               if (ret < 0)
+                       goto out;
+       }
+
+       if (val & PM2XXX_INT1_ITVBATDISCONNECT) {
+               ret = pm2xxx_charger_bat_disc_mngt(pm2,
+                               PM2XXX_INT1_ITVBATDISCONNECT);
+               if (ret < 0)
+                       goto out;
+       }
+out:
+       return ret;
+}
+
+static int pm2_int_reg1(void *pm2_data, int val)
+{
+       struct pm2xxx_charger *pm2 = pm2_data;
+       int ret = 0;
+
+       if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
+               dev_dbg(pm2->dev , "Main charger plugged\n");
+               ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val &
+                       (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG));
+       }
+
+       if (val &
+               (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) {
+               dev_dbg(pm2->dev , "Main charger unplugged\n");
+               ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val &
+                                               (PM2XXX_INT2_ITVPWR1UNPLUG |
+                                               PM2XXX_INT2_ITVPWR2UNPLUG));
+       }
+
+       return ret;
+}
+
+static int pm2_int_reg2(void *pm2_data, int val)
+{
+       struct pm2xxx_charger *pm2 = pm2_data;
+       int ret = 0;
+
+       if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD)
+               ret = pm2xxx_charger_wd_exp_mngt(pm2, val);
+
+       if (val & (PM2XXX_INT3_ITCHPRECHARGEWD |
+                               PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) {
+               dev_dbg(pm2->dev,
+                       "Watchdog occurred for precharge, CC and CV charge\n");
+       }
+
+       return ret;
+}
+
+static int pm2_int_reg3(void *pm2_data, int val)
+{
+       struct pm2xxx_charger *pm2 = pm2_data;
+       int ret = 0;
+
+       if (val & (PM2XXX_INT4_ITCHARGINGON)) {
+               dev_dbg(pm2->dev ,
+                       "chargind operation has started\n");
+       }
+
+       if (val & (PM2XXX_INT4_ITVRESUME)) {
+               dev_dbg(pm2->dev,
+                       "battery discharged down to VResume threshold\n");
+       }
+
+       if (val & (PM2XXX_INT4_ITBATTFULL)) {
+               dev_dbg(pm2->dev , "battery fully detected\n");
+       }
+
+       if (val & (PM2XXX_INT4_ITCVPHASE)) {
+               dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n");
+       }
+
+       if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
+               pm2->failure_case = VPWR_OVV;
+               ret = pm2xxx_charger_ovv_mngt(pm2, val &
+                       (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV));
+               dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n");
+       }
+
+       if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD |
+                               PM2XXX_INT4_S_ITBATTEMPHOT)) {
+               ret = pm2xxx_charger_batt_therm_mngt(pm2, val &
+                       (PM2XXX_INT4_S_ITBATTEMPCOLD |
+                       PM2XXX_INT4_S_ITBATTEMPHOT));
+               dev_dbg(pm2->dev, "BTEMP is too Low/High\n");
+       }
+
+       return ret;
+}
+
+static int pm2_int_reg4(void *pm2_data, int val)
+{
+       struct pm2xxx_charger *pm2 = pm2_data;
+       int ret = 0;
+
+       if (val & PM2XXX_INT5_ITVSYSTEMOVV) {
+               pm2->failure_case = VSYSTEM_OVV;
+               ret = pm2xxx_charger_ovv_mngt(pm2, val &
+                                               PM2XXX_INT5_ITVSYSTEMOVV);
+               dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n");
+       }
+
+       if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL |
+                               PM2XXX_INT5_ITTHERMALWARNINGRISE |
+                               PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
+                               PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) {
+               dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n");
+               ret = pm2xxx_charger_die_therm_mngt(pm2, val &
+                       (PM2XXX_INT5_ITTHERMALWARNINGFALL |
+                       PM2XXX_INT5_ITTHERMALWARNINGRISE |
+                       PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
+                       PM2XXX_INT5_ITTHERMALSHUTDOWNRISE));
+       }
+
+       return ret;
+}
+
+static int pm2_int_reg5(void *pm2_data, int val)
+{
+       struct pm2xxx_charger *pm2 = pm2_data;
+       int ret = 0;
+
+       if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
+               dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
+       }
+
+       if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE |
+                       PM2XXX_INT6_ITVPWR1VALIDRISE |
+                       PM2XXX_INT6_ITVPWR2VALIDFALL |
+                       PM2XXX_INT6_ITVPWR1VALIDFALL)) {
+               dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n");
+       }
+
+       return ret;
+}
+
+static irqreturn_t  pm2xxx_irq_int(int irq, void *data)
+{
+       struct pm2xxx_charger *pm2 = data;
+       struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
+       int i;
+
+       /* wake up the device */
+       pm_runtime_get_sync(pm2->dev);
+
+       do {
+               for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
+                       pm2xxx_reg_read(pm2,
+                               pm2xxx_interrupt_registers[i],
+                               &(interrupt->reg[i]));
+
+                       if (interrupt->reg[i] > 0)
+                               interrupt->handler[i](pm2, interrupt->reg[i]);
+               }
+       } while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0);
+
+       pm_runtime_mark_last_busy(pm2->dev);
+       pm_runtime_put_autosuspend(pm2->dev);
+
+       return IRQ_HANDLED;
+}
+
+static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2)
+{
+       int ret = 0;
+       u8 val;
+
+       if (pm2->ac.charger_connected && pm2->ac.charger_online) {
+
+               ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
+                       goto out;
+               }
+
+               if (val & PM2XXX_INT4_S_ITCVPHASE)
+                       ret = PM2XXX_CONST_VOLT;
+               else
+                       ret = PM2XXX_CONST_CURR;
+       }
+out:
+       return ret;
+}
+
+static int pm2xxx_current_to_regval(int curr)
+{
+       int i;
+
+       if (curr < pm2xxx_charger_current_map[0])
+               return 0;
+
+       for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) {
+               if (curr < pm2xxx_charger_current_map[i])
+                       return (i - 1);
+       }
+
+       i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1;
+       if (curr == pm2xxx_charger_current_map[i])
+               return i;
+       else
+               return -EINVAL;
+}
+
+static int pm2xxx_voltage_to_regval(int curr)
+{
+       int i;
+
+       if (curr < pm2xxx_charger_voltage_map[0])
+               return 0;
+
+       for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) {
+               if (curr < pm2xxx_charger_voltage_map[i])
+                       return i - 1;
+       }
+
+       i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1;
+       if (curr == pm2xxx_charger_voltage_map[i])
+               return i;
+       else
+               return -EINVAL;
+}
+
+static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger,
+               int ich_out)
+{
+       int ret;
+       int curr_index;
+       struct pm2xxx_charger *pm2;
+       u8 val;
+
+       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
+               pm2 = to_pm2xxx_charger_ac_device_info(charger);
+       else
+               return -ENXIO;
+
+       curr_index = pm2xxx_current_to_regval(ich_out);
+       if (curr_index < 0) {
+               dev_err(pm2->dev,
+                       "Charger current too high, charging not started\n");
+               return -ENXIO;
+       }
+
+       ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
+       if (ret >= 0) {
+               val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK;
+               val |= curr_index;
+               ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val);
+               if (ret < 0) {
+                       dev_err(pm2->dev,
+                               "%s write failed\n", __func__);
+               }
+       }
+       else
+               dev_err(pm2->dev, "%s read failed\n", __func__);
+
+       return ret;
+}
+
+static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct pm2xxx_charger *pm2;
+
+       pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy));
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (pm2->flags.mainextchnotok)
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               else if (pm2->ac.wd_expired)
+                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               else if (pm2->flags.main_thermal_prot)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else if (pm2->flags.ovv)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = pm2->ac.charger_online;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = pm2->ac.charger_connected;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2);
+               val->intval = pm2->ac.cv_active;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
+{
+       int ret = 0;
+
+       /* enable CC and CV watchdog */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3,
+               (PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN));
+       if( ret < 0)
+               return ret;
+
+       /* enable precharge watchdog */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4,
+                                       PM2XXX_CH_WD_PRECH_PHASE_60MIN);
+
+       /* Disable auto timeout */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG5,
+                                       PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN);
+
+       /*
+     * EOC current level = 100mA
+        * Precharge current level = 100mA
+        * CC current level = 1000mA
+        */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6,
+               (PM2XXX_DIR_CH_CC_CURRENT_1000MA |
+               PM2XXX_CH_PRECH_CURRENT_100MA |
+               PM2XXX_CH_EOC_CURRENT_100MA));
+
+       /*
+     * recharge threshold = 3.8V
+        * Precharge to CC threshold = 2.9V
+        */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG7,
+               (PM2XXX_CH_PRECH_VOL_2_9 | PM2XXX_CH_VRESUME_VOL_3_8));
+
+       /* float voltage charger level = 4.2V */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8,
+               PM2XXX_CH_VOLT_4_2);
+
+       /* Voltage drop between VBAT and VSYS in HW charging = 300mV */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG9,
+               (PM2XXX_CH_150MV_DROP_300MV | PM2XXX_CHARCHING_INFO_DIS |
+               PM2XXX_CH_CC_REDUCED_CURRENT_IDENT |
+               PM2XXX_CH_CC_MODEDROP_DIS));
+
+       /* Input charger level of over voltage = 10V */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR2,
+                                       PM2XXX_VPWR2_OVV_10);
+       ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR1,
+                                       PM2XXX_VPWR1_OVV_10);
+
+       /* Input charger drop */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR2,
+               (PM2XXX_VPWR2_HW_OPT_DIS | PM2XXX_VPWR2_VALID_DIS |
+               PM2XXX_VPWR2_DROP_DIS));
+       ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR1,
+               (PM2XXX_VPWR1_HW_OPT_DIS | PM2XXX_VPWR1_VALID_DIS |
+               PM2XXX_VPWR1_DROP_DIS));
+
+       /* Disable battery low monitoring */
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG,
+               PM2XXX_VBAT_LOW_MONITORING_ENA);
+
+       return ret;
+}
+
+static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
+       int enable, int vset, int iset)
+{
+       int ret;
+       int volt_index;
+       int curr_index;
+       u8 val;
+
+       struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger);
+
+       if (enable) {
+               if (!pm2->ac.charger_connected) {
+                       dev_dbg(pm2->dev, "AC charger not connected\n");
+                       return -ENXIO;
+               }
+
+               dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset);
+               if (!pm2->vddadc_en_ac) {
+                       ret = regulator_enable(pm2->regu);
+                       if (ret)
+                               dev_warn(pm2->dev,
+                                       "Failed to enable vddadc regulator\n");
+                       else
+                               pm2->vddadc_en_ac = true;
+               }
+
+               ret = pm2xxx_charging_init(pm2);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "%s charging init failed\n",
+                                       __func__);
+                       goto error_occured;
+               }
+
+               volt_index = pm2xxx_voltage_to_regval(vset);
+               curr_index = pm2xxx_current_to_regval(iset);
+
+               if (volt_index < 0 || curr_index < 0) {
+                       dev_err(pm2->dev,
+                               "Charger voltage or current too high, "
+                               "charging not started\n");
+                       return -ENXIO;
+               }
+
+               ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
+                       goto error_occured;
+               }
+               val &= ~PM2XXX_CH_VOLT_MASK;
+               val |= volt_index;
+               ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
+                       goto error_occured;
+               }
+
+               ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
+                       goto error_occured;
+               }
+               val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK;
+               val |= curr_index;
+               ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
+                       goto error_occured;
+               }
+
+               if (!pm2->bat->enable_overshoot) {
+                       ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val);
+                       if (ret < 0) {
+                               dev_err(pm2->dev, "%s pm2xxx read failed\n",
+                                                               __func__);
+                               goto error_occured;
+                       }
+                       val |= PM2XXX_ANTI_OVERSHOOT_EN;
+                       ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, val);
+                       if (ret < 0) {
+                               dev_err(pm2->dev, "%s pm2xxx write failed\n",
+                                                               __func__);
+                               goto error_occured;
+                       }
+               }
+
+               ret = pm2xxx_charging_enable_mngt(pm2);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "Failed to enable"
+                                               "pm2xxx ac charger\n");
+                       goto error_occured;
+               }
+
+               pm2->ac.charger_online = 1;
+       } else {
+               pm2->ac.charger_online = 0;
+               pm2->ac.wd_expired = false;
+
+               /* Disable regulator if enabled */
+               if (pm2->vddadc_en_ac) {
+                       regulator_disable(pm2->regu);
+                       pm2->vddadc_en_ac = false;
+               }
+
+               ret = pm2xxx_charging_disable_mngt(pm2);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "failed to disable"
+                                               "pm2xxx ac charger\n");
+                       goto error_occured;
+               }
+
+               dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n");
+       }
+       power_supply_changed(pm2->ac_chg.psy);
+
+error_occured:
+       return ret;
+}
+
+static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger)
+{
+       int ret;
+       struct pm2xxx_charger *pm2;
+
+       if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
+               pm2 = to_pm2xxx_charger_ac_device_info(charger);
+       else
+               return -ENXIO;
+
+       ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER);
+       if (ret)
+               dev_err(pm2->dev, "Failed to kick WD!\n");
+
+       return ret;
+}
+
+static void pm2xxx_charger_ac_work(struct work_struct *work)
+{
+       struct pm2xxx_charger *pm2 = container_of(work,
+               struct pm2xxx_charger, ac_work);
+
+
+       power_supply_changed(pm2->ac_chg.psy);
+       sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present");
+};
+
+static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work)
+{
+       u8 reg_value;
+
+       struct pm2xxx_charger *pm2 = container_of(work,
+               struct pm2xxx_charger, check_hw_failure_work.work);
+
+       if (pm2->flags.ovv) {
+               pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &reg_value);
+
+               if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV |
+                                       PM2XXX_INT4_S_ITVPWR2OVV))) {
+                       pm2->flags.ovv = false;
+                       power_supply_changed(pm2->ac_chg.psy);
+               }
+       }
+
+       /* If we still have a failure, schedule a new check */
+       if (pm2->flags.ovv) {
+               queue_delayed_work(pm2->charger_wq,
+                       &pm2->check_hw_failure_work, round_jiffies(HZ));
+       }
+}
+
+static void pm2xxx_charger_check_main_thermal_prot_work(
+       struct work_struct *work)
+{
+       int ret;
+       u8 val;
+
+       struct pm2xxx_charger *pm2 = container_of(work, struct pm2xxx_charger,
+                                       check_main_thermal_prot_work);
+
+       /* Check if die temp warning is still active */
+       ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT5, &val);
+       if (ret < 0) {
+               dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
+               return;
+       }
+       if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGRISE
+                       | PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE))
+               pm2->flags.main_thermal_prot = true;
+       else if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGFALL
+                               | PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL))
+               pm2->flags.main_thermal_prot = false;
+
+       power_supply_changed(pm2->ac_chg.psy);
+}
+
+static struct pm2xxx_interrupts pm2xxx_int = {
+       .handler[0] = pm2_int_reg0,
+       .handler[1] = pm2_int_reg1,
+       .handler[2] = pm2_int_reg2,
+       .handler[3] = pm2_int_reg3,
+       .handler[4] = pm2_int_reg4,
+       .handler[5] = pm2_int_reg5,
+};
+
+static struct pm2xxx_irq pm2xxx_charger_irq[] = {
+       {"PM2XXX_IRQ_INT", pm2xxx_irq_int},
+};
+
+static int __maybe_unused pm2xxx_wall_charger_resume(struct device *dev)
+{
+       struct i2c_client *i2c_client = to_i2c_client(dev);
+       struct pm2xxx_charger *pm2;
+
+       pm2 =  (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
+       set_lpn_pin(pm2);
+
+       /* If we still have a HW failure, schedule a new check */
+       if (pm2->flags.ovv)
+               queue_delayed_work(pm2->charger_wq,
+                               &pm2->check_hw_failure_work, 0);
+
+       return 0;
+}
+
+static int __maybe_unused pm2xxx_wall_charger_suspend(struct device *dev)
+{
+       struct i2c_client *i2c_client = to_i2c_client(dev);
+       struct pm2xxx_charger *pm2;
+
+       pm2 =  (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
+       clear_lpn_pin(pm2);
+
+       /* Cancel any pending HW failure check */
+       if (delayed_work_pending(&pm2->check_hw_failure_work))
+               cancel_delayed_work(&pm2->check_hw_failure_work);
+
+       flush_work(&pm2->ac_work);
+       flush_work(&pm2->check_main_thermal_prot_work);
+
+       return 0;
+}
+
+static int __maybe_unused pm2xxx_runtime_suspend(struct device *dev)
+{
+       struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
+       struct pm2xxx_charger *pm2;
+
+       pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
+       clear_lpn_pin(pm2);
+
+       return 0;
+}
+
+static int __maybe_unused pm2xxx_runtime_resume(struct device *dev)
+{
+       struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
+       struct pm2xxx_charger *pm2;
+
+       pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
+
+       if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0)
+               set_lpn_pin(pm2);
+
+       return 0;
+}
+
+static const struct dev_pm_ops pm2xxx_pm_ops __maybe_unused = {
+       SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend,
+               pm2xxx_wall_charger_resume)
+       SET_RUNTIME_PM_OPS(pm2xxx_runtime_suspend, pm2xxx_runtime_resume, NULL)
+};
+
+static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
+               const struct i2c_device_id *id)
+{
+       struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       struct pm2xxx_charger *pm2;
+       int ret = 0;
+       u8 val;
+       int i;
+
+       if (!pl_data) {
+               dev_err(&i2c_client->dev, "No platform data supplied\n");
+               return -EINVAL;
+       }
+
+       pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL);
+       if (!pm2) {
+               dev_err(&i2c_client->dev, "pm2xxx_charger allocation failed\n");
+               return -ENOMEM;
+       }
+
+       /* get parent data */
+       pm2->dev = &i2c_client->dev;
+
+       pm2->pm2_int = &pm2xxx_int;
+
+       /* get charger spcific platform data */
+       if (!pl_data->wall_charger) {
+               dev_err(pm2->dev, "no charger platform data supplied\n");
+               ret = -EINVAL;
+               goto free_device_info;
+       }
+
+       pm2->pdata = pl_data->wall_charger;
+
+       /* get battery specific platform data */
+       if (!pl_data->battery) {
+               dev_err(pm2->dev, "no battery platform data supplied\n");
+               ret = -EINVAL;
+               goto free_device_info;
+       }
+
+       pm2->bat = pl_data->battery;
+
+       if (!i2c_check_functionality(i2c_client->adapter,
+                       I2C_FUNC_SMBUS_BYTE_DATA |
+                       I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+               ret = -ENODEV;
+               dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n");
+               goto free_device_info;
+       }
+
+       pm2->config.pm2xxx_i2c = i2c_client;
+       pm2->config.pm2xxx_id = (struct i2c_device_id *) id;
+       i2c_set_clientdata(i2c_client, pm2);
+
+       /* AC supply */
+       /* power_supply base class */
+       pm2->ac_chg_desc.name = pm2->pdata->label;
+       pm2->ac_chg_desc.type = POWER_SUPPLY_TYPE_MAINS;
+       pm2->ac_chg_desc.properties = pm2xxx_charger_ac_props;
+       pm2->ac_chg_desc.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props);
+       pm2->ac_chg_desc.get_property = pm2xxx_charger_ac_get_property;
+
+       psy_cfg.supplied_to = pm2->pdata->supplied_to;
+       psy_cfg.num_supplicants = pm2->pdata->num_supplicants;
+       /* pm2xxx_charger sub-class */
+       pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en;
+       pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick;
+       pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current;
+       pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[
+               ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1];
+       pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[
+               ARRAY_SIZE(pm2xxx_charger_current_map) - 1];
+       pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL;
+       pm2->ac_chg.enabled = true;
+       pm2->ac_chg.external = true;
+
+       /* Create a work queue for the charger */
+       pm2->charger_wq = create_singlethread_workqueue("pm2xxx_charger_wq");
+       if (pm2->charger_wq == NULL) {
+               ret = -ENOMEM;
+               dev_err(pm2->dev, "failed to create work queue\n");
+               goto free_device_info;
+       }
+
+       /* Init work for charger detection */
+       INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work);
+
+       /* Init work for checking HW status */
+       INIT_WORK(&pm2->check_main_thermal_prot_work,
+               pm2xxx_charger_check_main_thermal_prot_work);
+
+       /* Init work for HW failure check */
+       INIT_DEFERRABLE_WORK(&pm2->check_hw_failure_work,
+               pm2xxx_charger_check_hw_failure_work);
+
+       /*
+        * VDD ADC supply needs to be enabled from this driver when there
+        * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
+        * interrupts during charging
+        */
+       pm2->regu = regulator_get(pm2->dev, "vddadc");
+       if (IS_ERR(pm2->regu)) {
+               ret = PTR_ERR(pm2->regu);
+               dev_err(pm2->dev, "failed to get vddadc regulator\n");
+               goto free_charger_wq;
+       }
+
+       /* Register AC charger class */
+       pm2->ac_chg.psy = power_supply_register(pm2->dev, &pm2->ac_chg_desc,
+                                               &psy_cfg);
+       if (IS_ERR(pm2->ac_chg.psy)) {
+               dev_err(pm2->dev, "failed to register AC charger\n");
+               ret = PTR_ERR(pm2->ac_chg.psy);
+               goto free_regulator;
+       }
+
+       /* Register interrupts */
+       ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number),
+                               NULL,
+                               pm2xxx_charger_irq[0].isr,
+                               pm2->pdata->irq_type,
+                               pm2xxx_charger_irq[0].name, pm2);
+
+       if (ret != 0) {
+               dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n",
+               pm2xxx_charger_irq[0].name,
+                       gpio_to_irq(pm2->pdata->gpio_irq_number), ret);
+               goto unregister_pm2xxx_charger;
+       }
+
+       ret = pm_runtime_set_active(pm2->dev);
+       if (ret)
+               dev_err(pm2->dev, "set active Error\n");
+
+       pm_runtime_enable(pm2->dev);
+       pm_runtime_set_autosuspend_delay(pm2->dev, PM2XXX_AUTOSUSPEND_DELAY);
+       pm_runtime_use_autosuspend(pm2->dev);
+       pm_runtime_resume(pm2->dev);
+
+       /* pm interrupt can wake up system */
+       ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
+       if (ret) {
+               dev_err(pm2->dev, "failed to set irq wake\n");
+               goto unregister_pm2xxx_interrupt;
+       }
+
+       mutex_init(&pm2->lock);
+
+       if (gpio_is_valid(pm2->pdata->lpn_gpio)) {
+               /* get lpn GPIO from platform data */
+               pm2->lpn_pin = pm2->pdata->lpn_gpio;
+
+               /*
+                * Charger detection mechanism requires pulling up the LPN pin
+                * while i2c communication if Charger is not connected
+                * LPN pin of PM2301 is GPIO60 of AB9540
+                */
+               ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio");
+
+               if (ret < 0) {
+                       dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n");
+                       goto disable_pm2_irq_wake;
+               }
+               ret = gpio_direction_output(pm2->lpn_pin, 0);
+               if (ret < 0) {
+                       dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n");
+                       goto free_gpio;
+               }
+               set_lpn_pin(pm2);
+       }
+
+       /* read  interrupt registers */
+       for (i = 0; i < PM2XXX_NUM_INT_REG; i++)
+               pm2xxx_reg_read(pm2,
+                       pm2xxx_interrupt_registers[i],
+                       &val);
+
+       ret = pm2xxx_charger_detection(pm2, &val);
+
+       if ((ret == 0) && val) {
+               pm2->ac.charger_connected = 1;
+               ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
+                                            AB8500_MAIN_CH_DET);
+               pm2->ac_conn = true;
+               power_supply_changed(pm2->ac_chg.psy);
+               sysfs_notify(&pm2->ac_chg.psy->dev.kobj, NULL, "present");
+       }
+
+       return 0;
+
+free_gpio:
+       if (gpio_is_valid(pm2->lpn_pin))
+               gpio_free(pm2->lpn_pin);
+disable_pm2_irq_wake:
+       disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
+unregister_pm2xxx_interrupt:
+       /* disable interrupt */
+       free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
+unregister_pm2xxx_charger:
+       /* unregister power supply */
+       power_supply_unregister(pm2->ac_chg.psy);
+free_regulator:
+       /* disable the regulator */
+       regulator_put(pm2->regu);
+free_charger_wq:
+       destroy_workqueue(pm2->charger_wq);
+free_device_info:
+       kfree(pm2);
+
+       return ret;
+}
+
+static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
+{
+       struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client);
+
+       /* Disable pm_runtime */
+       pm_runtime_disable(pm2->dev);
+       /* Disable AC charging */
+       pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0);
+
+       /* Disable wake by pm interrupt */
+       disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
+
+       /* Disable interrupts */
+       free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
+
+       /* Delete the work queue */
+       destroy_workqueue(pm2->charger_wq);
+
+       flush_scheduled_work();
+
+       /* disable the regulator */
+       regulator_put(pm2->regu);
+
+       power_supply_unregister(pm2->ac_chg.psy);
+
+       if (gpio_is_valid(pm2->lpn_pin))
+               gpio_free(pm2->lpn_pin);
+
+       kfree(pm2);
+
+       return 0;
+}
+
+static const struct i2c_device_id pm2xxx_id[] = {
+       { "pm2301", 0 },
+       { }
+};
+
+MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
+
+static struct i2c_driver pm2xxx_charger_driver = {
+       .probe = pm2xxx_wall_charger_probe,
+       .remove = pm2xxx_wall_charger_remove,
+       .driver = {
+               .name = "pm2xxx-wall_charger",
+               .pm = IS_ENABLED(CONFIG_PM) ? &pm2xxx_pm_ops : NULL,
+       },
+       .id_table = pm2xxx_id,
+};
+
+static int __init pm2xxx_charger_init(void)
+{
+       return i2c_add_driver(&pm2xxx_charger_driver);
+}
+
+static void __exit pm2xxx_charger_exit(void)
+{
+       i2c_del_driver(&pm2xxx_charger_driver);
+}
+
+device_initcall_sync(pm2xxx_charger_init);
+module_exit(pm2xxx_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
+MODULE_DESCRIPTION("PM2xxx charger management driver");
diff --git a/drivers/power/supply/pm2301_charger.h b/drivers/power/supply/pm2301_charger.h
new file mode 100644 (file)
index 0000000..24181cf
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * PM2301 power supply interface
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef PM2301_CHARGER_H
+#define PM2301_CHARGER_H
+
+/* Watchdog timeout constant */
+#define WD_TIMER                       0x30 /* 4min */
+#define WD_KICK_INTERVAL               (30 * HZ)
+
+#define PM2XXX_NUM_INT_REG             0x6
+
+/* Constant voltage/current */
+#define PM2XXX_CONST_CURR              0x0
+#define PM2XXX_CONST_VOLT              0x1
+
+/* Lowest charger voltage is 3.39V -> 0x4E */
+#define LOW_VOLT_REG                   0x4E
+
+#define PM2XXX_BATT_CTRL_REG1          0x00
+#define PM2XXX_BATT_CTRL_REG2          0x01
+#define PM2XXX_BATT_CTRL_REG3          0x02
+#define PM2XXX_BATT_CTRL_REG4          0x03
+#define PM2XXX_BATT_CTRL_REG5          0x04
+#define PM2XXX_BATT_CTRL_REG6          0x05
+#define PM2XXX_BATT_CTRL_REG7          0x06
+#define PM2XXX_BATT_CTRL_REG8          0x07
+#define PM2XXX_NTC_CTRL_REG1           0x08
+#define PM2XXX_NTC_CTRL_REG2           0x09
+#define PM2XXX_BATT_CTRL_REG9          0x0A
+#define PM2XXX_BATT_STAT_REG1          0x0B
+#define PM2XXX_INP_VOLT_VPWR2          0x11
+#define PM2XXX_INP_DROP_VPWR2          0x13
+#define PM2XXX_INP_VOLT_VPWR1          0x15
+#define PM2XXX_INP_DROP_VPWR1          0x17
+#define PM2XXX_INP_MODE_VPWR           0x18
+#define PM2XXX_BATT_WD_KICK            0x70
+#define PM2XXX_DEV_VER_STAT            0x0C
+#define PM2XXX_THERM_WARN_CTRL_REG     0x20
+#define PM2XXX_BATT_DISC_REG           0x21
+#define PM2XXX_BATT_LOW_LEV_COMP_REG   0x22
+#define PM2XXX_BATT_LOW_LEV_VAL_REG    0x23
+#define PM2XXX_I2C_PAD_CTRL_REG                0x24
+#define PM2XXX_SW_CTRL_REG             0x26
+#define PM2XXX_LED_CTRL_REG            0x28
+
+#define PM2XXX_REG_INT1                        0x40
+#define PM2XXX_MASK_REG_INT1           0x50
+#define PM2XXX_SRCE_REG_INT1           0x60
+#define PM2XXX_REG_INT2                        0x41
+#define PM2XXX_MASK_REG_INT2           0x51
+#define PM2XXX_SRCE_REG_INT2           0x61
+#define PM2XXX_REG_INT3                        0x42
+#define PM2XXX_MASK_REG_INT3           0x52
+#define PM2XXX_SRCE_REG_INT3           0x62
+#define PM2XXX_REG_INT4                        0x43
+#define PM2XXX_MASK_REG_INT4           0x53
+#define PM2XXX_SRCE_REG_INT4           0x63
+#define PM2XXX_REG_INT5                        0x44
+#define PM2XXX_MASK_REG_INT5           0x54
+#define PM2XXX_SRCE_REG_INT5           0x64
+#define PM2XXX_REG_INT6                        0x45
+#define PM2XXX_MASK_REG_INT6           0x55
+#define PM2XXX_SRCE_REG_INT6           0x65
+
+#define VPWR_OVV                       0x0
+#define VSYSTEM_OVV                    0x1
+
+/* control Reg 1 */
+#define PM2XXX_CH_RESUME_EN            0x1
+#define PM2XXX_CH_RESUME_DIS           0x0
+
+/* control Reg 2 */
+#define PM2XXX_CH_AUTO_RESUME_EN       0X2
+#define PM2XXX_CH_AUTO_RESUME_DIS      0X0
+#define PM2XXX_CHARGER_ENA             0x4
+#define PM2XXX_CHARGER_DIS             0x0
+
+/* control Reg 3 */
+#define PM2XXX_CH_WD_CC_PHASE_OFF      0x0
+#define PM2XXX_CH_WD_CC_PHASE_5MIN     0x1
+#define PM2XXX_CH_WD_CC_PHASE_10MIN    0x2
+#define PM2XXX_CH_WD_CC_PHASE_30MIN    0x3
+#define PM2XXX_CH_WD_CC_PHASE_60MIN    0x4
+#define PM2XXX_CH_WD_CC_PHASE_120MIN   0x5
+#define PM2XXX_CH_WD_CC_PHASE_240MIN   0x6
+#define PM2XXX_CH_WD_CC_PHASE_360MIN   0x7
+
+#define PM2XXX_CH_WD_CV_PHASE_OFF      (0x0<<3)
+#define PM2XXX_CH_WD_CV_PHASE_5MIN     (0x1<<3)
+#define PM2XXX_CH_WD_CV_PHASE_10MIN    (0x2<<3)
+#define PM2XXX_CH_WD_CV_PHASE_30MIN    (0x3<<3)
+#define PM2XXX_CH_WD_CV_PHASE_60MIN    (0x4<<3)
+#define PM2XXX_CH_WD_CV_PHASE_120MIN   (0x5<<3)
+#define PM2XXX_CH_WD_CV_PHASE_240MIN   (0x6<<3)
+#define PM2XXX_CH_WD_CV_PHASE_360MIN   (0x7<<3)
+
+/* control Reg 4 */
+#define PM2XXX_CH_WD_PRECH_PHASE_OFF   0x0
+#define PM2XXX_CH_WD_PRECH_PHASE_1MIN  0x1
+#define PM2XXX_CH_WD_PRECH_PHASE_5MIN  0x2
+#define PM2XXX_CH_WD_PRECH_PHASE_10MIN 0x3
+#define PM2XXX_CH_WD_PRECH_PHASE_30MIN 0x4
+#define PM2XXX_CH_WD_PRECH_PHASE_60MIN 0x5
+#define PM2XXX_CH_WD_PRECH_PHASE_120MIN        0x6
+#define PM2XXX_CH_WD_PRECH_PHASE_240MIN        0x7
+
+/* control Reg 5 */
+#define PM2XXX_CH_WD_AUTO_TIMEOUT_NONE 0x0
+#define PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN        0x1
+
+/* control Reg 6 */
+#define PM2XXX_DIR_CH_CC_CURRENT_MASK  0x0F
+#define PM2XXX_DIR_CH_CC_CURRENT_200MA 0x0
+#define PM2XXX_DIR_CH_CC_CURRENT_400MA 0x2
+#define PM2XXX_DIR_CH_CC_CURRENT_600MA 0x3
+#define PM2XXX_DIR_CH_CC_CURRENT_800MA 0x4
+#define PM2XXX_DIR_CH_CC_CURRENT_1000MA        0x5
+#define PM2XXX_DIR_CH_CC_CURRENT_1200MA        0x6
+#define PM2XXX_DIR_CH_CC_CURRENT_1400MA        0x7
+#define PM2XXX_DIR_CH_CC_CURRENT_1600MA        0x8
+#define PM2XXX_DIR_CH_CC_CURRENT_1800MA        0x9
+#define PM2XXX_DIR_CH_CC_CURRENT_2000MA        0xA
+#define PM2XXX_DIR_CH_CC_CURRENT_2200MA        0xB
+#define PM2XXX_DIR_CH_CC_CURRENT_2400MA        0xC
+#define PM2XXX_DIR_CH_CC_CURRENT_2600MA        0xD
+#define PM2XXX_DIR_CH_CC_CURRENT_2800MA        0xE
+#define PM2XXX_DIR_CH_CC_CURRENT_3000MA        0xF
+
+#define PM2XXX_CH_PRECH_CURRENT_MASK   0x30
+#define PM2XXX_CH_PRECH_CURRENT_25MA   (0x0<<4)
+#define PM2XXX_CH_PRECH_CURRENT_50MA   (0x1<<4)
+#define PM2XXX_CH_PRECH_CURRENT_75MA   (0x2<<4)
+#define PM2XXX_CH_PRECH_CURRENT_100MA  (0x3<<4)
+
+#define PM2XXX_CH_EOC_CURRENT_MASK     0xC0
+#define PM2XXX_CH_EOC_CURRENT_100MA    (0x0<<6)
+#define PM2XXX_CH_EOC_CURRENT_150MA    (0x1<<6)
+#define PM2XXX_CH_EOC_CURRENT_300MA    (0x2<<6)
+#define PM2XXX_CH_EOC_CURRENT_400MA    (0x3<<6)
+
+/* control Reg 7 */
+#define PM2XXX_CH_PRECH_VOL_2_5                0x0
+#define PM2XXX_CH_PRECH_VOL_2_7                0x1
+#define PM2XXX_CH_PRECH_VOL_2_9                0x2
+#define PM2XXX_CH_PRECH_VOL_3_1                0x3
+
+#define PM2XXX_CH_VRESUME_VOL_3_2      (0x0<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_4      (0x1<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_6      (0x2<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_8      (0x3<<2)
+
+/* control Reg 8 */
+#define PM2XXX_CH_VOLT_MASK            0x3F
+#define PM2XXX_CH_VOLT_3_5             0x0
+#define PM2XXX_CH_VOLT_3_5225          0x1
+#define PM2XXX_CH_VOLT_3_6             0x4
+#define PM2XXX_CH_VOLT_3_7             0x8
+#define PM2XXX_CH_VOLT_4_0             0x14
+#define PM2XXX_CH_VOLT_4_175           0x1B
+#define PM2XXX_CH_VOLT_4_2             0x1C
+#define PM2XXX_CH_VOLT_4_275           0x1F
+#define PM2XXX_CH_VOLT_4_3             0x20
+
+/*NTC control register 1*/
+#define PM2XXX_BTEMP_HIGH_TH_45                0x0
+#define PM2XXX_BTEMP_HIGH_TH_50                0x1
+#define PM2XXX_BTEMP_HIGH_TH_55                0x2
+#define PM2XXX_BTEMP_HIGH_TH_60                0x3
+#define PM2XXX_BTEMP_HIGH_TH_65                0x4
+
+#define PM2XXX_BTEMP_LOW_TH_N5         (0x0<<3)
+#define PM2XXX_BTEMP_LOW_TH_0          (0x1<<3)
+#define PM2XXX_BTEMP_LOW_TH_5          (0x2<<3)
+#define PM2XXX_BTEMP_LOW_TH_10         (0x3<<3)
+
+/*NTC control register 2*/
+#define PM2XXX_NTC_BETA_COEFF_3477     0x0
+#define PM2XXX_NTC_BETA_COEFF_3964     0x1
+
+#define PM2XXX_NTC_RES_10K             (0x0<<2)
+#define PM2XXX_NTC_RES_47K             (0x1<<2)
+#define PM2XXX_NTC_RES_100K            (0x2<<2)
+#define PM2XXX_NTC_RES_NO_NTC          (0x3<<2)
+
+/* control Reg 9 */
+#define PM2XXX_CH_CC_MODEDROP_EN       1
+#define PM2XXX_CH_CC_MODEDROP_DIS      0
+
+#define PM2XXX_CH_CC_REDUCED_CURRENT_100MA     (0x0<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_200MA     (0x1<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_400MA     (0x2<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_IDENT     (0x3<<1)
+
+#define PM2XXX_CHARCHING_INFO_DIS      (0<<3)
+#define PM2XXX_CHARCHING_INFO_EN       (1<<3)
+
+#define PM2XXX_CH_150MV_DROP_300MV     (0<<4)
+#define PM2XXX_CH_150MV_DROP_150MV     (1<<4)
+
+
+/* charger status register */
+#define PM2XXX_CHG_STATUS_OFF          0x0
+#define PM2XXX_CHG_STATUS_ON           0x1
+#define PM2XXX_CHG_STATUS_FULL         0x2
+#define PM2XXX_CHG_STATUS_ERR          0x3
+#define PM2XXX_CHG_STATUS_WAIT         0x4
+#define PM2XXX_CHG_STATUS_NOBAT                0x5
+
+/* Input charger voltage VPWR2 */
+#define PM2XXX_VPWR2_OVV_6_0           0x0
+#define PM2XXX_VPWR2_OVV_6_3           0x1
+#define PM2XXX_VPWR2_OVV_10            0x2
+#define PM2XXX_VPWR2_OVV_NONE          0x3
+
+/* Input charger drop VPWR2 */
+#define PM2XXX_VPWR2_HW_OPT_EN         (0x1<<4)
+#define PM2XXX_VPWR2_HW_OPT_DIS                (0x0<<4)
+
+#define PM2XXX_VPWR2_VALID_EN          (0x1<<3)
+#define PM2XXX_VPWR2_VALID_DIS         (0x0<<3)
+
+#define PM2XXX_VPWR2_DROP_EN           (0x1<<2)
+#define PM2XXX_VPWR2_DROP_DIS          (0x0<<2)
+
+/* Input charger voltage VPWR1 */
+#define PM2XXX_VPWR1_OVV_6_0           0x0
+#define PM2XXX_VPWR1_OVV_6_3           0x1
+#define PM2XXX_VPWR1_OVV_10            0x2
+#define PM2XXX_VPWR1_OVV_NONE          0x3
+
+/* Input charger drop VPWR1 */
+#define PM2XXX_VPWR1_HW_OPT_EN         (0x1<<4)
+#define PM2XXX_VPWR1_HW_OPT_DIS                (0x0<<4)
+
+#define PM2XXX_VPWR1_VALID_EN          (0x1<<3)
+#define PM2XXX_VPWR1_VALID_DIS         (0x0<<3)
+
+#define PM2XXX_VPWR1_DROP_EN           (0x1<<2)
+#define PM2XXX_VPWR1_DROP_DIS          (0x0<<2)
+
+/* Battery low level comparator control register */
+#define PM2XXX_VBAT_LOW_MONITORING_DIS 0x0
+#define PM2XXX_VBAT_LOW_MONITORING_ENA 0x1
+
+/* Battery low level value control register */
+#define PM2XXX_VBAT_LOW_LEVEL_2_3      0x0
+#define PM2XXX_VBAT_LOW_LEVEL_2_4      0x1
+#define PM2XXX_VBAT_LOW_LEVEL_2_5      0x2
+#define PM2XXX_VBAT_LOW_LEVEL_2_6      0x3
+#define PM2XXX_VBAT_LOW_LEVEL_2_7      0x4
+#define PM2XXX_VBAT_LOW_LEVEL_2_8      0x5
+#define PM2XXX_VBAT_LOW_LEVEL_2_9      0x6
+#define PM2XXX_VBAT_LOW_LEVEL_3_0      0x7
+#define PM2XXX_VBAT_LOW_LEVEL_3_1      0x8
+#define PM2XXX_VBAT_LOW_LEVEL_3_2      0x9
+#define PM2XXX_VBAT_LOW_LEVEL_3_3      0xA
+#define PM2XXX_VBAT_LOW_LEVEL_3_4      0xB
+#define PM2XXX_VBAT_LOW_LEVEL_3_5      0xC
+#define PM2XXX_VBAT_LOW_LEVEL_3_6      0xD
+#define PM2XXX_VBAT_LOW_LEVEL_3_7      0xE
+#define PM2XXX_VBAT_LOW_LEVEL_3_8      0xF
+#define PM2XXX_VBAT_LOW_LEVEL_3_9      0x10
+#define PM2XXX_VBAT_LOW_LEVEL_4_0      0x11
+#define PM2XXX_VBAT_LOW_LEVEL_4_1      0x12
+#define PM2XXX_VBAT_LOW_LEVEL_4_2      0x13
+
+/* SW CTRL */
+#define PM2XXX_SWCTRL_HW               0x0
+#define PM2XXX_SWCTRL_SW               0x1
+
+
+/* LED Driver Control */
+#define PM2XXX_LED_CURRENT_MASK                0x0C
+#define PM2XXX_LED_CURRENT_2_5MA       (0X0<<2)
+#define PM2XXX_LED_CURRENT_1MA         (0X1<<2)
+#define PM2XXX_LED_CURRENT_5MA         (0X2<<2)
+#define PM2XXX_LED_CURRENT_10MA                (0X3<<2)
+
+#define PM2XXX_LED_SELECT_MASK         0x02
+#define PM2XXX_LED_SELECT_EN           (0X0<<1)
+#define PM2XXX_LED_SELECT_DIS          (0X1<<1)
+
+#define PM2XXX_ANTI_OVERSHOOT_MASK     0x01
+#define PM2XXX_ANTI_OVERSHOOT_DIS      0X0
+#define PM2XXX_ANTI_OVERSHOOT_EN       0X1
+
+enum pm2xxx_reg_int1 {
+       PM2XXX_INT1_ITVBATDISCONNECT    = 0x02,
+       PM2XXX_INT1_ITVBATLOWR          = 0x04,
+       PM2XXX_INT1_ITVBATLOWF          = 0x08,
+};
+
+enum pm2xxx_mask_reg_int1 {
+       PM2XXX_INT1_M_ITVBATDISCONNECT  = 0x02,
+       PM2XXX_INT1_M_ITVBATLOWR        = 0x04,
+       PM2XXX_INT1_M_ITVBATLOWF        = 0x08,
+};
+
+enum pm2xxx_source_reg_int1 {
+       PM2XXX_INT1_S_ITVBATDISCONNECT  = 0x02,
+       PM2XXX_INT1_S_ITVBATLOWR        = 0x04,
+       PM2XXX_INT1_S_ITVBATLOWF        = 0x08,
+};
+
+enum pm2xxx_reg_int2 {
+       PM2XXX_INT2_ITVPWR2PLUG         = 0x01,
+       PM2XXX_INT2_ITVPWR2UNPLUG       = 0x02,
+       PM2XXX_INT2_ITVPWR1PLUG         = 0x04,
+       PM2XXX_INT2_ITVPWR1UNPLUG       = 0x08,
+};
+
+enum pm2xxx_mask_reg_int2 {
+       PM2XXX_INT2_M_ITVPWR2PLUG       = 0x01,
+       PM2XXX_INT2_M_ITVPWR2UNPLUG     = 0x02,
+       PM2XXX_INT2_M_ITVPWR1PLUG       = 0x04,
+       PM2XXX_INT2_M_ITVPWR1UNPLUG     = 0x08,
+};
+
+enum pm2xxx_source_reg_int2 {
+       PM2XXX_INT2_S_ITVPWR2PLUG       = 0x03,
+       PM2XXX_INT2_S_ITVPWR1PLUG       = 0x0c,
+};
+
+enum pm2xxx_reg_int3 {
+       PM2XXX_INT3_ITCHPRECHARGEWD     = 0x01,
+       PM2XXX_INT3_ITCHCCWD            = 0x02,
+       PM2XXX_INT3_ITCHCVWD            = 0x04,
+       PM2XXX_INT3_ITAUTOTIMEOUTWD     = 0x08,
+};
+
+enum pm2xxx_mask_reg_int3 {
+       PM2XXX_INT3_M_ITCHPRECHARGEWD   = 0x01,
+       PM2XXX_INT3_M_ITCHCCWD          = 0x02,
+       PM2XXX_INT3_M_ITCHCVWD          = 0x04,
+       PM2XXX_INT3_M_ITAUTOTIMEOUTWD   = 0x08,
+};
+
+enum pm2xxx_source_reg_int3 {
+       PM2XXX_INT3_S_ITCHPRECHARGEWD   = 0x01,
+       PM2XXX_INT3_S_ITCHCCWD          = 0x02,
+       PM2XXX_INT3_S_ITCHCVWD          = 0x04,
+       PM2XXX_INT3_S_ITAUTOTIMEOUTWD   = 0x08,
+};
+
+enum pm2xxx_reg_int4 {
+       PM2XXX_INT4_ITBATTEMPCOLD       = 0x01,
+       PM2XXX_INT4_ITBATTEMPHOT        = 0x02,
+       PM2XXX_INT4_ITVPWR2OVV          = 0x04,
+       PM2XXX_INT4_ITVPWR1OVV          = 0x08,
+       PM2XXX_INT4_ITCHARGINGON        = 0x10,
+       PM2XXX_INT4_ITVRESUME           = 0x20,
+       PM2XXX_INT4_ITBATTFULL          = 0x40,
+       PM2XXX_INT4_ITCVPHASE           = 0x80,
+};
+
+enum pm2xxx_mask_reg_int4 {
+       PM2XXX_INT4_M_ITBATTEMPCOLD     = 0x01,
+       PM2XXX_INT4_M_ITBATTEMPHOT      = 0x02,
+       PM2XXX_INT4_M_ITVPWR2OVV        = 0x04,
+       PM2XXX_INT4_M_ITVPWR1OVV        = 0x08,
+       PM2XXX_INT4_M_ITCHARGINGON      = 0x10,
+       PM2XXX_INT4_M_ITVRESUME         = 0x20,
+       PM2XXX_INT4_M_ITBATTFULL        = 0x40,
+       PM2XXX_INT4_M_ITCVPHASE         = 0x80,
+};
+
+enum pm2xxx_source_reg_int4 {
+       PM2XXX_INT4_S_ITBATTEMPCOLD     = 0x01,
+       PM2XXX_INT4_S_ITBATTEMPHOT      = 0x02,
+       PM2XXX_INT4_S_ITVPWR2OVV        = 0x04,
+       PM2XXX_INT4_S_ITVPWR1OVV        = 0x08,
+       PM2XXX_INT4_S_ITCHARGINGON      = 0x10,
+       PM2XXX_INT4_S_ITVRESUME         = 0x20,
+       PM2XXX_INT4_S_ITBATTFULL        = 0x40,
+       PM2XXX_INT4_S_ITCVPHASE         = 0x80,
+};
+
+enum pm2xxx_reg_int5 {
+       PM2XXX_INT5_ITTHERMALSHUTDOWNRISE       = 0x01,
+       PM2XXX_INT5_ITTHERMALSHUTDOWNFALL       = 0x02,
+       PM2XXX_INT5_ITTHERMALWARNINGRISE        = 0x04,
+       PM2XXX_INT5_ITTHERMALWARNINGFALL        = 0x08,
+       PM2XXX_INT5_ITVSYSTEMOVV                = 0x10,
+};
+
+enum pm2xxx_mask_reg_int5 {
+       PM2XXX_INT5_M_ITTHERMALSHUTDOWNRISE     = 0x01,
+       PM2XXX_INT5_M_ITTHERMALSHUTDOWNFALL     = 0x02,
+       PM2XXX_INT5_M_ITTHERMALWARNINGRISE      = 0x04,
+       PM2XXX_INT5_M_ITTHERMALWARNINGFALL      = 0x08,
+       PM2XXX_INT5_M_ITVSYSTEMOVV              = 0x10,
+};
+
+enum pm2xxx_source_reg_int5 {
+       PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE     = 0x01,
+       PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL     = 0x02,
+       PM2XXX_INT5_S_ITTHERMALWARNINGRISE      = 0x04,
+       PM2XXX_INT5_S_ITTHERMALWARNINGFALL      = 0x08,
+       PM2XXX_INT5_S_ITVSYSTEMOVV              = 0x10,
+};
+
+enum pm2xxx_reg_int6 {
+       PM2XXX_INT6_ITVPWR2DROP         = 0x01,
+       PM2XXX_INT6_ITVPWR1DROP         = 0x02,
+       PM2XXX_INT6_ITVPWR2VALIDRISE    = 0x04,
+       PM2XXX_INT6_ITVPWR2VALIDFALL    = 0x08,
+       PM2XXX_INT6_ITVPWR1VALIDRISE    = 0x10,
+       PM2XXX_INT6_ITVPWR1VALIDFALL    = 0x20,
+};
+
+enum pm2xxx_mask_reg_int6 {
+       PM2XXX_INT6_M_ITVPWR2DROP       = 0x01,
+       PM2XXX_INT6_M_ITVPWR1DROP       = 0x02,
+       PM2XXX_INT6_M_ITVPWR2VALIDRISE  = 0x04,
+       PM2XXX_INT6_M_ITVPWR2VALIDFALL  = 0x08,
+       PM2XXX_INT6_M_ITVPWR1VALIDRISE  = 0x10,
+       PM2XXX_INT6_M_ITVPWR1VALIDFALL  = 0x20,
+};
+
+enum pm2xxx_source_reg_int6 {
+       PM2XXX_INT6_S_ITVPWR2DROP       = 0x01,
+       PM2XXX_INT6_S_ITVPWR1DROP       = 0x02,
+       PM2XXX_INT6_S_ITVPWR2VALIDRISE  = 0x04,
+       PM2XXX_INT6_S_ITVPWR2VALIDFALL  = 0x08,
+       PM2XXX_INT6_S_ITVPWR1VALIDRISE  = 0x10,
+       PM2XXX_INT6_S_ITVPWR1VALIDFALL  = 0x20,
+};
+
+struct pm2xxx_charger_info {
+       int charger_connected;
+       int charger_online;
+       int cv_active;
+       bool wd_expired;
+};
+
+struct pm2xxx_charger_event_flags {
+       bool mainextchnotok;
+       bool main_thermal_prot;
+       bool ovv;
+       bool chgwdexp;
+};
+
+struct pm2xxx_interrupts {
+       u8 reg[PM2XXX_NUM_INT_REG];
+       int (*handler[PM2XXX_NUM_INT_REG])(void *, int);
+};
+
+struct pm2xxx_config {
+       struct i2c_client *pm2xxx_i2c;
+       struct i2c_device_id *pm2xxx_id;
+};
+
+struct pm2xxx_irq {
+       char *name;
+       irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct pm2xxx_charger {
+       struct device *dev;
+       u8 chip_id;
+       bool vddadc_en_ac;
+       struct pm2xxx_config config;
+       bool ac_conn;
+       unsigned int gpio_irq;
+       int vbat;
+       int old_vbat;
+       int failure_case;
+       int failure_input_ovv;
+       unsigned int lpn_pin;
+       struct pm2xxx_interrupts *pm2_int;
+       struct regulator *regu;
+       struct pm2xxx_bm_data *bat;
+       struct mutex lock;
+       struct ab8500 *parent;
+       struct pm2xxx_charger_info ac;
+       struct pm2xxx_charger_platform_data *pdata;
+       struct workqueue_struct *charger_wq;
+       struct delayed_work check_vbat_work;
+       struct work_struct ac_work;
+       struct work_struct check_main_thermal_prot_work;
+       struct delayed_work check_hw_failure_work;
+       struct ux500_charger ac_chg;
+       struct power_supply_desc ac_chg_desc;
+       struct pm2xxx_charger_event_flags flags;
+};
+
+#endif /* PM2301_CHARGER_H */
diff --git a/drivers/power/supply/pmu_battery.c b/drivers/power/supply/pmu_battery.c
new file mode 100644 (file)
index 0000000..9c8d525
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Battery class driver for Apple PMU
+ *
+ *     Copyright Â© 2006  David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/slab.h>
+
+static struct pmu_battery_dev {
+       struct power_supply *bat;
+       struct power_supply_desc bat_desc;
+       struct pmu_battery_info *pbi;
+       char name[16];
+       int propval;
+} *pbats[PMU_MAX_BATTERIES];
+
+#define to_pmu_battery_dev(x) power_supply_get_drvdata(x)
+
+/*********************************************************************
+ *             Power
+ *********************************************************************/
+
+static int pmu_get_ac_prop(struct power_supply *psy,
+                          enum power_supply_property psp,
+                          union power_supply_propval *val)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) ||
+                             (pmu_battery_count == 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property pmu_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc pmu_ac_desc = {
+       .name = "pmu-ac",
+       .type = POWER_SUPPLY_TYPE_MAINS,
+       .properties = pmu_ac_props,
+       .num_properties = ARRAY_SIZE(pmu_ac_props),
+       .get_property = pmu_get_ac_prop,
+};
+
+static struct power_supply *pmu_ac;
+
+/*********************************************************************
+ *             Battery properties
+ *********************************************************************/
+
+static char *pmu_batt_types[] = {
+       "Smart", "Comet", "Hooper", "Unknown"
+};
+
+static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi)
+{
+       switch (pbi->flags & PMU_BATT_TYPE_MASK) {
+       case PMU_BATT_TYPE_SMART:
+               return pmu_batt_types[0];
+       case PMU_BATT_TYPE_COMET:
+               return pmu_batt_types[1];
+       case PMU_BATT_TYPE_HOOPER:
+               return pmu_batt_types[2];
+       default: break;
+       }
+       return pmu_batt_types[3];
+}
+
+static int pmu_bat_get_property(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy);
+       struct pmu_battery_info *pbi = pbat->pbi;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (pbi->flags & PMU_BATT_CHARGING)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (pmu_power_flags & PMU_PWR_AC_PRESENT)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = !!(pbi->flags & PMU_BATT_PRESENT);
+               break;
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = pmu_bat_get_model_name(pbi);
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_AVG:
+               val->intval = pbi->charge     * 1000; /* mWh -> ÂµWh */
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_FULL:
+               val->intval = pbi->max_charge * 1000; /* mWh -> ÂµWh */
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               val->intval = pbi->amperage   * 1000; /* mA -> ÂµA */
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               val->intval = pbi->voltage    * 1000; /* mV -> ÂµV */
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+               val->intval = pbi->time_remaining;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property pmu_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_ENERGY_AVG,
+       POWER_SUPPLY_PROP_ENERGY_FULL,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+};
+
+/*********************************************************************
+ *             Initialisation
+ *********************************************************************/
+
+static struct platform_device *bat_pdev;
+
+static int __init pmu_bat_init(void)
+{
+       int ret = 0;
+       int i;
+
+       bat_pdev = platform_device_register_simple("pmu-battery",
+                                                  0, NULL, 0);
+       if (IS_ERR(bat_pdev)) {
+               ret = PTR_ERR(bat_pdev);
+               goto pdev_register_failed;
+       }
+
+       pmu_ac = power_supply_register(&bat_pdev->dev, &pmu_ac_desc, NULL);
+       if (IS_ERR(pmu_ac)) {
+               ret = PTR_ERR(pmu_ac);
+               goto ac_register_failed;
+       }
+
+       for (i = 0; i < pmu_battery_count; i++) {
+               struct power_supply_config psy_cfg = {};
+               struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat),
+                                                      GFP_KERNEL);
+               if (!pbat)
+                       break;
+
+               sprintf(pbat->name, "PMU_battery_%d", i);
+               pbat->bat_desc.name = pbat->name;
+               pbat->bat_desc.properties = pmu_bat_props;
+               pbat->bat_desc.num_properties = ARRAY_SIZE(pmu_bat_props);
+               pbat->bat_desc.get_property = pmu_bat_get_property;
+               pbat->pbi = &pmu_batteries[i];
+               psy_cfg.drv_data = pbat;
+
+               pbat->bat = power_supply_register(&bat_pdev->dev,
+                                                 &pbat->bat_desc,
+                                                 &psy_cfg);
+               if (IS_ERR(pbat->bat)) {
+                       ret = PTR_ERR(pbat->bat);
+                       kfree(pbat);
+                       goto battery_register_failed;
+               }
+               pbats[i] = pbat;
+       }
+
+       goto success;
+
+battery_register_failed:
+       while (i--) {
+               if (!pbats[i])
+                       continue;
+               power_supply_unregister(pbats[i]->bat);
+               kfree(pbats[i]);
+       }
+       power_supply_unregister(pmu_ac);
+ac_register_failed:
+       platform_device_unregister(bat_pdev);
+pdev_register_failed:
+success:
+       return ret;
+}
+
+static void __exit pmu_bat_exit(void)
+{
+       int i;
+
+       for (i = 0; i < PMU_MAX_BATTERIES; i++) {
+               if (!pbats[i])
+                       continue;
+               power_supply_unregister(pbats[i]->bat);
+               kfree(pbats[i]);
+       }
+       power_supply_unregister(pmu_ac);
+       platform_device_unregister(bat_pdev);
+}
+
+module_init(pmu_bat_init);
+module_exit(pmu_bat_exit);
+
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PMU battery driver");
diff --git a/drivers/power/supply/power_supply.h b/drivers/power/supply/power_supply.h
new file mode 100644 (file)
index 0000000..cc439fd
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Functions private to power supply class
+ *
+ *  Copyright Â© 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright Â© 2004  Szabolcs Gyurko
+ *  Copyright Â© 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+struct device;
+struct device_type;
+struct power_supply;
+
+#ifdef CONFIG_SYSFS
+
+extern void power_supply_init_attrs(struct device_type *dev_type);
+extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env);
+
+#else
+
+static inline void power_supply_init_attrs(struct device_type *dev_type) {}
+#define power_supply_uevent NULL
+
+#endif /* CONFIG_SYSFS */
+
+#ifdef CONFIG_LEDS_TRIGGERS
+
+extern void power_supply_update_leds(struct power_supply *psy);
+extern int power_supply_create_triggers(struct power_supply *psy);
+extern void power_supply_remove_triggers(struct power_supply *psy);
+
+#else
+
+static inline void power_supply_update_leds(struct power_supply *psy) {}
+static inline int power_supply_create_triggers(struct power_supply *psy)
+{ return 0; }
+static inline void power_supply_remove_triggers(struct power_supply *psy) {}
+
+#endif /* CONFIG_LEDS_TRIGGERS */
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
new file mode 100644 (file)
index 0000000..a74d8ca
--- /dev/null
@@ -0,0 +1,989 @@
+/*
+ *  Universal power supply monitor class
+ *
+ *  Copyright Â© 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright Â© 2004  Szabolcs Gyurko
+ *  Copyright Â© 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/thermal.h>
+#include "power_supply.h"
+
+/* exported for the APM Power driver, APM emulation */
+struct class *power_supply_class;
+EXPORT_SYMBOL_GPL(power_supply_class);
+
+ATOMIC_NOTIFIER_HEAD(power_supply_notifier);
+EXPORT_SYMBOL_GPL(power_supply_notifier);
+
+static struct device_type power_supply_dev_type;
+
+#define POWER_SUPPLY_DEFERRED_REGISTER_TIME    msecs_to_jiffies(10)
+
+static bool __power_supply_is_supplied_by(struct power_supply *supplier,
+                                        struct power_supply *supply)
+{
+       int i;
+
+       if (!supply->supplied_from && !supplier->supplied_to)
+               return false;
+
+       /* Support both supplied_to and supplied_from modes */
+       if (supply->supplied_from) {
+               if (!supplier->desc->name)
+                       return false;
+               for (i = 0; i < supply->num_supplies; i++)
+                       if (!strcmp(supplier->desc->name, supply->supplied_from[i]))
+                               return true;
+       } else {
+               if (!supply->desc->name)
+                       return false;
+               for (i = 0; i < supplier->num_supplicants; i++)
+                       if (!strcmp(supplier->supplied_to[i], supply->desc->name))
+                               return true;
+       }
+
+       return false;
+}
+
+static int __power_supply_changed_work(struct device *dev, void *data)
+{
+       struct power_supply *psy = data;
+       struct power_supply *pst = dev_get_drvdata(dev);
+
+       if (__power_supply_is_supplied_by(psy, pst)) {
+               if (pst->desc->external_power_changed)
+                       pst->desc->external_power_changed(pst);
+       }
+
+       return 0;
+}
+
+static void power_supply_changed_work(struct work_struct *work)
+{
+       unsigned long flags;
+       struct power_supply *psy = container_of(work, struct power_supply,
+                                               changed_work);
+
+       dev_dbg(&psy->dev, "%s\n", __func__);
+
+       spin_lock_irqsave(&psy->changed_lock, flags);
+       /*
+        * Check 'changed' here to avoid issues due to race between
+        * power_supply_changed() and this routine. In worst case
+        * power_supply_changed() can be called again just before we take above
+        * lock. During the first call of this routine we will mark 'changed' as
+        * false and it will stay false for the next call as well.
+        */
+       if (likely(psy->changed)) {
+               psy->changed = false;
+               spin_unlock_irqrestore(&psy->changed_lock, flags);
+               class_for_each_device(power_supply_class, NULL, psy,
+                                     __power_supply_changed_work);
+               power_supply_update_leds(psy);
+               atomic_notifier_call_chain(&power_supply_notifier,
+                               PSY_EVENT_PROP_CHANGED, psy);
+               kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE);
+               spin_lock_irqsave(&psy->changed_lock, flags);
+       }
+
+       /*
+        * Hold the wakeup_source until all events are processed.
+        * power_supply_changed() might have called again and have set 'changed'
+        * to true.
+        */
+       if (likely(!psy->changed))
+               pm_relax(&psy->dev);
+       spin_unlock_irqrestore(&psy->changed_lock, flags);
+}
+
+void power_supply_changed(struct power_supply *psy)
+{
+       unsigned long flags;
+
+       dev_dbg(&psy->dev, "%s\n", __func__);
+
+       spin_lock_irqsave(&psy->changed_lock, flags);
+       psy->changed = true;
+       pm_stay_awake(&psy->dev);
+       spin_unlock_irqrestore(&psy->changed_lock, flags);
+       schedule_work(&psy->changed_work);
+}
+EXPORT_SYMBOL_GPL(power_supply_changed);
+
+/*
+ * Notify that power supply was registered after parent finished the probing.
+ *
+ * Often power supply is registered from driver's probe function. However
+ * calling power_supply_changed() directly from power_supply_register()
+ * would lead to execution of get_property() function provided by the driver
+ * too early - before the probe ends.
+ *
+ * Avoid that by waiting on parent's mutex.
+ */
+static void power_supply_deferred_register_work(struct work_struct *work)
+{
+       struct power_supply *psy = container_of(work, struct power_supply,
+                                               deferred_register_work.work);
+
+       if (psy->dev.parent)
+               mutex_lock(&psy->dev.parent->mutex);
+
+       power_supply_changed(psy);
+
+       if (psy->dev.parent)
+               mutex_unlock(&psy->dev.parent->mutex);
+}
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+
+static int __power_supply_populate_supplied_from(struct device *dev,
+                                                void *data)
+{
+       struct power_supply *psy = data;
+       struct power_supply *epsy = dev_get_drvdata(dev);
+       struct device_node *np;
+       int i = 0;
+
+       do {
+               np = of_parse_phandle(psy->of_node, "power-supplies", i++);
+               if (!np)
+                       break;
+
+               if (np == epsy->of_node) {
+                       dev_info(&psy->dev, "%s: Found supply : %s\n",
+                               psy->desc->name, epsy->desc->name);
+                       psy->supplied_from[i-1] = (char *)epsy->desc->name;
+                       psy->num_supplies++;
+                       of_node_put(np);
+                       break;
+               }
+               of_node_put(np);
+       } while (np);
+
+       return 0;
+}
+
+static int power_supply_populate_supplied_from(struct power_supply *psy)
+{
+       int error;
+
+       error = class_for_each_device(power_supply_class, NULL, psy,
+                                     __power_supply_populate_supplied_from);
+
+       dev_dbg(&psy->dev, "%s %d\n", __func__, error);
+
+       return error;
+}
+
+static int  __power_supply_find_supply_from_node(struct device *dev,
+                                                void *data)
+{
+       struct device_node *np = data;
+       struct power_supply *epsy = dev_get_drvdata(dev);
+
+       /* returning non-zero breaks out of class_for_each_device loop */
+       if (epsy->of_node == np)
+               return 1;
+
+       return 0;
+}
+
+static int power_supply_find_supply_from_node(struct device_node *supply_node)
+{
+       int error;
+
+       /*
+        * class_for_each_device() either returns its own errors or values
+        * returned by __power_supply_find_supply_from_node().
+        *
+        * __power_supply_find_supply_from_node() will return 0 (no match)
+        * or 1 (match).
+        *
+        * We return 0 if class_for_each_device() returned 1, -EPROBE_DEFER if
+        * it returned 0, or error as returned by it.
+        */
+       error = class_for_each_device(power_supply_class, NULL, supply_node,
+                                      __power_supply_find_supply_from_node);
+
+       return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER;
+}
+
+static int power_supply_check_supplies(struct power_supply *psy)
+{
+       struct device_node *np;
+       int cnt = 0;
+
+       /* If there is already a list honor it */
+       if (psy->supplied_from && psy->num_supplies > 0)
+               return 0;
+
+       /* No device node found, nothing to do */
+       if (!psy->of_node)
+               return 0;
+
+       do {
+               int ret;
+
+               np = of_parse_phandle(psy->of_node, "power-supplies", cnt++);
+               if (!np)
+                       break;
+
+               ret = power_supply_find_supply_from_node(np);
+               of_node_put(np);
+
+               if (ret) {
+                       dev_dbg(&psy->dev, "Failed to find supply!\n");
+                       return ret;
+               }
+       } while (np);
+
+       /* Missing valid "power-supplies" entries */
+       if (cnt == 1)
+               return 0;
+
+       /* All supplies found, allocate char ** array for filling */
+       psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from),
+                                         GFP_KERNEL);
+       if (!psy->supplied_from) {
+               dev_err(&psy->dev, "Couldn't allocate memory for supply list\n");
+               return -ENOMEM;
+       }
+
+       *psy->supplied_from = devm_kzalloc(&psy->dev,
+                                          sizeof(char *) * (cnt - 1),
+                                          GFP_KERNEL);
+       if (!*psy->supplied_from) {
+               dev_err(&psy->dev, "Couldn't allocate memory for supply list\n");
+               return -ENOMEM;
+       }
+
+       return power_supply_populate_supplied_from(psy);
+}
+#else
+static inline int power_supply_check_supplies(struct power_supply *psy)
+{
+       return 0;
+}
+#endif
+
+static int __power_supply_am_i_supplied(struct device *dev, void *data)
+{
+       union power_supply_propval ret = {0,};
+       struct power_supply *psy = data;
+       struct power_supply *epsy = dev_get_drvdata(dev);
+
+       if (__power_supply_is_supplied_by(epsy, psy))
+               if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE,
+                                       &ret))
+                       return ret.intval;
+
+       return 0;
+}
+
+int power_supply_am_i_supplied(struct power_supply *psy)
+{
+       int error;
+
+       error = class_for_each_device(power_supply_class, NULL, psy,
+                                     __power_supply_am_i_supplied);
+
+       dev_dbg(&psy->dev, "%s %d\n", __func__, error);
+
+       return error;
+}
+EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
+
+static int __power_supply_is_system_supplied(struct device *dev, void *data)
+{
+       union power_supply_propval ret = {0,};
+       struct power_supply *psy = dev_get_drvdata(dev);
+       unsigned int *count = data;
+
+       (*count)++;
+       if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY)
+               if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE,
+                                       &ret))
+                       return ret.intval;
+
+       return 0;
+}
+
+int power_supply_is_system_supplied(void)
+{
+       int error;
+       unsigned int count = 0;
+
+       error = class_for_each_device(power_supply_class, NULL, &count,
+                                     __power_supply_is_system_supplied);
+
+       /*
+        * If no power class device was found at all, most probably we are
+        * running on a desktop system, so assume we are on mains power.
+        */
+       if (count == 0)
+               return 1;
+
+       return error;
+}
+EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
+
+int power_supply_set_battery_charged(struct power_supply *psy)
+{
+       if (atomic_read(&psy->use_cnt) >= 0 &&
+                       psy->desc->type == POWER_SUPPLY_TYPE_BATTERY &&
+                       psy->desc->set_charged) {
+               psy->desc->set_charged(psy);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(power_supply_set_battery_charged);
+
+static int power_supply_match_device_by_name(struct device *dev, const void *data)
+{
+       const char *name = data;
+       struct power_supply *psy = dev_get_drvdata(dev);
+
+       return strcmp(psy->desc->name, name) == 0;
+}
+
+/**
+ * power_supply_get_by_name() - Search for a power supply and returns its ref
+ * @name: Power supply name to fetch
+ *
+ * If power supply was found, it increases reference count for the
+ * internal power supply's device. The user should power_supply_put()
+ * after usage.
+ *
+ * Return: On success returns a reference to a power supply with
+ * matching name equals to @name, a NULL otherwise.
+ */
+struct power_supply *power_supply_get_by_name(const char *name)
+{
+       struct power_supply *psy = NULL;
+       struct device *dev = class_find_device(power_supply_class, NULL, name,
+                                       power_supply_match_device_by_name);
+
+       if (dev) {
+               psy = dev_get_drvdata(dev);
+               atomic_inc(&psy->use_cnt);
+       }
+
+       return psy;
+}
+EXPORT_SYMBOL_GPL(power_supply_get_by_name);
+
+/**
+ * power_supply_put() - Drop reference obtained with power_supply_get_by_name
+ * @psy: Reference to put
+ *
+ * The reference to power supply should be put before unregistering
+ * the power supply.
+ */
+void power_supply_put(struct power_supply *psy)
+{
+       might_sleep();
+
+       atomic_dec(&psy->use_cnt);
+       put_device(&psy->dev);
+}
+EXPORT_SYMBOL_GPL(power_supply_put);
+
+#ifdef CONFIG_OF
+static int power_supply_match_device_node(struct device *dev, const void *data)
+{
+       return dev->parent && dev->parent->of_node == data;
+}
+
+/**
+ * power_supply_get_by_phandle() - Search for a power supply and returns its ref
+ * @np: Pointer to device node holding phandle property
+ * @phandle_name: Name of property holding a power supply name
+ *
+ * If power supply was found, it increases reference count for the
+ * internal power supply's device. The user should power_supply_put()
+ * after usage.
+ *
+ * Return: On success returns a reference to a power supply with
+ * matching name equals to value under @property, NULL or ERR_PTR otherwise.
+ */
+struct power_supply *power_supply_get_by_phandle(struct device_node *np,
+                                                       const char *property)
+{
+       struct device_node *power_supply_np;
+       struct power_supply *psy = NULL;
+       struct device *dev;
+
+       power_supply_np = of_parse_phandle(np, property, 0);
+       if (!power_supply_np)
+               return ERR_PTR(-ENODEV);
+
+       dev = class_find_device(power_supply_class, NULL, power_supply_np,
+                                               power_supply_match_device_node);
+
+       of_node_put(power_supply_np);
+
+       if (dev) {
+               psy = dev_get_drvdata(dev);
+               atomic_inc(&psy->use_cnt);
+       }
+
+       return psy;
+}
+EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
+
+static void devm_power_supply_put(struct device *dev, void *res)
+{
+       struct power_supply **psy = res;
+
+       power_supply_put(*psy);
+}
+
+/**
+ * devm_power_supply_get_by_phandle() - Resource managed version of
+ *  power_supply_get_by_phandle()
+ * @dev: Pointer to device holding phandle property
+ * @phandle_name: Name of property holding a power supply phandle
+ *
+ * Return: On success returns a reference to a power supply with
+ * matching name equals to value under @property, NULL or ERR_PTR otherwise.
+ */
+struct power_supply *devm_power_supply_get_by_phandle(struct device *dev,
+                                                     const char *property)
+{
+       struct power_supply **ptr, *psy;
+
+       if (!dev->of_node)
+               return ERR_PTR(-ENODEV);
+
+       ptr = devres_alloc(devm_power_supply_put, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       psy = power_supply_get_by_phandle(dev->of_node, property);
+       if (IS_ERR_OR_NULL(psy)) {
+               devres_free(ptr);
+       } else {
+               *ptr = psy;
+               devres_add(dev, ptr);
+       }
+       return psy;
+}
+EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
+#endif /* CONFIG_OF */
+
+int power_supply_get_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       if (atomic_read(&psy->use_cnt) <= 0) {
+               if (!psy->initialized)
+                       return -EAGAIN;
+               return -ENODEV;
+       }
+
+       return psy->desc->get_property(psy, psp, val);
+}
+EXPORT_SYMBOL_GPL(power_supply_get_property);
+
+int power_supply_set_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           const union power_supply_propval *val)
+{
+       if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property)
+               return -ENODEV;
+
+       return psy->desc->set_property(psy, psp, val);
+}
+EXPORT_SYMBOL_GPL(power_supply_set_property);
+
+int power_supply_property_is_writeable(struct power_supply *psy,
+                                       enum power_supply_property psp)
+{
+       if (atomic_read(&psy->use_cnt) <= 0 ||
+                       !psy->desc->property_is_writeable)
+               return -ENODEV;
+
+       return psy->desc->property_is_writeable(psy, psp);
+}
+EXPORT_SYMBOL_GPL(power_supply_property_is_writeable);
+
+void power_supply_external_power_changed(struct power_supply *psy)
+{
+       if (atomic_read(&psy->use_cnt) <= 0 ||
+                       !psy->desc->external_power_changed)
+               return;
+
+       psy->desc->external_power_changed(psy);
+}
+EXPORT_SYMBOL_GPL(power_supply_external_power_changed);
+
+int power_supply_powers(struct power_supply *psy, struct device *dev)
+{
+       return sysfs_create_link(&psy->dev.kobj, &dev->kobj, "powers");
+}
+EXPORT_SYMBOL_GPL(power_supply_powers);
+
+static void power_supply_dev_release(struct device *dev)
+{
+       struct power_supply *psy = container_of(dev, struct power_supply, dev);
+       pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
+       kfree(psy);
+}
+
+int power_supply_reg_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&power_supply_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(power_supply_reg_notifier);
+
+void power_supply_unreg_notifier(struct notifier_block *nb)
+{
+       atomic_notifier_chain_unregister(&power_supply_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(power_supply_unreg_notifier);
+
+#ifdef CONFIG_THERMAL
+static int power_supply_read_temp(struct thermal_zone_device *tzd,
+               int *temp)
+{
+       struct power_supply *psy;
+       union power_supply_propval val;
+       int ret;
+
+       WARN_ON(tzd == NULL);
+       psy = tzd->devdata;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &val);
+       if (ret)
+               return ret;
+
+       /* Convert tenths of degree Celsius to milli degree Celsius. */
+       *temp = val.intval * 100;
+
+       return ret;
+}
+
+static struct thermal_zone_device_ops psy_tzd_ops = {
+       .get_temp = power_supply_read_temp,
+};
+
+static int psy_register_thermal(struct power_supply *psy)
+{
+       int i;
+
+       if (psy->desc->no_thermal)
+               return 0;
+
+       /* Register battery zone device psy reports temperature */
+       for (i = 0; i < psy->desc->num_properties; i++) {
+               if (psy->desc->properties[i] == POWER_SUPPLY_PROP_TEMP) {
+                       psy->tzd = thermal_zone_device_register(psy->desc->name,
+                                       0, 0, psy, &psy_tzd_ops, NULL, 0, 0);
+                       return PTR_ERR_OR_ZERO(psy->tzd);
+               }
+       }
+       return 0;
+}
+
+static void psy_unregister_thermal(struct power_supply *psy)
+{
+       if (IS_ERR_OR_NULL(psy->tzd))
+               return;
+       thermal_zone_device_unregister(psy->tzd);
+}
+
+/* thermal cooling device callbacks */
+static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd,
+                                       unsigned long *state)
+{
+       struct power_supply *psy;
+       union power_supply_propval val;
+       int ret;
+
+       psy = tcd->devdata;
+       ret = power_supply_get_property(psy,
+                       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
+       if (ret)
+               return ret;
+
+       *state = val.intval;
+
+       return ret;
+}
+
+static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd,
+                                       unsigned long *state)
+{
+       struct power_supply *psy;
+       union power_supply_propval val;
+       int ret;
+
+       psy = tcd->devdata;
+       ret = power_supply_get_property(psy,
+                       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
+       if (ret)
+               return ret;
+
+       *state = val.intval;
+
+       return ret;
+}
+
+static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
+                                       unsigned long state)
+{
+       struct power_supply *psy;
+       union power_supply_propval val;
+       int ret;
+
+       psy = tcd->devdata;
+       val.intval = state;
+       ret = psy->desc->set_property(psy,
+               POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
+
+       return ret;
+}
+
+static struct thermal_cooling_device_ops psy_tcd_ops = {
+       .get_max_state = ps_get_max_charge_cntl_limit,
+       .get_cur_state = ps_get_cur_chrage_cntl_limit,
+       .set_cur_state = ps_set_cur_charge_cntl_limit,
+};
+
+static int psy_register_cooler(struct power_supply *psy)
+{
+       int i;
+
+       /* Register for cooling device if psy can control charging */
+       for (i = 0; i < psy->desc->num_properties; i++) {
+               if (psy->desc->properties[i] ==
+                               POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) {
+                       psy->tcd = thermal_cooling_device_register(
+                                                       (char *)psy->desc->name,
+                                                       psy, &psy_tcd_ops);
+                       return PTR_ERR_OR_ZERO(psy->tcd);
+               }
+       }
+       return 0;
+}
+
+static void psy_unregister_cooler(struct power_supply *psy)
+{
+       if (IS_ERR_OR_NULL(psy->tcd))
+               return;
+       thermal_cooling_device_unregister(psy->tcd);
+}
+#else
+static int psy_register_thermal(struct power_supply *psy)
+{
+       return 0;
+}
+
+static void psy_unregister_thermal(struct power_supply *psy)
+{
+}
+
+static int psy_register_cooler(struct power_supply *psy)
+{
+       return 0;
+}
+
+static void psy_unregister_cooler(struct power_supply *psy)
+{
+}
+#endif
+
+static struct power_supply *__must_check
+__power_supply_register(struct device *parent,
+                                  const struct power_supply_desc *desc,
+                                  const struct power_supply_config *cfg,
+                                  bool ws)
+{
+       struct device *dev;
+       struct power_supply *psy;
+       int rc;
+
+       if (!parent)
+               pr_warn("%s: Expected proper parent device for '%s'\n",
+                       __func__, desc->name);
+
+       psy = kzalloc(sizeof(*psy), GFP_KERNEL);
+       if (!psy)
+               return ERR_PTR(-ENOMEM);
+
+       dev = &psy->dev;
+
+       device_initialize(dev);
+
+       dev->class = power_supply_class;
+       dev->type = &power_supply_dev_type;
+       dev->parent = parent;
+       dev->release = power_supply_dev_release;
+       dev_set_drvdata(dev, psy);
+       psy->desc = desc;
+       if (cfg) {
+               psy->drv_data = cfg->drv_data;
+               psy->of_node = cfg->of_node;
+               psy->supplied_to = cfg->supplied_to;
+               psy->num_supplicants = cfg->num_supplicants;
+       }
+
+       rc = dev_set_name(dev, "%s", desc->name);
+       if (rc)
+               goto dev_set_name_failed;
+
+       INIT_WORK(&psy->changed_work, power_supply_changed_work);
+       INIT_DELAYED_WORK(&psy->deferred_register_work,
+                         power_supply_deferred_register_work);
+
+       rc = power_supply_check_supplies(psy);
+       if (rc) {
+               dev_info(dev, "Not all required supplies found, defer probe\n");
+               goto check_supplies_failed;
+       }
+
+       spin_lock_init(&psy->changed_lock);
+       rc = device_init_wakeup(dev, ws);
+       if (rc)
+               goto wakeup_init_failed;
+
+       rc = device_add(dev);
+       if (rc)
+               goto device_add_failed;
+
+       rc = psy_register_thermal(psy);
+       if (rc)
+               goto register_thermal_failed;
+
+       rc = psy_register_cooler(psy);
+       if (rc)
+               goto register_cooler_failed;
+
+       rc = power_supply_create_triggers(psy);
+       if (rc)
+               goto create_triggers_failed;
+
+       /*
+        * Update use_cnt after any uevents (most notably from device_add()).
+        * We are here still during driver's probe but
+        * the power_supply_uevent() calls back driver's get_property
+        * method so:
+        * 1. Driver did not assigned the returned struct power_supply,
+        * 2. Driver could not finish initialization (anything in its probe
+        *    after calling power_supply_register()).
+        */
+       atomic_inc(&psy->use_cnt);
+       psy->initialized = true;
+
+       queue_delayed_work(system_power_efficient_wq,
+                          &psy->deferred_register_work,
+                          POWER_SUPPLY_DEFERRED_REGISTER_TIME);
+
+       return psy;
+
+create_triggers_failed:
+       psy_unregister_cooler(psy);
+register_cooler_failed:
+       psy_unregister_thermal(psy);
+register_thermal_failed:
+       device_del(dev);
+device_add_failed:
+wakeup_init_failed:
+check_supplies_failed:
+dev_set_name_failed:
+       put_device(dev);
+       return ERR_PTR(rc);
+}
+
+/**
+ * power_supply_register() - Register new power supply
+ * @parent:    Device to be a parent of power supply's device, usually
+ *             the device which probe function calls this
+ * @desc:      Description of power supply, must be valid through whole
+ *             lifetime of this power supply
+ * @cfg:       Run-time specific configuration accessed during registering,
+ *             may be NULL
+ *
+ * Return: A pointer to newly allocated power_supply on success
+ * or ERR_PTR otherwise.
+ * Use power_supply_unregister() on returned power_supply pointer to release
+ * resources.
+ */
+struct power_supply *__must_check power_supply_register(struct device *parent,
+               const struct power_supply_desc *desc,
+               const struct power_supply_config *cfg)
+{
+       return __power_supply_register(parent, desc, cfg, true);
+}
+EXPORT_SYMBOL_GPL(power_supply_register);
+
+/**
+ * power_supply_register_no_ws() - Register new non-waking-source power supply
+ * @parent:    Device to be a parent of power supply's device, usually
+ *             the device which probe function calls this
+ * @desc:      Description of power supply, must be valid through whole
+ *             lifetime of this power supply
+ * @cfg:       Run-time specific configuration accessed during registering,
+ *             may be NULL
+ *
+ * Return: A pointer to newly allocated power_supply on success
+ * or ERR_PTR otherwise.
+ * Use power_supply_unregister() on returned power_supply pointer to release
+ * resources.
+ */
+struct power_supply *__must_check
+power_supply_register_no_ws(struct device *parent,
+               const struct power_supply_desc *desc,
+               const struct power_supply_config *cfg)
+{
+       return __power_supply_register(parent, desc, cfg, false);
+}
+EXPORT_SYMBOL_GPL(power_supply_register_no_ws);
+
+static void devm_power_supply_release(struct device *dev, void *res)
+{
+       struct power_supply **psy = res;
+
+       power_supply_unregister(*psy);
+}
+
+/**
+ * devm_power_supply_register() - Register managed power supply
+ * @parent:    Device to be a parent of power supply's device, usually
+ *             the device which probe function calls this
+ * @desc:      Description of power supply, must be valid through whole
+ *             lifetime of this power supply
+ * @cfg:       Run-time specific configuration accessed during registering,
+ *             may be NULL
+ *
+ * Return: A pointer to newly allocated power_supply on success
+ * or ERR_PTR otherwise.
+ * The returned power_supply pointer will be automatically unregistered
+ * on driver detach.
+ */
+struct power_supply *__must_check
+devm_power_supply_register(struct device *parent,
+               const struct power_supply_desc *desc,
+               const struct power_supply_config *cfg)
+{
+       struct power_supply **ptr, *psy;
+
+       ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL);
+
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+       psy = __power_supply_register(parent, desc, cfg, true);
+       if (IS_ERR(psy)) {
+               devres_free(ptr);
+       } else {
+               *ptr = psy;
+               devres_add(parent, ptr);
+       }
+       return psy;
+}
+EXPORT_SYMBOL_GPL(devm_power_supply_register);
+
+/**
+ * devm_power_supply_register_no_ws() - Register managed non-waking-source power supply
+ * @parent:    Device to be a parent of power supply's device, usually
+ *             the device which probe function calls this
+ * @desc:      Description of power supply, must be valid through whole
+ *             lifetime of this power supply
+ * @cfg:       Run-time specific configuration accessed during registering,
+ *             may be NULL
+ *
+ * Return: A pointer to newly allocated power_supply on success
+ * or ERR_PTR otherwise.
+ * The returned power_supply pointer will be automatically unregistered
+ * on driver detach.
+ */
+struct power_supply *__must_check
+devm_power_supply_register_no_ws(struct device *parent,
+               const struct power_supply_desc *desc,
+               const struct power_supply_config *cfg)
+{
+       struct power_supply **ptr, *psy;
+
+       ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL);
+
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+       psy = __power_supply_register(parent, desc, cfg, false);
+       if (IS_ERR(psy)) {
+               devres_free(ptr);
+       } else {
+               *ptr = psy;
+               devres_add(parent, ptr);
+       }
+       return psy;
+}
+EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws);
+
+/**
+ * power_supply_unregister() - Remove this power supply from system
+ * @psy:       Pointer to power supply to unregister
+ *
+ * Remove this power supply from the system. The resources of power supply
+ * will be freed here or on last power_supply_put() call.
+ */
+void power_supply_unregister(struct power_supply *psy)
+{
+       WARN_ON(atomic_dec_return(&psy->use_cnt));
+       cancel_work_sync(&psy->changed_work);
+       cancel_delayed_work_sync(&psy->deferred_register_work);
+       sysfs_remove_link(&psy->dev.kobj, "powers");
+       power_supply_remove_triggers(psy);
+       psy_unregister_cooler(psy);
+       psy_unregister_thermal(psy);
+       device_init_wakeup(&psy->dev, false);
+       device_unregister(&psy->dev);
+}
+EXPORT_SYMBOL_GPL(power_supply_unregister);
+
+void *power_supply_get_drvdata(struct power_supply *psy)
+{
+       return psy->drv_data;
+}
+EXPORT_SYMBOL_GPL(power_supply_get_drvdata);
+
+static int __init power_supply_class_init(void)
+{
+       power_supply_class = class_create(THIS_MODULE, "power_supply");
+
+       if (IS_ERR(power_supply_class))
+               return PTR_ERR(power_supply_class);
+
+       power_supply_class->dev_uevent = power_supply_uevent;
+       power_supply_init_attrs(&power_supply_dev_type);
+
+       return 0;
+}
+
+static void __exit power_supply_class_exit(void)
+{
+       class_destroy(power_supply_class);
+}
+
+subsys_initcall(power_supply_class_init);
+module_exit(power_supply_class_exit);
+
+MODULE_DESCRIPTION("Universal power supply monitor class");
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
+             "Szabolcs Gyurko, "
+             "Anton Vorontsov <cbou@mail.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/power_supply_leds.c b/drivers/power/supply/power_supply_leds.c
new file mode 100644 (file)
index 0000000..2277ad9
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ *  LEDs triggers for power supply class
+ *
+ *  Copyright Â© 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright Â© 2004  Szabolcs Gyurko
+ *  Copyright Â© 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#include "power_supply.h"
+
+/* Battery specific LEDs triggers. */
+
+static void power_supply_update_bat_leds(struct power_supply *psy)
+{
+       union power_supply_propval status;
+       unsigned long delay_on = 0;
+       unsigned long delay_off = 0;
+
+       if (power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
+               return;
+
+       dev_dbg(&psy->dev, "%s %d\n", __func__, status.intval);
+
+       switch (status.intval) {
+       case POWER_SUPPLY_STATUS_FULL:
+               led_trigger_event(psy->charging_full_trig, LED_FULL);
+               led_trigger_event(psy->charging_trig, LED_OFF);
+               led_trigger_event(psy->full_trig, LED_FULL);
+               led_trigger_event(psy->charging_blink_full_solid_trig,
+                       LED_FULL);
+               break;
+       case POWER_SUPPLY_STATUS_CHARGING:
+               led_trigger_event(psy->charging_full_trig, LED_FULL);
+               led_trigger_event(psy->charging_trig, LED_FULL);
+               led_trigger_event(psy->full_trig, LED_OFF);
+               led_trigger_blink(psy->charging_blink_full_solid_trig,
+                       &delay_on, &delay_off);
+               break;
+       default:
+               led_trigger_event(psy->charging_full_trig, LED_OFF);
+               led_trigger_event(psy->charging_trig, LED_OFF);
+               led_trigger_event(psy->full_trig, LED_OFF);
+               led_trigger_event(psy->charging_blink_full_solid_trig,
+                       LED_OFF);
+               break;
+       }
+}
+
+static int power_supply_create_bat_triggers(struct power_supply *psy)
+{
+       psy->charging_full_trig_name = kasprintf(GFP_KERNEL,
+                                       "%s-charging-or-full", psy->desc->name);
+       if (!psy->charging_full_trig_name)
+               goto charging_full_failed;
+
+       psy->charging_trig_name = kasprintf(GFP_KERNEL,
+                                       "%s-charging", psy->desc->name);
+       if (!psy->charging_trig_name)
+               goto charging_failed;
+
+       psy->full_trig_name = kasprintf(GFP_KERNEL, "%s-full", psy->desc->name);
+       if (!psy->full_trig_name)
+               goto full_failed;
+
+       psy->charging_blink_full_solid_trig_name = kasprintf(GFP_KERNEL,
+               "%s-charging-blink-full-solid", psy->desc->name);
+       if (!psy->charging_blink_full_solid_trig_name)
+               goto charging_blink_full_solid_failed;
+
+       led_trigger_register_simple(psy->charging_full_trig_name,
+                                   &psy->charging_full_trig);
+       led_trigger_register_simple(psy->charging_trig_name,
+                                   &psy->charging_trig);
+       led_trigger_register_simple(psy->full_trig_name,
+                                   &psy->full_trig);
+       led_trigger_register_simple(psy->charging_blink_full_solid_trig_name,
+                                   &psy->charging_blink_full_solid_trig);
+
+       return 0;
+
+charging_blink_full_solid_failed:
+       kfree(psy->full_trig_name);
+full_failed:
+       kfree(psy->charging_trig_name);
+charging_failed:
+       kfree(psy->charging_full_trig_name);
+charging_full_failed:
+       return -ENOMEM;
+}
+
+static void power_supply_remove_bat_triggers(struct power_supply *psy)
+{
+       led_trigger_unregister_simple(psy->charging_full_trig);
+       led_trigger_unregister_simple(psy->charging_trig);
+       led_trigger_unregister_simple(psy->full_trig);
+       led_trigger_unregister_simple(psy->charging_blink_full_solid_trig);
+       kfree(psy->charging_blink_full_solid_trig_name);
+       kfree(psy->full_trig_name);
+       kfree(psy->charging_trig_name);
+       kfree(psy->charging_full_trig_name);
+}
+
+/* Generated power specific LEDs triggers. */
+
+static void power_supply_update_gen_leds(struct power_supply *psy)
+{
+       union power_supply_propval online;
+
+       if (power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
+               return;
+
+       dev_dbg(&psy->dev, "%s %d\n", __func__, online.intval);
+
+       if (online.intval)
+               led_trigger_event(psy->online_trig, LED_FULL);
+       else
+               led_trigger_event(psy->online_trig, LED_OFF);
+}
+
+static int power_supply_create_gen_triggers(struct power_supply *psy)
+{
+       psy->online_trig_name = kasprintf(GFP_KERNEL, "%s-online",
+                                         psy->desc->name);
+       if (!psy->online_trig_name)
+               return -ENOMEM;
+
+       led_trigger_register_simple(psy->online_trig_name, &psy->online_trig);
+
+       return 0;
+}
+
+static void power_supply_remove_gen_triggers(struct power_supply *psy)
+{
+       led_trigger_unregister_simple(psy->online_trig);
+       kfree(psy->online_trig_name);
+}
+
+/* Choice what triggers to create&update. */
+
+void power_supply_update_leds(struct power_supply *psy)
+{
+       if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
+               power_supply_update_bat_leds(psy);
+       else
+               power_supply_update_gen_leds(psy);
+}
+
+int power_supply_create_triggers(struct power_supply *psy)
+{
+       if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
+               return power_supply_create_bat_triggers(psy);
+       return power_supply_create_gen_triggers(psy);
+}
+
+void power_supply_remove_triggers(struct power_supply *psy)
+{
+       if (psy->desc->type == POWER_SUPPLY_TYPE_BATTERY)
+               power_supply_remove_bat_triggers(psy);
+       else
+               power_supply_remove_gen_triggers(psy);
+}
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
new file mode 100644 (file)
index 0000000..bcde8d1
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ *  Sysfs interface for the universal power supply monitor class
+ *
+ *  Copyright Â© 2007  David Woodhouse <dwmw2@infradead.org>
+ *  Copyright Â© 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright Â© 2004  Szabolcs Gyurko
+ *  Copyright Â© 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+
+#include "power_supply.h"
+
+/*
+ * This is because the name "current" breaks the device attr macro.
+ * The "current" word resolves to "(get_current())" so instead of
+ * "current" "(get_current())" appears in the sysfs.
+ *
+ * The source of this definition is the device.h which calls __ATTR
+ * macro in sysfs.h which calls the __stringify macro.
+ *
+ * Only modification that the name is not tried to be resolved
+ * (as a macro let's say).
+ */
+
+#define POWER_SUPPLY_ATTR(_name)                                       \
+{                                                                      \
+       .attr = { .name = #_name },                                     \
+       .show = power_supply_show_property,                             \
+       .store = power_supply_store_property,                           \
+}
+
+static struct device_attribute power_supply_attrs[];
+
+static ssize_t power_supply_show_property(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf) {
+       static char *type_text[] = {
+               "Unknown", "Battery", "UPS", "Mains", "USB",
+               "USB_DCP", "USB_CDP", "USB_ACA", "USB_C",
+               "USB_PD", "USB_PD_DRP"
+       };
+       static char *status_text[] = {
+               "Unknown", "Charging", "Discharging", "Not charging", "Full"
+       };
+       static char *charge_type[] = {
+               "Unknown", "N/A", "Trickle", "Fast"
+       };
+       static char *health_text[] = {
+               "Unknown", "Good", "Overheat", "Dead", "Over voltage",
+               "Unspecified failure", "Cold", "Watchdog timer expire",
+               "Safety timer expire"
+       };
+       static char *technology_text[] = {
+               "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
+               "LiMn"
+       };
+       static char *capacity_level_text[] = {
+               "Unknown", "Critical", "Low", "Normal", "High", "Full"
+       };
+       static char *scope_text[] = {
+               "Unknown", "System", "Device"
+       };
+       ssize_t ret = 0;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       const ptrdiff_t off = attr - power_supply_attrs;
+       union power_supply_propval value;
+
+       if (off == POWER_SUPPLY_PROP_TYPE) {
+               value.intval = psy->desc->type;
+       } else {
+               ret = power_supply_get_property(psy, off, &value);
+
+               if (ret < 0) {
+                       if (ret == -ENODATA)
+                               dev_dbg(dev, "driver has no data for `%s' property\n",
+                                       attr->attr.name);
+                       else if (ret != -ENODEV && ret != -EAGAIN)
+                               dev_err(dev, "driver failed to report `%s' property: %zd\n",
+                                       attr->attr.name, ret);
+                       return ret;
+               }
+       }
+
+       if (off == POWER_SUPPLY_PROP_STATUS)
+               return sprintf(buf, "%s\n", status_text[value.intval]);
+       else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
+               return sprintf(buf, "%s\n", charge_type[value.intval]);
+       else if (off == POWER_SUPPLY_PROP_HEALTH)
+               return sprintf(buf, "%s\n", health_text[value.intval]);
+       else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
+               return sprintf(buf, "%s\n", technology_text[value.intval]);
+       else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
+               return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
+       else if (off == POWER_SUPPLY_PROP_TYPE)
+               return sprintf(buf, "%s\n", type_text[value.intval]);
+       else if (off == POWER_SUPPLY_PROP_SCOPE)
+               return sprintf(buf, "%s\n", scope_text[value.intval]);
+       else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
+               return sprintf(buf, "%s\n", value.strval);
+
+       return sprintf(buf, "%d\n", value.intval);
+}
+
+static ssize_t power_supply_store_property(struct device *dev,
+                                          struct device_attribute *attr,
+                                          const char *buf, size_t count) {
+       ssize_t ret;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       const ptrdiff_t off = attr - power_supply_attrs;
+       union power_supply_propval value;
+       long long_val;
+
+       /* TODO: support other types than int */
+       ret = kstrtol(buf, 10, &long_val);
+       if (ret < 0)
+               return ret;
+
+       value.intval = long_val;
+
+       ret = power_supply_set_property(psy, off, &value);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+/* Must be in the same order as POWER_SUPPLY_PROP_* */
+static struct device_attribute power_supply_attrs[] = {
+       /* Properties of type `int' */
+       POWER_SUPPLY_ATTR(status),
+       POWER_SUPPLY_ATTR(charge_type),
+       POWER_SUPPLY_ATTR(health),
+       POWER_SUPPLY_ATTR(present),
+       POWER_SUPPLY_ATTR(online),
+       POWER_SUPPLY_ATTR(authentic),
+       POWER_SUPPLY_ATTR(technology),
+       POWER_SUPPLY_ATTR(cycle_count),
+       POWER_SUPPLY_ATTR(voltage_max),
+       POWER_SUPPLY_ATTR(voltage_min),
+       POWER_SUPPLY_ATTR(voltage_max_design),
+       POWER_SUPPLY_ATTR(voltage_min_design),
+       POWER_SUPPLY_ATTR(voltage_now),
+       POWER_SUPPLY_ATTR(voltage_avg),
+       POWER_SUPPLY_ATTR(voltage_ocv),
+       POWER_SUPPLY_ATTR(voltage_boot),
+       POWER_SUPPLY_ATTR(current_max),
+       POWER_SUPPLY_ATTR(current_now),
+       POWER_SUPPLY_ATTR(current_avg),
+       POWER_SUPPLY_ATTR(current_boot),
+       POWER_SUPPLY_ATTR(power_now),
+       POWER_SUPPLY_ATTR(power_avg),
+       POWER_SUPPLY_ATTR(charge_full_design),
+       POWER_SUPPLY_ATTR(charge_empty_design),
+       POWER_SUPPLY_ATTR(charge_full),
+       POWER_SUPPLY_ATTR(charge_empty),
+       POWER_SUPPLY_ATTR(charge_now),
+       POWER_SUPPLY_ATTR(charge_avg),
+       POWER_SUPPLY_ATTR(charge_counter),
+       POWER_SUPPLY_ATTR(constant_charge_current),
+       POWER_SUPPLY_ATTR(constant_charge_current_max),
+       POWER_SUPPLY_ATTR(constant_charge_voltage),
+       POWER_SUPPLY_ATTR(constant_charge_voltage_max),
+       POWER_SUPPLY_ATTR(charge_control_limit),
+       POWER_SUPPLY_ATTR(charge_control_limit_max),
+       POWER_SUPPLY_ATTR(input_current_limit),
+       POWER_SUPPLY_ATTR(energy_full_design),
+       POWER_SUPPLY_ATTR(energy_empty_design),
+       POWER_SUPPLY_ATTR(energy_full),
+       POWER_SUPPLY_ATTR(energy_empty),
+       POWER_SUPPLY_ATTR(energy_now),
+       POWER_SUPPLY_ATTR(energy_avg),
+       POWER_SUPPLY_ATTR(capacity),
+       POWER_SUPPLY_ATTR(capacity_alert_min),
+       POWER_SUPPLY_ATTR(capacity_alert_max),
+       POWER_SUPPLY_ATTR(capacity_level),
+       POWER_SUPPLY_ATTR(temp),
+       POWER_SUPPLY_ATTR(temp_max),
+       POWER_SUPPLY_ATTR(temp_min),
+       POWER_SUPPLY_ATTR(temp_alert_min),
+       POWER_SUPPLY_ATTR(temp_alert_max),
+       POWER_SUPPLY_ATTR(temp_ambient),
+       POWER_SUPPLY_ATTR(temp_ambient_alert_min),
+       POWER_SUPPLY_ATTR(temp_ambient_alert_max),
+       POWER_SUPPLY_ATTR(time_to_empty_now),
+       POWER_SUPPLY_ATTR(time_to_empty_avg),
+       POWER_SUPPLY_ATTR(time_to_full_now),
+       POWER_SUPPLY_ATTR(time_to_full_avg),
+       POWER_SUPPLY_ATTR(type),
+       POWER_SUPPLY_ATTR(scope),
+       POWER_SUPPLY_ATTR(charge_term_current),
+       POWER_SUPPLY_ATTR(calibrate),
+       /* Properties of type `const char *' */
+       POWER_SUPPLY_ATTR(model_name),
+       POWER_SUPPLY_ATTR(manufacturer),
+       POWER_SUPPLY_ATTR(serial_number),
+};
+
+static struct attribute *
+__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
+
+static umode_t power_supply_attr_is_visible(struct kobject *kobj,
+                                          struct attribute *attr,
+                                          int attrno)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct power_supply *psy = dev_get_drvdata(dev);
+       umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
+       int i;
+
+       if (attrno == POWER_SUPPLY_PROP_TYPE)
+               return mode;
+
+       for (i = 0; i < psy->desc->num_properties; i++) {
+               int property = psy->desc->properties[i];
+
+               if (property == attrno) {
+                       if (psy->desc->property_is_writeable &&
+                           psy->desc->property_is_writeable(psy, property) > 0)
+                               mode |= S_IWUSR;
+
+                       return mode;
+               }
+       }
+
+       return 0;
+}
+
+static struct attribute_group power_supply_attr_group = {
+       .attrs = __power_supply_attrs,
+       .is_visible = power_supply_attr_is_visible,
+};
+
+static const struct attribute_group *power_supply_attr_groups[] = {
+       &power_supply_attr_group,
+       NULL,
+};
+
+void power_supply_init_attrs(struct device_type *dev_type)
+{
+       int i;
+
+       dev_type->groups = power_supply_attr_groups;
+
+       for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
+               __power_supply_attrs[i] = &power_supply_attrs[i].attr;
+}
+
+static char *kstruprdup(const char *str, gfp_t gfp)
+{
+       char *ret, *ustr;
+
+       ustr = ret = kmalloc(strlen(str) + 1, gfp);
+
+       if (!ret)
+               return NULL;
+
+       while (*str)
+               *ustr++ = toupper(*str++);
+
+       *ustr = 0;
+
+       return ret;
+}
+
+int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       struct power_supply *psy = dev_get_drvdata(dev);
+       int ret = 0, j;
+       char *prop_buf;
+       char *attrname;
+
+       dev_dbg(dev, "uevent\n");
+
+       if (!psy || !psy->desc) {
+               dev_dbg(dev, "No power supply yet\n");
+               return ret;
+       }
+
+       dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->desc->name);
+
+       ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
+       if (ret)
+               return ret;
+
+       prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+       if (!prop_buf)
+               return -ENOMEM;
+
+       for (j = 0; j < psy->desc->num_properties; j++) {
+               struct device_attribute *attr;
+               char *line;
+
+               attr = &power_supply_attrs[psy->desc->properties[j]];
+
+               ret = power_supply_show_property(dev, attr, prop_buf);
+               if (ret == -ENODEV || ret == -ENODATA) {
+                       /* When a battery is absent, we expect -ENODEV. Don't abort;
+                          send the uevent with at least the the PRESENT=0 property */
+                       ret = 0;
+                       continue;
+               }
+
+               if (ret < 0)
+                       goto out;
+
+               line = strchr(prop_buf, '\n');
+               if (line)
+                       *line = 0;
+
+               attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
+               if (!attrname) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
+
+               ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
+               kfree(attrname);
+               if (ret)
+                       goto out;
+       }
+
+out:
+       free_page((unsigned long)prop_buf);
+
+       return ret;
+}
diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c
new file mode 100644 (file)
index 0000000..b5896ba
--- /dev/null
@@ -0,0 +1,972 @@
+/* Copyright (c) 2014, Sony Mobile Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * This driver is for the multi-block Switch-Mode Battery Charger and Boost
+ * (SMBB) hardware, found in Qualcomm PM8941 PMICs.  The charger is an
+ * integrated, single-cell lithium-ion battery charger.
+ *
+ * Sub-components:
+ *  - Charger core
+ *  - Buck
+ *  - DC charge-path
+ *  - USB charge-path
+ *  - Battery interface
+ *  - Boost (not implemented)
+ *  - Misc
+ *  - HF-Buck
+ */
+
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/extcon.h>
+
+#define SMBB_CHG_VMAX          0x040
+#define SMBB_CHG_VSAFE         0x041
+#define SMBB_CHG_CFG           0x043
+#define SMBB_CHG_IMAX          0x044
+#define SMBB_CHG_ISAFE         0x045
+#define SMBB_CHG_VIN_MIN       0x047
+#define SMBB_CHG_CTRL          0x049
+#define CTRL_EN                        BIT(7)
+#define SMBB_CHG_VBAT_WEAK     0x052
+#define SMBB_CHG_IBAT_TERM_CHG 0x05b
+#define IBAT_TERM_CHG_IEOC     BIT(7)
+#define IBAT_TERM_CHG_IEOC_BMS BIT(7)
+#define IBAT_TERM_CHG_IEOC_CHG 0
+#define SMBB_CHG_VBAT_DET      0x05d
+#define SMBB_CHG_TCHG_MAX_EN   0x060
+#define TCHG_MAX_EN            BIT(7)
+#define SMBB_CHG_WDOG_TIME     0x062
+#define SMBB_CHG_WDOG_EN       0x065
+#define WDOG_EN                        BIT(7)
+
+#define SMBB_BUCK_REG_MODE     0x174
+#define BUCK_REG_MODE          BIT(0)
+#define BUCK_REG_MODE_VBAT     BIT(0)
+#define BUCK_REG_MODE_VSYS     0
+
+#define SMBB_BAT_PRES_STATUS   0x208
+#define PRES_STATUS_BAT_PRES   BIT(7)
+#define SMBB_BAT_TEMP_STATUS   0x209
+#define TEMP_STATUS_OK         BIT(7)
+#define TEMP_STATUS_HOT                BIT(6)
+#define SMBB_BAT_BTC_CTRL      0x249
+#define BTC_CTRL_COMP_EN       BIT(7)
+#define BTC_CTRL_COLD_EXT      BIT(1)
+#define BTC_CTRL_HOT_EXT_N     BIT(0)
+
+#define SMBB_USB_IMAX          0x344
+#define SMBB_USB_ENUM_TIMER_STOP 0x34e
+#define ENUM_TIMER_STOP                BIT(0)
+#define SMBB_USB_SEC_ACCESS    0x3d0
+#define SEC_ACCESS_MAGIC       0xa5
+#define SMBB_USB_REV_BST       0x3ed
+#define REV_BST_CHG_GONE       BIT(7)
+
+#define SMBB_DC_IMAX           0x444
+
+#define SMBB_MISC_REV2         0x601
+#define SMBB_MISC_BOOT_DONE    0x642
+#define BOOT_DONE              BIT(7)
+
+#define STATUS_USBIN_VALID     BIT(0) /* USB connection is valid */
+#define STATUS_DCIN_VALID      BIT(1) /* DC connection is valid */
+#define STATUS_BAT_HOT         BIT(2) /* Battery temp 1=Hot, 0=Cold */
+#define STATUS_BAT_OK          BIT(3) /* Battery temp OK */
+#define STATUS_BAT_PRESENT     BIT(4) /* Battery is present */
+#define STATUS_CHG_DONE                BIT(5) /* Charge cycle is complete */
+#define STATUS_CHG_TRKL                BIT(6) /* Trickle charging */
+#define STATUS_CHG_FAST                BIT(7) /* Fast charging */
+#define STATUS_CHG_GONE                BIT(8) /* No charger is connected */
+
+enum smbb_attr {
+       ATTR_BAT_ISAFE,
+       ATTR_BAT_IMAX,
+       ATTR_USBIN_IMAX,
+       ATTR_DCIN_IMAX,
+       ATTR_BAT_VSAFE,
+       ATTR_BAT_VMAX,
+       ATTR_BAT_VMIN,
+       ATTR_CHG_VDET,
+       ATTR_VIN_MIN,
+       _ATTR_CNT,
+};
+
+struct smbb_charger {
+       unsigned int revision;
+       unsigned int addr;
+       struct device *dev;
+       struct extcon_dev *edev;
+
+       bool dc_disabled;
+       bool jeita_ext_temp;
+       unsigned long status;
+       struct mutex statlock;
+
+       unsigned int attr[_ATTR_CNT];
+
+       struct power_supply *usb_psy;
+       struct power_supply *dc_psy;
+       struct power_supply *bat_psy;
+       struct regmap *regmap;
+};
+
+static const unsigned int smbb_usb_extcon_cable[] = {
+       EXTCON_USB,
+       EXTCON_NONE,
+};
+
+static int smbb_vbat_weak_fn(unsigned int index)
+{
+       return 2100000 + index * 100000;
+}
+
+static int smbb_vin_fn(unsigned int index)
+{
+       if (index > 42)
+               return 5600000 + (index - 43) * 200000;
+       return 3400000 + index * 50000;
+}
+
+static int smbb_vmax_fn(unsigned int index)
+{
+       return 3240000 + index * 10000;
+}
+
+static int smbb_vbat_det_fn(unsigned int index)
+{
+       return 3240000 + index * 20000;
+}
+
+static int smbb_imax_fn(unsigned int index)
+{
+       if (index < 2)
+               return 100000 + index * 50000;
+       return index * 100000;
+}
+
+static int smbb_bat_imax_fn(unsigned int index)
+{
+       return index * 50000;
+}
+
+static unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int))
+{
+       unsigned int widx;
+       unsigned int sel;
+
+       for (widx = sel = 0; (*fn)(widx) <= val; ++widx)
+               sel = widx;
+
+       return sel;
+}
+
+static const struct smbb_charger_attr {
+       const char *name;
+       unsigned int reg;
+       unsigned int safe_reg;
+       unsigned int max;
+       unsigned int min;
+       unsigned int fail_ok;
+       int (*hw_fn)(unsigned int);
+} smbb_charger_attrs[] = {
+       [ATTR_BAT_ISAFE] = {
+               .name = "qcom,fast-charge-safe-current",
+               .reg = SMBB_CHG_ISAFE,
+               .max = 3000000,
+               .min = 200000,
+               .hw_fn = smbb_bat_imax_fn,
+               .fail_ok = 1,
+       },
+       [ATTR_BAT_IMAX] = {
+               .name = "qcom,fast-charge-current-limit",
+               .reg = SMBB_CHG_IMAX,
+               .safe_reg = SMBB_CHG_ISAFE,
+               .max = 3000000,
+               .min = 200000,
+               .hw_fn = smbb_bat_imax_fn,
+       },
+       [ATTR_DCIN_IMAX] = {
+               .name = "qcom,dc-current-limit",
+               .reg = SMBB_DC_IMAX,
+               .max = 2500000,
+               .min = 100000,
+               .hw_fn = smbb_imax_fn,
+       },
+       [ATTR_BAT_VSAFE] = {
+               .name = "qcom,fast-charge-safe-voltage",
+               .reg = SMBB_CHG_VSAFE,
+               .max = 5000000,
+               .min = 3240000,
+               .hw_fn = smbb_vmax_fn,
+               .fail_ok = 1,
+       },
+       [ATTR_BAT_VMAX] = {
+               .name = "qcom,fast-charge-high-threshold-voltage",
+               .reg = SMBB_CHG_VMAX,
+               .safe_reg = SMBB_CHG_VSAFE,
+               .max = 5000000,
+               .min = 3240000,
+               .hw_fn = smbb_vmax_fn,
+       },
+       [ATTR_BAT_VMIN] = {
+               .name = "qcom,fast-charge-low-threshold-voltage",
+               .reg = SMBB_CHG_VBAT_WEAK,
+               .max = 3600000,
+               .min = 2100000,
+               .hw_fn = smbb_vbat_weak_fn,
+       },
+       [ATTR_CHG_VDET] = {
+               .name = "qcom,auto-recharge-threshold-voltage",
+               .reg = SMBB_CHG_VBAT_DET,
+               .max = 5000000,
+               .min = 3240000,
+               .hw_fn = smbb_vbat_det_fn,
+       },
+       [ATTR_VIN_MIN] = {
+               .name = "qcom,minimum-input-voltage",
+               .reg = SMBB_CHG_VIN_MIN,
+               .max = 9600000,
+               .min = 4200000,
+               .hw_fn = smbb_vin_fn,
+       },
+       [ATTR_USBIN_IMAX] = {
+               .name = "usb-charge-current-limit",
+               .reg = SMBB_USB_IMAX,
+               .max = 2500000,
+               .min = 100000,
+               .hw_fn = smbb_imax_fn,
+       },
+};
+
+static int smbb_charger_attr_write(struct smbb_charger *chg,
+               enum smbb_attr which, unsigned int val)
+{
+       const struct smbb_charger_attr *prop;
+       unsigned int wval;
+       unsigned int out;
+       int rc;
+
+       prop = &smbb_charger_attrs[which];
+
+       if (val > prop->max || val < prop->min) {
+               dev_err(chg->dev, "value out of range for %s [%u:%u]\n",
+                       prop->name, prop->min, prop->max);
+               return -EINVAL;
+       }
+
+       if (prop->safe_reg) {
+               rc = regmap_read(chg->regmap,
+                               chg->addr + prop->safe_reg, &wval);
+               if (rc) {
+                       dev_err(chg->dev,
+                               "unable to read safe value for '%s'\n",
+                               prop->name);
+                       return rc;
+               }
+
+               wval = prop->hw_fn(wval);
+
+               if (val > wval) {
+                       dev_warn(chg->dev,
+                               "%s above safe value, clamping at %u\n",
+                               prop->name, wval);
+                       val = wval;
+               }
+       }
+
+       wval = smbb_hw_lookup(val, prop->hw_fn);
+
+       rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval);
+       if (rc) {
+               dev_err(chg->dev, "unable to update %s", prop->name);
+               return rc;
+       }
+       out = prop->hw_fn(wval);
+       if (out != val) {
+               dev_warn(chg->dev,
+                       "%s inaccurate, rounded to %u\n",
+                       prop->name, out);
+       }
+
+       dev_dbg(chg->dev, "%s <= %d\n", prop->name, out);
+
+       chg->attr[which] = out;
+
+       return 0;
+}
+
+static int smbb_charger_attr_read(struct smbb_charger *chg,
+               enum smbb_attr which)
+{
+       const struct smbb_charger_attr *prop;
+       unsigned int val;
+       int rc;
+
+       prop = &smbb_charger_attrs[which];
+
+       rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val);
+       if (rc) {
+               dev_err(chg->dev, "failed to read %s\n", prop->name);
+               return rc;
+       }
+       val = prop->hw_fn(val);
+       dev_dbg(chg->dev, "%s => %d\n", prop->name, val);
+
+       chg->attr[which] = val;
+
+       return 0;
+}
+
+static int smbb_charger_attr_parse(struct smbb_charger *chg,
+               enum smbb_attr which)
+{
+       const struct smbb_charger_attr *prop;
+       unsigned int val;
+       int rc;
+
+       prop = &smbb_charger_attrs[which];
+
+       rc = of_property_read_u32(chg->dev->of_node, prop->name, &val);
+       if (rc == 0) {
+               rc = smbb_charger_attr_write(chg, which, val);
+               if (!rc || !prop->fail_ok)
+                       return rc;
+       }
+       return smbb_charger_attr_read(chg, which);
+}
+
+static void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag)
+{
+       bool state;
+       int ret;
+
+       ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state);
+       if (ret < 0) {
+               dev_err(chg->dev, "failed to read irq line\n");
+               return;
+       }
+
+       mutex_lock(&chg->statlock);
+       if (state)
+               chg->status |= flag;
+       else
+               chg->status &= ~flag;
+       mutex_unlock(&chg->statlock);
+
+       dev_dbg(chg->dev, "status = %03lx\n", chg->status);
+}
+
+static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
+       extcon_set_cable_state_(chg->edev, EXTCON_USB,
+                               chg->status & STATUS_USBIN_VALID);
+       power_supply_changed(chg->usb_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_dc_valid_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID);
+       if (!chg->dc_disabled)
+               power_supply_changed(chg->dc_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_bat_temp_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+       unsigned int val;
+       int rc;
+
+       rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val);
+       if (rc)
+               return IRQ_HANDLED;
+
+       mutex_lock(&chg->statlock);
+       if (val & TEMP_STATUS_OK) {
+               chg->status |= STATUS_BAT_OK;
+       } else {
+               chg->status &= ~STATUS_BAT_OK;
+               if (val & TEMP_STATUS_HOT)
+                       chg->status |= STATUS_BAT_HOT;
+       }
+       mutex_unlock(&chg->statlock);
+
+       power_supply_changed(chg->bat_psy);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_bat_present_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT);
+       power_supply_changed(chg->bat_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_chg_done_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_CHG_DONE);
+       power_supply_changed(chg->bat_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_chg_gone_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_CHG_GONE);
+       power_supply_changed(chg->bat_psy);
+       power_supply_changed(chg->usb_psy);
+       if (!chg->dc_disabled)
+               power_supply_changed(chg->dc_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_chg_fast_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_CHG_FAST);
+       power_supply_changed(chg->bat_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_chg_trkl_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL);
+       power_supply_changed(chg->bat_psy);
+
+       return IRQ_HANDLED;
+}
+
+static const struct smbb_irq {
+       const char *name;
+       irqreturn_t (*handler)(int, void *);
+} smbb_charger_irqs[] = {
+       { "chg-done", smbb_chg_done_handler },
+       { "chg-fast", smbb_chg_fast_handler },
+       { "chg-trkl", smbb_chg_trkl_handler },
+       { "bat-temp-ok", smbb_bat_temp_handler },
+       { "bat-present", smbb_bat_present_handler },
+       { "chg-gone", smbb_chg_gone_handler },
+       { "usb-valid", smbb_usb_valid_handler },
+       { "dc-valid", smbb_dc_valid_handler },
+};
+
+static int smbb_usbin_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               mutex_lock(&chg->statlock);
+               val->intval = !(chg->status & STATUS_CHG_GONE) &&
+                               (chg->status & STATUS_USBIN_VALID);
+               mutex_unlock(&chg->statlock);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               val->intval = chg->attr[ATTR_USBIN_IMAX];
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
+               val->intval = 2500000;
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_usbin_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX,
+                               val->intval);
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_dcin_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               mutex_lock(&chg->statlock);
+               val->intval = !(chg->status & STATUS_CHG_GONE) &&
+                               (chg->status & STATUS_DCIN_VALID);
+               mutex_unlock(&chg->statlock);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               val->intval = chg->attr[ATTR_DCIN_IMAX];
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
+               val->intval = 2500000;
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_dcin_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX,
+                               val->intval);
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_charger_writable_property(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
+}
+
+static int smbb_battery_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       unsigned long status;
+       int rc = 0;
+
+       mutex_lock(&chg->statlock);
+       status = chg->status;
+       mutex_unlock(&chg->statlock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (status & STATUS_CHG_GONE)
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID)))
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (status & STATUS_CHG_DONE)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else if (!(status & STATUS_BAT_OK))
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL))
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else /* everything is ok for charging, but we are not... */
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (status & STATUS_BAT_OK)
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               else if (status & STATUS_BAT_HOT)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_COLD;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               if (status & STATUS_CHG_FAST)
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               else if (status & STATUS_CHG_TRKL)
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+               else
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = !!(status & STATUS_BAT_PRESENT);
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               val->intval = chg->attr[ATTR_BAT_IMAX];
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               val->intval = chg->attr[ATTR_BAT_VMAX];
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               /* this charger is a single-cell lithium-ion battery charger
+               * only.  If you hook up some other technology, there will be
+               * fireworks.
+               */
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = 3000000; /* single-cell li-ion low end */
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_battery_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval);
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_battery_writable_property(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static enum power_supply_property smbb_charger_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
+};
+
+static enum power_supply_property smbb_battery_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+};
+
+static const struct reg_off_mask_default {
+       unsigned int offset;
+       unsigned int mask;
+       unsigned int value;
+       unsigned int rev_mask;
+} smbb_charger_setup[] = {
+       /* The bootloader is supposed to set this... make sure anyway. */
+       { SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE },
+
+       /* Disable software timer */
+       { SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 },
+
+       /* Clear and disable watchdog */
+       { SMBB_CHG_WDOG_TIME, 0xff, 160 },
+       { SMBB_CHG_WDOG_EN, WDOG_EN, 0 },
+
+       /* Use charger based EoC detection */
+       { SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG },
+
+       /* Disable GSM PA load adjustment.
+       * The PA signal is incorrectly connected on v2.
+       */
+       { SMBB_CHG_CFG, 0xff, 0x00, BIT(3) },
+
+       /* Use VBAT (not VSYS) to compensate for IR drop during fast charging */
+       { SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT },
+
+       /* Enable battery temperature comparators */
+       { SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN },
+
+       /* Stop USB enumeration timer */
+       { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
+
+#if 0 /* FIXME supposedly only to disable hardware ARB termination */
+       { SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC },
+       { SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE },
+#endif
+
+       /* Stop USB enumeration timer, again */
+       { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
+
+       /* Enable charging */
+       { SMBB_CHG_CTRL, CTRL_EN, CTRL_EN },
+};
+
+static char *smbb_bif[] = { "smbb-bif" };
+
+static const struct power_supply_desc bat_psy_desc = {
+       .name = "smbb-bif",
+       .type = POWER_SUPPLY_TYPE_BATTERY,
+       .properties = smbb_battery_properties,
+       .num_properties = ARRAY_SIZE(smbb_battery_properties),
+       .get_property = smbb_battery_get_property,
+       .set_property = smbb_battery_set_property,
+       .property_is_writeable = smbb_battery_writable_property,
+};
+
+static const struct power_supply_desc usb_psy_desc = {
+       .name = "smbb-usbin",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = smbb_charger_properties,
+       .num_properties = ARRAY_SIZE(smbb_charger_properties),
+       .get_property = smbb_usbin_get_property,
+       .set_property = smbb_usbin_set_property,
+       .property_is_writeable = smbb_charger_writable_property,
+};
+
+static const struct power_supply_desc dc_psy_desc = {
+       .name = "smbb-dcin",
+       .type = POWER_SUPPLY_TYPE_MAINS,
+       .properties = smbb_charger_properties,
+       .num_properties = ARRAY_SIZE(smbb_charger_properties),
+       .get_property = smbb_dcin_get_property,
+       .set_property = smbb_dcin_set_property,
+       .property_is_writeable = smbb_charger_writable_property,
+};
+
+static int smbb_charger_probe(struct platform_device *pdev)
+{
+       struct power_supply_config bat_cfg = {};
+       struct power_supply_config usb_cfg = {};
+       struct power_supply_config dc_cfg = {};
+       struct smbb_charger *chg;
+       int rc, i;
+
+       chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
+       if (!chg)
+               return -ENOMEM;
+
+       chg->dev = &pdev->dev;
+       mutex_init(&chg->statlock);
+
+       chg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!chg->regmap) {
+               dev_err(&pdev->dev, "failed to locate regmap\n");
+               return -ENODEV;
+       }
+
+       rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr);
+       if (rc) {
+               dev_err(&pdev->dev, "missing or invalid 'reg' property\n");
+               return rc;
+       }
+
+       rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision);
+       if (rc) {
+               dev_err(&pdev->dev, "unable to read revision\n");
+               return rc;
+       }
+
+       chg->revision += 1;
+       if (chg->revision != 2 && chg->revision != 3) {
+               dev_err(&pdev->dev, "v1 hardware not supported\n");
+               return -ENODEV;
+       }
+       dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision);
+
+       chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc");
+
+       for (i = 0; i < _ATTR_CNT; ++i) {
+               rc = smbb_charger_attr_parse(chg, i);
+               if (rc) {
+                       dev_err(&pdev->dev, "failed to parse/apply settings\n");
+                       return rc;
+               }
+       }
+
+       bat_cfg.drv_data = chg;
+       bat_cfg.of_node = pdev->dev.of_node;
+       chg->bat_psy = devm_power_supply_register(&pdev->dev,
+                                                 &bat_psy_desc,
+                                                 &bat_cfg);
+       if (IS_ERR(chg->bat_psy)) {
+               dev_err(&pdev->dev, "failed to register battery\n");
+               return PTR_ERR(chg->bat_psy);
+       }
+
+       usb_cfg.drv_data = chg;
+       usb_cfg.supplied_to = smbb_bif;
+       usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
+       chg->usb_psy = devm_power_supply_register(&pdev->dev,
+                                                 &usb_psy_desc,
+                                                 &usb_cfg);
+       if (IS_ERR(chg->usb_psy)) {
+               dev_err(&pdev->dev, "failed to register USB power supply\n");
+               return PTR_ERR(chg->usb_psy);
+       }
+
+       chg->edev = devm_extcon_dev_allocate(&pdev->dev, smbb_usb_extcon_cable);
+       if (IS_ERR(chg->edev)) {
+               dev_err(&pdev->dev, "failed to allocate extcon device\n");
+               return -ENOMEM;
+       }
+
+       rc = devm_extcon_dev_register(&pdev->dev, chg->edev);
+       if (rc < 0) {
+               dev_err(&pdev->dev, "failed to register extcon device\n");
+               return rc;
+       }
+
+       if (!chg->dc_disabled) {
+               dc_cfg.drv_data = chg;
+               dc_cfg.supplied_to = smbb_bif;
+               dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
+               chg->dc_psy = devm_power_supply_register(&pdev->dev,
+                                                        &dc_psy_desc,
+                                                        &dc_cfg);
+               if (IS_ERR(chg->dc_psy)) {
+                       dev_err(&pdev->dev, "failed to register DC power supply\n");
+                       return PTR_ERR(chg->dc_psy);
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) {
+               int irq;
+
+               irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
+               if (irq < 0) {
+                       dev_err(&pdev->dev, "failed to get irq '%s'\n",
+                               smbb_charger_irqs[i].name);
+                       return irq;
+               }
+
+               smbb_charger_irqs[i].handler(irq, chg);
+
+               rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                               smbb_charger_irqs[i].handler, IRQF_ONESHOT,
+                               smbb_charger_irqs[i].name, chg);
+               if (rc) {
+                       dev_err(&pdev->dev, "failed to request irq '%s'\n",
+                               smbb_charger_irqs[i].name);
+                       return rc;
+               }
+       }
+
+       chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
+                       "qcom,jeita-extended-temp-range");
+
+       /* Set temperature range to [35%:70%] or [25%:80%] accordingly */
+       rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL,
+                       BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N,
+                       chg->jeita_ext_temp ?
+                               BTC_CTRL_COLD_EXT :
+                               BTC_CTRL_HOT_EXT_N);
+       if (rc) {
+               dev_err(&pdev->dev,
+                       "unable to set %s temperature range\n",
+                       chg->jeita_ext_temp ? "JEITA extended" : "normal");
+               return rc;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) {
+               const struct reg_off_mask_default *r = &smbb_charger_setup[i];
+
+               if (r->rev_mask & BIT(chg->revision))
+                       continue;
+
+               rc = regmap_update_bits(chg->regmap, chg->addr + r->offset,
+                               r->mask, r->value);
+               if (rc) {
+                       dev_err(&pdev->dev,
+                               "unable to initializing charging, bailing\n");
+                       return rc;
+               }
+       }
+
+       platform_set_drvdata(pdev, chg);
+
+       return 0;
+}
+
+static int smbb_charger_remove(struct platform_device *pdev)
+{
+       struct smbb_charger *chg;
+
+       chg = platform_get_drvdata(pdev);
+
+       regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0);
+
+       return 0;
+}
+
+static const struct of_device_id smbb_charger_id_table[] = {
+       { .compatible = "qcom,pm8941-charger" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, smbb_charger_id_table);
+
+static struct platform_driver smbb_charger_driver = {
+       .probe    = smbb_charger_probe,
+       .remove  = smbb_charger_remove,
+       .driver  = {
+               .name   = "qcom-smbb",
+               .of_match_table = smbb_charger_id_table,
+       },
+};
+module_platform_driver(smbb_charger_driver);
+
+MODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/rt5033_battery.c b/drivers/power/supply/rt5033_battery.c
new file mode 100644 (file)
index 0000000..bcdd830
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Fuel gauge driver for Richtek RT5033
+ *
+ * Copyright (C) 2014 Samsung Electronics, Co., Ltd.
+ * Author: Beomho Seo <beomho.seo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published bythe Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/rt5033-private.h>
+#include <linux/mfd/rt5033.h>
+
+static int rt5033_battery_get_capacity(struct i2c_client *client)
+{
+       struct rt5033_battery *battery = i2c_get_clientdata(client);
+       u32 msb;
+
+       regmap_read(battery->regmap, RT5033_FUEL_REG_SOC_H, &msb);
+
+       return msb;
+}
+
+static int rt5033_battery_get_present(struct i2c_client *client)
+{
+       struct rt5033_battery *battery = i2c_get_clientdata(client);
+       u32 val;
+
+       regmap_read(battery->regmap, RT5033_FUEL_REG_CONFIG_L, &val);
+
+       return (val & RT5033_FUEL_BAT_PRESENT) ? true : false;
+}
+
+static int rt5033_battery_get_watt_prop(struct i2c_client *client,
+               enum power_supply_property psp)
+{
+       struct rt5033_battery *battery = i2c_get_clientdata(client);
+       unsigned int regh, regl;
+       int ret;
+       u32 msb, lsb;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               regh = RT5033_FUEL_REG_VBAT_H;
+               regl = RT5033_FUEL_REG_VBAT_L;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               regh = RT5033_FUEL_REG_AVG_VOLT_H;
+               regl = RT5033_FUEL_REG_AVG_VOLT_L;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+               regh = RT5033_FUEL_REG_OCV_H;
+               regl = RT5033_FUEL_REG_OCV_L;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_read(battery->regmap, regh, &msb);
+       regmap_read(battery->regmap, regl, &lsb);
+
+       ret = ((msb << 4) + (lsb >> 4)) * 1250 / 1000;
+
+       return ret;
+}
+
+static int rt5033_battery_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct rt5033_battery *battery = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+       case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+               val->intval = rt5033_battery_get_watt_prop(battery->client,
+                                                                       psp);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = rt5033_battery_get_present(battery->client);
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = rt5033_battery_get_capacity(battery->client);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static enum power_supply_property rt5033_battery_props[] = {
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_OCV,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static const struct regmap_config rt5033_battery_regmap_config = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = RT5033_FUEL_REG_END,
+};
+
+static const struct power_supply_desc rt5033_battery_desc = {
+       .name           = "rt5033-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property   = rt5033_battery_get_property,
+       .properties     = rt5033_battery_props,
+       .num_properties = ARRAY_SIZE(rt5033_battery_props),
+};
+
+static int rt5033_battery_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct power_supply_config psy_cfg = {};
+       struct rt5033_battery *battery;
+       u32 ret;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
+               return -EIO;
+
+       battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL);
+       if (!battery)
+               return -EINVAL;
+
+       battery->client = client;
+       battery->regmap = devm_regmap_init_i2c(client,
+                       &rt5033_battery_regmap_config);
+       if (IS_ERR(battery->regmap)) {
+               dev_err(&client->dev, "Failed to initialize regmap\n");
+               return -EINVAL;
+       }
+
+       i2c_set_clientdata(client, battery);
+       psy_cfg.drv_data = battery;
+
+       battery->psy = power_supply_register(&client->dev,
+                                            &rt5033_battery_desc, &psy_cfg);
+       if (IS_ERR(battery->psy)) {
+               dev_err(&client->dev, "Failed to register power supply\n");
+               ret = PTR_ERR(battery->psy);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rt5033_battery_remove(struct i2c_client *client)
+{
+       struct rt5033_battery *battery = i2c_get_clientdata(client);
+
+       power_supply_unregister(battery->psy);
+
+       return 0;
+}
+
+static const struct i2c_device_id rt5033_battery_id[] = {
+       { "rt5033-battery", },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5033_battery_id);
+
+static struct i2c_driver rt5033_battery_driver = {
+       .driver = {
+               .name = "rt5033-battery",
+       },
+       .probe = rt5033_battery_probe,
+       .remove = rt5033_battery_remove,
+       .id_table = rt5033_battery_id,
+};
+module_i2c_driver(rt5033_battery_driver);
+
+MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver");
+MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/rt9455_charger.c b/drivers/power/supply/rt9455_charger.c
new file mode 100644 (file)
index 0000000..cfdbde9
--- /dev/null
@@ -0,0 +1,1763 @@
+/*
+ * Driver for Richtek RT9455WSC battery charger.
+ *
+ * Copyright (C) 2015 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/usb/phy.h>
+#include <linux/regmap.h>
+
+#define RT9455_MANUFACTURER                    "Richtek"
+#define RT9455_MODEL_NAME                      "RT9455"
+#define RT9455_DRIVER_NAME                     "rt9455-charger"
+
+#define RT9455_IRQ_NAME                                "interrupt"
+
+#define RT9455_PWR_RDY_DELAY                   1 /* 1 second */
+#define RT9455_MAX_CHARGING_TIME               21600 /* 6 hrs */
+#define RT9455_BATT_PRESENCE_DELAY             60 /* 60 seconds */
+
+#define RT9455_CHARGE_MODE                     0x00
+#define RT9455_BOOST_MODE                      0x01
+
+#define RT9455_FAULT                           0x03
+
+#define RT9455_IAICR_100MA                     0x00
+#define RT9455_IAICR_500MA                     0x01
+#define RT9455_IAICR_NO_LIMIT                  0x03
+
+#define RT9455_CHARGE_DISABLE                  0x00
+#define RT9455_CHARGE_ENABLE                   0x01
+
+#define RT9455_PWR_FAULT                       0x00
+#define RT9455_PWR_GOOD                                0x01
+
+#define RT9455_REG_CTRL1                       0x00 /* CTRL1 reg address */
+#define RT9455_REG_CTRL2                       0x01 /* CTRL2 reg address */
+#define RT9455_REG_CTRL3                       0x02 /* CTRL3 reg address */
+#define RT9455_REG_DEV_ID                      0x03 /* DEV_ID reg address */
+#define RT9455_REG_CTRL4                       0x04 /* CTRL4 reg address */
+#define RT9455_REG_CTRL5                       0x05 /* CTRL5 reg address */
+#define RT9455_REG_CTRL6                       0x06 /* CTRL6 reg address */
+#define RT9455_REG_CTRL7                       0x07 /* CTRL7 reg address */
+#define RT9455_REG_IRQ1                                0x08 /* IRQ1 reg address */
+#define RT9455_REG_IRQ2                                0x09 /* IRQ2 reg address */
+#define RT9455_REG_IRQ3                                0x0A /* IRQ3 reg address */
+#define RT9455_REG_MASK1                       0x0B /* MASK1 reg address */
+#define RT9455_REG_MASK2                       0x0C /* MASK2 reg address */
+#define RT9455_REG_MASK3                       0x0D /* MASK3 reg address */
+
+enum rt9455_fields {
+       F_STAT, F_BOOST, F_PWR_RDY, F_OTG_PIN_POLARITY, /* CTRL1 reg fields */
+
+       F_IAICR, F_TE_SHDN_EN, F_HIGHER_OCP, F_TE, F_IAICR_INT, F_HIZ,
+       F_OPA_MODE, /* CTRL2 reg fields */
+
+       F_VOREG, F_OTG_PL, F_OTG_EN, /* CTRL3 reg fields */
+
+       F_VENDOR_ID, F_CHIP_REV, /* DEV_ID reg fields */
+
+       F_RST, /* CTRL4 reg fields */
+
+       F_TMR_EN, F_MIVR, F_IPREC, F_IEOC_PERCENTAGE, /* CTRL5 reg fields*/
+
+       F_IAICR_SEL, F_ICHRG, F_VPREC, /* CTRL6 reg fields */
+
+       F_BATD_EN, F_CHG_EN, F_VMREG, /* CTRL7 reg fields */
+
+       F_TSDI, F_VINOVPI, F_BATAB, /* IRQ1 reg fields */
+
+       F_CHRVPI, F_CHBATOVI, F_CHTERMI, F_CHRCHGI, F_CH32MI, F_CHTREGI,
+       F_CHMIVRI, /* IRQ2 reg fields */
+
+       F_BSTBUSOVI, F_BSTOLI, F_BSTLOWVI, F_BST32SI, /* IRQ3 reg fields */
+
+       F_TSDM, F_VINOVPIM, F_BATABM, /* MASK1 reg fields */
+
+       F_CHRVPIM, F_CHBATOVIM, F_CHTERMIM, F_CHRCHGIM, F_CH32MIM, F_CHTREGIM,
+       F_CHMIVRIM, /* MASK2 reg fields */
+
+       F_BSTVINOVIM, F_BSTOLIM, F_BSTLOWVIM, F_BST32SIM, /* MASK3 reg fields */
+
+       F_MAX_FIELDS
+};
+
+static const struct reg_field rt9455_reg_fields[] = {
+       [F_STAT]                = REG_FIELD(RT9455_REG_CTRL1, 4, 5),
+       [F_BOOST]               = REG_FIELD(RT9455_REG_CTRL1, 3, 3),
+       [F_PWR_RDY]             = REG_FIELD(RT9455_REG_CTRL1, 2, 2),
+       [F_OTG_PIN_POLARITY]    = REG_FIELD(RT9455_REG_CTRL1, 1, 1),
+
+       [F_IAICR]               = REG_FIELD(RT9455_REG_CTRL2, 6, 7),
+       [F_TE_SHDN_EN]          = REG_FIELD(RT9455_REG_CTRL2, 5, 5),
+       [F_HIGHER_OCP]          = REG_FIELD(RT9455_REG_CTRL2, 4, 4),
+       [F_TE]                  = REG_FIELD(RT9455_REG_CTRL2, 3, 3),
+       [F_IAICR_INT]           = REG_FIELD(RT9455_REG_CTRL2, 2, 2),
+       [F_HIZ]                 = REG_FIELD(RT9455_REG_CTRL2, 1, 1),
+       [F_OPA_MODE]            = REG_FIELD(RT9455_REG_CTRL2, 0, 0),
+
+       [F_VOREG]               = REG_FIELD(RT9455_REG_CTRL3, 2, 7),
+       [F_OTG_PL]              = REG_FIELD(RT9455_REG_CTRL3, 1, 1),
+       [F_OTG_EN]              = REG_FIELD(RT9455_REG_CTRL3, 0, 0),
+
+       [F_VENDOR_ID]           = REG_FIELD(RT9455_REG_DEV_ID, 4, 7),
+       [F_CHIP_REV]            = REG_FIELD(RT9455_REG_DEV_ID, 0, 3),
+
+       [F_RST]                 = REG_FIELD(RT9455_REG_CTRL4, 7, 7),
+
+       [F_TMR_EN]              = REG_FIELD(RT9455_REG_CTRL5, 7, 7),
+       [F_MIVR]                = REG_FIELD(RT9455_REG_CTRL5, 4, 5),
+       [F_IPREC]               = REG_FIELD(RT9455_REG_CTRL5, 2, 3),
+       [F_IEOC_PERCENTAGE]     = REG_FIELD(RT9455_REG_CTRL5, 0, 1),
+
+       [F_IAICR_SEL]           = REG_FIELD(RT9455_REG_CTRL6, 7, 7),
+       [F_ICHRG]               = REG_FIELD(RT9455_REG_CTRL6, 4, 6),
+       [F_VPREC]               = REG_FIELD(RT9455_REG_CTRL6, 0, 2),
+
+       [F_BATD_EN]             = REG_FIELD(RT9455_REG_CTRL7, 6, 6),
+       [F_CHG_EN]              = REG_FIELD(RT9455_REG_CTRL7, 4, 4),
+       [F_VMREG]               = REG_FIELD(RT9455_REG_CTRL7, 0, 3),
+
+       [F_TSDI]                = REG_FIELD(RT9455_REG_IRQ1, 7, 7),
+       [F_VINOVPI]             = REG_FIELD(RT9455_REG_IRQ1, 6, 6),
+       [F_BATAB]               = REG_FIELD(RT9455_REG_IRQ1, 0, 0),
+
+       [F_CHRVPI]              = REG_FIELD(RT9455_REG_IRQ2, 7, 7),
+       [F_CHBATOVI]            = REG_FIELD(RT9455_REG_IRQ2, 5, 5),
+       [F_CHTERMI]             = REG_FIELD(RT9455_REG_IRQ2, 4, 4),
+       [F_CHRCHGI]             = REG_FIELD(RT9455_REG_IRQ2, 3, 3),
+       [F_CH32MI]              = REG_FIELD(RT9455_REG_IRQ2, 2, 2),
+       [F_CHTREGI]             = REG_FIELD(RT9455_REG_IRQ2, 1, 1),
+       [F_CHMIVRI]             = REG_FIELD(RT9455_REG_IRQ2, 0, 0),
+
+       [F_BSTBUSOVI]           = REG_FIELD(RT9455_REG_IRQ3, 7, 7),
+       [F_BSTOLI]              = REG_FIELD(RT9455_REG_IRQ3, 6, 6),
+       [F_BSTLOWVI]            = REG_FIELD(RT9455_REG_IRQ3, 5, 5),
+       [F_BST32SI]             = REG_FIELD(RT9455_REG_IRQ3, 3, 3),
+
+       [F_TSDM]                = REG_FIELD(RT9455_REG_MASK1, 7, 7),
+       [F_VINOVPIM]            = REG_FIELD(RT9455_REG_MASK1, 6, 6),
+       [F_BATABM]              = REG_FIELD(RT9455_REG_MASK1, 0, 0),
+
+       [F_CHRVPIM]             = REG_FIELD(RT9455_REG_MASK2, 7, 7),
+       [F_CHBATOVIM]           = REG_FIELD(RT9455_REG_MASK2, 5, 5),
+       [F_CHTERMIM]            = REG_FIELD(RT9455_REG_MASK2, 4, 4),
+       [F_CHRCHGIM]            = REG_FIELD(RT9455_REG_MASK2, 3, 3),
+       [F_CH32MIM]             = REG_FIELD(RT9455_REG_MASK2, 2, 2),
+       [F_CHTREGIM]            = REG_FIELD(RT9455_REG_MASK2, 1, 1),
+       [F_CHMIVRIM]            = REG_FIELD(RT9455_REG_MASK2, 0, 0),
+
+       [F_BSTVINOVIM]          = REG_FIELD(RT9455_REG_MASK3, 7, 7),
+       [F_BSTOLIM]             = REG_FIELD(RT9455_REG_MASK3, 6, 6),
+       [F_BSTLOWVIM]           = REG_FIELD(RT9455_REG_MASK3, 5, 5),
+       [F_BST32SIM]            = REG_FIELD(RT9455_REG_MASK3, 3, 3),
+};
+
+#define GET_MASK(fid)  (BIT(rt9455_reg_fields[fid].msb + 1) - \
+                        BIT(rt9455_reg_fields[fid].lsb))
+
+/*
+ * Each array initialised below shows the possible real-world values for a
+ * group of bits belonging to RT9455 registers. The arrays are sorted in
+ * ascending order. The index of each real-world value represents the value
+ * that is encoded in the group of bits belonging to RT9455 registers.
+ */
+/* REG06[6:4] (ICHRG) in uAh */
+static const int rt9455_ichrg_values[] = {
+        500000,  650000,  800000,  950000, 1100000, 1250000, 1400000, 1550000
+};
+
+/*
+ * When the charger is in charge mode, REG02[7:2] represent battery regulation
+ * voltage.
+ */
+/* REG02[7:2] (VOREG) in uV */
+static const int rt9455_voreg_values[] = {
+       3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000,
+       3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000,
+       3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000,
+       3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000,
+       4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000,
+       4300000, 4330000, 4350000, 4370000, 4390000, 4410000, 4430000, 4450000,
+       4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000,
+       4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000
+};
+
+/*
+ * When the charger is in boost mode, REG02[7:2] represent boost output
+ * voltage.
+ */
+/* REG02[7:2] (Boost output voltage) in uV */
+static const int rt9455_boost_voltage_values[] = {
+       4425000, 4450000, 4475000, 4500000, 4525000, 4550000, 4575000, 4600000,
+       4625000, 4650000, 4675000, 4700000, 4725000, 4750000, 4775000, 4800000,
+       4825000, 4850000, 4875000, 4900000, 4925000, 4950000, 4975000, 5000000,
+       5025000, 5050000, 5075000, 5100000, 5125000, 5150000, 5175000, 5200000,
+       5225000, 5250000, 5275000, 5300000, 5325000, 5350000, 5375000, 5400000,
+       5425000, 5450000, 5475000, 5500000, 5525000, 5550000, 5575000, 5600000,
+       5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000,
+       5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000,
+};
+
+/* REG07[3:0] (VMREG) in uV */
+static const int rt9455_vmreg_values[] = {
+       4200000, 4220000, 4240000, 4260000, 4280000, 4300000, 4320000, 4340000,
+       4360000, 4380000, 4400000, 4430000, 4450000, 4450000, 4450000, 4450000
+};
+
+/* REG05[5:4] (IEOC_PERCENTAGE) */
+static const int rt9455_ieoc_percentage_values[] = {
+       10, 30, 20, 30
+};
+
+/* REG05[1:0] (MIVR) in uV */
+static const int rt9455_mivr_values[] = {
+       4000000, 4250000, 4500000, 5000000
+};
+
+/* REG05[1:0] (IAICR) in uA */
+static const int rt9455_iaicr_values[] = {
+       100000, 500000, 1000000, 2000000
+};
+
+struct rt9455_info {
+       struct i2c_client               *client;
+       struct regmap                   *regmap;
+       struct regmap_field             *regmap_fields[F_MAX_FIELDS];
+       struct power_supply             *charger;
+#if IS_ENABLED(CONFIG_USB_PHY)
+       struct usb_phy                  *usb_phy;
+       struct notifier_block           nb;
+#endif
+       struct delayed_work             pwr_rdy_work;
+       struct delayed_work             max_charging_time_work;
+       struct delayed_work             batt_presence_work;
+       u32                             voreg;
+       u32                             boost_voltage;
+};
+
+/*
+ * Iterate through each element of the 'tbl' array until an element whose value
+ * is greater than v is found. Return the index of the respective element,
+ * or the index of the last element in the array, if no such element is found.
+ */
+static unsigned int rt9455_find_idx(const int tbl[], int tbl_size, int v)
+{
+       int i;
+
+       /*
+        * No need to iterate until the last index in the table because
+        * if no element greater than v is found in the table,
+        * or if only the last element is greater than v,
+        * function returns the index of the last element.
+        */
+       for (i = 0; i < tbl_size - 1; i++)
+               if (v <= tbl[i])
+                       return i;
+
+       return (tbl_size - 1);
+}
+
+static int rt9455_get_field_val(struct rt9455_info *info,
+                               enum rt9455_fields field,
+                               const int tbl[], int tbl_size, int *val)
+{
+       unsigned int v;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[field], &v);
+       if (ret)
+               return ret;
+
+       v = (v >= tbl_size) ? (tbl_size - 1) : v;
+       *val = tbl[v];
+
+       return 0;
+}
+
+static int rt9455_set_field_val(struct rt9455_info *info,
+                               enum rt9455_fields field,
+                               const int tbl[], int tbl_size, int val)
+{
+       unsigned int idx = rt9455_find_idx(tbl, tbl_size, val);
+
+       return regmap_field_write(info->regmap_fields[field], idx);
+}
+
+static int rt9455_register_reset(struct rt9455_info *info)
+{
+       struct device *dev = &info->client->dev;
+       unsigned int v;
+       int ret, limit = 100;
+
+       ret = regmap_field_write(info->regmap_fields[F_RST], 0x01);
+       if (ret) {
+               dev_err(dev, "Failed to set RST bit\n");
+               return ret;
+       }
+
+       /*
+        * To make sure that reset operation has finished, loop until RST bit
+        * is set to 0.
+        */
+       do {
+               ret = regmap_field_read(info->regmap_fields[F_RST], &v);
+               if (ret) {
+                       dev_err(dev, "Failed to read RST bit\n");
+                       return ret;
+               }
+
+               if (!v)
+                       break;
+
+               usleep_range(10, 100);
+       } while (--limit);
+
+       if (!limit)
+               return -EIO;
+
+       return 0;
+}
+
+/* Charger power supply property routines */
+static enum power_supply_property rt9455_charger_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_SCOPE,
+       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static char *rt9455_charger_supplied_to[] = {
+       "main-battery",
+};
+
+static int rt9455_charger_get_status(struct rt9455_info *info,
+                                    union power_supply_propval *val)
+{
+       unsigned int v, pwr_rdy;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY],
+                               &pwr_rdy);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read PWR_RDY bit\n");
+               return ret;
+       }
+
+       /*
+        * If PWR_RDY bit is unset, the battery is discharging. Otherwise,
+        * STAT bits value must be checked.
+        */
+       if (!pwr_rdy) {
+               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               return 0;
+       }
+
+       ret = regmap_field_read(info->regmap_fields[F_STAT], &v);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read STAT bits\n");
+               return ret;
+       }
+
+       switch (v) {
+       case 0:
+               /*
+                * If PWR_RDY bit is set, but STAT bits value is 0, the charger
+                * may be in one of the following cases:
+                * 1. CHG_EN bit is 0.
+                * 2. CHG_EN bit is 1 but the battery is not connected.
+                * In any of these cases, POWER_SUPPLY_STATUS_NOT_CHARGING is
+                * returned.
+                */
+               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               return 0;
+       case 1:
+               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               return 0;
+       case 2:
+               val->intval = POWER_SUPPLY_STATUS_FULL;
+               return 0;
+       default:
+               val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+               return 0;
+       }
+}
+
+static int rt9455_charger_get_health(struct rt9455_info *info,
+                                    union power_supply_propval *val)
+{
+       struct device *dev = &info->client->dev;
+       unsigned int v;
+       int ret;
+
+       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &v);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ1 register\n");
+               return ret;
+       }
+
+       if (v & GET_MASK(F_TSDI)) {
+               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               return 0;
+       }
+       if (v & GET_MASK(F_VINOVPI)) {
+               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               return 0;
+       }
+       if (v & GET_MASK(F_BATAB)) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ2, &v);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ2 register\n");
+               return ret;
+       }
+
+       if (v & GET_MASK(F_CHBATOVI)) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+       if (v & GET_MASK(F_CH32MI)) {
+               val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+               return 0;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ3, &v);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ3 register\n");
+               return ret;
+       }
+
+       if (v & GET_MASK(F_BSTBUSOVI)) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+       if (v & GET_MASK(F_BSTOLI)) {
+               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               return 0;
+       }
+       if (v & GET_MASK(F_BSTLOWVI)) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+       if (v & GET_MASK(F_BST32SI)) {
+               val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+               return 0;
+       }
+
+       ret = regmap_field_read(info->regmap_fields[F_STAT], &v);
+       if (ret) {
+               dev_err(dev, "Failed to read STAT bits\n");
+               return ret;
+       }
+
+       if (v == RT9455_FAULT) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt9455_charger_get_battery_presence(struct rt9455_info *info,
+                                              union power_supply_propval *val)
+{
+       unsigned int v;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[F_BATAB], &v);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read BATAB bit\n");
+               return ret;
+       }
+
+       /*
+        * Since BATAB is 1 when battery is NOT present and 0 otherwise,
+        * !BATAB is returned.
+        */
+       val->intval = !v;
+
+       return 0;
+}
+
+static int rt9455_charger_get_online(struct rt9455_info *info,
+                                    union power_supply_propval *val)
+{
+       unsigned int v;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY], &v);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read PWR_RDY bit\n");
+               return ret;
+       }
+
+       val->intval = (int)v;
+
+       return 0;
+}
+
+static int rt9455_charger_get_current(struct rt9455_info *info,
+                                     union power_supply_propval *val)
+{
+       int curr;
+       int ret;
+
+       ret = rt9455_get_field_val(info, F_ICHRG,
+                                  rt9455_ichrg_values,
+                                  ARRAY_SIZE(rt9455_ichrg_values),
+                                  &curr);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read ICHRG value\n");
+               return ret;
+       }
+
+       val->intval = curr;
+
+       return 0;
+}
+
+static int rt9455_charger_get_current_max(struct rt9455_info *info,
+                                         union power_supply_propval *val)
+{
+       int idx = ARRAY_SIZE(rt9455_ichrg_values) - 1;
+
+       val->intval = rt9455_ichrg_values[idx];
+
+       return 0;
+}
+
+static int rt9455_charger_get_voltage(struct rt9455_info *info,
+                                     union power_supply_propval *val)
+{
+       int voltage;
+       int ret;
+
+       ret = rt9455_get_field_val(info, F_VOREG,
+                                  rt9455_voreg_values,
+                                  ARRAY_SIZE(rt9455_voreg_values),
+                                  &voltage);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read VOREG value\n");
+               return ret;
+       }
+
+       val->intval = voltage;
+
+       return 0;
+}
+
+static int rt9455_charger_get_voltage_max(struct rt9455_info *info,
+                                         union power_supply_propval *val)
+{
+       int idx = ARRAY_SIZE(rt9455_vmreg_values) - 1;
+
+       val->intval = rt9455_vmreg_values[idx];
+
+       return 0;
+}
+
+static int rt9455_charger_get_term_current(struct rt9455_info *info,
+                                          union power_supply_propval *val)
+{
+       struct device *dev = &info->client->dev;
+       int ichrg, ieoc_percentage, ret;
+
+       ret = rt9455_get_field_val(info, F_ICHRG,
+                                  rt9455_ichrg_values,
+                                  ARRAY_SIZE(rt9455_ichrg_values),
+                                  &ichrg);
+       if (ret) {
+               dev_err(dev, "Failed to read ICHRG value\n");
+               return ret;
+       }
+
+       ret = rt9455_get_field_val(info, F_IEOC_PERCENTAGE,
+                                  rt9455_ieoc_percentage_values,
+                                  ARRAY_SIZE(rt9455_ieoc_percentage_values),
+                                  &ieoc_percentage);
+       if (ret) {
+               dev_err(dev, "Failed to read IEOC value\n");
+               return ret;
+       }
+
+       val->intval = ichrg * ieoc_percentage / 100;
+
+       return 0;
+}
+
+static int rt9455_charger_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct rt9455_info *info = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               return rt9455_charger_get_status(info, val);
+       case POWER_SUPPLY_PROP_HEALTH:
+               return rt9455_charger_get_health(info, val);
+       case POWER_SUPPLY_PROP_PRESENT:
+               return rt9455_charger_get_battery_presence(info, val);
+       case POWER_SUPPLY_PROP_ONLINE:
+               return rt9455_charger_get_online(info, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               return rt9455_charger_get_current(info, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               return rt9455_charger_get_current_max(info, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               return rt9455_charger_get_voltage(info, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               return rt9455_charger_get_voltage_max(info, val);
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+               return 0;
+       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+               return rt9455_charger_get_term_current(info, val);
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = RT9455_MODEL_NAME;
+               return 0;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = RT9455_MANUFACTURER;
+               return 0;
+       default:
+               return -ENODATA;
+       }
+}
+
+static int rt9455_hw_init(struct rt9455_info *info, u32 ichrg,
+                         u32 ieoc_percentage,
+                         u32 mivr, u32 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int idx, ret;
+
+       ret = rt9455_register_reset(info);
+       if (ret) {
+               dev_err(dev, "Power On Reset failed\n");
+               return ret;
+       }
+
+       /* Set TE bit in order to enable end of charge detection */
+       ret = regmap_field_write(info->regmap_fields[F_TE], 1);
+       if (ret) {
+               dev_err(dev, "Failed to set TE bit\n");
+               return ret;
+       }
+
+       /* Set TE_SHDN_EN bit in order to enable end of charge detection */
+       ret = regmap_field_write(info->regmap_fields[F_TE_SHDN_EN], 1);
+       if (ret) {
+               dev_err(dev, "Failed to set TE_SHDN_EN bit\n");
+               return ret;
+       }
+
+       /*
+        * Set BATD_EN bit in order to enable battery detection
+        * when charging is done
+        */
+       ret = regmap_field_write(info->regmap_fields[F_BATD_EN], 1);
+       if (ret) {
+               dev_err(dev, "Failed to set BATD_EN bit\n");
+               return ret;
+       }
+
+       /*
+        * Disable Safety Timer. In charge mode, this timer terminates charging
+        * if no read or write via I2C is done within 32 minutes. This timer
+        * avoids overcharging the baterry when the OS is not loaded and the
+        * charger is connected to a power source.
+        * In boost mode, this timer triggers BST32SI interrupt if no read or
+        * write via I2C is done within 32 seconds.
+        * When the OS is loaded and the charger driver is inserted, it is used
+        * delayed_work, named max_charging_time_work, to avoid overcharging
+        * the battery.
+        */
+       ret = regmap_field_write(info->regmap_fields[F_TMR_EN], 0x00);
+       if (ret) {
+               dev_err(dev, "Failed to disable Safety Timer\n");
+               return ret;
+       }
+
+       /* Set ICHRG to value retrieved from device-specific data */
+       ret = rt9455_set_field_val(info, F_ICHRG,
+                                  rt9455_ichrg_values,
+                                  ARRAY_SIZE(rt9455_ichrg_values), ichrg);
+       if (ret) {
+               dev_err(dev, "Failed to set ICHRG value\n");
+               return ret;
+       }
+
+       /* Set IEOC Percentage to value retrieved from device-specific data */
+       ret = rt9455_set_field_val(info, F_IEOC_PERCENTAGE,
+                                  rt9455_ieoc_percentage_values,
+                                  ARRAY_SIZE(rt9455_ieoc_percentage_values),
+                                  ieoc_percentage);
+       if (ret) {
+               dev_err(dev, "Failed to set IEOC Percentage value\n");
+               return ret;
+       }
+
+       /* Set VOREG to value retrieved from device-specific data */
+       ret = rt9455_set_field_val(info, F_VOREG,
+                                  rt9455_voreg_values,
+                                  ARRAY_SIZE(rt9455_voreg_values),
+                                  info->voreg);
+       if (ret) {
+               dev_err(dev, "Failed to set VOREG value\n");
+               return ret;
+       }
+
+       /* Set VMREG value to maximum (4.45V). */
+       idx = ARRAY_SIZE(rt9455_vmreg_values) - 1;
+       ret = rt9455_set_field_val(info, F_VMREG,
+                                  rt9455_vmreg_values,
+                                  ARRAY_SIZE(rt9455_vmreg_values),
+                                  rt9455_vmreg_values[idx]);
+       if (ret) {
+               dev_err(dev, "Failed to set VMREG value\n");
+               return ret;
+       }
+
+       /*
+        * Set MIVR to value retrieved from device-specific data.
+        * If no value is specified, default value for MIVR is 4.5V.
+        */
+       if (mivr == -1)
+               mivr = 4500000;
+
+       ret = rt9455_set_field_val(info, F_MIVR,
+                                  rt9455_mivr_values,
+                                  ARRAY_SIZE(rt9455_mivr_values), mivr);
+       if (ret) {
+               dev_err(dev, "Failed to set MIVR value\n");
+               return ret;
+       }
+
+       /*
+        * Set IAICR to value retrieved from device-specific data.
+        * If no value is specified, default value for IAICR is 500 mA.
+        */
+       if (iaicr == -1)
+               iaicr = 500000;
+
+       ret = rt9455_set_field_val(info, F_IAICR,
+                                  rt9455_iaicr_values,
+                                  ARRAY_SIZE(rt9455_iaicr_values), iaicr);
+       if (ret) {
+               dev_err(dev, "Failed to set IAICR value\n");
+               return ret;
+       }
+
+       /*
+        * Set IAICR_INT bit so that IAICR value is determined by IAICR bits
+        * and not by OTG pin.
+        */
+       ret = regmap_field_write(info->regmap_fields[F_IAICR_INT], 0x01);
+       if (ret) {
+               dev_err(dev, "Failed to set IAICR_INT bit\n");
+               return ret;
+       }
+
+       /*
+        * Disable CHMIVRI interrupt. Because the driver sets MIVR value,
+        * CHMIVRI is triggered, but there is no action to be taken by the
+        * driver when CHMIVRI is triggered.
+        */
+       ret = regmap_field_write(info->regmap_fields[F_CHMIVRIM], 0x01);
+       if (ret) {
+               dev_err(dev, "Failed to mask CHMIVRI interrupt\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+/*
+ * Before setting the charger into boost mode, boost output voltage is
+ * set. This is needed because boost output voltage may differ from battery
+ * regulation voltage. F_VOREG bits represent either battery regulation voltage
+ * or boost output voltage, depending on the mode the charger is. Both battery
+ * regulation voltage and boost output voltage are read from DT/ACPI during
+ * probe.
+ */
+static int rt9455_set_boost_voltage_before_boost_mode(struct rt9455_info *info)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       ret = rt9455_set_field_val(info, F_VOREG,
+                                  rt9455_boost_voltage_values,
+                                  ARRAY_SIZE(rt9455_boost_voltage_values),
+                                  info->boost_voltage);
+       if (ret) {
+               dev_err(dev, "Failed to set boost output voltage value\n");
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+/*
+ * Before setting the charger into charge mode, battery regulation voltage is
+ * set. This is needed because boost output voltage may differ from battery
+ * regulation voltage. F_VOREG bits represent either battery regulation voltage
+ * or boost output voltage, depending on the mode the charger is. Both battery
+ * regulation voltage and boost output voltage are read from DT/ACPI during
+ * probe.
+ */
+static int rt9455_set_voreg_before_charge_mode(struct rt9455_info *info)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       ret = rt9455_set_field_val(info, F_VOREG,
+                                  rt9455_voreg_values,
+                                  ARRAY_SIZE(rt9455_voreg_values),
+                                  info->voreg);
+       if (ret) {
+               dev_err(dev, "Failed to set VOREG value\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rt9455_irq_handler_check_irq1_register(struct rt9455_info *info,
+                                                 bool *_is_battery_absent,
+                                                 bool *_alert_userspace)
+{
+       unsigned int irq1, mask1, mask2;
+       struct device *dev = &info->client->dev;
+       bool is_battery_absent = false;
+       bool alert_userspace = false;
+       int ret;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &irq1);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ1 register\n");
+               return ret;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_MASK1, &mask1);
+       if (ret) {
+               dev_err(dev, "Failed to read MASK1 register\n");
+               return ret;
+       }
+
+       if (irq1 & GET_MASK(F_TSDI)) {
+               dev_err(dev, "Thermal shutdown fault occurred\n");
+               alert_userspace = true;
+       }
+
+       if (irq1 & GET_MASK(F_VINOVPI)) {
+               dev_err(dev, "Overvoltage input occurred\n");
+               alert_userspace = true;
+       }
+
+       if (irq1 & GET_MASK(F_BATAB)) {
+               dev_err(dev, "Battery absence occurred\n");
+               is_battery_absent = true;
+               alert_userspace = true;
+
+               if ((mask1 & GET_MASK(F_BATABM)) == 0) {
+                       ret = regmap_field_write(info->regmap_fields[F_BATABM],
+                                                0x01);
+                       if (ret) {
+                               dev_err(dev, "Failed to mask BATAB interrupt\n");
+                               return ret;
+                       }
+               }
+
+               ret = regmap_read(info->regmap, RT9455_REG_MASK2, &mask2);
+               if (ret) {
+                       dev_err(dev, "Failed to read MASK2 register\n");
+                       return ret;
+               }
+
+               if (mask2 & GET_MASK(F_CHTERMIM)) {
+                       ret = regmap_field_write(
+                               info->regmap_fields[F_CHTERMIM], 0x00);
+                       if (ret) {
+                               dev_err(dev, "Failed to unmask CHTERMI interrupt\n");
+                               return ret;
+                       }
+               }
+
+               if (mask2 & GET_MASK(F_CHRCHGIM)) {
+                       ret = regmap_field_write(
+                               info->regmap_fields[F_CHRCHGIM], 0x00);
+                       if (ret) {
+                               dev_err(dev, "Failed to unmask CHRCHGI interrupt\n");
+                               return ret;
+                       }
+               }
+
+               /*
+                * When the battery is absent, max_charging_time_work is
+                * cancelled, since no charging is done.
+                */
+               cancel_delayed_work_sync(&info->max_charging_time_work);
+               /*
+                * Since no interrupt is triggered when the battery is
+                * reconnected, max_charging_time_work is not rescheduled.
+                * Therefore, batt_presence_work is scheduled to check whether
+                * the battery is still absent or not.
+                */
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->batt_presence_work,
+                                  RT9455_BATT_PRESENCE_DELAY * HZ);
+       }
+
+       *_is_battery_absent = is_battery_absent;
+
+       if (alert_userspace)
+               *_alert_userspace = alert_userspace;
+
+       return 0;
+}
+
+static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
+                                                 bool is_battery_absent,
+                                                 bool *_alert_userspace)
+{
+       unsigned int irq2, mask2;
+       struct device *dev = &info->client->dev;
+       bool alert_userspace = false;
+       int ret;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ2, &irq2);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ2 register\n");
+               return ret;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_MASK2, &mask2);
+       if (ret) {
+               dev_err(dev, "Failed to read MASK2 register\n");
+               return ret;
+       }
+
+       if (irq2 & GET_MASK(F_CHRVPI)) {
+               dev_dbg(dev, "Charger fault occurred\n");
+               /*
+                * CHRVPI bit is set in 2 cases:
+                * 1. when the power source is connected to the charger.
+                * 2. when the power source is disconnected from the charger.
+                * To identify the case, PWR_RDY bit is checked. Because
+                * PWR_RDY bit is set / cleared after CHRVPI interrupt is
+                * triggered, it is used delayed_work to later read PWR_RDY bit.
+                * Also, do not set to true alert_userspace, because there is no
+                * need to notify userspace when CHRVPI interrupt has occurred.
+                * Userspace will be notified after PWR_RDY bit is read.
+                */
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->pwr_rdy_work,
+                                  RT9455_PWR_RDY_DELAY * HZ);
+       }
+       if (irq2 & GET_MASK(F_CHBATOVI)) {
+               dev_err(dev, "Battery OVP occurred\n");
+               alert_userspace = true;
+       }
+       if (irq2 & GET_MASK(F_CHTERMI)) {
+               dev_dbg(dev, "Charge terminated\n");
+               if (!is_battery_absent) {
+                       if ((mask2 & GET_MASK(F_CHTERMIM)) == 0) {
+                               ret = regmap_field_write(
+                                       info->regmap_fields[F_CHTERMIM], 0x01);
+                               if (ret) {
+                                       dev_err(dev, "Failed to mask CHTERMI interrupt\n");
+                                       return ret;
+                               }
+                               /*
+                                * Update MASK2 value, since CHTERMIM bit is
+                                * set.
+                                */
+                               mask2 = mask2 | GET_MASK(F_CHTERMIM);
+                       }
+                       cancel_delayed_work_sync(&info->max_charging_time_work);
+                       alert_userspace = true;
+               }
+       }
+       if (irq2 & GET_MASK(F_CHRCHGI)) {
+               dev_dbg(dev, "Recharge request\n");
+               ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+                                        RT9455_CHARGE_ENABLE);
+               if (ret) {
+                       dev_err(dev, "Failed to enable charging\n");
+                       return ret;
+               }
+               if (mask2 & GET_MASK(F_CHTERMIM)) {
+                       ret = regmap_field_write(
+                               info->regmap_fields[F_CHTERMIM], 0x00);
+                       if (ret) {
+                               dev_err(dev, "Failed to unmask CHTERMI interrupt\n");
+                               return ret;
+                       }
+                       /* Update MASK2 value, since CHTERMIM bit is cleared. */
+                       mask2 = mask2 & ~GET_MASK(F_CHTERMIM);
+               }
+               if (!is_battery_absent) {
+                       /*
+                        * No need to check whether the charger is connected to
+                        * power source when CHRCHGI is received, since CHRCHGI
+                        * is not triggered if the charger is not connected to
+                        * the power source.
+                        */
+                       queue_delayed_work(system_power_efficient_wq,
+                                          &info->max_charging_time_work,
+                                          RT9455_MAX_CHARGING_TIME * HZ);
+                       alert_userspace = true;
+               }
+       }
+       if (irq2 & GET_MASK(F_CH32MI)) {
+               dev_err(dev, "Charger fault. 32 mins timeout occurred\n");
+               alert_userspace = true;
+       }
+       if (irq2 & GET_MASK(F_CHTREGI)) {
+               dev_warn(dev,
+                        "Charger warning. Thermal regulation loop active\n");
+               alert_userspace = true;
+       }
+       if (irq2 & GET_MASK(F_CHMIVRI)) {
+               dev_dbg(dev,
+                       "Charger warning. Input voltage MIVR loop active\n");
+       }
+
+       if (alert_userspace)
+               *_alert_userspace = alert_userspace;
+
+       return 0;
+}
+
+static int rt9455_irq_handler_check_irq3_register(struct rt9455_info *info,
+                                                 bool *_alert_userspace)
+{
+       unsigned int irq3, mask3;
+       struct device *dev = &info->client->dev;
+       bool alert_userspace = false;
+       int ret;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ3, &irq3);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ3 register\n");
+               return ret;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_MASK3, &mask3);
+       if (ret) {
+               dev_err(dev, "Failed to read MASK3 register\n");
+               return ret;
+       }
+
+       if (irq3 & GET_MASK(F_BSTBUSOVI)) {
+               dev_err(dev, "Boost fault. Overvoltage input occurred\n");
+               alert_userspace = true;
+       }
+       if (irq3 & GET_MASK(F_BSTOLI)) {
+               dev_err(dev, "Boost fault. Overload\n");
+               alert_userspace = true;
+       }
+       if (irq3 & GET_MASK(F_BSTLOWVI)) {
+               dev_err(dev, "Boost fault. Battery voltage too low\n");
+               alert_userspace = true;
+       }
+       if (irq3 & GET_MASK(F_BST32SI)) {
+               dev_err(dev, "Boost fault. 32 seconds timeout occurred.\n");
+               alert_userspace = true;
+       }
+
+       if (alert_userspace) {
+               dev_info(dev, "Boost fault occurred, therefore the charger goes into charge mode\n");
+               ret = rt9455_set_voreg_before_charge_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+                       return ret;
+               }
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_CHARGE_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in charge mode\n");
+                       return ret;
+               }
+               *_alert_userspace = alert_userspace;
+       }
+
+       return 0;
+}
+
+static irqreturn_t rt9455_irq_handler_thread(int irq, void *data)
+{
+       struct rt9455_info *info = data;
+       struct device *dev;
+       bool alert_userspace = false;
+       bool is_battery_absent = false;
+       unsigned int status;
+       int ret;
+
+       if (!info)
+               return IRQ_NONE;
+
+       dev = &info->client->dev;
+
+       if (irq != info->client->irq) {
+               dev_err(dev, "Interrupt is not for RT9455 charger\n");
+               return IRQ_NONE;
+       }
+
+       ret = regmap_field_read(info->regmap_fields[F_STAT], &status);
+       if (ret) {
+               dev_err(dev, "Failed to read STAT bits\n");
+               return IRQ_HANDLED;
+       }
+       dev_dbg(dev, "Charger status is %d\n", status);
+
+       /*
+        * Each function that processes an IRQ register receives as output
+        * parameter alert_userspace pointer. alert_userspace is set to true
+        * in such a function only if an interrupt has occurred in the
+        * respective interrupt register. This way, it is avoided the following
+        * case: interrupt occurs only in IRQ1 register,
+        * rt9455_irq_handler_check_irq1_register() function sets to true
+        * alert_userspace, but rt9455_irq_handler_check_irq2_register()
+        * and rt9455_irq_handler_check_irq3_register() functions set to false
+        * alert_userspace and power_supply_changed() is never called.
+        */
+       ret = rt9455_irq_handler_check_irq1_register(info, &is_battery_absent,
+                                                    &alert_userspace);
+       if (ret) {
+               dev_err(dev, "Failed to handle IRQ1 register\n");
+               return IRQ_HANDLED;
+       }
+
+       ret = rt9455_irq_handler_check_irq2_register(info, is_battery_absent,
+                                                    &alert_userspace);
+       if (ret) {
+               dev_err(dev, "Failed to handle IRQ2 register\n");
+               return IRQ_HANDLED;
+       }
+
+       ret = rt9455_irq_handler_check_irq3_register(info, &alert_userspace);
+       if (ret) {
+               dev_err(dev, "Failed to handle IRQ3 register\n");
+               return IRQ_HANDLED;
+       }
+
+       if (alert_userspace) {
+               /*
+                * Sometimes, an interrupt occurs while rt9455_probe() function
+                * is executing and power_supply_register() is not yet called.
+                * Do not call power_supply_changed() in this case.
+                */
+               if (info->charger)
+                       power_supply_changed(info->charger);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int rt9455_discover_charger(struct rt9455_info *info, u32 *ichrg,
+                                  u32 *ieoc_percentage,
+                                  u32 *mivr, u32 *iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (!dev->of_node && !ACPI_HANDLE(dev)) {
+               dev_err(dev, "No support for either device tree or ACPI\n");
+               return -EINVAL;
+       }
+       /*
+        * ICHRG, IEOC_PERCENTAGE, VOREG and boost output voltage are mandatory
+        * parameters.
+        */
+       ret = device_property_read_u32(dev, "richtek,output-charge-current",
+                                      ichrg);
+       if (ret) {
+               dev_err(dev, "Error: missing \"output-charge-current\" property\n");
+               return ret;
+       }
+
+       ret = device_property_read_u32(dev, "richtek,end-of-charge-percentage",
+                                      ieoc_percentage);
+       if (ret) {
+               dev_err(dev, "Error: missing \"end-of-charge-percentage\" property\n");
+               return ret;
+       }
+
+       ret = device_property_read_u32(dev,
+                                      "richtek,battery-regulation-voltage",
+                                      &info->voreg);
+       if (ret) {
+               dev_err(dev, "Error: missing \"battery-regulation-voltage\" property\n");
+               return ret;
+       }
+
+       ret = device_property_read_u32(dev, "richtek,boost-output-voltage",
+                                      &info->boost_voltage);
+       if (ret) {
+               dev_err(dev, "Error: missing \"boost-output-voltage\" property\n");
+               return ret;
+       }
+
+       /*
+        * MIVR and IAICR are optional parameters. Do not return error if one of
+        * them is not present in ACPI table or device tree specification.
+        */
+       device_property_read_u32(dev, "richtek,min-input-voltage-regulation",
+                                mivr);
+       device_property_read_u32(dev, "richtek,avg-input-current-regulation",
+                                iaicr);
+
+       return 0;
+}
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+static int rt9455_usb_event_none(struct rt9455_info *info,
+                                u8 opa_mode, u8 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (opa_mode == RT9455_BOOST_MODE) {
+               ret = rt9455_set_voreg_before_charge_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+                       return ret;
+               }
+               /*
+                * If the charger is in boost mode, and it has received
+                * USB_EVENT_NONE, this means the consumer device powered by the
+                * charger is not connected anymore.
+                * In this case, the charger goes into charge mode.
+                */
+               dev_dbg(dev, "USB_EVENT_NONE received, therefore the charger goes into charge mode\n");
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_CHARGE_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in charge mode\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       dev_dbg(dev, "USB_EVENT_NONE received, therefore IAICR is set to its minimum value\n");
+       if (iaicr != RT9455_IAICR_100MA) {
+               ret = regmap_field_write(info->regmap_fields[F_IAICR],
+                                        RT9455_IAICR_100MA);
+               if (ret) {
+                       dev_err(dev, "Failed to set IAICR value\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_vbus(struct rt9455_info *info,
+                                u8 opa_mode, u8 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (opa_mode == RT9455_BOOST_MODE) {
+               ret = rt9455_set_voreg_before_charge_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+                       return ret;
+               }
+               /*
+                * If the charger is in boost mode, and it has received
+                * USB_EVENT_VBUS, this means the consumer device powered by the
+                * charger is not connected anymore.
+                * In this case, the charger goes into charge mode.
+                */
+               dev_dbg(dev, "USB_EVENT_VBUS received, therefore the charger goes into charge mode\n");
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_CHARGE_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in charge mode\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       dev_dbg(dev, "USB_EVENT_VBUS received, therefore IAICR is set to 500 mA\n");
+       if (iaicr != RT9455_IAICR_500MA) {
+               ret = regmap_field_write(info->regmap_fields[F_IAICR],
+                                        RT9455_IAICR_500MA);
+               if (ret) {
+                       dev_err(dev, "Failed to set IAICR value\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_id(struct rt9455_info *info,
+                              u8 opa_mode, u8 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (opa_mode == RT9455_CHARGE_MODE) {
+               ret = rt9455_set_boost_voltage_before_boost_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set boost output voltage before entering boost mode\n");
+                       return ret;
+               }
+               /*
+                * If the charger is in charge mode, and it has received
+                * USB_EVENT_ID, this means a consumer device is connected and
+                * it should be powered by the charger.
+                * In this case, the charger goes into boost mode.
+                */
+               dev_dbg(dev, "USB_EVENT_ID received, therefore the charger goes into boost mode\n");
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_BOOST_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in boost mode\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       dev_dbg(dev, "USB_EVENT_ID received, therefore IAICR is set to its minimum value\n");
+       if (iaicr != RT9455_IAICR_100MA) {
+               ret = regmap_field_write(info->regmap_fields[F_IAICR],
+                                        RT9455_IAICR_100MA);
+               if (ret) {
+                       dev_err(dev, "Failed to set IAICR value\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_charger(struct rt9455_info *info,
+                                   u8 opa_mode, u8 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (opa_mode == RT9455_BOOST_MODE) {
+               ret = rt9455_set_voreg_before_charge_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+                       return ret;
+               }
+               /*
+                * If the charger is in boost mode, and it has received
+                * USB_EVENT_CHARGER, this means the consumer device powered by
+                * the charger is not connected anymore.
+                * In this case, the charger goes into charge mode.
+                */
+               dev_dbg(dev, "USB_EVENT_CHARGER received, therefore the charger goes into charge mode\n");
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_CHARGE_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in charge mode\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       dev_dbg(dev, "USB_EVENT_CHARGER received, therefore IAICR is set to no current limit\n");
+       if (iaicr != RT9455_IAICR_NO_LIMIT) {
+               ret = regmap_field_write(info->regmap_fields[F_IAICR],
+                                        RT9455_IAICR_NO_LIMIT);
+               if (ret) {
+                       dev_err(dev, "Failed to set IAICR value\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int rt9455_usb_event(struct notifier_block *nb,
+                           unsigned long event, void *power)
+{
+       struct rt9455_info *info = container_of(nb, struct rt9455_info, nb);
+       struct device *dev = &info->client->dev;
+       unsigned int opa_mode, iaicr;
+       int ret;
+
+       /*
+        * Determine whether the charger is in charge mode
+        * or in boost mode.
+        */
+       ret = regmap_field_read(info->regmap_fields[F_OPA_MODE],
+                               &opa_mode);
+       if (ret) {
+               dev_err(dev, "Failed to read OPA_MODE value\n");
+               return NOTIFY_DONE;
+       }
+
+       ret = regmap_field_read(info->regmap_fields[F_IAICR],
+                               &iaicr);
+       if (ret) {
+               dev_err(dev, "Failed to read IAICR value\n");
+               return NOTIFY_DONE;
+       }
+
+       dev_dbg(dev, "Received USB event %lu\n", event);
+       switch (event) {
+       case USB_EVENT_NONE:
+               return rt9455_usb_event_none(info, opa_mode, iaicr);
+       case USB_EVENT_VBUS:
+               return rt9455_usb_event_vbus(info, opa_mode, iaicr);
+       case USB_EVENT_ID:
+               return rt9455_usb_event_id(info, opa_mode, iaicr);
+       case USB_EVENT_CHARGER:
+               return rt9455_usb_event_charger(info, opa_mode, iaicr);
+       default:
+               dev_err(dev, "Unknown USB event\n");
+       }
+       return NOTIFY_DONE;
+}
+#endif
+
+static void rt9455_pwr_rdy_work_callback(struct work_struct *work)
+{
+       struct rt9455_info *info = container_of(work, struct rt9455_info,
+                                               pwr_rdy_work.work);
+       struct device *dev = &info->client->dev;
+       unsigned int pwr_rdy;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY], &pwr_rdy);
+       if (ret) {
+               dev_err(dev, "Failed to read PWR_RDY bit\n");
+               return;
+       }
+       switch (pwr_rdy) {
+       case RT9455_PWR_FAULT:
+               dev_dbg(dev, "Charger disconnected from power source\n");
+               cancel_delayed_work_sync(&info->max_charging_time_work);
+               break;
+       case RT9455_PWR_GOOD:
+               dev_dbg(dev, "Charger connected to power source\n");
+               ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+                                        RT9455_CHARGE_ENABLE);
+               if (ret) {
+                       dev_err(dev, "Failed to enable charging\n");
+                       return;
+               }
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->max_charging_time_work,
+                                  RT9455_MAX_CHARGING_TIME * HZ);
+               break;
+       }
+       /*
+        * Notify userspace that the charger has been either connected to or
+        * disconnected from the power source.
+        */
+       power_supply_changed(info->charger);
+}
+
+static void rt9455_max_charging_time_work_callback(struct work_struct *work)
+{
+       struct rt9455_info *info = container_of(work, struct rt9455_info,
+                                               max_charging_time_work.work);
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       dev_err(dev, "Battery has been charging for at least 6 hours and is not yet fully charged. Battery is dead, therefore charging is disabled.\n");
+       ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+                                RT9455_CHARGE_DISABLE);
+       if (ret)
+               dev_err(dev, "Failed to disable charging\n");
+}
+
+static void rt9455_batt_presence_work_callback(struct work_struct *work)
+{
+       struct rt9455_info *info = container_of(work, struct rt9455_info,
+                                               batt_presence_work.work);
+       struct device *dev = &info->client->dev;
+       unsigned int irq1, mask1;
+       int ret;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &irq1);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ1 register\n");
+               return;
+       }
+
+       /*
+        * If the battery is still absent, batt_presence_work is rescheduled.
+        * Otherwise, max_charging_time is scheduled.
+        */
+       if (irq1 & GET_MASK(F_BATAB)) {
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->batt_presence_work,
+                                  RT9455_BATT_PRESENCE_DELAY * HZ);
+       } else {
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->max_charging_time_work,
+                                  RT9455_MAX_CHARGING_TIME * HZ);
+
+               ret = regmap_read(info->regmap, RT9455_REG_MASK1, &mask1);
+               if (ret) {
+                       dev_err(dev, "Failed to read MASK1 register\n");
+                       return;
+               }
+
+               if (mask1 & GET_MASK(F_BATABM)) {
+                       ret = regmap_field_write(info->regmap_fields[F_BATABM],
+                                                0x00);
+                       if (ret)
+                               dev_err(dev, "Failed to unmask BATAB interrupt\n");
+               }
+               /*
+                * Notify userspace that the battery is now connected to the
+                * charger.
+                */
+               power_supply_changed(info->charger);
+       }
+}
+
+static const struct power_supply_desc rt9455_charger_desc = {
+       .name                   = RT9455_DRIVER_NAME,
+       .type                   = POWER_SUPPLY_TYPE_USB,
+       .properties             = rt9455_charger_properties,
+       .num_properties         = ARRAY_SIZE(rt9455_charger_properties),
+       .get_property           = rt9455_charger_get_property,
+};
+
+static bool rt9455_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case RT9455_REG_DEV_ID:
+       case RT9455_REG_IRQ1:
+       case RT9455_REG_IRQ2:
+       case RT9455_REG_IRQ3:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static bool rt9455_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case RT9455_REG_DEV_ID:
+       case RT9455_REG_CTRL5:
+       case RT9455_REG_CTRL6:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static const struct regmap_config rt9455_regmap_config = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .writeable_reg  = rt9455_is_writeable_reg,
+       .volatile_reg   = rt9455_is_volatile_reg,
+       .max_register   = RT9455_REG_MASK3,
+       .cache_type     = REGCACHE_RBTREE,
+};
+
+static int rt9455_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct device *dev = &client->dev;
+       struct rt9455_info *info;
+       struct power_supply_config rt9455_charger_config = {};
+       /*
+        * Mandatory device-specific data values. Also, VOREG and boost output
+        * voltage are mandatory values, but they are stored in rt9455_info
+        * structure.
+        */
+       u32 ichrg, ieoc_percentage;
+       /* Optional device-specific data values. */
+       u32 mivr = -1, iaicr = -1;
+       int i, ret;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+               return -ENODEV;
+       }
+       info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->client = client;
+       i2c_set_clientdata(client, info);
+
+       info->regmap = devm_regmap_init_i2c(client,
+                                           &rt9455_regmap_config);
+       if (IS_ERR(info->regmap)) {
+               dev_err(dev, "Failed to initialize register map\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < F_MAX_FIELDS; i++) {
+               info->regmap_fields[i] =
+                       devm_regmap_field_alloc(dev, info->regmap,
+                                               rt9455_reg_fields[i]);
+               if (IS_ERR(info->regmap_fields[i])) {
+                       dev_err(dev,
+                               "Failed to allocate regmap field = %d\n", i);
+                       return PTR_ERR(info->regmap_fields[i]);
+               }
+       }
+
+       ret = rt9455_discover_charger(info, &ichrg, &ieoc_percentage,
+                                     &mivr, &iaicr);
+       if (ret) {
+               dev_err(dev, "Failed to discover charger\n");
+               return ret;
+       }
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+       info->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+       if (IS_ERR(info->usb_phy)) {
+               dev_err(dev, "Failed to get USB transceiver\n");
+       } else {
+               info->nb.notifier_call = rt9455_usb_event;
+               ret = usb_register_notifier(info->usb_phy, &info->nb);
+               if (ret) {
+                       dev_err(dev, "Failed to register USB notifier\n");
+                       /*
+                        * If usb_register_notifier() fails, set notifier_call
+                        * to NULL, to avoid calling usb_unregister_notifier().
+                        */
+                       info->nb.notifier_call = NULL;
+               }
+       }
+#endif
+
+       INIT_DEFERRABLE_WORK(&info->pwr_rdy_work, rt9455_pwr_rdy_work_callback);
+       INIT_DEFERRABLE_WORK(&info->max_charging_time_work,
+                            rt9455_max_charging_time_work_callback);
+       INIT_DEFERRABLE_WORK(&info->batt_presence_work,
+                            rt9455_batt_presence_work_callback);
+
+       rt9455_charger_config.of_node           = dev->of_node;
+       rt9455_charger_config.drv_data          = info;
+       rt9455_charger_config.supplied_to       = rt9455_charger_supplied_to;
+       rt9455_charger_config.num_supplicants   =
+                                       ARRAY_SIZE(rt9455_charger_supplied_to);
+       ret = devm_request_threaded_irq(dev, client->irq, NULL,
+                                       rt9455_irq_handler_thread,
+                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                       RT9455_DRIVER_NAME, info);
+       if (ret) {
+               dev_err(dev, "Failed to register IRQ handler\n");
+               goto put_usb_notifier;
+       }
+
+       ret = rt9455_hw_init(info, ichrg, ieoc_percentage, mivr, iaicr);
+       if (ret) {
+               dev_err(dev, "Failed to set charger to its default values\n");
+               goto put_usb_notifier;
+       }
+
+       info->charger = devm_power_supply_register(dev, &rt9455_charger_desc,
+                                                  &rt9455_charger_config);
+       if (IS_ERR(info->charger)) {
+               dev_err(dev, "Failed to register charger\n");
+               ret = PTR_ERR(info->charger);
+               goto put_usb_notifier;
+       }
+
+       return 0;
+
+put_usb_notifier:
+#if IS_ENABLED(CONFIG_USB_PHY)
+       if (info->nb.notifier_call)  {
+               usb_unregister_notifier(info->usb_phy, &info->nb);
+               info->nb.notifier_call = NULL;
+       }
+#endif
+       return ret;
+}
+
+static int rt9455_remove(struct i2c_client *client)
+{
+       int ret;
+       struct rt9455_info *info = i2c_get_clientdata(client);
+
+       ret = rt9455_register_reset(info);
+       if (ret)
+               dev_err(&info->client->dev, "Failed to set charger to its default values\n");
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+       if (info->nb.notifier_call)
+               usb_unregister_notifier(info->usb_phy, &info->nb);
+#endif
+
+       cancel_delayed_work_sync(&info->pwr_rdy_work);
+       cancel_delayed_work_sync(&info->max_charging_time_work);
+       cancel_delayed_work_sync(&info->batt_presence_work);
+
+       return ret;
+}
+
+static const struct i2c_device_id rt9455_i2c_id_table[] = {
+       { RT9455_DRIVER_NAME, 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, rt9455_i2c_id_table);
+
+static const struct of_device_id rt9455_of_match[] = {
+       { .compatible = "richtek,rt9455", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, rt9455_of_match);
+
+static const struct acpi_device_id rt9455_i2c_acpi_match[] = {
+       { "RT945500", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, rt9455_i2c_acpi_match);
+
+static struct i2c_driver rt9455_driver = {
+       .probe          = rt9455_probe,
+       .remove         = rt9455_remove,
+       .id_table       = rt9455_i2c_id_table,
+       .driver = {
+               .name           = RT9455_DRIVER_NAME,
+               .of_match_table = of_match_ptr(rt9455_of_match),
+               .acpi_match_table = ACPI_PTR(rt9455_i2c_acpi_match),
+       },
+};
+module_i2c_driver(rt9455_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anda-Maria Nicolae <anda-maria.nicolae@intel.com>");
+MODULE_DESCRIPTION("Richtek RT9455 Charger Driver");
diff --git a/drivers/power/supply/rx51_battery.c b/drivers/power/supply/rx51_battery.c
new file mode 100644 (file)
index 0000000..af9383d
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Nokia RX-51 battery driver
+ *
+ * Copyright (C) 2012  Pali Rohár <pali.rohar@gmail.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/i2c/twl4030-madc.h>
+#include <linux/iio/consumer.h>
+#include <linux/of.h>
+
+struct rx51_device_info {
+       struct device *dev;
+       struct power_supply *bat;
+       struct power_supply_desc bat_desc;
+       struct iio_channel *channel_temp;
+       struct iio_channel *channel_bsi;
+       struct iio_channel *channel_vbat;
+};
+
+/*
+ * Read ADCIN channel value, code copied from maemo kernel
+ */
+static int rx51_battery_read_adc(struct iio_channel *channel)
+{
+       int val, err;
+       err = iio_read_channel_average_raw(channel, &val);
+       if (err < 0)
+               return err;
+       return val;
+}
+
+/*
+ * Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage
+ * This conversion formula was extracted from maemo program bsi-read
+ */
+static int rx51_battery_read_voltage(struct rx51_device_info *di)
+{
+       int voltage = rx51_battery_read_adc(di->channel_vbat);
+
+       if (voltage < 0) {
+               dev_err(di->dev, "Could not read ADC: %d\n", voltage);
+               return voltage;
+       }
+
+       return 1000 * (10000 * voltage / 1705);
+}
+
+/*
+ * Temperature look-up tables
+ * TEMP = (1/(t1 + 1/298) - 273.15)
+ * Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255))
+ * Formula is based on experimental data, RX-51 CAL data, maemo program bme
+ * and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671
+ */
+
+/*
+ * Table1 (temperature for first 25 RAW values)
+ * Usage: TEMP = rx51_temp_table1[RAW]
+ *   RAW is between 1 and 24
+ *   TEMP is between 201 C and 55 C
+ */
+static u8 rx51_temp_table1[] = {
+       255, 201, 159, 138, 124, 114, 106,  99,  94,  89,  85,  82,  78,  75,
+        73,  70,  68,  66,  64,  62,  61,  59,  57,  56,  55
+};
+
+/*
+ * Table2 (lowest RAW value for temperature)
+ * Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first]
+ *   TEMP is between 53 C and -32 C
+ *   RAW is between 25 and 993
+ */
+#define rx51_temp_table2_first 53
+static u16 rx51_temp_table2[] = {
+        25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  39,
+        40,  41,  43,  44,  46,  48,  49,  51,  53,  55,  57,  59,  61,  64,
+        66,  69,  71,  74,  77,  80,  83,  86,  90,  94,  97, 101, 106, 110,
+       115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211,
+       221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415,
+       437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885,
+       937, 993, 1024
+};
+
+/*
+ * Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius
+ * Use Temperature look-up tables for conversation
+ */
+static int rx51_battery_read_temperature(struct rx51_device_info *di)
+{
+       int min = 0;
+       int max = ARRAY_SIZE(rx51_temp_table2) - 1;
+       int raw = rx51_battery_read_adc(di->channel_temp);
+
+       if (raw < 0)
+               dev_err(di->dev, "Could not read ADC: %d\n", raw);
+
+       /* Zero and negative values are undefined */
+       if (raw <= 0)
+               return INT_MAX;
+
+       /* ADC channels are 10 bit, higher value are undefined */
+       if (raw >= (1 << 10))
+               return INT_MIN;
+
+       /* First check for temperature in first direct table */
+       if (raw < ARRAY_SIZE(rx51_temp_table1))
+               return rx51_temp_table1[raw] * 10;
+
+       /* Binary search RAW value in second inverse table */
+       while (max - min > 1) {
+               int mid = (max + min) / 2;
+               if (rx51_temp_table2[mid] <= raw)
+                       min = mid;
+               else if (rx51_temp_table2[mid] > raw)
+                       max = mid;
+               if (rx51_temp_table2[mid] == raw)
+                       break;
+       }
+
+       return (rx51_temp_table2_first - min) * 10;
+}
+
+/*
+ * Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah
+ * This conversion formula was extracted from maemo program bsi-read
+ */
+static int rx51_battery_read_capacity(struct rx51_device_info *di)
+{
+       int capacity = rx51_battery_read_adc(di->channel_bsi);
+
+       if (capacity < 0) {
+               dev_err(di->dev, "Could not read ADC: %d\n", capacity);
+               return capacity;
+       }
+
+       return 1280 * (1200 * capacity)/(1024 - capacity);
+}
+
+/*
+ * Return power_supply property
+ */
+static int rx51_battery_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct rx51_device_info *di = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = 4200000;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = rx51_battery_read_voltage(di) ? 1 : 0;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = rx51_battery_read_voltage(di);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = rx51_battery_read_temperature(di);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = rx51_battery_read_capacity(di);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (val->intval == INT_MAX || val->intval == INT_MIN)
+               return -EINVAL;
+
+       return 0;
+}
+
+static enum power_supply_property rx51_battery_props[] = {
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+};
+
+static int rx51_battery_probe(struct platform_device *pdev)
+{
+       struct power_supply_config psy_cfg = {};
+       struct rx51_device_info *di;
+       int ret;
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       if (!di)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, di);
+
+       di->dev = &pdev->dev;
+       di->bat_desc.name = "rx51-battery";
+       di->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+       di->bat_desc.properties = rx51_battery_props;
+       di->bat_desc.num_properties = ARRAY_SIZE(rx51_battery_props);
+       di->bat_desc.get_property = rx51_battery_get_property;
+
+       psy_cfg.drv_data = di;
+
+       di->channel_temp = iio_channel_get(di->dev, "temp");
+       if (IS_ERR(di->channel_temp)) {
+               ret = PTR_ERR(di->channel_temp);
+               goto error;
+       }
+
+       di->channel_bsi  = iio_channel_get(di->dev, "bsi");
+       if (IS_ERR(di->channel_bsi)) {
+               ret = PTR_ERR(di->channel_bsi);
+               goto error_channel_temp;
+       }
+
+       di->channel_vbat = iio_channel_get(di->dev, "vbat");
+       if (IS_ERR(di->channel_vbat)) {
+               ret = PTR_ERR(di->channel_vbat);
+               goto error_channel_bsi;
+       }
+
+       di->bat = power_supply_register(di->dev, &di->bat_desc, &psy_cfg);
+       if (IS_ERR(di->bat)) {
+               ret = PTR_ERR(di->bat);
+               goto error_channel_vbat;
+       }
+
+       return 0;
+
+error_channel_vbat:
+       iio_channel_release(di->channel_vbat);
+error_channel_bsi:
+       iio_channel_release(di->channel_bsi);
+error_channel_temp:
+       iio_channel_release(di->channel_temp);
+error:
+
+       return ret;
+}
+
+static int rx51_battery_remove(struct platform_device *pdev)
+{
+       struct rx51_device_info *di = platform_get_drvdata(pdev);
+
+       power_supply_unregister(di->bat);
+
+       iio_channel_release(di->channel_vbat);
+       iio_channel_release(di->channel_bsi);
+       iio_channel_release(di->channel_temp);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id n900_battery_of_match[] = {
+       {.compatible = "nokia,n900-battery", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, n900_battery_of_match);
+#endif
+
+static struct platform_driver rx51_battery_driver = {
+       .probe = rx51_battery_probe,
+       .remove = rx51_battery_remove,
+       .driver = {
+               .name = "rx51-battery",
+               .of_match_table = of_match_ptr(n900_battery_of_match),
+       },
+};
+module_platform_driver(rx51_battery_driver);
+
+MODULE_ALIAS("platform:rx51-battery");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_DESCRIPTION("Nokia RX-51 battery driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/s3c_adc_battery.c b/drivers/power/supply/s3c_adc_battery.c
new file mode 100644 (file)
index 0000000..0ffe5cd
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ *     iPAQ h1930/h1940/rx1950 battery controller driver
+ *     Copyright (c) Vasily Khoruzhick
+ *     Based on h1940_battery.c by Arnaud Patard
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/s3c_adc_battery.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <plat/adc.h>
+
+#define BAT_POLL_INTERVAL              10000 /* ms */
+#define JITTER_DELAY                   500 /* ms */
+
+struct s3c_adc_bat {
+       struct power_supply             *psy;
+       struct s3c_adc_client           *client;
+       struct s3c_adc_bat_pdata        *pdata;
+       int                             volt_value;
+       int                             cur_value;
+       unsigned int                    timestamp;
+       int                             level;
+       int                             status;
+       int                             cable_plugged:1;
+};
+
+static struct delayed_work bat_work;
+
+static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
+{
+       schedule_delayed_work(&bat_work,
+               msecs_to_jiffies(JITTER_DELAY));
+}
+
+static int gather_samples(struct s3c_adc_client *client, int num, int channel)
+{
+       int value, i;
+
+       /* default to 1 if nothing is set */
+       if (num < 1)
+               num = 1;
+
+       value = 0;
+       for (i = 0; i < num; i++)
+               value += s3c_adc_read(client, channel);
+       value /= num;
+
+       return value;
+}
+
+static enum power_supply_property s3c_adc_backup_bat_props[] = {
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+};
+
+static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct s3c_adc_bat *bat = power_supply_get_drvdata(psy);
+
+       if (!bat) {
+               dev_err(&psy->dev, "%s: no battery infos ?!\n", __func__);
+               return -EINVAL;
+       }
+
+       if (bat->volt_value < 0 ||
+               jiffies_to_msecs(jiffies - bat->timestamp) >
+                       BAT_POLL_INTERVAL) {
+               bat->volt_value = gather_samples(bat->client,
+                       bat->pdata->backup_volt_samples,
+                       bat->pdata->backup_volt_channel);
+               bat->volt_value *= bat->pdata->backup_volt_mult;
+               bat->timestamp = jiffies;
+       }
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = bat->volt_value;
+               return 0;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+               val->intval = bat->pdata->backup_volt_min;
+               return 0;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = bat->pdata->backup_volt_max;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct power_supply_desc backup_bat_desc = {
+       .name           = "backup-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = s3c_adc_backup_bat_props,
+       .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props),
+       .get_property   = s3c_adc_backup_bat_get_property,
+       .use_for_apm    = 1,
+};
+
+static struct s3c_adc_bat backup_bat;
+
+static enum power_supply_property s3c_adc_main_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int calc_full_volt(int volt_val, int cur_val, int impedance)
+{
+       return volt_val + cur_val * impedance / 1000;
+}
+
+static int charge_finished(struct s3c_adc_bat *bat)
+{
+       return bat->pdata->gpio_inverted ?
+               !gpio_get_value(bat->pdata->gpio_charge_finished) :
+               gpio_get_value(bat->pdata->gpio_charge_finished);
+}
+
+static int s3c_adc_bat_get_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   union power_supply_propval *val)
+{
+       struct s3c_adc_bat *bat = power_supply_get_drvdata(psy);
+
+       int new_level;
+       int full_volt;
+       const struct s3c_adc_bat_thresh *lut;
+       unsigned int lut_size;
+
+       if (!bat) {
+               dev_err(&psy->dev, "no battery infos ?!\n");
+               return -EINVAL;
+       }
+
+       lut = bat->pdata->lut_noac;
+       lut_size = bat->pdata->lut_noac_cnt;
+
+       if (bat->volt_value < 0 || bat->cur_value < 0 ||
+               jiffies_to_msecs(jiffies - bat->timestamp) >
+                       BAT_POLL_INTERVAL) {
+               bat->volt_value = gather_samples(bat->client,
+                       bat->pdata->volt_samples,
+                       bat->pdata->volt_channel) * bat->pdata->volt_mult;
+               bat->cur_value = gather_samples(bat->client,
+                       bat->pdata->current_samples,
+                       bat->pdata->current_channel) * bat->pdata->current_mult;
+               bat->timestamp = jiffies;
+       }
+
+       if (bat->cable_plugged &&
+               ((bat->pdata->gpio_charge_finished < 0) ||
+               !charge_finished(bat))) {
+               lut = bat->pdata->lut_acin;
+               lut_size = bat->pdata->lut_acin_cnt;
+       }
+
+       new_level = 100000;
+       full_volt = calc_full_volt((bat->volt_value / 1000),
+               (bat->cur_value / 1000), bat->pdata->internal_impedance);
+
+       if (full_volt < calc_full_volt(lut->volt, lut->cur,
+               bat->pdata->internal_impedance)) {
+               lut_size--;
+               while (lut_size--) {
+                       int lut_volt1;
+                       int lut_volt2;
+
+                       lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur,
+                               bat->pdata->internal_impedance);
+                       lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur,
+                               bat->pdata->internal_impedance);
+                       if (full_volt < lut_volt1 && full_volt >= lut_volt2) {
+                               new_level = (lut[1].level +
+                                       (lut[0].level - lut[1].level) *
+                                       (full_volt - lut_volt2) /
+                                       (lut_volt1 - lut_volt2)) * 1000;
+                               break;
+                       }
+                       new_level = lut[1].level * 1000;
+                       lut++;
+               }
+       }
+
+       bat->level = new_level;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (bat->pdata->gpio_charge_finished < 0)
+                       val->intval = bat->level == 100000 ?
+                               POWER_SUPPLY_STATUS_FULL : bat->status;
+               else
+                       val->intval = bat->status;
+               return 0;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = 100000;
+               return 0;
+       case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
+               val->intval = 0;
+               return 0;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               val->intval = bat->level;
+               return 0;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = bat->volt_value;
+               return 0;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = bat->cur_value;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct power_supply_desc main_bat_desc = {
+       .name                   = "main-battery",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = s3c_adc_main_bat_props,
+       .num_properties         = ARRAY_SIZE(s3c_adc_main_bat_props),
+       .get_property           = s3c_adc_bat_get_property,
+       .external_power_changed = s3c_adc_bat_ext_power_changed,
+       .use_for_apm            = 1,
+};
+
+static struct s3c_adc_bat main_bat;
+
+static void s3c_adc_bat_work(struct work_struct *work)
+{
+       struct s3c_adc_bat *bat = &main_bat;
+       int is_charged;
+       int is_plugged;
+       static int was_plugged;
+
+       is_plugged = power_supply_am_i_supplied(bat->psy);
+       bat->cable_plugged = is_plugged;
+       if (is_plugged != was_plugged) {
+               was_plugged = is_plugged;
+               if (is_plugged) {
+                       if (bat->pdata->enable_charger)
+                               bat->pdata->enable_charger();
+                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
+               } else {
+                       if (bat->pdata->disable_charger)
+                               bat->pdata->disable_charger();
+                       bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+               }
+       } else {
+               if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) {
+                       is_charged = charge_finished(&main_bat);
+                       if (is_charged) {
+                               if (bat->pdata->disable_charger)
+                                       bat->pdata->disable_charger();
+                               bat->status = POWER_SUPPLY_STATUS_FULL;
+                       } else {
+                               if (bat->pdata->enable_charger)
+                                       bat->pdata->enable_charger();
+                               bat->status = POWER_SUPPLY_STATUS_CHARGING;
+                       }
+               }
+       }
+
+       power_supply_changed(bat->psy);
+}
+
+static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
+{
+       schedule_delayed_work(&bat_work,
+               msecs_to_jiffies(JITTER_DELAY));
+       return IRQ_HANDLED;
+}
+
+static int s3c_adc_bat_probe(struct platform_device *pdev)
+{
+       struct s3c_adc_client   *client;
+       struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+       int ret;
+
+       client = s3c_adc_register(pdev, NULL, NULL, 0);
+       if (IS_ERR(client)) {
+               dev_err(&pdev->dev, "cannot register adc\n");
+               return PTR_ERR(client);
+       }
+
+       platform_set_drvdata(pdev, client);
+
+       main_bat.client = client;
+       main_bat.pdata = pdata;
+       main_bat.volt_value = -1;
+       main_bat.cur_value = -1;
+       main_bat.cable_plugged = 0;
+       main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+       main_bat.psy = power_supply_register(&pdev->dev, &main_bat_desc, NULL);
+       if (IS_ERR(main_bat.psy)) {
+               ret = PTR_ERR(main_bat.psy);
+               goto err_reg_main;
+       }
+       if (pdata->backup_volt_mult) {
+               const struct power_supply_config psy_cfg
+                                               = { .drv_data = &backup_bat, };
+
+               backup_bat.client = client;
+               backup_bat.pdata = pdev->dev.platform_data;
+               backup_bat.volt_value = -1;
+               backup_bat.psy = power_supply_register(&pdev->dev,
+                                                      &backup_bat_desc,
+                                                      &psy_cfg);
+               if (IS_ERR(backup_bat.psy)) {
+                       ret = PTR_ERR(backup_bat.psy);
+                       goto err_reg_backup;
+               }
+       }
+
+       INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
+
+       if (pdata->gpio_charge_finished >= 0) {
+               ret = gpio_request(pdata->gpio_charge_finished, "charged");
+               if (ret)
+                       goto err_gpio;
+
+               ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished),
+                               s3c_adc_bat_charged,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               "battery charged", NULL);
+               if (ret)
+                       goto err_irq;
+       }
+
+       if (pdata->init) {
+               ret = pdata->init();
+               if (ret)
+                       goto err_platform;
+       }
+
+       dev_info(&pdev->dev, "successfully loaded\n");
+       device_init_wakeup(&pdev->dev, 1);
+
+       /* Schedule timer to check current status */
+       schedule_delayed_work(&bat_work,
+               msecs_to_jiffies(JITTER_DELAY));
+
+       return 0;
+
+err_platform:
+       if (pdata->gpio_charge_finished >= 0)
+               free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
+err_irq:
+       if (pdata->gpio_charge_finished >= 0)
+               gpio_free(pdata->gpio_charge_finished);
+err_gpio:
+       if (pdata->backup_volt_mult)
+               power_supply_unregister(backup_bat.psy);
+err_reg_backup:
+       power_supply_unregister(main_bat.psy);
+err_reg_main:
+       return ret;
+}
+
+static int s3c_adc_bat_remove(struct platform_device *pdev)
+{
+       struct s3c_adc_client *client = platform_get_drvdata(pdev);
+       struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+       power_supply_unregister(main_bat.psy);
+       if (pdata->backup_volt_mult)
+               power_supply_unregister(backup_bat.psy);
+
+       s3c_adc_release(client);
+
+       if (pdata->gpio_charge_finished >= 0) {
+               free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
+               gpio_free(pdata->gpio_charge_finished);
+       }
+
+       cancel_delayed_work(&bat_work);
+
+       if (pdata->exit)
+               pdata->exit();
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_adc_bat_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+       if (pdata->gpio_charge_finished >= 0) {
+               if (device_may_wakeup(&pdev->dev))
+                       enable_irq_wake(
+                               gpio_to_irq(pdata->gpio_charge_finished));
+               else {
+                       disable_irq(gpio_to_irq(pdata->gpio_charge_finished));
+                       main_bat.pdata->disable_charger();
+               }
+       }
+
+       return 0;
+}
+
+static int s3c_adc_bat_resume(struct platform_device *pdev)
+{
+       struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+       if (pdata->gpio_charge_finished >= 0) {
+               if (device_may_wakeup(&pdev->dev))
+                       disable_irq_wake(
+                               gpio_to_irq(pdata->gpio_charge_finished));
+               else
+                       enable_irq(gpio_to_irq(pdata->gpio_charge_finished));
+       }
+
+       /* Schedule timer to check current status */
+       schedule_delayed_work(&bat_work,
+               msecs_to_jiffies(JITTER_DELAY));
+
+       return 0;
+}
+#else
+#define s3c_adc_bat_suspend NULL
+#define s3c_adc_bat_resume NULL
+#endif
+
+static struct platform_driver s3c_adc_bat_driver = {
+       .driver         = {
+               .name   = "s3c-adc-battery",
+       },
+       .probe          = s3c_adc_bat_probe,
+       .remove         = s3c_adc_bat_remove,
+       .suspend        = s3c_adc_bat_suspend,
+       .resume         = s3c_adc_bat_resume,
+};
+
+module_platform_driver(s3c_adc_bat_driver);
+
+MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
+MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c
new file mode 100644 (file)
index 0000000..768b9fc
--- /dev/null
@@ -0,0 +1,998 @@
+/*
+ * Gas Gauge driver for SBS Compliant Batteries
+ *
+ * Copyright (c) 2010, NVIDIA 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/stat.h>
+
+#include <linux/power/sbs-battery.h>
+
+enum {
+       REG_MANUFACTURER_DATA,
+       REG_TEMPERATURE,
+       REG_VOLTAGE,
+       REG_CURRENT,
+       REG_CAPACITY,
+       REG_TIME_TO_EMPTY,
+       REG_TIME_TO_FULL,
+       REG_STATUS,
+       REG_CYCLE_COUNT,
+       REG_SERIAL_NUMBER,
+       REG_REMAINING_CAPACITY,
+       REG_REMAINING_CAPACITY_CHARGE,
+       REG_FULL_CHARGE_CAPACITY,
+       REG_FULL_CHARGE_CAPACITY_CHARGE,
+       REG_DESIGN_CAPACITY,
+       REG_DESIGN_CAPACITY_CHARGE,
+       REG_DESIGN_VOLTAGE_MIN,
+       REG_DESIGN_VOLTAGE_MAX,
+       REG_MANUFACTURER,
+       REG_MODEL_NAME,
+};
+
+/* Battery Mode defines */
+#define BATTERY_MODE_OFFSET            0x03
+#define BATTERY_MODE_MASK              0x8000
+enum sbs_battery_mode {
+       BATTERY_MODE_AMPS,
+       BATTERY_MODE_WATTS
+};
+
+/* manufacturer access defines */
+#define MANUFACTURER_ACCESS_STATUS     0x0006
+#define MANUFACTURER_ACCESS_SLEEP      0x0011
+
+/* battery status value bits */
+#define BATTERY_DISCHARGING            0x40
+#define BATTERY_FULL_CHARGED           0x20
+#define BATTERY_FULL_DISCHARGED                0x10
+
+/* min_value and max_value are only valid for numerical data */
+#define SBS_DATA(_psp, _addr, _min_value, _max_value) { \
+       .psp = _psp, \
+       .addr = _addr, \
+       .min_value = _min_value, \
+       .max_value = _max_value, \
+}
+
+static const struct chip_data {
+       enum power_supply_property psp;
+       u8 addr;
+       int min_value;
+       int max_value;
+} sbs_data[] = {
+       [REG_MANUFACTURER_DATA] =
+               SBS_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
+       [REG_TEMPERATURE] =
+               SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
+       [REG_VOLTAGE] =
+               SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
+       [REG_CURRENT] =
+               SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
+       [REG_CAPACITY] =
+               SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0D, 0, 100),
+       [REG_REMAINING_CAPACITY] =
+               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
+       [REG_REMAINING_CAPACITY_CHARGE] =
+               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535),
+       [REG_FULL_CHARGE_CAPACITY] =
+               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
+       [REG_FULL_CHARGE_CAPACITY_CHARGE] =
+               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535),
+       [REG_TIME_TO_EMPTY] =
+               SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535),
+       [REG_TIME_TO_FULL] =
+               SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535),
+       [REG_STATUS] =
+               SBS_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
+       [REG_CYCLE_COUNT] =
+               SBS_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535),
+       [REG_DESIGN_CAPACITY] =
+               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535),
+       [REG_DESIGN_CAPACITY_CHARGE] =
+               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, 65535),
+       [REG_DESIGN_VOLTAGE_MIN] =
+               SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 0x19, 0, 65535),
+       [REG_DESIGN_VOLTAGE_MAX] =
+               SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535),
+       [REG_SERIAL_NUMBER] =
+               SBS_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
+       /* Properties of type `const char *' */
+       [REG_MANUFACTURER] =
+               SBS_DATA(POWER_SUPPLY_PROP_MANUFACTURER, 0x20, 0, 65535),
+       [REG_MODEL_NAME] =
+               SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535)
+};
+
+static enum power_supply_property sbs_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+       POWER_SUPPLY_PROP_SERIAL_NUMBER,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_ENERGY_NOW,
+       POWER_SUPPLY_PROP_ENERGY_FULL,
+       POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       /* Properties of type `const char *' */
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_MODEL_NAME
+};
+
+struct sbs_info {
+       struct i2c_client               *client;
+       struct power_supply             *power_supply;
+       struct sbs_platform_data        *pdata;
+       bool                            is_present;
+       bool                            gpio_detect;
+       bool                            enable_detection;
+       int                             irq;
+       int                             last_state;
+       int                             poll_time;
+       struct delayed_work             work;
+       int                             ignore_changes;
+};
+
+static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
+static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
+static bool force_load;
+
+static int sbs_read_word_data(struct i2c_client *client, u8 address)
+{
+       struct sbs_info *chip = i2c_get_clientdata(client);
+       s32 ret = 0;
+       int retries = 1;
+
+       if (chip->pdata)
+               retries = max(chip->pdata->i2c_retry_count + 1, 1);
+
+       while (retries > 0) {
+               ret = i2c_smbus_read_word_data(client, address);
+               if (ret >= 0)
+                       break;
+               retries--;
+       }
+
+       if (ret < 0) {
+               dev_dbg(&client->dev,
+                       "%s: i2c read at address 0x%x failed\n",
+                       __func__, address);
+               return ret;
+       }
+
+       return le16_to_cpu(ret);
+}
+
+static int sbs_read_string_data(struct i2c_client *client, u8 address,
+                               char *values)
+{
+       struct sbs_info *chip = i2c_get_clientdata(client);
+       s32 ret = 0, block_length = 0;
+       int retries_length = 1, retries_block = 1;
+       u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
+
+       if (chip->pdata) {
+               retries_length = max(chip->pdata->i2c_retry_count + 1, 1);
+               retries_block = max(chip->pdata->i2c_retry_count + 1, 1);
+       }
+
+       /* Adapter needs to support these two functions */
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_BYTE_DATA |
+                                    I2C_FUNC_SMBUS_I2C_BLOCK)){
+               return -ENODEV;
+       }
+
+       /* Get the length of block data */
+       while (retries_length > 0) {
+               ret = i2c_smbus_read_byte_data(client, address);
+               if (ret >= 0)
+                       break;
+               retries_length--;
+       }
+
+       if (ret < 0) {
+               dev_dbg(&client->dev,
+                       "%s: i2c read at address 0x%x failed\n",
+                       __func__, address);
+               return ret;
+       }
+
+       /* block_length does not include NULL terminator */
+       block_length = ret;
+       if (block_length > I2C_SMBUS_BLOCK_MAX) {
+               dev_err(&client->dev,
+                       "%s: Returned block_length is longer than 0x%x\n",
+                       __func__, I2C_SMBUS_BLOCK_MAX);
+               return -EINVAL;
+       }
+
+       /* Get the block data */
+       while (retries_block > 0) {
+               ret = i2c_smbus_read_i2c_block_data(
+                               client, address,
+                               block_length + 1, block_buffer);
+               if (ret >= 0)
+                       break;
+               retries_block--;
+       }
+
+       if (ret < 0) {
+               dev_dbg(&client->dev,
+                       "%s: i2c read at address 0x%x failed\n",
+                       __func__, address);
+               return ret;
+       }
+
+       /* block_buffer[0] == block_length */
+       memcpy(values, block_buffer + 1, block_length);
+       values[block_length] = '\0';
+
+       return le16_to_cpu(ret);
+}
+
+static int sbs_write_word_data(struct i2c_client *client, u8 address,
+       u16 value)
+{
+       struct sbs_info *chip = i2c_get_clientdata(client);
+       s32 ret = 0;
+       int retries = 1;
+
+       if (chip->pdata)
+               retries = max(chip->pdata->i2c_retry_count + 1, 1);
+
+       while (retries > 0) {
+               ret = i2c_smbus_write_word_data(client, address,
+                       le16_to_cpu(value));
+               if (ret >= 0)
+                       break;
+               retries--;
+       }
+
+       if (ret < 0) {
+               dev_dbg(&client->dev,
+                       "%s: i2c write to address 0x%x failed\n",
+                       __func__, address);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int sbs_get_battery_presence_and_health(
+       struct i2c_client *client, enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       s32 ret;
+       struct sbs_info *chip = i2c_get_clientdata(client);
+
+       if (psp == POWER_SUPPLY_PROP_PRESENT &&
+               chip->gpio_detect) {
+               ret = gpio_get_value(chip->pdata->battery_detect);
+               if (ret == chip->pdata->battery_detect_present)
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               chip->is_present = val->intval;
+               return ret;
+       }
+
+       /* Write to ManufacturerAccess with
+        * ManufacturerAccess command and then
+        * read the status */
+       ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
+                                       MANUFACTURER_ACCESS_STATUS);
+       if (ret < 0) {
+               if (psp == POWER_SUPPLY_PROP_PRESENT)
+                       val->intval = 0; /* battery removed */
+               return ret;
+       }
+
+       ret = sbs_read_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr);
+       if (ret < 0)
+               return ret;
+
+       if (ret < sbs_data[REG_MANUFACTURER_DATA].min_value ||
+           ret > sbs_data[REG_MANUFACTURER_DATA].max_value) {
+               val->intval = 0;
+               return 0;
+       }
+
+       /* Mask the upper nibble of 2nd byte and
+        * lower byte of response then
+        * shift the result by 8 to get status*/
+       ret &= 0x0F00;
+       ret >>= 8;
+       if (psp == POWER_SUPPLY_PROP_PRESENT) {
+               if (ret == 0x0F)
+                       /* battery removed */
+                       val->intval = 0;
+               else
+                       val->intval = 1;
+       } else if (psp == POWER_SUPPLY_PROP_HEALTH) {
+               if (ret == 0x09)
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               else if (ret == 0x0B)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else if (ret == 0x0C)
+                       val->intval = POWER_SUPPLY_HEALTH_DEAD;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+       }
+
+       return 0;
+}
+
+static int sbs_get_battery_property(struct i2c_client *client,
+       int reg_offset, enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       struct sbs_info *chip = i2c_get_clientdata(client);
+       s32 ret;
+
+       ret = sbs_read_word_data(client, sbs_data[reg_offset].addr);
+       if (ret < 0)
+               return ret;
+
+       /* returned values are 16 bit */
+       if (sbs_data[reg_offset].min_value < 0)
+               ret = (s16)ret;
+
+       if (ret >= sbs_data[reg_offset].min_value &&
+           ret <= sbs_data[reg_offset].max_value) {
+               val->intval = ret;
+               if (psp != POWER_SUPPLY_PROP_STATUS)
+                       return 0;
+
+               if (ret & BATTERY_FULL_CHARGED)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else if (ret & BATTERY_DISCHARGING)
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+
+               if (chip->poll_time == 0)
+                       chip->last_state = val->intval;
+               else if (chip->last_state != val->intval) {
+                       cancel_delayed_work_sync(&chip->work);
+                       power_supply_changed(chip->power_supply);
+                       chip->poll_time = 0;
+               }
+       } else {
+               if (psp == POWER_SUPPLY_PROP_STATUS)
+                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+               else
+                       val->intval = 0;
+       }
+
+       return 0;
+}
+
+static int sbs_get_battery_string_property(struct i2c_client *client,
+       int reg_offset, enum power_supply_property psp, char *val)
+{
+       s32 ret;
+
+       ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val);
+
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static void  sbs_unit_adjustment(struct i2c_client *client,
+       enum power_supply_property psp, union power_supply_propval *val)
+{
+#define BASE_UNIT_CONVERSION           1000
+#define BATTERY_MODE_CAP_MULT_WATT     (10 * BASE_UNIT_CONVERSION)
+#define TIME_UNIT_CONVERSION           60
+#define TEMP_KELVIN_TO_CELSIUS         2731
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ENERGY_NOW:
+       case POWER_SUPPLY_PROP_ENERGY_FULL:
+       case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+               /* sbs provides energy in units of 10mWh.
+                * Convert to ÂµWh
+                */
+               val->intval *= BATTERY_MODE_CAP_MULT_WATT;
+               break;
+
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval *= BASE_UNIT_CONVERSION;
+               break;
+
+       case POWER_SUPPLY_PROP_TEMP:
+               /* sbs provides battery temperature in 0.1K
+                * so convert it to 0.1°C
+                */
+               val->intval -= TEMP_KELVIN_TO_CELSIUS;
+               break;
+
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+       case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+               /* sbs provides time to empty and time to full in minutes.
+                * Convert to seconds
+                */
+               val->intval *= TIME_UNIT_CONVERSION;
+               break;
+
+       default:
+               dev_dbg(&client->dev,
+                       "%s: no need for unit conversion %d\n", __func__, psp);
+       }
+}
+
+static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client,
+       enum sbs_battery_mode mode)
+{
+       int ret, original_val;
+
+       original_val = sbs_read_word_data(client, BATTERY_MODE_OFFSET);
+       if (original_val < 0)
+               return original_val;
+
+       if ((original_val & BATTERY_MODE_MASK) == mode)
+               return mode;
+
+       if (mode == BATTERY_MODE_AMPS)
+               ret = original_val & ~BATTERY_MODE_MASK;
+       else
+               ret = original_val | BATTERY_MODE_MASK;
+
+       ret = sbs_write_word_data(client, BATTERY_MODE_OFFSET, ret);
+       if (ret < 0)
+               return ret;
+
+       return original_val & BATTERY_MODE_MASK;
+}
+
+static int sbs_get_battery_capacity(struct i2c_client *client,
+       int reg_offset, enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       s32 ret;
+       enum sbs_battery_mode mode = BATTERY_MODE_WATTS;
+
+       if (power_supply_is_amp_property(psp))
+               mode = BATTERY_MODE_AMPS;
+
+       mode = sbs_set_battery_mode(client, mode);
+       if (mode < 0)
+               return mode;
+
+       ret = sbs_read_word_data(client, sbs_data[reg_offset].addr);
+       if (ret < 0)
+               return ret;
+
+       if (psp == POWER_SUPPLY_PROP_CAPACITY) {
+               /* sbs spec says that this can be >100 %
+               * even if max value is 100 % */
+               val->intval = min(ret, 100);
+       } else
+               val->intval = ret;
+
+       ret = sbs_set_battery_mode(client, mode);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static char sbs_serial[5];
+static int sbs_get_battery_serial_number(struct i2c_client *client,
+       union power_supply_propval *val)
+{
+       int ret;
+
+       ret = sbs_read_word_data(client, sbs_data[REG_SERIAL_NUMBER].addr);
+       if (ret < 0)
+               return ret;
+
+       ret = sprintf(sbs_serial, "%04x", ret);
+       val->strval = sbs_serial;
+
+       return 0;
+}
+
+static int sbs_get_property_index(struct i2c_client *client,
+       enum power_supply_property psp)
+{
+       int count;
+       for (count = 0; count < ARRAY_SIZE(sbs_data); count++)
+               if (psp == sbs_data[count].psp)
+                       return count;
+
+       dev_warn(&client->dev,
+               "%s: Invalid Property - %d\n", __func__, psp);
+
+       return -EINVAL;
+}
+
+static int sbs_get_property(struct power_supply *psy,
+       enum power_supply_property psp,
+       union power_supply_propval *val)
+{
+       int ret = 0;
+       struct sbs_info *chip = power_supply_get_drvdata(psy);
+       struct i2c_client *client = chip->client;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = sbs_get_battery_presence_and_health(client, psp, val);
+               if (psp == POWER_SUPPLY_PROP_PRESENT)
+                       return 0;
+               break;
+
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               goto done; /* don't trigger power_supply_changed()! */
+
+       case POWER_SUPPLY_PROP_ENERGY_NOW:
+       case POWER_SUPPLY_PROP_ENERGY_FULL:
+       case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = sbs_get_property_index(client, psp);
+               if (ret < 0)
+                       break;
+
+               ret = sbs_get_battery_capacity(client, ret, psp, val);
+               break;
+
+       case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+               ret = sbs_get_battery_serial_number(client, val);
+               break;
+
+       case POWER_SUPPLY_PROP_STATUS:
+       case POWER_SUPPLY_PROP_CYCLE_COUNT:
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+       case POWER_SUPPLY_PROP_TEMP:
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+       case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               ret = sbs_get_property_index(client, psp);
+               if (ret < 0)
+                       break;
+
+               ret = sbs_get_battery_property(client, ret, psp, val);
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               ret = sbs_get_property_index(client, psp);
+               if (ret < 0)
+                       break;
+
+               ret = sbs_get_battery_string_property(client, ret, psp,
+                                                     model_name);
+               val->strval = model_name;
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               ret = sbs_get_property_index(client, psp);
+               if (ret < 0)
+                       break;
+
+               ret = sbs_get_battery_string_property(client, ret, psp,
+                                                     manufacturer);
+               val->strval = manufacturer;
+               break;
+
+       default:
+               dev_err(&client->dev,
+                       "%s: INVALID property\n", __func__);
+               return -EINVAL;
+       }
+
+       if (!chip->enable_detection)
+               goto done;
+
+       if (!chip->gpio_detect &&
+               chip->is_present != (ret >= 0)) {
+               chip->is_present = (ret >= 0);
+               power_supply_changed(chip->power_supply);
+       }
+
+done:
+       if (!ret) {
+               /* Convert units to match requirements for power supply class */
+               sbs_unit_adjustment(client, psp, val);
+       }
+
+       dev_dbg(&client->dev,
+               "%s: property = %d, value = %x\n", __func__, psp, val->intval);
+
+       if (ret && chip->is_present)
+               return ret;
+
+       /* battery not present, so return NODATA for properties */
+       if (ret)
+               return -ENODATA;
+
+       return 0;
+}
+
+static irqreturn_t sbs_irq(int irq, void *devid)
+{
+       struct power_supply *battery = devid;
+
+       power_supply_changed(battery);
+
+       return IRQ_HANDLED;
+}
+
+static void sbs_external_power_changed(struct power_supply *psy)
+{
+       struct sbs_info *chip = power_supply_get_drvdata(psy);
+
+       if (chip->ignore_changes > 0) {
+               chip->ignore_changes--;
+               return;
+       }
+
+       /* cancel outstanding work */
+       cancel_delayed_work_sync(&chip->work);
+
+       schedule_delayed_work(&chip->work, HZ);
+       chip->poll_time = chip->pdata->poll_retry_count;
+}
+
+static void sbs_delayed_work(struct work_struct *work)
+{
+       struct sbs_info *chip;
+       s32 ret;
+
+       chip = container_of(work, struct sbs_info, work.work);
+
+       ret = sbs_read_word_data(chip->client, sbs_data[REG_STATUS].addr);
+       /* if the read failed, give up on this work */
+       if (ret < 0) {
+               chip->poll_time = 0;
+               return;
+       }
+
+       if (ret & BATTERY_FULL_CHARGED)
+               ret = POWER_SUPPLY_STATUS_FULL;
+       else if (ret & BATTERY_DISCHARGING)
+               ret = POWER_SUPPLY_STATUS_DISCHARGING;
+       else
+               ret = POWER_SUPPLY_STATUS_CHARGING;
+
+       if (chip->last_state != ret) {
+               chip->poll_time = 0;
+               power_supply_changed(chip->power_supply);
+               return;
+       }
+       if (chip->poll_time > 0) {
+               schedule_delayed_work(&chip->work, HZ);
+               chip->poll_time--;
+               return;
+       }
+}
+
+#if defined(CONFIG_OF)
+
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+static const struct of_device_id sbs_dt_ids[] = {
+       { .compatible = "sbs,sbs-battery" },
+       { .compatible = "ti,bq20z75" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sbs_dt_ids);
+
+static struct sbs_platform_data *sbs_of_populate_pdata(
+               struct i2c_client *client)
+{
+       struct device_node *of_node = client->dev.of_node;
+       struct sbs_platform_data *pdata = client->dev.platform_data;
+       enum of_gpio_flags gpio_flags;
+       int rc;
+       u32 prop;
+
+       /* verify this driver matches this device */
+       if (!of_node)
+               return NULL;
+
+       /* if platform data is set, honor it */
+       if (pdata)
+               return pdata;
+
+       /* first make sure at least one property is set, otherwise
+        * it won't change behavior from running without pdata.
+        */
+       if (!of_get_property(of_node, "sbs,i2c-retry-count", NULL) &&
+               !of_get_property(of_node, "sbs,poll-retry-count", NULL) &&
+               !of_get_property(of_node, "sbs,battery-detect-gpios", NULL))
+               goto of_out;
+
+       pdata = devm_kzalloc(&client->dev, sizeof(struct sbs_platform_data),
+                               GFP_KERNEL);
+       if (!pdata)
+               goto of_out;
+
+       rc = of_property_read_u32(of_node, "sbs,i2c-retry-count", &prop);
+       if (!rc)
+               pdata->i2c_retry_count = prop;
+
+       rc = of_property_read_u32(of_node, "sbs,poll-retry-count", &prop);
+       if (!rc)
+               pdata->poll_retry_count = prop;
+
+       if (!of_get_property(of_node, "sbs,battery-detect-gpios", NULL)) {
+               pdata->battery_detect = -1;
+               goto of_out;
+       }
+
+       pdata->battery_detect = of_get_named_gpio_flags(of_node,
+                       "sbs,battery-detect-gpios", 0, &gpio_flags);
+
+       if (gpio_flags & OF_GPIO_ACTIVE_LOW)
+               pdata->battery_detect_present = 0;
+       else
+               pdata->battery_detect_present = 1;
+
+of_out:
+       return pdata;
+}
+#else
+static struct sbs_platform_data *sbs_of_populate_pdata(
+       struct i2c_client *client)
+{
+       return client->dev.platform_data;
+}
+#endif
+
+static const struct power_supply_desc sbs_default_desc = {
+       .type = POWER_SUPPLY_TYPE_BATTERY,
+       .properties = sbs_properties,
+       .num_properties = ARRAY_SIZE(sbs_properties),
+       .get_property = sbs_get_property,
+       .external_power_changed = sbs_external_power_changed,
+};
+
+static int sbs_probe(struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       struct sbs_info *chip;
+       struct power_supply_desc *sbs_desc;
+       struct sbs_platform_data *pdata = client->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       int rc;
+       int irq;
+
+       sbs_desc = devm_kmemdup(&client->dev, &sbs_default_desc,
+                       sizeof(*sbs_desc), GFP_KERNEL);
+       if (!sbs_desc)
+               return -ENOMEM;
+
+       sbs_desc->name = devm_kasprintf(&client->dev, GFP_KERNEL, "sbs-%s",
+                       dev_name(&client->dev));
+       if (!sbs_desc->name)
+               return -ENOMEM;
+
+       chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->client = client;
+       chip->enable_detection = false;
+       chip->gpio_detect = false;
+       psy_cfg.of_node = client->dev.of_node;
+       psy_cfg.drv_data = chip;
+       /* ignore first notification of external change, it is generated
+        * from the power_supply_register call back
+        */
+       chip->ignore_changes = 1;
+       chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       pdata = sbs_of_populate_pdata(client);
+
+       if (pdata) {
+               chip->gpio_detect = gpio_is_valid(pdata->battery_detect);
+               chip->pdata = pdata;
+       }
+
+       i2c_set_clientdata(client, chip);
+
+       if (!chip->gpio_detect)
+               goto skip_gpio;
+
+       rc = gpio_request(pdata->battery_detect, dev_name(&client->dev));
+       if (rc) {
+               dev_warn(&client->dev, "Failed to request gpio: %d\n", rc);
+               chip->gpio_detect = false;
+               goto skip_gpio;
+       }
+
+       rc = gpio_direction_input(pdata->battery_detect);
+       if (rc) {
+               dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc);
+               gpio_free(pdata->battery_detect);
+               chip->gpio_detect = false;
+               goto skip_gpio;
+       }
+
+       irq = gpio_to_irq(pdata->battery_detect);
+       if (irq <= 0) {
+               dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq);
+               gpio_free(pdata->battery_detect);
+               chip->gpio_detect = false;
+               goto skip_gpio;
+       }
+
+       rc = request_irq(irq, sbs_irq,
+               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+               dev_name(&client->dev), chip->power_supply);
+       if (rc) {
+               dev_warn(&client->dev, "Failed to request irq: %d\n", rc);
+               gpio_free(pdata->battery_detect);
+               chip->gpio_detect = false;
+               goto skip_gpio;
+       }
+
+       chip->irq = irq;
+
+skip_gpio:
+       /*
+        * Before we register, we might need to make sure we can actually talk
+        * to the battery.
+        */
+       if (!force_load) {
+               rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr);
+
+               if (rc < 0) {
+                       dev_err(&client->dev, "%s: Failed to get device status\n",
+                               __func__);
+                       goto exit_psupply;
+               }
+       }
+
+       chip->power_supply = power_supply_register(&client->dev, sbs_desc,
+                                                  &psy_cfg);
+       if (IS_ERR(chip->power_supply)) {
+               dev_err(&client->dev,
+                       "%s: Failed to register power supply\n", __func__);
+               rc = PTR_ERR(chip->power_supply);
+               goto exit_psupply;
+       }
+
+       dev_info(&client->dev,
+               "%s: battery gas gauge device registered\n", client->name);
+
+       INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
+
+       chip->enable_detection = true;
+
+       return 0;
+
+exit_psupply:
+       if (chip->irq)
+               free_irq(chip->irq, chip->power_supply);
+       if (chip->gpio_detect)
+               gpio_free(pdata->battery_detect);
+
+       kfree(chip);
+
+       return rc;
+}
+
+static int sbs_remove(struct i2c_client *client)
+{
+       struct sbs_info *chip = i2c_get_clientdata(client);
+
+       if (chip->irq)
+               free_irq(chip->irq, chip->power_supply);
+       if (chip->gpio_detect)
+               gpio_free(chip->pdata->battery_detect);
+
+       power_supply_unregister(chip->power_supply);
+
+       cancel_delayed_work_sync(&chip->work);
+
+       kfree(chip);
+       chip = NULL;
+
+       return 0;
+}
+
+#if defined CONFIG_PM_SLEEP
+
+static int sbs_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct sbs_info *chip = i2c_get_clientdata(client);
+       s32 ret;
+
+       if (chip->poll_time > 0)
+               cancel_delayed_work_sync(&chip->work);
+
+       /* write to manufacturer access with sleep command */
+       ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
+               MANUFACTURER_ACCESS_SLEEP);
+       if (chip->is_present && ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL);
+#define SBS_PM_OPS (&sbs_pm_ops)
+
+#else
+#define SBS_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id sbs_id[] = {
+       { "bq20z75", 0 },
+       { "sbs-battery", 1 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, sbs_id);
+
+static struct i2c_driver sbs_battery_driver = {
+       .probe          = sbs_probe,
+       .remove         = sbs_remove,
+       .id_table       = sbs_id,
+       .driver = {
+               .name   = "sbs-battery",
+               .of_match_table = of_match_ptr(sbs_dt_ids),
+               .pm     = SBS_PM_OPS,
+       },
+};
+module_i2c_driver(sbs_battery_driver);
+
+MODULE_DESCRIPTION("SBS battery monitor driver");
+MODULE_LICENSE("GPL");
+
+module_param(force_load, bool, S_IRUSR | S_IRGRP | S_IROTH);
+MODULE_PARM_DESC(force_load,
+                "Attempt to load the driver even if no battery is connected");
diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c
new file mode 100644 (file)
index 0000000..072c518
--- /dev/null
@@ -0,0 +1,1334 @@
+/*
+ * Summit Microelectronics SMB347 Battery Charger Driver
+ *
+ * Copyright (C) 2011, Intel Corporation
+ *
+ * Authors: Bruce E. Robertson <bruce.e.robertson@intel.com>
+ *          Mika Westerberg <mika.westerberg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/power/smb347-charger.h>
+#include <linux/regmap.h>
+
+/*
+ * Configuration registers. These are mirrored to volatile RAM and can be
+ * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be
+ * reloaded from non-volatile registers after POR.
+ */
+#define CFG_CHARGE_CURRENT                     0x00
+#define CFG_CHARGE_CURRENT_FCC_MASK            0xe0
+#define CFG_CHARGE_CURRENT_FCC_SHIFT           5
+#define CFG_CHARGE_CURRENT_PCC_MASK            0x18
+#define CFG_CHARGE_CURRENT_PCC_SHIFT           3
+#define CFG_CHARGE_CURRENT_TC_MASK             0x07
+#define CFG_CURRENT_LIMIT                      0x01
+#define CFG_CURRENT_LIMIT_DC_MASK              0xf0
+#define CFG_CURRENT_LIMIT_DC_SHIFT             4
+#define CFG_CURRENT_LIMIT_USB_MASK             0x0f
+#define CFG_FLOAT_VOLTAGE                      0x03
+#define CFG_FLOAT_VOLTAGE_FLOAT_MASK           0x3f
+#define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK       0xc0
+#define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT      6
+#define CFG_STAT                               0x05
+#define CFG_STAT_DISABLED                      BIT(5)
+#define CFG_STAT_ACTIVE_HIGH                   BIT(7)
+#define CFG_PIN                                        0x06
+#define CFG_PIN_EN_CTRL_MASK                   0x60
+#define CFG_PIN_EN_CTRL_ACTIVE_HIGH            0x40
+#define CFG_PIN_EN_CTRL_ACTIVE_LOW             0x60
+#define CFG_PIN_EN_APSD_IRQ                    BIT(1)
+#define CFG_PIN_EN_CHARGER_ERROR               BIT(2)
+#define CFG_THERM                              0x07
+#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK   0x03
+#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT  0
+#define CFG_THERM_SOFT_COLD_COMPENSATION_MASK  0x0c
+#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
+#define CFG_THERM_MONITOR_DISABLED             BIT(4)
+#define CFG_SYSOK                              0x08
+#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED  BIT(2)
+#define CFG_OTHER                              0x09
+#define CFG_OTHER_RID_MASK                     0xc0
+#define CFG_OTHER_RID_ENABLED_AUTO_OTG         0xc0
+#define CFG_OTG                                        0x0a
+#define CFG_OTG_TEMP_THRESHOLD_MASK            0x30
+#define CFG_OTG_TEMP_THRESHOLD_SHIFT           4
+#define CFG_OTG_CC_COMPENSATION_MASK           0xc0
+#define CFG_OTG_CC_COMPENSATION_SHIFT          6
+#define CFG_TEMP_LIMIT                         0x0b
+#define CFG_TEMP_LIMIT_SOFT_HOT_MASK           0x03
+#define CFG_TEMP_LIMIT_SOFT_HOT_SHIFT          0
+#define CFG_TEMP_LIMIT_SOFT_COLD_MASK          0x0c
+#define CFG_TEMP_LIMIT_SOFT_COLD_SHIFT         2
+#define CFG_TEMP_LIMIT_HARD_HOT_MASK           0x30
+#define CFG_TEMP_LIMIT_HARD_HOT_SHIFT          4
+#define CFG_TEMP_LIMIT_HARD_COLD_MASK          0xc0
+#define CFG_TEMP_LIMIT_HARD_COLD_SHIFT         6
+#define CFG_FAULT_IRQ                          0x0c
+#define CFG_FAULT_IRQ_DCIN_UV                  BIT(2)
+#define CFG_STATUS_IRQ                         0x0d
+#define CFG_STATUS_IRQ_TERMINATION_OR_TAPER    BIT(4)
+#define CFG_STATUS_IRQ_CHARGE_TIMEOUT          BIT(7)
+#define CFG_ADDRESS                            0x0e
+
+/* Command registers */
+#define CMD_A                                  0x30
+#define CMD_A_CHG_ENABLED                      BIT(1)
+#define CMD_A_SUSPEND_ENABLED                  BIT(2)
+#define CMD_A_ALLOW_WRITE                      BIT(7)
+#define CMD_B                                  0x31
+#define CMD_C                                  0x33
+
+/* Interrupt Status registers */
+#define IRQSTAT_A                              0x35
+#define IRQSTAT_C                              0x37
+#define IRQSTAT_C_TERMINATION_STAT             BIT(0)
+#define IRQSTAT_C_TERMINATION_IRQ              BIT(1)
+#define IRQSTAT_C_TAPER_IRQ                    BIT(3)
+#define IRQSTAT_D                              0x38
+#define IRQSTAT_D_CHARGE_TIMEOUT_STAT          BIT(2)
+#define IRQSTAT_D_CHARGE_TIMEOUT_IRQ           BIT(3)
+#define IRQSTAT_E                              0x39
+#define IRQSTAT_E_USBIN_UV_STAT                        BIT(0)
+#define IRQSTAT_E_USBIN_UV_IRQ                 BIT(1)
+#define IRQSTAT_E_DCIN_UV_STAT                 BIT(4)
+#define IRQSTAT_E_DCIN_UV_IRQ                  BIT(5)
+#define IRQSTAT_F                              0x3a
+
+/* Status registers */
+#define STAT_A                                 0x3b
+#define STAT_A_FLOAT_VOLTAGE_MASK              0x3f
+#define STAT_B                                 0x3c
+#define STAT_C                                 0x3d
+#define STAT_C_CHG_ENABLED                     BIT(0)
+#define STAT_C_HOLDOFF_STAT                    BIT(3)
+#define STAT_C_CHG_MASK                                0x06
+#define STAT_C_CHG_SHIFT                       1
+#define STAT_C_CHG_TERM                                BIT(5)
+#define STAT_C_CHARGER_ERROR                   BIT(6)
+#define STAT_E                                 0x3f
+
+#define SMB347_MAX_REGISTER                    0x3f
+
+/**
+ * struct smb347_charger - smb347 charger instance
+ * @lock: protects concurrent access to online variables
+ * @dev: pointer to device
+ * @regmap: pointer to driver regmap
+ * @mains: power_supply instance for AC/DC power
+ * @usb: power_supply instance for USB power
+ * @battery: power_supply instance for battery
+ * @mains_online: is AC/DC input connected
+ * @usb_online: is USB input connected
+ * @charging_enabled: is charging enabled
+ * @pdata: pointer to platform data
+ */
+struct smb347_charger {
+       struct mutex            lock;
+       struct device           *dev;
+       struct regmap           *regmap;
+       struct power_supply     *mains;
+       struct power_supply     *usb;
+       struct power_supply     *battery;
+       bool                    mains_online;
+       bool                    usb_online;
+       bool                    charging_enabled;
+       const struct smb347_charger_platform_data *pdata;
+};
+
+/* Fast charge current in uA */
+static const unsigned int fcc_tbl[] = {
+       700000,
+       900000,
+       1200000,
+       1500000,
+       1800000,
+       2000000,
+       2200000,
+       2500000,
+};
+
+/* Pre-charge current in uA */
+static const unsigned int pcc_tbl[] = {
+       100000,
+       150000,
+       200000,
+       250000,
+};
+
+/* Termination current in uA */
+static const unsigned int tc_tbl[] = {
+       37500,
+       50000,
+       100000,
+       150000,
+       200000,
+       250000,
+       500000,
+       600000,
+};
+
+/* Input current limit in uA */
+static const unsigned int icl_tbl[] = {
+       300000,
+       500000,
+       700000,
+       900000,
+       1200000,
+       1500000,
+       1800000,
+       2000000,
+       2200000,
+       2500000,
+};
+
+/* Charge current compensation in uA */
+static const unsigned int ccc_tbl[] = {
+       250000,
+       700000,
+       900000,
+       1200000,
+};
+
+/* Convert register value to current using lookup table */
+static int hw_to_current(const unsigned int *tbl, size_t size, unsigned int val)
+{
+       if (val >= size)
+               return -EINVAL;
+       return tbl[val];
+}
+
+/* Convert current to register value using lookup table */
+static int current_to_hw(const unsigned int *tbl, size_t size, unsigned int val)
+{
+       size_t i;
+
+       for (i = 0; i < size; i++)
+               if (val < tbl[i])
+                       break;
+       return i > 0 ? i - 1 : -EINVAL;
+}
+
+/**
+ * smb347_update_ps_status - refreshes the power source status
+ * @smb: pointer to smb347 charger instance
+ *
+ * Function checks whether any power source is connected to the charger and
+ * updates internal state accordingly. If there is a change to previous state
+ * function returns %1, otherwise %0 and negative errno in case of errror.
+ */
+static int smb347_update_ps_status(struct smb347_charger *smb)
+{
+       bool usb = false;
+       bool dc = false;
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(smb->regmap, IRQSTAT_E, &val);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Dc and usb are set depending on whether they are enabled in
+        * platform data _and_ whether corresponding undervoltage is set.
+        */
+       if (smb->pdata->use_mains)
+               dc = !(val & IRQSTAT_E_DCIN_UV_STAT);
+       if (smb->pdata->use_usb)
+               usb = !(val & IRQSTAT_E_USBIN_UV_STAT);
+
+       mutex_lock(&smb->lock);
+       ret = smb->mains_online != dc || smb->usb_online != usb;
+       smb->mains_online = dc;
+       smb->usb_online = usb;
+       mutex_unlock(&smb->lock);
+
+       return ret;
+}
+
+/*
+ * smb347_is_ps_online - returns whether input power source is connected
+ * @smb: pointer to smb347 charger instance
+ *
+ * Returns %true if input power source is connected. Note that this is
+ * dependent on what platform has configured for usable power sources. For
+ * example if USB is disabled, this will return %false even if the USB cable
+ * is connected.
+ */
+static bool smb347_is_ps_online(struct smb347_charger *smb)
+{
+       bool ret;
+
+       mutex_lock(&smb->lock);
+       ret = smb->usb_online || smb->mains_online;
+       mutex_unlock(&smb->lock);
+
+       return ret;
+}
+
+/**
+ * smb347_charging_status - returns status of charging
+ * @smb: pointer to smb347 charger instance
+ *
+ * Function returns charging status. %0 means no charging is in progress,
+ * %1 means pre-charging, %2 fast-charging and %3 taper-charging.
+ */
+static int smb347_charging_status(struct smb347_charger *smb)
+{
+       unsigned int val;
+       int ret;
+
+       if (!smb347_is_ps_online(smb))
+               return 0;
+
+       ret = regmap_read(smb->regmap, STAT_C, &val);
+       if (ret < 0)
+               return 0;
+
+       return (val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT;
+}
+
+static int smb347_charging_set(struct smb347_charger *smb, bool enable)
+{
+       int ret = 0;
+
+       if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) {
+               dev_dbg(smb->dev, "charging enable/disable in SW disabled\n");
+               return 0;
+       }
+
+       mutex_lock(&smb->lock);
+       if (smb->charging_enabled != enable) {
+               ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
+                                        enable ? CMD_A_CHG_ENABLED : 0);
+               if (!ret)
+                       smb->charging_enabled = enable;
+       }
+       mutex_unlock(&smb->lock);
+       return ret;
+}
+
+static inline int smb347_charging_enable(struct smb347_charger *smb)
+{
+       return smb347_charging_set(smb, true);
+}
+
+static inline int smb347_charging_disable(struct smb347_charger *smb)
+{
+       return smb347_charging_set(smb, false);
+}
+
+static int smb347_start_stop_charging(struct smb347_charger *smb)
+{
+       int ret;
+
+       /*
+        * Depending on whether valid power source is connected or not, we
+        * disable or enable the charging. We do it manually because it
+        * depends on how the platform has configured the valid inputs.
+        */
+       if (smb347_is_ps_online(smb)) {
+               ret = smb347_charging_enable(smb);
+               if (ret < 0)
+                       dev_err(smb->dev, "failed to enable charging\n");
+       } else {
+               ret = smb347_charging_disable(smb);
+               if (ret < 0)
+                       dev_err(smb->dev, "failed to disable charging\n");
+       }
+
+       return ret;
+}
+
+static int smb347_set_charge_current(struct smb347_charger *smb)
+{
+       int ret;
+
+       if (smb->pdata->max_charge_current) {
+               ret = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl),
+                                   smb->pdata->max_charge_current);
+               if (ret < 0)
+                       return ret;
+
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_FCC_MASK,
+                                        ret << CFG_CHARGE_CURRENT_FCC_SHIFT);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->pre_charge_current) {
+               ret = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl),
+                                   smb->pdata->pre_charge_current);
+               if (ret < 0)
+                       return ret;
+
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_PCC_MASK,
+                                        ret << CFG_CHARGE_CURRENT_PCC_SHIFT);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->termination_current) {
+               ret = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl),
+                                   smb->pdata->termination_current);
+               if (ret < 0)
+                       return ret;
+
+               ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
+                                        CFG_CHARGE_CURRENT_TC_MASK, ret);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int smb347_set_current_limits(struct smb347_charger *smb)
+{
+       int ret;
+
+       if (smb->pdata->mains_current_limit) {
+               ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+                                   smb->pdata->mains_current_limit);
+               if (ret < 0)
+                       return ret;
+
+               ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
+                                        CFG_CURRENT_LIMIT_DC_MASK,
+                                        ret << CFG_CURRENT_LIMIT_DC_SHIFT);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->usb_hc_current_limit) {
+               ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl),
+                                   smb->pdata->usb_hc_current_limit);
+               if (ret < 0)
+                       return ret;
+
+               ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
+                                        CFG_CURRENT_LIMIT_USB_MASK, ret);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int smb347_set_voltage_limits(struct smb347_charger *smb)
+{
+       int ret;
+
+       if (smb->pdata->pre_to_fast_voltage) {
+               ret = smb->pdata->pre_to_fast_voltage;
+
+               /* uV */
+               ret = clamp_val(ret, 2400000, 3000000) - 2400000;
+               ret /= 200000;
+
+               ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
+                               CFG_FLOAT_VOLTAGE_THRESHOLD_MASK,
+                               ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->max_charge_voltage) {
+               ret = smb->pdata->max_charge_voltage;
+
+               /* uV */
+               ret = clamp_val(ret, 3500000, 4500000) - 3500000;
+               ret /= 20000;
+
+               ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
+                                        CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int smb347_set_temp_limits(struct smb347_charger *smb)
+{
+       bool enable_therm_monitor = false;
+       int ret = 0;
+       int val;
+
+       if (smb->pdata->chip_temp_threshold) {
+               val = smb->pdata->chip_temp_threshold;
+
+               /* degree C */
+               val = clamp_val(val, 100, 130) - 100;
+               val /= 10;
+
+               ret = regmap_update_bits(smb->regmap, CFG_OTG,
+                                        CFG_OTG_TEMP_THRESHOLD_MASK,
+                                        val << CFG_OTG_TEMP_THRESHOLD_SHIFT);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+               val = smb->pdata->soft_cold_temp_limit;
+
+               val = clamp_val(val, 0, 15);
+               val /= 5;
+               /* this goes from higher to lower so invert the value */
+               val = ~val & 0x3;
+
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_SOFT_COLD_MASK,
+                                        val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT);
+               if (ret < 0)
+                       return ret;
+
+               enable_therm_monitor = true;
+       }
+
+       if (smb->pdata->soft_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+               val = smb->pdata->soft_hot_temp_limit;
+
+               val = clamp_val(val, 40, 55) - 40;
+               val /= 5;
+
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_SOFT_HOT_MASK,
+                                        val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT);
+               if (ret < 0)
+                       return ret;
+
+               enable_therm_monitor = true;
+       }
+
+       if (smb->pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+               val = smb->pdata->hard_cold_temp_limit;
+
+               val = clamp_val(val, -5, 10) + 5;
+               val /= 5;
+               /* this goes from higher to lower so invert the value */
+               val = ~val & 0x3;
+
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_HARD_COLD_MASK,
+                                        val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT);
+               if (ret < 0)
+                       return ret;
+
+               enable_therm_monitor = true;
+       }
+
+       if (smb->pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) {
+               val = smb->pdata->hard_hot_temp_limit;
+
+               val = clamp_val(val, 50, 65) - 50;
+               val /= 5;
+
+               ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
+                                        CFG_TEMP_LIMIT_HARD_HOT_MASK,
+                                        val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT);
+               if (ret < 0)
+                       return ret;
+
+               enable_therm_monitor = true;
+       }
+
+       /*
+        * If any of the temperature limits are set, we also enable the
+        * thermistor monitoring.
+        *
+        * When soft limits are hit, the device will start to compensate
+        * current and/or voltage depending on the configuration.
+        *
+        * When hard limit is hit, the device will suspend charging
+        * depending on the configuration.
+        */
+       if (enable_therm_monitor) {
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                        CFG_THERM_MONITOR_DISABLED, 0);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->suspend_on_hard_temp_limit) {
+               ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
+                                CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->soft_temp_limit_compensation !=
+           SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) {
+               val = smb->pdata->soft_temp_limit_compensation & 0x3;
+
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                CFG_THERM_SOFT_HOT_COMPENSATION_MASK,
+                                val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT);
+               if (ret < 0)
+                       return ret;
+
+               ret = regmap_update_bits(smb->regmap, CFG_THERM,
+                                CFG_THERM_SOFT_COLD_COMPENSATION_MASK,
+                                val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (smb->pdata->charge_current_compensation) {
+               val = current_to_hw(ccc_tbl, ARRAY_SIZE(ccc_tbl),
+                                   smb->pdata->charge_current_compensation);
+               if (val < 0)
+                       return val;
+
+               ret = regmap_update_bits(smb->regmap, CFG_OTG,
+                               CFG_OTG_CC_COMPENSATION_MASK,
+                               (val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+/*
+ * smb347_set_writable - enables/disables writing to non-volatile registers
+ * @smb: pointer to smb347 charger instance
+ *
+ * You can enable/disable writing to the non-volatile configuration
+ * registers by calling this function.
+ *
+ * Returns %0 on success and negative errno in case of failure.
+ */
+static int smb347_set_writable(struct smb347_charger *smb, bool writable)
+{
+       return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
+                                 writable ? CMD_A_ALLOW_WRITE : 0);
+}
+
+static int smb347_hw_init(struct smb347_charger *smb)
+{
+       unsigned int val;
+       int ret;
+
+       ret = smb347_set_writable(smb, true);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Program the platform specific configuration values to the device
+        * first.
+        */
+       ret = smb347_set_charge_current(smb);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_set_current_limits(smb);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_set_voltage_limits(smb);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_set_temp_limits(smb);
+       if (ret < 0)
+               goto fail;
+
+       /* If USB charging is disabled we put the USB in suspend mode */
+       if (!smb->pdata->use_usb) {
+               ret = regmap_update_bits(smb->regmap, CMD_A,
+                                        CMD_A_SUSPEND_ENABLED,
+                                        CMD_A_SUSPEND_ENABLED);
+               if (ret < 0)
+                       goto fail;
+       }
+
+       /*
+        * If configured by platform data, we enable hardware Auto-OTG
+        * support for driving VBUS. Otherwise we disable it.
+        */
+       ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK,
+               smb->pdata->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0);
+       if (ret < 0)
+               goto fail;
+
+       /*
+        * Make the charging functionality controllable by a write to the
+        * command register unless pin control is specified in the platform
+        * data.
+        */
+       switch (smb->pdata->enable_control) {
+       case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW:
+               val = CFG_PIN_EN_CTRL_ACTIVE_LOW;
+               break;
+       case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH:
+               val = CFG_PIN_EN_CTRL_ACTIVE_HIGH;
+               break;
+       default:
+               val = 0;
+               break;
+       }
+
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK,
+                                val);
+       if (ret < 0)
+               goto fail;
+
+       /* Disable Automatic Power Source Detection (APSD) interrupt. */
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_update_ps_status(smb);
+       if (ret < 0)
+               goto fail;
+
+       ret = smb347_start_stop_charging(smb);
+
+fail:
+       smb347_set_writable(smb, false);
+       return ret;
+}
+
+static irqreturn_t smb347_interrupt(int irq, void *data)
+{
+       struct smb347_charger *smb = data;
+       unsigned int stat_c, irqstat_c, irqstat_d, irqstat_e;
+       bool handled = false;
+       int ret;
+
+       ret = regmap_read(smb->regmap, STAT_C, &stat_c);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading STAT_C failed\n");
+               return IRQ_NONE;
+       }
+
+       ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading IRQSTAT_C failed\n");
+               return IRQ_NONE;
+       }
+
+       ret = regmap_read(smb->regmap, IRQSTAT_D, &irqstat_d);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading IRQSTAT_D failed\n");
+               return IRQ_NONE;
+       }
+
+       ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e);
+       if (ret < 0) {
+               dev_warn(smb->dev, "reading IRQSTAT_E failed\n");
+               return IRQ_NONE;
+       }
+
+       /*
+        * If we get charger error we report the error back to user.
+        * If the error is recovered charging will resume again.
+        */
+       if (stat_c & STAT_C_CHARGER_ERROR) {
+               dev_err(smb->dev, "charging stopped due to charger error\n");
+               power_supply_changed(smb->battery);
+               handled = true;
+       }
+
+       /*
+        * If we reached the termination current the battery is charged and
+        * we can update the status now. Charging is automatically
+        * disabled by the hardware.
+        */
+       if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) {
+               if (irqstat_c & IRQSTAT_C_TERMINATION_STAT)
+                       power_supply_changed(smb->battery);
+               dev_dbg(smb->dev, "going to HW maintenance mode\n");
+               handled = true;
+       }
+
+       /*
+        * If we got a charger timeout INT that means the charge
+        * full is not detected with in charge timeout value.
+        */
+       if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ) {
+               dev_dbg(smb->dev, "total Charge Timeout INT received\n");
+
+               if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT)
+                       dev_warn(smb->dev, "charging stopped due to timeout\n");
+               power_supply_changed(smb->battery);
+               handled = true;
+       }
+
+       /*
+        * If we got an under voltage interrupt it means that AC/USB input
+        * was connected or disconnected.
+        */
+       if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) {
+               if (smb347_update_ps_status(smb) > 0) {
+                       smb347_start_stop_charging(smb);
+                       if (smb->pdata->use_mains)
+                               power_supply_changed(smb->mains);
+                       if (smb->pdata->use_usb)
+                               power_supply_changed(smb->usb);
+               }
+               handled = true;
+       }
+
+       return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int smb347_irq_set(struct smb347_charger *smb, bool enable)
+{
+       int ret;
+
+       ret = smb347_set_writable(smb, true);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Enable/disable interrupts for:
+        *      - under voltage
+        *      - termination current reached
+        *      - charger timeout
+        *      - charger error
+        */
+       ret = regmap_update_bits(smb->regmap, CFG_FAULT_IRQ, 0xff,
+                                enable ? CFG_FAULT_IRQ_DCIN_UV : 0);
+       if (ret < 0)
+               goto fail;
+
+       ret = regmap_update_bits(smb->regmap, CFG_STATUS_IRQ, 0xff,
+                       enable ? (CFG_STATUS_IRQ_TERMINATION_OR_TAPER |
+                                       CFG_STATUS_IRQ_CHARGE_TIMEOUT) : 0);
+       if (ret < 0)
+               goto fail;
+
+       ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR,
+                                enable ? CFG_PIN_EN_CHARGER_ERROR : 0);
+fail:
+       smb347_set_writable(smb, false);
+       return ret;
+}
+
+static inline int smb347_irq_enable(struct smb347_charger *smb)
+{
+       return smb347_irq_set(smb, true);
+}
+
+static inline int smb347_irq_disable(struct smb347_charger *smb)
+{
+       return smb347_irq_set(smb, false);
+}
+
+static int smb347_irq_init(struct smb347_charger *smb,
+                          struct i2c_client *client)
+{
+       const struct smb347_charger_platform_data *pdata = smb->pdata;
+       int ret, irq = gpio_to_irq(pdata->irq_gpio);
+
+       ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
+       if (ret < 0)
+               goto fail;
+
+       ret = request_threaded_irq(irq, NULL, smb347_interrupt,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  client->name, smb);
+       if (ret < 0)
+               goto fail_gpio;
+
+       ret = smb347_set_writable(smb, true);
+       if (ret < 0)
+               goto fail_irq;
+
+       /*
+        * Configure the STAT output to be suitable for interrupts: disable
+        * all other output (except interrupts) and make it active low.
+        */
+       ret = regmap_update_bits(smb->regmap, CFG_STAT,
+                                CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED,
+                                CFG_STAT_DISABLED);
+       if (ret < 0)
+               goto fail_readonly;
+
+       smb347_set_writable(smb, false);
+       client->irq = irq;
+       return 0;
+
+fail_readonly:
+       smb347_set_writable(smb, false);
+fail_irq:
+       free_irq(irq, smb);
+fail_gpio:
+       gpio_free(pdata->irq_gpio);
+fail:
+       client->irq = 0;
+       return ret;
+}
+
+/*
+ * Returns the constant charge current programmed
+ * into the charger in uA.
+ */
+static int get_const_charge_current(struct smb347_charger *smb)
+{
+       int ret, intval;
+       unsigned int v;
+
+       if (!smb347_is_ps_online(smb))
+               return -ENODATA;
+
+       ret = regmap_read(smb->regmap, STAT_B, &v);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * The current value is composition of FCC and PCC values
+        * and we can detect which table to use from bit 5.
+        */
+       if (v & 0x20) {
+               intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), v & 7);
+       } else {
+               v >>= 3;
+               intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), v & 7);
+       }
+
+       return intval;
+}
+
+/*
+ * Returns the constant charge voltage programmed
+ * into the charger in uV.
+ */
+static int get_const_charge_voltage(struct smb347_charger *smb)
+{
+       int ret, intval;
+       unsigned int v;
+
+       if (!smb347_is_ps_online(smb))
+               return -ENODATA;
+
+       ret = regmap_read(smb->regmap, STAT_A, &v);
+       if (ret < 0)
+               return ret;
+
+       v &= STAT_A_FLOAT_VOLTAGE_MASK;
+       if (v > 0x3d)
+               v = 0x3d;
+
+       intval = 3500000 + v * 20000;
+
+       return intval;
+}
+
+static int smb347_mains_get_property(struct power_supply *psy,
+                                    enum power_supply_property prop,
+                                    union power_supply_propval *val)
+{
+       struct smb347_charger *smb = power_supply_get_drvdata(psy);
+       int ret;
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = smb->mains_online;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = get_const_charge_voltage(smb);
+               if (ret < 0)
+                       return ret;
+               else
+                       val->intval = ret;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               ret = get_const_charge_current(smb);
+               if (ret < 0)
+                       return ret;
+               else
+                       val->intval = ret;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property smb347_mains_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+};
+
+static int smb347_usb_get_property(struct power_supply *psy,
+                                  enum power_supply_property prop,
+                                  union power_supply_propval *val)
+{
+       struct smb347_charger *smb = power_supply_get_drvdata(psy);
+       int ret;
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = smb->usb_online;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = get_const_charge_voltage(smb);
+               if (ret < 0)
+                       return ret;
+               else
+                       val->intval = ret;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               ret = get_const_charge_current(smb);
+               if (ret < 0)
+                       return ret;
+               else
+                       val->intval = ret;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property smb347_usb_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+};
+
+static int smb347_get_charging_status(struct smb347_charger *smb)
+{
+       int ret, status;
+       unsigned int val;
+
+       if (!smb347_is_ps_online(smb))
+               return POWER_SUPPLY_STATUS_DISCHARGING;
+
+       ret = regmap_read(smb->regmap, STAT_C, &val);
+       if (ret < 0)
+               return ret;
+
+       if ((val & STAT_C_CHARGER_ERROR) ||
+                       (val & STAT_C_HOLDOFF_STAT)) {
+               /*
+                * set to NOT CHARGING upon charger error
+                * or charging has stopped.
+                */
+               status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               if ((val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT) {
+                       /*
+                        * set to charging if battery is in pre-charge,
+                        * fast charge or taper charging mode.
+                        */
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+               } else if (val & STAT_C_CHG_TERM) {
+                       /*
+                        * set the status to FULL if battery is not in pre
+                        * charge, fast charge or taper charging mode AND
+                        * charging is terminated at least once.
+                        */
+                       status = POWER_SUPPLY_STATUS_FULL;
+               } else {
+                       /*
+                        * in this case no charger error or termination
+                        * occured but charging is not in progress!!!
+                        */
+                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               }
+       }
+
+       return status;
+}
+
+static int smb347_battery_get_property(struct power_supply *psy,
+                                      enum power_supply_property prop,
+                                      union power_supply_propval *val)
+{
+       struct smb347_charger *smb = power_supply_get_drvdata(psy);
+       const struct smb347_charger_platform_data *pdata = smb->pdata;
+       int ret;
+
+       ret = smb347_update_ps_status(smb);
+       if (ret < 0)
+               return ret;
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = smb347_get_charging_status(smb);
+               if (ret < 0)
+                       return ret;
+               val->intval = ret;
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               if (!smb347_is_ps_online(smb))
+                       return -ENODATA;
+
+               /*
+                * We handle trickle and pre-charging the same, and taper
+                * and none the same.
+                */
+               switch (smb347_charging_status(smb)) {
+               case 1:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+                       break;
+               case 2:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+                       break;
+               default:
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+                       break;
+               }
+               break;
+
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = pdata->battery_info.technology;
+               break;
+
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = pdata->battery_info.voltage_min_design;
+               break;
+
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = pdata->battery_info.voltage_max_design;
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = pdata->battery_info.charge_full_design;
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = pdata->battery_info.name;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property smb347_battery_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static bool smb347_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case IRQSTAT_A:
+       case IRQSTAT_C:
+       case IRQSTAT_E:
+       case IRQSTAT_F:
+       case STAT_A:
+       case STAT_B:
+       case STAT_C:
+       case STAT_E:
+               return true;
+       }
+
+       return false;
+}
+
+static bool smb347_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CFG_CHARGE_CURRENT:
+       case CFG_CURRENT_LIMIT:
+       case CFG_FLOAT_VOLTAGE:
+       case CFG_STAT:
+       case CFG_PIN:
+       case CFG_THERM:
+       case CFG_SYSOK:
+       case CFG_OTHER:
+       case CFG_OTG:
+       case CFG_TEMP_LIMIT:
+       case CFG_FAULT_IRQ:
+       case CFG_STATUS_IRQ:
+       case CFG_ADDRESS:
+       case CMD_A:
+       case CMD_B:
+       case CMD_C:
+               return true;
+       }
+
+       return smb347_volatile_reg(dev, reg);
+}
+
+static const struct regmap_config smb347_regmap = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = SMB347_MAX_REGISTER,
+       .volatile_reg   = smb347_volatile_reg,
+       .readable_reg   = smb347_readable_reg,
+};
+
+static const struct power_supply_desc smb347_mains_desc = {
+       .name           = "smb347-mains",
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+       .get_property   = smb347_mains_get_property,
+       .properties     = smb347_mains_properties,
+       .num_properties = ARRAY_SIZE(smb347_mains_properties),
+};
+
+static const struct power_supply_desc smb347_usb_desc = {
+       .name           = "smb347-usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .get_property   = smb347_usb_get_property,
+       .properties     = smb347_usb_properties,
+       .num_properties = ARRAY_SIZE(smb347_usb_properties),
+};
+
+static const struct power_supply_desc smb347_battery_desc = {
+       .name           = "smb347-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property   = smb347_battery_get_property,
+       .properties     = smb347_battery_properties,
+       .num_properties = ARRAY_SIZE(smb347_battery_properties),
+};
+
+static int smb347_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       static char *battery[] = { "smb347-battery" };
+       const struct smb347_charger_platform_data *pdata;
+       struct power_supply_config mains_usb_cfg = {}, battery_cfg = {};
+       struct device *dev = &client->dev;
+       struct smb347_charger *smb;
+       int ret;
+
+       pdata = dev->platform_data;
+       if (!pdata)
+               return -EINVAL;
+
+       if (!pdata->use_mains && !pdata->use_usb)
+               return -EINVAL;
+
+       smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL);
+       if (!smb)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, smb);
+
+       mutex_init(&smb->lock);
+       smb->dev = &client->dev;
+       smb->pdata = pdata;
+
+       smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap);
+       if (IS_ERR(smb->regmap))
+               return PTR_ERR(smb->regmap);
+
+       ret = smb347_hw_init(smb);
+       if (ret < 0)
+               return ret;
+
+       mains_usb_cfg.supplied_to = battery;
+       mains_usb_cfg.num_supplicants = ARRAY_SIZE(battery);
+       mains_usb_cfg.drv_data = smb;
+       if (smb->pdata->use_mains) {
+               smb->mains = power_supply_register(dev, &smb347_mains_desc,
+                                                  &mains_usb_cfg);
+               if (IS_ERR(smb->mains))
+                       return PTR_ERR(smb->mains);
+       }
+
+       if (smb->pdata->use_usb) {
+               smb->usb = power_supply_register(dev, &smb347_usb_desc,
+                                                &mains_usb_cfg);
+               if (IS_ERR(smb->usb)) {
+                       if (smb->pdata->use_mains)
+                               power_supply_unregister(smb->mains);
+                       return PTR_ERR(smb->usb);
+               }
+       }
+
+       battery_cfg.drv_data = smb;
+       smb->battery = power_supply_register(dev, &smb347_battery_desc,
+                                            &battery_cfg);
+       if (IS_ERR(smb->battery)) {
+               if (smb->pdata->use_usb)
+                       power_supply_unregister(smb->usb);
+               if (smb->pdata->use_mains)
+                       power_supply_unregister(smb->mains);
+               return PTR_ERR(smb->battery);
+       }
+
+       /*
+        * Interrupt pin is optional. If it is connected, we setup the
+        * interrupt support here.
+        */
+       if (pdata->irq_gpio >= 0) {
+               ret = smb347_irq_init(smb, client);
+               if (ret < 0) {
+                       dev_warn(dev, "failed to initialize IRQ: %d\n", ret);
+                       dev_warn(dev, "disabling IRQ support\n");
+               } else {
+                       smb347_irq_enable(smb);
+               }
+       }
+
+       return 0;
+}
+
+static int smb347_remove(struct i2c_client *client)
+{
+       struct smb347_charger *smb = i2c_get_clientdata(client);
+
+       if (client->irq) {
+               smb347_irq_disable(smb);
+               free_irq(client->irq, smb);
+               gpio_free(smb->pdata->irq_gpio);
+       }
+
+       power_supply_unregister(smb->battery);
+       if (smb->pdata->use_usb)
+               power_supply_unregister(smb->usb);
+       if (smb->pdata->use_mains)
+               power_supply_unregister(smb->mains);
+       return 0;
+}
+
+static const struct i2c_device_id smb347_id[] = {
+       { "smb347", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, smb347_id);
+
+static struct i2c_driver smb347_driver = {
+       .driver = {
+               .name = "smb347",
+       },
+       .probe        = smb347_probe,
+       .remove       = smb347_remove,
+       .id_table     = smb347_id,
+};
+
+module_i2c_driver(smb347_driver);
+
+MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_DESCRIPTION("SMB347 battery charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c
new file mode 100644 (file)
index 0000000..57246cd
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Power supply driver for testing.
+ *
+ * Copyright 2010  Anton Vorontsov <cbouatmailru@gmail.com>
+ *
+ * Dynamic module parameter code from the Virtual Battery Driver
+ * Copyright (C) 2008 Pylone, Inc.
+ * By: Masashi YOKOTA <yokota@pylone.jp>
+ * Originally found here:
+ * http://downloads.pylone.jp/src/virtual_battery/virtual_battery-0.0.1.tar.bz2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/vermagic.h>
+
+enum test_power_id {
+       TEST_AC,
+       TEST_BATTERY,
+       TEST_USB,
+       TEST_POWER_NUM,
+};
+
+static int ac_online                   = 1;
+static int usb_online                  = 1;
+static int battery_status              = POWER_SUPPLY_STATUS_DISCHARGING;
+static int battery_health              = POWER_SUPPLY_HEALTH_GOOD;
+static int battery_present             = 1; /* true */
+static int battery_technology          = POWER_SUPPLY_TECHNOLOGY_LION;
+static int battery_capacity            = 50;
+static int battery_voltage             = 3300;
+
+static bool module_initialized;
+
+static int test_power_get_ac_property(struct power_supply *psy,
+                                     enum power_supply_property psp,
+                                     union power_supply_propval *val)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = ac_online;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int test_power_get_usb_property(struct power_supply *psy,
+                                     enum power_supply_property psp,
+                                     union power_supply_propval *val)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = usb_online;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int test_power_get_battery_property(struct power_supply *psy,
+                                          enum power_supply_property psp,
+                                          union power_supply_propval *val)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = "Test battery";
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = "Linux";
+               break;
+       case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+               val->strval = UTS_RELEASE;
+               break;
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = battery_status;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = battery_health;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = battery_present;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = battery_technology;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+               val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               val->intval = battery_capacity;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               val->intval = 100;
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+       case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
+               val->intval = 3600;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = 26;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = battery_voltage;
+               break;
+       default:
+               pr_info("%s: some properties deliberately report errors.\n",
+                       __func__);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static enum power_supply_property test_power_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property test_power_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_SERIAL_NUMBER,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static char *test_power_ac_supplied_to[] = {
+       "test_battery",
+};
+
+static struct power_supply *test_power_supplies[TEST_POWER_NUM];
+
+static const struct power_supply_desc test_power_desc[] = {
+       [TEST_AC] = {
+               .name = "test_ac",
+               .type = POWER_SUPPLY_TYPE_MAINS,
+               .properties = test_power_ac_props,
+               .num_properties = ARRAY_SIZE(test_power_ac_props),
+               .get_property = test_power_get_ac_property,
+       },
+       [TEST_BATTERY] = {
+               .name = "test_battery",
+               .type = POWER_SUPPLY_TYPE_BATTERY,
+               .properties = test_power_battery_props,
+               .num_properties = ARRAY_SIZE(test_power_battery_props),
+               .get_property = test_power_get_battery_property,
+       },
+       [TEST_USB] = {
+               .name = "test_usb",
+               .type = POWER_SUPPLY_TYPE_USB,
+               .properties = test_power_ac_props,
+               .num_properties = ARRAY_SIZE(test_power_ac_props),
+               .get_property = test_power_get_usb_property,
+       },
+};
+
+static const struct power_supply_config test_power_configs[] = {
+       {
+               /* test_ac */
+               .supplied_to = test_power_ac_supplied_to,
+               .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to),
+       }, {
+               /* test_battery */
+       }, {
+               /* test_usb */
+               .supplied_to = test_power_ac_supplied_to,
+               .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to),
+       },
+};
+
+static int __init test_power_init(void)
+{
+       int i;
+       int ret;
+
+       BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_supplies));
+       BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_configs));
+
+       for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) {
+               test_power_supplies[i] = power_supply_register(NULL,
+                                               &test_power_desc[i],
+                                               &test_power_configs[i]);
+               if (IS_ERR(test_power_supplies[i])) {
+                       pr_err("%s: failed to register %s\n", __func__,
+                               test_power_desc[i].name);
+                       ret = PTR_ERR(test_power_supplies[i]);
+                       goto failed;
+               }
+       }
+
+       module_initialized = true;
+       return 0;
+failed:
+       while (--i >= 0)
+               power_supply_unregister(test_power_supplies[i]);
+       return ret;
+}
+module_init(test_power_init);
+
+static void __exit test_power_exit(void)
+{
+       int i;
+
+       /* Let's see how we handle changes... */
+       ac_online = 0;
+       usb_online = 0;
+       battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+       for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
+               power_supply_changed(test_power_supplies[i]);
+       pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n",
+               __func__);
+       ssleep(10);
+
+       for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
+               power_supply_unregister(test_power_supplies[i]);
+
+       module_initialized = false;
+}
+module_exit(test_power_exit);
+
+
+
+#define MAX_KEYLENGTH 256
+struct battery_property_map {
+       int value;
+       char const *key;
+};
+
+static struct battery_property_map map_ac_online[] = {
+       { 0,  "off"  },
+       { 1,  "on" },
+       { -1, NULL  },
+};
+
+static struct battery_property_map map_status[] = {
+       { POWER_SUPPLY_STATUS_CHARGING,     "charging"     },
+       { POWER_SUPPLY_STATUS_DISCHARGING,  "discharging"  },
+       { POWER_SUPPLY_STATUS_NOT_CHARGING, "not-charging" },
+       { POWER_SUPPLY_STATUS_FULL,         "full"         },
+       { -1,                               NULL           },
+};
+
+static struct battery_property_map map_health[] = {
+       { POWER_SUPPLY_HEALTH_GOOD,           "good"        },
+       { POWER_SUPPLY_HEALTH_OVERHEAT,       "overheat"    },
+       { POWER_SUPPLY_HEALTH_DEAD,           "dead"        },
+       { POWER_SUPPLY_HEALTH_OVERVOLTAGE,    "overvoltage" },
+       { POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, "failure"     },
+       { -1,                                 NULL          },
+};
+
+static struct battery_property_map map_present[] = {
+       { 0,  "false" },
+       { 1,  "true"  },
+       { -1, NULL    },
+};
+
+static struct battery_property_map map_technology[] = {
+       { POWER_SUPPLY_TECHNOLOGY_NiMH, "NiMH" },
+       { POWER_SUPPLY_TECHNOLOGY_LION, "LION" },
+       { POWER_SUPPLY_TECHNOLOGY_LIPO, "LIPO" },
+       { POWER_SUPPLY_TECHNOLOGY_LiFe, "LiFe" },
+       { POWER_SUPPLY_TECHNOLOGY_NiCd, "NiCd" },
+       { POWER_SUPPLY_TECHNOLOGY_LiMn, "LiMn" },
+       { -1,                           NULL   },
+};
+
+
+static int map_get_value(struct battery_property_map *map, const char *key,
+                               int def_val)
+{
+       char buf[MAX_KEYLENGTH];
+       int cr;
+
+       strncpy(buf, key, MAX_KEYLENGTH);
+       buf[MAX_KEYLENGTH-1] = '\0';
+
+       cr = strnlen(buf, MAX_KEYLENGTH) - 1;
+       if (cr < 0)
+               return def_val;
+       if (buf[cr] == '\n')
+               buf[cr] = '\0';
+
+       while (map->key) {
+               if (strncasecmp(map->key, buf, MAX_KEYLENGTH) == 0)
+                       return map->value;
+               map++;
+       }
+
+       return def_val;
+}
+
+
+static const char *map_get_key(struct battery_property_map *map, int value,
+                               const char *def_key)
+{
+       while (map->key) {
+               if (map->value == value)
+                       return map->key;
+               map++;
+       }
+
+       return def_key;
+}
+
+static inline void signal_power_supply_changed(struct power_supply *psy)
+{
+       if (module_initialized)
+               power_supply_changed(psy);
+}
+
+static int param_set_ac_online(const char *key, const struct kernel_param *kp)
+{
+       ac_online = map_get_value(map_ac_online, key, ac_online);
+       signal_power_supply_changed(test_power_supplies[TEST_AC]);
+       return 0;
+}
+
+static int param_get_ac_online(char *buffer, const struct kernel_param *kp)
+{
+       strcpy(buffer, map_get_key(map_ac_online, ac_online, "unknown"));
+       return strlen(buffer);
+}
+
+static int param_set_usb_online(const char *key, const struct kernel_param *kp)
+{
+       usb_online = map_get_value(map_ac_online, key, usb_online);
+       signal_power_supply_changed(test_power_supplies[TEST_USB]);
+       return 0;
+}
+
+static int param_get_usb_online(char *buffer, const struct kernel_param *kp)
+{
+       strcpy(buffer, map_get_key(map_ac_online, usb_online, "unknown"));
+       return strlen(buffer);
+}
+
+static int param_set_battery_status(const char *key,
+                                       const struct kernel_param *kp)
+{
+       battery_status = map_get_value(map_status, key, battery_status);
+       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
+       return 0;
+}
+
+static int param_get_battery_status(char *buffer, const struct kernel_param *kp)
+{
+       strcpy(buffer, map_get_key(map_status, battery_status, "unknown"));
+       return strlen(buffer);
+}
+
+static int param_set_battery_health(const char *key,
+                                       const struct kernel_param *kp)
+{
+       battery_health = map_get_value(map_health, key, battery_health);
+       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
+       return 0;
+}
+
+static int param_get_battery_health(char *buffer, const struct kernel_param *kp)
+{
+       strcpy(buffer, map_get_key(map_health, battery_health, "unknown"));
+       return strlen(buffer);
+}
+
+static int param_set_battery_present(const char *key,
+                                       const struct kernel_param *kp)
+{
+       battery_present = map_get_value(map_present, key, battery_present);
+       signal_power_supply_changed(test_power_supplies[TEST_AC]);
+       return 0;
+}
+
+static int param_get_battery_present(char *buffer,
+                                       const struct kernel_param *kp)
+{
+       strcpy(buffer, map_get_key(map_present, battery_present, "unknown"));
+       return strlen(buffer);
+}
+
+static int param_set_battery_technology(const char *key,
+                                       const struct kernel_param *kp)
+{
+       battery_technology = map_get_value(map_technology, key,
+                                               battery_technology);
+       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
+       return 0;
+}
+
+static int param_get_battery_technology(char *buffer,
+                                       const struct kernel_param *kp)
+{
+       strcpy(buffer,
+               map_get_key(map_technology, battery_technology, "unknown"));
+       return strlen(buffer);
+}
+
+static int param_set_battery_capacity(const char *key,
+                                       const struct kernel_param *kp)
+{
+       int tmp;
+
+       if (1 != sscanf(key, "%d", &tmp))
+               return -EINVAL;
+
+       battery_capacity = tmp;
+       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
+       return 0;
+}
+
+#define param_get_battery_capacity param_get_int
+
+static int param_set_battery_voltage(const char *key,
+                                       const struct kernel_param *kp)
+{
+       int tmp;
+
+       if (1 != sscanf(key, "%d", &tmp))
+               return -EINVAL;
+
+       battery_voltage = tmp;
+       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
+       return 0;
+}
+
+#define param_get_battery_voltage param_get_int
+
+static const struct kernel_param_ops param_ops_ac_online = {
+       .set = param_set_ac_online,
+       .get = param_get_ac_online,
+};
+
+static const struct kernel_param_ops param_ops_usb_online = {
+       .set = param_set_usb_online,
+       .get = param_get_usb_online,
+};
+
+static const struct kernel_param_ops param_ops_battery_status = {
+       .set = param_set_battery_status,
+       .get = param_get_battery_status,
+};
+
+static const struct kernel_param_ops param_ops_battery_present = {
+       .set = param_set_battery_present,
+       .get = param_get_battery_present,
+};
+
+static const struct kernel_param_ops param_ops_battery_technology = {
+       .set = param_set_battery_technology,
+       .get = param_get_battery_technology,
+};
+
+static const struct kernel_param_ops param_ops_battery_health = {
+       .set = param_set_battery_health,
+       .get = param_get_battery_health,
+};
+
+static const struct kernel_param_ops param_ops_battery_capacity = {
+       .set = param_set_battery_capacity,
+       .get = param_get_battery_capacity,
+};
+
+static const struct kernel_param_ops param_ops_battery_voltage = {
+       .set = param_set_battery_voltage,
+       .get = param_get_battery_voltage,
+};
+
+#define param_check_ac_online(name, p) __param_check(name, p, void);
+#define param_check_usb_online(name, p) __param_check(name, p, void);
+#define param_check_battery_status(name, p) __param_check(name, p, void);
+#define param_check_battery_present(name, p) __param_check(name, p, void);
+#define param_check_battery_technology(name, p) __param_check(name, p, void);
+#define param_check_battery_health(name, p) __param_check(name, p, void);
+#define param_check_battery_capacity(name, p) __param_check(name, p, void);
+#define param_check_battery_voltage(name, p) __param_check(name, p, void);
+
+
+module_param(ac_online, ac_online, 0644);
+MODULE_PARM_DESC(ac_online, "AC charging state <on|off>");
+
+module_param(usb_online, usb_online, 0644);
+MODULE_PARM_DESC(usb_online, "USB charging state <on|off>");
+
+module_param(battery_status, battery_status, 0644);
+MODULE_PARM_DESC(battery_status,
+       "battery status <charging|discharging|not-charging|full>");
+
+module_param(battery_present, battery_present, 0644);
+MODULE_PARM_DESC(battery_present,
+       "battery presence state <good|overheat|dead|overvoltage|failure>");
+
+module_param(battery_technology, battery_technology, 0644);
+MODULE_PARM_DESC(battery_technology,
+       "battery technology <NiMH|LION|LIPO|LiFe|NiCd|LiMn>");
+
+module_param(battery_health, battery_health, 0644);
+MODULE_PARM_DESC(battery_health,
+       "battery health state <good|overheat|dead|overvoltage|failure>");
+
+module_param(battery_capacity, battery_capacity, 0644);
+MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)");
+
+module_param(battery_voltage, battery_voltage, 0644);
+MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)");
+
+MODULE_DESCRIPTION("Power supply driver for testing");
+MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/supply/tosa_battery.c b/drivers/power/supply/tosa_battery.c
new file mode 100644 (file)
index 0000000..6e88c1b
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * Battery and Power Management code for the Sharp SL-6000x
+ *
+ * Copyright (c) 2005 Dirk Opfer
+ * Copyright (c) 2008 Dmitry Baryshkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/wm97xx.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-types.h>
+#include <mach/tosa.h>
+
+static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
+static struct work_struct bat_work;
+
+struct tosa_bat {
+       int status;
+       struct power_supply *psy;
+       int full_chrg;
+
+       struct mutex work_lock; /* protects data */
+
+       bool (*is_present)(struct tosa_bat *bat);
+       int gpio_full;
+       int gpio_charge_off;
+
+       int technology;
+
+       int gpio_bat;
+       int adc_bat;
+       int adc_bat_divider;
+       int bat_max;
+       int bat_min;
+
+       int gpio_temp;
+       int adc_temp;
+       int adc_temp_divider;
+};
+
+static struct tosa_bat tosa_bat_main;
+static struct tosa_bat tosa_bat_jacket;
+
+static unsigned long tosa_read_bat(struct tosa_bat *bat)
+{
+       unsigned long value = 0;
+
+       if (bat->gpio_bat < 0 || bat->adc_bat < 0)
+               return 0;
+
+       mutex_lock(&bat_lock);
+       gpio_set_value(bat->gpio_bat, 1);
+       msleep(5);
+       value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent),
+                       bat->adc_bat);
+       gpio_set_value(bat->gpio_bat, 0);
+       mutex_unlock(&bat_lock);
+
+       value = value * 1000000 / bat->adc_bat_divider;
+
+       return value;
+}
+
+static unsigned long tosa_read_temp(struct tosa_bat *bat)
+{
+       unsigned long value = 0;
+
+       if (bat->gpio_temp < 0 || bat->adc_temp < 0)
+               return 0;
+
+       mutex_lock(&bat_lock);
+       gpio_set_value(bat->gpio_temp, 1);
+       msleep(5);
+       value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent),
+                       bat->adc_temp);
+       gpio_set_value(bat->gpio_temp, 0);
+       mutex_unlock(&bat_lock);
+
+       value = value * 10000 / bat->adc_temp_divider;
+
+       return value;
+}
+
+static int tosa_bat_get_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       int ret = 0;
+       struct tosa_bat *bat = power_supply_get_drvdata(psy);
+
+       if (bat->is_present && !bat->is_present(bat)
+                       && psp != POWER_SUPPLY_PROP_PRESENT) {
+               return -ENODEV;
+       }
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = bat->status;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = bat->technology;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = tosa_read_bat(bat);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               if (bat->full_chrg == -1)
+                       val->intval = bat->bat_max;
+               else
+                       val->intval = bat->full_chrg;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = bat->bat_max;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = bat->bat_min;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = tosa_read_temp(bat);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = bat->is_present ? bat->is_present(bat) : 1;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static bool tosa_jacket_bat_is_present(struct tosa_bat *bat)
+{
+       return gpio_get_value(TOSA_GPIO_JACKET_DETECT) == 0;
+}
+
+static void tosa_bat_external_power_changed(struct power_supply *psy)
+{
+       schedule_work(&bat_work);
+}
+
+static irqreturn_t tosa_bat_gpio_isr(int irq, void *data)
+{
+       pr_info("tosa_bat_gpio irq\n");
+       schedule_work(&bat_work);
+       return IRQ_HANDLED;
+}
+
+static void tosa_bat_update(struct tosa_bat *bat)
+{
+       int old;
+       struct power_supply *psy = bat->psy;
+
+       mutex_lock(&bat->work_lock);
+
+       old = bat->status;
+
+       if (bat->is_present && !bat->is_present(bat)) {
+               printk(KERN_NOTICE "%s not present\n", psy->desc->name);
+               bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
+               bat->full_chrg = -1;
+       } else if (power_supply_am_i_supplied(psy)) {
+               if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+                       gpio_set_value(bat->gpio_charge_off, 0);
+                       mdelay(15);
+               }
+
+               if (gpio_get_value(bat->gpio_full)) {
+                       if (old == POWER_SUPPLY_STATUS_CHARGING ||
+                                       bat->full_chrg == -1)
+                               bat->full_chrg = tosa_read_bat(bat);
+
+                       gpio_set_value(bat->gpio_charge_off, 1);
+                       bat->status = POWER_SUPPLY_STATUS_FULL;
+               } else {
+                       gpio_set_value(bat->gpio_charge_off, 0);
+                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
+               }
+       } else {
+               gpio_set_value(bat->gpio_charge_off, 1);
+               bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+
+       if (old != bat->status)
+               power_supply_changed(psy);
+
+       mutex_unlock(&bat->work_lock);
+}
+
+static void tosa_bat_work(struct work_struct *work)
+{
+       tosa_bat_update(&tosa_bat_main);
+       tosa_bat_update(&tosa_bat_jacket);
+}
+
+
+static enum power_supply_property tosa_bat_main_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_PRESENT,
+};
+
+static enum power_supply_property tosa_bat_bu_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_PRESENT,
+};
+
+static const struct power_supply_desc tosa_bat_main_desc = {
+       .name           = "main-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = tosa_bat_main_props,
+       .num_properties = ARRAY_SIZE(tosa_bat_main_props),
+       .get_property   = tosa_bat_get_property,
+       .external_power_changed = tosa_bat_external_power_changed,
+       .use_for_apm    = 1,
+};
+
+static const struct power_supply_desc tosa_bat_jacket_desc = {
+       .name           = "jacket-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = tosa_bat_main_props,
+       .num_properties = ARRAY_SIZE(tosa_bat_main_props),
+       .get_property   = tosa_bat_get_property,
+       .external_power_changed = tosa_bat_external_power_changed,
+};
+
+static const struct power_supply_desc tosa_bat_bu_desc = {
+       .name           = "backup-battery",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = tosa_bat_bu_props,
+       .num_properties = ARRAY_SIZE(tosa_bat_bu_props),
+       .get_property   = tosa_bat_get_property,
+       .external_power_changed = tosa_bat_external_power_changed,
+};
+
+static struct tosa_bat tosa_bat_main = {
+       .status = POWER_SUPPLY_STATUS_DISCHARGING,
+       .full_chrg = -1,
+       .psy = NULL,
+
+       .gpio_full = TOSA_GPIO_BAT0_CRG,
+       .gpio_charge_off = TOSA_GPIO_CHARGE_OFF,
+
+       .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
+
+       .gpio_bat = TOSA_GPIO_BAT0_V_ON,
+       .adc_bat = WM97XX_AUX_ID3,
+       .adc_bat_divider = 414,
+       .bat_max = 4310000,
+       .bat_min = 1551 * 1000000 / 414,
+
+       .gpio_temp = TOSA_GPIO_BAT1_TH_ON,
+       .adc_temp = WM97XX_AUX_ID2,
+       .adc_temp_divider = 10000,
+};
+
+static struct tosa_bat tosa_bat_jacket = {
+       .status = POWER_SUPPLY_STATUS_DISCHARGING,
+       .full_chrg = -1,
+       .psy = NULL,
+
+       .is_present = tosa_jacket_bat_is_present,
+       .gpio_full = TOSA_GPIO_BAT1_CRG,
+       .gpio_charge_off = TOSA_GPIO_CHARGE_OFF_JC,
+
+       .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
+
+       .gpio_bat = TOSA_GPIO_BAT1_V_ON,
+       .adc_bat = WM97XX_AUX_ID3,
+       .adc_bat_divider = 414,
+       .bat_max = 4310000,
+       .bat_min = 1551 * 1000000 / 414,
+
+       .gpio_temp = TOSA_GPIO_BAT0_TH_ON,
+       .adc_temp = WM97XX_AUX_ID2,
+       .adc_temp_divider = 10000,
+};
+
+static struct tosa_bat tosa_bat_bu = {
+       .status = POWER_SUPPLY_STATUS_UNKNOWN,
+       .full_chrg = -1,
+       .psy = NULL,
+
+       .gpio_full = -1,
+       .gpio_charge_off = -1,
+
+       .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
+
+       .gpio_bat = TOSA_GPIO_BU_CHRG_ON,
+       .adc_bat = WM97XX_AUX_ID4,
+       .adc_bat_divider = 1266,
+
+       .gpio_temp = -1,
+       .adc_temp = -1,
+       .adc_temp_divider = -1,
+};
+
+static struct gpio tosa_bat_gpios[] = {
+       { TOSA_GPIO_CHARGE_OFF,    GPIOF_OUT_INIT_HIGH, "main charge off" },
+       { TOSA_GPIO_CHARGE_OFF_JC, GPIOF_OUT_INIT_HIGH, "jacket charge off" },
+       { TOSA_GPIO_BAT_SW_ON,     GPIOF_OUT_INIT_LOW,  "battery switch" },
+       { TOSA_GPIO_BAT0_V_ON,     GPIOF_OUT_INIT_LOW,  "main battery" },
+       { TOSA_GPIO_BAT1_V_ON,     GPIOF_OUT_INIT_LOW,  "jacket battery" },
+       { TOSA_GPIO_BAT1_TH_ON,    GPIOF_OUT_INIT_LOW,  "main battery temp" },
+       { TOSA_GPIO_BAT0_TH_ON,    GPIOF_OUT_INIT_LOW,  "jacket battery temp" },
+       { TOSA_GPIO_BU_CHRG_ON,    GPIOF_OUT_INIT_LOW,  "backup battery" },
+       { TOSA_GPIO_BAT0_CRG,      GPIOF_IN,            "main battery full" },
+       { TOSA_GPIO_BAT1_CRG,      GPIOF_IN,            "jacket battery full" },
+       { TOSA_GPIO_BAT0_LOW,      GPIOF_IN,            "main battery low" },
+       { TOSA_GPIO_BAT1_LOW,      GPIOF_IN,            "jacket battery low" },
+       { TOSA_GPIO_JACKET_DETECT, GPIOF_IN,            "jacket detect" },
+};
+
+#ifdef CONFIG_PM
+static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state)
+{
+       /* flush all pending status updates */
+       flush_work(&bat_work);
+       return 0;
+}
+
+static int tosa_bat_resume(struct platform_device *dev)
+{
+       /* things may have changed while we were away */
+       schedule_work(&bat_work);
+       return 0;
+}
+#else
+#define tosa_bat_suspend NULL
+#define tosa_bat_resume NULL
+#endif
+
+static int tosa_bat_probe(struct platform_device *dev)
+{
+       int ret;
+       struct power_supply_config main_psy_cfg = {},
+                                  jacket_psy_cfg = {},
+                                  bu_psy_cfg = {};
+
+       if (!machine_is_tosa())
+               return -ENODEV;
+
+       ret = gpio_request_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
+       if (ret)
+               return ret;
+
+       mutex_init(&tosa_bat_main.work_lock);
+       mutex_init(&tosa_bat_jacket.work_lock);
+
+       INIT_WORK(&bat_work, tosa_bat_work);
+
+       main_psy_cfg.drv_data = &tosa_bat_main;
+       tosa_bat_main.psy = power_supply_register(&dev->dev,
+                                                 &tosa_bat_main_desc,
+                                                 &main_psy_cfg);
+       if (IS_ERR(tosa_bat_main.psy)) {
+               ret = PTR_ERR(tosa_bat_main.psy);
+               goto err_psy_reg_main;
+       }
+
+       jacket_psy_cfg.drv_data = &tosa_bat_jacket;
+       tosa_bat_jacket.psy = power_supply_register(&dev->dev,
+                                                   &tosa_bat_jacket_desc,
+                                                   &jacket_psy_cfg);
+       if (IS_ERR(tosa_bat_jacket.psy)) {
+               ret = PTR_ERR(tosa_bat_jacket.psy);
+               goto err_psy_reg_jacket;
+       }
+
+       bu_psy_cfg.drv_data = &tosa_bat_bu;
+       tosa_bat_bu.psy = power_supply_register(&dev->dev, &tosa_bat_bu_desc,
+                                               &bu_psy_cfg);
+       if (IS_ERR(tosa_bat_bu.psy)) {
+               ret = PTR_ERR(tosa_bat_bu.psy);
+               goto err_psy_reg_bu;
+       }
+
+       ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG),
+                               tosa_bat_gpio_isr,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               "main full", &tosa_bat_main);
+       if (ret)
+               goto err_req_main;
+
+       ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG),
+                               tosa_bat_gpio_isr,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               "jacket full", &tosa_bat_jacket);
+       if (ret)
+               goto err_req_jacket;
+
+       ret = request_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT),
+                               tosa_bat_gpio_isr,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               "jacket detect", &tosa_bat_jacket);
+       if (!ret) {
+               schedule_work(&bat_work);
+               return 0;
+       }
+
+       free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
+err_req_jacket:
+       free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
+err_req_main:
+       power_supply_unregister(tosa_bat_bu.psy);
+err_psy_reg_bu:
+       power_supply_unregister(tosa_bat_jacket.psy);
+err_psy_reg_jacket:
+       power_supply_unregister(tosa_bat_main.psy);
+err_psy_reg_main:
+
+       /* see comment in tosa_bat_remove */
+       cancel_work_sync(&bat_work);
+
+       gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
+       return ret;
+}
+
+static int tosa_bat_remove(struct platform_device *dev)
+{
+       free_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), &tosa_bat_jacket);
+       free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
+       free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
+
+       power_supply_unregister(tosa_bat_bu.psy);
+       power_supply_unregister(tosa_bat_jacket.psy);
+       power_supply_unregister(tosa_bat_main.psy);
+
+       /*
+        * Now cancel the bat_work.  We won't get any more schedules,
+        * since all sources (isr and external_power_changed) are
+        * unregistered now.
+        */
+       cancel_work_sync(&bat_work);
+       gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
+       return 0;
+}
+
+static struct platform_driver tosa_bat_driver = {
+       .driver.name    = "wm97xx-battery",
+       .driver.owner   = THIS_MODULE,
+       .probe          = tosa_bat_probe,
+       .remove         = tosa_bat_remove,
+       .suspend        = tosa_bat_suspend,
+       .resume         = tosa_bat_resume,
+};
+
+module_platform_driver(tosa_bat_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dmitry Baryshkov");
+MODULE_DESCRIPTION("Tosa battery driver");
+MODULE_ALIAS("platform:wm97xx-battery");
diff --git a/drivers/power/supply/tps65090-charger.c b/drivers/power/supply/tps65090-charger.c
new file mode 100644 (file)
index 0000000..1b4b5e0
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Battery charger driver for TI's tps65090
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+
+ * This program is distributed in the hope 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.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/freezer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/tps65090.h>
+
+#define TPS65090_CHARGER_ENABLE        BIT(0)
+#define TPS65090_VACG          BIT(1)
+#define TPS65090_NOITERM       BIT(5)
+
+#define POLL_INTERVAL          (HZ * 2)        /* Used when no irq */
+
+struct tps65090_charger {
+       struct  device  *dev;
+       int     ac_online;
+       int     prev_ac_online;
+       int     irq;
+       struct task_struct      *poll_task;
+       bool                    passive_mode;
+       struct power_supply     *ac;
+       struct tps65090_platform_data *pdata;
+};
+
+static enum power_supply_property tps65090_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int tps65090_low_chrg_current(struct tps65090_charger *charger)
+{
+       int ret;
+
+       if (charger->passive_mode)
+               return 0;
+
+       ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5,
+                       TPS65090_NOITERM);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
+                       __func__, TPS65090_REG_CG_CTRL5);
+               return ret;
+       }
+       return 0;
+}
+
+static int tps65090_enable_charging(struct tps65090_charger *charger)
+{
+       int ret;
+       uint8_t ctrl0 = 0;
+
+       if (charger->passive_mode)
+               return 0;
+
+       ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0,
+                           &ctrl0);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
+                               __func__, TPS65090_REG_CG_CTRL0);
+               return ret;
+       }
+
+       ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL0,
+                               (ctrl0 | TPS65090_CHARGER_ENABLE));
+       if (ret < 0) {
+               dev_err(charger->dev, "%s(): error writing in register 0x%x\n",
+                               __func__, TPS65090_REG_CG_CTRL0);
+               return ret;
+       }
+       return 0;
+}
+
+static int tps65090_config_charger(struct tps65090_charger *charger)
+{
+       uint8_t intrmask = 0;
+       int ret;
+
+       if (charger->passive_mode)
+               return 0;
+
+       if (charger->pdata->enable_low_current_chrg) {
+               ret = tps65090_low_chrg_current(charger);
+               if (ret < 0) {
+                       dev_err(charger->dev,
+                               "error configuring low charge current\n");
+                       return ret;
+               }
+       }
+
+       /* Enable the VACG interrupt for AC power detect */
+       ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_MASK,
+                           &intrmask);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
+                       __func__, TPS65090_REG_INTR_MASK);
+               return ret;
+       }
+
+       ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_MASK,
+                            (intrmask | TPS65090_VACG));
+       if (ret < 0) {
+               dev_err(charger->dev, "%s(): error writing in register 0x%x\n",
+                       __func__, TPS65090_REG_CG_CTRL0);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int tps65090_ac_get_property(struct power_supply *psy,
+                       enum power_supply_property psp,
+                       union power_supply_propval *val)
+{
+       struct tps65090_charger *charger = power_supply_get_drvdata(psy);
+
+       if (psp == POWER_SUPPLY_PROP_ONLINE) {
+               val->intval = charger->ac_online;
+               charger->prev_ac_online = charger->ac_online;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
+{
+       struct tps65090_charger *charger = dev_id;
+       int ret;
+       uint8_t status1 = 0;
+       uint8_t intrsts = 0;
+
+       ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_STATUS1,
+                           &status1);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
+                               __func__, TPS65090_REG_CG_STATUS1);
+               return IRQ_HANDLED;
+       }
+       msleep(75);
+       ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_STS,
+                           &intrsts);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
+                               __func__, TPS65090_REG_INTR_STS);
+               return IRQ_HANDLED;
+       }
+
+       if (intrsts & TPS65090_VACG) {
+               ret = tps65090_enable_charging(charger);
+               if (ret < 0)
+                       return IRQ_HANDLED;
+               charger->ac_online = 1;
+       } else {
+               charger->ac_online = 0;
+       }
+
+       /* Clear interrupts. */
+       if (!charger->passive_mode) {
+               ret = tps65090_write(charger->dev->parent,
+                                    TPS65090_REG_INTR_STS, 0x00);
+               if (ret < 0) {
+                       dev_err(charger->dev,
+                               "%s(): Error in writing reg 0x%x\n",
+                               __func__, TPS65090_REG_INTR_STS);
+               }
+       }
+
+       if (charger->prev_ac_online != charger->ac_online)
+               power_supply_changed(charger->ac);
+
+       return IRQ_HANDLED;
+}
+
+static struct tps65090_platform_data *
+               tps65090_parse_dt_charger_data(struct platform_device *pdev)
+{
+       struct tps65090_platform_data *pdata;
+       struct device_node *np = pdev->dev.of_node;
+       unsigned int prop;
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata) {
+               dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n");
+               return NULL;
+       }
+
+       prop = of_property_read_bool(np, "ti,enable-low-current-chrg");
+       pdata->enable_low_current_chrg = prop;
+
+       pdata->irq_base = -1;
+
+       return pdata;
+
+}
+
+static int tps65090_charger_poll_task(void *data)
+{
+       set_freezable();
+
+       while (!kthread_should_stop()) {
+               schedule_timeout_interruptible(POLL_INTERVAL);
+               try_to_freeze();
+               tps65090_charger_isr(-1, data);
+       }
+       return 0;
+}
+
+static const struct power_supply_desc tps65090_charger_desc = {
+       .name                   = "tps65090-ac",
+       .type                   = POWER_SUPPLY_TYPE_MAINS,
+       .get_property           = tps65090_ac_get_property,
+       .properties             = tps65090_ac_props,
+       .num_properties         = ARRAY_SIZE(tps65090_ac_props),
+};
+
+static int tps65090_charger_probe(struct platform_device *pdev)
+{
+       struct tps65090_charger *cdata;
+       struct tps65090_platform_data *pdata;
+       struct power_supply_config psy_cfg = {};
+       uint8_t status1 = 0;
+       int ret;
+       int irq;
+
+       pdata = dev_get_platdata(pdev->dev.parent);
+
+       if (IS_ENABLED(CONFIG_OF) && !pdata && pdev->dev.of_node)
+               pdata = tps65090_parse_dt_charger_data(pdev);
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "%s():no platform data available\n",
+                               __func__);
+               return -ENODEV;
+       }
+
+       cdata = devm_kzalloc(&pdev->dev, sizeof(*cdata), GFP_KERNEL);
+       if (!cdata) {
+               dev_err(&pdev->dev, "failed to allocate memory status\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, cdata);
+
+       cdata->dev                      = &pdev->dev;
+       cdata->pdata                    = pdata;
+
+       psy_cfg.supplied_to             = pdata->supplied_to;
+       psy_cfg.num_supplicants         = pdata->num_supplicants;
+       psy_cfg.of_node                 = pdev->dev.of_node;
+       psy_cfg.drv_data                = cdata;
+
+       cdata->ac = power_supply_register(&pdev->dev, &tps65090_charger_desc,
+                       &psy_cfg);
+       if (IS_ERR(cdata->ac)) {
+               dev_err(&pdev->dev, "failed: power supply register\n");
+               return PTR_ERR(cdata->ac);
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               irq = -ENXIO;
+       cdata->irq = irq;
+
+       ret = tps65090_config_charger(cdata);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
+               goto fail_unregister_supply;
+       }
+
+       /* Check for charger presence */
+       ret = tps65090_read(cdata->dev->parent, TPS65090_REG_CG_STATUS1,
+                       &status1);
+       if (ret < 0) {
+               dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__,
+                       TPS65090_REG_CG_STATUS1);
+               goto fail_unregister_supply;
+       }
+
+       if (status1 != 0) {
+               ret = tps65090_enable_charging(cdata);
+               if (ret < 0) {
+                       dev_err(cdata->dev, "error enabling charger\n");
+                       goto fail_unregister_supply;
+               }
+               cdata->ac_online = 1;
+               power_supply_changed(cdata->ac);
+       }
+
+       if (irq != -ENXIO) {
+               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                       tps65090_charger_isr, 0, "tps65090-charger", cdata);
+               if (ret) {
+                       dev_err(cdata->dev,
+                               "Unable to register irq %d err %d\n", irq,
+                               ret);
+                       goto fail_unregister_supply;
+               }
+       } else {
+               cdata->poll_task = kthread_run(tps65090_charger_poll_task,
+                                             cdata, "ktps65090charger");
+               cdata->passive_mode = true;
+               if (IS_ERR(cdata->poll_task)) {
+                       ret = PTR_ERR(cdata->poll_task);
+                       dev_err(cdata->dev,
+                               "Unable to run kthread err %d\n", ret);
+                       goto fail_unregister_supply;
+               }
+       }
+
+       return 0;
+
+fail_unregister_supply:
+       power_supply_unregister(cdata->ac);
+
+       return ret;
+}
+
+static int tps65090_charger_remove(struct platform_device *pdev)
+{
+       struct tps65090_charger *cdata = platform_get_drvdata(pdev);
+
+       if (cdata->irq == -ENXIO)
+               kthread_stop(cdata->poll_task);
+       power_supply_unregister(cdata->ac);
+
+       return 0;
+}
+
+static const struct of_device_id of_tps65090_charger_match[] = {
+       { .compatible = "ti,tps65090-charger", },
+       { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, of_tps65090_charger_match);
+
+static struct platform_driver tps65090_charger_driver = {
+       .driver = {
+               .name   = "tps65090-charger",
+               .of_match_table = of_tps65090_charger_match,
+       },
+       .probe  = tps65090_charger_probe,
+       .remove = tps65090_charger_remove,
+};
+module_platform_driver(tps65090_charger_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>");
+MODULE_DESCRIPTION("tps65090 battery charger driver");
diff --git a/drivers/power/supply/tps65217_charger.c b/drivers/power/supply/tps65217_charger.c
new file mode 100644 (file)
index 0000000..73dfae4
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Battery charger driver for TI's tps65217
+ *
+ * Copyright (c) 2015, Collabora Ltd.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+
+ * This program is distributed in the hope 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.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Battery charger driver for TI's tps65217
+ */
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65217.h>
+
+#define POLL_INTERVAL          (HZ * 2)
+
+struct tps65217_charger {
+       struct tps65217 *tps;
+       struct device *dev;
+       struct power_supply *ac;
+
+       int     ac_online;
+       int     prev_ac_online;
+
+       struct task_struct      *poll_task;
+};
+
+static enum power_supply_property tps65217_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int tps65217_config_charger(struct tps65217_charger *charger)
+{
+       int ret;
+
+       dev_dbg(charger->dev, "%s\n", __func__);
+
+       /*
+        * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)
+        *
+        * The device can be configured to support a 100k NTC (B = 3960) by
+        * setting the the NTC_TYPE bit in register CHGCONFIG1 to 1. However it
+        * is not recommended to do so. In sleep mode, the charger continues
+        * charging the battery, but all register values are reset to default
+        * values. Therefore, the charger would get the wrong temperature
+        * information. If 100k NTC setting is required, please contact the
+        * factory.
+        *
+        * ATTENTION, conflicting information, from p. 46
+        *
+        * NTC TYPE (for battery temperature measurement)
+        *   0 â€“ 100k (curve 1, B = 3960)
+        *   1 â€“ 10k  (curve 2, B = 3480) (default on reset)
+        *
+        */
+       ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
+                                 TPS65217_CHGCONFIG1_NTC_TYPE,
+                                 TPS65217_PROTECT_NONE);
+       if (ret) {
+               dev_err(charger->dev,
+                       "failed to set 100k NTC setting: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int tps65217_enable_charging(struct tps65217_charger *charger)
+{
+       int ret;
+
+       /* charger already enabled */
+       if (charger->ac_online)
+               return 0;
+
+       dev_dbg(charger->dev, "%s: enable charging\n", __func__);
+       ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
+                               TPS65217_CHGCONFIG1_CHG_EN,
+                               TPS65217_CHGCONFIG1_CHG_EN,
+                               TPS65217_PROTECT_NONE);
+       if (ret) {
+               dev_err(charger->dev,
+                       "%s: Error in writing CHG_EN in reg 0x%x: %d\n",
+                       __func__, TPS65217_REG_CHGCONFIG1, ret);
+               return ret;
+       }
+
+       charger->ac_online = 1;
+
+       return 0;
+}
+
+static int tps65217_ac_get_property(struct power_supply *psy,
+                       enum power_supply_property psp,
+                       union power_supply_propval *val)
+{
+       struct tps65217_charger *charger = power_supply_get_drvdata(psy);
+
+       if (psp == POWER_SUPPLY_PROP_ONLINE) {
+               val->intval = charger->ac_online;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static irqreturn_t tps65217_charger_irq(int irq, void *dev)
+{
+       int ret, val;
+       struct tps65217_charger *charger = dev;
+
+       charger->prev_ac_online = charger->ac_online;
+
+       ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
+                       __func__, TPS65217_REG_STATUS);
+               return IRQ_HANDLED;
+       }
+
+       dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
+
+       /* check for AC status bit */
+       if (val & TPS65217_STATUS_ACPWR) {
+               ret = tps65217_enable_charging(charger);
+               if (ret) {
+                       dev_err(charger->dev,
+                               "failed to enable charger: %d\n", ret);
+                       return IRQ_HANDLED;
+               }
+       } else {
+               charger->ac_online = 0;
+       }
+
+       if (charger->prev_ac_online != charger->ac_online)
+               power_supply_changed(charger->ac);
+
+       ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
+                       __func__, TPS65217_REG_CHGCONFIG0);
+               return IRQ_HANDLED;
+       }
+
+       if (val & TPS65217_CHGCONFIG0_ACTIVE)
+               dev_dbg(charger->dev, "%s: charger is charging\n", __func__);
+       else
+               dev_dbg(charger->dev,
+                       "%s: charger is NOT charging\n", __func__);
+
+       return IRQ_HANDLED;
+}
+
+static int tps65217_charger_poll_task(void *data)
+{
+       set_freezable();
+
+       while (!kthread_should_stop()) {
+               schedule_timeout_interruptible(POLL_INTERVAL);
+               try_to_freeze();
+               tps65217_charger_irq(-1, data);
+       }
+       return 0;
+}
+
+static const struct power_supply_desc tps65217_charger_desc = {
+       .name                   = "tps65217-ac",
+       .type                   = POWER_SUPPLY_TYPE_MAINS,
+       .get_property           = tps65217_ac_get_property,
+       .properties             = tps65217_ac_props,
+       .num_properties         = ARRAY_SIZE(tps65217_ac_props),
+};
+
+static int tps65217_charger_probe(struct platform_device *pdev)
+{
+       struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+       struct tps65217_charger *charger;
+       struct power_supply_config cfg = {};
+       int ret;
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
+       if (!charger)
+               return -ENOMEM;
+
+       charger->tps = tps;
+       charger->dev = &pdev->dev;
+
+       cfg.of_node = pdev->dev.of_node;
+       cfg.drv_data = charger;
+
+       charger->ac = devm_power_supply_register(&pdev->dev,
+                                                &tps65217_charger_desc,
+                                                &cfg);
+       if (IS_ERR(charger->ac)) {
+               dev_err(&pdev->dev, "failed: power supply register\n");
+               return PTR_ERR(charger->ac);
+       }
+
+       ret = tps65217_config_charger(charger);
+       if (ret < 0) {
+               dev_err(charger->dev, "charger config failed, err %d\n", ret);
+               return ret;
+       }
+
+       charger->poll_task = kthread_run(tps65217_charger_poll_task,
+                                     charger, "ktps65217charger");
+       if (IS_ERR(charger->poll_task)) {
+               ret = PTR_ERR(charger->poll_task);
+               dev_err(charger->dev, "Unable to run kthread err %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int tps65217_charger_remove(struct platform_device *pdev)
+{
+       struct tps65217_charger *charger = platform_get_drvdata(pdev);
+
+       kthread_stop(charger->poll_task);
+
+       return 0;
+}
+
+static const struct of_device_id tps65217_charger_match_table[] = {
+       { .compatible = "ti,tps65217-charger", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);
+
+static struct platform_driver tps65217_charger_driver = {
+       .probe  = tps65217_charger_probe,
+       .remove = tps65217_charger_remove,
+       .driver = {
+               .name   = "tps65217-charger",
+               .of_match_table = of_match_ptr(tps65217_charger_match_table),
+       },
+
+};
+module_platform_driver(tps65217_charger_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
+MODULE_DESCRIPTION("TPS65217 battery charger driver");
diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c
new file mode 100644 (file)
index 0000000..bcd4dc3
--- /dev/null
@@ -0,0 +1,1162 @@
+/*
+ * TWL4030/TPS65950 BCI (Battery Charger Interface) driver
+ *
+ * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com>
+ *
+ * based on twl4030_bci_battery.c by TI
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/i2c/twl.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/usb/otg.h>
+#include <linux/iio/consumer.h>
+
+#define TWL4030_BCIMDEN                0x00
+#define TWL4030_BCIMDKEY       0x01
+#define TWL4030_BCIMSTATEC     0x02
+#define TWL4030_BCIICHG                0x08
+#define TWL4030_BCIVAC         0x0a
+#define TWL4030_BCIVBUS                0x0c
+#define TWL4030_BCIMFSTS3      0x0F
+#define TWL4030_BCIMFSTS4      0x10
+#define TWL4030_BCICTL1                0x23
+#define TWL4030_BB_CFG         0x12
+#define TWL4030_BCIIREF1       0x27
+#define TWL4030_BCIIREF2       0x28
+#define TWL4030_BCIMFKEY       0x11
+#define TWL4030_BCIMFEN3       0x14
+#define TWL4030_BCIMFTH8       0x1d
+#define TWL4030_BCIMFTH9       0x1e
+#define TWL4030_BCIWDKEY       0x21
+
+#define TWL4030_BCIMFSTS1      0x01
+
+#define TWL4030_BCIAUTOWEN     BIT(5)
+#define TWL4030_CONFIG_DONE    BIT(4)
+#define TWL4030_CVENAC         BIT(2)
+#define TWL4030_BCIAUTOUSB     BIT(1)
+#define TWL4030_BCIAUTOAC      BIT(0)
+#define TWL4030_CGAIN          BIT(5)
+#define TWL4030_USBFASTMCHG    BIT(2)
+#define TWL4030_STS_VBUS       BIT(7)
+#define TWL4030_STS_USB_ID     BIT(2)
+#define TWL4030_BBCHEN         BIT(4)
+#define TWL4030_BBSEL_MASK     0x0c
+#define TWL4030_BBSEL_2V5      0x00
+#define TWL4030_BBSEL_3V0      0x04
+#define TWL4030_BBSEL_3V1      0x08
+#define TWL4030_BBSEL_3V2      0x0c
+#define TWL4030_BBISEL_MASK    0x03
+#define TWL4030_BBISEL_25uA    0x00
+#define TWL4030_BBISEL_150uA   0x01
+#define TWL4030_BBISEL_500uA   0x02
+#define TWL4030_BBISEL_1000uA  0x03
+
+#define TWL4030_BATSTSPCHG     BIT(2)
+#define TWL4030_BATSTSMCHG     BIT(6)
+
+/* BCI interrupts */
+#define TWL4030_WOVF           BIT(0) /* Watchdog overflow */
+#define TWL4030_TMOVF          BIT(1) /* Timer overflow */
+#define TWL4030_ICHGHIGH       BIT(2) /* Battery charge current high */
+#define TWL4030_ICHGLOW                BIT(3) /* Battery cc. low / FSM state change */
+#define TWL4030_ICHGEOC                BIT(4) /* Battery current end-of-charge */
+#define TWL4030_TBATOR2                BIT(5) /* Battery temperature out of range 2 */
+#define TWL4030_TBATOR1                BIT(6) /* Battery temperature out of range 1 */
+#define TWL4030_BATSTS         BIT(7) /* Battery status */
+
+#define TWL4030_VBATLVL                BIT(0) /* VBAT level */
+#define TWL4030_VBATOV         BIT(1) /* VBAT overvoltage */
+#define TWL4030_VBUSOV         BIT(2) /* VBUS overvoltage */
+#define TWL4030_ACCHGOV                BIT(3) /* Ac charger overvoltage */
+
+#define TWL4030_MSTATEC_USB            BIT(4)
+#define TWL4030_MSTATEC_AC             BIT(5)
+#define TWL4030_MSTATEC_MASK           0x0f
+#define TWL4030_MSTATEC_QUICK1         0x02
+#define TWL4030_MSTATEC_QUICK7         0x07
+#define TWL4030_MSTATEC_COMPLETE1      0x0b
+#define TWL4030_MSTATEC_COMPLETE4      0x0e
+
+/*
+ * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
+ * then AC is available.
+ */
+static inline int ac_available(struct iio_channel *channel_vac)
+{
+       int val, err;
+
+       if (!channel_vac)
+               return 0;
+
+       err = iio_read_channel_processed(channel_vac, &val);
+       if (err < 0)
+               return 0;
+       return val > 4500;
+}
+
+static bool allow_usb;
+module_param(allow_usb, bool, 0644);
+MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
+
+struct twl4030_bci {
+       struct device           *dev;
+       struct power_supply     *ac;
+       struct power_supply     *usb;
+       struct usb_phy          *transceiver;
+       struct notifier_block   usb_nb;
+       struct work_struct      work;
+       int                     irq_chg;
+       int                     irq_bci;
+       int                     usb_enabled;
+
+       /*
+        * ichg_* and *_cur values in uA. If any are 'large', we set
+        * CGAIN to '1' which doubles the range for half the
+        * precision.
+        */
+       unsigned int            ichg_eoc, ichg_lo, ichg_hi;
+       unsigned int            usb_cur, ac_cur;
+       struct iio_channel      *channel_vac;
+       bool                    ac_is_active;
+       int                     usb_mode, ac_mode; /* charging mode requested */
+#define        CHARGE_OFF      0
+#define        CHARGE_AUTO     1
+#define        CHARGE_LINEAR   2
+
+       /* When setting the USB current we slowly increase the
+        * requested current until target is reached or the voltage
+        * drops below 4.75V.  In the latter case we step back one
+        * step.
+        */
+       unsigned int            usb_cur_target;
+       struct delayed_work     current_worker;
+#define        USB_CUR_STEP    20000   /* 20mA at a time */
+#define        USB_MIN_VOLT    4750000 /* 4.75V */
+#define        USB_CUR_DELAY   msecs_to_jiffies(100)
+#define        USB_MAX_CURRENT 1700000 /* TWL4030 caps at 1.7A */
+
+       unsigned long           event;
+};
+
+/* strings for 'usb_mode' values */
+static char *modes[] = { "off", "auto", "continuous" };
+
+/*
+ * clear and set bits on an given register on a given module
+ */
+static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
+{
+       u8 val = 0;
+       int ret;
+
+       ret = twl_i2c_read_u8(mod_no, &val, reg);
+       if (ret)
+               return ret;
+
+       val &= ~clear;
+       val |= set;
+
+       return twl_i2c_write_u8(mod_no, val, reg);
+}
+
+static int twl4030_bci_read(u8 reg, u8 *val)
+{
+       return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg);
+}
+
+static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
+{
+       return twl4030_clear_set(TWL_MODULE_PM_MASTER, clear,
+                       TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
+                       TWL4030_PM_MASTER_BOOT_BCI);
+}
+
+static int twl4030bci_read_adc_val(u8 reg)
+{
+       int ret, temp;
+       u8 val;
+
+       /* read MSB */
+       ret = twl4030_bci_read(reg + 1, &val);
+       if (ret)
+               return ret;
+
+       temp = (int)(val & 0x03) << 8;
+
+       /* read LSB */
+       ret = twl4030_bci_read(reg, &val);
+       if (ret)
+               return ret;
+
+       return temp | val;
+}
+
+/*
+ * Check if Battery Pack was present
+ */
+static int twl4030_is_battery_present(struct twl4030_bci *bci)
+{
+       int ret;
+       u8 val = 0;
+
+       /* Battery presence in Main charge? */
+       ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3);
+       if (ret)
+               return ret;
+       if (val & TWL4030_BATSTSMCHG)
+               return 0;
+
+       /*
+        * OK, It could be that bootloader did not enable main charger,
+        * pre-charge is h/w auto. So, Battery presence in Pre-charge?
+        */
+       ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val,
+                             TWL4030_BCIMFSTS1);
+       if (ret)
+               return ret;
+       if (val & TWL4030_BATSTSPCHG)
+               return 0;
+
+       return -ENODEV;
+}
+
+/*
+ * TI provided formulas:
+ * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
+ * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
+ * Here we use integer approximation of:
+ * CGAIN == 0: val * 1.6618 - 0.85 * 1000
+ * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2
+ */
+/*
+ * convert twl register value for currents into uA
+ */
+static int regval2ua(int regval, bool cgain)
+{
+       if (cgain)
+               return (regval * 16618 - 8500 * 1000) / 5;
+       else
+               return (regval * 16618 - 8500 * 1000) / 10;
+}
+
+/*
+ * convert uA currents into twl register value
+ */
+static int ua2regval(int ua, bool cgain)
+{
+       int ret;
+       if (cgain)
+               ua /= 2;
+       ret = (ua * 10 + 8500 * 1000) / 16618;
+       /* rounding problems */
+       if (ret < 512)
+               ret = 512;
+       return ret;
+}
+
+static int twl4030_charger_update_current(struct twl4030_bci *bci)
+{
+       int status;
+       int cur;
+       unsigned reg, cur_reg;
+       u8 bcictl1, oldreg, fullreg;
+       bool cgain = false;
+       u8 boot_bci;
+
+       /*
+        * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
+        * and AC is enabled, set current for 'ac'
+        */
+       if (ac_available(bci->channel_vac)) {
+               cur = bci->ac_cur;
+               bci->ac_is_active = true;
+       } else {
+               cur = bci->usb_cur;
+               bci->ac_is_active = false;
+               if (cur > bci->usb_cur_target) {
+                       cur = bci->usb_cur_target;
+                       bci->usb_cur = cur;
+               }
+               if (cur < bci->usb_cur_target)
+                       schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
+       }
+
+       /* First, check thresholds and see if cgain is needed */
+       if (bci->ichg_eoc >= 200000)
+               cgain = true;
+       if (bci->ichg_lo >= 400000)
+               cgain = true;
+       if (bci->ichg_hi >= 820000)
+               cgain = true;
+       if (cur > 852000)
+               cgain = true;
+
+       status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+       if (status < 0)
+               return status;
+       if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci,
+                           TWL4030_PM_MASTER_BOOT_BCI) < 0)
+               boot_bci = 0;
+       boot_bci &= 7;
+
+       if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN))
+               /* Need to turn for charging while we change the
+                * CGAIN bit.  Leave it off while everything is
+                * updated.
+                */
+               twl4030_clear_set_boot_bci(boot_bci, 0);
+
+       /*
+        * For ichg_eoc, the hardware only supports reg values matching
+        * 100XXXX000, and requires the XXXX be stored in the high nibble
+        * of TWL4030_BCIMFTH8.
+        */
+       reg = ua2regval(bci->ichg_eoc, cgain);
+       if (reg > 0x278)
+               reg = 0x278;
+       if (reg < 0x200)
+               reg = 0x200;
+       reg = (reg >> 3) & 0xf;
+       fullreg = reg << 4;
+
+       /*
+        * For ichg_lo, reg value must match 10XXXX0000.
+        * XXXX is stored in low nibble of TWL4030_BCIMFTH8.
+        */
+       reg = ua2regval(bci->ichg_lo, cgain);
+       if (reg > 0x2F0)
+               reg = 0x2F0;
+       if (reg < 0x200)
+               reg = 0x200;
+       reg = (reg >> 4) & 0xf;
+       fullreg |= reg;
+
+       /* ichg_eoc and ichg_lo live in same register */
+       status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg);
+       if (status < 0)
+               return status;
+       if (oldreg != fullreg) {
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4,
+                                         TWL4030_BCIMFKEY);
+               if (status < 0)
+                       return status;
+               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                fullreg, TWL4030_BCIMFTH8);
+       }
+
+       /* ichg_hi threshold must be 1XXXX01100 (I think) */
+       reg = ua2regval(bci->ichg_hi, cgain);
+       if (reg > 0x3E0)
+               reg = 0x3E0;
+       if (reg < 0x200)
+               reg = 0x200;
+       fullreg = (reg >> 5) & 0xF;
+       fullreg <<= 4;
+       status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg);
+       if (status < 0)
+               return status;
+       if ((oldreg & 0xF0) != fullreg) {
+               fullreg |= (oldreg & 0x0F);
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+                                         TWL4030_BCIMFKEY);
+               if (status < 0)
+                       return status;
+               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                fullreg, TWL4030_BCIMFTH9);
+       }
+
+       /*
+        * And finally, set the current.  This is stored in
+        * two registers.
+        */
+       reg = ua2regval(cur, cgain);
+       /* we have only 10 bits */
+       if (reg > 0x3ff)
+               reg = 0x3ff;
+       status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg);
+       if (status < 0)
+               return status;
+       cur_reg = oldreg;
+       status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg);
+       if (status < 0)
+               return status;
+       cur_reg |= oldreg << 8;
+       if (reg != oldreg) {
+               /* disable write protection for one write access for
+                * BCIIREF */
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+                                         TWL4030_BCIMFKEY);
+               if (status < 0)
+                       return status;
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                         (reg & 0x100) ? 3 : 2,
+                                         TWL4030_BCIIREF2);
+               if (status < 0)
+                       return status;
+               /* disable write protection for one write access for
+                * BCIIREF */
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
+                                         TWL4030_BCIMFKEY);
+               if (status < 0)
+                       return status;
+               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                         reg & 0xff,
+                                         TWL4030_BCIIREF1);
+       }
+       if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) {
+               /* Flip CGAIN and re-enable charging */
+               bcictl1 ^= TWL4030_CGAIN;
+               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+                                bcictl1, TWL4030_BCICTL1);
+               twl4030_clear_set_boot_bci(0, boot_bci);
+       }
+       return 0;
+}
+
+static int twl4030_charger_get_current(void);
+
+static void twl4030_current_worker(struct work_struct *data)
+{
+       int v, curr;
+       int res;
+       struct twl4030_bci *bci = container_of(data, struct twl4030_bci,
+                                              current_worker.work);
+
+       res = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
+       if (res < 0)
+               v = 0;
+       else
+               /* BCIVBUS uses ADCIN8, 7/1023 V/step */
+               v = res * 6843;
+       curr = twl4030_charger_get_current();
+
+       dev_dbg(bci->dev, "v=%d cur=%d limit=%d target=%d\n", v, curr,
+               bci->usb_cur, bci->usb_cur_target);
+
+       if (v < USB_MIN_VOLT) {
+               /* Back up and stop adjusting. */
+               bci->usb_cur -= USB_CUR_STEP;
+               bci->usb_cur_target = bci->usb_cur;
+       } else if (bci->usb_cur >= bci->usb_cur_target ||
+                  bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) {
+               /* Reached target and voltage is OK - stop */
+               return;
+       } else {
+               bci->usb_cur += USB_CUR_STEP;
+               schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
+       }
+       twl4030_charger_update_current(bci);
+}
+
+/*
+ * Enable/Disable USB Charge functionality.
+ */
+static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
+{
+       int ret;
+
+       if (bci->usb_mode == CHARGE_OFF)
+               enable = false;
+       if (enable && !IS_ERR_OR_NULL(bci->transceiver)) {
+
+               twl4030_charger_update_current(bci);
+
+               /* Need to keep phy powered */
+               if (!bci->usb_enabled) {
+                       pm_runtime_get_sync(bci->transceiver->dev);
+                       bci->usb_enabled = 1;
+               }
+
+               if (bci->usb_mode == CHARGE_AUTO)
+                       /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
+                       ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
+
+               /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
+               ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
+                       TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
+               if (bci->usb_mode == CHARGE_LINEAR) {
+                       twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC|TWL4030_CVENAC, 0);
+                       /* Watch dog key: WOVF acknowledge */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x33,
+                                              TWL4030_BCIWDKEY);
+                       /* 0x24 + EKEY6: off mode */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a,
+                                              TWL4030_BCIMDKEY);
+                       /* EKEY2: Linear charge: USB path */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x26,
+                                              TWL4030_BCIMDKEY);
+                       /* WDKEY5: stop watchdog count */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf3,
+                                              TWL4030_BCIWDKEY);
+                       /* enable MFEN3 access */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x9c,
+                                              TWL4030_BCIMFKEY);
+                        /* ICHGEOCEN - end-of-charge monitor (current < 80mA)
+                         *                      (charging continues)
+                         * ICHGLOWEN - current level monitor (charge continues)
+                         * don't monitor over-current or heat save
+                         */
+                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf0,
+                                              TWL4030_BCIMFEN3);
+               }
+       } else {
+               ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
+               ret |= twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a,
+                                       TWL4030_BCIMDKEY);
+               if (bci->usb_enabled) {
+                       pm_runtime_mark_last_busy(bci->transceiver->dev);
+                       pm_runtime_put_autosuspend(bci->transceiver->dev);
+                       bci->usb_enabled = 0;
+               }
+               bci->usb_cur = 0;
+       }
+
+       return ret;
+}
+
+/*
+ * Enable/Disable AC Charge funtionality.
+ */
+static int twl4030_charger_enable_ac(struct twl4030_bci *bci, bool enable)
+{
+       int ret;
+
+       if (bci->ac_mode == CHARGE_OFF)
+               enable = false;
+
+       if (enable)
+               ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
+       else
+               ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0);
+
+       return ret;
+}
+
+/*
+ * Enable/Disable charging of Backup Battery.
+ */
+static int twl4030_charger_enable_backup(int uvolt, int uamp)
+{
+       int ret;
+       u8 flags;
+
+       if (uvolt < 2500000 ||
+           uamp < 25) {
+               /* disable charging of backup battery */
+               ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
+                                       TWL4030_BBCHEN, 0, TWL4030_BB_CFG);
+               return ret;
+       }
+
+       flags = TWL4030_BBCHEN;
+       if (uvolt >= 3200000)
+               flags |= TWL4030_BBSEL_3V2;
+       else if (uvolt >= 3100000)
+               flags |= TWL4030_BBSEL_3V1;
+       else if (uvolt >= 3000000)
+               flags |= TWL4030_BBSEL_3V0;
+       else
+               flags |= TWL4030_BBSEL_2V5;
+
+       if (uamp >= 1000)
+               flags |= TWL4030_BBISEL_1000uA;
+       else if (uamp >= 500)
+               flags |= TWL4030_BBISEL_500uA;
+       else if (uamp >= 150)
+               flags |= TWL4030_BBISEL_150uA;
+       else
+               flags |= TWL4030_BBISEL_25uA;
+
+       ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
+                               TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK,
+                               flags,
+                               TWL4030_BB_CFG);
+
+       return ret;
+}
+
+/*
+ * TWL4030 CHG_PRES (AC charger presence) events
+ */
+static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
+{
+       struct twl4030_bci *bci = arg;
+
+       dev_dbg(bci->dev, "CHG_PRES irq\n");
+       /* reset current on each 'plug' event */
+       bci->ac_cur = 500000;
+       twl4030_charger_update_current(bci);
+       power_supply_changed(bci->ac);
+       power_supply_changed(bci->usb);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * TWL4030 BCI monitoring events
+ */
+static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
+{
+       struct twl4030_bci *bci = arg;
+       u8 irqs1, irqs2;
+       int ret;
+
+       ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1,
+                             TWL4030_INTERRUPTS_BCIISR1A);
+       if (ret < 0)
+               return IRQ_HANDLED;
+
+       ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2,
+                             TWL4030_INTERRUPTS_BCIISR2A);
+       if (ret < 0)
+               return IRQ_HANDLED;
+
+       dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1);
+
+       if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) {
+               /* charger state change, inform the core */
+               power_supply_changed(bci->ac);
+               power_supply_changed(bci->usb);
+       }
+       twl4030_charger_update_current(bci);
+
+       /* various monitoring events, for now we just log them here */
+       if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
+               dev_warn(bci->dev, "battery temperature out of range\n");
+
+       if (irqs1 & TWL4030_BATSTS)
+               dev_crit(bci->dev, "battery disconnected\n");
+
+       if (irqs2 & TWL4030_VBATOV)
+               dev_crit(bci->dev, "VBAT overvoltage\n");
+
+       if (irqs2 & TWL4030_VBUSOV)
+               dev_crit(bci->dev, "VBUS overvoltage\n");
+
+       if (irqs2 & TWL4030_ACCHGOV)
+               dev_crit(bci->dev, "Ac charger overvoltage\n");
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Provide "max_current" attribute in sysfs.
+ */
+static ssize_t
+twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr,
+       const char *buf, size_t n)
+{
+       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
+       int cur = 0;
+       int status = 0;
+       status = kstrtoint(buf, 10, &cur);
+       if (status)
+               return status;
+       if (cur < 0)
+               return -EINVAL;
+       if (dev == &bci->ac->dev)
+               bci->ac_cur = cur;
+       else
+               bci->usb_cur_target = cur;
+
+       twl4030_charger_update_current(bci);
+       return n;
+}
+
+/*
+ * sysfs max_current show
+ */
+static ssize_t twl4030_bci_max_current_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int status = 0;
+       int cur = -1;
+       u8 bcictl1;
+       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
+
+       if (dev == &bci->ac->dev) {
+               if (!bci->ac_is_active)
+                       cur = bci->ac_cur;
+       } else {
+               if (bci->ac_is_active)
+                       cur = bci->usb_cur_target;
+       }
+       if (cur < 0) {
+               cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
+               if (cur < 0)
+                       return cur;
+               status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+               if (status < 0)
+                       return status;
+               cur = regval2ua(cur, bcictl1 & TWL4030_CGAIN);
+       }
+       return scnprintf(buf, PAGE_SIZE, "%u\n", cur);
+}
+
+static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show,
+                       twl4030_bci_max_current_store);
+
+static void twl4030_bci_usb_work(struct work_struct *data)
+{
+       struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work);
+
+       switch (bci->event) {
+       case USB_EVENT_VBUS:
+       case USB_EVENT_CHARGER:
+               twl4030_charger_enable_usb(bci, true);
+               break;
+       case USB_EVENT_NONE:
+               twl4030_charger_enable_usb(bci, false);
+               break;
+       }
+}
+
+static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
+                              void *priv)
+{
+       struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb);
+
+       dev_dbg(bci->dev, "OTG notify %lu\n", val);
+
+       /* reset current on each 'plug' event */
+       if (allow_usb)
+               bci->usb_cur_target = 500000;
+       else
+               bci->usb_cur_target = 100000;
+
+       bci->event = val;
+       schedule_work(&bci->work);
+
+       return NOTIFY_OK;
+}
+
+/*
+ * sysfs charger enabled store
+ */
+static ssize_t
+twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t n)
+{
+       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
+       int mode;
+       int status;
+
+       if (sysfs_streq(buf, modes[0]))
+               mode = 0;
+       else if (sysfs_streq(buf, modes[1]))
+               mode = 1;
+       else if (sysfs_streq(buf, modes[2]))
+               mode = 2;
+       else
+               return -EINVAL;
+       if (dev == &bci->ac->dev) {
+               if (mode == 2)
+                       return -EINVAL;
+               twl4030_charger_enable_ac(bci, false);
+               bci->ac_mode = mode;
+               status = twl4030_charger_enable_ac(bci, true);
+       } else {
+               twl4030_charger_enable_usb(bci, false);
+               bci->usb_mode = mode;
+               status = twl4030_charger_enable_usb(bci, true);
+       }
+       return (status == 0) ? n : status;
+}
+
+/*
+ * sysfs charger enabled show
+ */
+static ssize_t
+twl4030_bci_mode_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
+       int len = 0;
+       int i;
+       int mode = bci->usb_mode;
+
+       if (dev == &bci->ac->dev)
+               mode = bci->ac_mode;
+
+       for (i = 0; i < ARRAY_SIZE(modes); i++)
+               if (mode == i)
+                       len += snprintf(buf+len, PAGE_SIZE-len,
+                                       "[%s] ", modes[i]);
+               else
+                       len += snprintf(buf+len, PAGE_SIZE-len,
+                                       "%s ", modes[i]);
+       buf[len-1] = '\n';
+       return len;
+}
+static DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show,
+                  twl4030_bci_mode_store);
+
+static int twl4030_charger_get_current(void)
+{
+       int curr;
+       int ret;
+       u8 bcictl1;
+
+       curr = twl4030bci_read_adc_val(TWL4030_BCIICHG);
+       if (curr < 0)
+               return curr;
+
+       ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
+       if (ret)
+               return ret;
+
+       return regval2ua(curr, bcictl1 & TWL4030_CGAIN);
+}
+
+/*
+ * Returns the main charge FSM state
+ * Or < 0 on failure.
+ */
+static int twl4030bci_state(struct twl4030_bci *bci)
+{
+       int ret;
+       u8 state;
+
+       ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state);
+       if (ret) {
+               pr_err("twl4030_bci: error reading BCIMSTATEC\n");
+               return ret;
+       }
+
+       dev_dbg(bci->dev, "state: %02x\n", state);
+
+       return state;
+}
+
+static int twl4030_bci_state_to_status(int state)
+{
+       state &= TWL4030_MSTATEC_MASK;
+       if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7)
+               return POWER_SUPPLY_STATUS_CHARGING;
+       else if (TWL4030_MSTATEC_COMPLETE1 <= state &&
+                                       state <= TWL4030_MSTATEC_COMPLETE4)
+               return POWER_SUPPLY_STATUS_FULL;
+       else
+               return POWER_SUPPLY_STATUS_NOT_CHARGING;
+}
+
+static int twl4030_bci_get_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   union power_supply_propval *val)
+{
+       struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent);
+       int is_charging;
+       int state;
+       int ret;
+
+       state = twl4030bci_state(bci);
+       if (state < 0)
+               return state;
+
+       if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
+               is_charging = state & TWL4030_MSTATEC_USB;
+       else
+               is_charging = state & TWL4030_MSTATEC_AC;
+       if (!is_charging) {
+               u8 s;
+               twl4030_bci_read(TWL4030_BCIMDEN, &s);
+               if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
+                       is_charging = s & 1;
+               else
+                       is_charging = s & 2;
+               if (is_charging)
+                       /* A little white lie */
+                       state = TWL4030_MSTATEC_QUICK1;
+       }
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (is_charging)
+                       val->intval = twl4030_bci_state_to_status(state);
+               else
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               /* charging must be active for meaningful result */
+               if (!is_charging)
+                       return -ENODATA;
+               if (psy->desc->type == POWER_SUPPLY_TYPE_USB) {
+                       ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
+                       if (ret < 0)
+                               return ret;
+                       /* BCIVBUS uses ADCIN8, 7/1023 V/step */
+                       val->intval = ret * 6843;
+               } else {
+                       ret = twl4030bci_read_adc_val(TWL4030_BCIVAC);
+                       if (ret < 0)
+                               return ret;
+                       /* BCIVAC uses ADCIN11, 10/1023 V/step */
+                       val->intval = ret * 9775;
+               }
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               if (!is_charging)
+                       return -ENODATA;
+               /* current measurement is shared between AC and USB */
+               ret = twl4030_charger_get_current();
+               if (ret < 0)
+                       return ret;
+               val->intval = ret;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = is_charging &&
+                       twl4030_bci_state_to_status(state) !=
+                               POWER_SUPPLY_STATUS_NOT_CHARGING;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property twl4030_charger_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+#ifdef CONFIG_OF
+static const struct twl4030_bci_platform_data *
+twl4030_bci_parse_dt(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct twl4030_bci_platform_data *pdata;
+       u32 num;
+
+       if (!np)
+               return NULL;
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return pdata;
+
+       if (of_property_read_u32(np, "ti,bb-uvolt", &num) == 0)
+               pdata->bb_uvolt = num;
+       if (of_property_read_u32(np, "ti,bb-uamp", &num) == 0)
+               pdata->bb_uamp = num;
+       return pdata;
+}
+#else
+static inline const struct twl4030_bci_platform_data *
+twl4030_bci_parse_dt(struct device *dev)
+{
+       return NULL;
+}
+#endif
+
+static const struct power_supply_desc twl4030_bci_ac_desc = {
+       .name           = "twl4030_ac",
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+       .properties     = twl4030_charger_props,
+       .num_properties = ARRAY_SIZE(twl4030_charger_props),
+       .get_property   = twl4030_bci_get_property,
+};
+
+static const struct power_supply_desc twl4030_bci_usb_desc = {
+       .name           = "twl4030_usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties     = twl4030_charger_props,
+       .num_properties = ARRAY_SIZE(twl4030_charger_props),
+       .get_property   = twl4030_bci_get_property,
+};
+
+static int twl4030_bci_probe(struct platform_device *pdev)
+{
+       struct twl4030_bci *bci;
+       const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
+       int ret;
+       u32 reg;
+
+       bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL);
+       if (bci == NULL)
+               return -ENOMEM;
+
+       if (!pdata)
+               pdata = twl4030_bci_parse_dt(&pdev->dev);
+
+       bci->ichg_eoc = 80100; /* Stop charging when current drops to here */
+       bci->ichg_lo = 241000; /* Low threshold */
+       bci->ichg_hi = 500000; /* High threshold */
+       bci->ac_cur = 500000; /* 500mA */
+       if (allow_usb)
+               bci->usb_cur_target = 500000;  /* 500mA */
+       else
+               bci->usb_cur_target = 100000;  /* 100mA */
+       bci->usb_mode = CHARGE_AUTO;
+       bci->ac_mode = CHARGE_AUTO;
+
+       bci->dev = &pdev->dev;
+       bci->irq_chg = platform_get_irq(pdev, 0);
+       bci->irq_bci = platform_get_irq(pdev, 1);
+
+       /* Only proceed further *IF* battery is physically present */
+       ret = twl4030_is_battery_present(bci);
+       if  (ret) {
+               dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, bci);
+
+       bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
+                                            NULL);
+       if (IS_ERR(bci->ac)) {
+               ret = PTR_ERR(bci->ac);
+               dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
+               return ret;
+       }
+
+       bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
+                                             NULL);
+       if (IS_ERR(bci->usb)) {
+               ret = PTR_ERR(bci->usb);
+               dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL,
+                       twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name,
+                       bci);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "could not request irq %d, status %d\n",
+                       bci->irq_chg, ret);
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL,
+                       twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "could not request irq %d, status %d\n",
+                       bci->irq_bci, ret);
+               return ret;
+       }
+
+       bci->channel_vac = iio_channel_get(&pdev->dev, "vac");
+       if (IS_ERR(bci->channel_vac)) {
+               bci->channel_vac = NULL;
+               dev_warn(&pdev->dev, "could not request vac iio channel");
+       }
+
+       INIT_WORK(&bci->work, twl4030_bci_usb_work);
+       INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
+
+       bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
+       if (bci->dev->of_node) {
+               struct device_node *phynode;
+
+               phynode = of_find_compatible_node(bci->dev->of_node->parent,
+                                                 NULL, "ti,twl4030-usb");
+               if (phynode)
+                       bci->transceiver = devm_usb_get_phy_by_node(
+                               bci->dev, phynode, &bci->usb_nb);
+       }
+
+       /* Enable interrupts now. */
+       reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
+               TWL4030_TBATOR1 | TWL4030_BATSTS);
+       ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
+                              TWL4030_INTERRUPTS_BCIIMR1A);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
+               goto fail;
+       }
+
+       reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
+       ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
+                              TWL4030_INTERRUPTS_BCIIMR2A);
+       if (ret < 0)
+               dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
+
+       twl4030_charger_update_current(bci);
+       if (device_create_file(&bci->usb->dev, &dev_attr_max_current))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+       if (device_create_file(&bci->usb->dev, &dev_attr_mode))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+       if (device_create_file(&bci->ac->dev, &dev_attr_mode))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+       if (device_create_file(&bci->ac->dev, &dev_attr_max_current))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+
+       twl4030_charger_enable_ac(bci, true);
+       if (!IS_ERR_OR_NULL(bci->transceiver))
+               twl4030_bci_usb_ncb(&bci->usb_nb,
+                                   bci->transceiver->last_event,
+                                   NULL);
+       else
+               twl4030_charger_enable_usb(bci, false);
+       if (pdata)
+               twl4030_charger_enable_backup(pdata->bb_uvolt,
+                                             pdata->bb_uamp);
+       else
+               twl4030_charger_enable_backup(0, 0);
+
+       return 0;
+fail:
+       iio_channel_release(bci->channel_vac);
+
+       return ret;
+}
+
+static int __exit twl4030_bci_remove(struct platform_device *pdev)
+{
+       struct twl4030_bci *bci = platform_get_drvdata(pdev);
+
+       twl4030_charger_enable_ac(bci, false);
+       twl4030_charger_enable_usb(bci, false);
+       twl4030_charger_enable_backup(0, 0);
+
+       iio_channel_release(bci->channel_vac);
+
+       device_remove_file(&bci->usb->dev, &dev_attr_max_current);
+       device_remove_file(&bci->usb->dev, &dev_attr_mode);
+       device_remove_file(&bci->ac->dev, &dev_attr_max_current);
+       device_remove_file(&bci->ac->dev, &dev_attr_mode);
+       /* mask interrupts */
+       twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
+                        TWL4030_INTERRUPTS_BCIIMR1A);
+       twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
+                        TWL4030_INTERRUPTS_BCIIMR2A);
+
+       return 0;
+}
+
+static const struct of_device_id twl_bci_of_match[] = {
+       {.compatible = "ti,twl4030-bci", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, twl_bci_of_match);
+
+static struct platform_driver twl4030_bci_driver = {
+       .probe = twl4030_bci_probe,
+       .driver = {
+               .name   = "twl4030_bci",
+               .of_match_table = of_match_ptr(twl_bci_of_match),
+       },
+       .remove = __exit_p(twl4030_bci_remove),
+};
+module_platform_driver(twl4030_bci_driver);
+
+MODULE_AUTHOR("Gražvydas Ignotas");
+MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl4030_bci");
diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c
new file mode 100644 (file)
index 0000000..f5817e4
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * Dumb driver for LiIon batteries using TWL4030 madc.
+ *
+ * Copyright 2013 Golden Delicious Computers
+ * Lukas Märdian <lukas@goldelico.com>
+ *
+ * Based on dumb driver for gta01 battery
+ * Copyright 2009 Openmoko, Inc
+ * Balaji Rao <balajirrao@openmoko.org>
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/i2c/twl4030-madc.h>
+#include <linux/power/twl4030_madc_battery.h>
+#include <linux/iio/consumer.h>
+
+struct twl4030_madc_battery {
+       struct power_supply *psy;
+       struct twl4030_madc_bat_platform_data *pdata;
+       struct iio_channel *channel_temp;
+       struct iio_channel *channel_ichg;
+       struct iio_channel *channel_vbat;
+};
+
+static enum power_supply_property twl4030_madc_bat_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+};
+
+static int madc_read(struct iio_channel *channel)
+{
+       int val, err;
+       err = iio_read_channel_processed(channel, &val);
+       if (err < 0)
+               return err;
+
+       return val;
+}
+
+static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery *bt)
+{
+       return (madc_read(bt->channel_ichg) > 0) ? 1 : 0;
+}
+
+static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery *bt)
+{
+       return madc_read(bt->channel_vbat);
+}
+
+static int twl4030_madc_bat_get_current(struct twl4030_madc_battery *bt)
+{
+       return madc_read(bt->channel_ichg) * 1000;
+}
+
+static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery *bt)
+{
+       return madc_read(bt->channel_temp) * 10;
+}
+
+static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat,
+                                       int volt)
+{
+       struct twl4030_madc_bat_calibration *calibration;
+       int i, res = 0;
+
+       /* choose charging curve */
+       if (twl4030_madc_bat_get_charging_status(bat))
+               calibration = bat->pdata->charging;
+       else
+               calibration = bat->pdata->discharging;
+
+       if (volt > calibration[0].voltage) {
+               res = calibration[0].level;
+       } else {
+               for (i = 0; calibration[i+1].voltage >= 0; i++) {
+                       if (volt <= calibration[i].voltage &&
+                                       volt >= calibration[i+1].voltage) {
+                               /* interval found - interpolate within range */
+                               res = calibration[i].level -
+                                       ((calibration[i].voltage - volt) *
+                                       (calibration[i].level -
+                                       calibration[i+1].level)) /
+                                       (calibration[i].voltage -
+                                       calibration[i+1].voltage);
+                               break;
+                       }
+               }
+       }
+       return res;
+}
+
+static int twl4030_madc_bat_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct twl4030_madc_battery *bat = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (twl4030_madc_bat_voltscale(bat,
+                               twl4030_madc_bat_get_voltage(bat)) > 95)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else {
+                       if (twl4030_madc_bat_get_charging_status(bat))
+                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+                       else
+                               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               }
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = twl4030_madc_bat_get_voltage(bat) * 1000;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = twl4030_madc_bat_get_current(bat);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               /* assume battery is always present */
+               val->intval = 1;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW: {
+                       int percent = twl4030_madc_bat_voltscale(bat,
+                                       twl4030_madc_bat_get_voltage(bat));
+                       val->intval = (percent * bat->pdata->capacity) / 100;
+                       break;
+               }
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = twl4030_madc_bat_voltscale(bat,
+                                       twl4030_madc_bat_get_voltage(bat));
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               val->intval = bat->pdata->capacity;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = twl4030_madc_bat_get_temp(bat);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: {
+                       int percent = twl4030_madc_bat_voltscale(bat,
+                                       twl4030_madc_bat_get_voltage(bat));
+                       /* in mAh */
+                       int chg = (percent * (bat->pdata->capacity/1000))/100;
+
+                       /* assume discharge with 400 mA (ca. 1.5W) */
+                       val->intval = (3600l * chg) / 400;
+                       break;
+               }
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void twl4030_madc_bat_ext_changed(struct power_supply *psy)
+{
+       power_supply_changed(psy);
+}
+
+static const struct power_supply_desc twl4030_madc_bat_desc = {
+       .name                   = "twl4030_battery",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = twl4030_madc_bat_props,
+       .num_properties         = ARRAY_SIZE(twl4030_madc_bat_props),
+       .get_property           = twl4030_madc_bat_get_property,
+       .external_power_changed = twl4030_madc_bat_ext_changed,
+
+};
+
+static int twl4030_cmp(const void *a, const void *b)
+{
+       return ((struct twl4030_madc_bat_calibration *)b)->voltage -
+               ((struct twl4030_madc_bat_calibration *)a)->voltage;
+}
+
+static int twl4030_madc_battery_probe(struct platform_device *pdev)
+{
+       struct twl4030_madc_battery *twl4030_madc_bat;
+       struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+       int ret = 0;
+
+       twl4030_madc_bat = devm_kzalloc(&pdev->dev, sizeof(*twl4030_madc_bat),
+                               GFP_KERNEL);
+       if (!twl4030_madc_bat)
+               return -ENOMEM;
+
+       twl4030_madc_bat->channel_temp = iio_channel_get(&pdev->dev, "temp");
+       if (IS_ERR(twl4030_madc_bat->channel_temp)) {
+               ret = PTR_ERR(twl4030_madc_bat->channel_temp);
+               goto err;
+       }
+
+       twl4030_madc_bat->channel_ichg = iio_channel_get(&pdev->dev, "ichg");
+       if (IS_ERR(twl4030_madc_bat->channel_ichg)) {
+               ret = PTR_ERR(twl4030_madc_bat->channel_ichg);
+               goto err_temp;
+       }
+
+       twl4030_madc_bat->channel_vbat = iio_channel_get(&pdev->dev, "vbat");
+       if (IS_ERR(twl4030_madc_bat->channel_vbat)) {
+               ret = PTR_ERR(twl4030_madc_bat->channel_vbat);
+               goto err_ichg;
+       }
+
+       /* sort charging and discharging calibration data */
+       sort(pdata->charging, pdata->charging_size,
+               sizeof(struct twl4030_madc_bat_calibration),
+               twl4030_cmp, NULL);
+       sort(pdata->discharging, pdata->discharging_size,
+               sizeof(struct twl4030_madc_bat_calibration),
+               twl4030_cmp, NULL);
+
+       twl4030_madc_bat->pdata = pdata;
+       platform_set_drvdata(pdev, twl4030_madc_bat);
+       psy_cfg.drv_data = twl4030_madc_bat;
+       twl4030_madc_bat->psy = power_supply_register(&pdev->dev,
+                                                     &twl4030_madc_bat_desc,
+                                                     &psy_cfg);
+       if (IS_ERR(twl4030_madc_bat->psy)) {
+               ret = PTR_ERR(twl4030_madc_bat->psy);
+               goto err_vbat;
+       }
+
+       return 0;
+
+err_vbat:
+       iio_channel_release(twl4030_madc_bat->channel_vbat);
+err_ichg:
+       iio_channel_release(twl4030_madc_bat->channel_ichg);
+err_temp:
+       iio_channel_release(twl4030_madc_bat->channel_temp);
+err:
+       return ret;
+}
+
+static int twl4030_madc_battery_remove(struct platform_device *pdev)
+{
+       struct twl4030_madc_battery *bat = platform_get_drvdata(pdev);
+
+       power_supply_unregister(bat->psy);
+
+       iio_channel_release(bat->channel_vbat);
+       iio_channel_release(bat->channel_ichg);
+       iio_channel_release(bat->channel_temp);
+
+       return 0;
+}
+
+static struct platform_driver twl4030_madc_battery_driver = {
+       .driver = {
+               .name = "twl4030_madc_battery",
+       },
+       .probe  = twl4030_madc_battery_probe,
+       .remove = twl4030_madc_battery_remove,
+};
+module_platform_driver(twl4030_madc_battery_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>");
+MODULE_DESCRIPTION("twl4030_madc battery driver");
+MODULE_ALIAS("platform:twl4030_madc_battery");
diff --git a/drivers/power/supply/wm831x_backup.c b/drivers/power/supply/wm831x_backup.c
new file mode 100644 (file)
index 0000000..2e33109
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Backup battery driver for Wolfson Microelectronics wm831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/pmu.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+struct wm831x_backup {
+       struct wm831x *wm831x;
+       struct power_supply *backup;
+       struct power_supply_desc backup_desc;
+       char name[20];
+};
+
+static int wm831x_backup_read_voltage(struct wm831x *wm831x,
+                                    enum wm831x_auxadc src,
+                                    union power_supply_propval *val)
+{
+       int ret;
+
+       ret = wm831x_auxadc_read_uv(wm831x, src);
+       if (ret >= 0)
+               val->intval = ret;
+
+       return ret;
+}
+
+/*********************************************************************
+ *             Backup supply properties
+ *********************************************************************/
+
+static void wm831x_config_backup(struct wm831x *wm831x)
+{
+       struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
+       struct wm831x_backup_pdata *pdata;
+       int ret, reg;
+
+       if (!wm831x_pdata || !wm831x_pdata->backup) {
+               dev_warn(wm831x->dev,
+                        "No backup battery charger configuration\n");
+               return;
+       }
+
+       pdata = wm831x_pdata->backup;
+
+       reg = 0;
+
+       if (pdata->charger_enable)
+               reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
+       if (pdata->no_constant_voltage)
+               reg |= WM831X_BKUP_CHG_MODE;
+
+       switch (pdata->vlim) {
+       case 2500:
+               break;
+       case 3100:
+               reg |= WM831X_BKUP_CHG_VLIM;
+               break;
+       default:
+               dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
+                       pdata->vlim);
+       }
+
+       switch (pdata->ilim) {
+       case 100:
+               break;
+       case 200:
+               reg |= 1;
+               break;
+       case 300:
+               reg |= 2;
+               break;
+       case 400:
+               reg |= 3;
+               break;
+       default:
+               dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
+                       pdata->ilim);
+       }
+
+       ret = wm831x_reg_unlock(wm831x);
+       if (ret != 0) {
+               dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
+               return;
+       }
+
+       ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
+                             WM831X_BKUP_CHG_ENA_MASK |
+                             WM831X_BKUP_CHG_MODE_MASK |
+                             WM831X_BKUP_BATT_DET_ENA_MASK |
+                             WM831X_BKUP_CHG_VLIM_MASK |
+                             WM831X_BKUP_CHG_ILIM_MASK,
+                             reg);
+       if (ret != 0)
+               dev_err(wm831x->dev,
+                       "Failed to set backup charger config: %d\n", ret);
+
+       wm831x_reg_lock(wm831x);
+}
+
+static int wm831x_backup_get_prop(struct power_supply *psy,
+                                 enum power_supply_property psp,
+                                 union power_supply_propval *val)
+{
+       struct wm831x_backup *devdata = dev_get_drvdata(psy->dev.parent);
+       struct wm831x *wm831x = devdata->wm831x;
+       int ret = 0;
+
+       ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
+       if (ret < 0)
+               return ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (ret & WM831X_BKUP_CHG_STS)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               break;
+
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = wm831x_backup_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
+                                               val);
+               break;
+
+       case POWER_SUPPLY_PROP_PRESENT:
+               if (ret & WM831X_BKUP_CHG_STS)
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property wm831x_backup_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_PRESENT,
+};
+
+/*********************************************************************
+ *             Initialisation
+ *********************************************************************/
+
+static int wm831x_backup_probe(struct platform_device *pdev)
+{
+       struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+       struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
+       struct wm831x_backup *devdata;
+
+       devdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_backup),
+                               GFP_KERNEL);
+       if (devdata == NULL)
+               return -ENOMEM;
+
+       devdata->wm831x = wm831x;
+       platform_set_drvdata(pdev, devdata);
+
+       /* We ignore configuration failures since we can still read
+        * back the status without enabling the charger (which may
+        * already be enabled anyway).
+        */
+       wm831x_config_backup(wm831x);
+
+       if (wm831x_pdata && wm831x_pdata->wm831x_num)
+               snprintf(devdata->name, sizeof(devdata->name),
+                        "wm831x-backup.%d", wm831x_pdata->wm831x_num);
+       else
+               snprintf(devdata->name, sizeof(devdata->name),
+                        "wm831x-backup");
+
+       devdata->backup_desc.name = devdata->name;
+       devdata->backup_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+       devdata->backup_desc.properties = wm831x_backup_props;
+       devdata->backup_desc.num_properties = ARRAY_SIZE(wm831x_backup_props);
+       devdata->backup_desc.get_property = wm831x_backup_get_prop;
+       devdata->backup = power_supply_register(&pdev->dev,
+                                               &devdata->backup_desc, NULL);
+
+       return PTR_ERR_OR_ZERO(devdata->backup);
+}
+
+static int wm831x_backup_remove(struct platform_device *pdev)
+{
+       struct wm831x_backup *devdata = platform_get_drvdata(pdev);
+
+       power_supply_unregister(devdata->backup);
+
+       return 0;
+}
+
+static struct platform_driver wm831x_backup_driver = {
+       .probe = wm831x_backup_probe,
+       .remove = wm831x_backup_remove,
+       .driver = {
+               .name = "wm831x-backup",
+       },
+};
+
+module_platform_driver(wm831x_backup_driver);
+
+MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-backup");
diff --git a/drivers/power/supply/wm831x_power.c b/drivers/power/supply/wm831x_power.c
new file mode 100644 (file)
index 0000000..7082301
--- /dev/null
@@ -0,0 +1,673 @@
+/*
+ * PMU driver for Wolfson Microelectronics wm831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/pmu.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+struct wm831x_power {
+       struct wm831x *wm831x;
+       struct power_supply *wall;
+       struct power_supply *usb;
+       struct power_supply *battery;
+       struct power_supply_desc wall_desc;
+       struct power_supply_desc usb_desc;
+       struct power_supply_desc battery_desc;
+       char wall_name[20];
+       char usb_name[20];
+       char battery_name[20];
+       bool have_battery;
+};
+
+static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
+                                    union power_supply_propval *val)
+{
+       int ret;
+
+       ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
+       if (ret < 0)
+               return ret;
+
+       if (ret & supply)
+               val->intval = 1;
+       else
+               val->intval = 0;
+
+       return 0;
+}
+
+static int wm831x_power_read_voltage(struct wm831x *wm831x,
+                                    enum wm831x_auxadc src,
+                                    union power_supply_propval *val)
+{
+       int ret;
+
+       ret = wm831x_auxadc_read_uv(wm831x, src);
+       if (ret >= 0)
+               val->intval = ret;
+
+       return ret;
+}
+
+/*********************************************************************
+ *             WALL Power
+ *********************************************************************/
+static int wm831x_wall_get_prop(struct power_supply *psy,
+                               enum power_supply_property psp,
+                               union power_supply_propval *val)
+{
+       struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
+       struct wm831x *wm831x = wm831x_power->wm831x;
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property wm831x_wall_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *             USB Power
+ *********************************************************************/
+static int wm831x_usb_get_prop(struct power_supply *psy,
+                              enum power_supply_property psp,
+                              union power_supply_propval *val)
+{
+       struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
+       struct wm831x *wm831x = wm831x_power->wm831x;
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property wm831x_usb_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *             Battery properties
+ *********************************************************************/
+
+struct chg_map {
+       int val;
+       int reg_val;
+};
+
+static struct chg_map trickle_ilims[] = {
+       {  50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT },
+       { 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT },
+       { 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT },
+       { 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT },
+};
+
+static struct chg_map vsels[] = {
+       { 4050, 0 << WM831X_CHG_VSEL_SHIFT },
+       { 4100, 1 << WM831X_CHG_VSEL_SHIFT },
+       { 4150, 2 << WM831X_CHG_VSEL_SHIFT },
+       { 4200, 3 << WM831X_CHG_VSEL_SHIFT },
+};
+
+static struct chg_map fast_ilims[] = {
+       {    0,  0 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {   50,  1 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  100,  2 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  150,  3 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  200,  4 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  250,  5 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  300,  6 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  350,  7 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  400,  8 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  450,  9 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  500, 10 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  600, 11 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  700, 12 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  800, 13 << WM831X_CHG_FAST_ILIM_SHIFT },
+       {  900, 14 << WM831X_CHG_FAST_ILIM_SHIFT },
+       { 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT },
+};
+
+static struct chg_map eoc_iterms[] = {
+       { 20, 0 << WM831X_CHG_ITERM_SHIFT },
+       { 30, 1 << WM831X_CHG_ITERM_SHIFT },
+       { 40, 2 << WM831X_CHG_ITERM_SHIFT },
+       { 50, 3 << WM831X_CHG_ITERM_SHIFT },
+       { 60, 4 << WM831X_CHG_ITERM_SHIFT },
+       { 70, 5 << WM831X_CHG_ITERM_SHIFT },
+       { 80, 6 << WM831X_CHG_ITERM_SHIFT },
+       { 90, 7 << WM831X_CHG_ITERM_SHIFT },
+};
+
+static struct chg_map chg_times[] = {
+       {  60,  0 << WM831X_CHG_TIME_SHIFT },
+       {  90,  1 << WM831X_CHG_TIME_SHIFT },
+       { 120,  2 << WM831X_CHG_TIME_SHIFT },
+       { 150,  3 << WM831X_CHG_TIME_SHIFT },
+       { 180,  4 << WM831X_CHG_TIME_SHIFT },
+       { 210,  5 << WM831X_CHG_TIME_SHIFT },
+       { 240,  6 << WM831X_CHG_TIME_SHIFT },
+       { 270,  7 << WM831X_CHG_TIME_SHIFT },
+       { 300,  8 << WM831X_CHG_TIME_SHIFT },
+       { 330,  9 << WM831X_CHG_TIME_SHIFT },
+       { 360, 10 << WM831X_CHG_TIME_SHIFT },
+       { 390, 11 << WM831X_CHG_TIME_SHIFT },
+       { 420, 12 << WM831X_CHG_TIME_SHIFT },
+       { 450, 13 << WM831X_CHG_TIME_SHIFT },
+       { 480, 14 << WM831X_CHG_TIME_SHIFT },
+       { 510, 15 << WM831X_CHG_TIME_SHIFT },
+};
+
+static void wm831x_battey_apply_config(struct wm831x *wm831x,
+                                      struct chg_map *map, int count, int val,
+                                      int *reg, const char *name,
+                                      const char *units)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               if (val == map[i].val)
+                       break;
+       if (i == count) {
+               dev_err(wm831x->dev, "Invalid %s %d%s\n",
+                       name, val, units);
+       } else {
+               *reg |= map[i].reg_val;
+               dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units);
+       }
+}
+
+static void wm831x_config_battery(struct wm831x *wm831x)
+{
+       struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
+       struct wm831x_battery_pdata *pdata;
+       int ret, reg1, reg2;
+
+       if (!wm831x_pdata || !wm831x_pdata->battery) {
+               dev_warn(wm831x->dev,
+                        "No battery charger configuration\n");
+               return;
+       }
+
+       pdata = wm831x_pdata->battery;
+
+       reg1 = 0;
+       reg2 = 0;
+
+       if (!pdata->enable) {
+               dev_info(wm831x->dev, "Battery charger disabled\n");
+               return;
+       }
+
+       reg1 |= WM831X_CHG_ENA;
+       if (pdata->off_mask)
+               reg2 |= WM831X_CHG_OFF_MSK;
+       if (pdata->fast_enable)
+               reg1 |= WM831X_CHG_FAST;
+
+       wm831x_battey_apply_config(wm831x, trickle_ilims,
+                                  ARRAY_SIZE(trickle_ilims),
+                                  pdata->trickle_ilim, &reg2,
+                                  "trickle charge current limit", "mA");
+
+       wm831x_battey_apply_config(wm831x, vsels, ARRAY_SIZE(vsels),
+                                  pdata->vsel, &reg2,
+                                  "target voltage", "mV");
+
+       wm831x_battey_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims),
+                                  pdata->fast_ilim, &reg2,
+                                  "fast charge current limit", "mA");
+
+       wm831x_battey_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms),
+                                  pdata->eoc_iterm, &reg1,
+                                  "end of charge current threshold", "mA");
+
+       wm831x_battey_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times),
+                                  pdata->timeout, &reg2,
+                                  "charger timeout", "min");
+
+       ret = wm831x_reg_unlock(wm831x);
+       if (ret != 0) {
+               dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
+               return;
+       }
+
+       ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
+                             WM831X_CHG_ENA_MASK |
+                             WM831X_CHG_FAST_MASK |
+                             WM831X_CHG_ITERM_MASK,
+                             reg1);
+       if (ret != 0)
+               dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
+                       ret);
+
+       ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
+                             WM831X_CHG_OFF_MSK |
+                             WM831X_CHG_TIME_MASK |
+                             WM831X_CHG_FAST_ILIM_MASK |
+                             WM831X_CHG_TRKL_ILIM_MASK |
+                             WM831X_CHG_VSEL_MASK,
+                             reg2);
+       if (ret != 0)
+               dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
+                       ret);
+
+       wm831x_reg_lock(wm831x);
+}
+
+static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
+{
+       int ret;
+
+       ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
+       if (ret < 0)
+               return ret;
+
+       if (ret & WM831X_PWR_SRC_BATT) {
+               *status = POWER_SUPPLY_STATUS_DISCHARGING;
+               return 0;
+       }
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
+       if (ret < 0)
+               return ret;
+
+       switch (ret & WM831X_CHG_STATE_MASK) {
+       case WM831X_CHG_STATE_OFF:
+               *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               break;
+       case WM831X_CHG_STATE_TRICKLE:
+       case WM831X_CHG_STATE_FAST:
+               *status = POWER_SUPPLY_STATUS_CHARGING;
+               break;
+
+       default:
+               *status = POWER_SUPPLY_STATUS_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
+{
+       int ret;
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
+       if (ret < 0)
+               return ret;
+
+       switch (ret & WM831X_CHG_STATE_MASK) {
+       case WM831X_CHG_STATE_TRICKLE:
+       case WM831X_CHG_STATE_TRICKLE_OT:
+               *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+               break;
+       case WM831X_CHG_STATE_FAST:
+       case WM831X_CHG_STATE_FAST_OT:
+               *type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               break;
+       default:
+               *type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               break;
+       }
+
+       return 0;
+}
+
+static int wm831x_bat_check_health(struct wm831x *wm831x, int *health)
+{
+       int ret;
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
+       if (ret < 0)
+               return ret;
+
+       if (ret & WM831X_BATT_HOT_STS) {
+               *health = POWER_SUPPLY_HEALTH_OVERHEAT;
+               return 0;
+       }
+
+       if (ret & WM831X_BATT_COLD_STS) {
+               *health = POWER_SUPPLY_HEALTH_COLD;
+               return 0;
+       }
+
+       if (ret & WM831X_BATT_OV_STS) {
+               *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               return 0;
+       }
+
+       switch (ret & WM831X_CHG_STATE_MASK) {
+       case WM831X_CHG_STATE_TRICKLE_OT:
+       case WM831X_CHG_STATE_FAST_OT:
+               *health = POWER_SUPPLY_HEALTH_OVERHEAT;
+               break;
+       case WM831X_CHG_STATE_DEFECTIVE:
+               *health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               break;
+       default:
+               *health = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       }
+
+       return 0;
+}
+
+static int wm831x_bat_get_prop(struct power_supply *psy,
+                              enum power_supply_property psp,
+                              union power_supply_propval *val)
+{
+       struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
+       struct wm831x *wm831x = wm831x_power->wm831x;
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = wm831x_bat_check_status(wm831x, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
+                                               val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = wm831x_bat_check_health(wm831x, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               ret = wm831x_bat_check_type(wm831x, &val->intval);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property wm831x_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+};
+
+static const char *wm831x_bat_irqs[] = {
+       "BATT HOT",
+       "BATT COLD",
+       "BATT FAIL",
+       "OV",
+       "END",
+       "TO",
+       "MODE",
+       "START",
+};
+
+static irqreturn_t wm831x_bat_irq(int irq, void *data)
+{
+       struct wm831x_power *wm831x_power = data;
+       struct wm831x *wm831x = wm831x_power->wm831x;
+
+       dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
+
+       /* The battery charger is autonomous so we don't need to do
+        * anything except kick user space */
+       if (wm831x_power->have_battery)
+               power_supply_changed(wm831x_power->battery);
+
+       return IRQ_HANDLED;
+}
+
+
+/*********************************************************************
+ *             Initialisation
+ *********************************************************************/
+
+static irqreturn_t wm831x_syslo_irq(int irq, void *data)
+{
+       struct wm831x_power *wm831x_power = data;
+       struct wm831x *wm831x = wm831x_power->wm831x;
+
+       /* Not much we can actually *do* but tell people for
+        * posterity, we're probably about to run out of power. */
+       dev_crit(wm831x->dev, "SYSVDD under voltage\n");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
+{
+       struct wm831x_power *wm831x_power = data;
+       struct wm831x *wm831x = wm831x_power->wm831x;
+
+       dev_dbg(wm831x->dev, "Power source changed\n");
+
+       /* Just notify for everything - little harm in overnotifying. */
+       if (wm831x_power->have_battery)
+               power_supply_changed(wm831x_power->battery);
+       power_supply_changed(wm831x_power->usb);
+       power_supply_changed(wm831x_power->wall);
+
+       return IRQ_HANDLED;
+}
+
+static int wm831x_power_probe(struct platform_device *pdev)
+{
+       struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+       struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
+       struct wm831x_power *power;
+       int ret, irq, i;
+
+       power = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_power),
+                            GFP_KERNEL);
+       if (power == NULL)
+               return -ENOMEM;
+
+       power->wm831x = wm831x;
+       platform_set_drvdata(pdev, power);
+
+       if (wm831x_pdata && wm831x_pdata->wm831x_num) {
+               snprintf(power->wall_name, sizeof(power->wall_name),
+                        "wm831x-wall.%d", wm831x_pdata->wm831x_num);
+               snprintf(power->battery_name, sizeof(power->wall_name),
+                        "wm831x-battery.%d", wm831x_pdata->wm831x_num);
+               snprintf(power->usb_name, sizeof(power->wall_name),
+                        "wm831x-usb.%d", wm831x_pdata->wm831x_num);
+       } else {
+               snprintf(power->wall_name, sizeof(power->wall_name),
+                        "wm831x-wall");
+               snprintf(power->battery_name, sizeof(power->wall_name),
+                        "wm831x-battery");
+               snprintf(power->usb_name, sizeof(power->wall_name),
+                        "wm831x-usb");
+       }
+
+       /* We ignore configuration failures since we can still read back
+        * the status without enabling the charger.
+        */
+       wm831x_config_battery(wm831x);
+
+       power->wall_desc.name = power->wall_name;
+       power->wall_desc.type = POWER_SUPPLY_TYPE_MAINS;
+       power->wall_desc.properties = wm831x_wall_props;
+       power->wall_desc.num_properties = ARRAY_SIZE(wm831x_wall_props);
+       power->wall_desc.get_property = wm831x_wall_get_prop;
+       power->wall = power_supply_register(&pdev->dev, &power->wall_desc,
+                                           NULL);
+       if (IS_ERR(power->wall)) {
+               ret = PTR_ERR(power->wall);
+               goto err;
+       }
+
+       power->usb_desc.name = power->usb_name,
+       power->usb_desc.type = POWER_SUPPLY_TYPE_USB;
+       power->usb_desc.properties = wm831x_usb_props;
+       power->usb_desc.num_properties = ARRAY_SIZE(wm831x_usb_props);
+       power->usb_desc.get_property = wm831x_usb_get_prop;
+       power->usb = power_supply_register(&pdev->dev, &power->usb_desc, NULL);
+       if (IS_ERR(power->usb)) {
+               ret = PTR_ERR(power->usb);
+               goto err_wall;
+       }
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
+       if (ret < 0)
+               goto err_wall;
+       power->have_battery = ret & WM831X_CHG_ENA;
+
+       if (power->have_battery) {
+               power->battery_desc.name = power->battery_name;
+               power->battery_desc.properties = wm831x_bat_props;
+               power->battery_desc.num_properties = ARRAY_SIZE(wm831x_bat_props);
+               power->battery_desc.get_property = wm831x_bat_get_prop;
+               power->battery_desc.use_for_apm = 1;
+               power->battery = power_supply_register(&pdev->dev,
+                                                      &power->battery_desc,
+                                                      NULL);
+               if (IS_ERR(power->battery)) {
+                       ret = PTR_ERR(power->battery);
+                       goto err_usb;
+               }
+       }
+
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
+       ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
+                                  IRQF_TRIGGER_RISING | IRQF_ONESHOT, "System power low",
+                                  power);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
+                       irq, ret);
+               goto err_battery;
+       }
+
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
+       ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
+                                  IRQF_TRIGGER_RISING | IRQF_ONESHOT, "Power source",
+                                  power);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
+                       irq, ret);
+               goto err_syslo;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
+               irq = wm831x_irq(wm831x,
+                                platform_get_irq_byname(pdev,
+                                                        wm831x_bat_irqs[i]));
+               ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
+                                          IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                          wm831x_bat_irqs[i],
+                                          power);
+               if (ret != 0) {
+                       dev_err(&pdev->dev,
+                               "Failed to request %s IRQ %d: %d\n",
+                               wm831x_bat_irqs[i], irq, ret);
+                       goto err_bat_irq;
+               }
+       }
+
+       return ret;
+
+err_bat_irq:
+       --i;
+       for (; i >= 0; i--) {
+               irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
+               free_irq(irq, power);
+       }
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
+       free_irq(irq, power);
+err_syslo:
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
+       free_irq(irq, power);
+err_battery:
+       if (power->have_battery)
+               power_supply_unregister(power->battery);
+err_usb:
+       power_supply_unregister(power->usb);
+err_wall:
+       power_supply_unregister(power->wall);
+err:
+       return ret;
+}
+
+static int wm831x_power_remove(struct platform_device *pdev)
+{
+       struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
+       struct wm831x *wm831x = wm831x_power->wm831x;
+       int irq, i;
+
+       for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
+               irq = wm831x_irq(wm831x, 
+                                platform_get_irq_byname(pdev,
+                                                        wm831x_bat_irqs[i]));
+               free_irq(irq, wm831x_power);
+       }
+
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
+       free_irq(irq, wm831x_power);
+
+       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
+       free_irq(irq, wm831x_power);
+
+       if (wm831x_power->have_battery)
+               power_supply_unregister(wm831x_power->battery);
+       power_supply_unregister(wm831x_power->wall);
+       power_supply_unregister(wm831x_power->usb);
+       return 0;
+}
+
+static struct platform_driver wm831x_power_driver = {
+       .probe = wm831x_power_probe,
+       .remove = wm831x_power_remove,
+       .driver = {
+               .name = "wm831x-power",
+       },
+};
+
+module_platform_driver(wm831x_power_driver);
+
+MODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-power");
diff --git a/drivers/power/supply/wm8350_power.c b/drivers/power/supply/wm8350_power.c
new file mode 100644 (file)
index 0000000..5c58806
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * Battery driver for wm8350 PMIC
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Based on OLPC Battery Driver
+ *
+ * Copyright 2006  David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/comparator.h>
+
+static int wm8350_read_battery_uvolts(struct wm8350 *wm8350)
+{
+       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, 0, 0)
+               * WM8350_AUX_COEFF;
+}
+
+static int wm8350_read_line_uvolts(struct wm8350 *wm8350)
+{
+       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, 0, 0)
+               * WM8350_AUX_COEFF;
+}
+
+static int wm8350_read_usb_uvolts(struct wm8350 *wm8350)
+{
+       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, 0, 0)
+               * WM8350_AUX_COEFF;
+}
+
+#define WM8350_BATT_SUPPLY     1
+#define WM8350_USB_SUPPLY      2
+#define WM8350_LINE_SUPPLY     4
+
+static inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min)
+{
+       if (!wm8350->power.rev_g_coeff)
+               return (((min - 30) / 15) & 0xf) << 8;
+       else
+               return (((min - 30) / 30) & 0xf) << 8;
+}
+
+static int wm8350_get_supplies(struct wm8350 *wm8350)
+{
+       u16 sm, ov, co, chrg;
+       int supplies = 0;
+
+       sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS);
+       ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES);
+       co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES);
+       chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
+
+       /* USB_SM */
+       sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT;
+
+       /* CHG_ISEL */
+       chrg &= WM8350_CHG_ISEL_MASK;
+
+       /* If the USB state machine is active then we're using that with or
+        * without battery, otherwise check for wall supply */
+       if (((sm == WM8350_USB_SM_100_SLV) ||
+            (sm == WM8350_USB_SM_500_SLV) ||
+            (sm == WM8350_USB_SM_STDBY_SLV))
+           && !(ov & WM8350_USB_LIMIT_OVRDE))
+               supplies = WM8350_USB_SUPPLY;
+       else if (((sm == WM8350_USB_SM_100_SLV) ||
+                 (sm == WM8350_USB_SM_500_SLV) ||
+                 (sm == WM8350_USB_SM_STDBY_SLV))
+                && (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0))
+               supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY;
+       else if (co & WM8350_WALL_FB_OVRDE)
+               supplies = WM8350_LINE_SUPPLY;
+       else
+               supplies = WM8350_BATT_SUPPLY;
+
+       return supplies;
+}
+
+static int wm8350_charger_config(struct wm8350 *wm8350,
+                                struct wm8350_charger_policy *policy)
+{
+       u16 reg, eoc_mA, fast_limit_mA;
+
+       if (!policy) {
+               dev_warn(wm8350->dev,
+                        "No charger policy, charger not configured.\n");
+               return -EINVAL;
+       }
+
+       /* make sure USB fast charge current is not > 500mA */
+       if (policy->fast_limit_USB_mA > 500) {
+               dev_err(wm8350->dev, "USB fast charge > 500mA\n");
+               return -EINVAL;
+       }
+
+       eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA);
+
+       wm8350_reg_unlock(wm8350);
+
+       reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1)
+               & WM8350_CHG_ENA_R168;
+       wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
+                        reg | eoc_mA | policy->trickle_start_mV |
+                        WM8350_CHG_TRICKLE_TEMP_CHOKE |
+                        WM8350_CHG_TRICKLE_USB_CHOKE |
+                        WM8350_CHG_FAST_USB_THROTTLE);
+
+       if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) {
+               fast_limit_mA =
+                       WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA);
+               wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
+                           policy->charge_mV | policy->trickle_charge_USB_mA |
+                           fast_limit_mA | wm8350_charge_time_min(wm8350,
+                                               policy->charge_timeout));
+
+       } else {
+               fast_limit_mA =
+                       WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA);
+               wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
+                           policy->charge_mV | policy->trickle_charge_mA |
+                           fast_limit_mA | wm8350_charge_time_min(wm8350,
+                                               policy->charge_timeout));
+       }
+
+       wm8350_reg_lock(wm8350);
+       return 0;
+}
+
+static int wm8350_batt_status(struct wm8350 *wm8350)
+{
+       u16 state;
+
+       state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
+       state &= WM8350_CHG_STS_MASK;
+
+       switch (state) {
+       case WM8350_CHG_STS_OFF:
+               return POWER_SUPPLY_STATUS_DISCHARGING;
+
+       case WM8350_CHG_STS_TRICKLE:
+       case WM8350_CHG_STS_FAST:
+               return POWER_SUPPLY_STATUS_CHARGING;
+
+       default:
+               return POWER_SUPPLY_STATUS_UNKNOWN;
+       }
+}
+
+static ssize_t charger_state_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(dev);
+       char *charge;
+       int state;
+
+       state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &
+           WM8350_CHG_STS_MASK;
+       switch (state) {
+       case WM8350_CHG_STS_OFF:
+               charge = "Charger Off";
+               break;
+       case WM8350_CHG_STS_TRICKLE:
+               charge = "Trickle Charging";
+               break;
+       case WM8350_CHG_STS_FAST:
+               charge = "Fast Charging";
+               break;
+       default:
+               return 0;
+       }
+
+       return sprintf(buf, "%s\n", charge);
+}
+
+static DEVICE_ATTR(charger_state, 0444, charger_state_show, NULL);
+
+static irqreturn_t wm8350_charger_handler(int irq, void *data)
+{
+       struct wm8350 *wm8350 = data;
+       struct wm8350_power *power = &wm8350->power;
+       struct wm8350_charger_policy *policy = power->policy;
+
+       switch (irq - wm8350->irq_base) {
+       case WM8350_IRQ_CHG_BAT_FAIL:
+               dev_err(wm8350->dev, "battery failed\n");
+               break;
+       case WM8350_IRQ_CHG_TO:
+               dev_err(wm8350->dev, "charger timeout\n");
+               power_supply_changed(power->battery);
+               break;
+
+       case WM8350_IRQ_CHG_BAT_HOT:
+       case WM8350_IRQ_CHG_BAT_COLD:
+       case WM8350_IRQ_CHG_START:
+       case WM8350_IRQ_CHG_END:
+               power_supply_changed(power->battery);
+               break;
+
+       case WM8350_IRQ_CHG_FAST_RDY:
+               dev_dbg(wm8350->dev, "fast charger ready\n");
+               wm8350_charger_config(wm8350, policy);
+               wm8350_reg_unlock(wm8350);
+               wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
+                               WM8350_CHG_FAST);
+               wm8350_reg_lock(wm8350);
+               break;
+
+       case WM8350_IRQ_CHG_VBATT_LT_3P9:
+               dev_warn(wm8350->dev, "battery < 3.9V\n");
+               break;
+       case WM8350_IRQ_CHG_VBATT_LT_3P1:
+               dev_warn(wm8350->dev, "battery < 3.1V\n");
+               break;
+       case WM8350_IRQ_CHG_VBATT_LT_2P85:
+               dev_warn(wm8350->dev, "battery < 2.85V\n");
+               break;
+
+               /* Supply change.  We will overnotify but it should do
+                * no harm. */
+       case WM8350_IRQ_EXT_USB_FB:
+       case WM8350_IRQ_EXT_WALL_FB:
+               wm8350_charger_config(wm8350, policy);
+       case WM8350_IRQ_EXT_BAT_FB:   /* Fall through */
+               power_supply_changed(power->battery);
+               power_supply_changed(power->usb);
+               power_supply_changed(power->ac);
+               break;
+
+       default:
+               dev_err(wm8350->dev, "Unknown interrupt %d\n", irq);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*********************************************************************
+ *             AC Power
+ *********************************************************************/
+static int wm8350_ac_get_prop(struct power_supply *psy,
+                             enum power_supply_property psp,
+                             union power_supply_propval *val)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!(wm8350_get_supplies(wm8350) &
+                                WM8350_LINE_SUPPLY);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = wm8350_read_line_uvolts(wm8350);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static enum power_supply_property wm8350_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *             USB Power
+ *********************************************************************/
+static int wm8350_usb_get_prop(struct power_supply *psy,
+                              enum power_supply_property psp,
+                              union power_supply_propval *val)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!(wm8350_get_supplies(wm8350) &
+                                WM8350_USB_SUPPLY);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = wm8350_read_usb_uvolts(wm8350);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static enum power_supply_property wm8350_usb_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+/*********************************************************************
+ *             Battery properties
+ *********************************************************************/
+
+static int wm8350_bat_check_health(struct wm8350 *wm8350)
+{
+       u16 reg;
+
+       if (wm8350_read_battery_uvolts(wm8350) < 2850000)
+               return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+
+       reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES);
+       if (reg & WM8350_CHG_BATT_HOT_OVRDE)
+               return POWER_SUPPLY_HEALTH_OVERHEAT;
+
+       if (reg & WM8350_CHG_BATT_COLD_OVRDE)
+               return POWER_SUPPLY_HEALTH_COLD;
+
+       return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int wm8350_bat_get_charge_type(struct wm8350 *wm8350)
+{
+       int state;
+
+       state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &
+           WM8350_CHG_STS_MASK;
+       switch (state) {
+       case WM8350_CHG_STS_OFF:
+               return POWER_SUPPLY_CHARGE_TYPE_NONE;
+       case WM8350_CHG_STS_TRICKLE:
+               return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+       case WM8350_CHG_STS_FAST:
+               return POWER_SUPPLY_CHARGE_TYPE_FAST;
+       default:
+               return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+       }
+}
+
+static int wm8350_bat_get_property(struct power_supply *psy,
+                                  enum power_supply_property psp,
+                                  union power_supply_propval *val)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = wm8350_batt_status(wm8350);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!(wm8350_get_supplies(wm8350) &
+                                WM8350_BATT_SUPPLY);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = wm8350_read_battery_uvolts(wm8350);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = wm8350_bat_check_health(wm8350);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               val->intval = wm8350_bat_get_charge_type(wm8350);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property wm8350_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+};
+
+static const struct power_supply_desc wm8350_ac_desc = {
+       .name           = "wm8350-ac",
+       .type           = POWER_SUPPLY_TYPE_MAINS,
+       .properties     = wm8350_ac_props,
+       .num_properties = ARRAY_SIZE(wm8350_ac_props),
+       .get_property   = wm8350_ac_get_prop,
+};
+
+static const struct power_supply_desc wm8350_battery_desc = {
+       .name           = "wm8350-battery",
+       .properties     = wm8350_bat_props,
+       .num_properties = ARRAY_SIZE(wm8350_bat_props),
+       .get_property   = wm8350_bat_get_property,
+       .use_for_apm    = 1,
+};
+
+static const struct power_supply_desc wm8350_usb_desc = {
+       .name           = "wm8350-usb",
+       .type           = POWER_SUPPLY_TYPE_USB,
+       .properties     = wm8350_usb_props,
+       .num_properties = ARRAY_SIZE(wm8350_usb_props),
+       .get_property   = wm8350_usb_get_prop,
+};
+
+/*********************************************************************
+ *             Initialisation
+ *********************************************************************/
+
+static void wm8350_init_charger(struct wm8350 *wm8350)
+{
+       /* register our interest in charger events */
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,
+                           wm8350_charger_handler, 0, "Battery hot", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,
+                           wm8350_charger_handler, 0, "Battery cold", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,
+                           wm8350_charger_handler, 0, "Battery fail", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,
+                           wm8350_charger_handler, 0,
+                           "Charger timeout", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,
+                           wm8350_charger_handler, 0,
+                           "Charge end", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,
+                           wm8350_charger_handler, 0,
+                           "Charge start", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,
+                           wm8350_charger_handler, 0,
+                           "Fast charge ready", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,
+                           wm8350_charger_handler, 0,
+                           "Battery <3.9V", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,
+                           wm8350_charger_handler, 0,
+                           "Battery <3.1V", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,
+                           wm8350_charger_handler, 0,
+                           "Battery <2.85V", wm8350);
+
+       /* and supply change events */
+       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB,
+                           wm8350_charger_handler, 0, "USB", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB,
+                           wm8350_charger_handler, 0, "Wall", wm8350);
+       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB,
+                           wm8350_charger_handler, 0, "Battery", wm8350);
+}
+
+static void free_charger_irq(struct wm8350 *wm8350)
+{
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350);
+       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, wm8350);
+}
+
+static int wm8350_power_probe(struct platform_device *pdev)
+{
+       struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+       struct wm8350_power *power = &wm8350->power;
+       struct wm8350_charger_policy *policy = power->policy;
+       int ret;
+
+       power->ac = power_supply_register(&pdev->dev, &wm8350_ac_desc, NULL);
+       if (IS_ERR(power->ac))
+               return PTR_ERR(power->ac);
+
+       power->battery = power_supply_register(&pdev->dev, &wm8350_battery_desc,
+                                              NULL);
+       if (IS_ERR(power->battery)) {
+               ret = PTR_ERR(power->battery);
+               goto battery_failed;
+       }
+
+       power->usb = power_supply_register(&pdev->dev, &wm8350_usb_desc, NULL);
+       if (IS_ERR(power->usb)) {
+               ret = PTR_ERR(power->usb);
+               goto usb_failed;
+       }
+
+       ret = device_create_file(&pdev->dev, &dev_attr_charger_state);
+       if (ret < 0)
+               dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret);
+       ret = 0;
+
+       wm8350_init_charger(wm8350);
+       if (wm8350_charger_config(wm8350, policy) == 0) {
+               wm8350_reg_unlock(wm8350);
+               wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA);
+               wm8350_reg_lock(wm8350);
+       }
+
+       return ret;
+
+usb_failed:
+       power_supply_unregister(power->battery);
+battery_failed:
+       power_supply_unregister(power->ac);
+
+       return ret;
+}
+
+static int wm8350_power_remove(struct platform_device *pdev)
+{
+       struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+       struct wm8350_power *power = &wm8350->power;
+
+       free_charger_irq(wm8350);
+       device_remove_file(&pdev->dev, &dev_attr_charger_state);
+       power_supply_unregister(power->battery);
+       power_supply_unregister(power->ac);
+       power_supply_unregister(power->usb);
+       return 0;
+}
+
+static struct platform_driver wm8350_power_driver = {
+       .probe = wm8350_power_probe,
+       .remove = wm8350_power_remove,
+       .driver = {
+               .name = "wm8350-power",
+       },
+};
+
+module_platform_driver(wm8350_power_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Power supply driver for WM8350");
+MODULE_ALIAS("platform:wm8350-power");
diff --git a/drivers/power/supply/wm97xx_battery.c b/drivers/power/supply/wm97xx_battery.c
new file mode 100644 (file)
index 0000000..6285626
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Battery measurement code for WM97xx
+ *
+ * based on tosa_battery.c
+ *
+ * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/wm97xx.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+
+static struct work_struct bat_work;
+static DEFINE_MUTEX(work_lock);
+static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
+static enum power_supply_property *prop;
+
+static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
+{
+       struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
+       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
+
+       return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent),
+                                       pdata->batt_aux) * pdata->batt_mult /
+                                       pdata->batt_div;
+}
+
+static unsigned long wm97xx_read_temp(struct power_supply *bat_ps)
+{
+       struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
+       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
+
+       return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent),
+                                       pdata->temp_aux) * pdata->temp_mult /
+                                       pdata->temp_div;
+}
+
+static int wm97xx_bat_get_property(struct power_supply *bat_ps,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
+       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = bat_status;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = pdata->batt_tech;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (pdata->batt_aux >= 0)
+                       val->intval = wm97xx_read_bat(bat_ps);
+               else
+                       return -EINVAL;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               if (pdata->temp_aux >= 0)
+                       val->intval = wm97xx_read_temp(bat_ps);
+               else
+                       return -EINVAL;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               if (pdata->max_voltage >= 0)
+                       val->intval = pdata->max_voltage;
+               else
+                       return -EINVAL;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+               if (pdata->min_voltage >= 0)
+                       val->intval = pdata->min_voltage;
+               else
+                       return -EINVAL;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps)
+{
+       schedule_work(&bat_work);
+}
+
+static void wm97xx_bat_update(struct power_supply *bat_ps)
+{
+       int old_status = bat_status;
+       struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
+       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
+
+       mutex_lock(&work_lock);
+
+       bat_status = (pdata->charge_gpio >= 0) ?
+                       (gpio_get_value(pdata->charge_gpio) ?
+                       POWER_SUPPLY_STATUS_DISCHARGING :
+                       POWER_SUPPLY_STATUS_CHARGING) :
+                       POWER_SUPPLY_STATUS_UNKNOWN;
+
+       if (old_status != bat_status) {
+               pr_debug("%s: %i -> %i\n", bat_ps->desc->name, old_status,
+                                       bat_status);
+               power_supply_changed(bat_ps);
+       }
+
+       mutex_unlock(&work_lock);
+}
+
+static struct power_supply *bat_psy;
+static struct power_supply_desc bat_psy_desc = {
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .get_property           = wm97xx_bat_get_property,
+       .external_power_changed = wm97xx_bat_external_power_changed,
+       .use_for_apm            = 1,
+};
+
+static void wm97xx_bat_work(struct work_struct *work)
+{
+       wm97xx_bat_update(bat_psy);
+}
+
+static irqreturn_t wm97xx_chrg_irq(int irq, void *data)
+{
+       schedule_work(&bat_work);
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+static int wm97xx_bat_suspend(struct device *dev)
+{
+       flush_work(&bat_work);
+       return 0;
+}
+
+static int wm97xx_bat_resume(struct device *dev)
+{
+       schedule_work(&bat_work);
+       return 0;
+}
+
+static const struct dev_pm_ops wm97xx_bat_pm_ops = {
+       .suspend        = wm97xx_bat_suspend,
+       .resume         = wm97xx_bat_resume,
+};
+#endif
+
+static int wm97xx_bat_probe(struct platform_device *dev)
+{
+       int ret = 0;
+       int props = 1;  /* POWER_SUPPLY_PROP_PRESENT */
+       int i = 0;
+       struct wm97xx_pdata *wmdata = dev->dev.platform_data;
+       struct wm97xx_batt_pdata *pdata;
+
+       if (!wmdata) {
+               dev_err(&dev->dev, "No platform data supplied\n");
+               return -EINVAL;
+       }
+
+       pdata = wmdata->batt_pdata;
+
+       if (dev->id != -1)
+               return -EINVAL;
+
+       if (!pdata) {
+               dev_err(&dev->dev, "No platform_data supplied\n");
+               return -EINVAL;
+       }
+
+       if (gpio_is_valid(pdata->charge_gpio)) {
+               ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
+               if (ret)
+                       goto err;
+               ret = gpio_direction_input(pdata->charge_gpio);
+               if (ret)
+                       goto err2;
+               ret = request_irq(gpio_to_irq(pdata->charge_gpio),
+                               wm97xx_chrg_irq, 0,
+                               "AC Detect", dev);
+               if (ret)
+                       goto err2;
+               props++;        /* POWER_SUPPLY_PROP_STATUS */
+       }
+
+       if (pdata->batt_tech >= 0)
+               props++;        /* POWER_SUPPLY_PROP_TECHNOLOGY */
+       if (pdata->temp_aux >= 0)
+               props++;        /* POWER_SUPPLY_PROP_TEMP */
+       if (pdata->batt_aux >= 0)
+               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
+       if (pdata->max_voltage >= 0)
+               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
+       if (pdata->min_voltage >= 0)
+               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
+
+       prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
+       if (!prop) {
+               ret = -ENOMEM;
+               goto err3;
+       }
+
+       prop[i++] = POWER_SUPPLY_PROP_PRESENT;
+       if (pdata->charge_gpio >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_STATUS;
+       if (pdata->batt_tech >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
+       if (pdata->temp_aux >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_TEMP;
+       if (pdata->batt_aux >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+       if (pdata->max_voltage >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
+       if (pdata->min_voltage >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
+
+       INIT_WORK(&bat_work, wm97xx_bat_work);
+
+       if (!pdata->batt_name) {
+               dev_info(&dev->dev, "Please consider setting proper battery "
+                               "name in platform definition file, falling "
+                               "back to name \"wm97xx-batt\"\n");
+               bat_psy_desc.name = "wm97xx-batt";
+       } else
+               bat_psy_desc.name = pdata->batt_name;
+
+       bat_psy_desc.properties = prop;
+       bat_psy_desc.num_properties = props;
+
+       bat_psy = power_supply_register(&dev->dev, &bat_psy_desc, NULL);
+       if (!IS_ERR(bat_psy)) {
+               schedule_work(&bat_work);
+       } else {
+               ret = PTR_ERR(bat_psy);
+               goto err4;
+       }
+
+       return 0;
+err4:
+       kfree(prop);
+err3:
+       if (gpio_is_valid(pdata->charge_gpio))
+               free_irq(gpio_to_irq(pdata->charge_gpio), dev);
+err2:
+       if (gpio_is_valid(pdata->charge_gpio))
+               gpio_free(pdata->charge_gpio);
+err:
+       return ret;
+}
+
+static int wm97xx_bat_remove(struct platform_device *dev)
+{
+       struct wm97xx_pdata *wmdata = dev->dev.platform_data;
+       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
+
+       if (pdata && gpio_is_valid(pdata->charge_gpio)) {
+               free_irq(gpio_to_irq(pdata->charge_gpio), dev);
+               gpio_free(pdata->charge_gpio);
+       }
+       cancel_work_sync(&bat_work);
+       power_supply_unregister(bat_psy);
+       kfree(prop);
+       return 0;
+}
+
+static struct platform_driver wm97xx_bat_driver = {
+       .driver = {
+               .name   = "wm97xx-battery",
+#ifdef CONFIG_PM
+               .pm     = &wm97xx_bat_pm_ops,
+#endif
+       },
+       .probe          = wm97xx_bat_probe,
+       .remove         = wm97xx_bat_remove,
+};
+
+module_platform_driver(wm97xx_bat_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("WM97xx battery driver");
diff --git a/drivers/power/supply/z2_battery.c b/drivers/power/supply/z2_battery.c
new file mode 100644 (file)
index 0000000..b201e3f
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Battery measurement code for Zipit Z2
+ *
+ * Copyright (C) 2009 Peter Edwards <sweetlilmre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/z2_battery.h>
+
+#define        Z2_DEFAULT_NAME "Z2"
+
+struct z2_charger {
+       struct z2_battery_info          *info;
+       int                             bat_status;
+       struct i2c_client               *client;
+       struct power_supply             *batt_ps;
+       struct power_supply_desc        batt_ps_desc;
+       struct mutex                    work_lock;
+       struct work_struct              bat_work;
+};
+
+static unsigned long z2_read_bat(struct z2_charger *charger)
+{
+       int data;
+       data = i2c_smbus_read_byte_data(charger->client,
+                                       charger->info->batt_I2C_reg);
+       if (data < 0)
+               return 0;
+
+       return data * charger->info->batt_mult / charger->info->batt_div;
+}
+
+static int z2_batt_get_property(struct power_supply *batt_ps,
+                           enum power_supply_property psp,
+                           union power_supply_propval *val)
+{
+       struct z2_charger *charger = power_supply_get_drvdata(batt_ps);
+       struct z2_battery_info *info = charger->info;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = charger->bat_status;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = info->batt_tech;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (info->batt_I2C_reg >= 0)
+                       val->intval = z2_read_bat(charger);
+               else
+                       return -EINVAL;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               if (info->max_voltage >= 0)
+                       val->intval = info->max_voltage;
+               else
+                       return -EINVAL;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+               if (info->min_voltage >= 0)
+                       val->intval = info->min_voltage;
+               else
+                       return -EINVAL;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void z2_batt_ext_power_changed(struct power_supply *batt_ps)
+{
+       struct z2_charger *charger = power_supply_get_drvdata(batt_ps);
+
+       schedule_work(&charger->bat_work);
+}
+
+static void z2_batt_update(struct z2_charger *charger)
+{
+       int old_status = charger->bat_status;
+       struct z2_battery_info *info;
+
+       info = charger->info;
+
+       mutex_lock(&charger->work_lock);
+
+       charger->bat_status = (info->charge_gpio >= 0) ?
+               (gpio_get_value(info->charge_gpio) ?
+               POWER_SUPPLY_STATUS_CHARGING :
+               POWER_SUPPLY_STATUS_DISCHARGING) :
+               POWER_SUPPLY_STATUS_UNKNOWN;
+
+       if (old_status != charger->bat_status) {
+               pr_debug("%s: %i -> %i\n", charger->batt_ps->desc->name,
+                               old_status,
+                               charger->bat_status);
+               power_supply_changed(charger->batt_ps);
+       }
+
+       mutex_unlock(&charger->work_lock);
+}
+
+static void z2_batt_work(struct work_struct *work)
+{
+       struct z2_charger *charger;
+       charger = container_of(work, struct z2_charger, bat_work);
+       z2_batt_update(charger);
+}
+
+static irqreturn_t z2_charge_switch_irq(int irq, void *devid)
+{
+       struct z2_charger *charger = devid;
+       schedule_work(&charger->bat_work);
+       return IRQ_HANDLED;
+}
+
+static int z2_batt_ps_init(struct z2_charger *charger, int props)
+{
+       int i = 0;
+       enum power_supply_property *prop;
+       struct z2_battery_info *info = charger->info;
+
+       if (info->charge_gpio >= 0)
+               props++;        /* POWER_SUPPLY_PROP_STATUS */
+       if (info->batt_tech >= 0)
+               props++;        /* POWER_SUPPLY_PROP_TECHNOLOGY */
+       if (info->batt_I2C_reg >= 0)
+               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
+       if (info->max_voltage >= 0)
+               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
+       if (info->min_voltage >= 0)
+               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
+
+       prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
+       if (!prop)
+               return -ENOMEM;
+
+       prop[i++] = POWER_SUPPLY_PROP_PRESENT;
+       if (info->charge_gpio >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_STATUS;
+       if (info->batt_tech >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
+       if (info->batt_I2C_reg >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+       if (info->max_voltage >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
+       if (info->min_voltage >= 0)
+               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
+
+       if (!info->batt_name) {
+               dev_info(&charger->client->dev,
+                               "Please consider setting proper battery "
+                               "name in platform definition file, falling "
+                               "back to name \" Z2_DEFAULT_NAME \"\n");
+               charger->batt_ps_desc.name = Z2_DEFAULT_NAME;
+       } else
+               charger->batt_ps_desc.name = info->batt_name;
+
+       charger->batt_ps_desc.properties        = prop;
+       charger->batt_ps_desc.num_properties    = props;
+       charger->batt_ps_desc.type              = POWER_SUPPLY_TYPE_BATTERY;
+       charger->batt_ps_desc.get_property      = z2_batt_get_property;
+       charger->batt_ps_desc.external_power_changed =
+                                               z2_batt_ext_power_changed;
+       charger->batt_ps_desc.use_for_apm       = 1;
+
+       return 0;
+}
+
+static int z2_batt_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       int ret = 0;
+       int props = 1;  /* POWER_SUPPLY_PROP_PRESENT */
+       struct z2_charger *charger;
+       struct z2_battery_info *info = client->dev.platform_data;
+       struct power_supply_config psy_cfg = {};
+
+       if (info == NULL) {
+               dev_err(&client->dev,
+                       "Please set platform device platform_data"
+                       " to a valid z2_battery_info pointer!\n");
+               return -EINVAL;
+       }
+
+       charger = kzalloc(sizeof(*charger), GFP_KERNEL);
+       if (charger == NULL)
+               return -ENOMEM;
+
+       charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
+       charger->info = info;
+       charger->client = client;
+       i2c_set_clientdata(client, charger);
+       psy_cfg.drv_data = charger;
+
+       mutex_init(&charger->work_lock);
+
+       if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
+               ret = gpio_request(info->charge_gpio, "BATT CHRG");
+               if (ret)
+                       goto err;
+
+               ret = gpio_direction_input(info->charge_gpio);
+               if (ret)
+                       goto err2;
+
+               irq_set_irq_type(gpio_to_irq(info->charge_gpio),
+                                IRQ_TYPE_EDGE_BOTH);
+               ret = request_irq(gpio_to_irq(info->charge_gpio),
+                               z2_charge_switch_irq, 0,
+                               "AC Detect", charger);
+               if (ret)
+                       goto err3;
+       }
+
+       ret = z2_batt_ps_init(charger, props);
+       if (ret)
+               goto err3;
+
+       INIT_WORK(&charger->bat_work, z2_batt_work);
+
+       charger->batt_ps = power_supply_register(&client->dev,
+                                                &charger->batt_ps_desc,
+                                                &psy_cfg);
+       if (IS_ERR(charger->batt_ps)) {
+               ret = PTR_ERR(charger->batt_ps);
+               goto err4;
+       }
+
+       schedule_work(&charger->bat_work);
+
+       return 0;
+
+err4:
+       kfree(charger->batt_ps_desc.properties);
+err3:
+       if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
+               free_irq(gpio_to_irq(info->charge_gpio), charger);
+err2:
+       if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
+               gpio_free(info->charge_gpio);
+err:
+       kfree(charger);
+       return ret;
+}
+
+static int z2_batt_remove(struct i2c_client *client)
+{
+       struct z2_charger *charger = i2c_get_clientdata(client);
+       struct z2_battery_info *info = charger->info;
+
+       cancel_work_sync(&charger->bat_work);
+       power_supply_unregister(charger->batt_ps);
+
+       kfree(charger->batt_ps_desc.properties);
+       if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
+               free_irq(gpio_to_irq(info->charge_gpio), charger);
+               gpio_free(info->charge_gpio);
+       }
+
+       kfree(charger);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int z2_batt_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct z2_charger *charger = i2c_get_clientdata(client);
+
+       flush_work(&charger->bat_work);
+       return 0;
+}
+
+static int z2_batt_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct z2_charger *charger = i2c_get_clientdata(client);
+
+       schedule_work(&charger->bat_work);
+       return 0;
+}
+
+static const struct dev_pm_ops z2_battery_pm_ops = {
+       .suspend        = z2_batt_suspend,
+       .resume         = z2_batt_resume,
+};
+
+#define        Z2_BATTERY_PM_OPS       (&z2_battery_pm_ops)
+
+#else
+#define        Z2_BATTERY_PM_OPS       (NULL)
+#endif
+
+static const struct i2c_device_id z2_batt_id[] = {
+       { "aer915", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, z2_batt_id);
+
+static struct i2c_driver z2_batt_driver = {
+       .driver = {
+               .name   = "z2-battery",
+               .owner  = THIS_MODULE,
+               .pm     = Z2_BATTERY_PM_OPS
+       },
+       .probe          = z2_batt_probe,
+       .remove         = z2_batt_remove,
+       .id_table       = z2_batt_id,
+};
+module_i2c_driver(z2_batt_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
+MODULE_DESCRIPTION("Zipit Z2 battery driver");
diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c
deleted file mode 100644 (file)
index 57246cd..0000000
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * Power supply driver for testing.
- *
- * Copyright 2010  Anton Vorontsov <cbouatmailru@gmail.com>
- *
- * Dynamic module parameter code from the Virtual Battery Driver
- * Copyright (C) 2008 Pylone, Inc.
- * By: Masashi YOKOTA <yokota@pylone.jp>
- * Originally found here:
- * http://downloads.pylone.jp/src/virtual_battery/virtual_battery-0.0.1.tar.bz2
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/power_supply.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/vermagic.h>
-
-enum test_power_id {
-       TEST_AC,
-       TEST_BATTERY,
-       TEST_USB,
-       TEST_POWER_NUM,
-};
-
-static int ac_online                   = 1;
-static int usb_online                  = 1;
-static int battery_status              = POWER_SUPPLY_STATUS_DISCHARGING;
-static int battery_health              = POWER_SUPPLY_HEALTH_GOOD;
-static int battery_present             = 1; /* true */
-static int battery_technology          = POWER_SUPPLY_TECHNOLOGY_LION;
-static int battery_capacity            = 50;
-static int battery_voltage             = 3300;
-
-static bool module_initialized;
-
-static int test_power_get_ac_property(struct power_supply *psy,
-                                     enum power_supply_property psp,
-                                     union power_supply_propval *val)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = ac_online;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int test_power_get_usb_property(struct power_supply *psy,
-                                     enum power_supply_property psp,
-                                     union power_supply_propval *val)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = usb_online;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int test_power_get_battery_property(struct power_supply *psy,
-                                          enum power_supply_property psp,
-                                          union power_supply_propval *val)
-{
-       switch (psp) {
-       case POWER_SUPPLY_PROP_MODEL_NAME:
-               val->strval = "Test battery";
-               break;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = "Linux";
-               break;
-       case POWER_SUPPLY_PROP_SERIAL_NUMBER:
-               val->strval = UTS_RELEASE;
-               break;
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = battery_status;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               val->intval = battery_health;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = battery_present;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = battery_technology;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-               val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               val->intval = battery_capacity;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               val->intval = 100;
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
-       case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
-               val->intval = 3600;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = 26;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = battery_voltage;
-               break;
-       default:
-               pr_info("%s: some properties deliberately report errors.\n",
-                       __func__);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static enum power_supply_property test_power_ac_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static enum power_supply_property test_power_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
-       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
-       POWER_SUPPLY_PROP_MODEL_NAME,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-       POWER_SUPPLY_PROP_SERIAL_NUMBER,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-};
-
-static char *test_power_ac_supplied_to[] = {
-       "test_battery",
-};
-
-static struct power_supply *test_power_supplies[TEST_POWER_NUM];
-
-static const struct power_supply_desc test_power_desc[] = {
-       [TEST_AC] = {
-               .name = "test_ac",
-               .type = POWER_SUPPLY_TYPE_MAINS,
-               .properties = test_power_ac_props,
-               .num_properties = ARRAY_SIZE(test_power_ac_props),
-               .get_property = test_power_get_ac_property,
-       },
-       [TEST_BATTERY] = {
-               .name = "test_battery",
-               .type = POWER_SUPPLY_TYPE_BATTERY,
-               .properties = test_power_battery_props,
-               .num_properties = ARRAY_SIZE(test_power_battery_props),
-               .get_property = test_power_get_battery_property,
-       },
-       [TEST_USB] = {
-               .name = "test_usb",
-               .type = POWER_SUPPLY_TYPE_USB,
-               .properties = test_power_ac_props,
-               .num_properties = ARRAY_SIZE(test_power_ac_props),
-               .get_property = test_power_get_usb_property,
-       },
-};
-
-static const struct power_supply_config test_power_configs[] = {
-       {
-               /* test_ac */
-               .supplied_to = test_power_ac_supplied_to,
-               .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to),
-       }, {
-               /* test_battery */
-       }, {
-               /* test_usb */
-               .supplied_to = test_power_ac_supplied_to,
-               .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to),
-       },
-};
-
-static int __init test_power_init(void)
-{
-       int i;
-       int ret;
-
-       BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_supplies));
-       BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_configs));
-
-       for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) {
-               test_power_supplies[i] = power_supply_register(NULL,
-                                               &test_power_desc[i],
-                                               &test_power_configs[i]);
-               if (IS_ERR(test_power_supplies[i])) {
-                       pr_err("%s: failed to register %s\n", __func__,
-                               test_power_desc[i].name);
-                       ret = PTR_ERR(test_power_supplies[i]);
-                       goto failed;
-               }
-       }
-
-       module_initialized = true;
-       return 0;
-failed:
-       while (--i >= 0)
-               power_supply_unregister(test_power_supplies[i]);
-       return ret;
-}
-module_init(test_power_init);
-
-static void __exit test_power_exit(void)
-{
-       int i;
-
-       /* Let's see how we handle changes... */
-       ac_online = 0;
-       usb_online = 0;
-       battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
-       for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
-               power_supply_changed(test_power_supplies[i]);
-       pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n",
-               __func__);
-       ssleep(10);
-
-       for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
-               power_supply_unregister(test_power_supplies[i]);
-
-       module_initialized = false;
-}
-module_exit(test_power_exit);
-
-
-
-#define MAX_KEYLENGTH 256
-struct battery_property_map {
-       int value;
-       char const *key;
-};
-
-static struct battery_property_map map_ac_online[] = {
-       { 0,  "off"  },
-       { 1,  "on" },
-       { -1, NULL  },
-};
-
-static struct battery_property_map map_status[] = {
-       { POWER_SUPPLY_STATUS_CHARGING,     "charging"     },
-       { POWER_SUPPLY_STATUS_DISCHARGING,  "discharging"  },
-       { POWER_SUPPLY_STATUS_NOT_CHARGING, "not-charging" },
-       { POWER_SUPPLY_STATUS_FULL,         "full"         },
-       { -1,                               NULL           },
-};
-
-static struct battery_property_map map_health[] = {
-       { POWER_SUPPLY_HEALTH_GOOD,           "good"        },
-       { POWER_SUPPLY_HEALTH_OVERHEAT,       "overheat"    },
-       { POWER_SUPPLY_HEALTH_DEAD,           "dead"        },
-       { POWER_SUPPLY_HEALTH_OVERVOLTAGE,    "overvoltage" },
-       { POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, "failure"     },
-       { -1,                                 NULL          },
-};
-
-static struct battery_property_map map_present[] = {
-       { 0,  "false" },
-       { 1,  "true"  },
-       { -1, NULL    },
-};
-
-static struct battery_property_map map_technology[] = {
-       { POWER_SUPPLY_TECHNOLOGY_NiMH, "NiMH" },
-       { POWER_SUPPLY_TECHNOLOGY_LION, "LION" },
-       { POWER_SUPPLY_TECHNOLOGY_LIPO, "LIPO" },
-       { POWER_SUPPLY_TECHNOLOGY_LiFe, "LiFe" },
-       { POWER_SUPPLY_TECHNOLOGY_NiCd, "NiCd" },
-       { POWER_SUPPLY_TECHNOLOGY_LiMn, "LiMn" },
-       { -1,                           NULL   },
-};
-
-
-static int map_get_value(struct battery_property_map *map, const char *key,
-                               int def_val)
-{
-       char buf[MAX_KEYLENGTH];
-       int cr;
-
-       strncpy(buf, key, MAX_KEYLENGTH);
-       buf[MAX_KEYLENGTH-1] = '\0';
-
-       cr = strnlen(buf, MAX_KEYLENGTH) - 1;
-       if (cr < 0)
-               return def_val;
-       if (buf[cr] == '\n')
-               buf[cr] = '\0';
-
-       while (map->key) {
-               if (strncasecmp(map->key, buf, MAX_KEYLENGTH) == 0)
-                       return map->value;
-               map++;
-       }
-
-       return def_val;
-}
-
-
-static const char *map_get_key(struct battery_property_map *map, int value,
-                               const char *def_key)
-{
-       while (map->key) {
-               if (map->value == value)
-                       return map->key;
-               map++;
-       }
-
-       return def_key;
-}
-
-static inline void signal_power_supply_changed(struct power_supply *psy)
-{
-       if (module_initialized)
-               power_supply_changed(psy);
-}
-
-static int param_set_ac_online(const char *key, const struct kernel_param *kp)
-{
-       ac_online = map_get_value(map_ac_online, key, ac_online);
-       signal_power_supply_changed(test_power_supplies[TEST_AC]);
-       return 0;
-}
-
-static int param_get_ac_online(char *buffer, const struct kernel_param *kp)
-{
-       strcpy(buffer, map_get_key(map_ac_online, ac_online, "unknown"));
-       return strlen(buffer);
-}
-
-static int param_set_usb_online(const char *key, const struct kernel_param *kp)
-{
-       usb_online = map_get_value(map_ac_online, key, usb_online);
-       signal_power_supply_changed(test_power_supplies[TEST_USB]);
-       return 0;
-}
-
-static int param_get_usb_online(char *buffer, const struct kernel_param *kp)
-{
-       strcpy(buffer, map_get_key(map_ac_online, usb_online, "unknown"));
-       return strlen(buffer);
-}
-
-static int param_set_battery_status(const char *key,
-                                       const struct kernel_param *kp)
-{
-       battery_status = map_get_value(map_status, key, battery_status);
-       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
-       return 0;
-}
-
-static int param_get_battery_status(char *buffer, const struct kernel_param *kp)
-{
-       strcpy(buffer, map_get_key(map_status, battery_status, "unknown"));
-       return strlen(buffer);
-}
-
-static int param_set_battery_health(const char *key,
-                                       const struct kernel_param *kp)
-{
-       battery_health = map_get_value(map_health, key, battery_health);
-       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
-       return 0;
-}
-
-static int param_get_battery_health(char *buffer, const struct kernel_param *kp)
-{
-       strcpy(buffer, map_get_key(map_health, battery_health, "unknown"));
-       return strlen(buffer);
-}
-
-static int param_set_battery_present(const char *key,
-                                       const struct kernel_param *kp)
-{
-       battery_present = map_get_value(map_present, key, battery_present);
-       signal_power_supply_changed(test_power_supplies[TEST_AC]);
-       return 0;
-}
-
-static int param_get_battery_present(char *buffer,
-                                       const struct kernel_param *kp)
-{
-       strcpy(buffer, map_get_key(map_present, battery_present, "unknown"));
-       return strlen(buffer);
-}
-
-static int param_set_battery_technology(const char *key,
-                                       const struct kernel_param *kp)
-{
-       battery_technology = map_get_value(map_technology, key,
-                                               battery_technology);
-       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
-       return 0;
-}
-
-static int param_get_battery_technology(char *buffer,
-                                       const struct kernel_param *kp)
-{
-       strcpy(buffer,
-               map_get_key(map_technology, battery_technology, "unknown"));
-       return strlen(buffer);
-}
-
-static int param_set_battery_capacity(const char *key,
-                                       const struct kernel_param *kp)
-{
-       int tmp;
-
-       if (1 != sscanf(key, "%d", &tmp))
-               return -EINVAL;
-
-       battery_capacity = tmp;
-       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
-       return 0;
-}
-
-#define param_get_battery_capacity param_get_int
-
-static int param_set_battery_voltage(const char *key,
-                                       const struct kernel_param *kp)
-{
-       int tmp;
-
-       if (1 != sscanf(key, "%d", &tmp))
-               return -EINVAL;
-
-       battery_voltage = tmp;
-       signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
-       return 0;
-}
-
-#define param_get_battery_voltage param_get_int
-
-static const struct kernel_param_ops param_ops_ac_online = {
-       .set = param_set_ac_online,
-       .get = param_get_ac_online,
-};
-
-static const struct kernel_param_ops param_ops_usb_online = {
-       .set = param_set_usb_online,
-       .get = param_get_usb_online,
-};
-
-static const struct kernel_param_ops param_ops_battery_status = {
-       .set = param_set_battery_status,
-       .get = param_get_battery_status,
-};
-
-static const struct kernel_param_ops param_ops_battery_present = {
-       .set = param_set_battery_present,
-       .get = param_get_battery_present,
-};
-
-static const struct kernel_param_ops param_ops_battery_technology = {
-       .set = param_set_battery_technology,
-       .get = param_get_battery_technology,
-};
-
-static const struct kernel_param_ops param_ops_battery_health = {
-       .set = param_set_battery_health,
-       .get = param_get_battery_health,
-};
-
-static const struct kernel_param_ops param_ops_battery_capacity = {
-       .set = param_set_battery_capacity,
-       .get = param_get_battery_capacity,
-};
-
-static const struct kernel_param_ops param_ops_battery_voltage = {
-       .set = param_set_battery_voltage,
-       .get = param_get_battery_voltage,
-};
-
-#define param_check_ac_online(name, p) __param_check(name, p, void);
-#define param_check_usb_online(name, p) __param_check(name, p, void);
-#define param_check_battery_status(name, p) __param_check(name, p, void);
-#define param_check_battery_present(name, p) __param_check(name, p, void);
-#define param_check_battery_technology(name, p) __param_check(name, p, void);
-#define param_check_battery_health(name, p) __param_check(name, p, void);
-#define param_check_battery_capacity(name, p) __param_check(name, p, void);
-#define param_check_battery_voltage(name, p) __param_check(name, p, void);
-
-
-module_param(ac_online, ac_online, 0644);
-MODULE_PARM_DESC(ac_online, "AC charging state <on|off>");
-
-module_param(usb_online, usb_online, 0644);
-MODULE_PARM_DESC(usb_online, "USB charging state <on|off>");
-
-module_param(battery_status, battery_status, 0644);
-MODULE_PARM_DESC(battery_status,
-       "battery status <charging|discharging|not-charging|full>");
-
-module_param(battery_present, battery_present, 0644);
-MODULE_PARM_DESC(battery_present,
-       "battery presence state <good|overheat|dead|overvoltage|failure>");
-
-module_param(battery_technology, battery_technology, 0644);
-MODULE_PARM_DESC(battery_technology,
-       "battery technology <NiMH|LION|LIPO|LiFe|NiCd|LiMn>");
-
-module_param(battery_health, battery_health, 0644);
-MODULE_PARM_DESC(battery_health,
-       "battery health state <good|overheat|dead|overvoltage|failure>");
-
-module_param(battery_capacity, battery_capacity, 0644);
-MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)");
-
-module_param(battery_voltage, battery_voltage, 0644);
-MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)");
-
-MODULE_DESCRIPTION("Power supply driver for testing");
-MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c
deleted file mode 100644 (file)
index 6e88c1b..0000000
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Battery and Power Management code for the Sharp SL-6000x
- *
- * Copyright (c) 2005 Dirk Opfer
- * Copyright (c) 2008 Dmitry Baryshkov
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/power_supply.h>
-#include <linux/wm97xx.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-
-#include <asm/mach-types.h>
-#include <mach/tosa.h>
-
-static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
-static struct work_struct bat_work;
-
-struct tosa_bat {
-       int status;
-       struct power_supply *psy;
-       int full_chrg;
-
-       struct mutex work_lock; /* protects data */
-
-       bool (*is_present)(struct tosa_bat *bat);
-       int gpio_full;
-       int gpio_charge_off;
-
-       int technology;
-
-       int gpio_bat;
-       int adc_bat;
-       int adc_bat_divider;
-       int bat_max;
-       int bat_min;
-
-       int gpio_temp;
-       int adc_temp;
-       int adc_temp_divider;
-};
-
-static struct tosa_bat tosa_bat_main;
-static struct tosa_bat tosa_bat_jacket;
-
-static unsigned long tosa_read_bat(struct tosa_bat *bat)
-{
-       unsigned long value = 0;
-
-       if (bat->gpio_bat < 0 || bat->adc_bat < 0)
-               return 0;
-
-       mutex_lock(&bat_lock);
-       gpio_set_value(bat->gpio_bat, 1);
-       msleep(5);
-       value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent),
-                       bat->adc_bat);
-       gpio_set_value(bat->gpio_bat, 0);
-       mutex_unlock(&bat_lock);
-
-       value = value * 1000000 / bat->adc_bat_divider;
-
-       return value;
-}
-
-static unsigned long tosa_read_temp(struct tosa_bat *bat)
-{
-       unsigned long value = 0;
-
-       if (bat->gpio_temp < 0 || bat->adc_temp < 0)
-               return 0;
-
-       mutex_lock(&bat_lock);
-       gpio_set_value(bat->gpio_temp, 1);
-       msleep(5);
-       value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy->dev.parent),
-                       bat->adc_temp);
-       gpio_set_value(bat->gpio_temp, 0);
-       mutex_unlock(&bat_lock);
-
-       value = value * 10000 / bat->adc_temp_divider;
-
-       return value;
-}
-
-static int tosa_bat_get_property(struct power_supply *psy,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       int ret = 0;
-       struct tosa_bat *bat = power_supply_get_drvdata(psy);
-
-       if (bat->is_present && !bat->is_present(bat)
-                       && psp != POWER_SUPPLY_PROP_PRESENT) {
-               return -ENODEV;
-       }
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = bat->status;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = bat->technology;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = tosa_read_bat(bat);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               if (bat->full_chrg == -1)
-                       val->intval = bat->bat_max;
-               else
-                       val->intval = bat->full_chrg;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               val->intval = bat->bat_max;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = bat->bat_min;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = tosa_read_temp(bat);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = bat->is_present ? bat->is_present(bat) : 1;
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static bool tosa_jacket_bat_is_present(struct tosa_bat *bat)
-{
-       return gpio_get_value(TOSA_GPIO_JACKET_DETECT) == 0;
-}
-
-static void tosa_bat_external_power_changed(struct power_supply *psy)
-{
-       schedule_work(&bat_work);
-}
-
-static irqreturn_t tosa_bat_gpio_isr(int irq, void *data)
-{
-       pr_info("tosa_bat_gpio irq\n");
-       schedule_work(&bat_work);
-       return IRQ_HANDLED;
-}
-
-static void tosa_bat_update(struct tosa_bat *bat)
-{
-       int old;
-       struct power_supply *psy = bat->psy;
-
-       mutex_lock(&bat->work_lock);
-
-       old = bat->status;
-
-       if (bat->is_present && !bat->is_present(bat)) {
-               printk(KERN_NOTICE "%s not present\n", psy->desc->name);
-               bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
-               bat->full_chrg = -1;
-       } else if (power_supply_am_i_supplied(psy)) {
-               if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
-                       gpio_set_value(bat->gpio_charge_off, 0);
-                       mdelay(15);
-               }
-
-               if (gpio_get_value(bat->gpio_full)) {
-                       if (old == POWER_SUPPLY_STATUS_CHARGING ||
-                                       bat->full_chrg == -1)
-                               bat->full_chrg = tosa_read_bat(bat);
-
-                       gpio_set_value(bat->gpio_charge_off, 1);
-                       bat->status = POWER_SUPPLY_STATUS_FULL;
-               } else {
-                       gpio_set_value(bat->gpio_charge_off, 0);
-                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
-               }
-       } else {
-               gpio_set_value(bat->gpio_charge_off, 1);
-               bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
-       }
-
-       if (old != bat->status)
-               power_supply_changed(psy);
-
-       mutex_unlock(&bat->work_lock);
-}
-
-static void tosa_bat_work(struct work_struct *work)
-{
-       tosa_bat_update(&tosa_bat_main);
-       tosa_bat_update(&tosa_bat_jacket);
-}
-
-
-static enum power_supply_property tosa_bat_main_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_PRESENT,
-};
-
-static enum power_supply_property tosa_bat_bu_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
-       POWER_SUPPLY_PROP_PRESENT,
-};
-
-static const struct power_supply_desc tosa_bat_main_desc = {
-       .name           = "main-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = tosa_bat_main_props,
-       .num_properties = ARRAY_SIZE(tosa_bat_main_props),
-       .get_property   = tosa_bat_get_property,
-       .external_power_changed = tosa_bat_external_power_changed,
-       .use_for_apm    = 1,
-};
-
-static const struct power_supply_desc tosa_bat_jacket_desc = {
-       .name           = "jacket-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = tosa_bat_main_props,
-       .num_properties = ARRAY_SIZE(tosa_bat_main_props),
-       .get_property   = tosa_bat_get_property,
-       .external_power_changed = tosa_bat_external_power_changed,
-};
-
-static const struct power_supply_desc tosa_bat_bu_desc = {
-       .name           = "backup-battery",
-       .type           = POWER_SUPPLY_TYPE_BATTERY,
-       .properties     = tosa_bat_bu_props,
-       .num_properties = ARRAY_SIZE(tosa_bat_bu_props),
-       .get_property   = tosa_bat_get_property,
-       .external_power_changed = tosa_bat_external_power_changed,
-};
-
-static struct tosa_bat tosa_bat_main = {
-       .status = POWER_SUPPLY_STATUS_DISCHARGING,
-       .full_chrg = -1,
-       .psy = NULL,
-
-       .gpio_full = TOSA_GPIO_BAT0_CRG,
-       .gpio_charge_off = TOSA_GPIO_CHARGE_OFF,
-
-       .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
-
-       .gpio_bat = TOSA_GPIO_BAT0_V_ON,
-       .adc_bat = WM97XX_AUX_ID3,
-       .adc_bat_divider = 414,
-       .bat_max = 4310000,
-       .bat_min = 1551 * 1000000 / 414,
-
-       .gpio_temp = TOSA_GPIO_BAT1_TH_ON,
-       .adc_temp = WM97XX_AUX_ID2,
-       .adc_temp_divider = 10000,
-};
-
-static struct tosa_bat tosa_bat_jacket = {
-       .status = POWER_SUPPLY_STATUS_DISCHARGING,
-       .full_chrg = -1,
-       .psy = NULL,
-
-       .is_present = tosa_jacket_bat_is_present,
-       .gpio_full = TOSA_GPIO_BAT1_CRG,
-       .gpio_charge_off = TOSA_GPIO_CHARGE_OFF_JC,
-
-       .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
-
-       .gpio_bat = TOSA_GPIO_BAT1_V_ON,
-       .adc_bat = WM97XX_AUX_ID3,
-       .adc_bat_divider = 414,
-       .bat_max = 4310000,
-       .bat_min = 1551 * 1000000 / 414,
-
-       .gpio_temp = TOSA_GPIO_BAT0_TH_ON,
-       .adc_temp = WM97XX_AUX_ID2,
-       .adc_temp_divider = 10000,
-};
-
-static struct tosa_bat tosa_bat_bu = {
-       .status = POWER_SUPPLY_STATUS_UNKNOWN,
-       .full_chrg = -1,
-       .psy = NULL,
-
-       .gpio_full = -1,
-       .gpio_charge_off = -1,
-
-       .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
-
-       .gpio_bat = TOSA_GPIO_BU_CHRG_ON,
-       .adc_bat = WM97XX_AUX_ID4,
-       .adc_bat_divider = 1266,
-
-       .gpio_temp = -1,
-       .adc_temp = -1,
-       .adc_temp_divider = -1,
-};
-
-static struct gpio tosa_bat_gpios[] = {
-       { TOSA_GPIO_CHARGE_OFF,    GPIOF_OUT_INIT_HIGH, "main charge off" },
-       { TOSA_GPIO_CHARGE_OFF_JC, GPIOF_OUT_INIT_HIGH, "jacket charge off" },
-       { TOSA_GPIO_BAT_SW_ON,     GPIOF_OUT_INIT_LOW,  "battery switch" },
-       { TOSA_GPIO_BAT0_V_ON,     GPIOF_OUT_INIT_LOW,  "main battery" },
-       { TOSA_GPIO_BAT1_V_ON,     GPIOF_OUT_INIT_LOW,  "jacket battery" },
-       { TOSA_GPIO_BAT1_TH_ON,    GPIOF_OUT_INIT_LOW,  "main battery temp" },
-       { TOSA_GPIO_BAT0_TH_ON,    GPIOF_OUT_INIT_LOW,  "jacket battery temp" },
-       { TOSA_GPIO_BU_CHRG_ON,    GPIOF_OUT_INIT_LOW,  "backup battery" },
-       { TOSA_GPIO_BAT0_CRG,      GPIOF_IN,            "main battery full" },
-       { TOSA_GPIO_BAT1_CRG,      GPIOF_IN,            "jacket battery full" },
-       { TOSA_GPIO_BAT0_LOW,      GPIOF_IN,            "main battery low" },
-       { TOSA_GPIO_BAT1_LOW,      GPIOF_IN,            "jacket battery low" },
-       { TOSA_GPIO_JACKET_DETECT, GPIOF_IN,            "jacket detect" },
-};
-
-#ifdef CONFIG_PM
-static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state)
-{
-       /* flush all pending status updates */
-       flush_work(&bat_work);
-       return 0;
-}
-
-static int tosa_bat_resume(struct platform_device *dev)
-{
-       /* things may have changed while we were away */
-       schedule_work(&bat_work);
-       return 0;
-}
-#else
-#define tosa_bat_suspend NULL
-#define tosa_bat_resume NULL
-#endif
-
-static int tosa_bat_probe(struct platform_device *dev)
-{
-       int ret;
-       struct power_supply_config main_psy_cfg = {},
-                                  jacket_psy_cfg = {},
-                                  bu_psy_cfg = {};
-
-       if (!machine_is_tosa())
-               return -ENODEV;
-
-       ret = gpio_request_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
-       if (ret)
-               return ret;
-
-       mutex_init(&tosa_bat_main.work_lock);
-       mutex_init(&tosa_bat_jacket.work_lock);
-
-       INIT_WORK(&bat_work, tosa_bat_work);
-
-       main_psy_cfg.drv_data = &tosa_bat_main;
-       tosa_bat_main.psy = power_supply_register(&dev->dev,
-                                                 &tosa_bat_main_desc,
-                                                 &main_psy_cfg);
-       if (IS_ERR(tosa_bat_main.psy)) {
-               ret = PTR_ERR(tosa_bat_main.psy);
-               goto err_psy_reg_main;
-       }
-
-       jacket_psy_cfg.drv_data = &tosa_bat_jacket;
-       tosa_bat_jacket.psy = power_supply_register(&dev->dev,
-                                                   &tosa_bat_jacket_desc,
-                                                   &jacket_psy_cfg);
-       if (IS_ERR(tosa_bat_jacket.psy)) {
-               ret = PTR_ERR(tosa_bat_jacket.psy);
-               goto err_psy_reg_jacket;
-       }
-
-       bu_psy_cfg.drv_data = &tosa_bat_bu;
-       tosa_bat_bu.psy = power_supply_register(&dev->dev, &tosa_bat_bu_desc,
-                                               &bu_psy_cfg);
-       if (IS_ERR(tosa_bat_bu.psy)) {
-               ret = PTR_ERR(tosa_bat_bu.psy);
-               goto err_psy_reg_bu;
-       }
-
-       ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG),
-                               tosa_bat_gpio_isr,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                               "main full", &tosa_bat_main);
-       if (ret)
-               goto err_req_main;
-
-       ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG),
-                               tosa_bat_gpio_isr,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                               "jacket full", &tosa_bat_jacket);
-       if (ret)
-               goto err_req_jacket;
-
-       ret = request_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT),
-                               tosa_bat_gpio_isr,
-                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                               "jacket detect", &tosa_bat_jacket);
-       if (!ret) {
-               schedule_work(&bat_work);
-               return 0;
-       }
-
-       free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
-err_req_jacket:
-       free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
-err_req_main:
-       power_supply_unregister(tosa_bat_bu.psy);
-err_psy_reg_bu:
-       power_supply_unregister(tosa_bat_jacket.psy);
-err_psy_reg_jacket:
-       power_supply_unregister(tosa_bat_main.psy);
-err_psy_reg_main:
-
-       /* see comment in tosa_bat_remove */
-       cancel_work_sync(&bat_work);
-
-       gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
-       return ret;
-}
-
-static int tosa_bat_remove(struct platform_device *dev)
-{
-       free_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), &tosa_bat_jacket);
-       free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
-       free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
-
-       power_supply_unregister(tosa_bat_bu.psy);
-       power_supply_unregister(tosa_bat_jacket.psy);
-       power_supply_unregister(tosa_bat_main.psy);
-
-       /*
-        * Now cancel the bat_work.  We won't get any more schedules,
-        * since all sources (isr and external_power_changed) are
-        * unregistered now.
-        */
-       cancel_work_sync(&bat_work);
-       gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
-       return 0;
-}
-
-static struct platform_driver tosa_bat_driver = {
-       .driver.name    = "wm97xx-battery",
-       .driver.owner   = THIS_MODULE,
-       .probe          = tosa_bat_probe,
-       .remove         = tosa_bat_remove,
-       .suspend        = tosa_bat_suspend,
-       .resume         = tosa_bat_resume,
-};
-
-module_platform_driver(tosa_bat_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Dmitry Baryshkov");
-MODULE_DESCRIPTION("Tosa battery driver");
-MODULE_ALIAS("platform:wm97xx-battery");
diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c
deleted file mode 100644 (file)
index 1b4b5e0..0000000
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Battery charger driver for TI's tps65090
- *
- * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
-
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
-
- * This program is distributed in the hope 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.
-
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/freezer.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-
-#include <linux/mfd/tps65090.h>
-
-#define TPS65090_CHARGER_ENABLE        BIT(0)
-#define TPS65090_VACG          BIT(1)
-#define TPS65090_NOITERM       BIT(5)
-
-#define POLL_INTERVAL          (HZ * 2)        /* Used when no irq */
-
-struct tps65090_charger {
-       struct  device  *dev;
-       int     ac_online;
-       int     prev_ac_online;
-       int     irq;
-       struct task_struct      *poll_task;
-       bool                    passive_mode;
-       struct power_supply     *ac;
-       struct tps65090_platform_data *pdata;
-};
-
-static enum power_supply_property tps65090_ac_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static int tps65090_low_chrg_current(struct tps65090_charger *charger)
-{
-       int ret;
-
-       if (charger->passive_mode)
-               return 0;
-
-       ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5,
-                       TPS65090_NOITERM);
-       if (ret < 0) {
-               dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
-                       __func__, TPS65090_REG_CG_CTRL5);
-               return ret;
-       }
-       return 0;
-}
-
-static int tps65090_enable_charging(struct tps65090_charger *charger)
-{
-       int ret;
-       uint8_t ctrl0 = 0;
-
-       if (charger->passive_mode)
-               return 0;
-
-       ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0,
-                           &ctrl0);
-       if (ret < 0) {
-               dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
-                               __func__, TPS65090_REG_CG_CTRL0);
-               return ret;
-       }
-
-       ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL0,
-                               (ctrl0 | TPS65090_CHARGER_ENABLE));
-       if (ret < 0) {
-               dev_err(charger->dev, "%s(): error writing in register 0x%x\n",
-                               __func__, TPS65090_REG_CG_CTRL0);
-               return ret;
-       }
-       return 0;
-}
-
-static int tps65090_config_charger(struct tps65090_charger *charger)
-{
-       uint8_t intrmask = 0;
-       int ret;
-
-       if (charger->passive_mode)
-               return 0;
-
-       if (charger->pdata->enable_low_current_chrg) {
-               ret = tps65090_low_chrg_current(charger);
-               if (ret < 0) {
-                       dev_err(charger->dev,
-                               "error configuring low charge current\n");
-                       return ret;
-               }
-       }
-
-       /* Enable the VACG interrupt for AC power detect */
-       ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_MASK,
-                           &intrmask);
-       if (ret < 0) {
-               dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
-                       __func__, TPS65090_REG_INTR_MASK);
-               return ret;
-       }
-
-       ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_MASK,
-                            (intrmask | TPS65090_VACG));
-       if (ret < 0) {
-               dev_err(charger->dev, "%s(): error writing in register 0x%x\n",
-                       __func__, TPS65090_REG_CG_CTRL0);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int tps65090_ac_get_property(struct power_supply *psy,
-                       enum power_supply_property psp,
-                       union power_supply_propval *val)
-{
-       struct tps65090_charger *charger = power_supply_get_drvdata(psy);
-
-       if (psp == POWER_SUPPLY_PROP_ONLINE) {
-               val->intval = charger->ac_online;
-               charger->prev_ac_online = charger->ac_online;
-               return 0;
-       }
-       return -EINVAL;
-}
-
-static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
-{
-       struct tps65090_charger *charger = dev_id;
-       int ret;
-       uint8_t status1 = 0;
-       uint8_t intrsts = 0;
-
-       ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_STATUS1,
-                           &status1);
-       if (ret < 0) {
-               dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
-                               __func__, TPS65090_REG_CG_STATUS1);
-               return IRQ_HANDLED;
-       }
-       msleep(75);
-       ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_STS,
-                           &intrsts);
-       if (ret < 0) {
-               dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
-                               __func__, TPS65090_REG_INTR_STS);
-               return IRQ_HANDLED;
-       }
-
-       if (intrsts & TPS65090_VACG) {
-               ret = tps65090_enable_charging(charger);
-               if (ret < 0)
-                       return IRQ_HANDLED;
-               charger->ac_online = 1;
-       } else {
-               charger->ac_online = 0;
-       }
-
-       /* Clear interrupts. */
-       if (!charger->passive_mode) {
-               ret = tps65090_write(charger->dev->parent,
-                                    TPS65090_REG_INTR_STS, 0x00);
-               if (ret < 0) {
-                       dev_err(charger->dev,
-                               "%s(): Error in writing reg 0x%x\n",
-                               __func__, TPS65090_REG_INTR_STS);
-               }
-       }
-
-       if (charger->prev_ac_online != charger->ac_online)
-               power_supply_changed(charger->ac);
-
-       return IRQ_HANDLED;
-}
-
-static struct tps65090_platform_data *
-               tps65090_parse_dt_charger_data(struct platform_device *pdev)
-{
-       struct tps65090_platform_data *pdata;
-       struct device_node *np = pdev->dev.of_node;
-       unsigned int prop;
-
-       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata) {
-               dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n");
-               return NULL;
-       }
-
-       prop = of_property_read_bool(np, "ti,enable-low-current-chrg");
-       pdata->enable_low_current_chrg = prop;
-
-       pdata->irq_base = -1;
-
-       return pdata;
-
-}
-
-static int tps65090_charger_poll_task(void *data)
-{
-       set_freezable();
-
-       while (!kthread_should_stop()) {
-               schedule_timeout_interruptible(POLL_INTERVAL);
-               try_to_freeze();
-               tps65090_charger_isr(-1, data);
-       }
-       return 0;
-}
-
-static const struct power_supply_desc tps65090_charger_desc = {
-       .name                   = "tps65090-ac",
-       .type                   = POWER_SUPPLY_TYPE_MAINS,
-       .get_property           = tps65090_ac_get_property,
-       .properties             = tps65090_ac_props,
-       .num_properties         = ARRAY_SIZE(tps65090_ac_props),
-};
-
-static int tps65090_charger_probe(struct platform_device *pdev)
-{
-       struct tps65090_charger *cdata;
-       struct tps65090_platform_data *pdata;
-       struct power_supply_config psy_cfg = {};
-       uint8_t status1 = 0;
-       int ret;
-       int irq;
-
-       pdata = dev_get_platdata(pdev->dev.parent);
-
-       if (IS_ENABLED(CONFIG_OF) && !pdata && pdev->dev.of_node)
-               pdata = tps65090_parse_dt_charger_data(pdev);
-
-       if (!pdata) {
-               dev_err(&pdev->dev, "%s():no platform data available\n",
-                               __func__);
-               return -ENODEV;
-       }
-
-       cdata = devm_kzalloc(&pdev->dev, sizeof(*cdata), GFP_KERNEL);
-       if (!cdata) {
-               dev_err(&pdev->dev, "failed to allocate memory status\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(pdev, cdata);
-
-       cdata->dev                      = &pdev->dev;
-       cdata->pdata                    = pdata;
-
-       psy_cfg.supplied_to             = pdata->supplied_to;
-       psy_cfg.num_supplicants         = pdata->num_supplicants;
-       psy_cfg.of_node                 = pdev->dev.of_node;
-       psy_cfg.drv_data                = cdata;
-
-       cdata->ac = power_supply_register(&pdev->dev, &tps65090_charger_desc,
-                       &psy_cfg);
-       if (IS_ERR(cdata->ac)) {
-               dev_err(&pdev->dev, "failed: power supply register\n");
-               return PTR_ERR(cdata->ac);
-       }
-
-       irq = platform_get_irq(pdev, 0);
-       if (irq < 0)
-               irq = -ENXIO;
-       cdata->irq = irq;
-
-       ret = tps65090_config_charger(cdata);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
-               goto fail_unregister_supply;
-       }
-
-       /* Check for charger presence */
-       ret = tps65090_read(cdata->dev->parent, TPS65090_REG_CG_STATUS1,
-                       &status1);
-       if (ret < 0) {
-               dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__,
-                       TPS65090_REG_CG_STATUS1);
-               goto fail_unregister_supply;
-       }
-
-       if (status1 != 0) {
-               ret = tps65090_enable_charging(cdata);
-               if (ret < 0) {
-                       dev_err(cdata->dev, "error enabling charger\n");
-                       goto fail_unregister_supply;
-               }
-               cdata->ac_online = 1;
-               power_supply_changed(cdata->ac);
-       }
-
-       if (irq != -ENXIO) {
-               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-                       tps65090_charger_isr, 0, "tps65090-charger", cdata);
-               if (ret) {
-                       dev_err(cdata->dev,
-                               "Unable to register irq %d err %d\n", irq,
-                               ret);
-                       goto fail_unregister_supply;
-               }
-       } else {
-               cdata->poll_task = kthread_run(tps65090_charger_poll_task,
-                                             cdata, "ktps65090charger");
-               cdata->passive_mode = true;
-               if (IS_ERR(cdata->poll_task)) {
-                       ret = PTR_ERR(cdata->poll_task);
-                       dev_err(cdata->dev,
-                               "Unable to run kthread err %d\n", ret);
-                       goto fail_unregister_supply;
-               }
-       }
-
-       return 0;
-
-fail_unregister_supply:
-       power_supply_unregister(cdata->ac);
-
-       return ret;
-}
-
-static int tps65090_charger_remove(struct platform_device *pdev)
-{
-       struct tps65090_charger *cdata = platform_get_drvdata(pdev);
-
-       if (cdata->irq == -ENXIO)
-               kthread_stop(cdata->poll_task);
-       power_supply_unregister(cdata->ac);
-
-       return 0;
-}
-
-static const struct of_device_id of_tps65090_charger_match[] = {
-       { .compatible = "ti,tps65090-charger", },
-       { /* end */ }
-};
-MODULE_DEVICE_TABLE(of, of_tps65090_charger_match);
-
-static struct platform_driver tps65090_charger_driver = {
-       .driver = {
-               .name   = "tps65090-charger",
-               .of_match_table = of_tps65090_charger_match,
-       },
-       .probe  = tps65090_charger_probe,
-       .remove = tps65090_charger_remove,
-};
-module_platform_driver(tps65090_charger_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>");
-MODULE_DESCRIPTION("tps65090 battery charger driver");
diff --git a/drivers/power/tps65217_charger.c b/drivers/power/tps65217_charger.c
deleted file mode 100644 (file)
index 73dfae4..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Battery charger driver for TI's tps65217
- *
- * Copyright (c) 2015, Collabora Ltd.
-
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
-
- * This program is distributed in the hope 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.
-
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Battery charger driver for TI's tps65217
- */
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/power_supply.h>
-
-#include <linux/mfd/core.h>
-#include <linux/mfd/tps65217.h>
-
-#define POLL_INTERVAL          (HZ * 2)
-
-struct tps65217_charger {
-       struct tps65217 *tps;
-       struct device *dev;
-       struct power_supply *ac;
-
-       int     ac_online;
-       int     prev_ac_online;
-
-       struct task_struct      *poll_task;
-};
-
-static enum power_supply_property tps65217_ac_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static int tps65217_config_charger(struct tps65217_charger *charger)
-{
-       int ret;
-
-       dev_dbg(charger->dev, "%s\n", __func__);
-
-       /*
-        * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)
-        *
-        * The device can be configured to support a 100k NTC (B = 3960) by
-        * setting the the NTC_TYPE bit in register CHGCONFIG1 to 1. However it
-        * is not recommended to do so. In sleep mode, the charger continues
-        * charging the battery, but all register values are reset to default
-        * values. Therefore, the charger would get the wrong temperature
-        * information. If 100k NTC setting is required, please contact the
-        * factory.
-        *
-        * ATTENTION, conflicting information, from p. 46
-        *
-        * NTC TYPE (for battery temperature measurement)
-        *   0 â€“ 100k (curve 1, B = 3960)
-        *   1 â€“ 10k  (curve 2, B = 3480) (default on reset)
-        *
-        */
-       ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
-                                 TPS65217_CHGCONFIG1_NTC_TYPE,
-                                 TPS65217_PROTECT_NONE);
-       if (ret) {
-               dev_err(charger->dev,
-                       "failed to set 100k NTC setting: %d\n", ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int tps65217_enable_charging(struct tps65217_charger *charger)
-{
-       int ret;
-
-       /* charger already enabled */
-       if (charger->ac_online)
-               return 0;
-
-       dev_dbg(charger->dev, "%s: enable charging\n", __func__);
-       ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
-                               TPS65217_CHGCONFIG1_CHG_EN,
-                               TPS65217_CHGCONFIG1_CHG_EN,
-                               TPS65217_PROTECT_NONE);
-       if (ret) {
-               dev_err(charger->dev,
-                       "%s: Error in writing CHG_EN in reg 0x%x: %d\n",
-                       __func__, TPS65217_REG_CHGCONFIG1, ret);
-               return ret;
-       }
-
-       charger->ac_online = 1;
-
-       return 0;
-}
-
-static int tps65217_ac_get_property(struct power_supply *psy,
-                       enum power_supply_property psp,
-                       union power_supply_propval *val)
-{
-       struct tps65217_charger *charger = power_supply_get_drvdata(psy);
-
-       if (psp == POWER_SUPPLY_PROP_ONLINE) {
-               val->intval = charger->ac_online;
-               return 0;
-       }
-       return -EINVAL;
-}
-
-static irqreturn_t tps65217_charger_irq(int irq, void *dev)
-{
-       int ret, val;
-       struct tps65217_charger *charger = dev;
-
-       charger->prev_ac_online = charger->ac_online;
-
-       ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
-       if (ret < 0) {
-               dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
-                       __func__, TPS65217_REG_STATUS);
-               return IRQ_HANDLED;
-       }
-
-       dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
-
-       /* check for AC status bit */
-       if (val & TPS65217_STATUS_ACPWR) {
-               ret = tps65217_enable_charging(charger);
-               if (ret) {
-                       dev_err(charger->dev,
-                               "failed to enable charger: %d\n", ret);
-                       return IRQ_HANDLED;
-               }
-       } else {
-               charger->ac_online = 0;
-       }
-
-       if (charger->prev_ac_online != charger->ac_online)
-               power_supply_changed(charger->ac);
-
-       ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
-       if (ret < 0) {
-               dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
-                       __func__, TPS65217_REG_CHGCONFIG0);
-               return IRQ_HANDLED;
-       }
-
-       if (val & TPS65217_CHGCONFIG0_ACTIVE)
-               dev_dbg(charger->dev, "%s: charger is charging\n", __func__);
-       else
-               dev_dbg(charger->dev,
-                       "%s: charger is NOT charging\n", __func__);
-
-       return IRQ_HANDLED;
-}
-
-static int tps65217_charger_poll_task(void *data)
-{
-       set_freezable();
-
-       while (!kthread_should_stop()) {
-               schedule_timeout_interruptible(POLL_INTERVAL);
-               try_to_freeze();
-               tps65217_charger_irq(-1, data);
-       }
-       return 0;
-}
-
-static const struct power_supply_desc tps65217_charger_desc = {
-       .name                   = "tps65217-ac",
-       .type                   = POWER_SUPPLY_TYPE_MAINS,
-       .get_property           = tps65217_ac_get_property,
-       .properties             = tps65217_ac_props,
-       .num_properties         = ARRAY_SIZE(tps65217_ac_props),
-};
-
-static int tps65217_charger_probe(struct platform_device *pdev)
-{
-       struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
-       struct tps65217_charger *charger;
-       struct power_supply_config cfg = {};
-       int ret;
-
-       dev_dbg(&pdev->dev, "%s\n", __func__);
-
-       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
-       if (!charger)
-               return -ENOMEM;
-
-       charger->tps = tps;
-       charger->dev = &pdev->dev;
-
-       cfg.of_node = pdev->dev.of_node;
-       cfg.drv_data = charger;
-
-       charger->ac = devm_power_supply_register(&pdev->dev,
-                                                &tps65217_charger_desc,
-                                                &cfg);
-       if (IS_ERR(charger->ac)) {
-               dev_err(&pdev->dev, "failed: power supply register\n");
-               return PTR_ERR(charger->ac);
-       }
-
-       ret = tps65217_config_charger(charger);
-       if (ret < 0) {
-               dev_err(charger->dev, "charger config failed, err %d\n", ret);
-               return ret;
-       }
-
-       charger->poll_task = kthread_run(tps65217_charger_poll_task,
-                                     charger, "ktps65217charger");
-       if (IS_ERR(charger->poll_task)) {
-               ret = PTR_ERR(charger->poll_task);
-               dev_err(charger->dev, "Unable to run kthread err %d\n", ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int tps65217_charger_remove(struct platform_device *pdev)
-{
-       struct tps65217_charger *charger = platform_get_drvdata(pdev);
-
-       kthread_stop(charger->poll_task);
-
-       return 0;
-}
-
-static const struct of_device_id tps65217_charger_match_table[] = {
-       { .compatible = "ti,tps65217-charger", },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);
-
-static struct platform_driver tps65217_charger_driver = {
-       .probe  = tps65217_charger_probe,
-       .remove = tps65217_charger_remove,
-       .driver = {
-               .name   = "tps65217-charger",
-               .of_match_table = of_match_ptr(tps65217_charger_match_table),
-       },
-
-};
-module_platform_driver(tps65217_charger_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
-MODULE_DESCRIPTION("TPS65217 battery charger driver");
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
deleted file mode 100644 (file)
index bcd4dc3..0000000
+++ /dev/null
@@ -1,1162 +0,0 @@
-/*
- * TWL4030/TPS65950 BCI (Battery Charger Interface) driver
- *
- * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com>
- *
- * based on twl4030_bci_battery.c by TI
- * Copyright (C) 2008 Texas Instruments, Inc.
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/i2c/twl.h>
-#include <linux/power_supply.h>
-#include <linux/notifier.h>
-#include <linux/usb/otg.h>
-#include <linux/iio/consumer.h>
-
-#define TWL4030_BCIMDEN                0x00
-#define TWL4030_BCIMDKEY       0x01
-#define TWL4030_BCIMSTATEC     0x02
-#define TWL4030_BCIICHG                0x08
-#define TWL4030_BCIVAC         0x0a
-#define TWL4030_BCIVBUS                0x0c
-#define TWL4030_BCIMFSTS3      0x0F
-#define TWL4030_BCIMFSTS4      0x10
-#define TWL4030_BCICTL1                0x23
-#define TWL4030_BB_CFG         0x12
-#define TWL4030_BCIIREF1       0x27
-#define TWL4030_BCIIREF2       0x28
-#define TWL4030_BCIMFKEY       0x11
-#define TWL4030_BCIMFEN3       0x14
-#define TWL4030_BCIMFTH8       0x1d
-#define TWL4030_BCIMFTH9       0x1e
-#define TWL4030_BCIWDKEY       0x21
-
-#define TWL4030_BCIMFSTS1      0x01
-
-#define TWL4030_BCIAUTOWEN     BIT(5)
-#define TWL4030_CONFIG_DONE    BIT(4)
-#define TWL4030_CVENAC         BIT(2)
-#define TWL4030_BCIAUTOUSB     BIT(1)
-#define TWL4030_BCIAUTOAC      BIT(0)
-#define TWL4030_CGAIN          BIT(5)
-#define TWL4030_USBFASTMCHG    BIT(2)
-#define TWL4030_STS_VBUS       BIT(7)
-#define TWL4030_STS_USB_ID     BIT(2)
-#define TWL4030_BBCHEN         BIT(4)
-#define TWL4030_BBSEL_MASK     0x0c
-#define TWL4030_BBSEL_2V5      0x00
-#define TWL4030_BBSEL_3V0      0x04
-#define TWL4030_BBSEL_3V1      0x08
-#define TWL4030_BBSEL_3V2      0x0c
-#define TWL4030_BBISEL_MASK    0x03
-#define TWL4030_BBISEL_25uA    0x00
-#define TWL4030_BBISEL_150uA   0x01
-#define TWL4030_BBISEL_500uA   0x02
-#define TWL4030_BBISEL_1000uA  0x03
-
-#define TWL4030_BATSTSPCHG     BIT(2)
-#define TWL4030_BATSTSMCHG     BIT(6)
-
-/* BCI interrupts */
-#define TWL4030_WOVF           BIT(0) /* Watchdog overflow */
-#define TWL4030_TMOVF          BIT(1) /* Timer overflow */
-#define TWL4030_ICHGHIGH       BIT(2) /* Battery charge current high */
-#define TWL4030_ICHGLOW                BIT(3) /* Battery cc. low / FSM state change */
-#define TWL4030_ICHGEOC                BIT(4) /* Battery current end-of-charge */
-#define TWL4030_TBATOR2                BIT(5) /* Battery temperature out of range 2 */
-#define TWL4030_TBATOR1                BIT(6) /* Battery temperature out of range 1 */
-#define TWL4030_BATSTS         BIT(7) /* Battery status */
-
-#define TWL4030_VBATLVL                BIT(0) /* VBAT level */
-#define TWL4030_VBATOV         BIT(1) /* VBAT overvoltage */
-#define TWL4030_VBUSOV         BIT(2) /* VBUS overvoltage */
-#define TWL4030_ACCHGOV                BIT(3) /* Ac charger overvoltage */
-
-#define TWL4030_MSTATEC_USB            BIT(4)
-#define TWL4030_MSTATEC_AC             BIT(5)
-#define TWL4030_MSTATEC_MASK           0x0f
-#define TWL4030_MSTATEC_QUICK1         0x02
-#define TWL4030_MSTATEC_QUICK7         0x07
-#define TWL4030_MSTATEC_COMPLETE1      0x0b
-#define TWL4030_MSTATEC_COMPLETE4      0x0e
-
-/*
- * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
- * then AC is available.
- */
-static inline int ac_available(struct iio_channel *channel_vac)
-{
-       int val, err;
-
-       if (!channel_vac)
-               return 0;
-
-       err = iio_read_channel_processed(channel_vac, &val);
-       if (err < 0)
-               return 0;
-       return val > 4500;
-}
-
-static bool allow_usb;
-module_param(allow_usb, bool, 0644);
-MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
-
-struct twl4030_bci {
-       struct device           *dev;
-       struct power_supply     *ac;
-       struct power_supply     *usb;
-       struct usb_phy          *transceiver;
-       struct notifier_block   usb_nb;
-       struct work_struct      work;
-       int                     irq_chg;
-       int                     irq_bci;
-       int                     usb_enabled;
-
-       /*
-        * ichg_* and *_cur values in uA. If any are 'large', we set
-        * CGAIN to '1' which doubles the range for half the
-        * precision.
-        */
-       unsigned int            ichg_eoc, ichg_lo, ichg_hi;
-       unsigned int            usb_cur, ac_cur;
-       struct iio_channel      *channel_vac;
-       bool                    ac_is_active;
-       int                     usb_mode, ac_mode; /* charging mode requested */
-#define        CHARGE_OFF      0
-#define        CHARGE_AUTO     1
-#define        CHARGE_LINEAR   2
-
-       /* When setting the USB current we slowly increase the
-        * requested current until target is reached or the voltage
-        * drops below 4.75V.  In the latter case we step back one
-        * step.
-        */
-       unsigned int            usb_cur_target;
-       struct delayed_work     current_worker;
-#define        USB_CUR_STEP    20000   /* 20mA at a time */
-#define        USB_MIN_VOLT    4750000 /* 4.75V */
-#define        USB_CUR_DELAY   msecs_to_jiffies(100)
-#define        USB_MAX_CURRENT 1700000 /* TWL4030 caps at 1.7A */
-
-       unsigned long           event;
-};
-
-/* strings for 'usb_mode' values */
-static char *modes[] = { "off", "auto", "continuous" };
-
-/*
- * clear and set bits on an given register on a given module
- */
-static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
-{
-       u8 val = 0;
-       int ret;
-
-       ret = twl_i2c_read_u8(mod_no, &val, reg);
-       if (ret)
-               return ret;
-
-       val &= ~clear;
-       val |= set;
-
-       return twl_i2c_write_u8(mod_no, val, reg);
-}
-
-static int twl4030_bci_read(u8 reg, u8 *val)
-{
-       return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg);
-}
-
-static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
-{
-       return twl4030_clear_set(TWL_MODULE_PM_MASTER, clear,
-                       TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
-                       TWL4030_PM_MASTER_BOOT_BCI);
-}
-
-static int twl4030bci_read_adc_val(u8 reg)
-{
-       int ret, temp;
-       u8 val;
-
-       /* read MSB */
-       ret = twl4030_bci_read(reg + 1, &val);
-       if (ret)
-               return ret;
-
-       temp = (int)(val & 0x03) << 8;
-
-       /* read LSB */
-       ret = twl4030_bci_read(reg, &val);
-       if (ret)
-               return ret;
-
-       return temp | val;
-}
-
-/*
- * Check if Battery Pack was present
- */
-static int twl4030_is_battery_present(struct twl4030_bci *bci)
-{
-       int ret;
-       u8 val = 0;
-
-       /* Battery presence in Main charge? */
-       ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3);
-       if (ret)
-               return ret;
-       if (val & TWL4030_BATSTSMCHG)
-               return 0;
-
-       /*
-        * OK, It could be that bootloader did not enable main charger,
-        * pre-charge is h/w auto. So, Battery presence in Pre-charge?
-        */
-       ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val,
-                             TWL4030_BCIMFSTS1);
-       if (ret)
-               return ret;
-       if (val & TWL4030_BATSTSPCHG)
-               return 0;
-
-       return -ENODEV;
-}
-
-/*
- * TI provided formulas:
- * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
- * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
- * Here we use integer approximation of:
- * CGAIN == 0: val * 1.6618 - 0.85 * 1000
- * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2
- */
-/*
- * convert twl register value for currents into uA
- */
-static int regval2ua(int regval, bool cgain)
-{
-       if (cgain)
-               return (regval * 16618 - 8500 * 1000) / 5;
-       else
-               return (regval * 16618 - 8500 * 1000) / 10;
-}
-
-/*
- * convert uA currents into twl register value
- */
-static int ua2regval(int ua, bool cgain)
-{
-       int ret;
-       if (cgain)
-               ua /= 2;
-       ret = (ua * 10 + 8500 * 1000) / 16618;
-       /* rounding problems */
-       if (ret < 512)
-               ret = 512;
-       return ret;
-}
-
-static int twl4030_charger_update_current(struct twl4030_bci *bci)
-{
-       int status;
-       int cur;
-       unsigned reg, cur_reg;
-       u8 bcictl1, oldreg, fullreg;
-       bool cgain = false;
-       u8 boot_bci;
-
-       /*
-        * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
-        * and AC is enabled, set current for 'ac'
-        */
-       if (ac_available(bci->channel_vac)) {
-               cur = bci->ac_cur;
-               bci->ac_is_active = true;
-       } else {
-               cur = bci->usb_cur;
-               bci->ac_is_active = false;
-               if (cur > bci->usb_cur_target) {
-                       cur = bci->usb_cur_target;
-                       bci->usb_cur = cur;
-               }
-               if (cur < bci->usb_cur_target)
-                       schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
-       }
-
-       /* First, check thresholds and see if cgain is needed */
-       if (bci->ichg_eoc >= 200000)
-               cgain = true;
-       if (bci->ichg_lo >= 400000)
-               cgain = true;
-       if (bci->ichg_hi >= 820000)
-               cgain = true;
-       if (cur > 852000)
-               cgain = true;
-
-       status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
-       if (status < 0)
-               return status;
-       if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci,
-                           TWL4030_PM_MASTER_BOOT_BCI) < 0)
-               boot_bci = 0;
-       boot_bci &= 7;
-
-       if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN))
-               /* Need to turn for charging while we change the
-                * CGAIN bit.  Leave it off while everything is
-                * updated.
-                */
-               twl4030_clear_set_boot_bci(boot_bci, 0);
-
-       /*
-        * For ichg_eoc, the hardware only supports reg values matching
-        * 100XXXX000, and requires the XXXX be stored in the high nibble
-        * of TWL4030_BCIMFTH8.
-        */
-       reg = ua2regval(bci->ichg_eoc, cgain);
-       if (reg > 0x278)
-               reg = 0x278;
-       if (reg < 0x200)
-               reg = 0x200;
-       reg = (reg >> 3) & 0xf;
-       fullreg = reg << 4;
-
-       /*
-        * For ichg_lo, reg value must match 10XXXX0000.
-        * XXXX is stored in low nibble of TWL4030_BCIMFTH8.
-        */
-       reg = ua2regval(bci->ichg_lo, cgain);
-       if (reg > 0x2F0)
-               reg = 0x2F0;
-       if (reg < 0x200)
-               reg = 0x200;
-       reg = (reg >> 4) & 0xf;
-       fullreg |= reg;
-
-       /* ichg_eoc and ichg_lo live in same register */
-       status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg);
-       if (status < 0)
-               return status;
-       if (oldreg != fullreg) {
-               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4,
-                                         TWL4030_BCIMFKEY);
-               if (status < 0)
-                       return status;
-               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
-                                fullreg, TWL4030_BCIMFTH8);
-       }
-
-       /* ichg_hi threshold must be 1XXXX01100 (I think) */
-       reg = ua2regval(bci->ichg_hi, cgain);
-       if (reg > 0x3E0)
-               reg = 0x3E0;
-       if (reg < 0x200)
-               reg = 0x200;
-       fullreg = (reg >> 5) & 0xF;
-       fullreg <<= 4;
-       status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg);
-       if (status < 0)
-               return status;
-       if ((oldreg & 0xF0) != fullreg) {
-               fullreg |= (oldreg & 0x0F);
-               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
-                                         TWL4030_BCIMFKEY);
-               if (status < 0)
-                       return status;
-               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
-                                fullreg, TWL4030_BCIMFTH9);
-       }
-
-       /*
-        * And finally, set the current.  This is stored in
-        * two registers.
-        */
-       reg = ua2regval(cur, cgain);
-       /* we have only 10 bits */
-       if (reg > 0x3ff)
-               reg = 0x3ff;
-       status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg);
-       if (status < 0)
-               return status;
-       cur_reg = oldreg;
-       status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg);
-       if (status < 0)
-               return status;
-       cur_reg |= oldreg << 8;
-       if (reg != oldreg) {
-               /* disable write protection for one write access for
-                * BCIIREF */
-               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
-                                         TWL4030_BCIMFKEY);
-               if (status < 0)
-                       return status;
-               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
-                                         (reg & 0x100) ? 3 : 2,
-                                         TWL4030_BCIIREF2);
-               if (status < 0)
-                       return status;
-               /* disable write protection for one write access for
-                * BCIIREF */
-               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
-                                         TWL4030_BCIMFKEY);
-               if (status < 0)
-                       return status;
-               status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
-                                         reg & 0xff,
-                                         TWL4030_BCIIREF1);
-       }
-       if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) {
-               /* Flip CGAIN and re-enable charging */
-               bcictl1 ^= TWL4030_CGAIN;
-               twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
-                                bcictl1, TWL4030_BCICTL1);
-               twl4030_clear_set_boot_bci(0, boot_bci);
-       }
-       return 0;
-}
-
-static int twl4030_charger_get_current(void);
-
-static void twl4030_current_worker(struct work_struct *data)
-{
-       int v, curr;
-       int res;
-       struct twl4030_bci *bci = container_of(data, struct twl4030_bci,
-                                              current_worker.work);
-
-       res = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
-       if (res < 0)
-               v = 0;
-       else
-               /* BCIVBUS uses ADCIN8, 7/1023 V/step */
-               v = res * 6843;
-       curr = twl4030_charger_get_current();
-
-       dev_dbg(bci->dev, "v=%d cur=%d limit=%d target=%d\n", v, curr,
-               bci->usb_cur, bci->usb_cur_target);
-
-       if (v < USB_MIN_VOLT) {
-               /* Back up and stop adjusting. */
-               bci->usb_cur -= USB_CUR_STEP;
-               bci->usb_cur_target = bci->usb_cur;
-       } else if (bci->usb_cur >= bci->usb_cur_target ||
-                  bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) {
-               /* Reached target and voltage is OK - stop */
-               return;
-       } else {
-               bci->usb_cur += USB_CUR_STEP;
-               schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
-       }
-       twl4030_charger_update_current(bci);
-}
-
-/*
- * Enable/Disable USB Charge functionality.
- */
-static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
-{
-       int ret;
-
-       if (bci->usb_mode == CHARGE_OFF)
-               enable = false;
-       if (enable && !IS_ERR_OR_NULL(bci->transceiver)) {
-
-               twl4030_charger_update_current(bci);
-
-               /* Need to keep phy powered */
-               if (!bci->usb_enabled) {
-                       pm_runtime_get_sync(bci->transceiver->dev);
-                       bci->usb_enabled = 1;
-               }
-
-               if (bci->usb_mode == CHARGE_AUTO)
-                       /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
-                       ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
-
-               /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
-               ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
-                       TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
-               if (bci->usb_mode == CHARGE_LINEAR) {
-                       twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC|TWL4030_CVENAC, 0);
-                       /* Watch dog key: WOVF acknowledge */
-                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x33,
-                                              TWL4030_BCIWDKEY);
-                       /* 0x24 + EKEY6: off mode */
-                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a,
-                                              TWL4030_BCIMDKEY);
-                       /* EKEY2: Linear charge: USB path */
-                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x26,
-                                              TWL4030_BCIMDKEY);
-                       /* WDKEY5: stop watchdog count */
-                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf3,
-                                              TWL4030_BCIWDKEY);
-                       /* enable MFEN3 access */
-                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x9c,
-                                              TWL4030_BCIMFKEY);
-                        /* ICHGEOCEN - end-of-charge monitor (current < 80mA)
-                         *                      (charging continues)
-                         * ICHGLOWEN - current level monitor (charge continues)
-                         * don't monitor over-current or heat save
-                         */
-                       ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf0,
-                                              TWL4030_BCIMFEN3);
-               }
-       } else {
-               ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
-               ret |= twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a,
-                                       TWL4030_BCIMDKEY);
-               if (bci->usb_enabled) {
-                       pm_runtime_mark_last_busy(bci->transceiver->dev);
-                       pm_runtime_put_autosuspend(bci->transceiver->dev);
-                       bci->usb_enabled = 0;
-               }
-               bci->usb_cur = 0;
-       }
-
-       return ret;
-}
-
-/*
- * Enable/Disable AC Charge funtionality.
- */
-static int twl4030_charger_enable_ac(struct twl4030_bci *bci, bool enable)
-{
-       int ret;
-
-       if (bci->ac_mode == CHARGE_OFF)
-               enable = false;
-
-       if (enable)
-               ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
-       else
-               ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0);
-
-       return ret;
-}
-
-/*
- * Enable/Disable charging of Backup Battery.
- */
-static int twl4030_charger_enable_backup(int uvolt, int uamp)
-{
-       int ret;
-       u8 flags;
-
-       if (uvolt < 2500000 ||
-           uamp < 25) {
-               /* disable charging of backup battery */
-               ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
-                                       TWL4030_BBCHEN, 0, TWL4030_BB_CFG);
-               return ret;
-       }
-
-       flags = TWL4030_BBCHEN;
-       if (uvolt >= 3200000)
-               flags |= TWL4030_BBSEL_3V2;
-       else if (uvolt >= 3100000)
-               flags |= TWL4030_BBSEL_3V1;
-       else if (uvolt >= 3000000)
-               flags |= TWL4030_BBSEL_3V0;
-       else
-               flags |= TWL4030_BBSEL_2V5;
-
-       if (uamp >= 1000)
-               flags |= TWL4030_BBISEL_1000uA;
-       else if (uamp >= 500)
-               flags |= TWL4030_BBISEL_500uA;
-       else if (uamp >= 150)
-               flags |= TWL4030_BBISEL_150uA;
-       else
-               flags |= TWL4030_BBISEL_25uA;
-
-       ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
-                               TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK,
-                               flags,
-                               TWL4030_BB_CFG);
-
-       return ret;
-}
-
-/*
- * TWL4030 CHG_PRES (AC charger presence) events
- */
-static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
-{
-       struct twl4030_bci *bci = arg;
-
-       dev_dbg(bci->dev, "CHG_PRES irq\n");
-       /* reset current on each 'plug' event */
-       bci->ac_cur = 500000;
-       twl4030_charger_update_current(bci);
-       power_supply_changed(bci->ac);
-       power_supply_changed(bci->usb);
-
-       return IRQ_HANDLED;
-}
-
-/*
- * TWL4030 BCI monitoring events
- */
-static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
-{
-       struct twl4030_bci *bci = arg;
-       u8 irqs1, irqs2;
-       int ret;
-
-       ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1,
-                             TWL4030_INTERRUPTS_BCIISR1A);
-       if (ret < 0)
-               return IRQ_HANDLED;
-
-       ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2,
-                             TWL4030_INTERRUPTS_BCIISR2A);
-       if (ret < 0)
-               return IRQ_HANDLED;
-
-       dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1);
-
-       if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) {
-               /* charger state change, inform the core */
-               power_supply_changed(bci->ac);
-               power_supply_changed(bci->usb);
-       }
-       twl4030_charger_update_current(bci);
-
-       /* various monitoring events, for now we just log them here */
-       if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
-               dev_warn(bci->dev, "battery temperature out of range\n");
-
-       if (irqs1 & TWL4030_BATSTS)
-               dev_crit(bci->dev, "battery disconnected\n");
-
-       if (irqs2 & TWL4030_VBATOV)
-               dev_crit(bci->dev, "VBAT overvoltage\n");
-
-       if (irqs2 & TWL4030_VBUSOV)
-               dev_crit(bci->dev, "VBUS overvoltage\n");
-
-       if (irqs2 & TWL4030_ACCHGOV)
-               dev_crit(bci->dev, "Ac charger overvoltage\n");
-
-       return IRQ_HANDLED;
-}
-
-/*
- * Provide "max_current" attribute in sysfs.
- */
-static ssize_t
-twl4030_bci_max_current_store(struct device *dev, struct device_attribute *attr,
-       const char *buf, size_t n)
-{
-       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
-       int cur = 0;
-       int status = 0;
-       status = kstrtoint(buf, 10, &cur);
-       if (status)
-               return status;
-       if (cur < 0)
-               return -EINVAL;
-       if (dev == &bci->ac->dev)
-               bci->ac_cur = cur;
-       else
-               bci->usb_cur_target = cur;
-
-       twl4030_charger_update_current(bci);
-       return n;
-}
-
-/*
- * sysfs max_current show
- */
-static ssize_t twl4030_bci_max_current_show(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-       int status = 0;
-       int cur = -1;
-       u8 bcictl1;
-       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
-
-       if (dev == &bci->ac->dev) {
-               if (!bci->ac_is_active)
-                       cur = bci->ac_cur;
-       } else {
-               if (bci->ac_is_active)
-                       cur = bci->usb_cur_target;
-       }
-       if (cur < 0) {
-               cur = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
-               if (cur < 0)
-                       return cur;
-               status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
-               if (status < 0)
-                       return status;
-               cur = regval2ua(cur, bcictl1 & TWL4030_CGAIN);
-       }
-       return scnprintf(buf, PAGE_SIZE, "%u\n", cur);
-}
-
-static DEVICE_ATTR(max_current, 0644, twl4030_bci_max_current_show,
-                       twl4030_bci_max_current_store);
-
-static void twl4030_bci_usb_work(struct work_struct *data)
-{
-       struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work);
-
-       switch (bci->event) {
-       case USB_EVENT_VBUS:
-       case USB_EVENT_CHARGER:
-               twl4030_charger_enable_usb(bci, true);
-               break;
-       case USB_EVENT_NONE:
-               twl4030_charger_enable_usb(bci, false);
-               break;
-       }
-}
-
-static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
-                              void *priv)
-{
-       struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb);
-
-       dev_dbg(bci->dev, "OTG notify %lu\n", val);
-
-       /* reset current on each 'plug' event */
-       if (allow_usb)
-               bci->usb_cur_target = 500000;
-       else
-               bci->usb_cur_target = 100000;
-
-       bci->event = val;
-       schedule_work(&bci->work);
-
-       return NOTIFY_OK;
-}
-
-/*
- * sysfs charger enabled store
- */
-static ssize_t
-twl4030_bci_mode_store(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t n)
-{
-       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
-       int mode;
-       int status;
-
-       if (sysfs_streq(buf, modes[0]))
-               mode = 0;
-       else if (sysfs_streq(buf, modes[1]))
-               mode = 1;
-       else if (sysfs_streq(buf, modes[2]))
-               mode = 2;
-       else
-               return -EINVAL;
-       if (dev == &bci->ac->dev) {
-               if (mode == 2)
-                       return -EINVAL;
-               twl4030_charger_enable_ac(bci, false);
-               bci->ac_mode = mode;
-               status = twl4030_charger_enable_ac(bci, true);
-       } else {
-               twl4030_charger_enable_usb(bci, false);
-               bci->usb_mode = mode;
-               status = twl4030_charger_enable_usb(bci, true);
-       }
-       return (status == 0) ? n : status;
-}
-
-/*
- * sysfs charger enabled show
- */
-static ssize_t
-twl4030_bci_mode_show(struct device *dev,
-                            struct device_attribute *attr, char *buf)
-{
-       struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
-       int len = 0;
-       int i;
-       int mode = bci->usb_mode;
-
-       if (dev == &bci->ac->dev)
-               mode = bci->ac_mode;
-
-       for (i = 0; i < ARRAY_SIZE(modes); i++)
-               if (mode == i)
-                       len += snprintf(buf+len, PAGE_SIZE-len,
-                                       "[%s] ", modes[i]);
-               else
-                       len += snprintf(buf+len, PAGE_SIZE-len,
-                                       "%s ", modes[i]);
-       buf[len-1] = '\n';
-       return len;
-}
-static DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show,
-                  twl4030_bci_mode_store);
-
-static int twl4030_charger_get_current(void)
-{
-       int curr;
-       int ret;
-       u8 bcictl1;
-
-       curr = twl4030bci_read_adc_val(TWL4030_BCIICHG);
-       if (curr < 0)
-               return curr;
-
-       ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
-       if (ret)
-               return ret;
-
-       return regval2ua(curr, bcictl1 & TWL4030_CGAIN);
-}
-
-/*
- * Returns the main charge FSM state
- * Or < 0 on failure.
- */
-static int twl4030bci_state(struct twl4030_bci *bci)
-{
-       int ret;
-       u8 state;
-
-       ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state);
-       if (ret) {
-               pr_err("twl4030_bci: error reading BCIMSTATEC\n");
-               return ret;
-       }
-
-       dev_dbg(bci->dev, "state: %02x\n", state);
-
-       return state;
-}
-
-static int twl4030_bci_state_to_status(int state)
-{
-       state &= TWL4030_MSTATEC_MASK;
-       if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7)
-               return POWER_SUPPLY_STATUS_CHARGING;
-       else if (TWL4030_MSTATEC_COMPLETE1 <= state &&
-                                       state <= TWL4030_MSTATEC_COMPLETE4)
-               return POWER_SUPPLY_STATUS_FULL;
-       else
-               return POWER_SUPPLY_STATUS_NOT_CHARGING;
-}
-
-static int twl4030_bci_get_property(struct power_supply *psy,
-                                   enum power_supply_property psp,
-                                   union power_supply_propval *val)
-{
-       struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent);
-       int is_charging;
-       int state;
-       int ret;
-
-       state = twl4030bci_state(bci);
-       if (state < 0)
-               return state;
-
-       if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
-               is_charging = state & TWL4030_MSTATEC_USB;
-       else
-               is_charging = state & TWL4030_MSTATEC_AC;
-       if (!is_charging) {
-               u8 s;
-               twl4030_bci_read(TWL4030_BCIMDEN, &s);
-               if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
-                       is_charging = s & 1;
-               else
-                       is_charging = s & 2;
-               if (is_charging)
-                       /* A little white lie */
-                       state = TWL4030_MSTATEC_QUICK1;
-       }
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (is_charging)
-                       val->intval = twl4030_bci_state_to_status(state);
-               else
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               /* charging must be active for meaningful result */
-               if (!is_charging)
-                       return -ENODATA;
-               if (psy->desc->type == POWER_SUPPLY_TYPE_USB) {
-                       ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
-                       if (ret < 0)
-                               return ret;
-                       /* BCIVBUS uses ADCIN8, 7/1023 V/step */
-                       val->intval = ret * 6843;
-               } else {
-                       ret = twl4030bci_read_adc_val(TWL4030_BCIVAC);
-                       if (ret < 0)
-                               return ret;
-                       /* BCIVAC uses ADCIN11, 10/1023 V/step */
-                       val->intval = ret * 9775;
-               }
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               if (!is_charging)
-                       return -ENODATA;
-               /* current measurement is shared between AC and USB */
-               ret = twl4030_charger_get_current();
-               if (ret < 0)
-                       return ret;
-               val->intval = ret;
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = is_charging &&
-                       twl4030_bci_state_to_status(state) !=
-                               POWER_SUPPLY_STATUS_NOT_CHARGING;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static enum power_supply_property twl4030_charger_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-};
-
-#ifdef CONFIG_OF
-static const struct twl4030_bci_platform_data *
-twl4030_bci_parse_dt(struct device *dev)
-{
-       struct device_node *np = dev->of_node;
-       struct twl4030_bci_platform_data *pdata;
-       u32 num;
-
-       if (!np)
-               return NULL;
-       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return pdata;
-
-       if (of_property_read_u32(np, "ti,bb-uvolt", &num) == 0)
-               pdata->bb_uvolt = num;
-       if (of_property_read_u32(np, "ti,bb-uamp", &num) == 0)
-               pdata->bb_uamp = num;
-       return pdata;
-}
-#else
-static inline const struct twl4030_bci_platform_data *
-twl4030_bci_parse_dt(struct device *dev)
-{
-       return NULL;
-}
-#endif
-
-static const struct power_supply_desc twl4030_bci_ac_desc = {
-       .name           = "twl4030_ac",
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-       .properties     = twl4030_charger_props,
-       .num_properties = ARRAY_SIZE(twl4030_charger_props),
-       .get_property   = twl4030_bci_get_property,
-};
-
-static const struct power_supply_desc twl4030_bci_usb_desc = {
-       .name           = "twl4030_usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .properties     = twl4030_charger_props,
-       .num_properties = ARRAY_SIZE(twl4030_charger_props),
-       .get_property   = twl4030_bci_get_property,
-};
-
-static int twl4030_bci_probe(struct platform_device *pdev)
-{
-       struct twl4030_bci *bci;
-       const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
-       int ret;
-       u32 reg;
-
-       bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL);
-       if (bci == NULL)
-               return -ENOMEM;
-
-       if (!pdata)
-               pdata = twl4030_bci_parse_dt(&pdev->dev);
-
-       bci->ichg_eoc = 80100; /* Stop charging when current drops to here */
-       bci->ichg_lo = 241000; /* Low threshold */
-       bci->ichg_hi = 500000; /* High threshold */
-       bci->ac_cur = 500000; /* 500mA */
-       if (allow_usb)
-               bci->usb_cur_target = 500000;  /* 500mA */
-       else
-               bci->usb_cur_target = 100000;  /* 100mA */
-       bci->usb_mode = CHARGE_AUTO;
-       bci->ac_mode = CHARGE_AUTO;
-
-       bci->dev = &pdev->dev;
-       bci->irq_chg = platform_get_irq(pdev, 0);
-       bci->irq_bci = platform_get_irq(pdev, 1);
-
-       /* Only proceed further *IF* battery is physically present */
-       ret = twl4030_is_battery_present(bci);
-       if  (ret) {
-               dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
-               return ret;
-       }
-
-       platform_set_drvdata(pdev, bci);
-
-       bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
-                                            NULL);
-       if (IS_ERR(bci->ac)) {
-               ret = PTR_ERR(bci->ac);
-               dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
-               return ret;
-       }
-
-       bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
-                                             NULL);
-       if (IS_ERR(bci->usb)) {
-               ret = PTR_ERR(bci->usb);
-               dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
-               return ret;
-       }
-
-       ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL,
-                       twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name,
-                       bci);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "could not request irq %d, status %d\n",
-                       bci->irq_chg, ret);
-               return ret;
-       }
-
-       ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL,
-                       twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "could not request irq %d, status %d\n",
-                       bci->irq_bci, ret);
-               return ret;
-       }
-
-       bci->channel_vac = iio_channel_get(&pdev->dev, "vac");
-       if (IS_ERR(bci->channel_vac)) {
-               bci->channel_vac = NULL;
-               dev_warn(&pdev->dev, "could not request vac iio channel");
-       }
-
-       INIT_WORK(&bci->work, twl4030_bci_usb_work);
-       INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
-
-       bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
-       if (bci->dev->of_node) {
-               struct device_node *phynode;
-
-               phynode = of_find_compatible_node(bci->dev->of_node->parent,
-                                                 NULL, "ti,twl4030-usb");
-               if (phynode)
-                       bci->transceiver = devm_usb_get_phy_by_node(
-                               bci->dev, phynode, &bci->usb_nb);
-       }
-
-       /* Enable interrupts now. */
-       reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
-               TWL4030_TBATOR1 | TWL4030_BATSTS);
-       ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
-                              TWL4030_INTERRUPTS_BCIIMR1A);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
-               goto fail;
-       }
-
-       reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
-       ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
-                              TWL4030_INTERRUPTS_BCIIMR2A);
-       if (ret < 0)
-               dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
-
-       twl4030_charger_update_current(bci);
-       if (device_create_file(&bci->usb->dev, &dev_attr_max_current))
-               dev_warn(&pdev->dev, "could not create sysfs file\n");
-       if (device_create_file(&bci->usb->dev, &dev_attr_mode))
-               dev_warn(&pdev->dev, "could not create sysfs file\n");
-       if (device_create_file(&bci->ac->dev, &dev_attr_mode))
-               dev_warn(&pdev->dev, "could not create sysfs file\n");
-       if (device_create_file(&bci->ac->dev, &dev_attr_max_current))
-               dev_warn(&pdev->dev, "could not create sysfs file\n");
-
-       twl4030_charger_enable_ac(bci, true);
-       if (!IS_ERR_OR_NULL(bci->transceiver))
-               twl4030_bci_usb_ncb(&bci->usb_nb,
-                                   bci->transceiver->last_event,
-                                   NULL);
-       else
-               twl4030_charger_enable_usb(bci, false);
-       if (pdata)
-               twl4030_charger_enable_backup(pdata->bb_uvolt,
-                                             pdata->bb_uamp);
-       else
-               twl4030_charger_enable_backup(0, 0);
-
-       return 0;
-fail:
-       iio_channel_release(bci->channel_vac);
-
-       return ret;
-}
-
-static int __exit twl4030_bci_remove(struct platform_device *pdev)
-{
-       struct twl4030_bci *bci = platform_get_drvdata(pdev);
-
-       twl4030_charger_enable_ac(bci, false);
-       twl4030_charger_enable_usb(bci, false);
-       twl4030_charger_enable_backup(0, 0);
-
-       iio_channel_release(bci->channel_vac);
-
-       device_remove_file(&bci->usb->dev, &dev_attr_max_current);
-       device_remove_file(&bci->usb->dev, &dev_attr_mode);
-       device_remove_file(&bci->ac->dev, &dev_attr_max_current);
-       device_remove_file(&bci->ac->dev, &dev_attr_mode);
-       /* mask interrupts */
-       twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
-                        TWL4030_INTERRUPTS_BCIIMR1A);
-       twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
-                        TWL4030_INTERRUPTS_BCIIMR2A);
-
-       return 0;
-}
-
-static const struct of_device_id twl_bci_of_match[] = {
-       {.compatible = "ti,twl4030-bci", },
-       { }
-};
-MODULE_DEVICE_TABLE(of, twl_bci_of_match);
-
-static struct platform_driver twl4030_bci_driver = {
-       .probe = twl4030_bci_probe,
-       .driver = {
-               .name   = "twl4030_bci",
-               .of_match_table = of_match_ptr(twl_bci_of_match),
-       },
-       .remove = __exit_p(twl4030_bci_remove),
-};
-module_platform_driver(twl4030_bci_driver);
-
-MODULE_AUTHOR("Gražvydas Ignotas");
-MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:twl4030_bci");
diff --git a/drivers/power/twl4030_madc_battery.c b/drivers/power/twl4030_madc_battery.c
deleted file mode 100644 (file)
index f5817e4..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Dumb driver for LiIon batteries using TWL4030 madc.
- *
- * Copyright 2013 Golden Delicious Computers
- * Lukas Märdian <lukas@goldelico.com>
- *
- * Based on dumb driver for gta01 battery
- * Copyright 2009 Openmoko, Inc
- * Balaji Rao <balajirrao@openmoko.org>
- */
-
-#include <linux/module.h>
-#include <linux/param.h>
-#include <linux/delay.h>
-#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/sort.h>
-#include <linux/i2c/twl4030-madc.h>
-#include <linux/power/twl4030_madc_battery.h>
-#include <linux/iio/consumer.h>
-
-struct twl4030_madc_battery {
-       struct power_supply *psy;
-       struct twl4030_madc_bat_platform_data *pdata;
-       struct iio_channel *channel_temp;
-       struct iio_channel *channel_ichg;
-       struct iio_channel *channel_vbat;
-};
-
-static enum power_supply_property twl4030_madc_bat_props[] = {
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-};
-
-static int madc_read(struct iio_channel *channel)
-{
-       int val, err;
-       err = iio_read_channel_processed(channel, &val);
-       if (err < 0)
-               return err;
-
-       return val;
-}
-
-static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery *bt)
-{
-       return (madc_read(bt->channel_ichg) > 0) ? 1 : 0;
-}
-
-static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery *bt)
-{
-       return madc_read(bt->channel_vbat);
-}
-
-static int twl4030_madc_bat_get_current(struct twl4030_madc_battery *bt)
-{
-       return madc_read(bt->channel_ichg) * 1000;
-}
-
-static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery *bt)
-{
-       return madc_read(bt->channel_temp) * 10;
-}
-
-static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat,
-                                       int volt)
-{
-       struct twl4030_madc_bat_calibration *calibration;
-       int i, res = 0;
-
-       /* choose charging curve */
-       if (twl4030_madc_bat_get_charging_status(bat))
-               calibration = bat->pdata->charging;
-       else
-               calibration = bat->pdata->discharging;
-
-       if (volt > calibration[0].voltage) {
-               res = calibration[0].level;
-       } else {
-               for (i = 0; calibration[i+1].voltage >= 0; i++) {
-                       if (volt <= calibration[i].voltage &&
-                                       volt >= calibration[i+1].voltage) {
-                               /* interval found - interpolate within range */
-                               res = calibration[i].level -
-                                       ((calibration[i].voltage - volt) *
-                                       (calibration[i].level -
-                                       calibration[i+1].level)) /
-                                       (calibration[i].voltage -
-                                       calibration[i+1].voltage);
-                               break;
-                       }
-               }
-       }
-       return res;
-}
-
-static int twl4030_madc_bat_get_property(struct power_supply *psy,
-                                       enum power_supply_property psp,
-                                       union power_supply_propval *val)
-{
-       struct twl4030_madc_battery *bat = power_supply_get_drvdata(psy);
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (twl4030_madc_bat_voltscale(bat,
-                               twl4030_madc_bat_get_voltage(bat)) > 95)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
-               else {
-                       if (twl4030_madc_bat_get_charging_status(bat))
-                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
-                       else
-                               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-               }
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = twl4030_madc_bat_get_voltage(bat) * 1000;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               val->intval = twl4030_madc_bat_get_current(bat);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               /* assume battery is always present */
-               val->intval = 1;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW: {
-                       int percent = twl4030_madc_bat_voltscale(bat,
-                                       twl4030_madc_bat_get_voltage(bat));
-                       val->intval = (percent * bat->pdata->capacity) / 100;
-                       break;
-               }
-       case POWER_SUPPLY_PROP_CAPACITY:
-               val->intval = twl4030_madc_bat_voltscale(bat,
-                                       twl4030_madc_bat_get_voltage(bat));
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               val->intval = bat->pdata->capacity;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               val->intval = twl4030_madc_bat_get_temp(bat);
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: {
-                       int percent = twl4030_madc_bat_voltscale(bat,
-                                       twl4030_madc_bat_get_voltage(bat));
-                       /* in mAh */
-                       int chg = (percent * (bat->pdata->capacity/1000))/100;
-
-                       /* assume discharge with 400 mA (ca. 1.5W) */
-                       val->intval = (3600l * chg) / 400;
-                       break;
-               }
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static void twl4030_madc_bat_ext_changed(struct power_supply *psy)
-{
-       power_supply_changed(psy);
-}
-
-static const struct power_supply_desc twl4030_madc_bat_desc = {
-       .name                   = "twl4030_battery",
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .properties             = twl4030_madc_bat_props,
-       .num_properties         = ARRAY_SIZE(twl4030_madc_bat_props),
-       .get_property           = twl4030_madc_bat_get_property,
-       .external_power_changed = twl4030_madc_bat_ext_changed,
-
-};
-
-static int twl4030_cmp(const void *a, const void *b)
-{
-       return ((struct twl4030_madc_bat_calibration *)b)->voltage -
-               ((struct twl4030_madc_bat_calibration *)a)->voltage;
-}
-
-static int twl4030_madc_battery_probe(struct platform_device *pdev)
-{
-       struct twl4030_madc_battery *twl4030_madc_bat;
-       struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-       int ret = 0;
-
-       twl4030_madc_bat = devm_kzalloc(&pdev->dev, sizeof(*twl4030_madc_bat),
-                               GFP_KERNEL);
-       if (!twl4030_madc_bat)
-               return -ENOMEM;
-
-       twl4030_madc_bat->channel_temp = iio_channel_get(&pdev->dev, "temp");
-       if (IS_ERR(twl4030_madc_bat->channel_temp)) {
-               ret = PTR_ERR(twl4030_madc_bat->channel_temp);
-               goto err;
-       }
-
-       twl4030_madc_bat->channel_ichg = iio_channel_get(&pdev->dev, "ichg");
-       if (IS_ERR(twl4030_madc_bat->channel_ichg)) {
-               ret = PTR_ERR(twl4030_madc_bat->channel_ichg);
-               goto err_temp;
-       }
-
-       twl4030_madc_bat->channel_vbat = iio_channel_get(&pdev->dev, "vbat");
-       if (IS_ERR(twl4030_madc_bat->channel_vbat)) {
-               ret = PTR_ERR(twl4030_madc_bat->channel_vbat);
-               goto err_ichg;
-       }
-
-       /* sort charging and discharging calibration data */
-       sort(pdata->charging, pdata->charging_size,
-               sizeof(struct twl4030_madc_bat_calibration),
-               twl4030_cmp, NULL);
-       sort(pdata->discharging, pdata->discharging_size,
-               sizeof(struct twl4030_madc_bat_calibration),
-               twl4030_cmp, NULL);
-
-       twl4030_madc_bat->pdata = pdata;
-       platform_set_drvdata(pdev, twl4030_madc_bat);
-       psy_cfg.drv_data = twl4030_madc_bat;
-       twl4030_madc_bat->psy = power_supply_register(&pdev->dev,
-                                                     &twl4030_madc_bat_desc,
-                                                     &psy_cfg);
-       if (IS_ERR(twl4030_madc_bat->psy)) {
-               ret = PTR_ERR(twl4030_madc_bat->psy);
-               goto err_vbat;
-       }
-
-       return 0;
-
-err_vbat:
-       iio_channel_release(twl4030_madc_bat->channel_vbat);
-err_ichg:
-       iio_channel_release(twl4030_madc_bat->channel_ichg);
-err_temp:
-       iio_channel_release(twl4030_madc_bat->channel_temp);
-err:
-       return ret;
-}
-
-static int twl4030_madc_battery_remove(struct platform_device *pdev)
-{
-       struct twl4030_madc_battery *bat = platform_get_drvdata(pdev);
-
-       power_supply_unregister(bat->psy);
-
-       iio_channel_release(bat->channel_vbat);
-       iio_channel_release(bat->channel_ichg);
-       iio_channel_release(bat->channel_temp);
-
-       return 0;
-}
-
-static struct platform_driver twl4030_madc_battery_driver = {
-       .driver = {
-               .name = "twl4030_madc_battery",
-       },
-       .probe  = twl4030_madc_battery_probe,
-       .remove = twl4030_madc_battery_remove,
-};
-module_platform_driver(twl4030_madc_battery_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>");
-MODULE_DESCRIPTION("twl4030_madc battery driver");
-MODULE_ALIAS("platform:twl4030_madc_battery");
diff --git a/drivers/power/wm831x_backup.c b/drivers/power/wm831x_backup.c
deleted file mode 100644 (file)
index 2e33109..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Backup battery driver for Wolfson Microelectronics wm831x PMICs
- *
- * Copyright 2009 Wolfson Microelectronics PLC.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-
-#include <linux/mfd/wm831x/core.h>
-#include <linux/mfd/wm831x/auxadc.h>
-#include <linux/mfd/wm831x/pmu.h>
-#include <linux/mfd/wm831x/pdata.h>
-
-struct wm831x_backup {
-       struct wm831x *wm831x;
-       struct power_supply *backup;
-       struct power_supply_desc backup_desc;
-       char name[20];
-};
-
-static int wm831x_backup_read_voltage(struct wm831x *wm831x,
-                                    enum wm831x_auxadc src,
-                                    union power_supply_propval *val)
-{
-       int ret;
-
-       ret = wm831x_auxadc_read_uv(wm831x, src);
-       if (ret >= 0)
-               val->intval = ret;
-
-       return ret;
-}
-
-/*********************************************************************
- *             Backup supply properties
- *********************************************************************/
-
-static void wm831x_config_backup(struct wm831x *wm831x)
-{
-       struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
-       struct wm831x_backup_pdata *pdata;
-       int ret, reg;
-
-       if (!wm831x_pdata || !wm831x_pdata->backup) {
-               dev_warn(wm831x->dev,
-                        "No backup battery charger configuration\n");
-               return;
-       }
-
-       pdata = wm831x_pdata->backup;
-
-       reg = 0;
-
-       if (pdata->charger_enable)
-               reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
-       if (pdata->no_constant_voltage)
-               reg |= WM831X_BKUP_CHG_MODE;
-
-       switch (pdata->vlim) {
-       case 2500:
-               break;
-       case 3100:
-               reg |= WM831X_BKUP_CHG_VLIM;
-               break;
-       default:
-               dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
-                       pdata->vlim);
-       }
-
-       switch (pdata->ilim) {
-       case 100:
-               break;
-       case 200:
-               reg |= 1;
-               break;
-       case 300:
-               reg |= 2;
-               break;
-       case 400:
-               reg |= 3;
-               break;
-       default:
-               dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
-                       pdata->ilim);
-       }
-
-       ret = wm831x_reg_unlock(wm831x);
-       if (ret != 0) {
-               dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
-               return;
-       }
-
-       ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
-                             WM831X_BKUP_CHG_ENA_MASK |
-                             WM831X_BKUP_CHG_MODE_MASK |
-                             WM831X_BKUP_BATT_DET_ENA_MASK |
-                             WM831X_BKUP_CHG_VLIM_MASK |
-                             WM831X_BKUP_CHG_ILIM_MASK,
-                             reg);
-       if (ret != 0)
-               dev_err(wm831x->dev,
-                       "Failed to set backup charger config: %d\n", ret);
-
-       wm831x_reg_lock(wm831x);
-}
-
-static int wm831x_backup_get_prop(struct power_supply *psy,
-                                 enum power_supply_property psp,
-                                 union power_supply_propval *val)
-{
-       struct wm831x_backup *devdata = dev_get_drvdata(psy->dev.parent);
-       struct wm831x *wm831x = devdata->wm831x;
-       int ret = 0;
-
-       ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
-       if (ret < 0)
-               return ret;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               if (ret & WM831X_BKUP_CHG_STS)
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
-               else
-                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               break;
-
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = wm831x_backup_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
-                                               val);
-               break;
-
-       case POWER_SUPPLY_PROP_PRESENT:
-               if (ret & WM831X_BKUP_CHG_STS)
-                       val->intval = 1;
-               else
-                       val->intval = 0;
-               break;
-
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property wm831x_backup_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_PRESENT,
-};
-
-/*********************************************************************
- *             Initialisation
- *********************************************************************/
-
-static int wm831x_backup_probe(struct platform_device *pdev)
-{
-       struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
-       struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
-       struct wm831x_backup *devdata;
-
-       devdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_backup),
-                               GFP_KERNEL);
-       if (devdata == NULL)
-               return -ENOMEM;
-
-       devdata->wm831x = wm831x;
-       platform_set_drvdata(pdev, devdata);
-
-       /* We ignore configuration failures since we can still read
-        * back the status without enabling the charger (which may
-        * already be enabled anyway).
-        */
-       wm831x_config_backup(wm831x);
-
-       if (wm831x_pdata && wm831x_pdata->wm831x_num)
-               snprintf(devdata->name, sizeof(devdata->name),
-                        "wm831x-backup.%d", wm831x_pdata->wm831x_num);
-       else
-               snprintf(devdata->name, sizeof(devdata->name),
-                        "wm831x-backup");
-
-       devdata->backup_desc.name = devdata->name;
-       devdata->backup_desc.type = POWER_SUPPLY_TYPE_BATTERY;
-       devdata->backup_desc.properties = wm831x_backup_props;
-       devdata->backup_desc.num_properties = ARRAY_SIZE(wm831x_backup_props);
-       devdata->backup_desc.get_property = wm831x_backup_get_prop;
-       devdata->backup = power_supply_register(&pdev->dev,
-                                               &devdata->backup_desc, NULL);
-
-       return PTR_ERR_OR_ZERO(devdata->backup);
-}
-
-static int wm831x_backup_remove(struct platform_device *pdev)
-{
-       struct wm831x_backup *devdata = platform_get_drvdata(pdev);
-
-       power_supply_unregister(devdata->backup);
-
-       return 0;
-}
-
-static struct platform_driver wm831x_backup_driver = {
-       .probe = wm831x_backup_probe,
-       .remove = wm831x_backup_remove,
-       .driver = {
-               .name = "wm831x-backup",
-       },
-};
-
-module_platform_driver(wm831x_backup_driver);
-
-MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs");
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:wm831x-backup");
diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c
deleted file mode 100644 (file)
index 7082301..0000000
+++ /dev/null
@@ -1,673 +0,0 @@
-/*
- * PMU driver for Wolfson Microelectronics wm831x PMICs
- *
- * Copyright 2009 Wolfson Microelectronics PLC.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-
-#include <linux/mfd/wm831x/core.h>
-#include <linux/mfd/wm831x/auxadc.h>
-#include <linux/mfd/wm831x/pmu.h>
-#include <linux/mfd/wm831x/pdata.h>
-
-struct wm831x_power {
-       struct wm831x *wm831x;
-       struct power_supply *wall;
-       struct power_supply *usb;
-       struct power_supply *battery;
-       struct power_supply_desc wall_desc;
-       struct power_supply_desc usb_desc;
-       struct power_supply_desc battery_desc;
-       char wall_name[20];
-       char usb_name[20];
-       char battery_name[20];
-       bool have_battery;
-};
-
-static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
-                                    union power_supply_propval *val)
-{
-       int ret;
-
-       ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
-       if (ret < 0)
-               return ret;
-
-       if (ret & supply)
-               val->intval = 1;
-       else
-               val->intval = 0;
-
-       return 0;
-}
-
-static int wm831x_power_read_voltage(struct wm831x *wm831x,
-                                    enum wm831x_auxadc src,
-                                    union power_supply_propval *val)
-{
-       int ret;
-
-       ret = wm831x_auxadc_read_uv(wm831x, src);
-       if (ret >= 0)
-               val->intval = ret;
-
-       return ret;
-}
-
-/*********************************************************************
- *             WALL Power
- *********************************************************************/
-static int wm831x_wall_get_prop(struct power_supply *psy,
-                               enum power_supply_property psp,
-                               union power_supply_propval *val)
-{
-       struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
-       struct wm831x *wm831x = wm831x_power->wm831x;
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = wm831x_power_check_online(wm831x, WM831X_PWR_WALL, val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_WALL, val);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property wm831x_wall_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-};
-
-/*********************************************************************
- *             USB Power
- *********************************************************************/
-static int wm831x_usb_get_prop(struct power_supply *psy,
-                              enum power_supply_property psp,
-                              union power_supply_propval *val)
-{
-       struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
-       struct wm831x *wm831x = wm831x_power->wm831x;
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = wm831x_power_check_online(wm831x, WM831X_PWR_USB, val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_USB, val);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property wm831x_usb_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-};
-
-/*********************************************************************
- *             Battery properties
- *********************************************************************/
-
-struct chg_map {
-       int val;
-       int reg_val;
-};
-
-static struct chg_map trickle_ilims[] = {
-       {  50, 0 << WM831X_CHG_TRKL_ILIM_SHIFT },
-       { 100, 1 << WM831X_CHG_TRKL_ILIM_SHIFT },
-       { 150, 2 << WM831X_CHG_TRKL_ILIM_SHIFT },
-       { 200, 3 << WM831X_CHG_TRKL_ILIM_SHIFT },
-};
-
-static struct chg_map vsels[] = {
-       { 4050, 0 << WM831X_CHG_VSEL_SHIFT },
-       { 4100, 1 << WM831X_CHG_VSEL_SHIFT },
-       { 4150, 2 << WM831X_CHG_VSEL_SHIFT },
-       { 4200, 3 << WM831X_CHG_VSEL_SHIFT },
-};
-
-static struct chg_map fast_ilims[] = {
-       {    0,  0 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {   50,  1 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  100,  2 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  150,  3 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  200,  4 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  250,  5 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  300,  6 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  350,  7 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  400,  8 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  450,  9 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  500, 10 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  600, 11 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  700, 12 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  800, 13 << WM831X_CHG_FAST_ILIM_SHIFT },
-       {  900, 14 << WM831X_CHG_FAST_ILIM_SHIFT },
-       { 1000, 15 << WM831X_CHG_FAST_ILIM_SHIFT },
-};
-
-static struct chg_map eoc_iterms[] = {
-       { 20, 0 << WM831X_CHG_ITERM_SHIFT },
-       { 30, 1 << WM831X_CHG_ITERM_SHIFT },
-       { 40, 2 << WM831X_CHG_ITERM_SHIFT },
-       { 50, 3 << WM831X_CHG_ITERM_SHIFT },
-       { 60, 4 << WM831X_CHG_ITERM_SHIFT },
-       { 70, 5 << WM831X_CHG_ITERM_SHIFT },
-       { 80, 6 << WM831X_CHG_ITERM_SHIFT },
-       { 90, 7 << WM831X_CHG_ITERM_SHIFT },
-};
-
-static struct chg_map chg_times[] = {
-       {  60,  0 << WM831X_CHG_TIME_SHIFT },
-       {  90,  1 << WM831X_CHG_TIME_SHIFT },
-       { 120,  2 << WM831X_CHG_TIME_SHIFT },
-       { 150,  3 << WM831X_CHG_TIME_SHIFT },
-       { 180,  4 << WM831X_CHG_TIME_SHIFT },
-       { 210,  5 << WM831X_CHG_TIME_SHIFT },
-       { 240,  6 << WM831X_CHG_TIME_SHIFT },
-       { 270,  7 << WM831X_CHG_TIME_SHIFT },
-       { 300,  8 << WM831X_CHG_TIME_SHIFT },
-       { 330,  9 << WM831X_CHG_TIME_SHIFT },
-       { 360, 10 << WM831X_CHG_TIME_SHIFT },
-       { 390, 11 << WM831X_CHG_TIME_SHIFT },
-       { 420, 12 << WM831X_CHG_TIME_SHIFT },
-       { 450, 13 << WM831X_CHG_TIME_SHIFT },
-       { 480, 14 << WM831X_CHG_TIME_SHIFT },
-       { 510, 15 << WM831X_CHG_TIME_SHIFT },
-};
-
-static void wm831x_battey_apply_config(struct wm831x *wm831x,
-                                      struct chg_map *map, int count, int val,
-                                      int *reg, const char *name,
-                                      const char *units)
-{
-       int i;
-
-       for (i = 0; i < count; i++)
-               if (val == map[i].val)
-                       break;
-       if (i == count) {
-               dev_err(wm831x->dev, "Invalid %s %d%s\n",
-                       name, val, units);
-       } else {
-               *reg |= map[i].reg_val;
-               dev_dbg(wm831x->dev, "Set %s of %d%s\n", name, val, units);
-       }
-}
-
-static void wm831x_config_battery(struct wm831x *wm831x)
-{
-       struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
-       struct wm831x_battery_pdata *pdata;
-       int ret, reg1, reg2;
-
-       if (!wm831x_pdata || !wm831x_pdata->battery) {
-               dev_warn(wm831x->dev,
-                        "No battery charger configuration\n");
-               return;
-       }
-
-       pdata = wm831x_pdata->battery;
-
-       reg1 = 0;
-       reg2 = 0;
-
-       if (!pdata->enable) {
-               dev_info(wm831x->dev, "Battery charger disabled\n");
-               return;
-       }
-
-       reg1 |= WM831X_CHG_ENA;
-       if (pdata->off_mask)
-               reg2 |= WM831X_CHG_OFF_MSK;
-       if (pdata->fast_enable)
-               reg1 |= WM831X_CHG_FAST;
-
-       wm831x_battey_apply_config(wm831x, trickle_ilims,
-                                  ARRAY_SIZE(trickle_ilims),
-                                  pdata->trickle_ilim, &reg2,
-                                  "trickle charge current limit", "mA");
-
-       wm831x_battey_apply_config(wm831x, vsels, ARRAY_SIZE(vsels),
-                                  pdata->vsel, &reg2,
-                                  "target voltage", "mV");
-
-       wm831x_battey_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims),
-                                  pdata->fast_ilim, &reg2,
-                                  "fast charge current limit", "mA");
-
-       wm831x_battey_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms),
-                                  pdata->eoc_iterm, &reg1,
-                                  "end of charge current threshold", "mA");
-
-       wm831x_battey_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times),
-                                  pdata->timeout, &reg2,
-                                  "charger timeout", "min");
-
-       ret = wm831x_reg_unlock(wm831x);
-       if (ret != 0) {
-               dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
-               return;
-       }
-
-       ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
-                             WM831X_CHG_ENA_MASK |
-                             WM831X_CHG_FAST_MASK |
-                             WM831X_CHG_ITERM_MASK,
-                             reg1);
-       if (ret != 0)
-               dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
-                       ret);
-
-       ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
-                             WM831X_CHG_OFF_MSK |
-                             WM831X_CHG_TIME_MASK |
-                             WM831X_CHG_FAST_ILIM_MASK |
-                             WM831X_CHG_TRKL_ILIM_MASK |
-                             WM831X_CHG_VSEL_MASK,
-                             reg2);
-       if (ret != 0)
-               dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
-                       ret);
-
-       wm831x_reg_lock(wm831x);
-}
-
-static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
-{
-       int ret;
-
-       ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
-       if (ret < 0)
-               return ret;
-
-       if (ret & WM831X_PWR_SRC_BATT) {
-               *status = POWER_SUPPLY_STATUS_DISCHARGING;
-               return 0;
-       }
-
-       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
-       if (ret < 0)
-               return ret;
-
-       switch (ret & WM831X_CHG_STATE_MASK) {
-       case WM831X_CHG_STATE_OFF:
-               *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               break;
-       case WM831X_CHG_STATE_TRICKLE:
-       case WM831X_CHG_STATE_FAST:
-               *status = POWER_SUPPLY_STATUS_CHARGING;
-               break;
-
-       default:
-               *status = POWER_SUPPLY_STATUS_UNKNOWN;
-               break;
-       }
-
-       return 0;
-}
-
-static int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
-{
-       int ret;
-
-       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
-       if (ret < 0)
-               return ret;
-
-       switch (ret & WM831X_CHG_STATE_MASK) {
-       case WM831X_CHG_STATE_TRICKLE:
-       case WM831X_CHG_STATE_TRICKLE_OT:
-               *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-               break;
-       case WM831X_CHG_STATE_FAST:
-       case WM831X_CHG_STATE_FAST_OT:
-               *type = POWER_SUPPLY_CHARGE_TYPE_FAST;
-               break;
-       default:
-               *type = POWER_SUPPLY_CHARGE_TYPE_NONE;
-               break;
-       }
-
-       return 0;
-}
-
-static int wm831x_bat_check_health(struct wm831x *wm831x, int *health)
-{
-       int ret;
-
-       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
-       if (ret < 0)
-               return ret;
-
-       if (ret & WM831X_BATT_HOT_STS) {
-               *health = POWER_SUPPLY_HEALTH_OVERHEAT;
-               return 0;
-       }
-
-       if (ret & WM831X_BATT_COLD_STS) {
-               *health = POWER_SUPPLY_HEALTH_COLD;
-               return 0;
-       }
-
-       if (ret & WM831X_BATT_OV_STS) {
-               *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-               return 0;
-       }
-
-       switch (ret & WM831X_CHG_STATE_MASK) {
-       case WM831X_CHG_STATE_TRICKLE_OT:
-       case WM831X_CHG_STATE_FAST_OT:
-               *health = POWER_SUPPLY_HEALTH_OVERHEAT;
-               break;
-       case WM831X_CHG_STATE_DEFECTIVE:
-               *health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-               break;
-       default:
-               *health = POWER_SUPPLY_HEALTH_GOOD;
-               break;
-       }
-
-       return 0;
-}
-
-static int wm831x_bat_get_prop(struct power_supply *psy,
-                              enum power_supply_property psp,
-                              union power_supply_propval *val)
-{
-       struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev.parent);
-       struct wm831x *wm831x = wm831x_power->wm831x;
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = wm831x_bat_check_status(wm831x, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
-                                               val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = wm831x_bat_check_health(wm831x, &val->intval);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               ret = wm831x_bat_check_type(wm831x, &val->intval);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property wm831x_bat_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-};
-
-static const char *wm831x_bat_irqs[] = {
-       "BATT HOT",
-       "BATT COLD",
-       "BATT FAIL",
-       "OV",
-       "END",
-       "TO",
-       "MODE",
-       "START",
-};
-
-static irqreturn_t wm831x_bat_irq(int irq, void *data)
-{
-       struct wm831x_power *wm831x_power = data;
-       struct wm831x *wm831x = wm831x_power->wm831x;
-
-       dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
-
-       /* The battery charger is autonomous so we don't need to do
-        * anything except kick user space */
-       if (wm831x_power->have_battery)
-               power_supply_changed(wm831x_power->battery);
-
-       return IRQ_HANDLED;
-}
-
-
-/*********************************************************************
- *             Initialisation
- *********************************************************************/
-
-static irqreturn_t wm831x_syslo_irq(int irq, void *data)
-{
-       struct wm831x_power *wm831x_power = data;
-       struct wm831x *wm831x = wm831x_power->wm831x;
-
-       /* Not much we can actually *do* but tell people for
-        * posterity, we're probably about to run out of power. */
-       dev_crit(wm831x->dev, "SYSVDD under voltage\n");
-
-       return IRQ_HANDLED;
-}
-
-static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
-{
-       struct wm831x_power *wm831x_power = data;
-       struct wm831x *wm831x = wm831x_power->wm831x;
-
-       dev_dbg(wm831x->dev, "Power source changed\n");
-
-       /* Just notify for everything - little harm in overnotifying. */
-       if (wm831x_power->have_battery)
-               power_supply_changed(wm831x_power->battery);
-       power_supply_changed(wm831x_power->usb);
-       power_supply_changed(wm831x_power->wall);
-
-       return IRQ_HANDLED;
-}
-
-static int wm831x_power_probe(struct platform_device *pdev)
-{
-       struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
-       struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
-       struct wm831x_power *power;
-       int ret, irq, i;
-
-       power = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_power),
-                            GFP_KERNEL);
-       if (power == NULL)
-               return -ENOMEM;
-
-       power->wm831x = wm831x;
-       platform_set_drvdata(pdev, power);
-
-       if (wm831x_pdata && wm831x_pdata->wm831x_num) {
-               snprintf(power->wall_name, sizeof(power->wall_name),
-                        "wm831x-wall.%d", wm831x_pdata->wm831x_num);
-               snprintf(power->battery_name, sizeof(power->wall_name),
-                        "wm831x-battery.%d", wm831x_pdata->wm831x_num);
-               snprintf(power->usb_name, sizeof(power->wall_name),
-                        "wm831x-usb.%d", wm831x_pdata->wm831x_num);
-       } else {
-               snprintf(power->wall_name, sizeof(power->wall_name),
-                        "wm831x-wall");
-               snprintf(power->battery_name, sizeof(power->wall_name),
-                        "wm831x-battery");
-               snprintf(power->usb_name, sizeof(power->wall_name),
-                        "wm831x-usb");
-       }
-
-       /* We ignore configuration failures since we can still read back
-        * the status without enabling the charger.
-        */
-       wm831x_config_battery(wm831x);
-
-       power->wall_desc.name = power->wall_name;
-       power->wall_desc.type = POWER_SUPPLY_TYPE_MAINS;
-       power->wall_desc.properties = wm831x_wall_props;
-       power->wall_desc.num_properties = ARRAY_SIZE(wm831x_wall_props);
-       power->wall_desc.get_property = wm831x_wall_get_prop;
-       power->wall = power_supply_register(&pdev->dev, &power->wall_desc,
-                                           NULL);
-       if (IS_ERR(power->wall)) {
-               ret = PTR_ERR(power->wall);
-               goto err;
-       }
-
-       power->usb_desc.name = power->usb_name,
-       power->usb_desc.type = POWER_SUPPLY_TYPE_USB;
-       power->usb_desc.properties = wm831x_usb_props;
-       power->usb_desc.num_properties = ARRAY_SIZE(wm831x_usb_props);
-       power->usb_desc.get_property = wm831x_usb_get_prop;
-       power->usb = power_supply_register(&pdev->dev, &power->usb_desc, NULL);
-       if (IS_ERR(power->usb)) {
-               ret = PTR_ERR(power->usb);
-               goto err_wall;
-       }
-
-       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
-       if (ret < 0)
-               goto err_wall;
-       power->have_battery = ret & WM831X_CHG_ENA;
-
-       if (power->have_battery) {
-               power->battery_desc.name = power->battery_name;
-               power->battery_desc.properties = wm831x_bat_props;
-               power->battery_desc.num_properties = ARRAY_SIZE(wm831x_bat_props);
-               power->battery_desc.get_property = wm831x_bat_get_prop;
-               power->battery_desc.use_for_apm = 1;
-               power->battery = power_supply_register(&pdev->dev,
-                                                      &power->battery_desc,
-                                                      NULL);
-               if (IS_ERR(power->battery)) {
-                       ret = PTR_ERR(power->battery);
-                       goto err_usb;
-               }
-       }
-
-       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
-       ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
-                                  IRQF_TRIGGER_RISING | IRQF_ONESHOT, "System power low",
-                                  power);
-       if (ret != 0) {
-               dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
-                       irq, ret);
-               goto err_battery;
-       }
-
-       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
-       ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
-                                  IRQF_TRIGGER_RISING | IRQF_ONESHOT, "Power source",
-                                  power);
-       if (ret != 0) {
-               dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
-                       irq, ret);
-               goto err_syslo;
-       }
-
-       for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
-               irq = wm831x_irq(wm831x,
-                                platform_get_irq_byname(pdev,
-                                                        wm831x_bat_irqs[i]));
-               ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
-                                          IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-                                          wm831x_bat_irqs[i],
-                                          power);
-               if (ret != 0) {
-                       dev_err(&pdev->dev,
-                               "Failed to request %s IRQ %d: %d\n",
-                               wm831x_bat_irqs[i], irq, ret);
-                       goto err_bat_irq;
-               }
-       }
-
-       return ret;
-
-err_bat_irq:
-       --i;
-       for (; i >= 0; i--) {
-               irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
-               free_irq(irq, power);
-       }
-       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
-       free_irq(irq, power);
-err_syslo:
-       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
-       free_irq(irq, power);
-err_battery:
-       if (power->have_battery)
-               power_supply_unregister(power->battery);
-err_usb:
-       power_supply_unregister(power->usb);
-err_wall:
-       power_supply_unregister(power->wall);
-err:
-       return ret;
-}
-
-static int wm831x_power_remove(struct platform_device *pdev)
-{
-       struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
-       struct wm831x *wm831x = wm831x_power->wm831x;
-       int irq, i;
-
-       for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
-               irq = wm831x_irq(wm831x, 
-                                platform_get_irq_byname(pdev,
-                                                        wm831x_bat_irqs[i]));
-               free_irq(irq, wm831x_power);
-       }
-
-       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
-       free_irq(irq, wm831x_power);
-
-       irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
-       free_irq(irq, wm831x_power);
-
-       if (wm831x_power->have_battery)
-               power_supply_unregister(wm831x_power->battery);
-       power_supply_unregister(wm831x_power->wall);
-       power_supply_unregister(wm831x_power->usb);
-       return 0;
-}
-
-static struct platform_driver wm831x_power_driver = {
-       .probe = wm831x_power_probe,
-       .remove = wm831x_power_remove,
-       .driver = {
-               .name = "wm831x-power",
-       },
-};
-
-module_platform_driver(wm831x_power_driver);
-
-MODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:wm831x-power");
diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c
deleted file mode 100644 (file)
index 5c58806..0000000
+++ /dev/null
@@ -1,540 +0,0 @@
-/*
- * Battery driver for wm8350 PMIC
- *
- * Copyright 2007, 2008 Wolfson Microelectronics PLC.
- *
- * Based on OLPC Battery Driver
- *
- * Copyright 2006  David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/mfd/wm8350/supply.h>
-#include <linux/mfd/wm8350/core.h>
-#include <linux/mfd/wm8350/comparator.h>
-
-static int wm8350_read_battery_uvolts(struct wm8350 *wm8350)
-{
-       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_BATT, 0, 0)
-               * WM8350_AUX_COEFF;
-}
-
-static int wm8350_read_line_uvolts(struct wm8350 *wm8350)
-{
-       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_LINE, 0, 0)
-               * WM8350_AUX_COEFF;
-}
-
-static int wm8350_read_usb_uvolts(struct wm8350 *wm8350)
-{
-       return wm8350_read_auxadc(wm8350, WM8350_AUXADC_USB, 0, 0)
-               * WM8350_AUX_COEFF;
-}
-
-#define WM8350_BATT_SUPPLY     1
-#define WM8350_USB_SUPPLY      2
-#define WM8350_LINE_SUPPLY     4
-
-static inline int wm8350_charge_time_min(struct wm8350 *wm8350, int min)
-{
-       if (!wm8350->power.rev_g_coeff)
-               return (((min - 30) / 15) & 0xf) << 8;
-       else
-               return (((min - 30) / 30) & 0xf) << 8;
-}
-
-static int wm8350_get_supplies(struct wm8350 *wm8350)
-{
-       u16 sm, ov, co, chrg;
-       int supplies = 0;
-
-       sm = wm8350_reg_read(wm8350, WM8350_STATE_MACHINE_STATUS);
-       ov = wm8350_reg_read(wm8350, WM8350_MISC_OVERRIDES);
-       co = wm8350_reg_read(wm8350, WM8350_COMPARATOR_OVERRIDES);
-       chrg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
-
-       /* USB_SM */
-       sm = (sm & WM8350_USB_SM_MASK) >> WM8350_USB_SM_SHIFT;
-
-       /* CHG_ISEL */
-       chrg &= WM8350_CHG_ISEL_MASK;
-
-       /* If the USB state machine is active then we're using that with or
-        * without battery, otherwise check for wall supply */
-       if (((sm == WM8350_USB_SM_100_SLV) ||
-            (sm == WM8350_USB_SM_500_SLV) ||
-            (sm == WM8350_USB_SM_STDBY_SLV))
-           && !(ov & WM8350_USB_LIMIT_OVRDE))
-               supplies = WM8350_USB_SUPPLY;
-       else if (((sm == WM8350_USB_SM_100_SLV) ||
-                 (sm == WM8350_USB_SM_500_SLV) ||
-                 (sm == WM8350_USB_SM_STDBY_SLV))
-                && (ov & WM8350_USB_LIMIT_OVRDE) && (chrg == 0))
-               supplies = WM8350_USB_SUPPLY | WM8350_BATT_SUPPLY;
-       else if (co & WM8350_WALL_FB_OVRDE)
-               supplies = WM8350_LINE_SUPPLY;
-       else
-               supplies = WM8350_BATT_SUPPLY;
-
-       return supplies;
-}
-
-static int wm8350_charger_config(struct wm8350 *wm8350,
-                                struct wm8350_charger_policy *policy)
-{
-       u16 reg, eoc_mA, fast_limit_mA;
-
-       if (!policy) {
-               dev_warn(wm8350->dev,
-                        "No charger policy, charger not configured.\n");
-               return -EINVAL;
-       }
-
-       /* make sure USB fast charge current is not > 500mA */
-       if (policy->fast_limit_USB_mA > 500) {
-               dev_err(wm8350->dev, "USB fast charge > 500mA\n");
-               return -EINVAL;
-       }
-
-       eoc_mA = WM8350_CHG_EOC_mA(policy->eoc_mA);
-
-       wm8350_reg_unlock(wm8350);
-
-       reg = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1)
-               & WM8350_CHG_ENA_R168;
-       wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
-                        reg | eoc_mA | policy->trickle_start_mV |
-                        WM8350_CHG_TRICKLE_TEMP_CHOKE |
-                        WM8350_CHG_TRICKLE_USB_CHOKE |
-                        WM8350_CHG_FAST_USB_THROTTLE);
-
-       if (wm8350_get_supplies(wm8350) & WM8350_USB_SUPPLY) {
-               fast_limit_mA =
-                       WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_USB_mA);
-               wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
-                           policy->charge_mV | policy->trickle_charge_USB_mA |
-                           fast_limit_mA | wm8350_charge_time_min(wm8350,
-                                               policy->charge_timeout));
-
-       } else {
-               fast_limit_mA =
-                       WM8350_CHG_FAST_LIMIT_mA(policy->fast_limit_mA);
-               wm8350_reg_write(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2,
-                           policy->charge_mV | policy->trickle_charge_mA |
-                           fast_limit_mA | wm8350_charge_time_min(wm8350,
-                                               policy->charge_timeout));
-       }
-
-       wm8350_reg_lock(wm8350);
-       return 0;
-}
-
-static int wm8350_batt_status(struct wm8350 *wm8350)
-{
-       u16 state;
-
-       state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2);
-       state &= WM8350_CHG_STS_MASK;
-
-       switch (state) {
-       case WM8350_CHG_STS_OFF:
-               return POWER_SUPPLY_STATUS_DISCHARGING;
-
-       case WM8350_CHG_STS_TRICKLE:
-       case WM8350_CHG_STS_FAST:
-               return POWER_SUPPLY_STATUS_CHARGING;
-
-       default:
-               return POWER_SUPPLY_STATUS_UNKNOWN;
-       }
-}
-
-static ssize_t charger_state_show(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       struct wm8350 *wm8350 = dev_get_drvdata(dev);
-       char *charge;
-       int state;
-
-       state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &
-           WM8350_CHG_STS_MASK;
-       switch (state) {
-       case WM8350_CHG_STS_OFF:
-               charge = "Charger Off";
-               break;
-       case WM8350_CHG_STS_TRICKLE:
-               charge = "Trickle Charging";
-               break;
-       case WM8350_CHG_STS_FAST:
-               charge = "Fast Charging";
-               break;
-       default:
-               return 0;
-       }
-
-       return sprintf(buf, "%s\n", charge);
-}
-
-static DEVICE_ATTR(charger_state, 0444, charger_state_show, NULL);
-
-static irqreturn_t wm8350_charger_handler(int irq, void *data)
-{
-       struct wm8350 *wm8350 = data;
-       struct wm8350_power *power = &wm8350->power;
-       struct wm8350_charger_policy *policy = power->policy;
-
-       switch (irq - wm8350->irq_base) {
-       case WM8350_IRQ_CHG_BAT_FAIL:
-               dev_err(wm8350->dev, "battery failed\n");
-               break;
-       case WM8350_IRQ_CHG_TO:
-               dev_err(wm8350->dev, "charger timeout\n");
-               power_supply_changed(power->battery);
-               break;
-
-       case WM8350_IRQ_CHG_BAT_HOT:
-       case WM8350_IRQ_CHG_BAT_COLD:
-       case WM8350_IRQ_CHG_START:
-       case WM8350_IRQ_CHG_END:
-               power_supply_changed(power->battery);
-               break;
-
-       case WM8350_IRQ_CHG_FAST_RDY:
-               dev_dbg(wm8350->dev, "fast charger ready\n");
-               wm8350_charger_config(wm8350, policy);
-               wm8350_reg_unlock(wm8350);
-               wm8350_set_bits(wm8350, WM8350_BATTERY_CHARGER_CONTROL_1,
-                               WM8350_CHG_FAST);
-               wm8350_reg_lock(wm8350);
-               break;
-
-       case WM8350_IRQ_CHG_VBATT_LT_3P9:
-               dev_warn(wm8350->dev, "battery < 3.9V\n");
-               break;
-       case WM8350_IRQ_CHG_VBATT_LT_3P1:
-               dev_warn(wm8350->dev, "battery < 3.1V\n");
-               break;
-       case WM8350_IRQ_CHG_VBATT_LT_2P85:
-               dev_warn(wm8350->dev, "battery < 2.85V\n");
-               break;
-
-               /* Supply change.  We will overnotify but it should do
-                * no harm. */
-       case WM8350_IRQ_EXT_USB_FB:
-       case WM8350_IRQ_EXT_WALL_FB:
-               wm8350_charger_config(wm8350, policy);
-       case WM8350_IRQ_EXT_BAT_FB:   /* Fall through */
-               power_supply_changed(power->battery);
-               power_supply_changed(power->usb);
-               power_supply_changed(power->ac);
-               break;
-
-       default:
-               dev_err(wm8350->dev, "Unknown interrupt %d\n", irq);
-       }
-
-       return IRQ_HANDLED;
-}
-
-/*********************************************************************
- *             AC Power
- *********************************************************************/
-static int wm8350_ac_get_prop(struct power_supply *psy,
-                             enum power_supply_property psp,
-                             union power_supply_propval *val)
-{
-       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = !!(wm8350_get_supplies(wm8350) &
-                                WM8350_LINE_SUPPLY);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = wm8350_read_line_uvolts(wm8350);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static enum power_supply_property wm8350_ac_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-};
-
-/*********************************************************************
- *             USB Power
- *********************************************************************/
-static int wm8350_usb_get_prop(struct power_supply *psy,
-                              enum power_supply_property psp,
-                              union power_supply_propval *val)
-{
-       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = !!(wm8350_get_supplies(wm8350) &
-                                WM8350_USB_SUPPLY);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = wm8350_read_usb_uvolts(wm8350);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-       return ret;
-}
-
-static enum power_supply_property wm8350_usb_props[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-};
-
-/*********************************************************************
- *             Battery properties
- *********************************************************************/
-
-static int wm8350_bat_check_health(struct wm8350 *wm8350)
-{
-       u16 reg;
-
-       if (wm8350_read_battery_uvolts(wm8350) < 2850000)
-               return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
-
-       reg = wm8350_reg_read(wm8350, WM8350_CHARGER_OVERRIDES);
-       if (reg & WM8350_CHG_BATT_HOT_OVRDE)
-               return POWER_SUPPLY_HEALTH_OVERHEAT;
-
-       if (reg & WM8350_CHG_BATT_COLD_OVRDE)
-               return POWER_SUPPLY_HEALTH_COLD;
-
-       return POWER_SUPPLY_HEALTH_GOOD;
-}
-
-static int wm8350_bat_get_charge_type(struct wm8350 *wm8350)
-{
-       int state;
-
-       state = wm8350_reg_read(wm8350, WM8350_BATTERY_CHARGER_CONTROL_2) &
-           WM8350_CHG_STS_MASK;
-       switch (state) {
-       case WM8350_CHG_STS_OFF:
-               return POWER_SUPPLY_CHARGE_TYPE_NONE;
-       case WM8350_CHG_STS_TRICKLE:
-               return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-       case WM8350_CHG_STS_FAST:
-               return POWER_SUPPLY_CHARGE_TYPE_FAST;
-       default:
-               return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
-       }
-}
-
-static int wm8350_bat_get_property(struct power_supply *psy,
-                                  enum power_supply_property psp,
-                                  union power_supply_propval *val)
-{
-       struct wm8350 *wm8350 = dev_get_drvdata(psy->dev.parent);
-       int ret = 0;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = wm8350_batt_status(wm8350);
-               break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               val->intval = !!(wm8350_get_supplies(wm8350) &
-                                WM8350_BATT_SUPPLY);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = wm8350_read_battery_uvolts(wm8350);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               val->intval = wm8350_bat_check_health(wm8350);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_TYPE:
-               val->intval = wm8350_bat_get_charge_type(wm8350);
-               break;
-       default:
-               ret = -EINVAL;
-               break;
-       }
-
-       return ret;
-}
-
-static enum power_supply_property wm8350_bat_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_CHARGE_TYPE,
-};
-
-static const struct power_supply_desc wm8350_ac_desc = {
-       .name           = "wm8350-ac",
-       .type           = POWER_SUPPLY_TYPE_MAINS,
-       .properties     = wm8350_ac_props,
-       .num_properties = ARRAY_SIZE(wm8350_ac_props),
-       .get_property   = wm8350_ac_get_prop,
-};
-
-static const struct power_supply_desc wm8350_battery_desc = {
-       .name           = "wm8350-battery",
-       .properties     = wm8350_bat_props,
-       .num_properties = ARRAY_SIZE(wm8350_bat_props),
-       .get_property   = wm8350_bat_get_property,
-       .use_for_apm    = 1,
-};
-
-static const struct power_supply_desc wm8350_usb_desc = {
-       .name           = "wm8350-usb",
-       .type           = POWER_SUPPLY_TYPE_USB,
-       .properties     = wm8350_usb_props,
-       .num_properties = ARRAY_SIZE(wm8350_usb_props),
-       .get_property   = wm8350_usb_get_prop,
-};
-
-/*********************************************************************
- *             Initialisation
- *********************************************************************/
-
-static void wm8350_init_charger(struct wm8350 *wm8350)
-{
-       /* register our interest in charger events */
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT,
-                           wm8350_charger_handler, 0, "Battery hot", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD,
-                           wm8350_charger_handler, 0, "Battery cold", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL,
-                           wm8350_charger_handler, 0, "Battery fail", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_TO,
-                           wm8350_charger_handler, 0,
-                           "Charger timeout", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_END,
-                           wm8350_charger_handler, 0,
-                           "Charge end", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_START,
-                           wm8350_charger_handler, 0,
-                           "Charge start", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_FAST_RDY,
-                           wm8350_charger_handler, 0,
-                           "Fast charge ready", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9,
-                           wm8350_charger_handler, 0,
-                           "Battery <3.9V", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1,
-                           wm8350_charger_handler, 0,
-                           "Battery <3.1V", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85,
-                           wm8350_charger_handler, 0,
-                           "Battery <2.85V", wm8350);
-
-       /* and supply change events */
-       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_USB_FB,
-                           wm8350_charger_handler, 0, "USB", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_WALL_FB,
-                           wm8350_charger_handler, 0, "Wall", wm8350);
-       wm8350_register_irq(wm8350, WM8350_IRQ_EXT_BAT_FB,
-                           wm8350_charger_handler, 0, "Battery", wm8350);
-}
-
-static void free_charger_irq(struct wm8350 *wm8350)
-{
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350);
-       wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, wm8350);
-}
-
-static int wm8350_power_probe(struct platform_device *pdev)
-{
-       struct wm8350 *wm8350 = platform_get_drvdata(pdev);
-       struct wm8350_power *power = &wm8350->power;
-       struct wm8350_charger_policy *policy = power->policy;
-       int ret;
-
-       power->ac = power_supply_register(&pdev->dev, &wm8350_ac_desc, NULL);
-       if (IS_ERR(power->ac))
-               return PTR_ERR(power->ac);
-
-       power->battery = power_supply_register(&pdev->dev, &wm8350_battery_desc,
-                                              NULL);
-       if (IS_ERR(power->battery)) {
-               ret = PTR_ERR(power->battery);
-               goto battery_failed;
-       }
-
-       power->usb = power_supply_register(&pdev->dev, &wm8350_usb_desc, NULL);
-       if (IS_ERR(power->usb)) {
-               ret = PTR_ERR(power->usb);
-               goto usb_failed;
-       }
-
-       ret = device_create_file(&pdev->dev, &dev_attr_charger_state);
-       if (ret < 0)
-               dev_warn(wm8350->dev, "failed to add charge sysfs: %d\n", ret);
-       ret = 0;
-
-       wm8350_init_charger(wm8350);
-       if (wm8350_charger_config(wm8350, policy) == 0) {
-               wm8350_reg_unlock(wm8350);
-               wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CHG_ENA);
-               wm8350_reg_lock(wm8350);
-       }
-
-       return ret;
-
-usb_failed:
-       power_supply_unregister(power->battery);
-battery_failed:
-       power_supply_unregister(power->ac);
-
-       return ret;
-}
-
-static int wm8350_power_remove(struct platform_device *pdev)
-{
-       struct wm8350 *wm8350 = platform_get_drvdata(pdev);
-       struct wm8350_power *power = &wm8350->power;
-
-       free_charger_irq(wm8350);
-       device_remove_file(&pdev->dev, &dev_attr_charger_state);
-       power_supply_unregister(power->battery);
-       power_supply_unregister(power->ac);
-       power_supply_unregister(power->usb);
-       return 0;
-}
-
-static struct platform_driver wm8350_power_driver = {
-       .probe = wm8350_power_probe,
-       .remove = wm8350_power_remove,
-       .driver = {
-               .name = "wm8350-power",
-       },
-};
-
-module_platform_driver(wm8350_power_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Power supply driver for WM8350");
-MODULE_ALIAS("platform:wm8350-power");
diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c
deleted file mode 100644 (file)
index c2f09ed..0000000
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * linux/drivers/power/wm97xx_battery.c
- *
- * Battery measurement code for WM97xx
- *
- * based on tosa_battery.c
- *
- * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/wm97xx.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <linux/irq.h>
-#include <linux/slab.h>
-
-static struct work_struct bat_work;
-static DEFINE_MUTEX(work_lock);
-static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
-static enum power_supply_property *prop;
-
-static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
-{
-       struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
-       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
-
-       return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent),
-                                       pdata->batt_aux) * pdata->batt_mult /
-                                       pdata->batt_div;
-}
-
-static unsigned long wm97xx_read_temp(struct power_supply *bat_ps)
-{
-       struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
-       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
-
-       return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent),
-                                       pdata->temp_aux) * pdata->temp_mult /
-                                       pdata->temp_div;
-}
-
-static int wm97xx_bat_get_property(struct power_supply *bat_ps,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
-       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = bat_status;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = pdata->batt_tech;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (pdata->batt_aux >= 0)
-                       val->intval = wm97xx_read_bat(bat_ps);
-               else
-                       return -EINVAL;
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               if (pdata->temp_aux >= 0)
-                       val->intval = wm97xx_read_temp(bat_ps);
-               else
-                       return -EINVAL;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               if (pdata->max_voltage >= 0)
-                       val->intval = pdata->max_voltage;
-               else
-                       return -EINVAL;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
-               if (pdata->min_voltage >= 0)
-                       val->intval = pdata->min_voltage;
-               else
-                       return -EINVAL;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = 1;
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps)
-{
-       schedule_work(&bat_work);
-}
-
-static void wm97xx_bat_update(struct power_supply *bat_ps)
-{
-       int old_status = bat_status;
-       struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
-       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
-
-       mutex_lock(&work_lock);
-
-       bat_status = (pdata->charge_gpio >= 0) ?
-                       (gpio_get_value(pdata->charge_gpio) ?
-                       POWER_SUPPLY_STATUS_DISCHARGING :
-                       POWER_SUPPLY_STATUS_CHARGING) :
-                       POWER_SUPPLY_STATUS_UNKNOWN;
-
-       if (old_status != bat_status) {
-               pr_debug("%s: %i -> %i\n", bat_ps->desc->name, old_status,
-                                       bat_status);
-               power_supply_changed(bat_ps);
-       }
-
-       mutex_unlock(&work_lock);
-}
-
-static struct power_supply *bat_psy;
-static struct power_supply_desc bat_psy_desc = {
-       .type                   = POWER_SUPPLY_TYPE_BATTERY,
-       .get_property           = wm97xx_bat_get_property,
-       .external_power_changed = wm97xx_bat_external_power_changed,
-       .use_for_apm            = 1,
-};
-
-static void wm97xx_bat_work(struct work_struct *work)
-{
-       wm97xx_bat_update(bat_psy);
-}
-
-static irqreturn_t wm97xx_chrg_irq(int irq, void *data)
-{
-       schedule_work(&bat_work);
-       return IRQ_HANDLED;
-}
-
-#ifdef CONFIG_PM
-static int wm97xx_bat_suspend(struct device *dev)
-{
-       flush_work(&bat_work);
-       return 0;
-}
-
-static int wm97xx_bat_resume(struct device *dev)
-{
-       schedule_work(&bat_work);
-       return 0;
-}
-
-static const struct dev_pm_ops wm97xx_bat_pm_ops = {
-       .suspend        = wm97xx_bat_suspend,
-       .resume         = wm97xx_bat_resume,
-};
-#endif
-
-static int wm97xx_bat_probe(struct platform_device *dev)
-{
-       int ret = 0;
-       int props = 1;  /* POWER_SUPPLY_PROP_PRESENT */
-       int i = 0;
-       struct wm97xx_pdata *wmdata = dev->dev.platform_data;
-       struct wm97xx_batt_pdata *pdata;
-
-       if (!wmdata) {
-               dev_err(&dev->dev, "No platform data supplied\n");
-               return -EINVAL;
-       }
-
-       pdata = wmdata->batt_pdata;
-
-       if (dev->id != -1)
-               return -EINVAL;
-
-       if (!pdata) {
-               dev_err(&dev->dev, "No platform_data supplied\n");
-               return -EINVAL;
-       }
-
-       if (gpio_is_valid(pdata->charge_gpio)) {
-               ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
-               if (ret)
-                       goto err;
-               ret = gpio_direction_input(pdata->charge_gpio);
-               if (ret)
-                       goto err2;
-               ret = request_irq(gpio_to_irq(pdata->charge_gpio),
-                               wm97xx_chrg_irq, 0,
-                               "AC Detect", dev);
-               if (ret)
-                       goto err2;
-               props++;        /* POWER_SUPPLY_PROP_STATUS */
-       }
-
-       if (pdata->batt_tech >= 0)
-               props++;        /* POWER_SUPPLY_PROP_TECHNOLOGY */
-       if (pdata->temp_aux >= 0)
-               props++;        /* POWER_SUPPLY_PROP_TEMP */
-       if (pdata->batt_aux >= 0)
-               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
-       if (pdata->max_voltage >= 0)
-               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
-       if (pdata->min_voltage >= 0)
-               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
-
-       prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
-       if (!prop) {
-               ret = -ENOMEM;
-               goto err3;
-       }
-
-       prop[i++] = POWER_SUPPLY_PROP_PRESENT;
-       if (pdata->charge_gpio >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_STATUS;
-       if (pdata->batt_tech >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
-       if (pdata->temp_aux >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_TEMP;
-       if (pdata->batt_aux >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
-       if (pdata->max_voltage >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
-       if (pdata->min_voltage >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
-
-       INIT_WORK(&bat_work, wm97xx_bat_work);
-
-       if (!pdata->batt_name) {
-               dev_info(&dev->dev, "Please consider setting proper battery "
-                               "name in platform definition file, falling "
-                               "back to name \"wm97xx-batt\"\n");
-               bat_psy_desc.name = "wm97xx-batt";
-       } else
-               bat_psy_desc.name = pdata->batt_name;
-
-       bat_psy_desc.properties = prop;
-       bat_psy_desc.num_properties = props;
-
-       bat_psy = power_supply_register(&dev->dev, &bat_psy_desc, NULL);
-       if (!IS_ERR(bat_psy)) {
-               schedule_work(&bat_work);
-       } else {
-               ret = PTR_ERR(bat_psy);
-               goto err4;
-       }
-
-       return 0;
-err4:
-       kfree(prop);
-err3:
-       if (gpio_is_valid(pdata->charge_gpio))
-               free_irq(gpio_to_irq(pdata->charge_gpio), dev);
-err2:
-       if (gpio_is_valid(pdata->charge_gpio))
-               gpio_free(pdata->charge_gpio);
-err:
-       return ret;
-}
-
-static int wm97xx_bat_remove(struct platform_device *dev)
-{
-       struct wm97xx_pdata *wmdata = dev->dev.platform_data;
-       struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
-
-       if (pdata && gpio_is_valid(pdata->charge_gpio)) {
-               free_irq(gpio_to_irq(pdata->charge_gpio), dev);
-               gpio_free(pdata->charge_gpio);
-       }
-       cancel_work_sync(&bat_work);
-       power_supply_unregister(bat_psy);
-       kfree(prop);
-       return 0;
-}
-
-static struct platform_driver wm97xx_bat_driver = {
-       .driver = {
-               .name   = "wm97xx-battery",
-#ifdef CONFIG_PM
-               .pm     = &wm97xx_bat_pm_ops,
-#endif
-       },
-       .probe          = wm97xx_bat_probe,
-       .remove         = wm97xx_bat_remove,
-};
-
-module_platform_driver(wm97xx_bat_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
-MODULE_DESCRIPTION("WM97xx battery driver");
diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c
deleted file mode 100644 (file)
index b201e3f..0000000
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Battery measurement code for Zipit Z2
- *
- * Copyright (C) 2009 Peter Edwards <sweetlilmre@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/z2_battery.h>
-
-#define        Z2_DEFAULT_NAME "Z2"
-
-struct z2_charger {
-       struct z2_battery_info          *info;
-       int                             bat_status;
-       struct i2c_client               *client;
-       struct power_supply             *batt_ps;
-       struct power_supply_desc        batt_ps_desc;
-       struct mutex                    work_lock;
-       struct work_struct              bat_work;
-};
-
-static unsigned long z2_read_bat(struct z2_charger *charger)
-{
-       int data;
-       data = i2c_smbus_read_byte_data(charger->client,
-                                       charger->info->batt_I2C_reg);
-       if (data < 0)
-               return 0;
-
-       return data * charger->info->batt_mult / charger->info->batt_div;
-}
-
-static int z2_batt_get_property(struct power_supply *batt_ps,
-                           enum power_supply_property psp,
-                           union power_supply_propval *val)
-{
-       struct z2_charger *charger = power_supply_get_drvdata(batt_ps);
-       struct z2_battery_info *info = charger->info;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               val->intval = charger->bat_status;
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = info->batt_tech;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               if (info->batt_I2C_reg >= 0)
-                       val->intval = z2_read_bat(charger);
-               else
-                       return -EINVAL;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               if (info->max_voltage >= 0)
-                       val->intval = info->max_voltage;
-               else
-                       return -EINVAL;
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
-               if (info->min_voltage >= 0)
-                       val->intval = info->min_voltage;
-               else
-                       return -EINVAL;
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = 1;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static void z2_batt_ext_power_changed(struct power_supply *batt_ps)
-{
-       struct z2_charger *charger = power_supply_get_drvdata(batt_ps);
-
-       schedule_work(&charger->bat_work);
-}
-
-static void z2_batt_update(struct z2_charger *charger)
-{
-       int old_status = charger->bat_status;
-       struct z2_battery_info *info;
-
-       info = charger->info;
-
-       mutex_lock(&charger->work_lock);
-
-       charger->bat_status = (info->charge_gpio >= 0) ?
-               (gpio_get_value(info->charge_gpio) ?
-               POWER_SUPPLY_STATUS_CHARGING :
-               POWER_SUPPLY_STATUS_DISCHARGING) :
-               POWER_SUPPLY_STATUS_UNKNOWN;
-
-       if (old_status != charger->bat_status) {
-               pr_debug("%s: %i -> %i\n", charger->batt_ps->desc->name,
-                               old_status,
-                               charger->bat_status);
-               power_supply_changed(charger->batt_ps);
-       }
-
-       mutex_unlock(&charger->work_lock);
-}
-
-static void z2_batt_work(struct work_struct *work)
-{
-       struct z2_charger *charger;
-       charger = container_of(work, struct z2_charger, bat_work);
-       z2_batt_update(charger);
-}
-
-static irqreturn_t z2_charge_switch_irq(int irq, void *devid)
-{
-       struct z2_charger *charger = devid;
-       schedule_work(&charger->bat_work);
-       return IRQ_HANDLED;
-}
-
-static int z2_batt_ps_init(struct z2_charger *charger, int props)
-{
-       int i = 0;
-       enum power_supply_property *prop;
-       struct z2_battery_info *info = charger->info;
-
-       if (info->charge_gpio >= 0)
-               props++;        /* POWER_SUPPLY_PROP_STATUS */
-       if (info->batt_tech >= 0)
-               props++;        /* POWER_SUPPLY_PROP_TECHNOLOGY */
-       if (info->batt_I2C_reg >= 0)
-               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
-       if (info->max_voltage >= 0)
-               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
-       if (info->min_voltage >= 0)
-               props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
-
-       prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
-       if (!prop)
-               return -ENOMEM;
-
-       prop[i++] = POWER_SUPPLY_PROP_PRESENT;
-       if (info->charge_gpio >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_STATUS;
-       if (info->batt_tech >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
-       if (info->batt_I2C_reg >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
-       if (info->max_voltage >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
-       if (info->min_voltage >= 0)
-               prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
-
-       if (!info->batt_name) {
-               dev_info(&charger->client->dev,
-                               "Please consider setting proper battery "
-                               "name in platform definition file, falling "
-                               "back to name \" Z2_DEFAULT_NAME \"\n");
-               charger->batt_ps_desc.name = Z2_DEFAULT_NAME;
-       } else
-               charger->batt_ps_desc.name = info->batt_name;
-
-       charger->batt_ps_desc.properties        = prop;
-       charger->batt_ps_desc.num_properties    = props;
-       charger->batt_ps_desc.type              = POWER_SUPPLY_TYPE_BATTERY;
-       charger->batt_ps_desc.get_property      = z2_batt_get_property;
-       charger->batt_ps_desc.external_power_changed =
-                                               z2_batt_ext_power_changed;
-       charger->batt_ps_desc.use_for_apm       = 1;
-
-       return 0;
-}
-
-static int z2_batt_probe(struct i2c_client *client,
-                               const struct i2c_device_id *id)
-{
-       int ret = 0;
-       int props = 1;  /* POWER_SUPPLY_PROP_PRESENT */
-       struct z2_charger *charger;
-       struct z2_battery_info *info = client->dev.platform_data;
-       struct power_supply_config psy_cfg = {};
-
-       if (info == NULL) {
-               dev_err(&client->dev,
-                       "Please set platform device platform_data"
-                       " to a valid z2_battery_info pointer!\n");
-               return -EINVAL;
-       }
-
-       charger = kzalloc(sizeof(*charger), GFP_KERNEL);
-       if (charger == NULL)
-               return -ENOMEM;
-
-       charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
-       charger->info = info;
-       charger->client = client;
-       i2c_set_clientdata(client, charger);
-       psy_cfg.drv_data = charger;
-
-       mutex_init(&charger->work_lock);
-
-       if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
-               ret = gpio_request(info->charge_gpio, "BATT CHRG");
-               if (ret)
-                       goto err;
-
-               ret = gpio_direction_input(info->charge_gpio);
-               if (ret)
-                       goto err2;
-
-               irq_set_irq_type(gpio_to_irq(info->charge_gpio),
-                                IRQ_TYPE_EDGE_BOTH);
-               ret = request_irq(gpio_to_irq(info->charge_gpio),
-                               z2_charge_switch_irq, 0,
-                               "AC Detect", charger);
-               if (ret)
-                       goto err3;
-       }
-
-       ret = z2_batt_ps_init(charger, props);
-       if (ret)
-               goto err3;
-
-       INIT_WORK(&charger->bat_work, z2_batt_work);
-
-       charger->batt_ps = power_supply_register(&client->dev,
-                                                &charger->batt_ps_desc,
-                                                &psy_cfg);
-       if (IS_ERR(charger->batt_ps)) {
-               ret = PTR_ERR(charger->batt_ps);
-               goto err4;
-       }
-
-       schedule_work(&charger->bat_work);
-
-       return 0;
-
-err4:
-       kfree(charger->batt_ps_desc.properties);
-err3:
-       if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
-               free_irq(gpio_to_irq(info->charge_gpio), charger);
-err2:
-       if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
-               gpio_free(info->charge_gpio);
-err:
-       kfree(charger);
-       return ret;
-}
-
-static int z2_batt_remove(struct i2c_client *client)
-{
-       struct z2_charger *charger = i2c_get_clientdata(client);
-       struct z2_battery_info *info = charger->info;
-
-       cancel_work_sync(&charger->bat_work);
-       power_supply_unregister(charger->batt_ps);
-
-       kfree(charger->batt_ps_desc.properties);
-       if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
-               free_irq(gpio_to_irq(info->charge_gpio), charger);
-               gpio_free(info->charge_gpio);
-       }
-
-       kfree(charger);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int z2_batt_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct z2_charger *charger = i2c_get_clientdata(client);
-
-       flush_work(&charger->bat_work);
-       return 0;
-}
-
-static int z2_batt_resume(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct z2_charger *charger = i2c_get_clientdata(client);
-
-       schedule_work(&charger->bat_work);
-       return 0;
-}
-
-static const struct dev_pm_ops z2_battery_pm_ops = {
-       .suspend        = z2_batt_suspend,
-       .resume         = z2_batt_resume,
-};
-
-#define        Z2_BATTERY_PM_OPS       (&z2_battery_pm_ops)
-
-#else
-#define        Z2_BATTERY_PM_OPS       (NULL)
-#endif
-
-static const struct i2c_device_id z2_batt_id[] = {
-       { "aer915", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, z2_batt_id);
-
-static struct i2c_driver z2_batt_driver = {
-       .driver = {
-               .name   = "z2-battery",
-               .owner  = THIS_MODULE,
-               .pm     = Z2_BATTERY_PM_OPS
-       },
-       .probe          = z2_batt_probe,
-       .remove         = z2_batt_remove,
-       .id_table       = z2_batt_id,
-};
-module_i2c_driver(z2_batt_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
-MODULE_DESCRIPTION("Zipit Z2 battery driver");