]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge tag 'for-v3.10' of git://git.infradead.org/battery-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 30 Apr 2013 22:15:24 +0000 (15:15 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 30 Apr 2013 22:15:24 +0000 (15:15 -0700)
Pull battery updates from Anton Vorontsov:
 "Highlights:

   - OpenFirmware/DeviceTree support for the Power Supply core: the core
     now automatically populates supplied_from hierarchy from the device
     tree.  With these patches chargers and batteries can now lookup
     each other without the board files support shim.  Rhyland Klein at
     NVIDIA did the work

   - New ST-Ericsson ABX500 hwmon driver.  The driver is heavily using
     the AB85xx core and depends on some recent changes to it, so that
     is why the driver comes through the battery tree.  It has an
     appropriate ack from the hwmon maintainer (i.e.  Guenter Roeck).
     Martin Persson at ST-Ericsson and Hongbo Zhang at Linaro authored
     the driver

   - Final bits to sync AB85xx ST-Ericsson changes into mainline.  The
     changes touch mfd parts, but these were acked by the appropriate
     MFD maintainer (ie Samuel Ortiz).  Lee Jones at Linaro did most of
     the work and lead the submission process.

  Minor changes, but still worth mentioning:

   - Battery temperature reporting fix for Nokia N900 phones
   - Versatile Express poweroff driver moved into drivers/power/reset/
   - Tree-wide: use devm_kzalloc() where appropriate
   - Tree-wide: dev_pm_ops cleanups/fixes"

* tag 'for-v3.10' of git://git.infradead.org/battery-2.6: (112 commits)
  pm2301-charger: Fix suspend/resume
  charger-manager: Use kmemdup instead of kzalloc + memcpy
  power_supply: Populate supplied_from hierarchy from the device tree
  power_supply: Add core support for supplied_from
  power_supply: Define Binding for power-supplies
  rx51_battery: Fix reporting temperature
  hwmon: Add ST-Ericsson ABX500 hwmon driver
  ab8500_bmdata: Export abx500_res_to_temp tables for hwmon
  ab8500_{bmdata,fg}: Add const attributes to some data arrays
  ab8500_bmdata: Eliminate CamelCase warning of some variables
  ab8500_btemp: Make ab8500_btemp_get* interfaces public
  goldfish_battery: Use resource_size()
  lp8788-charger: Use PAGE_SIZE for the sysfs read operation
  max8925_power: Use devm_kzalloc()
  da9030_battery: Use devm_kzalloc()
  da9052-battery: Use devm_kzalloc()
  ds2760_battery: Use devm_kzalloc()
  ds2780_battery: Use devm_kzalloc()
  gpio-charger: Use devm_kzalloc()
  isp1704_charger: Use devm_kzalloc()
  ...

64 files changed:
Documentation/devicetree/bindings/power_supply/power_supply.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power_supply/tps65090.txt [new file with mode: 0644]
Documentation/hwmon/ab8500 [new file with mode: 0644]
Documentation/hwmon/abx500 [new file with mode: 0644]
arch/arm/mach-vexpress/Kconfig
arch/arm/mach-vexpress/Makefile
arch/arm/mach-vexpress/v2m.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/ab8500.c [new file with mode: 0644]
drivers/hwmon/abx500.c [new file with mode: 0644]
drivers/hwmon/abx500.h [new file with mode: 0644]
drivers/mfd/ab8500-core.c
drivers/mfd/ab8500-debugfs.c
drivers/mfd/ab8500-gpadc.c
drivers/mfd/ab8500-sysctrl.c
drivers/power/88pm860x_charger.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/ab8500_bmdata.c
drivers/power/ab8500_btemp.c
drivers/power/ab8500_charger.c
drivers/power/ab8500_fg.c
drivers/power/abx500_chargalg.c
drivers/power/charger-manager.c
drivers/power/da9030_battery.c
drivers/power/da9052-battery.c
drivers/power/ds2760_battery.c
drivers/power/ds2780_battery.c
drivers/power/ds2782_battery.c
drivers/power/goldfish_battery.c
drivers/power/gpio-charger.c
drivers/power/isp1704_charger.c
drivers/power/lp8788-charger.c
drivers/power/max17040_battery.c
drivers/power/max8903_charger.c
drivers/power/max8925_power.c
drivers/power/max8997_charger.c
drivers/power/max8998_charger.c
drivers/power/pcf50633-charger.c
drivers/power/pm2301_charger.c
drivers/power/pm2301_charger.h
drivers/power/power_supply_core.c
drivers/power/reset/Kconfig
drivers/power/reset/Makefile
drivers/power/reset/vexpress-poweroff.c [moved from arch/arm/mach-vexpress/reset.c with 93% similarity]
drivers/power/rx51_battery.c
drivers/power/s3c_adc_battery.c
drivers/power/sbs-battery.c
drivers/power/test_power.c
drivers/power/tps65090-charger.c [new file with mode: 0644]
drivers/power/twl4030_charger.c
drivers/power/wm831x_backup.c
include/linux/mfd/abx500.h
include/linux/mfd/abx500/ab8500-bm.h
include/linux/mfd/abx500/ab8500-gpadc.h
include/linux/mfd/abx500/ab8500-sysctrl.h
include/linux/mfd/abx500/ab8500.h
include/linux/mfd/abx500/ux500_chargalg.h
include/linux/mfd/tps65090.h
include/linux/pm2301_charger.h
include/linux/power/ab8500.h [new file with mode: 0644]
include/linux/power_supply.h
include/linux/vexpress.h

diff --git a/Documentation/devicetree/bindings/power_supply/power_supply.txt b/Documentation/devicetree/bindings/power_supply/power_supply.txt
new file mode 100644 (file)
index 0000000..8391bfa
--- /dev/null
@@ -0,0 +1,23 @@
+Power Supply Core Support
+
+Optional Properties:
+ - power-supplies : This property is added to a supply in order to list the
+   devices which supply it power, referenced by their phandles.
+
+Example:
+
+       usb-charger: power@e {
+               compatible = "some,usb-charger";
+               ...
+       };
+
+       ac-charger: power@c {
+               compatible = "some,ac-charger";
+               ...
+       };
+
+       battery@b {
+               compatible = "some,battery";
+               ...
+               power-supplies = <&usb-charger>, <&ac-charger>;
+       };
diff --git a/Documentation/devicetree/bindings/power_supply/tps65090.txt b/Documentation/devicetree/bindings/power_supply/tps65090.txt
new file mode 100644 (file)
index 0000000..8e5e0d3
--- /dev/null
@@ -0,0 +1,17 @@
+TPS65090 Frontend PMU with Switchmode Charger
+
+Required Properties:
+-compatible: "ti,tps65090-charger"
+
+Optional Properties:
+-ti,enable-low-current-chrg: Enables charging when a low current is detected
+ while the default logic is to stop charging.
+
+This node is a subnode of the tps65090 PMIC.
+
+Example:
+
+       tps65090-charger {
+               compatible = "ti,tps65090-charger";
+               ti,enable-low-current-chrg;
+       };
diff --git a/Documentation/hwmon/ab8500 b/Documentation/hwmon/ab8500
new file mode 100644 (file)
index 0000000..cf169c8
--- /dev/null
@@ -0,0 +1,22 @@
+Kernel driver ab8500
+====================
+
+Supported chips:
+  * ST-Ericsson AB8500
+    Prefix: 'ab8500'
+    Addresses scanned: -
+    Datasheet: http://www.stericsson.com/developers/documentation.jsp
+
+Authors:
+        Martin Persson <martin.persson@stericsson.com>
+        Hongbo Zhang <hongbo.zhang@linaro.org>
+
+Description
+-----------
+
+See also Documentation/hwmon/abx500. This is the ST-Ericsson AB8500 specific
+driver.
+
+Currently only the AB8500 internal sensor and one external sensor for battery
+temperature are monitored. Other GPADC channels can also be monitored if needed
+in future.
diff --git a/Documentation/hwmon/abx500 b/Documentation/hwmon/abx500
new file mode 100644 (file)
index 0000000..319a058
--- /dev/null
@@ -0,0 +1,28 @@
+Kernel driver abx500
+====================
+
+Supported chips:
+  * ST-Ericsson ABx500 series
+    Prefix: 'abx500'
+    Addresses scanned: -
+    Datasheet: http://www.stericsson.com/developers/documentation.jsp
+
+Authors:
+        Martin Persson <martin.persson@stericsson.com>
+        Hongbo Zhang <hongbo.zhang@linaro.org>
+
+Description
+-----------
+
+Every ST-Ericsson Ux500 SOC consists of both ABx500 and DBx500 physically,
+this is kernel hwmon driver for ABx500.
+
+There are some GPADCs inside ABx500 which are designed for connecting to
+thermal sensors, and there is also a thermal sensor inside ABx500 too, which
+raises interrupt when critical temperature reached.
+
+This abx500 is a common layer which can monitor all of the sensors, every
+specific abx500 chip has its special configurations in its own file, e.g. some
+sensors can be configured invisible if they are not available on that chip, and
+the corresponding gpadc_addr should be set to 0, thus this sensor won't be
+polled.
index 52d315b792c8e8bb1561b4ae703194a724e865e0..0f1c5e53fb27b26a3353080a2cf551583a8379a8 100644 (file)
@@ -17,6 +17,9 @@ config ARCH_VEXPRESS
        select NO_IOPORT
        select PLAT_VERSATILE
        select PLAT_VERSATILE_CLCD
+       select POWER_RESET
+       select POWER_RESET_VEXPRESS
+       select POWER_SUPPLY
        select REGULATOR_FIXED_VOLTAGE if REGULATOR
        select VEXPRESS_CONFIG
        help
index 80b64971fbddbbf07b8c03eb626c773529db1b5e..42703e8b4d3bcdb674b99f4c01425e0dbcb44a21 100644 (file)
@@ -4,7 +4,7 @@
 ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
        -I$(srctree)/arch/arm/plat-versatile/include
 
-obj-y                                  := v2m.o reset.o
+obj-y                                  := v2m.o
 obj-$(CONFIG_ARCH_VEXPRESS_CA9X4)      += ct-ca9x4.o
 obj-$(CONFIG_SMP)                      += platsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)              += hotplug.o
index c5e20b52e3b780b98eafc8bc2c77f8573e21f7fd..eb2b3a627f03ce12b8a68f2564a39647820d44a0 100644 (file)
@@ -363,8 +363,6 @@ static void __init v2m_init(void)
        for (i = 0; i < ARRAY_SIZE(v2m_amba_devs); i++)
                amba_device_register(v2m_amba_devs[i], &iomem_resource);
 
-       pm_power_off = vexpress_power_off;
-
        ct_desc->init_tile();
 }
 
@@ -376,7 +374,6 @@ MACHINE_START(VEXPRESS, "ARM-Versatile Express")
        .init_irq       = v2m_init_irq,
        .init_time      = v2m_timer_init,
        .init_machine   = v2m_init,
-       .restart        = vexpress_restart,
 MACHINE_END
 
 static struct map_desc v2m_rs1_io_desc __initdata = {
@@ -470,7 +467,6 @@ static void __init v2m_dt_init(void)
 {
        l2x0_of_init(0x00400000, 0xfe0fffff);
        of_platform_populate(NULL, v2m_dt_bus_match, NULL, NULL);
-       pm_power_off = vexpress_power_off;
 }
 
 static const char * const v2m_dt_match[] __initconst = {
@@ -487,5 +483,4 @@ DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
        .init_irq       = irqchip_init,
        .init_time      = v2m_dt_timer_init,
        .init_machine   = v2m_dt_init,
-       .restart        = vexpress_restart,
 MACHINE_END
index f61d98a629677c68459c03dcb1770a343012e608..9c333d47143301291840441bdbc8fec57749d02a 100644 (file)
@@ -39,6 +39,19 @@ config HWMON_DEBUG_CHIP
 
 comment "Native drivers"
 
+config SENSORS_AB8500
+       tristate "AB8500 thermal monitoring"
+       depends on AB8500_GPADC && AB8500_BM
+       default n
+       help
+         If you say yes here you get support for the thermal sensor part
+         of the AB8500 chip. The driver includes thermal management for
+         AB8500 die and two GPADC channels. The GPADC channel are preferably
+         used to access sensors outside the AB8500 chip.
+
+         This driver can also be built as a module.  If so, the module
+         will be called abx500-temp.
+
 config SENSORS_ABITUGURU
        tristate "Abit uGuru (rev 1 & 2)"
        depends on X86 && DMI
index c51b0dc35dc810344cb615179ff87aaedd438654..d17d3e64f9f475d7b2661140d14995e6500aea2b 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_SENSORS_W83795)  += w83795.o
 obj-$(CONFIG_SENSORS_W83781D)  += w83781d.o
 obj-$(CONFIG_SENSORS_W83791D)  += w83791d.o
 
+obj-$(CONFIG_SENSORS_AB8500)   += abx500.o ab8500.o
 obj-$(CONFIG_SENSORS_ABITUGURU)        += abituguru.o
 obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
 obj-$(CONFIG_SENSORS_AD7314)   += ad7314.o
diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c
new file mode 100644 (file)
index 0000000..d844dc8
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) ST-Ericsson 2010 - 2013
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ *         Hongbo Zhang <hongbo.zhang@linaro.org>
+ * License Terms: GNU General Public License v2
+ *
+ * When the AB8500 thermal warning temperature is reached (threshold cannot
+ * be changed by SW), an interrupt is set, and if no further action is taken
+ * within a certain time frame, pm_power off will be called.
+ *
+ * When AB8500 thermal shutdown temperature is reached a hardware shutdown of
+ * the AB8500 will occur.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power/ab8500.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include "abx500.h"
+
+#define DEFAULT_POWER_OFF_DELAY        (HZ * 10)
+#define THERMAL_VCC            1800
+#define PULL_UP_RESISTOR       47000
+/* Number of monitored sensors should not greater than NUM_SENSORS */
+#define NUM_MONITORED_SENSORS  4
+
+struct ab8500_gpadc_cfg {
+       const struct abx500_res_to_temp *temp_tbl;
+       int tbl_sz;
+       int vcc;
+       int r_up;
+};
+
+struct ab8500_temp {
+       struct ab8500_gpadc *gpadc;
+       struct ab8500_btemp *btemp;
+       struct delayed_work power_off_work;
+       struct ab8500_gpadc_cfg cfg;
+       struct abx500_temp *abx500_data;
+};
+
+/*
+ * The hardware connection is like this:
+ * VCC----[ R_up ]-----[ NTC ]----GND
+ * where R_up is pull-up resistance, and GPADC measures voltage on NTC.
+ * and res_to_temp table is strictly sorted by falling resistance values.
+ */
+static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg,
+               int v_ntc, int *temp)
+{
+       int r_ntc, i = 0, tbl_sz = cfg->tbl_sz;
+       const struct abx500_res_to_temp *tbl = cfg->temp_tbl;
+
+       if (cfg->vcc < 0 || v_ntc >= cfg->vcc)
+               return -EINVAL;
+
+       r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc);
+       if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist)
+               return -EINVAL;
+
+       while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) &&
+                       i < tbl_sz - 2)
+               i++;
+
+       /* return milli-Celsius */
+       *temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 *
+               (r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
+
+       return 0;
+}
+
+static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp)
+{
+       int voltage, ret;
+       struct ab8500_temp *ab8500_data = data->plat_data;
+
+       if (sensor == BAT_CTRL) {
+               *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp);
+       } else if (sensor == BTEMP_BALL) {
+               *temp = ab8500_btemp_get_temp(ab8500_data->btemp);
+       } else {
+               voltage = ab8500_gpadc_convert(ab8500_data->gpadc, sensor);
+               if (voltage < 0)
+                       return voltage;
+
+               ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void ab8500_thermal_power_off(struct work_struct *work)
+{
+       struct ab8500_temp *ab8500_data = container_of(work,
+                               struct ab8500_temp, power_off_work.work);
+       struct abx500_temp *abx500_data = ab8500_data->abx500_data;
+
+       dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n");
+
+       pm_power_off();
+}
+
+static ssize_t ab8500_show_name(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       return sprintf(buf, "ab8500\n");
+}
+
+static ssize_t ab8500_show_label(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       char *label;
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int index = attr->index;
+
+       switch (index) {
+       case 1:
+               label = "ext_adc1";
+               break;
+       case 2:
+               label = "ext_adc2";
+               break;
+       case 3:
+               label = "bat_temp";
+               break;
+       case 4:
+               label = "bat_ctrl";
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return sprintf(buf, "%s\n", label);
+}
+
+static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data)
+{
+       struct ab8500_temp *ab8500_data = data->plat_data;
+
+       dev_warn(&data->pdev->dev, "Power off in %d s\n",
+                DEFAULT_POWER_OFF_DELAY / HZ);
+
+       schedule_delayed_work(&ab8500_data->power_off_work,
+               DEFAULT_POWER_OFF_DELAY);
+       return 0;
+}
+
+int abx500_hwmon_init(struct abx500_temp *data)
+{
+       struct ab8500_temp *ab8500_data;
+
+       ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data),
+               GFP_KERNEL);
+       if (!ab8500_data)
+               return -ENOMEM;
+
+       ab8500_data->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       if (IS_ERR(ab8500_data->gpadc))
+               return PTR_ERR(ab8500_data->gpadc);
+
+       ab8500_data->btemp = ab8500_btemp_get();
+       if (IS_ERR(ab8500_data->btemp))
+               return PTR_ERR(ab8500_data->btemp);
+
+       INIT_DELAYED_WORK(&ab8500_data->power_off_work,
+                         ab8500_thermal_power_off);
+
+       ab8500_data->cfg.vcc = THERMAL_VCC;
+       ab8500_data->cfg.r_up = PULL_UP_RESISTOR;
+       ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor;
+       ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size;
+
+       data->plat_data = ab8500_data;
+
+       /*
+        * ADC_AUX1 and ADC_AUX2, connected to external NTC
+        * BTEMP_BALL and BAT_CTRL, fixed usage
+        */
+       data->gpadc_addr[0] = ADC_AUX1;
+       data->gpadc_addr[1] = ADC_AUX2;
+       data->gpadc_addr[2] = BTEMP_BALL;
+       data->gpadc_addr[3] = BAT_CTRL;
+       data->monitored_sensors = NUM_MONITORED_SENSORS;
+
+       data->ops.read_sensor = ab8500_read_sensor;
+       data->ops.irq_handler = ab8500_temp_irq_handler;
+       data->ops.show_name = ab8500_show_name;
+       data->ops.show_label = ab8500_show_label;
+       data->ops.is_visible = NULL;
+
+       return 0;
+}
+EXPORT_SYMBOL(abx500_hwmon_init);
+
+MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@linaro.org>");
+MODULE_DESCRIPTION("AB8500 temperature driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c
new file mode 100644 (file)
index 0000000..b4ad87b
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) ST-Ericsson 2010 - 2013
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ *         Hongbo Zhang <hongbo.zhang@linaro.org>
+ * License Terms: GNU General Public License v2
+ *
+ * ABX500 does not provide auto ADC, so to monitor the required temperatures,
+ * a periodic work is used. It is more important to not wake up the CPU than
+ * to perform this job, hence the use of a deferred delay.
+ *
+ * A deferred delay for thermal monitor is considered safe because:
+ * If the chip gets too hot during a sleep state it's most likely due to
+ * external factors, such as the surrounding temperature. I.e. no SW decisions
+ * will make any difference.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include "abx500.h"
+
+#define DEFAULT_MONITOR_DELAY  HZ
+#define DEFAULT_MAX_TEMP       130
+
+static inline void schedule_monitor(struct abx500_temp *data)
+{
+       data->work_active = true;
+       schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY);
+}
+
+static void threshold_updated(struct abx500_temp *data)
+{
+       int i;
+       for (i = 0; i < data->monitored_sensors; i++)
+               if (data->max[i] != 0 || data->min[i] != 0) {
+                       schedule_monitor(data);
+                       return;
+               }
+
+       dev_dbg(&data->pdev->dev, "No active thresholds.\n");
+       cancel_delayed_work_sync(&data->work);
+       data->work_active = false;
+}
+
+static void gpadc_monitor(struct work_struct *work)
+{
+       int temp, i, ret;
+       char alarm_node[30];
+       bool updated_min_alarm, updated_max_alarm;
+       struct abx500_temp *data;
+
+       data = container_of(work, struct abx500_temp, work.work);
+       mutex_lock(&data->lock);
+
+       for (i = 0; i < data->monitored_sensors; i++) {
+               /* Thresholds are considered inactive if set to 0 */
+               if (data->max[i] == 0 && data->min[i] == 0)
+                       continue;
+
+               if (data->max[i] < data->min[i])
+                       continue;
+
+               ret = data->ops.read_sensor(data, data->gpadc_addr[i], &temp);
+               if (ret < 0) {
+                       dev_err(&data->pdev->dev, "GPADC read failed\n");
+                       continue;
+               }
+
+               updated_min_alarm = false;
+               updated_max_alarm = false;
+
+               if (data->min[i] != 0) {
+                       if (temp < data->min[i]) {
+                               if (data->min_alarm[i] == false) {
+                                       data->min_alarm[i] = true;
+                                       updated_min_alarm = true;
+                               }
+                       } else {
+                               if (data->min_alarm[i] == true) {
+                                       data->min_alarm[i] = false;
+                                       updated_min_alarm = true;
+                               }
+                       }
+               }
+               if (data->max[i] != 0) {
+                       if (temp > data->max[i]) {
+                               if (data->max_alarm[i] == false) {
+                                       data->max_alarm[i] = true;
+                                       updated_max_alarm = true;
+                               }
+                       } else if (temp < data->max[i] - data->max_hyst[i]) {
+                               if (data->max_alarm[i] == true) {
+                                       data->max_alarm[i] = false;
+                                       updated_max_alarm = true;
+                               }
+                       }
+               }
+
+               if (updated_min_alarm) {
+                       ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1);
+                       sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+               }
+               if (updated_max_alarm) {
+                       ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1);
+                       sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+               }
+       }
+
+       schedule_monitor(data);
+       mutex_unlock(&data->lock);
+}
+
+/* HWMON sysfs interfaces */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+                        char *buf)
+{
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       /* Show chip name */
+       return data->ops.show_name(dev, devattr, buf);
+}
+
+static ssize_t show_label(struct device *dev,
+                         struct device_attribute *devattr, char *buf)
+{
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       /* Show each sensor label */
+       return data->ops.show_label(dev, devattr, buf);
+}
+
+static ssize_t show_input(struct device *dev,
+                         struct device_attribute *devattr, char *buf)
+{
+       int ret, temp;
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       u8 gpadc_addr = data->gpadc_addr[attr->index];
+
+       ret = data->ops.read_sensor(data, gpadc_addr, &temp);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n", temp);
+}
+
+/* Set functions (RW nodes) */
+static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
+                      const char *buf, size_t count)
+{
+       unsigned long val;
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int res = kstrtol(buf, 10, &val);
+       if (res < 0)
+               return res;
+
+       val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+       mutex_lock(&data->lock);
+       data->min[attr->index] = val;
+       threshold_updated(data);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
+                      const char *buf, size_t count)
+{
+       unsigned long val;
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int res = kstrtol(buf, 10, &val);
+       if (res < 0)
+               return res;
+
+       val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+       mutex_lock(&data->lock);
+       data->max[attr->index] = val;
+       threshold_updated(data);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t set_max_hyst(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf, size_t count)
+{
+       unsigned long val;
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int res = kstrtoul(buf, 10, &val);
+       if (res < 0)
+               return res;
+
+       val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+       mutex_lock(&data->lock);
+       data->max_hyst[attr->index] = val;
+       threshold_updated(data);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+/* Show functions (RO nodes) */
+static ssize_t show_min(struct device *dev,
+                       struct device_attribute *devattr, char *buf)
+{
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%ld\n", data->min[attr->index]);
+}
+
+static ssize_t show_max(struct device *dev,
+                       struct device_attribute *devattr, char *buf)
+{
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%ld\n", data->max[attr->index]);
+}
+
+static ssize_t show_max_hyst(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%ld\n", data->max_hyst[attr->index]);
+}
+
+static ssize_t show_min_alarm(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+{
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%d\n", data->min_alarm[attr->index]);
+}
+
+static ssize_t show_max_alarm(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+{
+       struct abx500_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%d\n", data->max_alarm[attr->index]);
+}
+
+static mode_t abx500_attrs_visible(struct kobject *kobj,
+                                  struct attribute *attr, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct abx500_temp *data = dev_get_drvdata(dev);
+
+       if (data->ops.is_visible)
+               return data->ops.is_visible(attr, n);
+
+       return attr->mode;
+}
+
+/* Chip name, required by hwmon */
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+/* GPADC - SENSOR1 */
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+                         show_max_hyst, set_max_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 0);
+
+/* GPADC - SENSOR2 */
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO,
+                         show_max_hyst, set_max_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
+
+/* GPADC - SENSOR3 */
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IWUSR | S_IRUGO,
+                         show_max_hyst, set_max_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_min_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_max_alarm, NULL, 2);
+
+/* GPADC - SENSOR4 */
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_input, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_min, set_min, 3);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_max, set_max, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IWUSR | S_IRUGO,
+                         show_max_hyst, set_max_hyst, 3);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_min_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_max_alarm, NULL, 3);
+
+struct attribute *abx500_temp_attributes[] = {
+       &sensor_dev_attr_name.dev_attr.attr,
+
+       &sensor_dev_attr_temp1_label.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp2_label.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp3_label.dev_attr.attr,
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_min.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp4_label.dev_attr.attr,
+       &sensor_dev_attr_temp4_input.dev_attr.attr,
+       &sensor_dev_attr_temp4_min.dev_attr.attr,
+       &sensor_dev_attr_temp4_max.dev_attr.attr,
+       &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group abx500_temp_group = {
+       .attrs = abx500_temp_attributes,
+       .is_visible = abx500_attrs_visible,
+};
+
+static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data)
+{
+       struct platform_device *pdev = irq_data;
+       struct abx500_temp *data = platform_get_drvdata(pdev);
+
+       data->ops.irq_handler(irq, data);
+       return IRQ_HANDLED;
+}
+
+static int setup_irqs(struct platform_device *pdev)
+{
+       int ret;
+       int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM");
+
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Get irq by name failed\n");
+               return irq;
+       }
+
+       ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+               abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev);
+       if (ret < 0)
+               dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
+
+       return ret;
+}
+
+static int abx500_temp_probe(struct platform_device *pdev)
+{
+       struct abx500_temp *data;
+       int err;
+
+       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->pdev = pdev;
+       mutex_init(&data->lock);
+
+       /* Chip specific initialization */
+       err = abx500_hwmon_init(data);
+       if (err < 0 || !data->ops.read_sensor || !data->ops.show_name ||
+                       !data->ops.show_label)
+               return err;
+
+       INIT_DEFERRABLE_WORK(&data->work, gpadc_monitor);
+
+       platform_set_drvdata(pdev, data);
+
+       err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+               return err;
+       }
+
+       data->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+               goto exit_sysfs_group;
+       }
+
+       if (data->ops.irq_handler) {
+               err = setup_irqs(pdev);
+               if (err < 0)
+                       goto exit_hwmon_reg;
+       }
+       return 0;
+
+exit_hwmon_reg:
+       hwmon_device_unregister(data->hwmon_dev);
+exit_sysfs_group:
+       sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
+       return err;
+}
+
+static int abx500_temp_remove(struct platform_device *pdev)
+{
+       struct abx500_temp *data = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&data->work);
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
+
+       return 0;
+}
+
+static int abx500_temp_suspend(struct platform_device *pdev,
+                              pm_message_t state)
+{
+       struct abx500_temp *data = platform_get_drvdata(pdev);
+
+       if (data->work_active)
+               cancel_delayed_work_sync(&data->work);
+
+       return 0;
+}
+
+static int abx500_temp_resume(struct platform_device *pdev)
+{
+       struct abx500_temp *data = platform_get_drvdata(pdev);
+
+       if (data->work_active)
+               schedule_monitor(data);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id abx500_temp_match[] = {
+       { .compatible = "stericsson,abx500-temp" },
+       {},
+};
+#endif
+
+static struct platform_driver abx500_temp_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "abx500-temp",
+               .of_match_table = of_match_ptr(abx500_temp_match),
+       },
+       .suspend = abx500_temp_suspend,
+       .resume = abx500_temp_resume,
+       .probe = abx500_temp_probe,
+       .remove = abx500_temp_remove,
+};
+
+module_platform_driver(abx500_temp_driver);
+
+MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>");
+MODULE_DESCRIPTION("ABX500 temperature driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/abx500.h b/drivers/hwmon/abx500.h
new file mode 100644 (file)
index 0000000..9b295e6
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) ST-Ericsson 2010 - 2013
+ * License terms: GNU General Public License v2
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ *         Hongbo Zhang <hongbo.zhang@linaro.com>
+ */
+
+#ifndef _ABX500_H
+#define _ABX500_H
+
+#define NUM_SENSORS 5
+
+struct abx500_temp;
+
+/*
+ * struct abx500_temp_ops - abx500 chip specific ops
+ * @read_sensor: reads gpadc output
+ * @irq_handler: irq handler
+ * @show_name: hwmon device name
+ * @show_label: hwmon attribute label
+ * @is_visible: is attribute visible
+ */
+struct abx500_temp_ops {
+       int (*read_sensor)(struct abx500_temp *, u8, int *);
+       int (*irq_handler)(int, struct abx500_temp *);
+       ssize_t (*show_name)(struct device *,
+                       struct device_attribute *, char *);
+       ssize_t (*show_label) (struct device *,
+                       struct device_attribute *, char *);
+       int (*is_visible)(struct attribute *, int);
+};
+
+/*
+ * struct abx500_temp - representation of temp mon device
+ * @pdev: platform device
+ * @hwmon_dev: hwmon device
+ * @ops: abx500 chip specific ops
+ * @gpadc_addr: gpadc channel address
+ * @min: sensor temperature min value
+ * @max: sensor temperature max value
+ * @max_hyst: sensor temperature hysteresis value for max limit
+ * @min_alarm: sensor temperature min alarm
+ * @max_alarm: sensor temperature max alarm
+ * @work: delayed work scheduled to monitor temperature periodically
+ * @work_active: True if work is active
+ * @lock: mutex
+ * @monitored_sensors: number of monitored sensors
+ * @plat_data: private usage, usually points to platform specific data
+ */
+struct abx500_temp {
+       struct platform_device *pdev;
+       struct device *hwmon_dev;
+       struct abx500_temp_ops ops;
+       u8 gpadc_addr[NUM_SENSORS];
+       unsigned long min[NUM_SENSORS];
+       unsigned long max[NUM_SENSORS];
+       unsigned long max_hyst[NUM_SENSORS];
+       bool min_alarm[NUM_SENSORS];
+       bool max_alarm[NUM_SENSORS];
+       struct delayed_work work;
+       bool work_active;
+       struct mutex lock;
+       int monitored_sensors;
+       void *plat_data;
+};
+
+int abx500_hwmon_init(struct abx500_temp *data);
+
+#endif /* _ABX500_H */
index 7c84ced2e01b44e2de6e2991ebae0e7a388ba246..f276352cc9ef3d2d5493f983f01d8257c6f42726 100644 (file)
@@ -95,6 +95,7 @@
 #define AB8500_IT_MASK22_REG           0x55
 #define AB8500_IT_MASK23_REG           0x56
 #define AB8500_IT_MASK24_REG           0x57
+#define AB8500_IT_MASK25_REG           0x58
 
 /*
  * latch hierarchy registers
 #define AB8500_IT_LATCHHIER1_REG       0x60
 #define AB8500_IT_LATCHHIER2_REG       0x61
 #define AB8500_IT_LATCHHIER3_REG       0x62
+#define AB8540_IT_LATCHHIER4_REG       0x63
 
 #define AB8500_IT_LATCHHIER_NUM                3
+#define AB8540_IT_LATCHHIER_NUM                4
 
 #define AB8500_REV_REG                 0x80
 #define AB8500_IC_NAME_REG             0x82
 #define AB8500_SWITCH_OFF_STATUS       0x00
 
 #define AB8500_TURN_ON_STATUS          0x00
+#define AB8505_TURN_ON_STATUS_2        0x04
 
+#define AB8500_CH_USBCH_STAT1_REG      0x02
+#define VBUS_DET_DBNC100               0x02
+#define VBUS_DET_DBNC1                 0x01
+
+static DEFINE_SPINLOCK(on_stat_lock);
+static u8 turn_on_stat_mask = 0xFF;
+static u8 turn_on_stat_set;
 static bool no_bm; /* No battery management */
 module_param(no_bm, bool, S_IRUGO);
 
@@ -130,9 +141,15 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
        0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,
 };
 
-/* AB9540 support */
+/* AB9540 / AB8505 support */
 static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = {
-       0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24,
+       0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23
+};
+
+/* AB8540 support */
+static const int ab8540_irq_regoffset[AB8540_NUM_IRQ_REGS] = {
+       0, 1, 2, 3, 4, -1, -1, -1, -1, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23,
+       25, 26, 27, 28, 29, 30, 31,
 };
 
 static const char ab8500_version_str[][7] = {
@@ -352,6 +369,9 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
                        is_ab8500_1p1_or_earlier(ab8500))
                        continue;
 
+               if (ab8500->irq_reg_offset[i] < 0)
+                       continue;
+
                ab8500->oldmask[i] = new;
 
                reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
@@ -423,6 +443,18 @@ static struct irq_chip ab8500_irq_chip = {
        .irq_set_type           = ab8500_irq_set_type,
 };
 
+static void update_latch_offset(u8 *offset, int i)
+{
+       /* Fix inconsistent ITFromLatch25 bit mapping... */
+       if (unlikely(*offset == 17))
+                       *offset = 24;
+       /* Fix inconsistent ab8540 bit mapping... */
+       if (unlikely(*offset == 16))
+                       *offset = 25;
+       if ((i==3) && (*offset >= 24))
+                       *offset += 2;
+}
+
 static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
                                        int latch_offset, u8 latch_val)
 {
@@ -474,9 +506,7 @@ static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500,
                latch_bit = __ffs(hier_val);
                latch_offset = (hier_offset << 3) + latch_bit;
 
-               /* Fix inconsistent ITFromLatch25 bit mapping... */
-               if (unlikely(latch_offset == 17))
-                       latch_offset = 24;
+               update_latch_offset(&latch_offset, hier_offset);
 
                status = get_register_interruptible(ab8500,
                                AB8500_INTERRUPT,
@@ -504,7 +534,7 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
        dev_vdbg(ab8500->dev, "interrupt\n");
 
        /*  Hierarchical interrupt version */
-       for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) {
+       for (i = 0; i < (ab8500->it_latchhier_num); i++) {
                int status;
                u8 hier_val;
 
@@ -520,63 +550,6 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
-/**
- * ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
- *
- * @ab8500: ab8500_irq controller to operate on.
- * @irq: index of the interrupt requested in the chip IRQs
- *
- * Useful for drivers to request their own IRQs.
- */
-static int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
-{
-       if (!ab8500)
-               return -EINVAL;
-
-       return irq_create_mapping(ab8500->domain, irq);
-}
-
-static irqreturn_t ab8500_irq(int irq, void *dev)
-{
-       struct ab8500 *ab8500 = dev;
-       int i;
-
-       dev_vdbg(ab8500->dev, "interrupt\n");
-
-       atomic_inc(&ab8500->transfer_ongoing);
-
-       for (i = 0; i < ab8500->mask_size; i++) {
-               int regoffset = ab8500->irq_reg_offset[i];
-               int status;
-               u8 value;
-
-               /*
-                * Interrupt register 12 doesn't exist prior to AB8500 version
-                * 2.0
-                */
-               if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500))
-                       continue;
-
-               status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
-                       AB8500_IT_LATCH1_REG + regoffset, &value);
-               if (status < 0 || value == 0)
-                       continue;
-
-               do {
-                       int bit = __ffs(value);
-                       int line = i * 8 + bit;
-                       int virq = ab8500_irq_get_virq(ab8500, line);
-
-                       handle_nested_irq(virq);
-                       ab8500_debug_register_interrupt(line);
-                       value &= ~(1 << bit);
-
-               } while (value);
-       }
-       atomic_dec(&ab8500->transfer_ongoing);
-       return IRQ_HANDLED;
-}
-
 static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
                                irq_hw_number_t hwirq)
 {
@@ -607,7 +580,9 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
 {
        int num_irqs;
 
-       if (is_ab9540(ab8500))
+       if (is_ab8540(ab8500))
+               num_irqs = AB8540_NR_IRQS;
+       else if (is_ab9540(ab8500))
                num_irqs = AB9540_NR_IRQS;
        else if (is_ab8505(ab8500))
                num_irqs = AB8505_NR_IRQS;
@@ -650,6 +625,15 @@ static struct resource ab8500_gpadc_resources[] = {
        },
 };
 
+static struct resource ab8505_gpadc_resources[] = {
+       {
+               .name   = "SW_CONV_END",
+               .start  = AB8500_INT_GP_SW_ADC_CONV_END,
+               .end    = AB8500_INT_GP_SW_ADC_CONV_END,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
 static struct resource ab8500_rtc_resources[] = {
        {
                .name   = "60S",
@@ -973,6 +957,30 @@ static struct resource ab8505_iddet_resources[] = {
                .end   = AB8505_INT_KEYSTUCK,
                .flags = IORESOURCE_IRQ,
        },
+       {
+               .name = "VBUS_DET_R",
+               .start = AB8500_INT_VBUS_DET_R,
+               .end = AB8500_INT_VBUS_DET_R,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "VBUS_DET_F",
+               .start = AB8500_INT_VBUS_DET_F,
+               .end = AB8500_INT_VBUS_DET_F,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "ID_DET_PLUGR",
+               .start = AB8500_INT_ID_DET_PLUGR,
+               .end = AB8500_INT_ID_DET_PLUGR,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .name = "ID_DET_PLUGF",
+               .start = AB8500_INT_ID_DET_PLUGF,
+               .end = AB8500_INT_ID_DET_PLUGF,
+               .flags = IORESOURCE_IRQ,
+       },
 };
 
 static struct resource ab8500_temp_resources[] = {
@@ -984,7 +992,42 @@ static struct resource ab8500_temp_resources[] = {
        },
 };
 
-static struct mfd_cell abx500_common_devs[] = {
+static struct mfd_cell ab8500_bm_devs[] = {
+       {
+               .name = "ab8500-charger",
+               .of_compatible = "stericsson,ab8500-charger",
+               .num_resources = ARRAY_SIZE(ab8500_charger_resources),
+               .resources = ab8500_charger_resources,
+               .platform_data = &ab8500_bm_data,
+               .pdata_size = sizeof(ab8500_bm_data),
+       },
+       {
+               .name = "ab8500-btemp",
+               .of_compatible = "stericsson,ab8500-btemp",
+               .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+               .resources = ab8500_btemp_resources,
+               .platform_data = &ab8500_bm_data,
+               .pdata_size = sizeof(ab8500_bm_data),
+       },
+       {
+               .name = "ab8500-fg",
+               .of_compatible = "stericsson,ab8500-fg",
+               .num_resources = ARRAY_SIZE(ab8500_fg_resources),
+               .resources = ab8500_fg_resources,
+               .platform_data = &ab8500_bm_data,
+               .pdata_size = sizeof(ab8500_bm_data),
+       },
+       {
+               .name = "ab8500-chargalg",
+               .of_compatible = "stericsson,ab8500-chargalg",
+               .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+               .resources = ab8500_chargalg_resources,
+               .platform_data = &ab8500_bm_data,
+               .pdata_size = sizeof(ab8500_bm_data),
+       },
+};
+
+static struct mfd_cell ab8500_devs[] = {
 #ifdef CONFIG_DEBUG_FS
        {
                .name = "ab8500-debug",
@@ -1007,7 +1050,6 @@ static struct mfd_cell abx500_common_devs[] = {
        },
        {
                .name = "ab8500-gpadc",
-               .of_compatible = "stericsson,ab8500-gpadc",
                .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
                .resources = ab8500_gpadc_resources,
        },
@@ -1024,6 +1066,7 @@ static struct mfd_cell abx500_common_devs[] = {
                .resources = ab8500_av_acc_detect_resources,
        },
        {
+
                .name = "ab8500-poweron-key",
                .of_compatible = "stericsson,ab8500-poweron-key",
                .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
@@ -1052,83 +1095,221 @@ static struct mfd_cell abx500_common_devs[] = {
                .name = "ab8500-denc",
                .of_compatible = "stericsson,ab8500-denc",
        },
+       {
+               .name = "ab8500-gpio",
+               .of_compatible = "stericsson,ab8500-gpio",
+       },
        {
                .name = "abx500-temp",
                .of_compatible = "stericsson,abx500-temp",
                .num_resources = ARRAY_SIZE(ab8500_temp_resources),
                .resources = ab8500_temp_resources,
        },
+       {
+               .name = "ab8500-usb",
+               .num_resources = ARRAY_SIZE(ab8500_usb_resources),
+               .resources = ab8500_usb_resources,
+       },
+       {
+               .name = "ab8500-codec",
+       },
 };
 
-static struct mfd_cell ab8500_bm_devs[] = {
+static struct mfd_cell ab9540_devs[] = {
+#ifdef CONFIG_DEBUG_FS
        {
-               .name = "ab8500-charger",
-               .of_compatible = "stericsson,ab8500-charger",
-               .num_resources = ARRAY_SIZE(ab8500_charger_resources),
-               .resources = ab8500_charger_resources,
-               .platform_data = &ab8500_bm_data,
-               .pdata_size = sizeof(ab8500_bm_data),
+               .name = "ab8500-debug",
+               .num_resources = ARRAY_SIZE(ab8500_debug_resources),
+               .resources = ab8500_debug_resources,
        },
+#endif
        {
-               .name = "ab8500-btemp",
-               .of_compatible = "stericsson,ab8500-btemp",
-               .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
-               .resources = ab8500_btemp_resources,
-               .platform_data = &ab8500_bm_data,
-               .pdata_size = sizeof(ab8500_bm_data),
+               .name = "ab8500-sysctrl",
        },
        {
-               .name = "ab8500-fg",
-               .of_compatible = "stericsson,ab8500-fg",
-               .num_resources = ARRAY_SIZE(ab8500_fg_resources),
-               .resources = ab8500_fg_resources,
-               .platform_data = &ab8500_bm_data,
-               .pdata_size = sizeof(ab8500_bm_data),
+               .name = "ab8500-regulator",
        },
        {
-               .name = "ab8500-chargalg",
-               .of_compatible = "stericsson,ab8500-chargalg",
-               .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
-               .resources = ab8500_chargalg_resources,
-               .platform_data = &ab8500_bm_data,
-               .pdata_size = sizeof(ab8500_bm_data),
+               .name = "abx500-clk",
+               .of_compatible = "stericsson,abx500-clk",
+       },
+       {
+               .name = "ab8500-gpadc",
+               .of_compatible = "stericsson,ab8500-gpadc",
+               .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
+               .resources = ab8500_gpadc_resources,
+       },
+       {
+               .name = "ab8500-rtc",
+               .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
+               .resources = ab8500_rtc_resources,
+       },
+       {
+               .name = "ab8500-acc-det",
+               .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+               .resources = ab8500_av_acc_detect_resources,
+       },
+       {
+               .name = "ab8500-poweron-key",
+               .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+               .resources = ab8500_poweronkey_db_resources,
+       },
+       {
+               .name = "ab8500-pwm",
+               .id = 1,
+       },
+       {
+               .name = "ab8500-leds",
+       },
+       {
+               .name = "abx500-temp",
+               .num_resources = ARRAY_SIZE(ab8500_temp_resources),
+               .resources = ab8500_temp_resources,
+       },
+       {
+               .name = "pinctrl-ab9540",
+               .of_compatible = "stericsson,ab9540-gpio",
+       },
+       {
+               .name = "ab9540-usb",
+               .num_resources = ARRAY_SIZE(ab8500_usb_resources),
+               .resources = ab8500_usb_resources,
+       },
+       {
+               .name = "ab9540-codec",
+       },
+       {
+               .name = "ab-iddet",
+               .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
+               .resources = ab8505_iddet_resources,
        },
 };
 
-static struct mfd_cell ab8500_devs[] = {
+/* Device list for ab8505  */
+static struct mfd_cell ab8505_devs[] = {
+#ifdef CONFIG_DEBUG_FS
        {
-               .name = "pinctrl-ab8500",
-               .of_compatible = "stericsson,ab8500-gpio",
+               .name = "ab8500-debug",
+               .num_resources = ARRAY_SIZE(ab8500_debug_resources),
+               .resources = ab8500_debug_resources,
+       },
+#endif
+       {
+               .name = "ab8500-sysctrl",
+       },
+       {
+               .name = "ab8500-regulator",
+       },
+       {
+               .name = "abx500-clk",
+               .of_compatible = "stericsson,abx500-clk",
+       },
+       {
+               .name = "ab8500-gpadc",
+               .num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
+               .resources = ab8505_gpadc_resources,
+       },
+       {
+               .name = "ab8500-rtc",
+               .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
+               .resources = ab8500_rtc_resources,
+       },
+       {
+               .name = "ab8500-acc-det",
+               .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+               .resources = ab8500_av_acc_detect_resources,
+       },
+       {
+               .name = "ab8500-poweron-key",
+               .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+               .resources = ab8500_poweronkey_db_resources,
+       },
+       {
+               .name = "ab8500-pwm",
+               .id = 1,
+       },
+       {
+               .name = "ab8500-leds",
+       },
+       {
+               .name = "ab8500-gpio",
        },
        {
                .name = "ab8500-usb",
-               .of_compatible = "stericsson,ab8500-usb",
                .num_resources = ARRAY_SIZE(ab8500_usb_resources),
                .resources = ab8500_usb_resources,
        },
        {
                .name = "ab8500-codec",
-               .of_compatible = "stericsson,ab8500-codec",
+       },
+       {
+               .name = "ab-iddet",
+               .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
+               .resources = ab8505_iddet_resources,
        },
 };
 
-static struct mfd_cell ab9540_devs[] = {
+static struct mfd_cell ab8540_devs[] = {
+#ifdef CONFIG_DEBUG_FS
        {
-               .name = "pinctrl-ab9540",
-               .of_compatible = "stericsson,ab9540-gpio",
+               .name = "ab8500-debug",
+               .num_resources = ARRAY_SIZE(ab8500_debug_resources),
+               .resources = ab8500_debug_resources,
+       },
+#endif
+       {
+               .name = "ab8500-sysctrl",
        },
        {
-               .name = "ab9540-usb",
+               .name = "ab8500-regulator",
+       },
+       {
+               .name = "abx500-clk",
+               .of_compatible = "stericsson,abx500-clk",
+       },
+       {
+               .name = "ab8500-gpadc",
+               .num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
+               .resources = ab8505_gpadc_resources,
+       },
+       {
+               .name = "ab8500-rtc",
+               .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
+               .resources = ab8500_rtc_resources,
+       },
+       {
+               .name = "ab8500-acc-det",
+               .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
+               .resources = ab8500_av_acc_detect_resources,
+       },
+       {
+               .name = "ab8500-poweron-key",
+               .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
+               .resources = ab8500_poweronkey_db_resources,
+       },
+       {
+               .name = "ab8500-pwm",
+               .id = 1,
+       },
+       {
+               .name = "ab8500-leds",
+       },
+       {
+               .name = "abx500-temp",
+               .num_resources = ARRAY_SIZE(ab8500_temp_resources),
+               .resources = ab8500_temp_resources,
+       },
+       {
+               .name = "ab8500-gpio",
+       },
+       {
+               .name = "ab8540-usb",
                .num_resources = ARRAY_SIZE(ab8500_usb_resources),
                .resources = ab8500_usb_resources,
        },
        {
-               .name = "ab9540-codec",
+               .name = "ab8540-codec",
        },
-};
-
-/* Device list common to ab9540 and ab8505 */
-static struct mfd_cell ab9540_ab8505_devs[] = {
        {
                .name = "ab-iddet",
                .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
@@ -1142,6 +1323,7 @@ static ssize_t show_chip_id(struct device *dev,
        struct ab8500 *ab8500;
 
        ab8500 = dev_get_drvdata(dev);
+
        return sprintf(buf, "%#x\n", ab8500 ? ab8500->chip_id : -EINVAL);
 }
 
@@ -1171,6 +1353,15 @@ static ssize_t show_switch_off_status(struct device *dev,
        return sprintf(buf, "%#x\n", value);
 }
 
+/* use mask and set to override the register turn_on_stat value */
+void ab8500_override_turn_on_stat(u8 mask, u8 set)
+{
+       spin_lock(&on_stat_lock);
+       turn_on_stat_mask = mask;
+       turn_on_stat_set = set;
+       spin_unlock(&on_stat_lock);
+}
+
 /*
  * ab8500 has turned on due to (TURN_ON_STATUS):
  * 0x01 PORnVbat
@@ -1194,9 +1385,38 @@ static ssize_t show_turn_on_status(struct device *dev,
                AB8500_TURN_ON_STATUS, &value);
        if (ret < 0)
                return ret;
+
+       /*
+        * In L9540, turn_on_status register is not updated correctly if
+        * the device is rebooted with AC/USB charger connected. Due to
+        * this, the device boots android instead of entering into charge
+        * only mode. Read the AC/USB status register to detect the charger
+        * presence and update the turn on status manually.
+        */
+       if (is_ab9540(ab8500)) {
+               spin_lock(&on_stat_lock);
+               value = (value & turn_on_stat_mask) | turn_on_stat_set;
+               spin_unlock(&on_stat_lock);
+       }
+
        return sprintf(buf, "%#x\n", value);
 }
 
+static ssize_t show_turn_on_status_2(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int ret;
+       u8 value;
+       struct ab8500 *ab8500;
+
+       ab8500 = dev_get_drvdata(dev);
+       ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
+               AB8505_TURN_ON_STATUS_2, &value);
+       if (ret < 0)
+               return ret;
+       return sprintf(buf, "%#x\n", (value & 0x1));
+}
+
 static ssize_t show_ab9540_dbbrstn(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
@@ -1253,6 +1473,7 @@ exit:
 static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
 static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
 static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
+static DEVICE_ATTR(turn_on_status_2, S_IRUGO, show_turn_on_status_2, NULL);
 static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR,
                        show_ab9540_dbbrstn, store_ab9540_dbbrstn);
 
@@ -1263,6 +1484,11 @@ static struct attribute *ab8500_sysfs_entries[] = {
        NULL,
 };
 
+static struct attribute *ab8505_sysfs_entries[] = {
+       &dev_attr_turn_on_status_2.attr,
+       NULL,
+};
+
 static struct attribute *ab9540_sysfs_entries[] = {
        &dev_attr_chip_id.attr,
        &dev_attr_switch_off_status.attr,
@@ -1275,6 +1501,10 @@ static struct attribute_group ab8500_attr_group = {
        .attrs  = ab8500_sysfs_entries,
 };
 
+static struct attribute_group ab8505_attr_group = {
+       .attrs  = ab8505_sysfs_entries,
+};
+
 static struct attribute_group ab9540_attr_group = {
        .attrs  = ab9540_sysfs_entries,
 };
@@ -1290,6 +1520,15 @@ static int ab8500_probe(struct platform_device *pdev)
                "Battery level lower than power on reset threshold",
                "Power on key 1 pressed longer than 10 seconds",
                "DB8500 thermal shutdown"};
+       static char *turn_on_status[] = {
+               "Battery rising (Vbat)",
+               "Power On Key 1 dbF",
+               "Power On Key 2 dbF",
+               "RTC Alarm",
+               "Main Charger Detect",
+               "Vbus Detect (USB)",
+               "USB ID Detect",
+               "UART Factory Mode Detect"};
        struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
        const struct platform_device_id *platid = platform_get_device_id(pdev);
        enum ab8500_version version = AB8500_VERSION_UNDEFINED;
@@ -1351,13 +1590,20 @@ static int ab8500_probe(struct platform_device *pdev)
                        ab8500->chip_id >> 4,
                        ab8500->chip_id & 0x0F);
 
-       /* Configure AB8500 or AB9540 IRQ */
-       if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
+       /* Configure AB8540 */
+       if (is_ab8540(ab8500)) {
+               ab8500->mask_size = AB8540_NUM_IRQ_REGS;
+               ab8500->irq_reg_offset = ab8540_irq_regoffset;
+               ab8500->it_latchhier_num = AB8540_IT_LATCHHIER_NUM;
+       }/* Configure AB8500 or AB9540 IRQ */
+       else if (is_ab9540(ab8500) || is_ab8505(ab8500)) {
                ab8500->mask_size = AB9540_NUM_IRQ_REGS;
                ab8500->irq_reg_offset = ab9540_irq_regoffset;
+               ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM;
        } else {
                ab8500->mask_size = AB8500_NUM_IRQ_REGS;
                ab8500->irq_reg_offset = ab8500_irq_regoffset;
+               ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM;
        }
        ab8500->mask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL);
        if (!ab8500->mask)
@@ -1396,10 +1642,36 @@ static int ab8500_probe(struct platform_device *pdev)
        } else {
                printk(KERN_CONT " None\n");
        }
+       ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
+               AB8500_TURN_ON_STATUS, &value);
+       if (ret < 0)
+               return ret;
+       dev_info(ab8500->dev, "turn on reason(s) (%#x): ", value);
+
+       if (value) {
+               for (i = 0; i < ARRAY_SIZE(turn_on_status); i++) {
+                       if (value & 1)
+                               printk("\"%s\" ", turn_on_status[i]);
+                       value = value >> 1;
+               }
+               printk("\n");
+       } else {
+               printk("None\n");
+       }
 
        if (plat && plat->init)
                plat->init(ab8500);
 
+       if (is_ab9540(ab8500)) {
+               ret = get_register_interruptible(ab8500, AB8500_CHARGER,
+                       AB8500_CH_USBCH_STAT1_REG, &value);
+               if (ret < 0)
+                       return ret;
+               if ((value & VBUS_DET_DBNC1) && (value & VBUS_DET_DBNC100))
+                       ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
+                                                    AB8500_VBUS_DET);
+       }
+
        /* Clear and mask all interrupts */
        for (i = 0; i < ab8500->mask_size; i++) {
                /*
@@ -1410,6 +1682,9 @@ static int ab8500_probe(struct platform_device *pdev)
                                is_ab8500_1p1_or_earlier(ab8500))
                        continue;
 
+               if (ab8500->irq_reg_offset[i] < 0)
+                       continue;
+
                get_register_interruptible(ab8500, AB8500_INTERRUPT,
                        AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i],
                        &value);
@@ -1428,26 +1703,10 @@ static int ab8500_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       /*  Activate this feature only in ab9540 */
-       /*  till tests are done on ab8500 1p2 or later*/
-       if (is_ab9540(ab8500)) {
-               ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
-                                               ab8500_hierarchical_irq,
-                                               IRQF_ONESHOT | IRQF_NO_SUSPEND,
-                                               "ab8500", ab8500);
-       }
-       else {
-               ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
-                                               ab8500_irq,
-                                               IRQF_ONESHOT | IRQF_NO_SUSPEND,
-                                               "ab8500", ab8500);
-               if (ret)
-                       return ret;
-       }
-
-       ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
-                       ARRAY_SIZE(abx500_common_devs), NULL,
-                       ab8500->irq_base, ab8500->domain);
+       ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL,
+                       ab8500_hierarchical_irq,
+                       IRQF_ONESHOT | IRQF_NO_SUSPEND,
+                       "ab8500", ab8500);
        if (ret)
                return ret;
 
@@ -1455,6 +1714,14 @@ static int ab8500_probe(struct platform_device *pdev)
                ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
                                ARRAY_SIZE(ab9540_devs), NULL,
                                ab8500->irq_base, ab8500->domain);
+       else if (is_ab8540(ab8500))
+               ret = mfd_add_devices(ab8500->dev, 0, ab8540_devs,
+                             ARRAY_SIZE(ab8540_devs), NULL,
+                             ab8500->irq_base, ab8500->domain);
+       else if (is_ab8505(ab8500))
+               ret = mfd_add_devices(ab8500->dev, 0, ab8505_devs,
+                             ARRAY_SIZE(ab8505_devs), NULL,
+                             ab8500->irq_base, ab8500->domain);
        else
                ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
                                ARRAY_SIZE(ab8500_devs), NULL,
@@ -1462,13 +1729,6 @@ static int ab8500_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       if (is_ab9540(ab8500) || is_ab8505(ab8500))
-               ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
-                               ARRAY_SIZE(ab9540_ab8505_devs), NULL,
-                               ab8500->irq_base, ab8500->domain);
-       if (ret)
-               return ret;
-
        if (!no_bm) {
                /* Add battery management devices */
                ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
@@ -1478,12 +1738,19 @@ static int ab8500_probe(struct platform_device *pdev)
                        dev_err(ab8500->dev, "error adding bm devices\n");
        }
 
-       if (is_ab9540(ab8500))
+       if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
+                       ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))
                ret = sysfs_create_group(&ab8500->dev->kobj,
                                        &ab9540_attr_group);
        else
                ret = sysfs_create_group(&ab8500->dev->kobj,
                                        &ab8500_attr_group);
+
+       if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
+                       ab8500->chip_id >= AB8500_CUT2P0)
+               ret = sysfs_create_group(&ab8500->dev->kobj,
+                                        &ab8505_attr_group);
+
        if (ret)
                dev_err(ab8500->dev, "error creating sysfs entries\n");
 
@@ -1494,11 +1761,16 @@ static int ab8500_remove(struct platform_device *pdev)
 {
        struct ab8500 *ab8500 = platform_get_drvdata(pdev);
 
-       if (is_ab9540(ab8500))
+       if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
+                       ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))
                sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
        else
                sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
 
+       if ((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
+                       ab8500->chip_id >= AB8500_CUT2P0)
+               sysfs_remove_group(&ab8500->dev->kobj, &ab8505_attr_group);
+
        mfd_remove_devices(ab8500->dev);
 
        return 0;
index 45fe3c50eb035f5dfff77e30c7cee3f6127413f3..b88bbbc15f1e962f8f131d0f50bc25768091f55c 100644 (file)
@@ -80,6 +80,7 @@
 #include <linux/interrupt.h>
 #include <linux/kobject.h>
 #include <linux/slab.h>
+#include <linux/irq.h>
 
 #include <linux/mfd/abx500.h>
 #include <linux/mfd/abx500/ab8500.h>
@@ -90,6 +91,9 @@
 #include <linux/ctype.h>
 #endif
 
+/* TODO: this file should not reference IRQ_DB8500_AB8500! */
+#include <mach/irqs.h>
+
 static u32 debug_bank;
 static u32 debug_address;
 
@@ -101,6 +105,11 @@ static int num_irqs;
 static struct device_attribute **dev_attr;
 static char **event_name;
 
+static u8 avg_sample = SAMPLE_16;
+static u8 trig_edge = RISING_EDGE;
+static u8 conv_type = ADC_SW;
+static u8 trig_timer;
+
 /**
  * struct ab8500_reg_range
  * @first: the first address of the range
@@ -150,7 +159,9 @@ static struct hwreg_cfg hwreg_cfg = {
 
 #define AB8500_REV_REG 0x80
 
-static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
+static struct ab8500_prcmu_ranges *debug_ranges;
+
+struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = {
        [0x0] = {
                .num_ranges = 0,
                .range = NULL,
@@ -354,7 +365,7 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
                        },
                        {
                                .first = 0xf5,
-                               .last = 0xf6,
+                               .last = 0xf6,
                        },
                },
        },
@@ -479,699 +490,2081 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
        },
 };
 
-static irqreturn_t ab8500_debug_handler(int irq, void *data)
-{
-       char buf[16];
-       struct kobject *kobj = (struct kobject *)data;
-       unsigned int irq_abb = irq - irq_first;
-
-       if (irq_abb < num_irqs)
-               irq_count[irq_abb]++;
-       /*
-        * This makes it possible to use poll for events (POLLPRI | POLLERR)
-        * from userspace on sysfs file named <irq-nr>
-        */
-       sprintf(buf, "%d", irq);
-       sysfs_notify(kobj, NULL, buf);
-
-       return IRQ_HANDLED;
-}
-
-/* Prints to seq_file or log_buf */
-static int ab8500_registers_print(struct device *dev, u32 bank,
-                               struct seq_file *s)
-{
-       unsigned int i;
-
-       for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
-               u32 reg;
-
-               for (reg = debug_ranges[bank].range[i].first;
-                       reg <= debug_ranges[bank].range[i].last;
-                       reg++) {
-                       u8 value;
-                       int err;
-
-                       err = abx500_get_register_interruptible(dev,
-                               (u8)bank, (u8)reg, &value);
-                       if (err < 0) {
-                               dev_err(dev, "ab->read fail %d\n", err);
-                               return err;
-                       }
-
-                       if (s) {
-                               err = seq_printf(s, "  [%u/0x%02X]: 0x%02X\n",
-                                       bank, reg, value);
-                               if (err < 0) {
-                                       dev_err(dev,
-                                       "seq_printf overflow bank=%d reg=%d\n",
-                                               bank, reg);
-                                       /* Error is not returned here since
-                                        * the output is wanted in any case */
-                                       return 0;
-                               }
-                       } else {
-                               printk(KERN_INFO" [%u/0x%02X]: 0x%02X\n", bank,
-                                       reg, value);
-                       }
-               }
-       }
-       return 0;
-}
-
-static int ab8500_print_bank_registers(struct seq_file *s, void *p)
-{
-       struct device *dev = s->private;
-       u32 bank = debug_bank;
-
-       seq_printf(s, AB8500_NAME_STRING " register values:\n");
-
-       seq_printf(s, " bank %u:\n", bank);
-
-       ab8500_registers_print(dev, bank, s);
-       return 0;
-}
-
-static int ab8500_registers_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ab8500_print_bank_registers, inode->i_private);
-}
-
-static const struct file_operations ab8500_registers_fops = {
-       .open = ab8500_registers_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-       .owner = THIS_MODULE,
-};
-
-static int ab8500_print_all_banks(struct seq_file *s, void *p)
-{
-       struct device *dev = s->private;
-       unsigned int i;
-       int err;
-
-       seq_printf(s, AB8500_NAME_STRING " register values:\n");
-
-       for (i = 1; i < AB8500_NUM_BANKS; i++) {
-               err = seq_printf(s, " bank %u:\n", i);
-               if (err < 0)
-                       dev_err(dev, "seq_printf overflow, bank=%d\n", i);
-
-               ab8500_registers_print(dev, i, s);
-       }
-       return 0;
-}
-
-/* Dump registers to kernel log */
-void ab8500_dump_all_banks(struct device *dev)
-{
-       unsigned int i;
-
-       printk(KERN_INFO"ab8500 register values:\n");
-
-       for (i = 1; i < AB8500_NUM_BANKS; i++) {
-               printk(KERN_INFO" bank %u:\n", i);
-               ab8500_registers_print(dev, i, NULL);
-       }
-}
-
-static int ab8500_all_banks_open(struct inode *inode, struct file *file)
-{
-       struct seq_file *s;
-       int err;
-
-       err = single_open(file, ab8500_print_all_banks, inode->i_private);
-       if (!err) {
-               /* Default buf size in seq_read is not enough */
-               s = (struct seq_file *)file->private_data;
-               s->size = (PAGE_SIZE * 2);
-               s->buf = kmalloc(s->size, GFP_KERNEL);
-               if (!s->buf) {
-                       single_release(inode, file);
-                       err = -ENOMEM;
-               }
-       }
-       return err;
-}
-
-static const struct file_operations ab8500_all_banks_fops = {
-       .open = ab8500_all_banks_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-       .owner = THIS_MODULE,
-};
-
-static int ab8500_bank_print(struct seq_file *s, void *p)
-{
-       return seq_printf(s, "%d\n", debug_bank);
-}
-
-static int ab8500_bank_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ab8500_bank_print, inode->i_private);
-}
-
-static ssize_t ab8500_bank_write(struct file *file,
-       const char __user *user_buf,
-       size_t count, loff_t *ppos)
-{
-       struct device *dev = ((struct seq_file *)(file->private_data))->private;
-       unsigned long user_bank;
-       int err;
-
-       /* Get userspace string and assure termination */
-       err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
-       if (err)
-               return err;
-
-       if (user_bank >= AB8500_NUM_BANKS) {
-               dev_err(dev, "debugfs error input > number of banks\n");
-               return -EINVAL;
-       }
-
-       debug_bank = user_bank;
-
-       return count;
-}
-
-static int ab8500_address_print(struct seq_file *s, void *p)
-{
-       return seq_printf(s, "0x%02X\n", debug_address);
-}
-
-static int ab8500_address_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ab8500_address_print, inode->i_private);
-}
-
-static ssize_t ab8500_address_write(struct file *file,
-       const char __user *user_buf,
-       size_t count, loff_t *ppos)
-{
-       struct device *dev = ((struct seq_file *)(file->private_data))->private;
-       unsigned long user_address;
-       int err;
-
-       /* Get userspace string and assure termination */
-       err = kstrtoul_from_user(user_buf, count, 0, &user_address);
-       if (err)
-               return err;
-
-       if (user_address > 0xff) {
-               dev_err(dev, "debugfs error input > 0xff\n");
-               return -EINVAL;
-       }
-       debug_address = user_address;
-       return count;
-}
-
-static int ab8500_val_print(struct seq_file *s, void *p)
-{
-       struct device *dev = s->private;
-       int ret;
-       u8 regvalue;
-
-       ret = abx500_get_register_interruptible(dev,
-               (u8)debug_bank, (u8)debug_address, &regvalue);
-       if (ret < 0) {
-               dev_err(dev, "abx500_get_reg fail %d, %d\n",
-                       ret, __LINE__);
-               return -EINVAL;
-       }
-       seq_printf(s, "0x%02X\n", regvalue);
-
-       return 0;
-}
-
-static int ab8500_val_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ab8500_val_print, inode->i_private);
-}
-
-static ssize_t ab8500_val_write(struct file *file,
-       const char __user *user_buf,
-       size_t count, loff_t *ppos)
-{
-       struct device *dev = ((struct seq_file *)(file->private_data))->private;
-       unsigned long user_val;
-       int err;
-
-       /* Get userspace string and assure termination */
-       err = kstrtoul_from_user(user_buf, count, 0, &user_val);
-       if (err)
-               return err;
-
-       if (user_val > 0xff) {
-               dev_err(dev, "debugfs error input > 0xff\n");
-               return -EINVAL;
-       }
-       err = abx500_set_register_interruptible(dev,
-               (u8)debug_bank, debug_address, (u8)user_val);
-       if (err < 0) {
-               printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__);
-               return -EINVAL;
-       }
-
-       return count;
-}
-
-/*
- * Interrupt status
- */
-static u32 num_interrupts[AB8500_MAX_NR_IRQS];
-static int num_interrupt_lines;
-
-void ab8500_debug_register_interrupt(int line)
+struct ab8500_prcmu_ranges ab8505_debug_ranges[AB8500_NUM_BANKS] = {
+       [0x0] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_SYS_CTRL1_BLOCK] = {
+               .num_ranges = 5,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x04,
+                       },
+                       {
+                               .first = 0x42,
+                               .last = 0x42,
+                       },
+                       {
+                               .first = 0x52,
+                               .last = 0x52,
+                       },
+                       {
+                               .first = 0x54,
+                               .last = 0x57,
+                       },
+                       {
+                               .first = 0x80,
+                               .last = 0x83,
+                       },
+               },
+       },
+       [AB8500_SYS_CTRL2_BLOCK] = {
+               .num_ranges = 5,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x0D,
+                       },
+                       {
+                               .first = 0x0F,
+                               .last = 0x17,
+                       },
+                       {
+                               .first = 0x20,
+                               .last = 0x20,
+                       },
+                       {
+                               .first = 0x30,
+                               .last = 0x30,
+                       },
+                       {
+                               .first = 0x32,
+                               .last = 0x3A,
+                       },
+               },
+       },
+       [AB8500_REGU_CTRL1] = {
+               .num_ranges = 3,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x00,
+                       },
+                       {
+                               .first = 0x03,
+                               .last = 0x11,
+                       },
+                       {
+                               .first = 0x80,
+                               .last = 0x86,
+                       },
+               },
+       },
+       [AB8500_REGU_CTRL2] = {
+               .num_ranges = 6,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x06,
+                       },
+                       {
+                               .first = 0x08,
+                               .last = 0x15,
+                       },
+                       {
+                               .first = 0x17,
+                               .last = 0x19,
+                       },
+                       {
+                               .first = 0x1B,
+                               .last = 0x1D,
+                       },
+                       {
+                               .first = 0x1F,
+                               .last = 0x30,
+                       },
+                       {
+                               .first = 0x40,
+                               .last = 0x48,
+                       },
+                       /* 0x80-0x8B is SIM registers and should
+                        * not be accessed from here */
+               },
+       },
+       [AB8500_USB] = {
+               .num_ranges = 3,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x80,
+                               .last = 0x83,
+                       },
+                       {
+                               .first = 0x87,
+                               .last = 0x8A,
+                       },
+                       {
+                               .first = 0x91,
+                               .last = 0x94,
+                       },
+               },
+       },
+       [AB8500_TVOUT] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_DBI] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_ECI_AV_ACC] = {
+               .num_ranges = 1,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x80,
+                               .last = 0x82,
+                       },
+               },
+       },
+       [AB8500_RESERVED] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_GPADC] = {
+               .num_ranges = 1,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x08,
+                       },
+               },
+       },
+       [AB8500_CHARGER] = {
+               .num_ranges = 9,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x02,
+                               .last = 0x03,
+                       },
+                       {
+                               .first = 0x05,
+                               .last = 0x05,
+                       },
+                       {
+                               .first = 0x40,
+                               .last = 0x44,
+                       },
+                       {
+                               .first = 0x50,
+                               .last = 0x57,
+                       },
+                       {
+                               .first = 0x60,
+                               .last = 0x60,
+                       },
+                       {
+                               .first = 0xA0,
+                               .last = 0xA7,
+                       },
+                       {
+                               .first = 0xAF,
+                               .last = 0xB2,
+                       },
+                       {
+                               .first = 0xC0,
+                               .last = 0xC2,
+                       },
+                       {
+                               .first = 0xF5,
+                               .last = 0xF5,
+                       },
+               },
+       },
+       [AB8500_GAS_GAUGE] = {
+               .num_ranges = 3,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x00,
+                       },
+                       {
+                               .first = 0x07,
+                               .last = 0x0A,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x14,
+                       },
+               },
+       },
+       [AB8500_AUDIO] = {
+               .num_ranges = 1,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x83,
+                       },
+               },
+       },
+       [AB8500_INTERRUPT] = {
+               .num_ranges = 11,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x04,
+                       },
+                       {
+                               .first = 0x06,
+                               .last = 0x07,
+                       },
+                       {
+                               .first = 0x09,
+                               .last = 0x09,
+                       },
+                       {
+                               .first = 0x0B,
+                               .last = 0x0C,
+                       },
+                       {
+                               .first = 0x12,
+                               .last = 0x15,
+                       },
+                       {
+                               .first = 0x18,
+                               .last = 0x18,
+                       },
+                       /* Latch registers should not be read here */
+                       {
+                               .first = 0x40,
+                               .last = 0x44,
+                       },
+                       {
+                               .first = 0x46,
+                               .last = 0x49,
+                       },
+                       {
+                               .first = 0x4B,
+                               .last = 0x4D,
+                       },
+                       {
+                               .first = 0x52,
+                               .last = 0x55,
+                       },
+                       {
+                               .first = 0x58,
+                               .last = 0x58,
+                       },
+                       /* LatchHier registers should not be read here */
+               },
+       },
+       [AB8500_RTC] = {
+               .num_ranges = 2,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x14,
+                       },
+                       {
+                               .first = 0x16,
+                               .last = 0x17,
+                       },
+               },
+       },
+       [AB8500_MISC] = {
+               .num_ranges = 8,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x06,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x16,
+                       },
+                       {
+                               .first = 0x20,
+                               .last = 0x26,
+                       },
+                       {
+                               .first = 0x30,
+                               .last = 0x36,
+                       },
+                       {
+                               .first = 0x40,
+                               .last = 0x46,
+                       },
+                       {
+                               .first = 0x50,
+                               .last = 0x50,
+                       },
+                       {
+                               .first = 0x60,
+                               .last = 0x6B,
+                       },
+                       {
+                               .first = 0x80,
+                               .last = 0x82,
+                       },
+               },
+       },
+       [AB8500_DEVELOPMENT] = {
+               .num_ranges = 2,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x00,
+                       },
+                       {
+                               .first = 0x05,
+                               .last = 0x05,
+                       },
+               },
+       },
+       [AB8500_DEBUG] = {
+               .num_ranges = 1,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x05,
+                               .last = 0x07,
+                       },
+               },
+       },
+       [AB8500_PROD_TEST] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_STE_TEST] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_OTP_EMUL] = {
+               .num_ranges = 1,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x01,
+                               .last = 0x15,
+                       },
+               },
+       },
+};
+
+struct ab8500_prcmu_ranges ab8540_debug_ranges[AB8500_NUM_BANKS] = {
+       [AB8500_M_FSM_RANK] = {
+               .num_ranges = 1,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x0B,
+                       },
+               },
+       },
+       [AB8500_SYS_CTRL1_BLOCK] = {
+               .num_ranges = 6,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x04,
+                       },
+                       {
+                               .first = 0x42,
+                               .last = 0x42,
+                       },
+                       {
+                               .first = 0x50,
+                               .last = 0x54,
+                       },
+                       {
+                               .first = 0x57,
+                               .last = 0x57,
+                       },
+                       {
+                               .first = 0x80,
+                               .last = 0x83,
+                       },
+                       {
+                               .first = 0x90,
+                               .last = 0x90,
+                       },
+               },
+       },
+       [AB8500_SYS_CTRL2_BLOCK] = {
+               .num_ranges = 5,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x0D,
+                       },
+                       {
+                               .first = 0x0F,
+                               .last = 0x10,
+                       },
+                       {
+                               .first = 0x20,
+                               .last = 0x21,
+                       },
+                       {
+                               .first = 0x32,
+                               .last = 0x3C,
+                       },
+                       {
+                               .first = 0x40,
+                               .last = 0x42,
+                       },
+               },
+       },
+       [AB8500_REGU_CTRL1] = {
+               .num_ranges = 4,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x03,
+                               .last = 0x15,
+                       },
+                       {
+                               .first = 0x20,
+                               .last = 0x20,
+                       },
+                       {
+                               .first = 0x80,
+                               .last = 0x85,
+                       },
+                       {
+                               .first = 0x87,
+                               .last = 0x88,
+                       },
+               },
+       },
+       [AB8500_REGU_CTRL2] = {
+               .num_ranges = 8,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x06,
+                       },
+                       {
+                               .first = 0x08,
+                               .last = 0x15,
+                       },
+                       {
+                               .first = 0x17,
+                               .last = 0x19,
+                       },
+                       {
+                               .first = 0x1B,
+                               .last = 0x1D,
+                       },
+                       {
+                               .first = 0x1F,
+                               .last = 0x2F,
+                       },
+                       {
+                               .first = 0x31,
+                               .last = 0x3A,
+                       },
+                       {
+                               .first = 0x43,
+                               .last = 0x44,
+                       },
+                       {
+                               .first = 0x48,
+                               .last = 0x49,
+                       },
+               },
+       },
+       [AB8500_USB] = {
+               .num_ranges = 3,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x80,
+                               .last = 0x83,
+                       },
+                       {
+                               .first = 0x87,
+                               .last = 0x8A,
+                       },
+                       {
+                               .first = 0x91,
+                               .last = 0x94,
+                       },
+               },
+       },
+       [AB8500_TVOUT] = {
+               .num_ranges = 0,
+               .range = NULL
+       },
+       [AB8500_DBI] = {
+               .num_ranges = 4,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x07,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x11,
+                       },
+                       {
+                               .first = 0x20,
+                               .last = 0x21,
+                       },
+                       {
+                               .first = 0x30,
+                               .last = 0x43,
+                       },
+               },
+       },
+       [AB8500_ECI_AV_ACC] = {
+               .num_ranges = 2,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x03,
+                       },
+                       {
+                               .first = 0x80,
+                               .last = 0x82,
+                       },
+               },
+       },
+       [AB8500_RESERVED] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_GPADC] = {
+               .num_ranges = 4,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x01,
+                       },
+                       {
+                               .first = 0x04,
+                               .last = 0x06,
+                       },
+                       {
+                               .first = 0x09,
+                               .last = 0x0A,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x14,
+                       },
+               },
+       },
+       [AB8500_CHARGER] = {
+               .num_ranges = 10,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x00,
+                       },
+                       {
+                               .first = 0x02,
+                               .last = 0x05,
+                       },
+                       {
+                               .first = 0x40,
+                               .last = 0x44,
+                       },
+                       {
+                               .first = 0x50,
+                               .last = 0x57,
+                       },
+                       {
+                               .first = 0x60,
+                               .last = 0x60,
+                       },
+                       {
+                               .first = 0x70,
+                               .last = 0x70,
+                       },
+                       {
+                               .first = 0xA0,
+                               .last = 0xA9,
+                       },
+                       {
+                               .first = 0xAF,
+                               .last = 0xB2,
+                       },
+                       {
+                               .first = 0xC0,
+                               .last = 0xC6,
+                       },
+                       {
+                               .first = 0xF5,
+                               .last = 0xF5,
+                       },
+               },
+       },
+       [AB8500_GAS_GAUGE] = {
+               .num_ranges = 3,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x00,
+                       },
+                       {
+                               .first = 0x07,
+                               .last = 0x0A,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x14,
+                       },
+               },
+       },
+       [AB8500_AUDIO] = {
+               .num_ranges = 1,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x9f,
+                       },
+               },
+       },
+       [AB8500_INTERRUPT] = {
+               .num_ranges = 6,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x05,
+                       },
+                       {
+                               .first = 0x0B,
+                               .last = 0x0D,
+                       },
+                       {
+                               .first = 0x12,
+                               .last = 0x20,
+                       },
+                       /* Latch registers should not be read here */
+                       {
+                               .first = 0x40,
+                               .last = 0x45,
+                       },
+                       {
+                               .first = 0x4B,
+                               .last = 0x4D,
+                       },
+                       {
+                               .first = 0x52,
+                               .last = 0x60,
+                       },
+                       /* LatchHier registers should not be read here */
+               },
+       },
+       [AB8500_RTC] = {
+               .num_ranges = 3,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x07,
+                       },
+                       {
+                               .first = 0x0B,
+                               .last = 0x18,
+                       },
+                       {
+                               .first = 0x20,
+                               .last = 0x25,
+                       },
+               },
+       },
+       [AB8500_MISC] = {
+               .num_ranges = 9,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x06,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x16,
+                       },
+                       {
+                               .first = 0x20,
+                               .last = 0x26,
+                       },
+                       {
+                               .first = 0x30,
+                               .last = 0x36,
+                       },
+                       {
+                               .first = 0x40,
+                               .last = 0x49,
+                       },
+                       {
+                               .first = 0x50,
+                               .last = 0x50,
+                       },
+                       {
+                               .first = 0x60,
+                               .last = 0x6B,
+                       },
+                       {
+                               .first = 0x70,
+                               .last = 0x74,
+                       },
+                       {
+                               .first = 0x80,
+                               .last = 0x82,
+                       },
+               },
+       },
+       [AB8500_DEVELOPMENT] = {
+               .num_ranges = 3,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x01,
+                       },
+                       {
+                               .first = 0x06,
+                               .last = 0x06,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x21,
+                       },
+               },
+       },
+       [AB8500_DEBUG] = {
+               .num_ranges = 3,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x01,
+                               .last = 0x0C,
+                       },
+                       {
+                               .first = 0x0E,
+                               .last = 0x11,
+                       },
+                       {
+                               .first = 0x80,
+                               .last = 0x81,
+                       },
+               },
+       },
+       [AB8500_PROD_TEST] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_STE_TEST] = {
+               .num_ranges = 0,
+               .range = NULL,
+       },
+       [AB8500_OTP_EMUL] = {
+               .num_ranges = 1,
+               .range = (struct ab8500_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x3F,
+                       },
+               },
+       },
+};
+
+
+static irqreturn_t ab8500_debug_handler(int irq, void *data)
+{
+       char buf[16];
+       struct kobject *kobj = (struct kobject *)data;
+       unsigned int irq_abb = irq - irq_first;
+
+       if (irq_abb < num_irqs)
+               irq_count[irq_abb]++;
+       /*
+        * This makes it possible to use poll for events (POLLPRI | POLLERR)
+        * from userspace on sysfs file named <irq-nr>
+        */
+       sprintf(buf, "%d", irq);
+       sysfs_notify(kobj, NULL, buf);
+
+       return IRQ_HANDLED;
+}
+
+/* Prints to seq_file or log_buf */
+static int ab8500_registers_print(struct device *dev, u32 bank,
+                               struct seq_file *s)
+{
+       unsigned int i;
+
+       for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
+               u32 reg;
+
+               for (reg = debug_ranges[bank].range[i].first;
+                       reg <= debug_ranges[bank].range[i].last;
+                       reg++) {
+                       u8 value;
+                       int err;
+
+                       err = abx500_get_register_interruptible(dev,
+                               (u8)bank, (u8)reg, &value);
+                       if (err < 0) {
+                               dev_err(dev, "ab->read fail %d\n", err);
+                               return err;
+                       }
+
+                       if (s) {
+                               err = seq_printf(s, "  [0x%02X/0x%02X]: 0x%02X\n",
+                                       bank, reg, value);
+                               if (err < 0) {
+                                       /* Error is not returned here since
+                                        * the output is wanted in any case */
+                                       return 0;
+                               }
+                       } else {
+                               printk(KERN_INFO" [0x%02X/0x%02X]: 0x%02X\n",
+                                       bank, reg, value);
+                       }
+               }
+       }
+       return 0;
+}
+
+static int ab8500_print_bank_registers(struct seq_file *s, void *p)
+{
+       struct device *dev = s->private;
+       u32 bank = debug_bank;
+
+       seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+       seq_printf(s, " bank 0x%02X:\n", bank);
+
+       ab8500_registers_print(dev, bank, s);
+       return 0;
+}
+
+static int ab8500_registers_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_print_bank_registers, inode->i_private);
+}
+
+static const struct file_operations ab8500_registers_fops = {
+       .open = ab8500_registers_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_print_all_banks(struct seq_file *s, void *p)
+{
+       struct device *dev = s->private;
+       unsigned int i;
+       int err;
+
+       seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+       for (i = 0; i < AB8500_NUM_BANKS; i++) {
+               err = seq_printf(s, " bank 0x%02X:\n", i);
+
+               ab8500_registers_print(dev, i, s);
+       }
+       return 0;
+}
+
+/* Dump registers to kernel log */
+void ab8500_dump_all_banks(struct device *dev)
+{
+       unsigned int i;
+
+       printk(KERN_INFO"ab8500 register values:\n");
+
+       for (i = 1; i < AB8500_NUM_BANKS; i++) {
+               printk(KERN_INFO" bank 0x%02X:\n", i);
+               ab8500_registers_print(dev, i, NULL);
+       }
+}
+
+/* Space for 500 registers. */
+#define DUMP_MAX_REGS 700
+struct ab8500_register_dump
+{
+       u8 bank;
+       u8 reg;
+       u8 value;
+} ab8500_complete_register_dump[DUMP_MAX_REGS];
+
+extern int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
+
+/* This shall only be called upon kernel panic! */
+void ab8500_dump_all_banks_to_mem(void)
+{
+       int i, r = 0;
+       u8 bank;
+       int err = 0;
+
+       pr_info("Saving all ABB registers at \"ab8500_complete_register_dump\" "
+               "for crash analyze.\n");
+
+       for (bank = 0; bank < AB8500_NUM_BANKS; bank++) {
+               for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
+                       u8 reg;
+
+                       for (reg = debug_ranges[bank].range[i].first;
+                            reg <= debug_ranges[bank].range[i].last;
+                            reg++) {
+                               u8 value;
+
+                               err = prcmu_abb_read(bank, reg, &value, 1);
+
+                               if (err < 0)
+                                       goto out;
+
+                               ab8500_complete_register_dump[r].bank = bank;
+                               ab8500_complete_register_dump[r].reg = reg;
+                               ab8500_complete_register_dump[r].value = value;
+
+                               r++;
+
+                               if (r >= DUMP_MAX_REGS) {
+                                       pr_err("%s: too many register to dump!\n",
+                                               __func__);
+                                       err = -EINVAL;
+                                       goto out;
+                               }
+                       }
+               }
+       }
+out:
+       if (err >= 0)
+               pr_info("Saved all ABB registers.\n");
+       else
+               pr_info("Failed to save all ABB registers.\n");
+}
+
+static int ab8500_all_banks_open(struct inode *inode, struct file *file)
+{
+       struct seq_file *s;
+       int err;
+
+       err = single_open(file, ab8500_print_all_banks, inode->i_private);
+       if (!err) {
+               /* Default buf size in seq_read is not enough */
+               s = (struct seq_file *)file->private_data;
+               s->size = (PAGE_SIZE * 2);
+               s->buf = kmalloc(s->size, GFP_KERNEL);
+               if (!s->buf) {
+                       single_release(inode, file);
+                       err = -ENOMEM;
+               }
+       }
+       return err;
+}
+
+static const struct file_operations ab8500_all_banks_fops = {
+       .open = ab8500_all_banks_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_bank_print(struct seq_file *s, void *p)
+{
+       return seq_printf(s, "0x%02X\n", debug_bank);
+}
+
+static int ab8500_bank_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_bank_print, inode->i_private);
+}
+
+static ssize_t ab8500_bank_write(struct file *file,
+       const char __user *user_buf,
+       size_t count, loff_t *ppos)
+{
+       struct device *dev = ((struct seq_file *)(file->private_data))->private;
+       unsigned long user_bank;
+       int err;
+
+       err = kstrtoul_from_user(user_buf, count, 0, &user_bank);
+       if (err)
+               return err;
+
+       if (user_bank >= AB8500_NUM_BANKS) {
+               dev_err(dev, "debugfs error input > number of banks\n");
+               return -EINVAL;
+       }
+
+       debug_bank = user_bank;
+
+       return count;
+}
+
+static int ab8500_address_print(struct seq_file *s, void *p)
+{
+       return seq_printf(s, "0x%02X\n", debug_address);
+}
+
+static int ab8500_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_address_print, inode->i_private);
+}
+
+static ssize_t ab8500_address_write(struct file *file,
+                                   const char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct device *dev = ((struct seq_file *)(file->private_data))->private;
+       unsigned long user_address;
+       int err;
+
+       err = kstrtoul_from_user(user_buf, count, 0, &user_address);
+       if (err)
+               return err;
+
+       if (user_address > 0xff) {
+               dev_err(dev, "debugfs error input > 0xff\n");
+               return -EINVAL;
+       }
+       debug_address = user_address;
+
+       return count;
+}
+
+static int ab8500_val_print(struct seq_file *s, void *p)
+{
+       struct device *dev = s->private;
+       int ret;
+       u8 regvalue;
+
+       ret = abx500_get_register_interruptible(dev,
+               (u8)debug_bank, (u8)debug_address, &regvalue);
+       if (ret < 0) {
+               dev_err(dev, "abx500_get_reg fail %d, %d\n",
+                       ret, __LINE__);
+               return -EINVAL;
+       }
+       seq_printf(s, "0x%02X\n", regvalue);
+
+       return 0;
+}
+
+static int ab8500_val_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_val_print, inode->i_private);
+}
+
+static ssize_t ab8500_val_write(struct file *file,
+                               const char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct device *dev = ((struct seq_file *)(file->private_data))->private;
+       unsigned long user_val;
+       int err;
+
+       err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+       if (err)
+               return err;
+
+       if (user_val > 0xff) {
+               dev_err(dev, "debugfs error input > 0xff\n");
+               return -EINVAL;
+       }
+       err = abx500_set_register_interruptible(dev,
+               (u8)debug_bank, debug_address, (u8)user_val);
+       if (err < 0) {
+               printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__);
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+/*
+ * Interrupt status
+ */
+static u32 num_interrupts[AB8500_MAX_NR_IRQS];
+static u32 num_wake_interrupts[AB8500_MAX_NR_IRQS];
+static int num_interrupt_lines;
+
+bool __attribute__((weak)) suspend_test_wake_cause_interrupt_is_mine(u32 my_int)
+{
+       return false;
+}
+
+void ab8500_debug_register_interrupt(int line)
+{
+       if (line < num_interrupt_lines) {
+               num_interrupts[line]++;
+               if (suspend_test_wake_cause_interrupt_is_mine(IRQ_DB8500_AB8500))
+                       num_wake_interrupts[line]++;
+       }
+}
+
+static int ab8500_interrupts_print(struct seq_file *s, void *p)
+{
+       int line;
+
+       seq_printf(s, "name: number:  number of: wake:\n");
+
+       for (line = 0; line < num_interrupt_lines; line++) {
+               struct irq_desc *desc = irq_to_desc(line + irq_first);
+               struct irqaction *action = desc->action;
+
+               seq_printf(s, "%3i:  %6i %4i", line,
+                          num_interrupts[line],
+                          num_wake_interrupts[line]);
+
+               if (desc && desc->name)
+                       seq_printf(s, "-%-8s", desc->name);
+               if (action) {
+                       seq_printf(s, "  %s", action->name);
+                       while ((action = action->next) != NULL)
+                               seq_printf(s, ", %s", action->name);
+               }
+               seq_putc(s, '\n');
+       }
+
+       return 0;
+}
+
+static int ab8500_interrupts_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_interrupts_print, inode->i_private);
+}
+
+/*
+ * - HWREG DB8500 formated routines
+ */
+static int ab8500_hwreg_print(struct seq_file *s, void *d)
+{
+       struct device *dev = s->private;
+       int ret;
+       u8 regvalue;
+
+       ret = abx500_get_register_interruptible(dev,
+               (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
+       if (ret < 0) {
+               dev_err(dev, "abx500_get_reg fail %d, %d\n",
+                       ret, __LINE__);
+               return -EINVAL;
+       }
+
+       if (hwreg_cfg.shift >= 0)
+               regvalue >>= hwreg_cfg.shift;
+       else
+               regvalue <<= -hwreg_cfg.shift;
+       regvalue &= hwreg_cfg.mask;
+
+       if (REG_FMT_DEC(&hwreg_cfg))
+               seq_printf(s, "%d\n", regvalue);
+       else
+               seq_printf(s, "0x%02X\n", regvalue);
+       return 0;
+}
+
+static int ab8500_hwreg_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_hwreg_print, inode->i_private);
+}
+
+#define AB8500_SUPPLY_CONTROL_CONFIG_1 0x01
+#define AB8500_SUPPLY_CONTROL_REG 0x00
+#define AB8500_FIRST_SIM_REG 0x80
+#define AB8500_LAST_SIM_REG 0x8B
+#define AB8505_LAST_SIM_REG 0x8C
+
+static int ab8500_print_modem_registers(struct seq_file *s, void *p)
+{
+       struct device *dev = s->private;
+       struct ab8500 *ab8500;
+       int err;
+       u8 value;
+       u8 orig_value;
+       u32 bank = AB8500_REGU_CTRL2;
+       u32 last_sim_reg = AB8500_LAST_SIM_REG;
+       u32 reg;
+
+       ab8500 = dev_get_drvdata(dev->parent);
+       dev_warn(dev, "WARNING! This operation can interfer with modem side\n"
+               "and should only be done with care\n");
+
+       err = abx500_get_register_interruptible(dev,
+               AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, &orig_value);
+       if (err < 0) {
+               dev_err(dev, "ab->read fail %d\n", err);
+               return err;
+       }
+       /* Config 1 will allow APE side to read SIM registers */
+       err = abx500_set_register_interruptible(dev,
+               AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG,
+               AB8500_SUPPLY_CONTROL_CONFIG_1);
+       if (err < 0) {
+               dev_err(dev, "ab->write fail %d\n", err);
+               return err;
+       }
+
+       seq_printf(s, " bank 0x%02X:\n", bank);
+
+       if (is_ab9540(ab8500) || is_ab8505(ab8500))
+               last_sim_reg = AB8505_LAST_SIM_REG;
+
+       for (reg = AB8500_FIRST_SIM_REG; reg <= last_sim_reg; reg++) {
+               err = abx500_get_register_interruptible(dev,
+                       bank, reg, &value);
+               if (err < 0) {
+                       dev_err(dev, "ab->read fail %d\n", err);
+                       return err;
+               }
+               err = seq_printf(s, "  [0x%02X/0x%02X]: 0x%02X\n",
+                       bank, reg, value);
+       }
+       err = abx500_set_register_interruptible(dev,
+               AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, orig_value);
+       if (err < 0) {
+               dev_err(dev, "ab->write fail %d\n", err);
+               return err;
+       }
+       return 0;
+}
+
+static int ab8500_modem_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_print_modem_registers, inode->i_private);
+}
+
+static const struct file_operations ab8500_modem_fops = {
+       .open = ab8500_modem_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
+{
+       int bat_ctrl_raw;
+       int bat_ctrl_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+               BAT_CTRL, bat_ctrl_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+               bat_ctrl_convert, bat_ctrl_raw);
+}
+
+static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_bat_ctrl_fops = {
+       .open = ab8500_gpadc_bat_ctrl_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
+{
+       int btemp_ball_raw;
+       int btemp_ball_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
+               btemp_ball_raw);
+
+       return seq_printf(s,
+               "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw);
+}
+
+static int ab8500_gpadc_btemp_ball_open(struct inode *inode,
+                                       struct file *file)
+{
+       return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_btemp_ball_fops = {
+       .open = ab8500_gpadc_btemp_ball_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
+{
+       int main_charger_v_raw;
+       int main_charger_v_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+               MAIN_CHARGER_V, main_charger_v_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+                       main_charger_v_convert, main_charger_v_raw);
+}
+
+static int ab8500_gpadc_main_charger_v_open(struct inode *inode,
+                                           struct file *file)
 {
-       if (line < num_interrupt_lines)
-               num_interrupts[line]++;
+       return single_open(file, ab8500_gpadc_main_charger_v_print,
+               inode->i_private);
 }
 
-static int ab8500_interrupts_print(struct seq_file *s, void *p)
+static const struct file_operations ab8500_gpadc_main_charger_v_fops = {
+       .open = ab8500_gpadc_main_charger_v_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
 {
-       int line;
+       int acc_detect1_raw;
+       int acc_detect1_convert;
+       struct ab8500_gpadc *gpadc;
 
-       seq_printf(s, "irq:  number of\n");
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
+               acc_detect1_raw);
 
-       for (line = 0; line < num_interrupt_lines; line++)
-               seq_printf(s, "%3i:  %6i\n", line, num_interrupts[line]);
+       return seq_printf(s, "%d,0x%X\n",
+               acc_detect1_convert, acc_detect1_raw);
+}
 
-       return 0;
+static int ab8500_gpadc_acc_detect1_open(struct inode *inode,
+                                        struct file *file)
+{
+       return single_open(file, ab8500_gpadc_acc_detect1_print,
+               inode->i_private);
 }
 
-static int ab8500_interrupts_open(struct inode *inode, struct file *file)
+static const struct file_operations ab8500_gpadc_acc_detect1_fops = {
+       .open = ab8500_gpadc_acc_detect1_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
 {
-       return single_open(file, ab8500_interrupts_print, inode->i_private);
+       int acc_detect2_raw;
+       int acc_detect2_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+               ACC_DETECT2, acc_detect2_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+               acc_detect2_convert, acc_detect2_raw);
 }
 
-/*
- * - HWREG DB8500 formated routines
- */
-static int ab8500_hwreg_print(struct seq_file *s, void *d)
+static int ab8500_gpadc_acc_detect2_open(struct inode *inode,
+               struct file *file)
 {
-       struct device *dev = s->private;
-       int ret;
-       u8 regvalue;
+       return single_open(file, ab8500_gpadc_acc_detect2_print,
+               inode->i_private);
+}
 
-       ret = abx500_get_register_interruptible(dev,
-               (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
-       if (ret < 0) {
-               dev_err(dev, "abx500_get_reg fail %d, %d\n",
-                       ret, __LINE__);
-               return -EINVAL;
-       }
+static const struct file_operations ab8500_gpadc_acc_detect2_fops = {
+       .open = ab8500_gpadc_acc_detect2_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
 
-       if (hwreg_cfg.shift >= 0)
-               regvalue >>= hwreg_cfg.shift;
-       else
-               regvalue <<= -hwreg_cfg.shift;
-       regvalue &= hwreg_cfg.mask;
+static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
+{
+       int aux1_raw;
+       int aux1_convert;
+       struct ab8500_gpadc *gpadc;
 
-       if (REG_FMT_DEC(&hwreg_cfg))
-               seq_printf(s, "%d\n", regvalue);
-       else
-               seq_printf(s, "0x%02X\n", regvalue);
-       return 0;
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
+               aux1_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+               aux1_convert, aux1_raw);
 }
 
-static int ab8500_hwreg_open(struct inode *inode, struct file *file)
+static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, ab8500_hwreg_print, inode->i_private);
+       return single_open(file, ab8500_gpadc_aux1_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_aux1_fops = {
+       .open = ab8500_gpadc_aux1_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
+{
+       int aux2_raw;
+       int aux2_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
+               aux2_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+                       aux2_convert, aux2_raw);
+}
+
+static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_gpadc_aux2_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_aux2_fops = {
+       .open = ab8500_gpadc_aux2_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
+{
+       int main_bat_v_raw;
+       int main_bat_v_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
+               main_bat_v_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+               main_bat_v_convert, main_bat_v_raw);
+}
+
+static int ab8500_gpadc_main_bat_v_open(struct inode *inode,
+                                       struct file *file)
+{
+       return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_bat_v_fops = {
+       .open = ab8500_gpadc_main_bat_v_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
+{
+       int vbus_v_raw;
+       int vbus_v_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       vbus_v_raw =  ab8500_gpadc_read_raw(gpadc, VBUS_V,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
+               vbus_v_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+               vbus_v_convert, vbus_v_raw);
+}
+
+static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_vbus_v_fops = {
+       .open = ab8500_gpadc_vbus_v_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
+{
+       int main_charger_c_raw;
+       int main_charger_c_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+               MAIN_CHARGER_C, main_charger_c_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+               main_charger_c_convert, main_charger_c_raw);
+}
+
+static int ab8500_gpadc_main_charger_c_open(struct inode *inode,
+               struct file *file)
+{
+       return single_open(file, ab8500_gpadc_main_charger_c_print,
+               inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_charger_c_fops = {
+       .open = ab8500_gpadc_main_charger_c_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
+{
+       int usb_charger_c_raw;
+       int usb_charger_c_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+               USB_CHARGER_C, usb_charger_c_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+               usb_charger_c_convert, usb_charger_c_raw);
+}
+
+static int ab8500_gpadc_usb_charger_c_open(struct inode *inode,
+               struct file *file)
+{
+       return single_open(file, ab8500_gpadc_usb_charger_c_print,
+               inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_usb_charger_c_fops = {
+       .open = ab8500_gpadc_usb_charger_c_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
+{
+       int bk_bat_v_raw;
+       int bk_bat_v_convert;
+       struct ab8500_gpadc *gpadc;
+
+       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+       bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+               BK_BAT_V, bk_bat_v_raw);
+
+       return seq_printf(s, "%d,0x%X\n",
+               bk_bat_v_convert, bk_bat_v_raw);
+}
+
+static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private);
 }
 
-static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
+static const struct file_operations ab8500_gpadc_bk_bat_v_fops = {
+       .open = ab8500_gpadc_bk_bat_v_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
 {
-       int bat_ctrl_raw;
-       int bat_ctrl_convert;
+       int die_temp_raw;
+       int die_temp_convert;
        struct ab8500_gpadc *gpadc;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL);
-       bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
-                       BAT_CTRL, bat_ctrl_raw);
+       die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
+               die_temp_raw);
 
        return seq_printf(s, "%d,0x%X\n",
-                       bat_ctrl_convert, bat_ctrl_raw);
+               die_temp_convert, die_temp_raw);
 }
 
-static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file)
+static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private);
+       return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_bat_ctrl_fops = {
-       .open = ab8500_gpadc_bat_ctrl_open,
+static const struct file_operations ab8500_gpadc_die_temp_fops = {
+       .open = ab8500_gpadc_die_temp_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
+static int ab8500_gpadc_usb_id_print(struct seq_file *s, void *p)
 {
-       int btemp_ball_raw;
-       int btemp_ball_convert;
+       int usb_id_raw;
+       int usb_id_convert;
        struct ab8500_gpadc *gpadc;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL);
-       btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
-                       btemp_ball_raw);
+       usb_id_raw = ab8500_gpadc_read_raw(gpadc, USB_ID,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       usb_id_convert = ab8500_gpadc_ad_to_voltage(gpadc, USB_ID,
+               usb_id_raw);
 
-       return seq_printf(s,
-                       "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw);
+       return seq_printf(s, "%d,0x%X\n",
+               usb_id_convert, usb_id_raw);
 }
 
-static int ab8500_gpadc_btemp_ball_open(struct inode *inode,
-               struct file *file)
+static int ab8500_gpadc_usb_id_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private);
+       return single_open(file, ab8500_gpadc_usb_id_print, inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_btemp_ball_fops = {
-       .open = ab8500_gpadc_btemp_ball_open,
+static const struct file_operations ab8500_gpadc_usb_id_fops = {
+       .open = ab8500_gpadc_usb_id_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
+static int ab8540_gpadc_xtal_temp_print(struct seq_file *s, void *p)
 {
-       int main_charger_v_raw;
-       int main_charger_v_convert;
+       int xtal_temp_raw;
+       int xtal_temp_convert;
        struct ab8500_gpadc *gpadc;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V);
-       main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
-                       MAIN_CHARGER_V, main_charger_v_raw);
+       xtal_temp_raw = ab8500_gpadc_read_raw(gpadc, XTAL_TEMP,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       xtal_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, XTAL_TEMP,
+               xtal_temp_raw);
 
        return seq_printf(s, "%d,0x%X\n",
-                       main_charger_v_convert, main_charger_v_raw);
+               xtal_temp_convert, xtal_temp_raw);
 }
 
-static int ab8500_gpadc_main_charger_v_open(struct inode *inode,
-               struct file *file)
+static int ab8540_gpadc_xtal_temp_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, ab8500_gpadc_main_charger_v_print,
-                       inode->i_private);
+       return single_open(file, ab8540_gpadc_xtal_temp_print,
+               inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_main_charger_v_fops = {
-       .open = ab8500_gpadc_main_charger_v_open,
+static const struct file_operations ab8540_gpadc_xtal_temp_fops = {
+       .open = ab8540_gpadc_xtal_temp_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
+static int ab8540_gpadc_vbat_true_meas_print(struct seq_file *s, void *p)
 {
-       int acc_detect1_raw;
-       int acc_detect1_convert;
+       int vbat_true_meas_raw;
+       int vbat_true_meas_convert;
        struct ab8500_gpadc *gpadc;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1);
-       acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
-                       acc_detect1_raw);
+       vbat_true_meas_raw = ab8500_gpadc_read_raw(gpadc, VBAT_TRUE_MEAS,
+               avg_sample, trig_edge, trig_timer, conv_type);
+       vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS,
+               vbat_true_meas_raw);
 
        return seq_printf(s, "%d,0x%X\n",
-                       acc_detect1_convert, acc_detect1_raw);
+               vbat_true_meas_convert, vbat_true_meas_raw);
 }
 
-static int ab8500_gpadc_acc_detect1_open(struct inode *inode,
+static int ab8540_gpadc_vbat_true_meas_open(struct inode *inode,
                struct file *file)
 {
-       return single_open(file, ab8500_gpadc_acc_detect1_print,
-                       inode->i_private);
+       return single_open(file, ab8540_gpadc_vbat_true_meas_print,
+               inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_acc_detect1_fops = {
-       .open = ab8500_gpadc_acc_detect1_open,
+static const struct file_operations ab8540_gpadc_vbat_true_meas_fops = {
+       .open = ab8540_gpadc_vbat_true_meas_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
+static int ab8540_gpadc_bat_ctrl_and_ibat_print(struct seq_file *s, void *p)
 {
-       int acc_detect2_raw;
-       int acc_detect2_convert;
+       int bat_ctrl_raw;
+       int bat_ctrl_convert;
+       int ibat_raw;
+       int ibat_convert;
        struct ab8500_gpadc *gpadc;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2);
-       acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
-           ACC_DETECT2, acc_detect2_raw);
+       bat_ctrl_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_CTRL_AND_IBAT,
+               avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw);
 
-       return seq_printf(s, "%d,0x%X\n",
-                       acc_detect2_convert, acc_detect2_raw);
+       bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, BAT_CTRL,
+               bat_ctrl_raw);
+       ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL,
+               ibat_raw);
+
+       return seq_printf(s, "%d,0x%X\n"  "%d,0x%X\n",
+               bat_ctrl_convert, bat_ctrl_raw,
+               ibat_convert, ibat_raw);
 }
 
-static int ab8500_gpadc_acc_detect2_open(struct inode *inode,
+static int ab8540_gpadc_bat_ctrl_and_ibat_open(struct inode *inode,
                struct file *file)
 {
-       return single_open(file, ab8500_gpadc_acc_detect2_print,
-           inode->i_private);
+       return single_open(file, ab8540_gpadc_bat_ctrl_and_ibat_print,
+               inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_acc_detect2_fops = {
-       .open = ab8500_gpadc_acc_detect2_open,
+static const struct file_operations ab8540_gpadc_bat_ctrl_and_ibat_fops = {
+       .open = ab8540_gpadc_bat_ctrl_and_ibat_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
+static int ab8540_gpadc_vbat_meas_and_ibat_print(struct seq_file *s, void *p)
 {
-       int aux1_raw;
-       int aux1_convert;
+       int vbat_meas_raw;
+       int vbat_meas_convert;
+       int ibat_raw;
+       int ibat_convert;
        struct ab8500_gpadc *gpadc;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1);
-       aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
-                       aux1_raw);
-
-       return seq_printf(s, "%d,0x%X\n",
-                       aux1_convert, aux1_raw);
+       vbat_meas_raw = ab8500_gpadc_double_read_raw(gpadc, VBAT_MEAS_AND_IBAT,
+               avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw);
+       vbat_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
+               vbat_meas_raw);
+       ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL,
+               ibat_raw);
+
+       return seq_printf(s, "%d,0x%X\n"  "%d,0x%X\n",
+               vbat_meas_convert, vbat_meas_raw,
+               ibat_convert, ibat_raw);
 }
 
-static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file)
+static int ab8540_gpadc_vbat_meas_and_ibat_open(struct inode *inode,
+               struct file *file)
 {
-       return single_open(file, ab8500_gpadc_aux1_print, inode->i_private);
+       return single_open(file, ab8540_gpadc_vbat_meas_and_ibat_print,
+               inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_aux1_fops = {
-       .open = ab8500_gpadc_aux1_open,
+static const struct file_operations ab8540_gpadc_vbat_meas_and_ibat_fops = {
+       .open = ab8540_gpadc_vbat_meas_and_ibat_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
+static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, void *p)
 {
-       int aux2_raw;
-       int aux2_convert;
+       int vbat_true_meas_raw;
+       int vbat_true_meas_convert;
+       int ibat_raw;
+       int ibat_convert;
        struct ab8500_gpadc *gpadc;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2);
-       aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
-                       aux2_raw);
-
-       return seq_printf(s, "%d,0x%X\n",
-                       aux2_convert, aux2_raw);
+       vbat_true_meas_raw = ab8500_gpadc_double_read_raw(gpadc,
+                       VBAT_TRUE_MEAS_AND_IBAT, avg_sample, trig_edge,
+                       trig_timer, conv_type, &ibat_raw);
+       vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+                       VBAT_TRUE_MEAS, vbat_true_meas_raw);
+       ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL,
+               ibat_raw);
+
+       return seq_printf(s, "%d,0x%X\n"  "%d,0x%X\n",
+               vbat_true_meas_convert, vbat_true_meas_raw,
+               ibat_convert, ibat_raw);
 }
 
-static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file)
+static int ab8540_gpadc_vbat_true_meas_and_ibat_open(struct inode *inode,
+               struct file *file)
 {
-       return single_open(file, ab8500_gpadc_aux2_print, inode->i_private);
+       return single_open(file, ab8540_gpadc_vbat_true_meas_and_ibat_print,
+               inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_aux2_fops = {
-       .open = ab8500_gpadc_aux2_open,
+static const struct file_operations ab8540_gpadc_vbat_true_meas_and_ibat_fops = {
+       .open = ab8540_gpadc_vbat_true_meas_and_ibat_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
+static int ab8540_gpadc_bat_temp_and_ibat_print(struct seq_file *s, void *p)
 {
-       int main_bat_v_raw;
-       int main_bat_v_convert;
+       int bat_temp_raw;
+       int bat_temp_convert;
+       int ibat_raw;
+       int ibat_convert;
        struct ab8500_gpadc *gpadc;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V);
-       main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
-                       main_bat_v_raw);
-
-       return seq_printf(s, "%d,0x%X\n",
-                       main_bat_v_convert, main_bat_v_raw);
+       bat_temp_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_TEMP_AND_IBAT,
+               avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw);
+       bat_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
+               bat_temp_raw);
+       ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL,
+               ibat_raw);
+
+       return seq_printf(s, "%d,0x%X\n"  "%d,0x%X\n",
+               bat_temp_convert, bat_temp_raw,
+               ibat_convert, ibat_raw);
 }
 
-static int ab8500_gpadc_main_bat_v_open(struct inode *inode,
+static int ab8540_gpadc_bat_temp_and_ibat_open(struct inode *inode,
                struct file *file)
 {
-       return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private);
+       return single_open(file, ab8540_gpadc_bat_temp_and_ibat_print,
+               inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_main_bat_v_fops = {
-       .open = ab8500_gpadc_main_bat_v_open,
+static const struct file_operations ab8540_gpadc_bat_temp_and_ibat_fops = {
+       .open = ab8540_gpadc_bat_temp_and_ibat_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
+static int ab8540_gpadc_otp_cal_print(struct seq_file *s, void *p)
 {
-       int vbus_v_raw;
-       int vbus_v_convert;
        struct ab8500_gpadc *gpadc;
+       u16 vmain_l, vmain_h, btemp_l, btemp_h;
+       u16 vbat_l, vbat_h, ibat_l, ibat_h;
 
        gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V);
-       vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
-                       vbus_v_raw);
-
-       return seq_printf(s, "%d,0x%X\n",
-                       vbus_v_convert, vbus_v_raw);
+       ab8540_gpadc_get_otp(gpadc, &vmain_l, &vmain_h, &btemp_l, &btemp_h,
+                       &vbat_l, &vbat_h, &ibat_l, &ibat_h);
+       return seq_printf(s, "VMAIN_L:0x%X\n"
+               "VMAIN_H:0x%X\n"
+               "BTEMP_L:0x%X\n"
+               "BTEMP_H:0x%X\n"
+               "VBAT_L:0x%X\n"
+               "VBAT_H:0x%X\n"
+               "IBAT_L:0x%X\n"
+               "IBAT_H:0x%X\n",
+               vmain_l, vmain_h, btemp_l, btemp_h, vbat_l, vbat_h, ibat_l, ibat_h);
 }
 
-static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file)
+static int ab8540_gpadc_otp_cal_open(struct inode *inode, struct file *file)
 {
-       return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private);
+       return single_open(file, ab8540_gpadc_otp_cal_print, inode->i_private);
 }
 
-static const struct file_operations ab8500_gpadc_vbus_v_fops = {
-       .open = ab8500_gpadc_vbus_v_open,
+static const struct file_operations ab8540_gpadc_otp_calib_fops = {
+       .open = ab8540_gpadc_otp_cal_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
+static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p)
 {
-       int main_charger_c_raw;
-       int main_charger_c_convert;
-       struct ab8500_gpadc *gpadc;
-
-       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C);
-       main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
-                       MAIN_CHARGER_C, main_charger_c_raw);
+       return seq_printf(s, "%d\n", avg_sample);
+}
 
-       return seq_printf(s, "%d,0x%X\n",
-                       main_charger_c_convert, main_charger_c_raw);
+static int ab8500_gpadc_avg_sample_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_gpadc_avg_sample_print,
+               inode->i_private);
 }
 
-static int ab8500_gpadc_main_charger_c_open(struct inode *inode,
-               struct file *file)
+static ssize_t ab8500_gpadc_avg_sample_write(struct file *file,
+       const char __user *user_buf,
+       size_t count, loff_t *ppos)
 {
-       return single_open(file, ab8500_gpadc_main_charger_c_print,
-                       inode->i_private);
+       struct device *dev = ((struct seq_file *)(file->private_data))->private;
+       unsigned long user_avg_sample;
+       int err;
+
+       err = kstrtoul_from_user(user_buf, count, 0, &user_avg_sample);
+       if (err)
+               return err;
+
+       if ((user_avg_sample == SAMPLE_1) || (user_avg_sample == SAMPLE_4)
+                       || (user_avg_sample == SAMPLE_8)
+                       || (user_avg_sample == SAMPLE_16)) {
+               avg_sample = (u8) user_avg_sample;
+       } else {
+               dev_err(dev, "debugfs error input: "
+                       "should be egal to 1, 4, 8 or 16\n");
+               return -EINVAL;
+       }
+
+       return count;
 }
 
-static const struct file_operations ab8500_gpadc_main_charger_c_fops = {
-       .open = ab8500_gpadc_main_charger_c_open,
+static const struct file_operations ab8500_gpadc_avg_sample_fops = {
+       .open = ab8500_gpadc_avg_sample_open,
        .read = seq_read,
+       .write = ab8500_gpadc_avg_sample_write,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
+static int ab8500_gpadc_trig_edge_print(struct seq_file *s, void *p)
 {
-       int usb_charger_c_raw;
-       int usb_charger_c_convert;
-       struct ab8500_gpadc *gpadc;
-
-       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C);
-       usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
-           USB_CHARGER_C, usb_charger_c_raw);
+       return seq_printf(s, "%d\n", trig_edge);
+}
 
-       return seq_printf(s, "%d,0x%X\n",
-                       usb_charger_c_convert, usb_charger_c_raw);
+static int ab8500_gpadc_trig_edge_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_gpadc_trig_edge_print,
+               inode->i_private);
 }
 
-static int ab8500_gpadc_usb_charger_c_open(struct inode *inode,
-               struct file *file)
+static ssize_t ab8500_gpadc_trig_edge_write(struct file *file,
+       const char __user *user_buf,
+       size_t count, loff_t *ppos)
 {
-       return single_open(file, ab8500_gpadc_usb_charger_c_print,
-           inode->i_private);
+       struct device *dev = ((struct seq_file *)(file->private_data))->private;
+       unsigned long user_trig_edge;
+       int err;
+
+       err = kstrtoul_from_user(user_buf, count, 0, &user_trig_edge);
+       if (err)
+               return err;
+
+       if ((user_trig_edge == RISING_EDGE)
+                       || (user_trig_edge == FALLING_EDGE)) {
+               trig_edge = (u8) user_trig_edge;
+       } else {
+               dev_err(dev, "Wrong input:\n"
+                       "Enter 0. Rising edge\n"
+                       "Enter 1. Falling edge\n");
+               return -EINVAL;
+       }
+
+       return count;
 }
 
-static const struct file_operations ab8500_gpadc_usb_charger_c_fops = {
-       .open = ab8500_gpadc_usb_charger_c_open,
+static const struct file_operations ab8500_gpadc_trig_edge_fops = {
+       .open = ab8500_gpadc_trig_edge_open,
        .read = seq_read,
+       .write = ab8500_gpadc_trig_edge_write,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
+static int ab8500_gpadc_trig_timer_print(struct seq_file *s, void *p)
 {
-       int bk_bat_v_raw;
-       int bk_bat_v_convert;
-       struct ab8500_gpadc *gpadc;
-
-       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V);
-       bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
-                       BK_BAT_V, bk_bat_v_raw);
+       return seq_printf(s, "%d\n", trig_timer);
+}
 
-       return seq_printf(s, "%d,0x%X\n",
-                       bk_bat_v_convert, bk_bat_v_raw);
+static int ab8500_gpadc_trig_timer_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_gpadc_trig_timer_print,
+               inode->i_private);
 }
 
-static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file)
+static ssize_t ab8500_gpadc_trig_timer_write(struct file *file,
+       const char __user *user_buf,
+       size_t count, loff_t *ppos)
 {
-       return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private);
+       struct device *dev = ((struct seq_file *)(file->private_data))->private;
+       unsigned long user_trig_timer;
+       int err;
+
+       err = kstrtoul_from_user(user_buf, count, 0, &user_trig_timer);
+       if (err)
+               return err;
+
+       if ((user_trig_timer >= 0) && (user_trig_timer <= 255)) {
+               trig_timer = (u8) user_trig_timer;
+       } else {
+               dev_err(dev, "debugfs error input: "
+                       "should be beetween 0 to 255\n");
+               return -EINVAL;
+       }
+
+       return count;
 }
 
-static const struct file_operations ab8500_gpadc_bk_bat_v_fops = {
-       .open = ab8500_gpadc_bk_bat_v_open,
+static const struct file_operations ab8500_gpadc_trig_timer_fops = {
+       .open = ab8500_gpadc_trig_timer_open,
        .read = seq_read,
+       .write = ab8500_gpadc_trig_timer_write,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
 };
 
-static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
+static int ab8500_gpadc_conv_type_print(struct seq_file *s, void *p)
 {
-       int die_temp_raw;
-       int die_temp_convert;
-       struct ab8500_gpadc *gpadc;
-
-       gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
-       die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP);
-       die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
-                       die_temp_raw);
+       return seq_printf(s, "%d\n", conv_type);
+}
 
-       return seq_printf(s, "%d,0x%X\n",
-                       die_temp_convert, die_temp_raw);
+static int ab8500_gpadc_conv_type_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab8500_gpadc_conv_type_print,
+               inode->i_private);
 }
 
-static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file)
+static ssize_t ab8500_gpadc_conv_type_write(struct file *file,
+       const char __user *user_buf,
+       size_t count, loff_t *ppos)
 {
-       return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private);
+       struct device *dev = ((struct seq_file *)(file->private_data))->private;
+       unsigned long user_conv_type;
+       int err;
+
+       err = kstrtoul_from_user(user_buf, count, 0, &user_conv_type);
+       if (err)
+               return err;
+
+       if ((user_conv_type == ADC_SW)
+                       || (user_conv_type == ADC_HW)) {
+               conv_type = (u8) user_conv_type;
+       } else {
+               dev_err(dev, "Wrong input:\n"
+                       "Enter 0. ADC SW conversion\n"
+                       "Enter 1. ADC HW conversion\n");
+               return -EINVAL;
+       }
+
+       return count;
 }
 
-static const struct file_operations ab8500_gpadc_die_temp_fops = {
-       .open = ab8500_gpadc_die_temp_open,
+static const struct file_operations ab8500_gpadc_conv_type_fops = {
+       .open = ab8500_gpadc_conv_type_open,
        .read = seq_read,
+       .write = ab8500_gpadc_conv_type_write,
        .llseek = seq_lseek,
        .release = single_release,
        .owner = THIS_MODULE,
@@ -1352,7 +2745,7 @@ static int ab8500_subscribe_unsubscribe_open(struct inode *inode,
                                             struct file *file)
 {
        return single_open(file, ab8500_subscribe_unsubscribe_print,
-                          inode->i_private);
+               inode->i_private);
 }
 
 /*
@@ -1382,21 +2775,14 @@ static ssize_t ab8500_subscribe_write(struct file *file,
                                      size_t count, loff_t *ppos)
 {
        struct device *dev = ((struct seq_file *)(file->private_data))->private;
-       char buf[32];
-       int buf_size;
        unsigned long user_val;
        int err;
        unsigned int irq_index;
 
-       /* Get userspace string and assure termination */
-       buf_size = min(count, (sizeof(buf)-1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       err = strict_strtoul(buf, 0, &user_val);
+       err = kstrtoul_from_user(user_buf, count, 0, &user_val);
        if (err)
-               return -EINVAL;
+               return err;
+
        if (user_val < irq_first) {
                dev_err(dev, "debugfs error input < %d\n", irq_first);
                return -EINVAL;
@@ -1416,7 +2802,7 @@ static ssize_t ab8500_subscribe_write(struct file *file,
         */
        dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute),
                GFP_KERNEL);
-       event_name[irq_index] = kmalloc(buf_size, GFP_KERNEL);
+       event_name[irq_index] = kmalloc(count, GFP_KERNEL);
        sprintf(event_name[irq_index], "%lu", user_val);
        dev_attr[irq_index]->show = show_irq;
        dev_attr[irq_index]->store = NULL;
@@ -1438,7 +2824,7 @@ static ssize_t ab8500_subscribe_write(struct file *file,
                return err;
        }
 
-       return buf_size;
+       return count;
 }
 
 static ssize_t ab8500_unsubscribe_write(struct file *file,
@@ -1446,21 +2832,14 @@ static ssize_t ab8500_unsubscribe_write(struct file *file,
                                        size_t count, loff_t *ppos)
 {
        struct device *dev = ((struct seq_file *)(file->private_data))->private;
-       char buf[32];
-       int buf_size;
        unsigned long user_val;
        int err;
        unsigned int irq_index;
 
-       /* Get userspace string and assure termination */
-       buf_size = min(count, (sizeof(buf)-1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       err = strict_strtoul(buf, 0, &user_val);
+       err = kstrtoul_from_user(user_buf, count, 0, &user_val);
        if (err)
-               return -EINVAL;
+               return err;
+
        if (user_val < irq_first) {
                dev_err(dev, "debugfs error input < %d\n", irq_first);
                return -EINVAL;
@@ -1485,7 +2864,7 @@ static ssize_t ab8500_unsubscribe_write(struct file *file,
        kfree(event_name[irq_index]);
        kfree(dev_attr[irq_index]);
 
-       return buf_size;
+       return count;
 }
 
 /*
@@ -1583,7 +2962,7 @@ static int ab8500_debug_probe(struct platform_device *plf)
        irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
        if (irq_first < 0) {
                dev_err(&plf->dev, "First irq not found, err %d\n",
-                               irq_first);
+                       irq_first);
                ret = irq_first;
                goto out_freeevent_name;
        }
@@ -1591,9 +2970,9 @@ static int ab8500_debug_probe(struct platform_device *plf)
        irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
        if (irq_last < 0) {
                dev_err(&plf->dev, "Last irq not found, err %d\n",
-                               irq_last);
+                       irq_last);
                ret = irq_last;
-                goto out_freeevent_name;
+               goto out_freeevent_name;
        }
 
        ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
@@ -1601,124 +2980,198 @@ static int ab8500_debug_probe(struct platform_device *plf)
                goto err;
 
        ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING,
-           ab8500_dir);
+               ab8500_dir);
        if (!ab8500_gpadc_dir)
                goto err;
 
        file = debugfs_create_file("all-bank-registers", S_IRUGO,
-           ab8500_dir, &plf->dev, &ab8500_registers_fops);
+               ab8500_dir, &plf->dev, &ab8500_registers_fops);
        if (!file)
                goto err;
 
        file = debugfs_create_file("all-banks", S_IRUGO,
-           ab8500_dir, &plf->dev, &ab8500_all_banks_fops);
+               ab8500_dir, &plf->dev, &ab8500_all_banks_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR),
-           ab8500_dir, &plf->dev, &ab8500_bank_fops);
+       file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_dir, &plf->dev, &ab8500_bank_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR),
-           ab8500_dir, &plf->dev, &ab8500_address_fops);
+       file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_dir, &plf->dev, &ab8500_address_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR),
-           ab8500_dir, &plf->dev, &ab8500_val_fops);
+       file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_dir, &plf->dev, &ab8500_val_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR),
-           ab8500_dir, &plf->dev, &ab8500_subscribe_fops);
+       file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_dir, &plf->dev, &ab8500_subscribe_fops);
        if (!file)
                goto err;
 
-       if (is_ab8500(ab8500))
+       if (is_ab8500(ab8500)) {
+               debug_ranges = ab8500_debug_ranges;
                num_interrupt_lines = AB8500_NR_IRQS;
-       else if (is_ab8505(ab8500))
+       } else if (is_ab8505(ab8500)) {
+               debug_ranges = ab8505_debug_ranges;
                num_interrupt_lines = AB8505_NR_IRQS;
-       else if (is_ab9540(ab8500))
+       } else if (is_ab9540(ab8500)) {
+               debug_ranges = ab8505_debug_ranges;
                num_interrupt_lines = AB9540_NR_IRQS;
+       } else if (is_ab8540(ab8500)) {
+               debug_ranges = ab8540_debug_ranges;
+               num_interrupt_lines = AB8540_NR_IRQS;
+       }
 
        file = debugfs_create_file("interrupts", (S_IRUGO),
-           ab8500_dir, &plf->dev, &ab8500_interrupts_fops);
+               ab8500_dir, &plf->dev, &ab8500_interrupts_fops);
+       if (!file)
+               goto err;
+
+       file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR),
-           ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops);
+       file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_dir, &plf->dev, &ab8500_hwreg_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR),
-           ab8500_dir, &plf->dev, &ab8500_hwreg_fops);
+       file = debugfs_create_file("all-modem-registers", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_dir, &plf->dev, &ab8500_modem_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops);
+       file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops);
+       file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops);
+       file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops);
+       file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops);
+       file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops);
+       file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops);
+       file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops);
+       file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops);
+       file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops);
+       file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops);
+       if (!file)
+               goto err;
+
+       file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops);
+       if (!file)
+               goto err;
+
+       file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops);
+       if (!file)
+               goto err;
+
+       file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops);
+       if (!file)
+               goto err;
+
+       file = debugfs_create_file("usb_id", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_id_fops);
+       if (!file)
+               goto err;
+
+       if (is_ab8540(ab8500)) {
+               file = debugfs_create_file("xtal_temp", (S_IRUGO | S_IWUSR | S_IWGRP),
+                       ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_xtal_temp_fops);
+               if (!file)
+                       goto err;
+               file = debugfs_create_file("vbattruemeas", (S_IRUGO | S_IWUSR | S_IWGRP),
+                       ab8500_gpadc_dir, &plf->dev,
+                       &ab8540_gpadc_vbat_true_meas_fops);
+               if (!file)
+                       goto err;
+               file = debugfs_create_file("batctrl_and_ibat",
+                       (S_IRUGO | S_IWUGO), ab8500_gpadc_dir,
+                       &plf->dev, &ab8540_gpadc_bat_ctrl_and_ibat_fops);
+               if (!file)
+                       goto err;
+               file = debugfs_create_file("vbatmeas_and_ibat",
+                       (S_IRUGO | S_IWUGO), ab8500_gpadc_dir,
+                       &plf->dev,
+                       &ab8540_gpadc_vbat_meas_and_ibat_fops);
+               if (!file)
+                       goto err;
+               file = debugfs_create_file("vbattruemeas_and_ibat",
+                       (S_IRUGO | S_IWUGO), ab8500_gpadc_dir,
+                       &plf->dev,
+                       &ab8540_gpadc_vbat_true_meas_and_ibat_fops);
+               if (!file)
+                       goto err;
+               file = debugfs_create_file("battemp_and_ibat",
+                       (S_IRUGO | S_IWUGO), ab8500_gpadc_dir,
+                       &plf->dev, &ab8540_gpadc_bat_temp_and_ibat_fops);
+               if (!file)
+                       goto err;
+               file = debugfs_create_file("otp_calib", (S_IRUGO | S_IWUSR | S_IWGRP),
+                       ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_otp_calib_fops);
+               if (!file)
+                       goto err;
+       }
+       file = debugfs_create_file("avg_sample", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_avg_sample_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops);
+       file = debugfs_create_file("trig_edge", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_edge_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops);
+       file = debugfs_create_file("trig_timer", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_timer_fops);
        if (!file)
                goto err;
 
-       file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR),
-           ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops);
+       file = debugfs_create_file("conv_type", (S_IRUGO | S_IWUSR | S_IWGRP),
+               ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_conv_type_fops);
        if (!file)
                goto err;
 
index 5f341a50ee5a5783e6e84118124e9efcfe177bd3..65f72284185d9cbf2d9eff0125015c7557fd119a 100644 (file)
 #define AB8500_GPADC_AUTODATAL_REG     0x07
 #define AB8500_GPADC_AUTODATAH_REG     0x08
 #define AB8500_GPADC_MUX_CTRL_REG      0x09
+#define AB8540_GPADC_MANDATA2L_REG     0x09
+#define AB8540_GPADC_MANDATA2H_REG     0x0A
+#define AB8540_GPADC_APEAAX_REG                0x10
+#define AB8540_GPADC_APEAAT_REG                0x11
+#define AB8540_GPADC_APEAAM_REG                0x12
+#define AB8540_GPADC_APEAAH_REG                0x13
+#define AB8540_GPADC_APEAAL_REG                0x14
 
 /*
  * OTP register offsets
 #define AB8500_GPADC_CAL_5             0x13
 #define AB8500_GPADC_CAL_6             0x14
 #define AB8500_GPADC_CAL_7             0x15
+/* New calibration for 8540 */
+#define AB8540_GPADC_OTP4_REG_7        0x38
+#define AB8540_GPADC_OTP4_REG_6        0x39
+#define AB8540_GPADC_OTP4_REG_5        0x3A
 
 /* gpadc constants */
 #define EN_VINTCORE12                  0x04
 #define EN_VTVOUT                      0x02
 #define EN_GPADC                       0x01
 #define DIS_GPADC                      0x00
-#define SW_AVG_16                      0x60
+#define AVG_1                          0x00
+#define AVG_4                          0x20
+#define AVG_8                          0x40
+#define AVG_16                         0x60
 #define ADC_SW_CONV                    0x04
 #define EN_ICHAR                       0x80
 #define BTEMP_PULL_UP                  0x08
 #define EN_BUF                         0x40
 #define DIS_ZERO                       0x00
 #define GPADC_BUSY                     0x01
+#define EN_FALLING                     0x10
+#define EN_TRIG_EDGE                   0x02
+#define EN_VBIAS_XTAL_TEMP             0x02
 
 /* GPADC constants from AB8500 spec, UM0836 */
 #define ADC_RESOLUTION                 1024
 #define ADC_CH_BKBAT_MIN               0
 #define ADC_CH_BKBAT_MAX               3200
 
+/* GPADC constants from AB8540 spec */
+#define ADC_CH_IBAT_MIN                        (-6000) /* mA range measured by ADC for ibat*/
+#define ADC_CH_IBAT_MAX                        6000
+#define ADC_CH_IBAT_MIN_V              (-60)   /* mV range measured by ADC for ibat*/
+#define ADC_CH_IBAT_MAX_V              60
+#define IBAT_VDROP_L                   (-56)  /* mV */
+#define IBAT_VDROP_H                   56
+
 /* This is used to not lose precision when dividing to get gain and offset */
-#define CALIB_SCALE                    1000
+#define CALIB_SCALE            1000
+/*
+ * Number of bits shift used to not lose precision
+ * when dividing to get ibat gain.
+ */
+#define CALIB_SHIFT_IBAT       20
 
 /* Time in ms before disabling regulator */
 #define GPADC_AUDOSUSPEND_DELAY                1
@@ -92,6 +122,7 @@ enum cal_channels {
        ADC_INPUT_VMAIN = 0,
        ADC_INPUT_BTEMP,
        ADC_INPUT_VBAT,
+       ADC_INPUT_IBAT,
        NBR_CAL_INPUTS,
 };
 
@@ -102,8 +133,10 @@ enum cal_channels {
  * @offset:            Offset of the ADC channel
  */
 struct adc_cal_data {
-       u64 gain;
-       u64 offset;
+       s64 gain;
+       s64 offset;
+       u16 otp_calib_hi;
+       u16 otp_calib_lo;
 };
 
 /**
@@ -116,7 +149,10 @@ struct adc_cal_data {
  *                             the completion of gpadc conversion
  * @ab8500_gpadc_lock:         structure of type mutex
  * @regu:                      pointer to the struct regulator
- * @irq:                       interrupt number that is used by gpadc
+ * @irq_sw:                    interrupt number that is used by gpadc for Sw
+ *                             conversion
+ * @irq_hw:                    interrupt number that is used by gpadc for Hw
+ *                             conversion
  * @cal_data                   array of ADC calibration data structs
  */
 struct ab8500_gpadc {
@@ -126,7 +162,8 @@ struct ab8500_gpadc {
        struct completion ab8500_gpadc_complete;
        struct mutex ab8500_gpadc_lock;
        struct regulator *regu;
-       int irq;
+       int irq_sw;
+       int irq_hw;
        struct adc_cal_data cal_data[NBR_CAL_INPUTS];
 };
 
@@ -171,6 +208,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
                        gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE;
                break;
 
+       case XTAL_TEMP:
        case BAT_CTRL:
        case BTEMP_BALL:
        case ACC_DETECT1:
@@ -189,6 +227,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
                break;
 
        case MAIN_BAT_V:
+       case VBAT_TRUE_MEAS:
                /* For some reason we don't have calibrated data */
                if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) {
                        res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX -
@@ -232,6 +271,20 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
                        ADC_RESOLUTION;
                break;
 
+       case IBAT_VIRTUAL_CHANNEL:
+               /* For some reason we don't have calibrated data */
+               if (!gpadc->cal_data[ADC_INPUT_IBAT].gain) {
+                       res = ADC_CH_IBAT_MIN + (ADC_CH_IBAT_MAX -
+                               ADC_CH_IBAT_MIN) * ad_value /
+                               ADC_RESOLUTION;
+                       break;
+               }
+               /* Here we can use the calibrated data */
+               res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_IBAT].gain +
+                               gpadc->cal_data[ADC_INPUT_IBAT].offset)
+                               >> CALIB_SHIFT_IBAT;
+               break;
+
        default:
                dev_err(gpadc->dev,
                        "unknown channel, not possible to convert\n");
@@ -244,25 +297,35 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
 EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
 
 /**
- * ab8500_gpadc_convert() - gpadc conversion
+ * ab8500_gpadc_sw_hw_convert() - gpadc conversion
  * @channel:   analog channel to be converted to digital data
+ * @avg_sample:  number of ADC sample to average
+ * @trig_egde:  selected ADC trig edge
+ * @trig_timer: selected ADC trigger delay timer
+ * @conv_type: selected conversion type (HW or SW conversion)
  *
  * This function converts the selected analog i/p to digital
  * data.
  */
-int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
+int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
+               u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
 {
        int ad_value;
        int voltage;
 
-       ad_value = ab8500_gpadc_read_raw(gpadc, channel);
-       if (ad_value < 0) {
-               dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
+       ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
+                       trig_edge, trig_timer, conv_type);
+/* On failure retry a second time */
+       if (ad_value < 0)
+               ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
+                       trig_edge, trig_timer, conv_type);
+if (ad_value < 0) {
+               dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n",
+                               channel);
                return ad_value;
        }
 
        voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
-
        if (voltage < 0)
                dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
                        " %d AD: 0x%x\n", channel, ad_value);
@@ -274,21 +337,46 @@ EXPORT_SYMBOL(ab8500_gpadc_convert);
 /**
  * ab8500_gpadc_read_raw() - gpadc read
  * @channel:   analog channel to be read
+ * @avg_sample:  number of ADC sample to average
+ * @trig_edge:  selected trig edge
+ * @trig_timer: selected ADC trigger delay timer
+ * @conv_type: selected conversion type (HW or SW conversion)
  *
- * This function obtains the raw ADC value, this then needs
- * to be converted by calling ab8500_gpadc_ad_to_voltage()
+ * This function obtains the raw ADC value for an hardware conversion,
+ * this then needs to be converted by calling ab8500_gpadc_ad_to_voltage()
  */
-int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
+int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
+               u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
+{
+       int raw_data;
+       raw_data = ab8500_gpadc_double_read_raw(gpadc, channel,
+                       avg_sample, trig_edge, trig_timer, conv_type, NULL);
+       return raw_data;
+}
+
+int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
+               u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type,
+               int *ibat)
 {
        int ret;
        int looplimit = 0;
-       u8 val, low_data, high_data;
+       unsigned long completion_timeout;
+       u8 val, low_data, high_data, low_data2, high_data2;
+       u8 val_reg1 = 0;
+       unsigned int delay_min = 0;
+       unsigned int delay_max = 0;
+       u8 data_low_addr, data_high_addr;
 
        if (!gpadc)
                return -ENODEV;
 
-       mutex_lock(&gpadc->ab8500_gpadc_lock);
+       /* check if convertion is supported */
+       if ((gpadc->irq_sw < 0) && (conv_type == ADC_SW))
+               return -ENOTSUPP;
+       if ((gpadc->irq_hw < 0) && (conv_type == ADC_HW))
+               return -ENOTSUPP;
 
+       mutex_lock(&gpadc->ab8500_gpadc_lock);
        /* Enable VTVout LDO this is required for GPADC */
        pm_runtime_get_sync(gpadc->dev);
 
@@ -309,16 +397,34 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
        }
 
        /* Enable GPADC */
-       ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
-               AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC);
-       if (ret < 0) {
-               dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n");
-               goto out;
+       val_reg1 |= EN_GPADC;
+
+       /* Select the channel source and set average samples */
+       switch (avg_sample) {
+       case SAMPLE_1:
+               val = channel | AVG_1;
+               break;
+       case SAMPLE_4:
+               val = channel | AVG_4;
+               break;
+       case SAMPLE_8:
+               val = channel | AVG_8;
+               break;
+       default:
+               val = channel | AVG_16;
+               break;
        }
 
-       /* Select the channel source and set average samples to 16 */
-       ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
-               AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
+       if (conv_type == ADC_HW) {
+               ret = abx500_set_register_interruptible(gpadc->dev,
+                               AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val);
+               val_reg1 |= EN_TRIG_EDGE;
+               if (trig_edge)
+                       val_reg1 |= EN_FALLING;
+       }
+       else
+               ret = abx500_set_register_interruptible(gpadc->dev,
+                               AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val);
        if (ret < 0) {
                dev_err(gpadc->dev,
                        "gpadc_conversion: set avg samples failed\n");
@@ -333,71 +439,129 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
        switch (channel) {
        case MAIN_CHARGER_C:
        case USB_CHARGER_C:
-               ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
-                       AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
-                       EN_BUF | EN_ICHAR,
-                       EN_BUF | EN_ICHAR);
+               val_reg1 |= EN_BUF | EN_ICHAR;
                break;
        case BTEMP_BALL:
                if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
-                       /* Turn on btemp pull-up on ABB 3.0 */
-                       ret = abx500_mask_and_set_register_interruptible(
-                               gpadc->dev,
-                               AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
-                               EN_BUF | BTEMP_PULL_UP,
-                               EN_BUF | BTEMP_PULL_UP);
-
-                /*
-                 * Delay might be needed for ABB8500 cut 3.0, if not, remove
-                 * when hardware will be available
-                 */
-                       usleep_range(1000, 1000);
+                       val_reg1 |= EN_BUF | BTEMP_PULL_UP;
+                       /*
+                       * Delay might be needed for ABB8500 cut 3.0, if not,
+                       * remove when hardware will be availible
+                       */
+                       delay_min = 1000; /* Delay in micro seconds */
+                       delay_max = 10000; /* large range to optimise sleep mode */
                        break;
                }
                /* Intentional fallthrough */
        default:
-               ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
-                       AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
+               val_reg1 |= EN_BUF;
                break;
        }
+
+       /* Write configuration to register */
+       ret = abx500_set_register_interruptible(gpadc->dev,
+               AB8500_GPADC, AB8500_GPADC_CTRL1_REG, val_reg1);
        if (ret < 0) {
                dev_err(gpadc->dev,
-                       "gpadc_conversion: select falling edge failed\n");
+                       "gpadc_conversion: set Control register failed\n");
                goto out;
        }
 
-       ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
-               AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV);
-       if (ret < 0) {
-               dev_err(gpadc->dev,
-                       "gpadc_conversion: start s/w conversion failed\n");
-               goto out;
+       if (delay_min != 0)
+               usleep_range(delay_min, delay_max);
+
+       if (conv_type == ADC_HW) {
+               /* Set trigger delay timer */
+               ret = abx500_set_register_interruptible(gpadc->dev,
+                       AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer);
+               if (ret < 0) {
+                       dev_err(gpadc->dev,
+                               "gpadc_conversion: trig timer failed\n");
+                       goto out;
+               }
+               completion_timeout = 2 * HZ;
+               data_low_addr = AB8500_GPADC_AUTODATAL_REG;
+               data_high_addr = AB8500_GPADC_AUTODATAH_REG;
+       } else {
+               /* Start SW conversion */
+               ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+                       AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+                       ADC_SW_CONV, ADC_SW_CONV);
+               if (ret < 0) {
+                       dev_err(gpadc->dev,
+                               "gpadc_conversion: start s/w conv failed\n");
+                       goto out;
+               }
+               completion_timeout = msecs_to_jiffies(CONVERSION_TIME);
+               data_low_addr = AB8500_GPADC_MANDATAL_REG;
+               data_high_addr = AB8500_GPADC_MANDATAH_REG;
        }
+
        /* wait for completion of conversion */
        if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
-                                        msecs_to_jiffies(CONVERSION_TIME))) {
+                       completion_timeout)) {
                dev_err(gpadc->dev,
-                       "timeout: didn't receive GPADC conversion interrupt\n");
+                       "timeout didn't receive GPADC conv interrupt\n");
                ret = -EINVAL;
                goto out;
        }
 
        /* Read the converted RAW data */
-       ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
-               AB8500_GPADC_MANDATAL_REG, &low_data);
+       ret = abx500_get_register_interruptible(gpadc->dev,
+                       AB8500_GPADC, data_low_addr, &low_data);
        if (ret < 0) {
                dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n");
                goto out;
        }
 
-       ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
-               AB8500_GPADC_MANDATAH_REG, &high_data);
+       ret = abx500_get_register_interruptible(gpadc->dev,
+               AB8500_GPADC, data_high_addr, &high_data);
        if (ret < 0) {
-               dev_err(gpadc->dev,
-                       "gpadc_conversion: read high data failed\n");
+               dev_err(gpadc->dev, "gpadc_conversion: read high data failed\n");
                goto out;
        }
 
+       /* Check if double convertion is required */
+       if ((channel == BAT_CTRL_AND_IBAT) ||
+                       (channel == VBAT_MEAS_AND_IBAT) ||
+                       (channel == VBAT_TRUE_MEAS_AND_IBAT) ||
+                       (channel == BAT_TEMP_AND_IBAT)) {
+
+               if (conv_type == ADC_HW) {
+                       /* not supported */
+                       ret = -ENOTSUPP;
+                       dev_err(gpadc->dev,
+                               "gpadc_conversion: only SW double conversion supported\n");
+                       goto out;
+               } else {
+                       /* Read the converted RAW data 2 */
+                       ret = abx500_get_register_interruptible(gpadc->dev,
+                               AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG,
+                               &low_data2);
+                       if (ret < 0) {
+                               dev_err(gpadc->dev,
+                                       "gpadc_conversion: read sw low data 2 failed\n");
+                               goto out;
+                       }
+
+                       ret = abx500_get_register_interruptible(gpadc->dev,
+                               AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG,
+                               &high_data2);
+                       if (ret < 0) {
+                               dev_err(gpadc->dev,
+                                       "gpadc_conversion: read sw high data 2 failed\n");
+                               goto out;
+                       }
+                       if (ibat != NULL) {
+                               *ibat = (high_data2 << 8) | low_data2;
+                       } else {
+                               dev_warn(gpadc->dev,
+                                       "gpadc_conversion: ibat not stored\n");
+                       }
+
+               }
+       }
+
        /* Disable GPADC */
        ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
                AB8500_GPADC_CTRL1_REG, DIS_GPADC);
@@ -406,6 +570,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
                goto out;
        }
 
+       /* Disable VTVout LDO this is required for GPADC */
        pm_runtime_mark_last_busy(gpadc->dev);
        pm_runtime_put_autosuspend(gpadc->dev);
 
@@ -422,9 +587,7 @@ out:
         */
        (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
                AB8500_GPADC_CTRL1_REG, DIS_GPADC);
-
        pm_runtime_put(gpadc->dev);
-
        mutex_unlock(&gpadc->ab8500_gpadc_lock);
        dev_err(gpadc->dev,
                "gpadc_conversion: Failed to AD convert channel %d\n", channel);
@@ -433,16 +596,16 @@ out:
 EXPORT_SYMBOL(ab8500_gpadc_read_raw);
 
 /**
- * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
+ * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion
  * @irq:       irq number
  * @data:      pointer to the data passed during request irq
  *
- * This is a interrupt service routine for s/w gpadc conversion completion.
+ * This is a interrupt service routine for gpadc conversion completion.
  * Notifies the gpadc completion is completed and the converted raw value
  * can be read from the registers.
  * Returns IRQ status(IRQ_HANDLED)
  */
-static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc)
+static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc)
 {
        struct ab8500_gpadc *gpadc = _gpadc;
 
@@ -461,15 +624,27 @@ static int otp_cal_regs[] = {
        AB8500_GPADC_CAL_7,
 };
 
+static int otp4_cal_regs[] = {
+       AB8540_GPADC_OTP4_REG_7,
+       AB8540_GPADC_OTP4_REG_6,
+       AB8540_GPADC_OTP4_REG_5,
+};
+
 static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
 {
        int i;
        int ret[ARRAY_SIZE(otp_cal_regs)];
        u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)];
-
+       int ret_otp4[ARRAY_SIZE(otp4_cal_regs)];
+       u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)];
        int vmain_high, vmain_low;
        int btemp_high, btemp_low;
        int vbat_high, vbat_low;
+       int ibat_high, ibat_low;
+       s64 V_gain, V_offset, V2A_gain, V2A_offset;
+       struct ab8500 *ab8500;
+
+       ab8500 = gpadc->parent;
 
        /* First we read all OTP registers and store the error code */
        for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
@@ -489,7 +664,7 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
         * bt_h/l = btemp_high/low
         * vb_h/l = vbat_high/low
         *
-        * Data bits:
+        * Data bits 8500/9540:
         * | 7     | 6     | 5     | 4     | 3     | 2     | 1     | 0
         * |.......|.......|.......|.......|.......|.......|.......|.......
         * |                                               | vm_h9 | vm_h8
@@ -507,6 +682,35 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
         * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
         * |.......|.......|.......|.......|.......|.......|.......|.......
         *
+        * Data bits 8540:
+        * OTP2
+        * | 7     | 6     | 5     | 4     | 3     | 2     | 1     | 0
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * |
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        *
+        * Data bits 8540:
+        * OTP4
+        * | 7     | 6     | 5     | 4     | 3     | 2     | 1     | 0
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * |                                       | ib_h9 | ib_h8 | ib_h7
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5
+        * |.......|.......|.......|.......|.......|.......|.......|.......
+        * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 |
+        *
         *
         * Ideal output ADC codes corresponding to injected input voltages
         * during manufacturing is:
@@ -519,38 +723,116 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
         * vbat_low:   Vin = 2380mV  / ADC ideal code = 33
         */
 
-       /* Calculate gain and offset for VMAIN if all reads succeeded */
-       if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
-               vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
-                       ((gpadc_cal[1] & 0x3F) << 2) |
-                       ((gpadc_cal[2] & 0xC0) >> 6));
+       if (is_ab8540(ab8500)) {
+               /* Calculate gain and offset for VMAIN if all reads succeeded*/
+               if (!(ret[1] < 0 || ret[2] < 0)) {
+                       vmain_high = (((gpadc_cal[1] & 0xFF) << 2) |
+                               ((gpadc_cal[2] & 0xC0) >> 6));
+                       vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+                       gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
+                               (u16)vmain_high;
+                       gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
+                               (u16)vmain_low;
+
+                       gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
+                               (19500 - 315) / (vmain_high - vmain_low);
+                       gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
+                               19500 - (CALIB_SCALE * (19500 - 315) /
+                               (vmain_high - vmain_low)) * vmain_high;
+               } else {
+               gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+               }
 
-               vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+               /* Read IBAT calibration Data */
+               for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) {
+                       ret_otp4[i] = abx500_get_register_interruptible(
+                                       gpadc->dev, AB8500_OTP_EMUL,
+                                       otp4_cal_regs[i],  &gpadc_otp4[i]);
+                       if (ret_otp4[i] < 0)
+                               dev_err(gpadc->dev,
+                                       "%s: read otp4 reg 0x%02x failed\n",
+                                       __func__, otp4_cal_regs[i]);
+               }
 
-               gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
-                       (19500 - 315) / (vmain_high - vmain_low);
+               /* Calculate gain and offset for IBAT if all reads succeeded */
+               if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) {
+                       ibat_high = (((gpadc_otp4[0] & 0x07) << 7) |
+                               ((gpadc_otp4[1] & 0xFE) >> 1));
+                       ibat_low = (((gpadc_otp4[1] & 0x01) << 5) |
+                               ((gpadc_otp4[2] & 0xF8) >> 3));
+
+                       gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi =
+                               (u16)ibat_high;
+                       gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo =
+                               (u16)ibat_low;
+
+                       V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L)
+                               << CALIB_SHIFT_IBAT) / (ibat_high - ibat_low);
+
+                       V_offset = (IBAT_VDROP_H << CALIB_SHIFT_IBAT) -
+                               (((IBAT_VDROP_H - IBAT_VDROP_L) <<
+                               CALIB_SHIFT_IBAT) / (ibat_high - ibat_low))
+                               * ibat_high;
+                       /*
+                        * Result obtained is in mV (at a scale factor),
+                        * we need to calculate gain and offset to get mA
+                        */
+                       V2A_gain = (ADC_CH_IBAT_MAX - ADC_CH_IBAT_MIN)/
+                               (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
+                       V2A_offset = ((ADC_CH_IBAT_MAX_V * ADC_CH_IBAT_MIN -
+                               ADC_CH_IBAT_MAX * ADC_CH_IBAT_MIN_V)
+                               << CALIB_SHIFT_IBAT)
+                               / (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
+
+                       gpadc->cal_data[ADC_INPUT_IBAT].gain = V_gain * V2A_gain;
+                       gpadc->cal_data[ADC_INPUT_IBAT].offset = V_offset *
+                               V2A_gain + V2A_offset;
+               } else {
+                       gpadc->cal_data[ADC_INPUT_IBAT].gain = 0;
+               }
 
-               gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 -
-                       (CALIB_SCALE * (19500 - 315) /
-                        (vmain_high - vmain_low)) * vmain_high;
+               dev_dbg(gpadc->dev, "IBAT gain %llu offset %llu\n",
+                       gpadc->cal_data[ADC_INPUT_IBAT].gain,
+                       gpadc->cal_data[ADC_INPUT_IBAT].offset);
        } else {
-               gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+               /* Calculate gain and offset for VMAIN if all reads succeeded */
+               if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
+                       vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
+                               ((gpadc_cal[1] & 0x3F) << 2) |
+                               ((gpadc_cal[2] & 0xC0) >> 6));
+                       vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+                       gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi =
+                               (u16)vmain_high;
+                       gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo =
+                               (u16)vmain_low;
+
+                       gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE *
+                               (19500 - 315) / (vmain_high - vmain_low);
+
+                       gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE *
+                               19500 - (CALIB_SCALE * (19500 - 315) /
+                               (vmain_high - vmain_low)) * vmain_high;
+               } else {
+                       gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0;
+               }
        }
 
        /* Calculate gain and offset for BTEMP if all reads succeeded */
        if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) {
                btemp_high = (((gpadc_cal[2] & 0x01) << 9) |
-                       (gpadc_cal[3] << 1) |
-                       ((gpadc_cal[4] & 0x80) >> 7));
-
+                       (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7));
                btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
 
+               gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi = (u16)btemp_high;
+               gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo = (u16)btemp_low;
+
                gpadc->cal_data[ADC_INPUT_BTEMP].gain =
                        CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
-
                gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 -
-                       (CALIB_SCALE * (1300 - 21) /
-                       (btemp_high - btemp_low)) * btemp_high;
+                       (CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low))
+                       * btemp_high;
        } else {
                gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0;
        }
@@ -560,9 +842,11 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
                vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
                vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
 
+               gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi = (u16)vbat_high;
+               gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo = (u16)vbat_low;
+
                gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE *
                        (4700 - 2380) / (vbat_high - vbat_low);
-
                gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 -
                        (CALIB_SCALE * (4700 - 2380) /
                        (vbat_high - vbat_low)) * vbat_high;
@@ -608,6 +892,31 @@ static int ab8500_gpadc_runtime_idle(struct device *dev)
        return 0;
 }
 
+static int ab8500_gpadc_suspend(struct device *dev)
+{
+       struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+       mutex_lock(&gpadc->ab8500_gpadc_lock);
+
+       pm_runtime_get_sync(dev);
+
+       regulator_disable(gpadc->regu);
+       return 0;
+}
+
+static int ab8500_gpadc_resume(struct device *dev)
+{
+       struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+       regulator_enable(gpadc->regu);
+
+       pm_runtime_mark_last_busy(gpadc->dev);
+       pm_runtime_put_autosuspend(gpadc->dev);
+
+       mutex_unlock(&gpadc->ab8500_gpadc_lock);
+       return 0;
+}
+
 static int ab8500_gpadc_probe(struct platform_device *pdev)
 {
        int ret = 0;
@@ -619,13 +928,13 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
-       if (gpadc->irq < 0) {
-               dev_err(&pdev->dev, "failed to get platform irq-%d\n",
-                       gpadc->irq);
-               ret = gpadc->irq;
-               goto fail;
-       }
+       gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
+       if (gpadc->irq_sw < 0)
+               dev_err(gpadc->dev, "failed to get platform sw_conv_end irq\n");
+
+       gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
+       if (gpadc->irq_hw < 0)
+               dev_err(gpadc->dev, "failed to get platform hw_conv_end irq\n");
 
        gpadc->dev = &pdev->dev;
        gpadc->parent = dev_get_drvdata(pdev->dev.parent);
@@ -634,15 +943,31 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
        /* Initialize completion used to notify completion of conversion */
        init_completion(&gpadc->ab8500_gpadc_complete);
 
-       /* Register interrupt  - SwAdcComplete */
-       ret = request_threaded_irq(gpadc->irq, NULL,
-               ab8500_bm_gpswadcconvend_handler,
-               IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
-                               "ab8500-gpadc", gpadc);
-       if (ret < 0) {
-               dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
-                       gpadc->irq);
-               goto fail;
+       /* Register interrupts */
+       if (gpadc->irq_sw >= 0) {
+               ret = request_threaded_irq(gpadc->irq_sw, NULL,
+                       ab8500_bm_gpadcconvend_handler,
+                       IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw",
+                       gpadc);
+               if (ret < 0) {
+                       dev_err(gpadc->dev,
+                               "Failed to register interrupt irq: %d\n",
+                               gpadc->irq_sw);
+                       goto fail;
+               }
+       }
+
+       if (gpadc->irq_hw >= 0) {
+               ret = request_threaded_irq(gpadc->irq_hw, NULL,
+                       ab8500_bm_gpadcconvend_handler,
+                       IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw",
+                       gpadc);
+               if (ret < 0) {
+                       dev_err(gpadc->dev,
+                               "Failed to register interrupt irq: %d\n",
+                               gpadc->irq_hw);
+                       goto fail_irq;
+               }
        }
 
        /* VTVout LDO used to power up ab8500-GPADC */
@@ -669,11 +994,13 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
        ab8500_gpadc_read_calibration_data(gpadc);
        list_add_tail(&gpadc->node, &ab8500_gpadc_list);
        dev_dbg(gpadc->dev, "probe success\n");
+
        return 0;
 
 fail_enable:
 fail_irq:
-       free_irq(gpadc->irq, gpadc);
+       free_irq(gpadc->irq_sw, gpadc);
+       free_irq(gpadc->irq_hw, gpadc);
 fail:
        kfree(gpadc);
        gpadc = NULL;
@@ -687,7 +1014,10 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
        /* remove this gpadc entry from the list */
        list_del(&gpadc->node);
        /* remove interrupt  - completion of Sw ADC conversion */
-       free_irq(gpadc->irq, gpadc);
+       if (gpadc->irq_sw >= 0)
+               free_irq(gpadc->irq_sw, gpadc);
+       if (gpadc->irq_hw >= 0)
+               free_irq(gpadc->irq_hw, gpadc);
 
        pm_runtime_get_sync(gpadc->dev);
        pm_runtime_disable(gpadc->dev);
@@ -707,6 +1037,9 @@ static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
        SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
                           ab8500_gpadc_runtime_resume,
                           ab8500_gpadc_runtime_idle)
+       SET_SYSTEM_SLEEP_PM_OPS(ab8500_gpadc_suspend,
+                               ab8500_gpadc_resume)
+
 };
 
 static struct platform_driver ab8500_gpadc_driver = {
@@ -729,10 +1062,30 @@ static void __exit ab8500_gpadc_exit(void)
        platform_driver_unregister(&ab8500_gpadc_driver);
 }
 
+/**
+ * ab8540_gpadc_get_otp() - returns OTP values
+ *
+ */
+void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
+                       u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h,
+                       u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h)
+{
+       *vmain_l = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo;
+       *vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi;
+       *btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo;
+       *btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi;
+       *vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo;
+       *vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi;
+       *ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo;
+       *ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi;
+       return ;
+}
+
 subsys_initcall_sync(ab8500_gpadc_init);
 module_exit(ab8500_gpadc_exit);
 
 MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson");
+MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson,"
+               "M'boumba Cedric Madianga");
 MODULE_ALIAS("platform:ab8500_gpadc");
 MODULE_DESCRIPTION("AB8500 GPADC driver");
index 108fd86552f0329df01bd905f4109882e236ba62..272479cdb1072442f200827c7f0ffe7d0900bec0 100644 (file)
 #include <linux/mfd/abx500/ab8500.h>
 #include <linux/mfd/abx500/ab8500-sysctrl.h>
 
+/* RtcCtrl bits */
+#define AB8500_ALARM_MIN_LOW  0x08
+#define AB8500_ALARM_MIN_MID 0x09
+#define RTC_CTRL 0x0B
+#define RTC_ALARM_ENABLE 0x4
+
 static struct device *sysctrl_dev;
 
 void ab8500_power_off(void)
 {
        sigset_t old;
        sigset_t all;
-       static char *pss[] = {"ab8500_ac", "ab8500_usb"};
+       static char *pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
        int i;
        bool charger_present = false;
        union power_supply_propval val;
        struct power_supply *psy;
        int ret;
 
+       if (sysctrl_dev == NULL) {
+               pr_err("%s: sysctrl not initialized\n", __func__);
+               return;
+       }
+
        /*
         * If we have a charger connected and we're powering off,
         * reboot into charge-only mode.
@@ -74,6 +85,63 @@ shutdown:
        }
 }
 
+/*
+ * Use the AB WD to reset the platform. It will perform a hard
+ * reset instead of a soft reset. Write the reset reason to
+ * the AB before reset, which can be read upon restart.
+ */
+void ab8500_restart(char mode, const char *cmd)
+{
+       struct ab8500_platform_data *plat;
+       struct ab8500_sysctrl_platform_data *pdata;
+       u16 reason = 0;
+       u8 val;
+
+       if (sysctrl_dev == NULL) {
+               pr_err("%s: sysctrl not initialized\n", __func__);
+               return;
+       }
+
+       plat = dev_get_platdata(sysctrl_dev->parent);
+       pdata = plat->sysctrl;
+       if (pdata->reboot_reason_code)
+               reason = pdata->reboot_reason_code(cmd);
+       else
+               pr_warn("[%s] No reboot reason set. Default reason %d\n",
+                       __func__, reason);
+
+       /*
+        * Disable RTC alarm, just a precaution so that no alarm
+        * is running when WD reset is executed.
+        */
+       abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC,
+               RTC_CTRL , &val);
+       abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
+               RTC_CTRL , (val & ~RTC_ALARM_ENABLE));
+
+       /*
+        * Android is not using the RTC alarm registers during reboot
+        * so we borrow them for writing the reason of reset
+        */
+
+       /* reason[8 LSB] */
+       val = reason & 0xFF;
+       abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
+               AB8500_ALARM_MIN_LOW , val);
+
+       /* reason[8 MSB] */
+       val = (reason>>8) & 0xFF;
+       abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
+               AB8500_ALARM_MIN_MID , val);
+
+       /* Setting WD timeout to 0 */
+       ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0);
+
+       /* Setting the parameters to AB8500 WD*/
+       ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD |
+               AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD));
+}
+
 static inline bool valid_bank(u8 bank)
 {
        return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
@@ -85,7 +153,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
        u8 bank;
 
        if (sysctrl_dev == NULL)
-               return -EAGAIN;
+               return -EINVAL;
 
        bank = (reg >> 8);
        if (!valid_bank(bank))
@@ -101,7 +169,7 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
        u8 bank;
 
        if (sysctrl_dev == NULL)
-               return -EAGAIN;
+               return -EINVAL;
 
        bank = (reg >> 8);
        if (!valid_bank(bank))
@@ -114,28 +182,36 @@ EXPORT_SYMBOL(ab8500_sysctrl_write);
 
 static int ab8500_sysctrl_probe(struct platform_device *pdev)
 {
+       struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
        struct ab8500_platform_data *plat;
        struct ab8500_sysctrl_platform_data *pdata;
 
-       sysctrl_dev = &pdev->dev;
        plat = dev_get_platdata(pdev->dev.parent);
+
+       if (!(plat && plat->sysctrl))
+               return -EINVAL;
+
        if (plat->pm_power_off)
                pm_power_off = ab8500_power_off;
 
        pdata = plat->sysctrl;
 
        if (pdata) {
-               int ret, i, j;
+               int last, ret, i, j;
+
+               if (is_ab8505(ab8500))
+                       last = AB8500_SYSCLKREQ4RFCLKBUF;
+               else
+                       last = AB8500_SYSCLKREQ8RFCLKBUF;
 
-               for (i = AB8500_SYSCLKREQ1RFCLKBUF;
-                    i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) {
+               for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) {
                        j = i - AB8500_SYSCLKREQ1RFCLKBUF;
                        ret = ab8500_sysctrl_write(i, 0xff,
-                                                  pdata->initial_req_buf_config[j]);
+                                       pdata->initial_req_buf_config[j]);
                        dev_dbg(&pdev->dev,
-                               "Setting SysClkReq%dRfClkBuf 0x%X\n",
-                               j + 1,
-                               pdata->initial_req_buf_config[j]);
+                                       "Setting SysClkReq%dRfClkBuf 0x%X\n",
+                                       j + 1,
+                                       pdata->initial_req_buf_config[j]);
                        if (ret < 0) {
                                dev_err(&pdev->dev,
                                        "unable to set sysClkReq%dRfClkBuf: "
index 4b37a5af8deb20ee7e2cb64630376ae4284d7eb7..36fb4b5a4b0d6e314e84e9e8803c3193f9cb5931 100644 (file)
@@ -714,7 +714,6 @@ out_irq:
        while (--i >= 0)
                free_irq(info->irq[i], info);
 out:
-       kfree(info);
        return ret;
 }
 
@@ -728,7 +727,6 @@ static int pm860x_charger_remove(struct platform_device *pdev)
        free_irq(info->irq[0], info);
        for (i = 0; i < info->irq_nums; i++)
                free_irq(info->irq[i], info);
-       kfree(info);
        return 0;
 }
 
index ffe02fb7cbc78a64c40818d2a0323b07a1f7a515..0d0b5d7d19d02f9e2c0a245de4da43f084e3340e 100644 (file)
@@ -340,6 +340,13 @@ config CHARGER_SMB347
          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 AB8500_BM
        bool "AB8500 Battery Management Driver"
        depends on AB8500_CORE && AB8500_GPADC
@@ -353,13 +360,6 @@ config BATTERY_GOLDFISH
          Say Y to enable support for the battery and AC power in the
          Goldfish emulator.
 
-config CHARGER_PM2301
-       bool "PM2301 Battery Charger Driver"
-       depends on AB8500_BM
-       help
-         Say Y to include support for PM2301 charger driver.
-         Depends on AB8500 battery management core.
-
 source "drivers/power/reset/Kconfig"
 
 endif # POWER_SUPPLY
index 3f66436af45cc4a2b630c9170b5bf0a35ccb49ee..653bf6ceff30a6c999ec4b1a7bf2932fb8f48359 100644 (file)
@@ -39,7 +39,7 @@ 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
+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
@@ -47,10 +47,10 @@ 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_PM2301)   += pm2301_charger.o
 obj-$(CONFIG_CHARGER_MAX8997)  += max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
 obj-$(CONFIG_CHARGER_BQ2415X)  += bq2415x_charger.o
 obj-$(CONFIG_POWER_AVS)                += avs/
 obj-$(CONFIG_CHARGER_SMB347)   += smb347-charger.o
+obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
 obj-$(CONFIG_POWER_RESET)      += reset/
index 7a96c0650fbbc1daf8c3fdf1dbc699a66b7d7a32..d2986453309348ce8a5fa832c694b27406e89456 100644 (file)
@@ -11,7 +11,7 @@
  * Note that the res_to_temp table must be strictly sorted by falling resistance
  * values to work.
  */
-static struct abx500_res_to_temp temp_tbl_A_thermistor[] = {
+const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
        {-5, 53407},
        { 0, 48594},
        { 5, 43804},
@@ -28,8 +28,12 @@ static struct abx500_res_to_temp temp_tbl_A_thermistor[] = {
        {60, 13437},
        {65, 12500},
 };
+EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor);
 
-static struct abx500_res_to_temp temp_tbl_B_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},
@@ -46,8 +50,12 @@ static struct abx500_res_to_temp temp_tbl_B_thermistor[] = {
        {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 struct abx500_v_to_cap cap_tbl_A_thermistor[] = {
+static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
        {4171,  100},
        {4114,   95},
        {4009,   83},
@@ -70,7 +78,7 @@ static struct abx500_v_to_cap cap_tbl_A_thermistor[] = {
        {3247,    0},
 };
 
-static struct abx500_v_to_cap cap_tbl_B_thermistor[] = {
+static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
        {4161,  100},
        {4124,   98},
        {4044,   90},
@@ -93,7 +101,7 @@ static struct abx500_v_to_cap cap_tbl_B_thermistor[] = {
        {3250,    0},
 };
 
-static struct abx500_v_to_cap cap_tbl[] = {
+static const struct abx500_v_to_cap cap_tbl[] = {
        {4186,  100},
        {4163,   99},
        {4114,   95},
@@ -124,7 +132,7 @@ static struct abx500_v_to_cap cap_tbl[] = {
  * Note that the res_to_temp table must be strictly sorted by falling
  * resistance values to work.
  */
-static struct abx500_res_to_temp temp_tbl[] = {
+static const struct abx500_res_to_temp temp_tbl[] = {
        {-5, 214834},
        { 0, 162943},
        { 5, 124820},
@@ -146,7 +154,7 @@ static struct abx500_res_to_temp temp_tbl[] = {
  * Note that the batres_vs_temp table must be strictly sorted by falling
  * temperature values to work.
  */
-static struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
+static const struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
        { 40, 120},
        { 30, 135},
        { 20, 165},
@@ -160,7 +168,7 @@ static struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
  * Note that the batres_vs_temp table must be strictly sorted by falling
  * temperature values to work.
  */
-static struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
+static const struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
        { 60, 300},
        { 30, 300},
        { 20, 300},
@@ -171,7 +179,7 @@ static struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
 };
 
 /* battery resistance table for LI ION 9100 battery */
-static struct batres_vs_temp temp_to_batres_tbl_9100[] = {
+static const struct batres_vs_temp temp_to_batres_tbl_9100[] = {
        { 60, 180},
        { 30, 180},
        { 20, 180},
@@ -230,10 +238,10 @@ static struct abx500_battery_type bat_type_thermistor[] = {
                .maint_b_chg_timer_h = 200,
                .low_high_cur_lvl = 300,
                .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
-               .r_to_t_tbl = temp_tbl_A_thermistor,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
-               .v_to_cap_tbl = cap_tbl_A_thermistor,
+               .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,
 
@@ -258,10 +266,10 @@ static struct abx500_battery_type bat_type_thermistor[] = {
                .maint_b_chg_timer_h = 200,
                .low_high_cur_lvl = 300,
                .low_high_vol_lvl = 4000,
-               .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
-               .r_to_t_tbl = temp_tbl_B_thermistor,
-               .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
-               .v_to_cap_tbl = cap_tbl_B_thermistor,
+               .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,
        },
@@ -407,15 +415,27 @@ static const struct abx500_fg_parameters fg = {
        .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 maxi_params = {
+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,
@@ -423,6 +443,46 @@ static const struct abx500_bm_charger_parameters chg = {
        .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,
@@ -442,22 +502,60 @@ struct abx500_bm_data ab8500_bm_data = {
        .fg_res                 = 100,
        .cap_levels             = &cap_levels,
        .bat_type               = bat_type_thermistor,
-       .n_btypes               = 3,
+       .n_btypes               = ARRAY_SIZE(bat_type_thermistor),
        .batt_id                = 0,
        .interval_charging      = 5,
        .interval_not_charging  = 120,
        .temp_hysteresis        = 3,
        .gnd_lift_resistance    = 34,
-       .maxi                   = &maxi_params,
+       .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)
 {
-       struct batres_vs_temp *tmp_batres_tbl;
+       const struct batres_vs_temp *tmp_batres_tbl;
        struct device_node *battery_node;
        const char *btech;
        int i;
index 07689064996ecccba2cfe5196b6826c364c2a687..d412d34bf3df1998246e59a83373d06390d3a1c4 100644 (file)
@@ -42,6 +42,9 @@
 #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
+
 #define to_ab8500_btemp_device_info(x) container_of((x), \
        struct ab8500_btemp, btemp_psy);
 
@@ -76,8 +79,8 @@ struct ab8500_btemp_ranges {
  * @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:          Battery temperature in degree Celcius
- * @prev_bat_temp      Last dispatched battery temperature
+ * @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
@@ -128,6 +131,7 @@ struct ab8500_btemp *ab8500_btemp_get(void)
 
        return btemp;
 }
+EXPORT_SYMBOL(ab8500_btemp_get);
 
 /**
  * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
@@ -155,7 +159,7 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
        if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
                /*
                 * If the battery has internal NTC, we use the current
-                * source to calculate the resistance, 7uA or 20uA
+                * source to calculate the resistance.
                 */
                rbs = (v_batctrl * 1000
                       - di->bm->gnd_lift_resistance * inst_curr)
@@ -216,7 +220,12 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
        /* Only do this for batteries with internal NTC */
        if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
 
-               if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+               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
@@ -257,7 +266,14 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
        } else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
                dev_dbg(di->dev, "Disable BATCTRL curr source\n");
 
-               if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+               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,
@@ -314,7 +330,13 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
         * if we got an error above
         */
 disable_curr_source:
-       if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+       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,
@@ -541,7 +563,9 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 {
        int res;
        u8 i;
-       if (is_ab9540(di->parent) || is_ab8505(di->parent))
+       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;
@@ -579,12 +603,17 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 
        /*
         * We only have to change current source if the
-        * detected type is Type 1, else we use the 7uA source
+        * detected type is Type 1.
         */
        if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
-                       di->bm->batt_id == 1) {
-               if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
-                       dev_dbg(di->dev, "Set BATCTRL current source to 16uA\n");
+           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");
@@ -604,22 +633,37 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 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) {
-               di->initialized = true;
                /* Identify the battery */
                if (ab8500_btemp_id(di) < 0)
                        dev_warn(di->dev, "failed to identify the battery\n");
        }
 
-       di->bat_temp = ab8500_btemp_measure_temp(di);
-
-       if (di->bat_temp != di->prev_bat_temp) {
-               di->prev_bat_temp = di->bat_temp;
+       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;
@@ -772,7 +816,7 @@ static void ab8500_btemp_periodic(struct ab8500_btemp *di,
  *
  * Returns battery temperature
  */
-static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
+int ab8500_btemp_get_temp(struct ab8500_btemp *di)
 {
        int temp = 0;
 
@@ -808,6 +852,7 @@ static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
        }
        return temp;
 }
+EXPORT_SYMBOL(ab8500_btemp_get_temp);
 
 /**
  * ab8500_btemp_get_batctrl_temp() - get the temperature
@@ -819,6 +864,7 @@ 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
index 24b30b7ea5ca55219cb52918bb25a31090aeb1c8..a558318b169ce2e8b9b75a02a72513d5c101b4be 100644 (file)
@@ -15,6 +15,7 @@
 #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>
 #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
@@ -77,7 +83,9 @@
 
 /* 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 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 {
@@ -196,10 +208,15 @@ struct ab8500_charger_usb_state {
        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
- * @max_usb_in_curr:   Max USB charger input current
  * @vbus_detected:     VBUS detected
  * @vbus_detected_start:
  *                     VBUS detected during startup
@@ -214,7 +231,6 @@ struct ab8500_charger_usb_state {
  * @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_usb_host:       Indicate if last detected USB type is host
  * @is_aca_rid:                Incicate if accessory is ACA type
  * @current_stepping_sessions:
  *                     Counter for current stepping sessions
@@ -223,6 +239,7 @@ struct ab8500_charger_usb_state {
  * @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
@@ -254,7 +271,6 @@ struct ab8500_charger_usb_state {
  */
 struct ab8500_charger {
        struct device *dev;
-       int max_usb_in_curr;
        bool vbus_detected;
        bool vbus_detected_start;
        bool ac_conn;
@@ -266,7 +282,6 @@ struct ab8500_charger {
        bool autopower;
        bool autopower_cfg;
        int invalid_charger_detect_state;
-       bool is_usb_host;
        int is_aca_rid;
        atomic_t current_stepping_sessions;
        struct ab8500 *parent;
@@ -274,6 +289,7 @@ struct ab8500_charger {
        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;
@@ -415,13 +431,18 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
        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);
 
-                       queue_delayed_work(di->charger_wq,
+                       if (is_ab8500(di->parent))
+                               queue_delayed_work(di->charger_wq,
                                           &di->usb_charger_attached_work,
                                           HZ);
                } else {
@@ -668,23 +689,19 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
        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_CH_IP_CUR_LVL_0P5;
-               di->is_usb_host = true;
+               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_CH_IP_CUR_LVL_0P5;
-               di->is_usb_host = true;
+               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_CH_IP_CUR_LVL_0P5;
-               di->is_usb_host = true;
+               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_CH_IP_CUR_LVL_0P9;
-               di->is_usb_host = false;
+               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:
@@ -693,8 +710,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
                 * can consume (900mA). Closest level is 500mA
                 */
                dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
-               di->is_usb_host = false;
+               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:
@@ -702,38 +718,35 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
                 * Dedicated charger level minus 120mA (20mA for ACA and
                 * 100mA for potential accessory). Closest level is 1300mA
                 */
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
+               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);
-               di->is_usb_host = false;
+                               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_CH_IP_CUR_LVL_0P5;
-               di->is_usb_host = true;
+               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_CH_IP_CUR_LVL_1P5;
-               di->is_usb_host = false;
+               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_CH_IP_CUR_LVL_1P5;
-               di->is_usb_host = false;
+               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_CH_IP_CUR_LVL_1P5;
+                       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_CH_IP_CUR_LVL_0P05;
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
                ret = -ENXIO;
                break;
        case USB_STAT_RESERVED:
@@ -743,12 +756,13 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
                                                "VBUS has collapsed\n");
                        ret = -ENXIO;
                        break;
-               }
-               if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+               } else {
                        dev_dbg(di->dev, "USB Type - Charging not allowed\n");
-                       di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+                       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);
+                               link_status,
+                               di->max_usb_in_curr.usb_type_max);
                        ret = -ENXIO;
                        break;
                }
@@ -757,23 +771,24 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
        case USB_STAT_CARKIT_2:
        case USB_STAT_ACA_DOCK_CHARGER:
        case USB_STAT_CHARGER_LINE_1:
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+               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);
+                               di->max_usb_in_curr.usb_type_max);
        case USB_STAT_NOT_VALID_LINK:
                dev_err(di->dev, "USB Type invalid - try charging anyway\n");
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+               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_CH_IP_CUR_LVL_0P05;
+               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);
+               link_status, di->max_usb_in_curr.set_max);
 
        return ret;
 }
@@ -796,21 +811,22 @@ static int ab8500_charger_read_usb_type(struct ab8500_charger *di)
                dev_err(di->dev, "%s ab8500 read failed\n", __func__);
                return ret;
        }
-       if (is_ab8500(di->parent)) {
+       if (is_ab8500(di->parent))
                ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-                               AB8500_USB_LINE_STAT_REG, &val);
-       } else {
-               if (is_ab9540(di->parent) || is_ab8505(di->parent))
-                       ret = abx500_get_register_interruptible(di->dev,
-                               AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
-       }
+                       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 */
-       val = (val & AB8500_USB_LINK_STATUS) >> 3;
+       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);
 
@@ -865,7 +881,12 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
                 */
 
                /* get the USB type */
-               val = (val & AB8500_USB_LINK_STATUS) >> 3;
+               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;
        }
@@ -960,51 +981,6 @@ static int ab8500_charger_voltage_map[] = {
        4600 ,
 };
 
-/*
- * This array maps the raw hex value to charger current used by the AB8500
- * Values taken from the UM0836
- */
-static int ab8500_charger_current_map[] = {
-       100 ,
-       200 ,
-       300 ,
-       400 ,
-       500 ,
-       600 ,
-       700 ,
-       800 ,
-       900 ,
-       1000 ,
-       1100 ,
-       1200 ,
-       1300 ,
-       1400 ,
-       1500 ,
-};
-
-/*
- * This array maps the raw hex value to VBUS input current used by the AB8500
- * Values taken from the UM0836
- */
-static int ab8500_charger_vbus_in_curr_map[] = {
-       USB_CH_IP_CUR_LVL_0P05,
-       USB_CH_IP_CUR_LVL_0P09,
-       USB_CH_IP_CUR_LVL_0P19,
-       USB_CH_IP_CUR_LVL_0P29,
-       USB_CH_IP_CUR_LVL_0P38,
-       USB_CH_IP_CUR_LVL_0P45,
-       USB_CH_IP_CUR_LVL_0P5,
-       USB_CH_IP_CUR_LVL_0P6,
-       USB_CH_IP_CUR_LVL_0P7,
-       USB_CH_IP_CUR_LVL_0P8,
-       USB_CH_IP_CUR_LVL_0P9,
-       USB_CH_IP_CUR_LVL_1P0,
-       USB_CH_IP_CUR_LVL_1P1,
-       USB_CH_IP_CUR_LVL_1P3,
-       USB_CH_IP_CUR_LVL_1P4,
-       USB_CH_IP_CUR_LVL_1P5,
-};
-
 static int ab8500_voltage_to_regval(int voltage)
 {
        int i;
@@ -1026,41 +1002,41 @@ static int ab8500_voltage_to_regval(int voltage)
                return -1;
 }
 
-static int ab8500_current_to_regval(int curr)
+static int ab8500_current_to_regval(struct ab8500_charger *di, int curr)
 {
        int i;
 
-       if (curr < ab8500_charger_current_map[0])
+       if (curr < di->bm->chg_output_curr[0])
                return 0;
 
-       for (i = 0; i < ARRAY_SIZE(ab8500_charger_current_map); i++) {
-               if (curr < ab8500_charger_current_map[i])
+       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 = ARRAY_SIZE(ab8500_charger_current_map) - 1;
-       if (curr == ab8500_charger_current_map[i])
+       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(int curr)
+static int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr)
 {
        int i;
 
-       if (curr < ab8500_charger_vbus_in_curr_map[0])
+       if (curr < di->bm->chg_input_curr[0])
                return 0;
 
-       for (i = 0; i < ARRAY_SIZE(ab8500_charger_vbus_in_curr_map); i++) {
-               if (curr < ab8500_charger_vbus_in_curr_map[i])
+       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 = ARRAY_SIZE(ab8500_charger_vbus_in_curr_map) - 1;
-       if (curr == ab8500_charger_vbus_in_curr_map[i])
+       i = di->bm->n_chg_in_curr - 1;
+       if (curr == di->bm->chg_input_curr[i])
                return i;
        else
                return -1;
@@ -1077,28 +1053,48 @@ static int ab8500_vbus_in_curr_to_regval(int curr)
  */
 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_CH_IP_CUR_LVL_0P09;
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P09;
                break;
        case 200:
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P19;
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P19;
                break;
        case 300:
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P29;
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P29;
                break;
        case 400:
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P38;
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P38;
                break;
        case 500:
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
                break;
        default:
-               di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
-               return -1;
+               di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
+               ret = -EPERM;
                break;
        };
-       return 0;
+       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;
 }
 
 /**
@@ -1118,7 +1114,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
        int ich, int reg)
 {
        int ret = 0;
-       int auto_curr_index, curr_index, prev_curr_index, shift_value, i;
+       int curr_index, prev_curr_index, shift_value, i;
        u8 reg_value;
        u32 step_udelay;
        bool no_stepping = false;
@@ -1136,39 +1132,27 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
        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(ich);
+               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:
-               shift_value = VBUS_IN_CURR_LIM_SHIFT;
+               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(ich);
+               curr_index = ab8500_vbus_in_curr_to_regval(di, ich);
                step_udelay = STEP_UDELAY * 100;
 
-               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__);
-                       goto exit_set_current;
-               }
-               auto_curr_index =
-                       reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT;
-
-               dev_dbg(di->dev, "%s Auto VBUS curr is %d mA\n",
-                       __func__,
-                       ab8500_charger_vbus_in_curr_map[auto_curr_index]);
-
-               prev_curr_index = min(prev_curr_index, auto_curr_index);
-
                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(ich);
+               curr_index = ab8500_current_to_regval(di, ich);
                step_udelay = STEP_UDELAY;
                if (curr_index && (curr_index - prev_curr_index) > 1)
                        step_udelay *= 100;
@@ -1219,7 +1203,8 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
                                usleep_range(step_udelay, step_udelay * 2);
                }
        } else {
-               for (i = prev_curr_index + 1; i <= curr_index; i++) {
+               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,
@@ -1230,6 +1215,8 @@ static int ab8500_charger_set_current(struct ab8500_charger *di,
                        }
                        if (i != curr_index)
                                usleep_range(step_udelay, step_udelay * 2);
+
+                       allow = ab8500_charger_check_continue_stepping(di, reg);
                }
        }
 
@@ -1255,6 +1242,11 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
 
        /* 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:
@@ -1400,8 +1392,8 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
 
                /* Check if the requested voltage or current is valid */
                volt_index = ab8500_voltage_to_regval(vset);
-               curr_index = ab8500_current_to_regval(iset);
-               input_curr_index = ab8500_current_to_regval(
+               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,
@@ -1572,7 +1564,7 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
 
                /* Check if the requested voltage or current is valid */
                volt_index = ab8500_voltage_to_regval(vset);
-               curr_index = ab8500_current_to_regval(ich_out);
+               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, "
@@ -1609,7 +1601,8 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
                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);
+               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;
@@ -1668,8 +1661,7 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
                dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
 
                /* Cancel any pending Vbat check work */
-               if (delayed_work_pending(&di->check_vbat_work))
-                       cancel_delayed_work(&di->check_vbat_work);
+               cancel_delayed_work(&di->check_vbat_work);
 
        }
        ab8500_power_supply_changed(di, &di->usb_chg.psy);
@@ -1677,6 +1669,128 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
        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
@@ -1734,8 +1848,68 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
 
        /* Reset the main and usb drop input current measurement counter */
        ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                               AB8500_CHARGER_CTRL,
-                               0x1);
+                               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.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.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;
@@ -1823,9 +1997,10 @@ static void ab8500_charger_check_vbat_work(struct work_struct *work)
                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, di->vbat,
-                       di->old_vbat);
-               ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+                       " 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);
        }
 
@@ -2105,7 +2280,8 @@ static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
 
        /* 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);
+               ret = ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr.usb_type_max);
                if (ret)
                        return;
        }
@@ -2125,6 +2301,7 @@ 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);
@@ -2144,38 +2321,61 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
         * to start the charging process. but by jumping
         * thru a few hoops it can be forced to start.
         */
-       ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-                       AB8500_USB_LINE_STAT_REG, &val);
+       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 & AB8500_USB_LINK_STATUS) >> 3) == USB_STAT_NOT_VALID_LINK &&
+               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");
+                       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, 0x01, 0x01);
+                                       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_MCH_IPT_CURLVL_REG, 0x01, 0x01);
+                       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");
+                       dev_dbg(di->dev,
+                                       "Invalid charger detected, state= 1\n");
                        /*Stop charger detection*/
-                       abx500_mask_and_set_register_interruptible(di->dev, AB8500_USB,
-                                       AB8500_MCH_IPT_CURLVL_REG, 0x01, 0x00);
+                       abx500_mask_and_set_register_interruptible(di->dev,
+                                       AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
+                                       USB_CH_DET, 0x00);
                        /*Check link status*/
-                       ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
-                                       AB8500_USB_LINE_STAT_REG, &val);
+                       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 & AB8500_USB_LINK_STATUS) >> 3);
+                               (val & link_status) >> USB_LINK_STATUS_SHIFT);
                        di->invalid_charger_detect_state = 2;
                }
        } else {
@@ -2273,7 +2473,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
                if (!ab8500_charger_get_usb_cur(di)) {
                        /* Update maximum input current */
                        ret = ab8500_charger_set_vbus_in_curr(di,
-                                       di->max_usb_in_curr);
+                                       di->max_usb_in_curr.usb_type_max);
                        if (ret)
                                return;
 
@@ -2422,7 +2622,9 @@ static irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
 
        mutex_lock(&di->charger_attached_mutex);
        mutex_unlock(&di->charger_attached_mutex);
-       queue_delayed_work(di->charger_wq,
+
+       if (is_ab8500(di->parent))
+               queue_delayed_work(di->charger_wq,
                           &di->ac_charger_attached_work,
                           HZ);
        return IRQ_HANDLED;
@@ -2491,6 +2693,8 @@ 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;
 
@@ -2498,8 +2702,45 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
        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);
+               ab8500_charger_set_vbus_in_curr(di,
+                                       di->max_usb_in_curr.usb_type_max);
 }
 
 /**
@@ -2654,8 +2895,13 @@ static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_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(30 * HZ));
+                          round_jiffies(VBUS_IN_CURR_LIM_RETRY_SET_TIME * HZ));
 
        return IRQ_HANDLED;
 }
@@ -2836,6 +3082,7 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy,
 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)) {
@@ -2848,9 +3095,14 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
                        goto out;
                }
 
-               ret = abx500_set_register_interruptible(di->dev,
-                       AB8500_CHARGER,
-                       AB8500_CH_OPT_CRNTLVL_MAX_REG, CH_OP_CUR_LVL_1P6);
+               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");
@@ -2858,7 +3110,8 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
                }
        }
 
-       if (is_ab9540_2p0(di->parent) || is_ab8505_2p0(di->parent))
+       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,
@@ -2930,14 +3183,6 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
                goto out;
        }
 
-       /* Set charger 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");
@@ -2945,15 +3190,30 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
        }
 
        /* 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 |
-               di->bm->bkup_bat_i);
+               (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,
@@ -2962,6 +3222,25 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
        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;
 }
@@ -3055,11 +3334,8 @@ static int ab8500_charger_resume(struct platform_device *pdev)
                        dev_err(di->dev, "Failed to kick WD!\n");
 
                /* If not already pending start a new timer */
-               if (!delayed_work_pending(
-                       &di->kick_wd_work)) {
-                       queue_delayed_work(di->charger_wq, &di->kick_wd_work,
-                               round_jiffies(WD_KICK_INTERVAL));
-               }
+               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 */
@@ -3079,12 +3355,9 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
 {
        struct ab8500_charger *di = platform_get_drvdata(pdev);
 
-       /* Cancel any pending HW failure check */
-       if (delayed_work_pending(&di->check_hw_failure_work))
-               cancel_delayed_work(&di->check_hw_failure_work);
-
-       if (delayed_work_pending(&di->vbus_drop_end_work))
-               cancel_delayed_work(&di->vbus_drop_end_work);
+       /* 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);
@@ -3107,6 +3380,10 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
 #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);
@@ -3136,13 +3413,18 @@ static int ab8500_charger_remove(struct platform_device *pdev)
        /* 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)
+       if (di->usb_chg.enabled)
                power_supply_unregister(&di->usb_chg.psy);
-#if !defined(CONFIG_CHARGER_PM2301)
-       if(di->ac_chg.enabled)
+
+       if (di->ac_chg.enabled && !di->ac_chg.external)
                power_supply_unregister(&di->ac_chg.psy);
-#endif
+
        platform_set_drvdata(pdev, NULL);
 
        return 0;
@@ -3206,16 +3488,22 @@ static int ab8500_charger_probe(struct platform_device *pdev)
        di->ac_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface),
        /* 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 = ab8500_charger_current_map[
-               ARRAY_SIZE(ab8500_charger_current_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 */
        /* power_supply base class */
        di->usb_chg.psy.name = "ab8500_usb";
@@ -3227,15 +3515,20 @@ static int ab8500_charger_probe(struct platform_device *pdev)
        di->usb_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface),
        /* 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 = ab8500_charger_current_map[
-               ARRAY_SIZE(ab8500_charger_current_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 =
@@ -3316,7 +3609,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
        }
 
        /* Register AC charger class */
-       if(di->ac_chg.enabled) {
+       if (di->ac_chg.enabled) {
                ret = power_supply_register(di->dev, &di->ac_chg.psy);
                if (ret) {
                        dev_err(di->dev, "failed to register AC charger\n");
@@ -3325,7 +3618,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
        }
 
        /* Register USB charger class */
-       if(di->usb_chg.enabled) {
+       if (di->usb_chg.enabled) {
                ret = power_supply_register(di->dev, &di->usb_chg.psy);
                if (ret) {
                        dev_err(di->dev, "failed to register USB charger\n");
@@ -3385,14 +3678,16 @@ static int ab8500_charger_probe(struct platform_device *pdev)
        ch_stat = ab8500_charger_detect_chargers(di, false);
 
        if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
-               queue_delayed_work(di->charger_wq,
-                                  &di->ac_charger_attached_work,
-                                  HZ);
+               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) {
-               queue_delayed_work(di->charger_wq,
-                                  &di->usb_charger_attached_work,
-                                  HZ);
+               if (is_ab8500(di->parent))
+                       queue_delayed_work(di->charger_wq,
+                                          &di->usb_charger_attached_work,
+                                          HZ);
        }
 
        mutex_unlock(&di->charger_attached_mutex);
@@ -3410,10 +3705,10 @@ free_irq:
 put_usb_phy:
        usb_put_phy(di->usb_phy);
 free_usb:
-       if(di->usb_chg.enabled)
+       if (di->usb_chg.enabled)
                power_supply_unregister(&di->usb_chg.psy);
 free_ac:
-       if(di->ac_chg.enabled)
+       if (di->ac_chg.enabled)
                power_supply_unregister(&di->ac_chg.psy);
 free_charger_wq:
        destroy_workqueue(di->charger_wq);
index 25dae4c4b0ef328501792ce48acd0c5fde474b4d..c5391f5c372dae2a41f3c05b7c77c7e374533f73 100644 (file)
@@ -36,7 +36,7 @@
 
 #define MILLI_TO_MICRO                 1000
 #define FG_LSB_IN_MA                   1627
-#define QLSB_NANO_AMP_HOURS_X10                1129
+#define QLSB_NANO_AMP_HOURS_X10                1071
 #define INS_CURR_TIMEOUT               (3 * HZ)
 
 #define SEC_TO_SAMPLE(S)               (S * 4)
@@ -672,11 +672,11 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
        /*
         * Convert to unit value in mA
         * Full scale input voltage is
-        * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
+        * 63.160mV => LSB = 63.160mV/(4096*res) = 1.542mA
         * Given a 250ms conversion cycle time the LSB corresponds
-        * to 112.9 nAh. Convert to current by dividing by the conversion
+        * to 107.1 nAh. Convert to current by dividing by the conversion
         * time in hours (250ms = 1 / (3600 * 4)h)
-        * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+        * 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);
@@ -863,7 +863,7 @@ static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
 static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
 {
        int i, tbl_size;
-       struct abx500_v_to_cap *tbl;
+       const struct abx500_v_to_cap *tbl;
        int cap = 0;
 
        tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl,
@@ -915,7 +915,7 @@ static int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di)
 static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
 {
        int i, tbl_size;
-       struct batres_vs_temp *tbl;
+       const struct batres_vs_temp *tbl;
        int resist = 0;
 
        tbl = di->bm->bat_type[di->bm->batt_id].batres_tbl;
@@ -1354,9 +1354,6 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
                         * algorithm says.
                         */
                        di->bat_cap.prev_percent = 1;
-                       di->bat_cap.permille = 1;
-                       di->bat_cap.prev_mah = 1;
-                       di->bat_cap.mah = 1;
                        percent = 1;
 
                        changed = true;
@@ -1683,7 +1680,6 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
                break;
 
        case AB8500_FG_DISCHARGE_WAKEUP:
-               ab8500_fg_coulomb_counter(di, true);
                ab8500_fg_calc_cap_discharge_voltage(di, true);
 
                di->fg_samples = SEC_TO_SAMPLE(
@@ -1768,9 +1764,10 @@ static void ab8500_fg_algorithm(struct ab8500_fg *di)
                        ab8500_fg_algorithm_discharging(di);
        }
 
-       dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
+       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,
@@ -1982,7 +1979,7 @@ static void ab8500_fg_instant_work(struct work_struct *work)
 }
 
 /**
- * ab8500_fg_cc_data_end_handler() - isr to get battery avg current.
+ * ab8500_fg_cc_data_end_handler() - end of data conversion isr.
  * @irq:       interrupt number
  * @_di:       pointer to the ab8500_fg structure
  *
@@ -2002,7 +1999,7 @@ static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
 }
 
 /**
- * ab8500_fg_cc_convend_handler() - isr to get battery avg current.
+ * ab8500_fg_cc_int_calib_handler () - end of calibration isr.
  * @irq:       interrupt number
  * @_di:       pointer to the ab8500_fg structure
  *
@@ -2153,9 +2150,7 @@ static int ab8500_fg_get_property(struct power_supply *psy,
                        val->intval = di->bat_cap.prev_mah;
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
-               if (di->bm->capacity_scaling)
-                       val->intval = di->bat_cap.cap_scale.scaled_cap;
-               else if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
+               if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
                                di->flags.batt_id_received)
                        val->intval = 100;
                else
@@ -2344,6 +2339,50 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
                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;
 }
@@ -2546,6 +2585,428 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di)
 
        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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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;
+
+       di = to_ab8500_fg_device_info(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 device *dev)
+{
+       unsigned int i, j;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di;
+
+       di = to_ab8500_fg_device_info(psy);
+
+       if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
+            abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
+           || is_ab8540(di->parent)) {
+               for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++)
+                       if (device_create_file(dev, &ab8505_fg_sysfs_psy_attrs[j]))
+                               goto sysfs_psy_create_attrs_failed_ab8505;
+       }
+       return 0;
+sysfs_psy_create_attrs_failed_ab8505:
+       dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n");
+       while (j--)
+               device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
+
+       return -EIO;
+}
+
+static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev)
+{
+       unsigned int i;
+       struct power_supply *psy = dev_get_drvdata(dev);
+       struct ab8500_fg *di;
+
+       di = to_ab8500_fg_device_info(psy);
+
+       if (((is_ab8505(di->parent) || is_ab9540(di->parent)) &&
+            abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0)
+           || is_ab8540(di->parent)) {
+               for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++)
+                       (void)device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]);
+       }
+}
+
 /* Exposure to the sysfs interface <<END>> */
 
 #if defined(CONFIG_PM)
@@ -2607,6 +3068,7 @@ static int ab8500_fg_remove(struct platform_device *pdev)
        ab8500_fg_sysfs_exit(di);
 
        flush_scheduled_work();
+       ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev);
        power_supply_unregister(&di->fg_psy);
        platform_set_drvdata(pdev, NULL);
        return ret;
@@ -2772,6 +3234,13 @@ static int ab8500_fg_probe(struct platform_device *pdev)
                goto free_irq;
        }
 
+       ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev);
+       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;
index f043c0851a7600d1417711b7f1ec4364e9331985..9863e423602c247e92e5502fc801dc8b080b8cd9 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) ST-Ericsson SA 2012
+ * Copyright (c) 2012 Sony Mobile Communications AB
  *
  * Charging algorithm driver for abx500 variants
  *
@@ -8,11 +9,13 @@
  *     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/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
+
 #define to_abx500_chargalg_device_info(x) container_of((x), \
        struct abx500_chargalg, chargalg_psy);
 
@@ -66,6 +83,11 @@ struct abx500_chargalg_suspension_status {
        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;
@@ -82,6 +104,7 @@ enum abx500_chargalg_states {
        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,
@@ -113,6 +136,7 @@ static const char *states[] = {
        "HW_TEMP_PROTECT_INIT",
        "HW_TEMP_PROTECT",
        "NORMAL_INIT",
+       "USB_PP_PRE_CHARGE",
        "NORMAL",
        "WAIT_FOR_RECHARGE_INIT",
        "WAIT_FOR_RECHARGE",
@@ -204,6 +228,8 @@ enum maxim_ret {
  * @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
@@ -227,6 +253,8 @@ struct abx500_chargalg {
        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;
@@ -236,51 +264,69 @@ struct abx500_chargalg {
        struct delayed_work chargalg_periodic_work;
        struct delayed_work chargalg_wd_work;
        struct work_struct chargalg_work;
-       struct timer_list safety_timer;
-       struct timer_list maintenance_timer;
+       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
- * @data:      pointer to the abx500_chargalg structure
+ * @timer:     pointer to the hrtimer structure
  *
  * This function gets called when the safety timer for the charger
  * expires
  */
-static void abx500_chargalg_safety_timer_expired(unsigned long data)
+static enum hrtimer_restart
+abx500_chargalg_safety_timer_expired(struct hrtimer *timer)
 {
-       struct abx500_chargalg *di = (struct abx500_chargalg *) data;
+       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
- * @i:         pointer to the abx500_chargalg structure
+ * @timer:     pointer to the timer structure
  *
  * This function gets called when the maintenence timer
  * expires
  */
-static void abx500_chargalg_maintenance_timer_expired(unsigned long data)
+static enum hrtimer_restart
+abx500_chargalg_maintenance_timer_expired(struct hrtimer *timer)
 {
 
-       struct abx500_chargalg *di = (struct abx500_chargalg *) data;
+       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;
 }
 
 /**
@@ -303,6 +349,30 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di,
        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
@@ -347,6 +417,22 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
        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
@@ -356,19 +442,16 @@ static int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di)
  */
 static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
 {
-       unsigned long timer_expiration = 0;
+       /* Charger-dependent expiration time in hours*/
+       int timer_expiration = 0;
 
        switch (di->chg_info.charger_type) {
        case AC_CHG:
-               timer_expiration =
-               round_jiffies(jiffies +
-                       (di->bm->main_safety_tmr_h * 3600 * HZ));
+               timer_expiration = di->bm->main_safety_tmr_h;
                break;
 
        case USB_CHG:
-               timer_expiration =
-               round_jiffies(jiffies +
-                       (di->bm->usb_safety_tmr_h * 3600 * HZ));
+               timer_expiration = di->bm->usb_safety_tmr_h;
                break;
 
        default:
@@ -377,11 +460,10 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
        }
 
        di->events.safety_timer_expired = false;
-       di->safety_timer.expires = timer_expiration;
-       if (!timer_pending(&di->safety_timer))
-               add_timer(&di->safety_timer);
-       else
-               mod_timer(&di->safety_timer, timer_expiration);
+       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);
 }
 
 /**
@@ -392,8 +474,8 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
  */
 static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
 {
-       di->events.safety_timer_expired = false;
-       del_timer(&di->safety_timer);
+       if (hrtimer_try_to_cancel(&di->safety_timer) >= 0)
+               di->events.safety_timer_expired = false;
 }
 
 /**
@@ -408,17 +490,11 @@ static void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di)
 static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
        int duration)
 {
-       unsigned long timer_expiration;
-
-       /* Convert from hours to jiffies */
-       timer_expiration = round_jiffies(jiffies + (duration * 3600 * HZ));
-
+       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;
-       di->maintenance_timer.expires = timer_expiration;
-       if (!timer_pending(&di->maintenance_timer))
-               add_timer(&di->maintenance_timer);
-       else
-               mod_timer(&di->maintenance_timer, timer_expiration);
+       hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL);
 }
 
 /**
@@ -430,8 +506,8 @@ static void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di,
  */
 static void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di)
 {
-       di->events.maintenance_timer_expired = false;
-       del_timer(&di->maintenance_timer);
+       if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0)
+               di->events.maintenance_timer_expired = false;
 }
 
 /**
@@ -477,6 +553,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
 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;
 
@@ -489,6 +567,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
        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);
 }
 
@@ -520,6 +606,37 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
        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
@@ -613,8 +730,6 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
 static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
        int vset, int iset)
 {
-       bool start_chargalg_wd = true;
-
        switch (di->chg_info.charger_type) {
        case AC_CHG:
                dev_dbg(di->dev,
@@ -632,12 +747,8 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
 
        default:
                dev_err(di->dev, "Unknown charger to charge from\n");
-               start_chargalg_wd = false;
                break;
        }
-
-       if (start_chargalg_wd && !delayed_work_pending(&di->chargalg_wd_work))
-               queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
 }
 
 /**
@@ -725,6 +836,9 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
                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");
@@ -1217,6 +1331,8 @@ static void abx500_chargalg_external_power_changed(struct power_supply *psy)
 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,
@@ -1227,6 +1343,15 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *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
@@ -1416,9 +1541,34 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
                break;
 
        case STATE_NORMAL_INIT:
-               abx500_chargalg_start_charging(di,
-                       di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
-                       di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
+               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);
@@ -1430,6 +1580,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 
                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 &&
@@ -1653,99 +1810,134 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
 
 /* Exposure to the sysfs interface */
 
-/**
- * abx500_chargalg_sysfs_show() - sysfs show operations
- * @kobj:      pointer to the struct kobject
- * @attr:      pointer to the struct attribute
- * @buf:       buffer that holds the parameter to send to userspace
- *
- * Returns a buffer to be displayed in user space
- */
-static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
-                                         struct attribute *attr, char *buf)
+static ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di,
+                                             char *buf)
 {
-       struct abx500_chargalg *di = container_of(kobj,
-               struct abx500_chargalg, chargalg_kobject);
+       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);
 }
 
-/**
- * abx500_chargalg_sysfs_charger() - sysfs store operations
- * @kobj:      pointer to the struct kobject
- * @attr:      pointer to the struct attribute
- * @buf:       buffer that holds the parameter passed from userspace
- * @length:    length of the parameter passed
- *
- * Returns length of the buffer(input taken from user space) on success
- * else error code on failure
- * The operation to be performed on passing the parameters from the user space.
- */
-static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
-       struct attribute *attr, const char *buf, size_t length)
+static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di,
+       const char *buf, size_t length)
 {
-       struct abx500_chargalg *di = container_of(kobj,
-               struct abx500_chargalg, chargalg_kobject);
        long int param;
        int ac_usb;
        int ret;
-       char entry = *attr->name;
 
-       switch (entry) {
-       case 'c':
-               ret = strict_strtol(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");
-               };
+       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 attribute abx500_chargalg_en_charger = \
+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)
 {
-       .name = "chargalg",
-       .mode = S_IRUGO | S_IWUSR,
-};
+       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,
-       NULL
+       &abx500_chargalg_en_charger.attr,
+       &abx500_chargalg_curr_step.attr,
+       NULL,
 };
 
 static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
@@ -1832,10 +2024,16 @@ static int abx500_chargalg_remove(struct platform_device *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);
 
-       flush_scheduled_work();
        power_supply_unregister(&di->chargalg_psy);
        platform_set_drvdata(pdev, NULL);
 
@@ -1873,8 +2071,9 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
                }
        }
 
-       /* get device struct */
+       /* get device struct and parent */
        di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
 
        /* chargalg supply */
        di->chargalg_psy.name = "abx500_chargalg";
@@ -1888,15 +2087,13 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
                abx500_chargalg_external_power_changed;
 
        /* Initilialize safety timer */
-       init_timer(&di->safety_timer);
+       hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
        di->safety_timer.function = abx500_chargalg_safety_timer_expired;
-       di->safety_timer.data = (unsigned long) di;
 
        /* Initilialize maintenance timer */
-       init_timer(&di->maintenance_timer);
+       hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
        di->maintenance_timer.function =
                abx500_chargalg_maintenance_timer_expired;
-       di->maintenance_timer.data = (unsigned long) di;
 
        /* Create a work queue for the chargalg */
        di->chargalg_wq =
@@ -1933,6 +2130,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
                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);
@@ -1964,18 +2162,7 @@ static struct platform_driver abx500_chargalg_driver = {
        },
 };
 
-static int __init abx500_chargalg_init(void)
-{
-       return platform_driver_register(&abx500_chargalg_driver);
-}
-
-static void __exit abx500_chargalg_exit(void)
-{
-       platform_driver_unregister(&abx500_chargalg_driver);
-}
-
-module_init(abx500_chargalg_init);
-module_exit(abx500_chargalg_exit);
+module_platform_driver(abx500_chargalg_driver);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
index 8acc3f8d303c70876e9e53e01a3dffa985cddc44..fefc39fe42bef0e600a47d5e2aade039e6a96d83 100644 (file)
@@ -1485,13 +1485,12 @@ static int charger_manager_probe(struct platform_device *pdev)
 
        /* Basic Values. Unspecified are Null or 0 */
        cm->dev = &pdev->dev;
-       cm->desc = kzalloc(sizeof(struct charger_desc), GFP_KERNEL);
+       cm->desc = kmemdup(desc, sizeof(struct charger_desc), GFP_KERNEL);
        if (!cm->desc) {
                dev_err(&pdev->dev, "Cannot allocate memory.\n");
                ret = -ENOMEM;
                goto err_alloc_desc;
        }
-       memcpy(cm->desc, desc, sizeof(struct charger_desc));
        cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
 
        /*
index e8c5a391a4987b650f4699b516d6371456b06bda..ae6c41835ee6043eef81481b07db6ac467057abc 100644 (file)
@@ -505,7 +505,7 @@ static int da9030_battery_probe(struct platform_device *pdev)
            pdata->charge_millivolt > 4350)
                return -EINVAL;
 
-       charger = kzalloc(sizeof(*charger), GFP_KERNEL);
+       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
        if (charger == NULL)
                return -ENOMEM;
 
@@ -557,8 +557,6 @@ err_notifier:
        cancel_delayed_work(&charger->work);
 
 err_charger_init:
-       kfree(charger);
-
        return ret;
 }
 
@@ -575,8 +573,6 @@ static int da9030_battery_remove(struct platform_device *dev)
        da9030_set_charge(charger, 0);
        power_supply_unregister(&charger->psy);
 
-       kfree(charger);
-
        return 0;
 }
 
index 08193feb3b088bc7ae7d43134e6a12e7e2f75da9..f8f4c0f7c17da6085914b7868aa396b41073714c 100644 (file)
@@ -594,7 +594,8 @@ static s32 da9052_bat_probe(struct platform_device *pdev)
        int ret;
        int i;
 
-       bat = kzalloc(sizeof(struct da9052_battery), GFP_KERNEL);
+       bat = devm_kzalloc(&pdev->dev, sizeof(struct da9052_battery),
+                               GFP_KERNEL);
        if (!bat)
                return -ENOMEM;
 
@@ -635,7 +636,6 @@ err:
        while (--i >= 0)
                da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
 
-       kfree(bat);
        return ret;
 }
 static int da9052_bat_remove(struct platform_device *pdev)
@@ -647,7 +647,6 @@ static int da9052_bat_remove(struct platform_device *pdev)
                da9052_free_irq(bat->da9052, da9052_bat_irq_bits[i], bat);
 
        power_supply_unregister(&bat->psy);
-       kfree(bat);
 
        return 0;
 }
index 704e652072be08acb238f8a3b8965beb90cdf10b..85b4e6eca0b1984cc39ea0a3e0d6fbd5614ab543 100644 (file)
@@ -512,7 +512,7 @@ static int ds2760_battery_probe(struct platform_device *pdev)
        int retval = 0;
        struct ds2760_device_info *di;
 
-       di = kzalloc(sizeof(*di), GFP_KERNEL);
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
        if (!di) {
                retval = -ENOMEM;
                goto di_alloc_failed;
@@ -576,7 +576,6 @@ static int ds2760_battery_probe(struct platform_device *pdev)
 workqueue_failed:
        power_supply_unregister(&di->bat);
 batt_failed:
-       kfree(di);
 di_alloc_failed:
 success:
        return retval;
@@ -590,7 +589,6 @@ static int ds2760_battery_remove(struct platform_device *pdev)
        cancel_delayed_work_sync(&di->set_charged_work);
        destroy_workqueue(di->monitor_wqueue);
        power_supply_unregister(&di->bat);
-       kfree(di);
 
        return 0;
 }
index 8b6c4539e7f409cac61c885060a578467d0d1464..9f418fa879e5027141414444e619d64db0a73d64 100644 (file)
@@ -760,7 +760,7 @@ static int ds2780_battery_probe(struct platform_device *pdev)
        int ret = 0;
        struct ds2780_device_info *dev_info;
 
-       dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
+       dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
        if (!dev_info) {
                ret = -ENOMEM;
                goto fail;
@@ -779,7 +779,7 @@ static int ds2780_battery_probe(struct platform_device *pdev)
        ret = power_supply_register(&pdev->dev, &dev_info->bat);
        if (ret) {
                dev_err(dev_info->dev, "failed to register battery\n");
-               goto fail_free_info;
+               goto fail;
        }
 
        ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
@@ -813,8 +813,6 @@ fail_remove_group:
        sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
 fail_unregister:
        power_supply_unregister(&dev_info->bat);
-fail_free_info:
-       kfree(dev_info);
 fail:
        return ret;
 }
@@ -828,7 +826,6 @@ static int ds2780_battery_remove(struct platform_device *pdev)
 
        power_supply_unregister(&dev_info->bat);
 
-       kfree(dev_info);
        return 0;
 }
 
index c09e7726c96cc25c6b890872db9b64f4f7903b52..563174891c90d07b7c9cc5c711588fd249695dd1 100644 (file)
@@ -332,32 +332,32 @@ static int ds278x_battery_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 
-static int ds278x_suspend(struct i2c_client *client,
-               pm_message_t state)
+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 i2c_client *client)
+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;
 }
 
-#else
-
-#define ds278x_suspend NULL
-#define ds278x_resume NULL
-
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(ds278x_battery_pm_ops, ds278x_suspend, ds278x_resume);
+#define DS278X_BATTERY_PM_OPS (&ds278x_battery_pm_ops)
 
+#else
+#define DS278X_BATTERY_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
 
 enum ds278x_num_id {
        DS2782 = 0,
@@ -460,11 +460,10 @@ 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,
-       .suspend        = ds278x_suspend,
-       .resume         = ds278x_resume,
        .id_table       = ds278x_id,
 };
 module_i2c_driver(ds278x_battery_driver);
index c10f460f986f8cdaa2c158240b8387d787cd6e64..29eba88a296367ffe10c1886462cb77822f05ac8 100644 (file)
@@ -178,7 +178,7 @@ static int goldfish_battery_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       data->reg_base = devm_ioremap(&pdev->dev, r->start, r->end - r->start + 1);
+       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;
index e3e40a9f3af2a962e39a4f4687359e348bc87d11..e9883eeeee76a0506a9b891ddcdf19320ce2d6b3 100644 (file)
@@ -86,7 +86,8 @@ static int gpio_charger_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       gpio_charger = kzalloc(sizeof(*gpio_charger), GFP_KERNEL);
+       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;
@@ -140,7 +141,6 @@ static int gpio_charger_probe(struct platform_device *pdev)
 err_gpio_free:
        gpio_free(pdata->gpio);
 err_free:
-       kfree(gpio_charger);
        return ret;
 }
 
@@ -156,7 +156,6 @@ static int gpio_charger_remove(struct platform_device *pdev)
        gpio_free(gpio_charger->pdata->gpio);
 
        platform_set_drvdata(pdev, NULL);
-       kfree(gpio_charger);
 
        return 0;
 }
index 176ad59d99f5799ce10d8b4550d614a88f1e7e8d..fc04d191579bd987ba0ad94ddd6b298c329c2b18 100644 (file)
@@ -411,7 +411,7 @@ static int isp1704_charger_probe(struct platform_device *pdev)
        struct isp1704_charger  *isp;
        int                     ret = -ENODEV;
 
-       isp = kzalloc(sizeof *isp, GFP_KERNEL);
+       isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
        if (!isp)
                return -ENOMEM;
 
@@ -477,8 +477,6 @@ fail1:
        isp1704_charger_set_power(isp, 0);
        usb_put_phy(isp->phy);
 fail0:
-       kfree(isp);
-
        dev_err(&pdev->dev, "failed to register isp1704 with error %d\n", ret);
 
        return ret;
@@ -492,7 +490,6 @@ static int isp1704_charger_remove(struct platform_device *pdev)
        power_supply_unregister(&isp->psy);
        usb_put_phy(isp->phy);
        isp1704_charger_set_power(isp, 0);
-       kfree(isp);
 
        return 0;
 }
index 6d1f452810b8753435af954e1aedcef53c9c5e67..ed49b50b220b3ed6541c5b6f82079e1d0373e8f7 100644 (file)
@@ -49,7 +49,6 @@
 #define LP8788_CHG_START               0x11
 #define LP8788_CHG_END                 0x1C
 
-#define LP8788_BUF_SIZE                        40
 #define LP8788_ISEL_MAX                        23
 #define LP8788_ISEL_STEP               50
 #define LP8788_VTERM_MIN               4100
@@ -633,7 +632,7 @@ static ssize_t lp8788_show_charger_status(struct device *dev,
        lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data);
        state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S;
 
-       return scnprintf(buf, LP8788_BUF_SIZE, "%s\n", desc[state]);
+       return scnprintf(buf, PAGE_SIZE, "%s\n", desc[state]);
 }
 
 static ssize_t lp8788_show_eoc_time(struct device *dev,
@@ -647,7 +646,7 @@ static ssize_t lp8788_show_eoc_time(struct device *dev,
        lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
        val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S;
 
-       return scnprintf(buf, LP8788_BUF_SIZE, "End Of Charge Time: %s\n",
+       return scnprintf(buf, PAGE_SIZE, "End Of Charge Time: %s\n",
                        stime[val]);
 }
 
@@ -667,8 +666,7 @@ static ssize_t lp8788_show_eoc_level(struct device *dev,
        val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S;
        level = mode ? abs_level[val] : relative_level[val];
 
-       return scnprintf(buf, LP8788_BUF_SIZE, "End Of Charge Level: %s\n",
-                       level);
+       return scnprintf(buf, PAGE_SIZE, "End Of Charge Level: %s\n", level);
 }
 
 static DEVICE_ATTR(charger_status, S_IRUSR, lp8788_show_charger_status, NULL);
index 74a0bd9bc1621ef8a139ebace08e6d8f12c2f775..c7ff6d67f158179aa891a1d41b8a10952191f240 100644 (file)
@@ -246,31 +246,34 @@ static int max17040_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 
-static int max17040_suspend(struct i2c_client *client,
-               pm_message_t state)
+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 i2c_client *client)
+static int max17040_resume(struct device *dev)
 {
+       struct i2c_client *client = to_i2c_client(dev);
        struct max17040_chip *chip = i2c_get_clientdata(client);
 
        schedule_delayed_work(&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_suspend NULL
-#define max17040_resume NULL
+#define MAX17040_PM_OPS NULL
 
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
 
 static const struct i2c_device_id max17040_id[] = {
        { "max17040", 0 },
@@ -281,11 +284,10 @@ 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,
-       .suspend        = max17040_suspend,
-       .resume         = max17040_resume,
        .id_table       = max17040_id,
 };
 module_i2c_driver(max17040_i2c_driver);
index 14e2b96d93b0f8eb43cc78379804cfe818bf9049..08f0d7909b6b9a2c95a90ee0292bd301129eb52b 100644 (file)
@@ -189,7 +189,7 @@ static int max8903_probe(struct platform_device *pdev)
        int ta_in = 0;
        int usb_in = 0;
 
-       data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL);
+       data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
        if (data == NULL) {
                dev_err(dev, "Cannot allocate memory.\n");
                return -ENOMEM;
@@ -341,7 +341,6 @@ err_dc_irq:
 err_psy:
        power_supply_unregister(&data->psy);
 err:
-       kfree(data);
        return ret;
 }
 
@@ -359,7 +358,6 @@ static int max8903_remove(struct platform_device *pdev)
                if (pdata->dc_valid)
                        free_irq(gpio_to_irq(pdata->dok), data);
                power_supply_unregister(&data->psy);
-               kfree(data);
        }
 
        return 0;
index 665cdc76c26502e7a207ac8cbfc1dbfb90be5864..0ee1e14f76e9a8957f298cb797fb3243958a0af9 100644 (file)
@@ -489,7 +489,8 @@ static int max8925_power_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       info = kzalloc(sizeof(struct max8925_power_info), GFP_KERNEL);
+       info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
+                               GFP_KERNEL);
        if (!info)
                return -ENOMEM;
        info->chip = chip;
@@ -546,7 +547,6 @@ out_battery:
 out_usb:
        power_supply_unregister(&info->ac);
 out:
-       kfree(info);
        return ret;
 }
 
@@ -559,7 +559,6 @@ static int max8925_power_remove(struct platform_device *pdev)
                power_supply_unregister(&info->usb);
                power_supply_unregister(&info->battery);
                max8925_deinit_charger(info);
-               kfree(info);
        }
        return 0;
 }
index e757885b620c5c6cf00639fc6a5952631308041b..4bdedfed936d87ab55cc1c7c1b7c84e884361fd6 100644 (file)
@@ -138,7 +138,8 @@ static int max8997_battery_probe(struct platform_device *pdev)
                return ret;
        }
 
-       charger = kzalloc(sizeof(struct charger_data), GFP_KERNEL);
+       charger = devm_kzalloc(&pdev->dev, sizeof(struct charger_data),
+                               GFP_KERNEL);
        if (charger == NULL) {
                dev_err(&pdev->dev, "Cannot allocate memory.\n");
                return -ENOMEM;
@@ -158,13 +159,10 @@ static int max8997_battery_probe(struct platform_device *pdev)
        ret = power_supply_register(&pdev->dev, &charger->battery);
        if (ret) {
                dev_err(&pdev->dev, "failed: power supply register\n");
-               goto err;
+               return ret;
        }
 
        return 0;
-err:
-       kfree(charger);
-       return ret;
 }
 
 static int max8997_battery_remove(struct platform_device *pdev)
@@ -172,7 +170,6 @@ static int max8997_battery_remove(struct platform_device *pdev)
        struct charger_data *charger = platform_get_drvdata(pdev);
 
        power_supply_unregister(&charger->battery);
-       kfree(charger);
        return 0;
 }
 
index bf677e3daec93a031a9db5f2061775dc1336c265..5017470c2fc913965bfe93020072cc08b421dae8 100644 (file)
@@ -88,7 +88,8 @@ static int max8998_battery_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       max8998 = kzalloc(sizeof(struct max8998_battery_data), GFP_KERNEL);
+       max8998 = devm_kzalloc(&pdev->dev, sizeof(struct max8998_battery_data),
+                               GFP_KERNEL);
        if (!max8998)
                return -ENOMEM;
 
@@ -174,7 +175,6 @@ static int max8998_battery_probe(struct platform_device *pdev)
 
        return 0;
 err:
-       kfree(max8998);
        return ret;
 }
 
@@ -183,7 +183,6 @@ static int max8998_battery_remove(struct platform_device *pdev)
        struct max8998_battery_data *max8998 = platform_get_drvdata(pdev);
 
        power_supply_unregister(&max8998->battery);
-       kfree(max8998);
 
        return 0;
 }
index c2122a7ad06577449f980f31e3f43183cfe4a0ac..17fd77f24b2ab3ea8b0e05b392dd038534a19b03 100644 (file)
@@ -373,7 +373,7 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
        int i;
        u8 mbcs1;
 
-       mbc = kzalloc(sizeof(*mbc), GFP_KERNEL);
+       mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
        if (!mbc)
                return -ENOMEM;
 
@@ -413,7 +413,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
        ret = power_supply_register(&pdev->dev, &mbc->adapter);
        if (ret) {
                dev_err(mbc->pcf->dev, "failed to register adapter\n");
-               kfree(mbc);
                return ret;
        }
 
@@ -421,7 +420,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
        if (ret) {
                dev_err(mbc->pcf->dev, "failed to register usb\n");
                power_supply_unregister(&mbc->adapter);
-               kfree(mbc);
                return ret;
        }
 
@@ -430,7 +428,6 @@ static int pcf50633_mbc_probe(struct platform_device *pdev)
                dev_err(mbc->pcf->dev, "failed to register ac\n");
                power_supply_unregister(&mbc->adapter);
                power_supply_unregister(&mbc->usb);
-               kfree(mbc);
                return ret;
        }
 
@@ -461,8 +458,6 @@ static int pcf50633_mbc_remove(struct platform_device *pdev)
        power_supply_unregister(&mbc->adapter);
        power_supply_unregister(&mbc->ac);
 
-       kfree(mbc);
-
        return 0;
 }
 
index ee346d443571032ee2f6e3f2dd2d7514225ce4aa..a44175139bbf3a619d65fe611cc9fa961388cd76 100644 (file)
 #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/i2c.h>
 #include <linux/workqueue.h>
-#include <linux/kobject.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/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,
@@ -113,33 +114,24 @@ static const struct i2c_device_id pm2xxx_ident[] = {
 
 static void set_lpn_pin(struct pm2xxx_charger *pm2)
 {
-       if (pm2->ac.charger_connected)
-               return;
-       gpio_set_value(pm2->lpn_pin, 1);
-
-       return;
+       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)
-               return;
-       gpio_set_value(pm2->lpn_pin, 0);
-
-       return;
+       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;
-       /*
-        * When AC adaptor is unplugged, the host
-        * must put LPN high to be able to
-        * communicate by I2C with PM2301
-        * and receive I2C "acknowledge" from PM2301.
-        */
-       mutex_lock(&pm2->lock);
-       set_lpn_pin(pm2);
+
+       /* wake up the device */
+       pm_runtime_get_sync(pm2->dev);
 
        ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
                                1, val);
@@ -147,8 +139,8 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
                dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
        else
                ret = 0;
-       clear_lpn_pin(pm2);
-       mutex_unlock(&pm2->lock);
+
+       pm_runtime_put_sync(pm2->dev);
 
        return ret;
 }
@@ -156,14 +148,9 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
 static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
 {
        int ret;
-       /*
-        * When AC adaptor is unplugged, the host
-        * must put LPN high to be able to
-        * communicate by I2C with PM2301
-        * and receive I2C "acknowledge" from PM2301.
-        */
-       mutex_lock(&pm2->lock);
-       set_lpn_pin(pm2);
+
+       /* wake up the device */
+       pm_runtime_get_sync(pm2->dev);
 
        ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
                                1, &val);
@@ -171,8 +158,8 @@ static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
                dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
        else
                ret = 0;
-       clear_lpn_pin(pm2);
-       mutex_unlock(&pm2->lock);
+
+       pm_runtime_put_sync(pm2->dev);
 
        return ret;
 }
@@ -192,11 +179,22 @@ 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 ret;
+       return 0;
 }
 
 static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
@@ -216,21 +214,14 @@ int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
 
 static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val)
 {
-       int ret = 0;
+       dev_err(pm2->dev, "Overvoltage detected\n");
+       pm2->flags.ovv = true;
+       power_supply_changed(&pm2->ac_chg.psy);
 
-       pm2->failure_input_ovv++;
-       if (pm2->failure_input_ovv < 4) {
-               ret = pm2xxx_charging_enable_mngt(pm2);
-               goto out;
-       } else {
-               pm2->failure_input_ovv = 0;
-               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);
 
-out:
-       return ret;
+       return 0;
 }
 
 static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
@@ -245,13 +236,29 @@ static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
 
 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:
@@ -322,16 +329,27 @@ static int pm2_int_reg0(void *pm2_data, int val)
        struct pm2xxx_charger *pm2 = pm2_data;
        int ret = 0;
 
-       if (val & (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
-               ret = pm2xxx_charger_vbat_lsig_mngt(pm2, val &
-                       (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF));
+       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;
 }
 
@@ -447,7 +465,6 @@ 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");
        }
@@ -468,14 +485,22 @@ static irqreturn_t  pm2xxx_irq_int(int irq, void *data)
        struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
        int i;
 
-       for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
-                pm2xxx_reg_read(pm2,
+       /* 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]);
-       }
+                       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;
 }
@@ -592,6 +617,8 @@ static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
                        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;
@@ -674,10 +701,6 @@ static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
        ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG,
                PM2XXX_VBAT_LOW_MONITORING_ENA);
 
-       /* Disable LED */
-       ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
-               PM2XXX_LED_SELECT_DIS);
-
        return ret;
 }
 
@@ -822,10 +845,54 @@ static void pm2xxx_charger_ac_work(struct work_struct *work)
        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,
@@ -840,24 +907,105 @@ static struct pm2xxx_irq pm2xxx_charger_irq[] = {
        {"PM2XXX_IRQ_INT", pm2xxx_irq_int},
 };
 
-static int pm2xxx_wall_charger_resume(struct i2c_client *i2c_client)
+#ifdef CONFIG_PM
+
+#ifdef CONFIG_PM_SLEEP
+
+static int 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 pm2xxx_wall_charger_suspend(struct i2c_client *i2c_client,
-       pm_message_t state)
+static int 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 __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int  pm2xxx_runtime_suspend(struct device *dev)
+{
+       struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
+       struct pm2xxx_charger *pm2;
+       int ret = 0;
+
+       pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
+       if (!pm2) {
+               dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
+               ret = -EINVAL;
+               return ret;
+       }
+
+       clear_lpn_pin(pm2);
+
+       return ret;
+}
+
+static int  pm2xxx_runtime_resume(struct device *dev)
+{
+       struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
+       struct pm2xxx_charger *pm2;
+       int ret = 0;
+
+       pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
+       if (!pm2) {
+               dev_err(pm2->dev, "no pm2xxx_charger data supplied\n");
+               ret = -EINVAL;
+               return ret;
+       }
+
+       if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0)
+               set_lpn_pin(pm2);
+
+       return ret;
+}
+
+#endif
+
+static const struct dev_pm_ops pm2xxx_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend,
+               pm2xxx_wall_charger_resume)
+       SET_RUNTIME_PM_OPS(pm2xxx_runtime_suspend, pm2xxx_runtime_resume, NULL)
+};
+#define  PM2XXX_PM_OPS (&pm2xxx_pm_ops)
+#else
+#define  PM2XXX_PM_OPS  NULL
+#endif
+
+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 pm2xxx_charger *pm2;
        int ret = 0;
        u8 val;
+       int i;
 
        pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL);
        if (!pm2) {
@@ -867,7 +1015,6 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
 
        /* get parent data */
        pm2->dev = &i2c_client->dev;
-       pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
        pm2->pm2_int = &pm2xxx_int;
 
@@ -889,14 +1036,6 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
 
        pm2->bat = pl_data->battery;
 
-       /*get lpn GPIO from platform data*/
-       if (!pm2->pdata->lpn_gpio) {
-               dev_err(pm2->dev, "no lpn gpio data supplied\n");
-               ret = -EINVAL;
-               goto free_device_info;
-       }
-       pm2->lpn_pin = pm2->pdata->lpn_gpio;
-
        if (!i2c_check_functionality(i2c_client->adapter,
                        I2C_FUNC_SMBUS_BYTE_DATA |
                        I2C_FUNC_SMBUS_READ_WORD_DATA)) {
@@ -945,6 +1084,10 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
        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
@@ -965,40 +1108,72 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
        }
 
        /* Register interrupts */
-       ret = request_threaded_irq(pm2->pdata->irq_number, NULL,
+       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, pm2->pdata->irq_number, ret);
+               pm2xxx_charger_irq[0].name,
+                       gpio_to_irq(pm2->pdata->gpio_irq_number), ret);
                goto unregister_pm2xxx_charger;
        }
 
-       /*Initialize lock*/
-       mutex_init(&pm2->lock);
+       ret = pm_runtime_set_active(pm2->dev);
+       if (ret)
+               dev_err(pm2->dev, "set active Error\n");
 
-       /*
-        * 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 unregister_pm2xxx_charger;
+       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;
        }
-       ret = gpio_direction_output(pm2->lpn_pin, 0);
-       if (ret < 0) {
-               dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n");
-               goto free_gpio;
+
+       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");
@@ -1007,7 +1182,13 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
        return 0;
 
 free_gpio:
-       gpio_free(pm2->lpn_pin);
+       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);
@@ -1018,18 +1199,24 @@ free_charger_wq:
        destroy_workqueue(pm2->charger_wq);
 free_device_info:
        kfree(pm2);
+
        return ret;
 }
 
-static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
+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(pm2->pdata->irq_number, pm2);
+       free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
 
        /* Delete the work queue */
        destroy_workqueue(pm2->charger_wq);
@@ -1041,8 +1228,8 @@ static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
 
        power_supply_unregister(&pm2->ac_chg.psy);
 
-       /*Free GPIO60*/
-       gpio_free(pm2->lpn_pin);
+       if (gpio_is_valid(pm2->lpn_pin))
+               gpio_free(pm2->lpn_pin);
 
        kfree(pm2);
 
@@ -1058,12 +1245,11 @@ MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
 
 static struct i2c_driver pm2xxx_charger_driver = {
        .probe = pm2xxx_wall_charger_probe,
-       .remove = __devexit_p(pm2xxx_wall_charger_remove),
-       .suspend = pm2xxx_wall_charger_suspend,
-       .resume = pm2xxx_wall_charger_resume,
+       .remove = pm2xxx_wall_charger_remove,
        .driver = {
                .name = "pm2xxx-wall_charger",
                .owner = THIS_MODULE,
+               .pm = PM2XXX_PM_OPS,
        },
        .id_table = pm2xxx_id,
 };
@@ -1078,11 +1264,10 @@ static void __exit pm2xxx_charger_exit(void)
        i2c_del_driver(&pm2xxx_charger_driver);
 }
 
-subsys_initcall_sync(pm2xxx_charger_init);
+device_initcall_sync(pm2xxx_charger_init);
 module_exit(pm2xxx_charger_exit);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
 MODULE_ALIAS("platform:pm2xxx-charger");
 MODULE_DESCRIPTION("PM2xxx charger management driver");
-
index e6319cdbc94fd6a6aac822f86b902ff6b7939e5e..8ce3cc0195df084e065c4c1d3ad0da55ce70b584 100644 (file)
@@ -9,27 +9,6 @@
 #ifndef PM2301_CHARGER_H
 #define PM2301_CHARGER_H
 
-#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 MAIN_CH_DET                    0x01
-#define MAIN_CH_CV_ON                  0x04
-#define OTP_ENABLE_WD                  0x01
-
-#define MAIN_CH_INPUT_CURR_SHIFT       4
-
-#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 MAIN_CH_NOK                    0x01
-
 /* Watchdog timeout constant */
 #define WD_TIMER                       0x30 /* 4min */
 #define WD_KICK_INTERVAL               (30 * HZ)
@@ -495,7 +474,6 @@ struct pm2xxx_charger {
        int failure_input_ovv;
        unsigned int lpn_pin;
        struct pm2xxx_interrupts *pm2_int;
-       struct ab8500_gpadc *gpadc;
        struct regulator *regu;
        struct pm2xxx_bm_data *bat;
        struct mutex lock;
@@ -506,6 +484,7 @@ struct pm2xxx_charger {
        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 pm2xxx_charger_event_flags flags;
 };
index 5deac432e2ae7b2025bc2e985ec5b6da6dff214d..1c517c34e4be8edf7145f93eeabc1768c0295f4a 100644 (file)
@@ -26,17 +26,42 @@ EXPORT_SYMBOL_GPL(power_supply_class);
 
 static struct device_type power_supply_dev_type;
 
+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->name)
+                       return false;
+               for (i = 0; i < supply->num_supplies; i++)
+                       if (!strcmp(supplier->name, supply->supplied_from[i]))
+                               return true;
+       } else {
+               if (!supply->name)
+                       return false;
+               for (i = 0; i < supplier->num_supplicants; i++)
+                       if (!strcmp(supplier->supplied_to[i], supply->name))
+                               return true;
+       }
+
+       return false;
+}
+
 static int __power_supply_changed_work(struct device *dev, void *data)
 {
        struct power_supply *psy = (struct power_supply *)data;
        struct power_supply *pst = dev_get_drvdata(dev);
-       int i;
 
-       for (i = 0; i < psy->num_supplicants; i++)
-               if (!strcmp(psy->supplied_to[i], pst->name)) {
-                       if (pst->external_power_changed)
-                               pst->external_power_changed(pst);
-               }
+       if (__power_supply_is_supplied_by(psy, pst)) {
+               if (pst->external_power_changed)
+                       pst->external_power_changed(pst);
+       }
+
        return 0;
 }
 
@@ -63,22 +88,151 @@ void power_supply_changed(struct power_supply *psy)
 }
 EXPORT_SYMBOL_GPL(power_supply_changed);
 
+#ifdef CONFIG_OF
+#include <linux/of.h>
+
+static int __power_supply_populate_supplied_from(struct device *dev,
+                                                void *data)
+{
+       struct power_supply *psy = (struct power_supply *)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)
+                       continue;
+
+               if (np == epsy->of_node) {
+                       dev_info(psy->dev, "%s: Found supply : %s\n",
+                               psy->name, epsy->name);
+                       psy->supplied_from[i-1] = (char *)epsy->name;
+                       psy->num_supplies++;
+                       break;
+               }
+       } 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 = (struct device_node *)data;
+       struct power_supply *epsy = dev_get_drvdata(dev);
+
+       /* return error breaks out of class_for_each_device loop */
+       if (epsy->of_node == np)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int power_supply_find_supply_from_node(struct device_node *supply_node)
+{
+       int error;
+       struct device *dev;
+       struct class_dev_iter iter;
+
+       /*
+        * Use iterator to see if any other device is registered.
+        * This is required since class_for_each_device returns 0
+        * if there are no devices registered.
+        */
+       class_dev_iter_init(&iter, power_supply_class, NULL, NULL);
+       dev = class_dev_iter_next(&iter);
+
+       if (!dev)
+               return -EPROBE_DEFER;
+
+       /*
+        * We have to treat the return value as inverted, because if
+        * we return error on not found, then it won't continue looking.
+        * So we trick it by returning error on success to stop looking
+        * once the matching device is found.
+        */
+       error = class_for_each_device(power_supply_class, NULL, supply_node,
+                                      __power_supply_find_supply_from_node);
+
+       return error ? 0 : -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)
+                       continue;
+
+               ret = power_supply_find_supply_from_node(np);
+               if (ret) {
+                       dev_dbg(psy->dev, "Failed to find supply, defer!\n");
+                       return -EPROBE_DEFER;
+               }
+       } while (np);
+
+       /* 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,
+                                          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 = (struct power_supply *)data;
        struct power_supply *epsy = dev_get_drvdata(dev);
-       int i;
 
-       for (i = 0; i < epsy->num_supplicants; i++) {
-               if (!strcmp(epsy->supplied_to[i], psy->name)) {
-                       if (epsy->get_property(epsy,
-                                 POWER_SUPPLY_PROP_ONLINE, &ret))
-                               continue;
+       if (__power_supply_is_supplied_by(epsy, psy))
+               if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) {
                        if (ret.intval)
                                return ret.intval;
                }
-       }
+
        return 0;
 }
 
@@ -336,6 +490,12 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
 
        INIT_WORK(&psy->changed_work, power_supply_changed_work);
 
+       rc = power_supply_check_supplies(psy);
+       if (rc) {
+               dev_info(dev, "Not all required supplies found, defer probe\n");
+               goto check_supplies_failed;
+       }
+
        rc = kobject_set_name(&dev->kobj, "%s", psy->name);
        if (rc)
                goto kobject_set_name_failed;
@@ -368,6 +528,7 @@ register_thermal_failed:
        device_del(dev);
 kobject_set_name_failed:
 device_add_failed:
+check_supplies_failed:
        put_device(dev);
 success:
        return rc;
index 1ae65b822864b6b3eb5d4d36e2e1ccebd05fb0f4..349e9ae8090a75eaf3c98f8ece4f99695b2308f4 100644 (file)
@@ -30,3 +30,10 @@ config POWER_RESET_RESTART
          Some boards don't actually have the ability to power off.
          Instead they restart, and u-boot holds the SoC until the
          user presses a key. u-boot then boots into Linux.
+
+config POWER_RESET_VEXPRESS
+       bool
+       depends on POWER_RESET
+       help
+         Power off and reset support for the ARM Ltd. Versatile
+         Express boards.
index 0f317f50c56fb90f5ef0b76ab1ef7d324fdbf16e..372807fd83f78d32d352e1479721407293c24fce 100644 (file)
@@ -1,3 +1,4 @@
 obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
 obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
-obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
\ No newline at end of file
+obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
+obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
similarity index 93%
rename from arch/arm/mach-vexpress/reset.c
rename to drivers/power/reset/vexpress-poweroff.c
index 465923aa3819483cbd45f7a7e51a07a6aa4bfaf1..469e6962b2cff16a6e6a4ff40c4a2c7d40b79ec1 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/stat.h>
 #include <linux/vexpress.h>
 
+#include <asm/system_misc.h>
+
 static void vexpress_reset_do(struct device *dev, const char *what)
 {
        int err = -ENOENT;
@@ -39,14 +41,14 @@ static void vexpress_reset_do(struct device *dev, const char *what)
 
 static struct device *vexpress_power_off_device;
 
-void vexpress_power_off(void)
+static void vexpress_power_off(void)
 {
        vexpress_reset_do(vexpress_power_off_device, "power off");
 }
 
 static struct device *vexpress_restart_device;
 
-void vexpress_restart(char str, const char *cmd)
+static void vexpress_restart(char str, const char *cmd)
 {
        vexpress_reset_do(vexpress_restart_device, "restart");
 }
@@ -103,14 +105,17 @@ static int vexpress_reset_probe(struct platform_device *pdev)
        switch (func) {
        case FUNC_SHUTDOWN:
                vexpress_power_off_device = &pdev->dev;
+               pm_power_off = vexpress_power_off;
                break;
        case FUNC_RESET:
                if (!vexpress_restart_device)
                        vexpress_restart_device = &pdev->dev;
+               arm_pm_restart = vexpress_restart;
                device_create_file(&pdev->dev, &dev_attr_active);
                break;
        case FUNC_REBOOT:
                vexpress_restart_device = &pdev->dev;
+               arm_pm_restart = vexpress_restart;
                device_create_file(&pdev->dev, &dev_attr_active);
                break;
        };
index 8208888b844e973004ba2a146cb40bf70c1458b5..1a1dcb831a170f167b9556c1706950d47cf828e9 100644 (file)
@@ -119,7 +119,7 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
 
        /* First check for temperature in first direct table */
        if (raw < ARRAY_SIZE(rx51_temp_table1))
-               return rx51_temp_table1[raw] * 100;
+               return rx51_temp_table1[raw] * 10;
 
        /* Binary search RAW value in second inverse table */
        while (max - min > 1) {
@@ -132,7 +132,7 @@ static int rx51_battery_read_temperature(struct rx51_device_info *di)
                        break;
        }
 
-       return (rx51_temp_table2_first - min) * 100;
+       return (rx51_temp_table2_first - min) * 10;
 }
 
 /*
@@ -202,7 +202,7 @@ static int rx51_battery_probe(struct platform_device *pdev)
        struct rx51_device_info *di;
        int ret;
 
-       di = kzalloc(sizeof(*di), GFP_KERNEL);
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
        if (!di)
                return -ENOMEM;
 
@@ -217,7 +217,6 @@ static int rx51_battery_probe(struct platform_device *pdev)
        ret = power_supply_register(di->dev, &di->bat);
        if (ret) {
                platform_set_drvdata(pdev, NULL);
-               kfree(di);
                return ret;
        }
 
@@ -230,7 +229,6 @@ static int rx51_battery_remove(struct platform_device *pdev)
 
        power_supply_unregister(&di->bat);
        platform_set_drvdata(pdev, NULL);
-       kfree(di);
 
        return 0;
 }
index d2ca989dcbdce0be3b98a7513c4517b802f1650d..5948ce058bdddefaed394a7fcf3039876946c062 100644 (file)
@@ -145,14 +145,17 @@ static int s3c_adc_bat_get_property(struct power_supply *psy,
 
        int new_level;
        int full_volt;
-       const struct s3c_adc_bat_thresh *lut = bat->pdata->lut_noac;
-       unsigned int lut_size = bat->pdata->lut_noac_cnt;
+       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) {
index 3960f0b2afe97200ff8c8e95312de66b36060f0e..c8c78a74e75a1483ec8c88d71e79fbccc4bf6e24 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
+#include <linux/of.h>
 
 #include <linux/power/sbs-battery.h>
 
@@ -667,7 +668,6 @@ of_out:
        return pdata;
 }
 #else
-#define sbs_dt_ids NULL
 static struct sbs_platform_data *sbs_of_populate_pdata(
        struct i2c_client *client)
 {
@@ -820,10 +820,11 @@ static int sbs_remove(struct i2c_client *client)
        return 0;
 }
 
-#if defined CONFIG_PM
-static int sbs_suspend(struct i2c_client *client,
-       pm_message_t state)
+#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;
 
@@ -838,11 +839,13 @@ static int sbs_suspend(struct i2c_client *client,
 
        return 0;
 }
+
+static SIMPLE_DEV_PM_OPS(sbs_pm_ops, sbs_suspend, NULL);
+#define SBS_PM_OPS (&sbs_pm_ops)
+
 #else
-#define sbs_suspend            NULL
+#define SBS_PM_OPS NULL
 #endif
-/* any smbus transaction will wake up sbs */
-#define sbs_resume             NULL
 
 static const struct i2c_device_id sbs_id[] = {
        { "bq20z75", 0 },
@@ -854,12 +857,11 @@ MODULE_DEVICE_TABLE(i2c, sbs_id);
 static struct i2c_driver sbs_battery_driver = {
        .probe          = sbs_probe,
        .remove         = sbs_remove,
-       .suspend        = sbs_suspend,
-       .resume         = sbs_resume,
        .id_table       = sbs_id,
        .driver = {
                .name   = "sbs-battery",
-               .of_match_table = sbs_dt_ids,
+               .of_match_table = of_match_ptr(sbs_dt_ids),
+               .pm     = SBS_PM_OPS,
        },
 };
 module_i2c_driver(sbs_battery_driver);
index b99a452a4fda40165493a1c3640dbbc23084f18f..0152f35dca5c71c8f4bdddd617606e544831ff69 100644 (file)
@@ -30,6 +30,8 @@ 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)
@@ -185,6 +187,7 @@ static int __init test_power_init(void)
                }
        }
 
+       module_initialized = true;
        return 0;
 failed:
        while (--i >= 0)
@@ -209,6 +212,8 @@ static void __exit test_power_exit(void)
 
        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);
 
@@ -221,8 +226,8 @@ struct battery_property_map {
 };
 
 static struct battery_property_map map_ac_online[] = {
-       { 0,  "on"  },
-       { 1,  "off" },
+       { 0,  "off"  },
+       { 1,  "on" },
        { -1, NULL  },
 };
 
@@ -295,10 +300,16 @@ static const char *map_get_key(struct battery_property_map *map, int value,
        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);
-       power_supply_changed(&test_power_supplies[0]);
+       signal_power_supply_changed(&test_power_supplies[0]);
        return 0;
 }
 
@@ -311,7 +322,7 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp)
 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);
-       power_supply_changed(&test_power_supplies[2]);
+       signal_power_supply_changed(&test_power_supplies[2]);
        return 0;
 }
 
@@ -325,7 +336,7 @@ static int param_set_battery_status(const char *key,
                                        const struct kernel_param *kp)
 {
        battery_status = map_get_value(map_status, key, battery_status);
-       power_supply_changed(&test_power_supplies[1]);
+       signal_power_supply_changed(&test_power_supplies[1]);
        return 0;
 }
 
@@ -339,7 +350,7 @@ static int param_set_battery_health(const char *key,
                                        const struct kernel_param *kp)
 {
        battery_health = map_get_value(map_health, key, battery_health);
-       power_supply_changed(&test_power_supplies[1]);
+       signal_power_supply_changed(&test_power_supplies[1]);
        return 0;
 }
 
@@ -353,7 +364,7 @@ static int param_set_battery_present(const char *key,
                                        const struct kernel_param *kp)
 {
        battery_present = map_get_value(map_present, key, battery_present);
-       power_supply_changed(&test_power_supplies[0]);
+       signal_power_supply_changed(&test_power_supplies[0]);
        return 0;
 }
 
@@ -369,7 +380,7 @@ static int param_set_battery_technology(const char *key,
 {
        battery_technology = map_get_value(map_technology, key,
                                                battery_technology);
-       power_supply_changed(&test_power_supplies[1]);
+       signal_power_supply_changed(&test_power_supplies[1]);
        return 0;
 }
 
@@ -390,7 +401,7 @@ static int param_set_battery_capacity(const char *key,
                return -EINVAL;
 
        battery_capacity = tmp;
-       power_supply_changed(&test_power_supplies[1]);
+       signal_power_supply_changed(&test_power_supplies[1]);
        return 0;
 }
 
@@ -405,7 +416,7 @@ static int param_set_battery_voltage(const char *key,
                return -EINVAL;
 
        battery_voltage = tmp;
-       power_supply_changed(&test_power_supplies[1]);
+       signal_power_supply_changed(&test_power_supplies[1]);
        return 0;
 }
 
diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c
new file mode 100644 (file)
index 0000000..9fbca31
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * 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/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/tps65090.h>
+
+#define TPS65090_REG_INTR_STS  0x00
+#define TPS65090_REG_CG_CTRL0  0x04
+#define TPS65090_REG_CG_CTRL1  0x05
+#define TPS65090_REG_CG_CTRL2  0x06
+#define TPS65090_REG_CG_CTRL3  0x07
+#define TPS65090_REG_CG_CTRL4  0x08
+#define TPS65090_REG_CG_CTRL5  0x09
+#define TPS65090_REG_CG_STATUS1        0x0a
+#define TPS65090_REG_CG_STATUS2        0x0b
+
+#define TPS65090_CHARGER_ENABLE        BIT(0)
+#define TPS65090_VACG          BIT(1)
+#define TPS65090_NOITERM       BIT(5)
+
+struct tps65090_charger {
+       struct  device  *dev;
+       int     ac_online;
+       int     prev_ac_online;
+       int     irq;
+       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;
+
+       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,
+       uint8_t enable)
+{
+       int ret;
+       uint8_t ctrl0 = 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 reading in register 0x%x\n",
+                               __func__, TPS65090_REG_CG_CTRL0);
+               return ret;
+       }
+       return 0;
+}
+
+static int tps65090_config_charger(struct tps65090_charger *charger)
+{
+       int ret;
+
+       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;
+               }
+       }
+
+       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 = container_of(psy,
+                                       struct tps65090_charger, ac);
+
+       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, 1);
+               if (ret < 0)
+                       return IRQ_HANDLED;
+               charger->ac_online = 1;
+       } else {
+               charger->ac_online = 0;
+       }
+
+       if (charger->prev_ac_online != charger->ac_online)
+               power_supply_changed(&charger->ac);
+
+       return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_OF)
+
+#include <linux/of_device.h>
+
+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;
+
+}
+#else
+static struct tps65090_platform_data *
+               tps65090_parse_dt_charger_data(struct platform_device *pdev)
+{
+       return NULL;
+}
+#endif
+
+static int tps65090_charger_probe(struct platform_device *pdev)
+{
+       struct tps65090_charger *cdata;
+       struct tps65090_platform_data *pdata;
+       uint8_t status1 = 0;
+       int ret;
+       int irq;
+
+       pdata = dev_get_platdata(pdev->dev.parent);
+
+       if (!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;
+       }
+
+       dev_set_drvdata(&pdev->dev, cdata);
+
+       cdata->dev                      = &pdev->dev;
+       cdata->pdata                    = pdata;
+
+       cdata->ac.name                  = "tps65090-ac";
+       cdata->ac.type                  = POWER_SUPPLY_TYPE_MAINS;
+       cdata->ac.get_property          = tps65090_ac_get_property;
+       cdata->ac.properties            = tps65090_ac_props;
+       cdata->ac.num_properties        = ARRAY_SIZE(tps65090_ac_props);
+       cdata->ac.supplied_to           = pdata->supplied_to;
+       cdata->ac.num_supplicants       = pdata->num_supplicants;
+
+       ret = power_supply_register(&pdev->dev, &cdata->ac);
+       if (ret) {
+               dev_err(&pdev->dev, "failed: power supply register\n");
+               return ret;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0) {
+               dev_warn(&pdev->dev, "Unable to get charger irq = %d\n", irq);
+               ret = irq;
+               goto fail_unregister_supply;
+       }
+
+       cdata->irq = irq;
+
+       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_free_irq;
+       }
+
+       ret = tps65090_config_charger(cdata);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
+               goto fail_free_irq;
+       }
+
+       /* 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_free_irq;
+       }
+
+       if (status1 != 0) {
+               ret = tps65090_enable_charging(cdata, 1);
+               if (ret < 0) {
+                       dev_err(cdata->dev, "error enabling charger\n");
+                       goto fail_free_irq;
+               }
+               cdata->ac_online = 1;
+               power_supply_changed(&cdata->ac);
+       }
+
+       return 0;
+
+fail_free_irq:
+       devm_free_irq(cdata->dev, irq, cdata);
+fail_unregister_supply:
+       power_supply_unregister(&cdata->ac);
+
+       return ret;
+}
+
+static int tps65090_charger_remove(struct platform_device *pdev)
+{
+       struct tps65090_charger *cdata = dev_get_drvdata(&pdev->dev);
+
+       devm_free_irq(cdata->dev, cdata->irq, cdata);
+       power_supply_unregister(&cdata->ac);
+
+       return 0;
+}
+
+static struct of_device_id of_tps65090_charger_match[] = {
+       { .compatible = "ti,tps65090-charger", },
+       { /* end */ }
+};
+
+static struct platform_driver tps65090_charger_driver = {
+       .driver = {
+               .name   = "tps65090-charger",
+               .of_match_table = of_tps65090_charger_match,
+               .owner  = THIS_MODULE,
+       },
+       .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");
index a69d0d11b54006b35d3ad3c04a9c53f3456d842f..bed458172dd28ff1073d33c783d71ad3de63991c 100644 (file)
@@ -636,17 +636,7 @@ static struct platform_driver twl4030_bci_driver = {
        .remove = __exit_p(twl4030_bci_remove),
 };
 
-static int __init twl4030_bci_init(void)
-{
-       return platform_driver_probe(&twl4030_bci_driver, twl4030_bci_probe);
-}
-module_init(twl4030_bci_init);
-
-static void __exit twl4030_bci_exit(void)
-{
-       platform_driver_unregister(&twl4030_bci_driver);
-}
-module_exit(twl4030_bci_exit);
+module_platform_driver_probe(twl4030_bci_driver, twl4030_bci_probe);
 
 MODULE_AUTHOR("Gražvydas Ignotas");
 MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
index d9cc169f1424d94624fb7fe24e41ef4be3657822..58cbb009b74f144564b069bf9344e801a5f02743 100644 (file)
@@ -169,7 +169,8 @@ static int wm831x_backup_probe(struct platform_device *pdev)
        struct power_supply *backup;
        int ret;
 
-       devdata = kzalloc(sizeof(struct wm831x_backup), GFP_KERNEL);
+       devdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_backup),
+                               GFP_KERNEL);
        if (devdata == NULL)
                return -ENOMEM;
 
@@ -197,14 +198,8 @@ static int wm831x_backup_probe(struct platform_device *pdev)
        backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
        backup->get_property = wm831x_backup_get_prop;
        ret = power_supply_register(&pdev->dev, backup);
-       if (ret)
-               goto err_kmalloc;
 
        return ret;
-
-err_kmalloc:
-       kfree(devdata);
-       return ret;
 }
 
 static int wm831x_backup_remove(struct platform_device *pdev)
@@ -213,7 +208,6 @@ static int wm831x_backup_remove(struct platform_device *pdev)
 
        power_supply_unregister(&devdata->backup);
        kfree(devdata->backup.name);
-       kfree(devdata);
 
        return 0;
 }
index 9ead60bc66b73be3de722213d91e3eb8d51cbae2..3301b2031c8d03b5900bf9364901a76763585bb7 100644 (file)
@@ -89,6 +89,11 @@ struct abx500_fg;
  *                             points.
  * @maint_thres                        This is the threshold where we stop reporting
  *                             battery full while in maintenance, in per cent
+ * @pcut_enable:                       Enable power cut feature in ab8505
+ * @pcut_max_time:             Max time threshold
+ * @pcut_flag_time:            Flagtime threshold
+ * @pcut_max_restart:          Max number of restarts
+ * @pcut_debounce_time:                Sets battery debounce time
  */
 struct abx500_fg_parameters {
        int recovery_sleep_timer;
@@ -106,6 +111,11 @@ struct abx500_fg_parameters {
        int battok_raising_th_sel1;
        int user_cap_limit;
        int maint_thres;
+       bool pcut_enable;
+       u8 pcut_max_time;
+       u8 pcut_flag_time;
+       u8 pcut_max_restart;
+       u8 pcut_debounce_time;
 };
 
 /**
@@ -173,11 +183,11 @@ struct abx500_battery_type {
        int low_high_vol_lvl;
        int battery_resistance;
        int n_temp_tbl_elements;
-       struct abx500_res_to_temp *r_to_t_tbl;
+       const struct abx500_res_to_temp *r_to_t_tbl;
        int n_v_cap_tbl_elements;
-       struct abx500_v_to_cap *v_to_cap_tbl;
+       const struct abx500_v_to_cap *v_to_cap_tbl;
        int n_batres_tbl_elements;
-       struct batres_vs_temp *batres_tbl;
+       const struct batres_vs_temp *batres_tbl;
 };
 
 /**
@@ -236,7 +246,11 @@ struct abx500_bm_charger_parameters {
  * @interval_not_charging charge alg cycle period time when not charging (sec)
  * @temp_hysteresis    temperature hysteresis
  * @gnd_lift_resistance        Battery ground to phone ground resistance (mOhm)
- * @maxi:              maximization parameters
+ * @n_chg_out_curr             number of elements in array chg_output_curr
+ * @n_chg_in_curr              number of elements in array chg_input_curr
+ * @chg_output_curr    charger output current level map
+ * @chg_input_curr             charger input current level map
+ * @maxi               maximization parameters
  * @cap_levels         capacity in percent for the different capacity levels
  * @bat_type           table of supported battery types
  * @chg_params         charger parameters
@@ -257,6 +271,7 @@ struct abx500_bm_data {
        bool autopower_cfg;
        bool ac_enabled;
        bool usb_enabled;
+       bool usb_power_path;
        bool no_maintenance;
        bool capacity_scaling;
        bool chg_unknown_bat;
@@ -270,6 +285,10 @@ struct abx500_bm_data {
        int interval_not_charging;
        int temp_hysteresis;
        int gnd_lift_resistance;
+       int n_chg_out_curr;
+       int n_chg_in_curr;
+       int *chg_output_curr;
+       int *chg_input_curr;
        const struct abx500_maxim_parameters *maxi;
        const struct abx500_bm_capacity_levels *cap_levels;
        struct abx500_battery_type *bat_type;
index 8d35bfe164c87f46f7c84e01ede1034322fa2870..cc892a8d8d6e14c76ceba26da741086ea9db3d43 100644 (file)
@@ -23,6 +23,7 @@
  * Bank : 0x5
  */
 #define AB8500_USB_LINE_STAT_REG       0x80
+#define AB8500_USB_LINE_CTRL2_REG      0x82
 #define AB8500_USB_LINK1_STAT_REG      0x94
 
 /*
@@ -33,7 +34,7 @@
 #define AB8500_CH_STATUS2_REG          0x01
 #define AB8500_CH_USBCH_STAT1_REG      0x02
 #define AB8500_CH_USBCH_STAT2_REG      0x03
-#define AB8500_CH_FSM_STAT_REG         0x04
+#define AB8540_CH_USBCH_STAT3_REG      0x04
 #define AB8500_CH_STAT_REG             0x05
 
 /*
@@ -69,6 +70,8 @@
 #define AB8500_USBCH_CTRL1_REG         0xC0
 #define AB8500_USBCH_CTRL2_REG         0xC1
 #define AB8500_USBCH_IPT_CRNTLVL_REG   0xC2
+#define AB8540_USB_PP_MODE_REG         0xC5
+#define AB8540_USB_PP_CHR_REG          0xC6
 
 /*
  * Gas Gauge register offsets
 #define AB8500_RTC_BACKUP_CHG_REG      0x0C
 #define AB8500_RTC_CC_CONF_REG         0x01
 #define AB8500_RTC_CTRL_REG            0x0B
+#define AB8500_RTC_CTRL1_REG           0x11
 
 /*
  * OTP register offsets
 #define CH_OP_CUR_LVL_1P4              0x0D
 #define CH_OP_CUR_LVL_1P5              0x0E
 #define CH_OP_CUR_LVL_1P6              0x0F
+#define CH_OP_CUR_LVL_2P               0x3F
 
 /* BTEMP High thermal limits */
 #define BTEMP_HIGH_TH_57_0             0x00
 #define BUP_ICH_SEL_300UA              0x08
 #define BUP_ICH_SEL_700UA              0x0C
 
-#define BUP_VCH_SEL_2P5V               0x00
-#define BUP_VCH_SEL_2P6V               0x01
-#define BUP_VCH_SEL_2P8V               0x02
-#define BUP_VCH_SEL_3P1V               0x03
+enum bup_vch_sel {
+       BUP_VCH_SEL_2P5V,
+       BUP_VCH_SEL_2P6V,
+       BUP_VCH_SEL_2P8V,
+       BUP_VCH_SEL_3P1V,
+       /*
+        * Note that the following 5 values 2.7v, 2.9v, 3.0v, 3.2v, 3.3v
+        * are only available on ab8540. You can't choose these 5
+        * voltage on ab8500/ab8505/ab9540.
+        */
+       BUP_VCH_SEL_2P7V,
+       BUP_VCH_SEL_2P9V,
+       BUP_VCH_SEL_3P0V,
+       BUP_VCH_SEL_3P2V,
+       BUP_VCH_SEL_3P3V,
+};
+
+#define BUP_VCH_RANGE          0x02
+#define VBUP33_VRTCN           0x01
 
 /* Battery OVV constants */
 #define BATT_OVV_ENA                   0x02
 #define BAT_CTRL_20U_ENA               0x02
 #define BAT_CTRL_18U_ENA               0x01
 #define BAT_CTRL_16U_ENA               0x02
+#define BAT_CTRL_60U_ENA               0x01
+#define BAT_CTRL_120U_ENA              0x02
 #define BAT_CTRL_CMP_ENA               0x04
 #define FORCE_BAT_CTRL_CMP_HIGH                0x08
 #define BAT_CTRL_PULL_UP_ENA           0x10
 /* Battery type */
 #define BATTERY_UNKNOWN                        00
 
+/* Registers for pcut feature in ab8505 and ab9540 */
+#define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12
+#define AB8505_RTC_PCUT_TIME_REG       0x13
+#define AB8505_RTC_PCUT_MAX_TIME_REG   0x14
+#define AB8505_RTC_PCUT_FLAG_TIME_REG  0x15
+#define AB8505_RTC_PCUT_RESTART_REG    0x16
+#define AB8505_RTC_PCUT_DEBOUNCE_REG   0x17
+
+/* USB Power Path constants for ab8540 */
+#define BUS_VSYS_VOL_SELECT_MASK               0x06
+#define BUS_VSYS_VOL_SELECT_3P6V               0x00
+#define BUS_VSYS_VOL_SELECT_3P325V             0x02
+#define BUS_VSYS_VOL_SELECT_3P9V               0x04
+#define BUS_VSYS_VOL_SELECT_4P3V               0x06
+#define BUS_POWER_PATH_MODE_ENA                        0x01
+#define BUS_PP_PRECHG_CURRENT_MASK             0x0E
+#define BUS_POWER_PATH_PRECHG_ENA              0x01
+
 /**
  * struct res_to_temp - defines one point in a temp to res curve. To
  * be used in battery packs that combines the identification resistor with a
@@ -283,6 +323,11 @@ struct ab8500_fg;
  *                             points.
  * @maint_thres                        This is the threshold where we stop reporting
  *                             battery full while in maintenance, in per cent
+ * @pcut_enable:                       Enable power cut feature in ab8505
+ * @pcut_max_time:             Max time threshold
+ * @pcut_flag_time:            Flagtime threshold
+ * @pcut_max_restart:          Max number of restarts
+ * @pcut_debunce_time: Sets battery debounce time
  */
 struct ab8500_fg_parameters {
        int recovery_sleep_timer;
@@ -299,6 +344,11 @@ struct ab8500_fg_parameters {
        int battok_raising_th_sel1;
        int user_cap_limit;
        int maint_thres;
+       bool pcut_enable;
+       u8 pcut_max_time;
+       u8 pcut_flag_time;
+       u8 pcut_max_restart;
+       u8 pcut_debunce_time;
 };
 
 /**
@@ -415,6 +465,7 @@ void ab8500_fg_reinit(void);
 void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
 struct ab8500_btemp *ab8500_btemp_get(void);
 int ab8500_btemp_get_batctrl_temp(struct ab8500_btemp *btemp);
+int ab8500_btemp_get_temp(struct ab8500_btemp *btemp);
 struct ab8500_fg *ab8500_fg_get(void);
 int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev);
 int ab8500_fg_inst_curr_start(struct ab8500_fg *di);
index 252966769d939fae571aa6dc260b86e1280bfc2f..49ded001049bed2a96f59e4a1fe12cc313a8e894 100644 (file)
@@ -4,32 +4,72 @@
  *
  * Author: Arun R Murthy <arun.murthy@stericsson.com>
  * Author: Daniel Willerud <daniel.willerud@stericsson.com>
+ * Author: M'boumba Cedric Madianga <cedric.madianga@stericsson.com>
  */
 
 #ifndef        _AB8500_GPADC_H
 #define _AB8500_GPADC_H
 
-/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2) */
-#define BAT_CTRL       0x01
-#define BTEMP_BALL     0x02
-#define MAIN_CHARGER_V 0x03
-#define ACC_DETECT1    0x04
-#define ACC_DETECT2    0x05
-#define ADC_AUX1       0x06
-#define ADC_AUX2       0x07
-#define MAIN_BAT_V     0x08
-#define VBUS_V         0x09
-#define MAIN_CHARGER_C 0x0A
-#define USB_CHARGER_C  0x0B
-#define BK_BAT_V       0x0C
-#define DIE_TEMP       0x0D
+/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2
+ * and ADCHwSel[4:0] in GPADCCtrl3 ) */
+#define BAT_CTRL               0x01
+#define BTEMP_BALL             0x02
+#define MAIN_CHARGER_V         0x03
+#define ACC_DETECT1            0x04
+#define ACC_DETECT2            0x05
+#define ADC_AUX1               0x06
+#define ADC_AUX2               0x07
+#define MAIN_BAT_V             0x08
+#define VBUS_V                 0x09
+#define MAIN_CHARGER_C         0x0A
+#define USB_CHARGER_C          0x0B
+#define BK_BAT_V               0x0C
+#define DIE_TEMP               0x0D
+#define USB_ID                 0x0E
+#define XTAL_TEMP              0x12
+#define VBAT_TRUE_MEAS         0x13
+#define BAT_CTRL_AND_IBAT      0x1C
+#define VBAT_MEAS_AND_IBAT     0x1D
+#define VBAT_TRUE_MEAS_AND_IBAT        0x1E
+#define BAT_TEMP_AND_IBAT      0x1F
+
+/* Virtual channel used only for ibat convertion to ampere
+ * Battery current conversion (ibat) cannot be requested as a single conversion
+ *  but it is always in combination with other input requests
+ */
+#define IBAT_VIRTUAL_CHANNEL           0xFF
+
+#define SAMPLE_1        1
+#define SAMPLE_4        4
+#define SAMPLE_8        8
+#define SAMPLE_16       16
+#define RISING_EDGE     0
+#define FALLING_EDGE    1
+
+/* Arbitrary ADC conversion type constants */
+#define ADC_SW                         0
+#define ADC_HW                         1
 
 struct ab8500_gpadc;
 
 struct ab8500_gpadc *ab8500_gpadc_get(char *name);
-int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel);
-int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel);
+int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
+               u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type);
+static inline int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
+{
+       return ab8500_gpadc_sw_hw_convert(gpadc, channel,
+                       SAMPLE_16, 0, 0, ADC_SW);
+}
+
+int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
+               u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type);
+int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
+               u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type,
+               int *ibat);
 int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc,
-    u8 channel, int ad_value);
+               u8 channel, int ad_value);
+void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
+                       u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h,
+                       u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h);
 
 #endif /* _AB8500_GPADC_H */
index ebf12e793db982fd06e96682c9e1ad1b7f4a6c42..990bc93f46e103f47d42f48108fcf523f19160ff 100644 (file)
@@ -12,6 +12,7 @@
 
 int ab8500_sysctrl_read(u16 reg, u8 *value);
 int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value);
+void ab8500_restart(char mode, const char *cmd);
 
 #else
 
@@ -40,6 +41,7 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits)
 /* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */
 struct ab8500_sysctrl_platform_data {
        u8 initial_req_buf_config[8];
+       u16 (*reboot_reason_code)(const char *cmd);
 };
 
 /* Registers */
@@ -299,4 +301,8 @@ struct ab8500_sysctrl_platform_data {
 #define AB9540_SYSCLK12BUF4VALID_SYSCLK12BUF4VALID_MASK 0xFF
 #define AB9540_SYSCLK12BUF4VALID_SYSCLK12BUF4VALID_SHIFT 0
 
+#define AB8500_ENABLE_WD 0x1
+#define AB8500_KICK_WD 0x2
+#define AB8500_WD_RESTART_ON_EXPIRE 0x10
+
 #endif /* __AB8500_SYSCTRL_H */
index 84f449475c255330ae73bb14482c308873afe802..fb1bf7d6a41018edf8358c76ffce8b74e2282cba 100644 (file)
@@ -362,6 +362,7 @@ struct ab8500 {
        u8 *oldmask;
        int mask_size;
        const int *irq_reg_offset;
+       int it_latchhier_num;
 };
 
 struct ab8500_regulator_platform_data;
@@ -505,6 +506,8 @@ static inline int is_ab9540_2p0_or_earlier(struct ab8500 *ab)
        return (is_ab9540(ab) && (ab->chip_id < AB8500_CUT2P0));
 }
 
+void ab8500_override_turn_on_stat(u8 mask, u8 set);
+
 #ifdef CONFIG_AB8500_DEBUG
 void ab8500_dump_all_banks(struct device *dev);
 void ab8500_debug_register_interrupt(int line);
index d43ac0f355263e7ac188d09616fef59476feec03..234c99143bf778019c0ef28330c9967530bb7d43 100644 (file)
@@ -17,8 +17,11 @@ struct ux500_charger;
 
 struct ux500_charger_ops {
        int (*enable) (struct ux500_charger *, int, int, int);
+       int (*check_enable) (struct ux500_charger *, int, int);
        int (*kick_wd) (struct ux500_charger *);
        int (*update_curr) (struct ux500_charger *, int);
+       int (*pp_enable) (struct ux500_charger *, bool);
+       int (*pre_chg_enable) (struct ux500_charger *, bool);
 };
 
 /**
@@ -29,6 +32,7 @@ struct ux500_charger_ops {
  * @max_out_curr       maximum output charger current in mA
  * @enabled            indicates if this charger is used or not
  * @external           external charger unit (pm2xxx)
+ * @power_path         USB power path support
  */
 struct ux500_charger {
        struct power_supply psy;
@@ -38,6 +42,9 @@ struct ux500_charger {
        int wdt_refresh;
        bool enabled;
        bool external;
+       bool power_path;
 };
 
+extern struct blocking_notifier_head charger_notifier_list;
+
 #endif
index 6694cf43e8b8998d1dce53712472c0eeba5b24b0..998628a2b08b2acbc02541a4b29da58ad654d1f1 100644 (file)
@@ -86,6 +86,11 @@ struct tps65090_regulator_plat_data {
 
 struct tps65090_platform_data {
        int irq_base;
+
+       char **supplied_to;
+       size_t num_supplicants;
+       int enable_low_current_chrg;
+
        struct tps65090_regulator_plat_data *reg_pdata[TPS65090_REGULATOR_MAX];
 };
 
index fc3f026922ae0eedae497c42ea35123a29561265..85c16defe11ac21d1b4d9c8687fbe99f9ff161af 100644 (file)
@@ -48,7 +48,7 @@ struct pm2xxx_charger_platform_data {
        size_t num_supplicants;
        int i2c_bus;
        const char *label;
-       int irq_number;
+       int gpio_irq_number;
        unsigned int lpn_gpio;
        int irq_type;
 };
diff --git a/include/linux/power/ab8500.h b/include/linux/power/ab8500.h
new file mode 100644 (file)
index 0000000..cdbb6c2
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) ST-Ericsson 2013
+ * Author: Hongbo Zhang <hongbo.zhang@linaro.com>
+ * License terms: GNU General Public License v2
+ */
+
+#ifndef PWR_AB8500_H
+#define PWR_AB8500_H
+
+extern const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[];
+extern const int ab8500_temp_tbl_a_size;
+
+extern const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[];
+extern const int ab8500_temp_tbl_b_size;
+
+#endif /* PWR_AB8500_H */
index 002a99f96331dd2f7e939ae24713db9c351c0fff..3828cefb4f65876e635e5533d24233d32ab6590b 100644 (file)
@@ -171,6 +171,12 @@ struct power_supply {
        char **supplied_to;
        size_t num_supplicants;
 
+       char **supplied_from;
+       size_t num_supplies;
+#ifdef CONFIG_OF
+       struct device_node *of_node;
+#endif
+
        int (*get_property)(struct power_supply *psy,
                            enum power_supply_property psp,
                            union power_supply_propval *val);
index 75818744ab5998bdb3f88de62ff87e319548272d..ea7168a680810dba82505afd827ff4909bf5130e 100644 (file)
@@ -115,9 +115,6 @@ unsigned __vexpress_get_site(struct device *dev, struct device_node *node);
 void vexpress_sysreg_early_init(void __iomem *base);
 void vexpress_sysreg_of_early_init(void);
 
-void vexpress_power_off(void);
-void vexpress_restart(char str, const char *cmd);
-
 /* Clocks */
 
 struct clk *vexpress_osc_setup(struct device *dev);