Browse Source

Merge tag 'for-v3.9' of git://git.infradead.org/battery-2.6

Pull battery updates from Anton Vorontsov:
 "Four new drivers:

   - goldfish_battery:

     This is Android Emulator battery driver.  Originally from Google,
     but Intel folks reshaped it for mainline

   - pm2301_charger:

     A new driver for ST-Ericsson 2301 Power Management chip, uses
     AB8500 battery management core

   - qnap-poweroff:

     The driver adds poweroff functionality for QNAP NAS boxes

   - restart-poweroff:

     A generic driver that implements 'power off by restarting'.  The
     actual poweroff functionality is implemented through a bootloader,
     so Linux' task is just to restart the box.  The driver is useful on
     Buffalo Linkstation LS-XHL and LS-CHLv2 boards.  Andrew Lunn worked
     on submitting the driver (as well as qnap-poweroff above).

  Additionally:

   - A lot of fixes for ab8500 drivers.  This is a part of efforts of
     syncing internal ST-Ericsson development tree with the mainline.
     Lee Jones @ Linaro worked on compilation and reshaping these
     series.

   - New health properties for the power supplies: "Watchdog timer
     expire" and "Safety timer expire"

   - As usual, a bunch of fixes/cleanups here and there"

* tag 'for-v3.9' of git://git.infradead.org/battery-2.6: (81 commits)
  bq2415x_charger: Add support for offline and 100mA mode
  generic-adc-battery: Fix forever loop in gab_remove()
  goldfish_battery: Add missing GENERIC_HARDIRQS dependency
  da9030_battery: Include notifier.h
  bq27x00_battery: Fix reporting battery temperature
  power/reset: Remove newly introduced __dev* annotations
  lp8727_charger: Small cleanup in naming
  ab8500_btemp: Demote initcall sequence
  ds2782_battery: Add power_supply_changed() calls for proper uevent support
  power: Add battery driver for goldfish emulator
  u8500-charger: Delay for USB enumeration
  ab8500-bm: Remove individual [charger|btemp|fg|chargalg] pdata structures
  ab8500-charger: Do not touch VBUSOVV bits
  ab8500-fg: Use correct battery charge full design
  pm2301: LPN mode control support
  pm2301: Enable vbat low monitoring
  ab8500-bm: Flush all work queues before suspending
  ab8500-fg: Go to INIT_RECOVERY when charger removed
  ab8500-charger: Add support for autopower on AB8505 and AB9540
  abx500-chargalg: Add new sysfs interface to get current charge status
  ...

Fix up fairly straightforward conflicts in the ab8500 driver.  But since
it seems to be ARM-specific, I can't even compile-test the result..
Linus Torvalds 12 years ago
parent
commit
5a1203914a
36 changed files with 3845 additions and 908 deletions
  1. 13 0
      Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt
  2. 8 0
      Documentation/devicetree/bindings/power_supply/restart-poweroff.txt
  3. 16 0
      MAINTAINERS
  4. 6 8
      drivers/mfd/ab8500-core.c
  5. 4 9
      drivers/power/88pm860x_battery.c
  6. 14 0
      drivers/power/Kconfig
  7. 3 1
      drivers/power/Makefile
  8. 256 268
      drivers/power/ab8500_bmdata.c
  9. 117 75
      drivers/power/ab8500_btemp.c
  10. 534 115
      drivers/power/ab8500_charger.c
  11. 318 128
      drivers/power/ab8500_fg.c
  12. 123 81
      drivers/power/abx500_chargalg.c
  13. 25 29
      drivers/power/bq2415x_charger.c
  14. 5 7
      drivers/power/bq27x00_battery.c
  15. 184 126
      drivers/power/charger-manager.c
  16. 1 0
      drivers/power/da9030_battery.c
  17. 1 1
      drivers/power/da9052-battery.c
  18. 69 0
      drivers/power/ds2782_battery.c
  19. 9 7
      drivers/power/generic-adc-battery.c
  20. 236 0
      drivers/power/goldfish_battery.c
  21. 4 4
      drivers/power/lp8727_charger.c
  22. 9 8
      drivers/power/lp8788-charger.c
  23. 1 3
      drivers/power/max17040_battery.c
  24. 1088 0
      drivers/power/pm2301_charger.c
  25. 513 0
      drivers/power/pm2301_charger.h
  26. 2 1
      drivers/power/power_supply_sysfs.c
  27. 17 0
      drivers/power/reset/Kconfig
  28. 2 0
      drivers/power/reset/Makefile
  29. 116 0
      drivers/power/reset/qnap-poweroff.c
  30. 65 0
      drivers/power/reset/restart-poweroff.c
  31. 10 5
      include/linux/mfd/abx500.h
  32. 6 31
      include/linux/mfd/abx500/ab8500-bm.h
  33. 5 0
      include/linux/mfd/abx500/ux500_chargalg.h
  34. 61 0
      include/linux/pm2301_charger.h
  35. 2 1
      include/linux/power/bq2415x_charger.h
  36. 2 0
      include/linux/power_supply.h

+ 13 - 0
Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt

@@ -0,0 +1,13 @@
+* QNAP Power Off
+
+QNAP NAS devices have a microcontroller controlling the main power
+supply. This microcontroller is connected to UART1 of the Kirkwood and
+Orion5x SoCs. Sending the charactor 'A', at 19200 baud, tells the
+microcontroller to turn the power off. This driver adds a handler to
+pm_power_off which is called to turn the power off.
+
+Required Properties:
+- compatible: Should be "qnap,power-off"
+
+- reg: Address and length of the register set for UART1
+- clocks: tclk clock

+ 8 - 0
Documentation/devicetree/bindings/power_supply/restart-poweroff.txt

@@ -0,0 +1,8 @@
+* Restart Power Off
+
+Buffalo Linkstation LS-XHL and LS-CHLv2, and other devices power off
+by restarting and letting u-boot keep hold of the machine until the
+user presses a button.
+
+Required Properties:
+- compatible: Should be "restart-poweroff"

+ 16 - 0
MAINTAINERS

@@ -7612,6 +7612,22 @@ F:	Documentation/backlight/lp855x-driver.txt
 F:	drivers/video/backlight/lp855x_bl.c
 F:	include/linux/platform_data/lp855x.h
 
+TI LP8727 CHARGER DRIVER
+M:	Milo Kim <milo.kim@ti.com>
+S:	Maintained
+F:	drivers/power/lp8727_charger.c
+F:	include/linux/platform_data/lp8727.h
+
+TI LP8788 MFD DRIVER
+M:	Milo Kim <milo.kim@ti.com>
+S:	Maintained
+F:	drivers/iio/adc/lp8788_adc.c
+F:	drivers/leds/leds-lp8788.c
+F:	drivers/mfd/lp8788*.c
+F:	drivers/power/lp8788-charger.c
+F:	drivers/regulator/lp8788-*.c
+F:	include/linux/mfd/lp8788*.h
+
 TI TWL4030 SERIES SOC CODEC DRIVER
 M:	Peter Ujfalusi <peter.ujfalusi@ti.com>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)

+ 6 - 8
drivers/mfd/ab8500-core.c

@@ -750,6 +750,12 @@ static struct resource ab8500_charger_resources[] = {
 		.end = AB8500_INT_CH_WD_EXP,
 		.flags = IORESOURCE_IRQ,
 	},
+	{
+		.name = "VBUS_CH_DROP_END",
+		.start = AB8500_INT_VBUS_CH_DROP_END,
+		.end = AB8500_INT_VBUS_CH_DROP_END,
+		.flags = IORESOURCE_IRQ,
+	},
 };
 
 static struct resource ab8500_btemp_resources[] = {
@@ -1012,40 +1018,32 @@ static struct mfd_cell ab8500_bm_devs[] = {
 		.of_compatible = "stericsson,ab8500-charger",
 		.num_resources = ARRAY_SIZE(ab8500_charger_resources),
 		.resources = ab8500_charger_resources,
-#ifndef CONFIG_OF
 		.platform_data = &ab8500_bm_data,
 		.pdata_size = sizeof(ab8500_bm_data),
-#endif
 	},
 	{
 		.name = "ab8500-btemp",
 		.of_compatible = "stericsson,ab8500-btemp",
 		.num_resources = ARRAY_SIZE(ab8500_btemp_resources),
 		.resources = ab8500_btemp_resources,
-#ifndef CONFIG_OF
 		.platform_data = &ab8500_bm_data,
 		.pdata_size = sizeof(ab8500_bm_data),
-#endif
 	},
 	{
 		.name = "ab8500-fg",
 		.of_compatible = "stericsson,ab8500-fg",
 		.num_resources = ARRAY_SIZE(ab8500_fg_resources),
 		.resources = ab8500_fg_resources,
-#ifndef CONFIG_OF
 		.platform_data = &ab8500_bm_data,
 		.pdata_size = sizeof(ab8500_bm_data),
-#endif
 	},
 	{
 		.name = "ab8500-chargalg",
 		.of_compatible = "stericsson,ab8500-chargalg",
 		.num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
 		.resources = ab8500_chargalg_resources,
-#ifndef CONFIG_OF
 		.platform_data = &ab8500_bm_data,
 		.pdata_size = sizeof(ab8500_bm_data),
-#endif
 	},
 };
 

+ 4 - 9
drivers/power/88pm860x_battery.c

@@ -915,15 +915,13 @@ static int pm860x_battery_probe(struct platform_device *pdev)
 	info->irq_cc = platform_get_irq(pdev, 0);
 	if (info->irq_cc <= 0) {
 		dev_err(&pdev->dev, "No IRQ resource!\n");
-		ret = -EINVAL;
-		goto out;
+		return -EINVAL;
 	}
 
 	info->irq_batt = platform_get_irq(pdev, 1);
 	if (info->irq_batt <= 0) {
 		dev_err(&pdev->dev, "No IRQ resource!\n");
-		ret = -EINVAL;
-		goto out;
+		return -EINVAL;
 	}
 
 	info->chip = chip;
@@ -957,7 +955,7 @@ static int pm860x_battery_probe(struct platform_device *pdev)
 
 	ret = power_supply_register(&pdev->dev, &info->battery);
 	if (ret)
-		goto out;
+		return ret;
 	info->battery.dev->parent = &pdev->dev;
 
 	ret = request_threaded_irq(info->irq_cc, NULL,
@@ -984,8 +982,6 @@ out_coulomb:
 	free_irq(info->irq_cc, info);
 out_reg:
 	power_supply_unregister(&info->battery);
-out:
-	kfree(info);
 	return ret;
 }
 
@@ -993,10 +989,9 @@ static int pm860x_battery_remove(struct platform_device *pdev)
 {
 	struct pm860x_battery_info *info = platform_get_drvdata(pdev);
 
-	power_supply_unregister(&info->battery);
 	free_irq(info->irq_batt, info);
 	free_irq(info->irq_cc, info);
-	kfree(info);
+	power_supply_unregister(&info->battery);
 	platform_set_drvdata(pdev, NULL);
 	return 0;
 }

+ 14 - 0
drivers/power/Kconfig

@@ -346,6 +346,20 @@ config AB8500_BM
 	help
 	  Say Y to include support for AB8500 battery management.
 
+config BATTERY_GOLDFISH
+	tristate "Goldfish battery driver"
+	depends on GENERIC_HARDIRQS
+	help
+	  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

+ 3 - 1
drivers/power/Makefile

@@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760)	+= ds2760_battery.o
 obj-$(CONFIG_BATTERY_DS2780)	+= ds2780_battery.o
 obj-$(CONFIG_BATTERY_DS2781)	+= ds2781_battery.o
 obj-$(CONFIG_BATTERY_DS2782)	+= ds2782_battery.o
+obj-$(CONFIG_BATTERY_GOLDFISH)	+= goldfish_battery.o
 obj-$(CONFIG_BATTERY_PMU)	+= pmu_battery.o
 obj-$(CONFIG_BATTERY_OLPC)	+= olpc_battery.o
 obj-$(CONFIG_BATTERY_TOSA)	+= tosa_battery.o
@@ -38,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_btemp.o ab8500_fg.o abx500_chargalg.o
+obj-$(CONFIG_AB8500_BM)		+= ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
 obj-$(CONFIG_CHARGER_ISP1704)	+= isp1704_charger.o
 obj-$(CONFIG_CHARGER_MAX8903)	+= max8903_charger.o
 obj-$(CONFIG_CHARGER_TWL4030)	+= twl4030_charger.o
@@ -46,6 +47,7 @@ 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

+ 256 - 268
drivers/power/ab8500_bmdata.c

@@ -182,206 +182,206 @@ static struct batres_vs_temp temp_to_batres_tbl_9100[] = {
 };
 
 static struct abx500_battery_type bat_type_thermistor[] = {
-[BATTERY_UNKNOWN] = {
-	/* First element always represent the UNKNOWN battery */
-	.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
-	.resis_high = 0,
-	.resis_low = 0,
-	.battery_resistance = 300,
-	.charge_full_design = 612,
-	.nominal_voltage = 3700,
-	.termination_vol = 4050,
-	.termination_curr = 200,
-	.recharge_vol = 3990,
-	.normal_cur_lvl = 400,
-	.normal_vol_lvl = 4100,
-	.maint_a_cur_lvl = 400,
-	.maint_a_vol_lvl = 4050,
-	.maint_a_chg_timer_h = 60,
-	.maint_b_cur_lvl = 400,
-	.maint_b_vol_lvl = 4000,
-	.maint_b_chg_timer_h = 200,
-	.low_high_cur_lvl = 300,
-	.low_high_vol_lvl = 4000,
-	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-	.r_to_t_tbl = temp_tbl,
-	.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-	.v_to_cap_tbl = cap_tbl,
-	.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-	.batres_tbl = temp_to_batres_tbl_thermistor,
-},
-{
-	.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
-	.resis_high = 53407,
-	.resis_low = 12500,
-	.battery_resistance = 300,
-	.charge_full_design = 900,
-	.nominal_voltage = 3600,
-	.termination_vol = 4150,
-	.termination_curr = 80,
-	.recharge_vol = 4130,
-	.normal_cur_lvl = 700,
-	.normal_vol_lvl = 4200,
-	.maint_a_cur_lvl = 600,
-	.maint_a_vol_lvl = 4150,
-	.maint_a_chg_timer_h = 60,
-	.maint_b_cur_lvl = 600,
-	.maint_b_vol_lvl = 4100,
-	.maint_b_chg_timer_h = 200,
-	.low_high_cur_lvl = 300,
-	.low_high_vol_lvl = 4000,
-	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_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_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-	.batres_tbl = temp_to_batres_tbl_thermistor,
+	[BATTERY_UNKNOWN] = {
+		/* First element always represent the UNKNOWN battery */
+		.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
+		.resis_high = 0,
+		.resis_low = 0,
+		.battery_resistance = 300,
+		.charge_full_design = 612,
+		.nominal_voltage = 3700,
+		.termination_vol = 4050,
+		.termination_curr = 200,
+		.recharge_cap = 95,
+		.normal_cur_lvl = 400,
+		.normal_vol_lvl = 4100,
+		.maint_a_cur_lvl = 400,
+		.maint_a_vol_lvl = 4050,
+		.maint_a_chg_timer_h = 60,
+		.maint_b_cur_lvl = 400,
+		.maint_b_vol_lvl = 4000,
+		.maint_b_chg_timer_h = 200,
+		.low_high_cur_lvl = 300,
+		.low_high_vol_lvl = 4000,
+		.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+		.r_to_t_tbl = temp_tbl,
+		.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+		.v_to_cap_tbl = cap_tbl,
+		.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+		.batres_tbl = temp_to_batres_tbl_thermistor,
+	},
+	{
+		.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+		.resis_high = 53407,
+		.resis_low = 12500,
+		.battery_resistance = 300,
+		.charge_full_design = 900,
+		.nominal_voltage = 3600,
+		.termination_vol = 4150,
+		.termination_curr = 80,
+		.recharge_cap = 95,
+		.normal_cur_lvl = 700,
+		.normal_vol_lvl = 4200,
+		.maint_a_cur_lvl = 600,
+		.maint_a_vol_lvl = 4150,
+		.maint_a_chg_timer_h = 60,
+		.maint_b_cur_lvl = 600,
+		.maint_b_vol_lvl = 4100,
+		.maint_b_chg_timer_h = 200,
+		.low_high_cur_lvl = 300,
+		.low_high_vol_lvl = 4000,
+		.n_temp_tbl_elements = ARRAY_SIZE(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_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+		.batres_tbl = temp_to_batres_tbl_thermistor,
 
-},
-{
-	.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
-	.resis_high = 200000,
-	.resis_low = 82869,
-	.battery_resistance = 300,
-	.charge_full_design = 900,
-	.nominal_voltage = 3600,
-	.termination_vol = 4150,
-	.termination_curr = 80,
-	.recharge_vol = 4130,
-	.normal_cur_lvl = 700,
-	.normal_vol_lvl = 4200,
-	.maint_a_cur_lvl = 600,
-	.maint_a_vol_lvl = 4150,
-	.maint_a_chg_timer_h = 60,
-	.maint_b_cur_lvl = 600,
-	.maint_b_vol_lvl = 4100,
-	.maint_b_chg_timer_h = 200,
-	.low_high_cur_lvl = 300,
-	.low_high_vol_lvl = 4000,
-	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_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_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-	.batres_tbl = temp_to_batres_tbl_thermistor,
-},
+	},
+	{
+		.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+		.resis_high = 200000,
+		.resis_low = 82869,
+		.battery_resistance = 300,
+		.charge_full_design = 900,
+		.nominal_voltage = 3600,
+		.termination_vol = 4150,
+		.termination_curr = 80,
+		.recharge_cap = 95,
+		.normal_cur_lvl = 700,
+		.normal_vol_lvl = 4200,
+		.maint_a_cur_lvl = 600,
+		.maint_a_vol_lvl = 4150,
+		.maint_a_chg_timer_h = 60,
+		.maint_b_cur_lvl = 600,
+		.maint_b_vol_lvl = 4100,
+		.maint_b_chg_timer_h = 200,
+		.low_high_cur_lvl = 300,
+		.low_high_vol_lvl = 4000,
+		.n_temp_tbl_elements = ARRAY_SIZE(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_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+		.batres_tbl = temp_to_batres_tbl_thermistor,
+	},
 };
 
 static struct abx500_battery_type bat_type_ext_thermistor[] = {
-[BATTERY_UNKNOWN] = {
-	/* First element always represent the UNKNOWN battery */
-	.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
-	.resis_high = 0,
-	.resis_low = 0,
-	.battery_resistance = 300,
-	.charge_full_design = 612,
-	.nominal_voltage = 3700,
-	.termination_vol = 4050,
-	.termination_curr = 200,
-	.recharge_vol = 3990,
-	.normal_cur_lvl = 400,
-	.normal_vol_lvl = 4100,
-	.maint_a_cur_lvl = 400,
-	.maint_a_vol_lvl = 4050,
-	.maint_a_chg_timer_h = 60,
-	.maint_b_cur_lvl = 400,
-	.maint_b_vol_lvl = 4000,
-	.maint_b_chg_timer_h = 200,
-	.low_high_cur_lvl = 300,
-	.low_high_vol_lvl = 4000,
-	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-	.r_to_t_tbl = temp_tbl,
-	.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-	.v_to_cap_tbl = cap_tbl,
-	.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-	.batres_tbl = temp_to_batres_tbl_thermistor,
-},
+	[BATTERY_UNKNOWN] = {
+		/* First element always represent the UNKNOWN battery */
+		.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
+		.resis_high = 0,
+		.resis_low = 0,
+		.battery_resistance = 300,
+		.charge_full_design = 612,
+		.nominal_voltage = 3700,
+		.termination_vol = 4050,
+		.termination_curr = 200,
+		.recharge_cap = 95,
+		.normal_cur_lvl = 400,
+		.normal_vol_lvl = 4100,
+		.maint_a_cur_lvl = 400,
+		.maint_a_vol_lvl = 4050,
+		.maint_a_chg_timer_h = 60,
+		.maint_b_cur_lvl = 400,
+		.maint_b_vol_lvl = 4000,
+		.maint_b_chg_timer_h = 200,
+		.low_high_cur_lvl = 300,
+		.low_high_vol_lvl = 4000,
+		.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+		.r_to_t_tbl = temp_tbl,
+		.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+		.v_to_cap_tbl = cap_tbl,
+		.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+		.batres_tbl = temp_to_batres_tbl_thermistor,
+	},
 /*
  * These are the batteries that doesn't have an internal NTC resistor to measure
  * its temperature. The temperature in this case is measure with a NTC placed
  * near the battery but on the PCB.
  */
-{
-	.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
-	.resis_high = 76000,
-	.resis_low = 53000,
-	.battery_resistance = 300,
-	.charge_full_design = 900,
-	.nominal_voltage = 3700,
-	.termination_vol = 4150,
-	.termination_curr = 100,
-	.recharge_vol = 4130,
-	.normal_cur_lvl = 700,
-	.normal_vol_lvl = 4200,
-	.maint_a_cur_lvl = 600,
-	.maint_a_vol_lvl = 4150,
-	.maint_a_chg_timer_h = 60,
-	.maint_b_cur_lvl = 600,
-	.maint_b_vol_lvl = 4100,
-	.maint_b_chg_timer_h = 200,
-	.low_high_cur_lvl = 300,
-	.low_high_vol_lvl = 4000,
-	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-	.r_to_t_tbl = temp_tbl,
-	.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-	.v_to_cap_tbl = cap_tbl,
-	.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-	.batres_tbl = temp_to_batres_tbl_thermistor,
-},
-{
-	.name = POWER_SUPPLY_TECHNOLOGY_LION,
-	.resis_high = 30000,
-	.resis_low = 10000,
-	.battery_resistance = 300,
-	.charge_full_design = 950,
-	.nominal_voltage = 3700,
-	.termination_vol = 4150,
-	.termination_curr = 100,
-	.recharge_vol = 4130,
-	.normal_cur_lvl = 700,
-	.normal_vol_lvl = 4200,
-	.maint_a_cur_lvl = 600,
-	.maint_a_vol_lvl = 4150,
-	.maint_a_chg_timer_h = 60,
-	.maint_b_cur_lvl = 600,
-	.maint_b_vol_lvl = 4100,
-	.maint_b_chg_timer_h = 200,
-	.low_high_cur_lvl = 300,
-	.low_high_vol_lvl = 4000,
-	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-	.r_to_t_tbl = temp_tbl,
-	.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-	.v_to_cap_tbl = cap_tbl,
-	.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-	.batres_tbl = temp_to_batres_tbl_thermistor,
-},
-{
-	.name = POWER_SUPPLY_TECHNOLOGY_LION,
-	.resis_high = 95000,
-	.resis_low = 76001,
-	.battery_resistance = 300,
-	.charge_full_design = 950,
-	.nominal_voltage = 3700,
-	.termination_vol = 4150,
-	.termination_curr = 100,
-	.recharge_vol = 4130,
-	.normal_cur_lvl = 700,
-	.normal_vol_lvl = 4200,
-	.maint_a_cur_lvl = 600,
-	.maint_a_vol_lvl = 4150,
-	.maint_a_chg_timer_h = 60,
-	.maint_b_cur_lvl = 600,
-	.maint_b_vol_lvl = 4100,
-	.maint_b_chg_timer_h = 200,
-	.low_high_cur_lvl = 300,
-	.low_high_vol_lvl = 4000,
-	.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
-	.r_to_t_tbl = temp_tbl,
-	.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
-	.v_to_cap_tbl = cap_tbl,
-	.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
-	.batres_tbl = temp_to_batres_tbl_thermistor,
-},
+	{
+		.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+		.resis_high = 76000,
+		.resis_low = 53000,
+		.battery_resistance = 300,
+		.charge_full_design = 900,
+		.nominal_voltage = 3700,
+		.termination_vol = 4150,
+		.termination_curr = 100,
+		.recharge_cap = 95,
+		.normal_cur_lvl = 700,
+		.normal_vol_lvl = 4200,
+		.maint_a_cur_lvl = 600,
+		.maint_a_vol_lvl = 4150,
+		.maint_a_chg_timer_h = 60,
+		.maint_b_cur_lvl = 600,
+		.maint_b_vol_lvl = 4100,
+		.maint_b_chg_timer_h = 200,
+		.low_high_cur_lvl = 300,
+		.low_high_vol_lvl = 4000,
+		.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+		.r_to_t_tbl = temp_tbl,
+		.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+		.v_to_cap_tbl = cap_tbl,
+		.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+		.batres_tbl = temp_to_batres_tbl_thermistor,
+	},
+	{
+		.name = POWER_SUPPLY_TECHNOLOGY_LION,
+		.resis_high = 30000,
+		.resis_low = 10000,
+		.battery_resistance = 300,
+		.charge_full_design = 950,
+		.nominal_voltage = 3700,
+		.termination_vol = 4150,
+		.termination_curr = 100,
+		.recharge_cap = 95,
+		.normal_cur_lvl = 700,
+		.normal_vol_lvl = 4200,
+		.maint_a_cur_lvl = 600,
+		.maint_a_vol_lvl = 4150,
+		.maint_a_chg_timer_h = 60,
+		.maint_b_cur_lvl = 600,
+		.maint_b_vol_lvl = 4100,
+		.maint_b_chg_timer_h = 200,
+		.low_high_cur_lvl = 300,
+		.low_high_vol_lvl = 4000,
+		.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+		.r_to_t_tbl = temp_tbl,
+		.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+		.v_to_cap_tbl = cap_tbl,
+		.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+		.batres_tbl = temp_to_batres_tbl_thermistor,
+	},
+	{
+		.name = POWER_SUPPLY_TECHNOLOGY_LION,
+		.resis_high = 95000,
+		.resis_low = 76001,
+		.battery_resistance = 300,
+		.charge_full_design = 950,
+		.nominal_voltage = 3700,
+		.termination_vol = 4150,
+		.termination_curr = 100,
+		.recharge_cap = 95,
+		.normal_cur_lvl = 700,
+		.normal_vol_lvl = 4200,
+		.maint_a_cur_lvl = 600,
+		.maint_a_vol_lvl = 4150,
+		.maint_a_chg_timer_h = 60,
+		.maint_b_cur_lvl = 600,
+		.maint_b_vol_lvl = 4100,
+		.maint_b_chg_timer_h = 200,
+		.low_high_cur_lvl = 300,
+		.low_high_vol_lvl = 4000,
+		.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+		.r_to_t_tbl = temp_tbl,
+		.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+		.v_to_cap_tbl = cap_tbl,
+		.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+		.batres_tbl = temp_to_batres_tbl_thermistor,
+	},
 };
 
 static const struct abx500_bm_capacity_levels cap_levels = {
@@ -405,8 +405,8 @@ static const struct abx500_fg_parameters fg = {
 	.lowbat_threshold = 3100,
 	.battok_falling_th_sel0 = 2860,
 	.battok_raising_th_sel1 = 2860,
+	.maint_thres = 95,
 	.user_cap_limit = 15,
-	.maint_thres = 97,
 };
 
 static const struct abx500_maxim_parameters maxi_params = {
@@ -424,96 +424,84 @@ static const struct abx500_bm_charger_parameters chg = {
 };
 
 struct abx500_bm_data ab8500_bm_data = {
-	.temp_under		= 3,
-	.temp_low		= 8,
-	.temp_high		= 43,
-	.temp_over		= 48,
-	.main_safety_tmr_h	= 4,
-	.temp_interval_chg	= 20,
-	.temp_interval_nochg	= 120,
-	.usb_safety_tmr_h	= 4,
-	.bkup_bat_v		= BUP_VCH_SEL_2P6V,
-	.bkup_bat_i		= BUP_ICH_SEL_150UA,
-	.no_maintenance		= false,
-	.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		= 3,
-	.batt_id		= 0,
-	.interval_charging	= 5,
-	.interval_not_charging	= 120,
-	.temp_hysteresis	= 3,
-	.gnd_lift_resistance	= 34,
-	.maxi			= &maxi_params,
-	.chg_params		= &chg,
-	.fg_params		= &fg,
+	.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               = 3,
+	.batt_id                = 0,
+	.interval_charging      = 5,
+	.interval_not_charging  = 120,
+	.temp_hysteresis        = 3,
+	.gnd_lift_resistance    = 34,
+	.maxi                   = &maxi_params,
+	.chg_params             = &chg,
+	.fg_params              = &fg,
 };
 
-int bmdevs_of_probe(struct device *dev, struct device_node *np,
-		    struct abx500_bm_data **battery)
+int ab8500_bm_of_probe(struct device *dev,
+		       struct device_node *np,
+		       struct abx500_bm_data *bm)
 {
-	struct	abx500_battery_type *btype;
-	struct  device_node *np_bat_supply;
-	struct	abx500_bm_data *bat;
+	struct batres_vs_temp *tmp_batres_tbl;
+	struct device_node *battery_node;
 	const char *btech;
-	char bat_tech[8];
-	int i, thermistor;
-
-	*battery = &ab8500_bm_data;
+	int i;
 
 	/* get phandle to 'battery-info' node */
-	np_bat_supply = of_parse_phandle(np, "battery", 0);
-	if (!np_bat_supply) {
-		dev_err(dev, "missing property battery\n");
+	battery_node = of_parse_phandle(np, "battery", 0);
+	if (!battery_node) {
+		dev_err(dev, "battery node or reference missing\n");
 		return -EINVAL;
 	}
-	if (of_property_read_bool(np_bat_supply,
-			"thermistor-on-batctrl"))
-		thermistor = NTC_INTERNAL;
-	else
-		thermistor = NTC_EXTERNAL;
 
-	bat = *battery;
-	if (thermistor == NTC_EXTERNAL) {
-		bat->n_btypes  = 4;
-		bat->bat_type  = bat_type_ext_thermistor;
-		bat->adc_therm = ABx500_ADC_THERM_BATTEMP;
-	}
-	btech = of_get_property(np_bat_supply,
-		"stericsson,battery-type", NULL);
+	btech = of_get_property(battery_node, "stericsson,battery-type", NULL);
 	if (!btech) {
 		dev_warn(dev, "missing property battery-name/type\n");
-		strcpy(bat_tech, "UNKNOWN");
-	} else {
-		strcpy(bat_tech, btech);
+		return -EINVAL;
 	}
 
-	if (strncmp(bat_tech, "LION", 4) == 0) {
-		bat->no_maintenance  = true;
-		bat->chg_unknown_bat = true;
-		bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
-		bat->bat_type[BATTERY_UNKNOWN].termination_vol    = 4150;
-		bat->bat_type[BATTERY_UNKNOWN].recharge_vol	  = 4130;
-		bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl	  = 520;
-		bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl	  = 4200;
+	if (strncmp(btech, "LION", 4) == 0) {
+		bm->no_maintenance  = true;
+		bm->chg_unknown_bat = true;
+		bm->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
+		bm->bat_type[BATTERY_UNKNOWN].termination_vol    = 4150;
+		bm->bat_type[BATTERY_UNKNOWN].recharge_cap       = 95;
+		bm->bat_type[BATTERY_UNKNOWN].normal_cur_lvl     = 520;
+		bm->bat_type[BATTERY_UNKNOWN].normal_vol_lvl     = 4200;
 	}
-	/* select the battery resolution table */
-	for (i = 0; i < bat->n_btypes; ++i) {
-		btype = (bat->bat_type + i);
-		if (thermistor == NTC_EXTERNAL) {
-			btype->batres_tbl =
-				temp_to_batres_tbl_ext_thermistor;
-		} else if (strncmp(bat_tech, "LION", 4) == 0) {
-			btype->batres_tbl =
-				temp_to_batres_tbl_9100;
-		} else {
-			btype->batres_tbl =
-				temp_to_batres_tbl_thermistor;
-		}
+
+	if (of_property_read_bool(battery_node, "thermistor-on-batctrl")) {
+		if (strncmp(btech, "LION", 4) == 0)
+			tmp_batres_tbl = temp_to_batres_tbl_9100;
+		else
+			tmp_batres_tbl = temp_to_batres_tbl_thermistor;
+	} else {
+		bm->n_btypes   = 4;
+		bm->bat_type   = bat_type_ext_thermistor;
+		bm->adc_therm  = ABx500_ADC_THERM_BATTEMP;
+		tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
 	}
-	of_node_put(np_bat_supply);
+
+	/* select the battery resolution table */
+	for (i = 0; i < bm->n_btypes; ++i)
+		bm->bat_type[i].batres_tbl = tmp_batres_tbl;
+
+	of_node_put(battery_node);
+
 	return 0;
 }

+ 117 - 75
drivers/power/ab8500_btemp.c

@@ -39,6 +39,9 @@
 #define BTEMP_BATCTRL_CURR_SRC_7UA	7
 #define BTEMP_BATCTRL_CURR_SRC_20UA	20
 
+#define BTEMP_BATCTRL_CURR_SRC_16UA	16
+#define BTEMP_BATCTRL_CURR_SRC_18UA	18
+
 #define to_ab8500_btemp_device_info(x) container_of((x), \
 	struct ab8500_btemp, btemp_psy);
 
@@ -78,12 +81,13 @@ struct ab8500_btemp_ranges {
  * @parent:		Pointer to the struct ab8500
  * @gpadc:		Pointer to the struct gpadc
  * @fg:			Pointer to the struct fg
- * @bat:		Pointer to the abx500_bm platform data
+ * @bm:           	Platform specific battery management information
  * @btemp_psy:		Structure for BTEMP specific battery properties
  * @events:		Structure for information about events triggered
  * @btemp_ranges:	Battery temperature range structure
  * @btemp_wq:		Work queue for measuring the temperature periodically
  * @btemp_periodic_work:	Work for measuring the temperature periodically
+ * @initialized:	True if battery id read.
  */
 struct ab8500_btemp {
 	struct device *dev;
@@ -94,12 +98,13 @@ struct ab8500_btemp {
 	struct ab8500 *parent;
 	struct ab8500_gpadc *gpadc;
 	struct ab8500_fg *fg;
-	struct abx500_bm_data *bat;
+	struct abx500_bm_data *bm;
 	struct power_supply btemp_psy;
 	struct ab8500_btemp_events events;
 	struct ab8500_btemp_ranges btemp_ranges;
 	struct workqueue_struct *btemp_wq;
 	struct delayed_work btemp_periodic_work;
+	bool initialized;
 };
 
 /* BTEMP power supply properties */
@@ -147,13 +152,13 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
 		return (450000 * (v_batctrl)) / (1800 - v_batctrl);
 	}
 
-	if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL) {
+	if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
 		/*
 		 * If the battery has internal NTC, we use the current
 		 * source to calculate the resistance, 7uA or 20uA
 		 */
 		rbs = (v_batctrl * 1000
-		       - di->bat->gnd_lift_resistance * inst_curr)
+		       - di->bm->gnd_lift_resistance * inst_curr)
 		      / di->curr_source;
 	} else {
 		/*
@@ -209,11 +214,19 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
 		return 0;
 
 	/* Only do this for batteries with internal NTC */
-	if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
-		if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
-			curr = BAT_CTRL_7U_ENA;
-		else
-			curr = BAT_CTRL_20U_ENA;
+	if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
+
+		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+			if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA)
+				curr = BAT_CTRL_16U_ENA;
+			else
+				curr = BAT_CTRL_18U_ENA;
+		} else {
+			if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
+				curr = BAT_CTRL_7U_ENA;
+			else
+				curr = BAT_CTRL_20U_ENA;
+		}
 
 		dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
 
@@ -241,14 +254,25 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
 				__func__);
 			goto disable_curr_source;
 		}
-	} else if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
+	} else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
 		dev_dbg(di->dev, "Disable BATCTRL curr source\n");
 
-		/* Write 0 to the curr bits */
-		ret = abx500_mask_and_set_register_interruptible(di->dev,
-			AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
-			BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
-			~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+		if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+			/* Write 0 to the curr bits */
+			ret = abx500_mask_and_set_register_interruptible(
+				di->dev,
+				AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+				BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
+				~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
+		} else {
+			/* Write 0 to the curr bits */
+			ret = abx500_mask_and_set_register_interruptible(
+				di->dev,
+				AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+				BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
+				~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+		}
+
 		if (ret) {
 			dev_err(di->dev, "%s failed disabling current source\n",
 				__func__);
@@ -290,11 +314,20 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
 	 * if we got an error above
 	 */
 disable_curr_source:
-	/* Write 0 to the curr bits */
-	ret = abx500_mask_and_set_register_interruptible(di->dev,
+	if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+		/* Write 0 to the curr bits */
+		ret = abx500_mask_and_set_register_interruptible(di->dev,
+			AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
+			BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
+			~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
+	} else {
+		/* Write 0 to the curr bits */
+		ret = abx500_mask_and_set_register_interruptible(di->dev,
 			AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
 			BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
 			~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
+	}
+
 	if (ret) {
 		dev_err(di->dev, "%s failed disabling current source\n",
 			__func__);
@@ -372,13 +405,10 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
 		return ret;
 	}
 
-	/*
-	 * Since there is no interrupt when current measurement is done,
-	 * loop for over 250ms (250ms is one sample conversion time
-	 * with 32.768 Khz RTC clock). Note that a stop time must be set
-	 * since the ab8500_btemp_read_batctrl_voltage call can block and
-	 * take an unknown amount of time to complete.
-	 */
+	do {
+		msleep(20);
+	} while (!ab8500_fg_inst_curr_started(di->fg));
+
 	i = 0;
 
 	do {
@@ -457,9 +487,9 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
 	int rbat, rntc, vntc;
 	u8 id;
 
-	id = di->bat->batt_id;
+	id = di->bm->batt_id;
 
-	if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+	if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
 			id != BATTERY_UNKNOWN) {
 
 		rbat = ab8500_btemp_get_batctrl_res(di);
@@ -474,8 +504,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
 		}
 
 		temp = ab8500_btemp_res_to_temp(di,
-			di->bat->bat_type[id].r_to_t_tbl,
-			di->bat->bat_type[id].n_temp_tbl_elements, rbat);
+			di->bm->bat_type[id].r_to_t_tbl,
+			di->bm->bat_type[id].n_temp_tbl_elements, rbat);
 	} else {
 		vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL);
 		if (vntc < 0) {
@@ -491,8 +521,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
 		rntc = 230000 * vntc / (VTVOUT_V - vntc);
 
 		temp = ab8500_btemp_res_to_temp(di,
-			di->bat->bat_type[id].r_to_t_tbl,
-			di->bat->bat_type[id].n_temp_tbl_elements, rntc);
+			di->bm->bat_type[id].r_to_t_tbl,
+			di->bm->bat_type[id].n_temp_tbl_elements, rntc);
 		prev = temp;
 	}
 	dev_dbg(di->dev, "Battery temperature is %d\n", temp);
@@ -511,9 +541,12 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 {
 	int res;
 	u8 i;
+	if (is_ab9540(di->parent) || is_ab8505(di->parent))
+		di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
+	else
+		di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
 
-	di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
-	di->bat->batt_id = BATTERY_UNKNOWN;
+	di->bm->batt_id = BATTERY_UNKNOWN;
 
 	res =  ab8500_btemp_get_batctrl_res(di);
 	if (res < 0) {
@@ -522,23 +555,23 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
 	}
 
 	/* BATTERY_UNKNOWN is defined on position 0, skip it! */
-	for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) {
-		if ((res <= di->bat->bat_type[i].resis_high) &&
-			(res >= di->bat->bat_type[i].resis_low)) {
+	for (i = BATTERY_UNKNOWN + 1; i < di->bm->n_btypes; i++) {
+		if ((res <= di->bm->bat_type[i].resis_high) &&
+			(res >= di->bm->bat_type[i].resis_low)) {
 			dev_dbg(di->dev, "Battery detected on %s"
 				" low %d < res %d < high: %d"
 				" index: %d\n",
-				di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL ?
+				di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ?
 				"BATCTRL" : "BATTEMP",
-				di->bat->bat_type[i].resis_low, res,
-				di->bat->bat_type[i].resis_high, i);
+				di->bm->bat_type[i].resis_low, res,
+				di->bm->bat_type[i].resis_high, i);
 
-			di->bat->batt_id = i;
+			di->bm->batt_id = i;
 			break;
 		}
 	}
 
-	if (di->bat->batt_id == BATTERY_UNKNOWN) {
+	if (di->bm->batt_id == BATTERY_UNKNOWN) {
 		dev_warn(di->dev, "Battery identified as unknown"
 			", resistance %d Ohm\n", res);
 		return -ENXIO;
@@ -548,13 +581,18 @@ 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
 	 */
-	if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
-			di->bat->batt_id == 1) {
-		dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
-		di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+	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->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
+		} else {
+			dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
+			di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+		}
 	}
 
-	return di->bat->batt_id;
+	return di->bm->batt_id;
 }
 
 /**
@@ -569,6 +607,13 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
 	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) {
@@ -577,9 +622,9 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
 	}
 
 	if (di->events.ac_conn || di->events.usb_conn)
-		interval = di->bat->temp_interval_chg;
+		interval = di->bm->temp_interval_chg;
 	else
-		interval = di->bat->temp_interval_nochg;
+		interval = di->bm->temp_interval_nochg;
 
 	/* Schedule a new measurement */
 	queue_delayed_work(di->btemp_wq,
@@ -616,9 +661,9 @@ static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
 {
 	struct ab8500_btemp *di = _di;
 
-	if (is_ab8500_2p0_or_earlier(di->parent)) {
+	if (is_ab8500_3p3_or_earlier(di->parent)) {
 		dev_dbg(di->dev, "Ignore false btemp low irq"
-			" for ABB cut 1.0, 1.1 and 2.0\n");
+			" for ABB cut 1.0, 1.1, 2.0 and 3.3\n");
 	} else {
 		dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
 
@@ -732,30 +777,30 @@ static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
 	int temp = 0;
 
 	/*
-	 * The BTEMP events are not reliabe on AB8500 cut2.0
+	 * The BTEMP events are not reliabe on AB8500 cut3.3
 	 * and prior versions
 	 */
-	if (is_ab8500_2p0_or_earlier(di->parent)) {
+	if (is_ab8500_3p3_or_earlier(di->parent)) {
 		temp = di->bat_temp * 10;
 	} else {
 		if (di->events.btemp_low) {
 			if (temp > di->btemp_ranges.btemp_low_limit)
-				temp = di->btemp_ranges.btemp_low_limit;
+				temp = di->btemp_ranges.btemp_low_limit * 10;
 			else
 				temp = di->bat_temp * 10;
 		} else if (di->events.btemp_high) {
 			if (temp < di->btemp_ranges.btemp_high_limit)
-				temp = di->btemp_ranges.btemp_high_limit;
+				temp = di->btemp_ranges.btemp_high_limit * 10;
 			else
 				temp = di->bat_temp * 10;
 		} else if (di->events.btemp_lowmed) {
 			if (temp > di->btemp_ranges.btemp_med_limit)
-				temp = di->btemp_ranges.btemp_med_limit;
+				temp = di->btemp_ranges.btemp_med_limit * 10;
 			else
 				temp = di->bat_temp * 10;
 		} else if (di->events.btemp_medhigh) {
 			if (temp < di->btemp_ranges.btemp_med_limit)
-				temp = di->btemp_ranges.btemp_med_limit;
+				temp = di->btemp_ranges.btemp_med_limit * 10;
 			else
 				temp = di->bat_temp * 10;
 		} else
@@ -806,7 +851,7 @@ static int ab8500_btemp_get_property(struct power_supply *psy,
 			val->intval = 1;
 		break;
 	case POWER_SUPPLY_PROP_TECHNOLOGY:
-		val->intval = di->bat->bat_type[di->bat->batt_id].name;
+		val->intval = di->bm->bat_type[di->bm->batt_id].name;
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
 		val->intval = ab8500_btemp_get_temp(di);
@@ -967,6 +1012,7 @@ static char *supply_interface[] = {
 static int ab8500_btemp_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
+	struct abx500_bm_data *plat = pdev->dev.platform_data;
 	struct ab8500_btemp *di;
 	int irq, i, ret = 0;
 	u8 val;
@@ -976,21 +1022,19 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__);
 		return -ENOMEM;
 	}
-	di->bat = pdev->mfd_cell->platform_data;
-	if (!di->bat) {
-		if (np) {
-			ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
-			if (ret) {
-				dev_err(&pdev->dev,
-					"failed to get battery information\n");
-				return ret;
-			}
-		} else {
-			dev_err(&pdev->dev, "missing dt node for ab8500_btemp\n");
-			return -EINVAL;
+
+	if (!plat) {
+		dev_err(&pdev->dev, "no battery management data supplied\n");
+		return -EINVAL;
+	}
+	di->bm = plat;
+
+	if (np) {
+		ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to get battery information\n");
+			return ret;
 		}
-	} else {
-		dev_info(&pdev->dev, "falling back to legacy platform data\n");
 	}
 
 	/* get parent data */
@@ -998,6 +1042,8 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
 	di->parent = dev_get_drvdata(pdev->dev.parent);
 	di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
 
+	di->initialized = false;
+
 	/* BTEMP supply */
 	di->btemp_psy.name = "ab8500_btemp";
 	di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY;
@@ -1022,10 +1068,6 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
 	INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
 		ab8500_btemp_periodic_work);
 
-	/* Identify the battery */
-	if (ab8500_btemp_id(di) < 0)
-		dev_warn(di->dev, "failed to identify the battery\n");
-
 	/* Set BTEMP thermal limits. Low and Med are fixed */
 	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
 	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
@@ -1123,7 +1165,7 @@ static void __exit ab8500_btemp_exit(void)
 	platform_driver_unregister(&ab8500_btemp_driver);
 }
 
-subsys_initcall_sync(ab8500_btemp_init);
+device_initcall(ab8500_btemp_init);
 module_exit(ab8500_btemp_exit);
 
 MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large
+ 534 - 115
drivers/power/ab8500_charger.c


File diff suppressed because it is too large
+ 318 - 128
drivers/power/ab8500_fg.c


+ 123 - 81
drivers/power/abx500_chargalg.c

@@ -33,9 +33,6 @@
 /* End-of-charge criteria counter */
 #define EOC_COND_CNT			10
 
-/* Recharge criteria counter */
-#define RCH_COND_CNT			3
-
 #define to_abx500_chargalg_device_info(x) container_of((x), \
 	struct abx500_chargalg, chargalg_psy);
 
@@ -196,7 +193,6 @@ enum maxim_ret {
  * @dev:		pointer to the structure device
  * @charge_status:	battery operating status
  * @eoc_cnt:		counter used to determine end-of_charge
- * @rch_cnt:		counter used to determine start of recharge
  * @maintenance_chg:	indicate if maintenance charge is active
  * @t_hyst_norm		temperature hysteresis when the temperature has been
  *			over or under normal limits
@@ -207,7 +203,7 @@ enum maxim_ret {
  * @chg_info:		information about connected charger types
  * @batt_data:		data of the battery
  * @susp_status:	current charger suspension status
- * @bat:		pointer to the abx500_bm platform data
+ * @bm:           	Platform specific battery management information
  * @chargalg_psy:	structure that holds the battery properties exposed by
  *			the charging algorithm
  * @events:		structure for information about events triggered
@@ -223,7 +219,6 @@ struct abx500_chargalg {
 	struct device *dev;
 	int charge_status;
 	int eoc_cnt;
-	int rch_cnt;
 	bool maintenance_chg;
 	int t_hyst_norm;
 	int t_hyst_lowhigh;
@@ -232,7 +227,7 @@ 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 abx500_bm_data *bat;
+	struct abx500_bm_data *bm;
 	struct power_supply chargalg_psy;
 	struct ux500_charger *ac_chg;
 	struct ux500_charger *usb_chg;
@@ -367,13 +362,13 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
 	case AC_CHG:
 		timer_expiration =
 		round_jiffies(jiffies +
-			(di->bat->main_safety_tmr_h * 3600 * HZ));
+			(di->bm->main_safety_tmr_h * 3600 * HZ));
 		break;
 
 	case USB_CHG:
 		timer_expiration =
 		round_jiffies(jiffies +
-			(di->bat->usb_safety_tmr_h * 3600 * HZ));
+			(di->bm->usb_safety_tmr_h * 3600 * HZ));
 		break;
 
 	default:
@@ -450,8 +445,18 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
 {
 	/* Check if charger exists and kick watchdog if charging */
 	if (di->ac_chg && di->ac_chg->ops.kick_wd &&
-			di->chg_info.online_chg & AC_CHG)
+	    di->chg_info.online_chg & AC_CHG) {
+		/*
+		 * If AB charger watchdog expired, pm2xxx charging
+		 * gets disabled. To be safe, kick both AB charger watchdog
+		 * and pm2xxx watchdog.
+		 */
+		if (di->ac_chg->external &&
+		    di->usb_chg && di->usb_chg->ops.kick_wd)
+			di->usb_chg->ops.kick_wd(di->usb_chg);
+
 		return di->ac_chg->ops.kick_wd(di->ac_chg);
+	}
 	else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
 			di->chg_info.online_chg & USB_CHG)
 		return di->usb_chg->ops.kick_wd(di->usb_chg);
@@ -608,6 +613,8 @@ 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,
@@ -625,8 +632,12 @@ 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);
 }
 
 /**
@@ -638,32 +649,32 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
  */
 static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
 {
-	if (di->batt_data.temp > (di->bat->temp_low + di->t_hyst_norm) &&
-		di->batt_data.temp < (di->bat->temp_high - di->t_hyst_norm)) {
+	if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) &&
+		di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) {
 		/* Temp OK! */
 		di->events.btemp_underover = false;
 		di->events.btemp_lowhigh = false;
 		di->t_hyst_norm = 0;
 		di->t_hyst_lowhigh = 0;
 	} else {
-		if (((di->batt_data.temp >= di->bat->temp_high) &&
+		if (((di->batt_data.temp >= di->bm->temp_high) &&
 			(di->batt_data.temp <
-				(di->bat->temp_over - di->t_hyst_lowhigh))) ||
+				(di->bm->temp_over - di->t_hyst_lowhigh))) ||
 			((di->batt_data.temp >
-				(di->bat->temp_under + di->t_hyst_lowhigh)) &&
-			(di->batt_data.temp <= di->bat->temp_low))) {
+				(di->bm->temp_under + di->t_hyst_lowhigh)) &&
+			(di->batt_data.temp <= di->bm->temp_low))) {
 			/* TEMP minor!!!!! */
 			di->events.btemp_underover = false;
 			di->events.btemp_lowhigh = true;
-			di->t_hyst_norm = di->bat->temp_hysteresis;
+			di->t_hyst_norm = di->bm->temp_hysteresis;
 			di->t_hyst_lowhigh = 0;
-		} else if (di->batt_data.temp <= di->bat->temp_under ||
-			di->batt_data.temp >= di->bat->temp_over) {
+		} else if (di->batt_data.temp <= di->bm->temp_under ||
+			di->batt_data.temp >= di->bm->temp_over) {
 			/* TEMP major!!!!! */
 			di->events.btemp_underover = true;
 			di->events.btemp_lowhigh = false;
 			di->t_hyst_norm = 0;
-			di->t_hyst_lowhigh = di->bat->temp_hysteresis;
+			di->t_hyst_lowhigh = di->bm->temp_hysteresis;
 		} else {
 		/* Within hysteresis */
 		dev_dbg(di->dev, "Within hysteresis limit temp: %d "
@@ -682,12 +693,12 @@ static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
  */
 static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
 {
-	if (di->chg_info.usb_volt > di->bat->chg_params->usb_volt_max)
+	if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max)
 		di->chg_info.usb_chg_ok = false;
 	else
 		di->chg_info.usb_chg_ok = true;
 
-	if (di->chg_info.ac_volt > di->bat->chg_params->ac_volt_max)
+	if (di->chg_info.ac_volt > di->bm->chg_params->ac_volt_max)
 		di->chg_info.ac_chg_ok = false;
 	else
 		di->chg_info.ac_chg_ok = true;
@@ -707,10 +718,10 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
 	if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
 		di->charge_state == STATE_NORMAL &&
 		!di->maintenance_chg && (di->batt_data.volt >=
-		di->bat->bat_type[di->bat->batt_id].termination_vol ||
+		di->bm->bat_type[di->bm->batt_id].termination_vol ||
 		di->events.usb_cv_active || di->events.ac_cv_active) &&
 		di->batt_data.avg_curr <
-		di->bat->bat_type[di->bat->batt_id].termination_curr &&
+		di->bm->bat_type[di->bm->batt_id].termination_curr &&
 		di->batt_data.avg_curr > 0) {
 		if (++di->eoc_cnt >= EOC_COND_CNT) {
 			di->eoc_cnt = 0;
@@ -733,12 +744,12 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
 static void init_maxim_chg_curr(struct abx500_chargalg *di)
 {
 	di->ccm.original_iset =
-		di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
+		di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
 	di->ccm.current_iset =
-		di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
-	di->ccm.test_delta_i = di->bat->maxi->charger_curr_step;
-	di->ccm.max_current = di->bat->maxi->chg_curr;
-	di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+		di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
+	di->ccm.test_delta_i = di->bm->maxi->charger_curr_step;
+	di->ccm.max_current = di->bm->maxi->chg_curr;
+	di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
 	di->ccm.level = 0;
 }
 
@@ -755,7 +766,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
 {
 	int delta_i;
 
-	if (!di->bat->maxi->ena_maxi)
+	if (!di->bm->maxi->ena_maxi)
 		return MAXIM_RET_NOACTION;
 
 	delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
@@ -766,7 +777,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
 		if (di->ccm.wait_cnt == 0) {
 			dev_dbg(di->dev, "lowering current\n");
 			di->ccm.wait_cnt++;
-			di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+			di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
 			di->ccm.max_current =
 				di->ccm.current_iset - di->ccm.test_delta_i;
 			di->ccm.current_iset = di->ccm.max_current;
@@ -791,7 +802,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
 		if (di->ccm.current_iset == di->ccm.original_iset)
 			return MAXIM_RET_NOACTION;
 
-		di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+		di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
 		di->ccm.current_iset = di->ccm.original_iset;
 		di->ccm.level = 0;
 
@@ -803,7 +814,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
 		di->ccm.max_current) {
 		if (di->ccm.condition_cnt-- == 0) {
 			/* Increse the iset with cco.test_delta_i */
-			di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+			di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
 			di->ccm.current_iset += di->ccm.test_delta_i;
 			di->ccm.level++;
 			dev_dbg(di->dev, " Maximization needed, increase"
@@ -818,7 +829,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
 			return MAXIM_RET_NOACTION;
 		}
 	}  else {
-		di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+		di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
 		return MAXIM_RET_NOACTION;
 	}
 }
@@ -838,7 +849,7 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di)
 		break;
 	case MAXIM_RET_IBAT_TOO_HIGH:
 		result = abx500_chargalg_update_chg_curr(di,
-			di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
+			di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
 		if (result)
 			dev_err(di->dev, "failed to set chg curr\n");
 		break;
@@ -858,6 +869,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 	union power_supply_propval ret;
 	int i, j;
 	bool psy_found = false;
+	bool capacity_updated = false;
 
 	psy = (struct power_supply *)data;
 	ext = dev_get_drvdata(dev);
@@ -870,6 +882,16 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 	if (!psy_found)
 		return 0;
 
+	/*
+	 *  If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its
+	 * property because of handling that sysfs entry on its own, this is
+	 * the place to get the battery capacity.
+	 */
+	if (!ext->get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) {
+		di->batt_data.percent = ret.intval;
+		capacity_updated = true;
+	}
+
 	/* Go through all properties for the psy */
 	for (j = 0; j < ext->num_properties; j++) {
 		enum power_supply_property prop;
@@ -1154,7 +1176,8 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
 			}
 			break;
 		case POWER_SUPPLY_PROP_CAPACITY:
-			di->batt_data.percent = ret.intval;
+			if (!capacity_updated)
+				di->batt_data.percent = ret.intval;
 			break;
 		default:
 			break;
@@ -1210,7 +1233,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 	 * this way
 	 */
 	if (!charger_status ||
-		(di->events.batt_unknown && !di->bat->chg_unknown_bat)) {
+		(di->events.batt_unknown && !di->bm->chg_unknown_bat)) {
 		if (di->charge_state != STATE_HANDHELD) {
 			di->events.safety_timer_expired = false;
 			abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
@@ -1394,8 +1417,8 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 
 	case STATE_NORMAL_INIT:
 		abx500_chargalg_start_charging(di,
-			di->bat->bat_type[di->bat->batt_id].normal_vol_lvl,
-			di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
+			di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
+			di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
 		abx500_chargalg_state_to(di, STATE_NORMAL);
 		abx500_chargalg_start_safety_timer(di);
 		abx500_chargalg_stop_maintenance_timer(di);
@@ -1411,7 +1434,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 		handle_maxim_chg_curr(di);
 		if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
 			di->maintenance_chg) {
-			if (di->bat->no_maintenance)
+			if (di->bm->no_maintenance)
 				abx500_chargalg_state_to(di,
 					STATE_WAIT_FOR_RECHARGE_INIT);
 			else
@@ -1424,28 +1447,25 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 	case STATE_WAIT_FOR_RECHARGE_INIT:
 		abx500_chargalg_hold_charging(di);
 		abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
-		di->rch_cnt = RCH_COND_CNT;
 		/* Intentional fallthrough */
 
 	case STATE_WAIT_FOR_RECHARGE:
-		if (di->batt_data.volt <=
-			di->bat->bat_type[di->bat->batt_id].recharge_vol) {
-			if (di->rch_cnt-- == 0)
-				abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
-		} else
-			di->rch_cnt = RCH_COND_CNT;
+		if (di->batt_data.percent <=
+		    di->bm->bat_type[di->bm->batt_id].
+		    recharge_cap)
+			abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
 		break;
 
 	case STATE_MAINTENANCE_A_INIT:
 		abx500_chargalg_stop_safety_timer(di);
 		abx500_chargalg_start_maintenance_timer(di,
-			di->bat->bat_type[
-				di->bat->batt_id].maint_a_chg_timer_h);
+			di->bm->bat_type[
+				di->bm->batt_id].maint_a_chg_timer_h);
 		abx500_chargalg_start_charging(di,
-			di->bat->bat_type[
-				di->bat->batt_id].maint_a_vol_lvl,
-			di->bat->bat_type[
-				di->bat->batt_id].maint_a_cur_lvl);
+			di->bm->bat_type[
+				di->bm->batt_id].maint_a_vol_lvl,
+			di->bm->bat_type[
+				di->bm->batt_id].maint_a_cur_lvl);
 		abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
 		power_supply_changed(&di->chargalg_psy);
 		/* Intentional fallthrough*/
@@ -1459,13 +1479,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 
 	case STATE_MAINTENANCE_B_INIT:
 		abx500_chargalg_start_maintenance_timer(di,
-			di->bat->bat_type[
-				di->bat->batt_id].maint_b_chg_timer_h);
+			di->bm->bat_type[
+				di->bm->batt_id].maint_b_chg_timer_h);
 		abx500_chargalg_start_charging(di,
-			di->bat->bat_type[
-				di->bat->batt_id].maint_b_vol_lvl,
-			di->bat->bat_type[
-				di->bat->batt_id].maint_b_cur_lvl);
+			di->bm->bat_type[
+				di->bm->batt_id].maint_b_vol_lvl,
+			di->bm->bat_type[
+				di->bm->batt_id].maint_b_cur_lvl);
 		abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
 		power_supply_changed(&di->chargalg_psy);
 		/* Intentional fallthrough*/
@@ -1479,10 +1499,10 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 
 	case STATE_TEMP_LOWHIGH_INIT:
 		abx500_chargalg_start_charging(di,
-			di->bat->bat_type[
-				di->bat->batt_id].low_high_vol_lvl,
-			di->bat->bat_type[
-				di->bat->batt_id].low_high_cur_lvl);
+			di->bm->bat_type[
+				di->bm->batt_id].low_high_vol_lvl,
+			di->bm->bat_type[
+				di->bm->batt_id].low_high_cur_lvl);
 		abx500_chargalg_stop_maintenance_timer(di);
 		di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
 		abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
@@ -1543,11 +1563,11 @@ static void abx500_chargalg_periodic_work(struct work_struct *work)
 	if (di->chg_info.conn_chg)
 		queue_delayed_work(di->chargalg_wq,
 			&di->chargalg_periodic_work,
-			di->bat->interval_charging * HZ);
+			di->bm->interval_charging * HZ);
 	else
 		queue_delayed_work(di->chargalg_wq,
 			&di->chargalg_periodic_work,
-			di->bat->interval_not_charging * HZ);
+			di->bm->interval_not_charging * HZ);
 }
 
 /**
@@ -1614,10 +1634,13 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
 		if (di->events.batt_ovv) {
 			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
 		} else if (di->events.btemp_underover) {
-			if (di->batt_data.temp <= di->bat->temp_under)
+			if (di->batt_data.temp <= di->bm->temp_under)
 				val->intval = POWER_SUPPLY_HEALTH_COLD;
 			else
 				val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+		} else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED ||
+			   di->charge_state == STATE_SAFETY_TIMER_EXPIRED_INIT) {
+			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
 		} else {
 			val->intval = POWER_SUPPLY_HEALTH_GOOD;
 		}
@@ -1630,6 +1653,25 @@ 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)
+{
+	struct abx500_chargalg *di = container_of(kobj,
+               struct abx500_chargalg, chargalg_kobject);
+
+	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
@@ -1698,7 +1740,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
 static struct attribute abx500_chargalg_en_charger = \
 {
 	.name = "chargalg",
-	.mode = S_IWUGO,
+	.mode = S_IRUGO | S_IWUSR,
 };
 
 static struct attribute *abx500_chargalg_chg[] = {
@@ -1707,6 +1749,7 @@ static struct attribute *abx500_chargalg_chg[] = {
 };
 
 static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
+	.show = abx500_chargalg_sysfs_show,
 	.store = abx500_chargalg_sysfs_charger,
 };
 
@@ -1806,6 +1849,7 @@ static char *supply_interface[] = {
 static int abx500_chargalg_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
+	struct abx500_bm_data *plat = pdev->dev.platform_data;
 	struct abx500_chargalg *di;
 	int ret = 0;
 
@@ -1814,21 +1858,19 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__);
 		return -ENOMEM;
 	}
-	di->bat = pdev->mfd_cell->platform_data;
-	if (!di->bat) {
-		if (np) {
-			ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
-			if (ret) {
-				dev_err(&pdev->dev,
-					"failed to get battery information\n");
-				return ret;
-			}
-		} else {
-			dev_err(&pdev->dev, "missing dt node for ab8500_chargalg\n");
-			return -EINVAL;
+
+	if (!plat) {
+		dev_err(&pdev->dev, "no battery management data supplied\n");
+		return -EINVAL;
+	}
+	di->bm = plat;
+
+	if (np) {
+		ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to get battery information\n");
+			return ret;
 		}
-	} else {
-		dev_info(&pdev->dev, "falling back to legacy platform data\n");
 	}
 
 	/* get device struct */

+ 25 - 29
drivers/power/bq2415x_charger.c

@@ -28,7 +28,6 @@
  * http://www.ti.com/product/bq24155
  */
 
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/param.h>
@@ -734,12 +733,10 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
 	int charger = 0;
 	int boost = 0;
 
-	if (mode == BQ2415X_MODE_HOST_CHARGER ||
-		mode == BQ2415X_MODE_DEDICATED_CHARGER)
-			charger = 1;
-
 	if (mode == BQ2415X_MODE_BOOST)
 		boost = 1;
+	else if (mode != BQ2415X_MODE_OFF)
+		charger = 1;
 
 	if (!charger)
 		ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
@@ -751,6 +748,10 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
 		return ret;
 
 	switch (mode) {
+	case BQ2415X_MODE_OFF:
+		dev_dbg(bq->dev, "changing mode to: Offline\n");
+		ret = bq2415x_set_current_limit(bq, 100);
+		break;
 	case BQ2415X_MODE_NONE:
 		dev_dbg(bq->dev, "changing mode to: N/A\n");
 		ret = bq2415x_set_current_limit(bq, 100);
@@ -843,7 +844,7 @@ static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
 	dev_err(bq->dev, "%s\n", msg);
 	if (bq->automode > 0)
 		bq->automode = 0;
-	bq2415x_set_mode(bq, BQ2415X_MODE_NONE);
+	bq2415x_set_mode(bq, BQ2415X_MODE_OFF);
 	bq2415x_set_autotimer(bq, 0);
 }
 
@@ -1136,6 +1137,10 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
 			return -ENOSYS;
 		bq->automode = 1;
 		mode = bq->reported_mode;
+	} else if (strncmp(buf, "off", 3) == 0) {
+		if (bq->automode > 0)
+			bq->automode = 0;
+		mode = BQ2415X_MODE_OFF;
 	} else if (strncmp(buf, "none", 4) == 0) {
 		if (bq->automode > 0)
 			bq->automode = 0;
@@ -1183,6 +1188,9 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
 		ret += sprintf(buf+ret, "auto (");
 
 	switch (bq->mode) {
+	case BQ2415X_MODE_OFF:
+		ret += sprintf(buf+ret, "off");
+		break;
 	case BQ2415X_MODE_NONE:
 		ret += sprintf(buf+ret, "none");
 		break;
@@ -1217,6 +1225,8 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
 		return -EINVAL;
 
 	switch (bq->reported_mode) {
+	case BQ2415X_MODE_OFF:
+		return sprintf(buf, "off\n");
 	case BQ2415X_MODE_NONE:
 		return sprintf(buf, "none\n");
 	case BQ2415X_MODE_HOST_CHARGER:
@@ -1523,7 +1533,7 @@ static int bq2415x_probe(struct i2c_client *client,
 		goto error_1;
 	}
 
-	bq = kzalloc(sizeof(*bq), GFP_KERNEL);
+	bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
 	if (!bq) {
 		dev_err(&client->dev, "failed to allocate device data\n");
 		ret = -ENOMEM;
@@ -1536,8 +1546,8 @@ static int bq2415x_probe(struct i2c_client *client,
 	bq->dev = &client->dev;
 	bq->chip = id->driver_data;
 	bq->name = name;
-	bq->mode = BQ2415X_MODE_NONE;
-	bq->reported_mode = BQ2415X_MODE_NONE;
+	bq->mode = BQ2415X_MODE_OFF;
+	bq->reported_mode = BQ2415X_MODE_OFF;
 	bq->autotimer = 0;
 	bq->automode = 0;
 
@@ -1549,19 +1559,19 @@ static int bq2415x_probe(struct i2c_client *client,
 	ret = bq2415x_power_supply_init(bq);
 	if (ret) {
 		dev_err(bq->dev, "failed to register power supply: %d\n", ret);
-		goto error_3;
+		goto error_2;
 	}
 
 	ret = bq2415x_sysfs_init(bq);
 	if (ret) {
 		dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
-		goto error_4;
+		goto error_3;
 	}
 
 	ret = bq2415x_set_defaults(bq);
 	if (ret) {
 		dev_err(bq->dev, "failed to set default values: %d\n", ret);
-		goto error_5;
+		goto error_4;
 	}
 
 	if (bq->init_data.set_mode_hook) {
@@ -1585,12 +1595,10 @@ static int bq2415x_probe(struct i2c_client *client,
 	dev_info(bq->dev, "driver registered\n");
 	return 0;
 
-error_5:
-	bq2415x_sysfs_exit(bq);
 error_4:
-	bq2415x_power_supply_exit(bq);
+	bq2415x_sysfs_exit(bq);
 error_3:
-	kfree(bq);
+	bq2415x_power_supply_exit(bq);
 error_2:
 	kfree(name);
 error_1:
@@ -1622,7 +1630,6 @@ static int bq2415x_remove(struct i2c_client *client)
 	dev_info(bq->dev, "driver unregistered\n");
 
 	kfree(bq->name);
-	kfree(bq);
 
 	return 0;
 }
@@ -1652,18 +1659,7 @@ static struct i2c_driver bq2415x_driver = {
 	.remove = bq2415x_remove,
 	.id_table = bq2415x_i2c_id_table,
 };
-
-static int __init bq2415x_init(void)
-{
-	return i2c_add_driver(&bq2415x_driver);
-}
-module_init(bq2415x_init);
-
-static void __exit bq2415x_exit(void)
-{
-	i2c_del_driver(&bq2415x_driver);
-}
-module_exit(bq2415x_exit);
+module_i2c_driver(bq2415x_driver);
 
 MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
 MODULE_DESCRIPTION("bq2415x charger driver");

+ 5 - 7
drivers/power/bq27x00_battery.c

@@ -299,7 +299,7 @@ static int bq27x00_battery_read_energy(struct bq27x00_device_info *di)
 }
 
 /*
- * Return the battery temperature in tenths of degree Celsius
+ * Return the battery temperature in tenths of degree Kelvin
  * Or < 0 if something fails.
  */
 static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
@@ -312,10 +312,8 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
 		return temp;
 	}
 
-	if (bq27xxx_is_chip_version_higher(di))
-		temp -= 2731;
-	else
-		temp = ((temp * 5) - 5463) / 2;
+	if (!bq27xxx_is_chip_version_higher(di))
+		temp = 5 * temp / 2;
 
 	return temp;
 }
@@ -448,7 +446,6 @@ static void bq27x00_update(struct bq27x00_device_info *di)
 		cache.temperature = bq27x00_battery_read_temperature(di);
 		if (!is_bq27425)
 			cache.cycle_count = bq27x00_battery_read_cyct(di);
-		cache.cycle_count = bq27x00_battery_read_cyct(di);
 		cache.power_avg =
 			bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG);
 
@@ -642,6 +639,8 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
 		ret = bq27x00_simple_value(di->cache.temperature, val);
+		if (ret == 0)
+			val->intval -= 2731;
 		break;
 	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
 		ret = bq27x00_simple_value(di->cache.time_to_empty, val);
@@ -696,7 +695,6 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
 	int ret;
 
 	di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
-	di->chip = BQ27425;
 	if (di->chip == BQ27425) {
 		di->bat.properties = bq27425_battery_props;
 		di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props);

+ 184 - 126
drivers/power/charger-manager.c

@@ -669,15 +669,21 @@ static void _setup_polling(struct work_struct *work)
 	WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
 			    ". try it later. %s\n", __func__);
 
+	/*
+	 * Use mod_delayed_work() iff the next polling interval should
+	 * occur before the currently scheduled one.  If @cm_monitor_work
+	 * isn't active, the end result is the same, so no need to worry
+	 * about stale @next_polling.
+	 */
 	_next_polling = jiffies + polling_jiffy;
 
-	if (!delayed_work_pending(&cm_monitor_work) ||
-	    (delayed_work_pending(&cm_monitor_work) &&
-	     time_after(next_polling, _next_polling))) {
-		next_polling = jiffies + polling_jiffy;
+	if (time_before(_next_polling, next_polling)) {
 		mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
+		next_polling = _next_polling;
+	} else {
+		if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy))
+			next_polling = _next_polling;
 	}
-
 out:
 	mutex_unlock(&cm_list_mtx);
 }
@@ -751,8 +757,7 @@ static void misc_event_handler(struct charger_manager *cm,
 	if (cm_suspended)
 		device_set_wakeup_capable(cm->dev, true);
 
-	if (!delayed_work_pending(&cm_monitor_work) &&
-	    is_polling_required(cm) && cm->desc->polling_interval_ms)
+	if (is_polling_required(cm) && cm->desc->polling_interval_ms)
 		schedule_work(&setup_polling);
 	uevent_notify(cm, default_event_names[type]);
 }
@@ -1170,8 +1175,7 @@ static int charger_extcon_notifier(struct notifier_block *self,
 	 * when charger cable is attached.
 	 */
 	if (cable->attached && is_polling_required(cable->cm)) {
-		if (work_pending(&setup_polling))
-			cancel_work_sync(&setup_polling);
+		cancel_work_sync(&setup_polling);
 		schedule_work(&setup_polling);
 	}
 
@@ -1215,6 +1219,55 @@ static int charger_extcon_init(struct charger_manager *cm,
 	return ret;
 }
 
+/**
+ * charger_manager_register_extcon - Register extcon device to recevie state
+ *				     of charger cable.
+ * @cm: the Charger Manager representing the battery.
+ *
+ * This function support EXTCON(External Connector) subsystem to detect the
+ * state of charger cables for enabling or disabling charger(regulator) and
+ * select the charger cable for charging among a number of external cable
+ * according to policy of H/W board.
+ */
+static int charger_manager_register_extcon(struct charger_manager *cm)
+{
+	struct charger_desc *desc = cm->desc;
+	struct charger_regulator *charger;
+	int ret = 0;
+	int i;
+	int j;
+
+	for (i = 0; i < desc->num_charger_regulators; i++) {
+		charger = &desc->charger_regulators[i];
+
+		charger->consumer = regulator_get(cm->dev,
+					charger->regulator_name);
+		if (charger->consumer == NULL) {
+			dev_err(cm->dev, "Cannot find charger(%s)n",
+					charger->regulator_name);
+			ret = -EINVAL;
+			goto err;
+		}
+		charger->cm = cm;
+
+		for (j = 0; j < charger->num_cables; j++) {
+			struct charger_cable *cable = &charger->cables[j];
+
+			ret = charger_extcon_init(cm, cable);
+			if (ret < 0) {
+				dev_err(cm->dev, "Cannot initialize charger(%s)n",
+						charger->regulator_name);
+				goto err;
+			}
+			cable->charger = charger;
+			cable->cm = cm;
+		}
+	}
+
+err:
+	return ret;
+}
+
 /* help function of sysfs node to control charger(regulator) */
 static ssize_t charger_name_show(struct device *dev,
 				struct device_attribute *attr, char *buf)
@@ -1274,7 +1327,7 @@ static ssize_t charger_externally_control_store(struct device *dev,
 
 	for (i = 0; i < desc->num_charger_regulators; i++) {
 		if (&desc->charger_regulators[i] != charger &&
-			      !desc->charger_regulators[i].externally_control) {
+			!desc->charger_regulators[i].externally_control) {
 			/*
 			 * At least, one charger is controlled by
 			 * charger-manager
@@ -1303,13 +1356,107 @@ static ssize_t charger_externally_control_store(struct device *dev,
 	return count;
 }
 
+/**
+ * charger_manager_register_sysfs - Register sysfs entry for each charger
+ * @cm: the Charger Manager representing the battery.
+ *
+ * This function add sysfs entry for charger(regulator) to control charger from
+ * user-space. If some development board use one more chargers for charging
+ * but only need one charger on specific case which is dependent on user
+ * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/
+ * class/power_supply/battery/charger.[index]/externally_control'. For example,
+ * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/
+ * externally_control, this charger isn't controlled from charger-manager and
+ * always stay off state of regulator.
+ */
+static int charger_manager_register_sysfs(struct charger_manager *cm)
+{
+	struct charger_desc *desc = cm->desc;
+	struct charger_regulator *charger;
+	int chargers_externally_control = 1;
+	char buf[11];
+	char *str;
+	int ret = 0;
+	int i;
+
+	/* Create sysfs entry to control charger(regulator) */
+	for (i = 0; i < desc->num_charger_regulators; i++) {
+		charger = &desc->charger_regulators[i];
+
+		snprintf(buf, 10, "charger.%d", i);
+		str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
+		if (!str) {
+			dev_err(cm->dev, "Cannot allocate memory: %s\n",
+					charger->regulator_name);
+			ret = -ENOMEM;
+			goto err;
+		}
+		strcpy(str, buf);
+
+		charger->attrs[0] = &charger->attr_name.attr;
+		charger->attrs[1] = &charger->attr_state.attr;
+		charger->attrs[2] = &charger->attr_externally_control.attr;
+		charger->attrs[3] = NULL;
+		charger->attr_g.name = str;
+		charger->attr_g.attrs = charger->attrs;
+
+		sysfs_attr_init(&charger->attr_name.attr);
+		charger->attr_name.attr.name = "name";
+		charger->attr_name.attr.mode = 0444;
+		charger->attr_name.show = charger_name_show;
+
+		sysfs_attr_init(&charger->attr_state.attr);
+		charger->attr_state.attr.name = "state";
+		charger->attr_state.attr.mode = 0444;
+		charger->attr_state.show = charger_state_show;
+
+		sysfs_attr_init(&charger->attr_externally_control.attr);
+		charger->attr_externally_control.attr.name
+				= "externally_control";
+		charger->attr_externally_control.attr.mode = 0644;
+		charger->attr_externally_control.show
+				= charger_externally_control_show;
+		charger->attr_externally_control.store
+				= charger_externally_control_store;
+
+		if (!desc->charger_regulators[i].externally_control ||
+				!chargers_externally_control)
+			chargers_externally_control = 0;
+
+		dev_info(cm->dev, "'%s' regulator's externally_control"
+				"is %d\n", charger->regulator_name,
+				charger->externally_control);
+
+		ret = sysfs_create_group(&cm->charger_psy.dev->kobj,
+					&charger->attr_g);
+		if (ret < 0) {
+			dev_err(cm->dev, "Cannot create sysfs entry"
+					"of %s regulator\n",
+					charger->regulator_name);
+			ret = -EINVAL;
+			goto err;
+		}
+	}
+
+	if (chargers_externally_control) {
+		dev_err(cm->dev, "Cannot register regulator because "
+				"charger-manager must need at least "
+				"one charger for charging battery\n");
+
+		ret = -EINVAL;
+		goto err;
+	}
+
+err:
+	return ret;
+}
+
 static int charger_manager_probe(struct platform_device *pdev)
 {
 	struct charger_desc *desc = dev_get_platdata(&pdev->dev);
 	struct charger_manager *cm;
 	int ret = 0, i = 0;
 	int j = 0;
-	int chargers_externally_control = 1;
 	union power_supply_propval val;
 
 	if (g_desc && !rtc_dev && g_desc->rtc_name) {
@@ -1440,11 +1587,10 @@ static int charger_manager_probe(struct platform_device *pdev)
 
 	memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default));
 
-	if (!desc->psy_name) {
+	if (!desc->psy_name)
 		strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
-	} else {
+	else
 		strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
-	}
 	cm->charger_psy.name = cm->psy_name_buf;
 
 	/* Allocate for psy properties because they may vary */
@@ -1496,105 +1642,19 @@ static int charger_manager_probe(struct platform_device *pdev)
 		goto err_register;
 	}
 
-	for (i = 0 ; i < desc->num_charger_regulators ; i++) {
-		struct charger_regulator *charger
-					= &desc->charger_regulators[i];
-		char buf[11];
-		char *str;
-
-		charger->consumer = regulator_get(&pdev->dev,
-					charger->regulator_name);
-		if (charger->consumer == NULL) {
-			dev_err(&pdev->dev, "Cannot find charger(%s)n",
-						charger->regulator_name);
-			ret = -EINVAL;
-			goto err_chg_get;
-		}
-		charger->cm = cm;
-
-		for (j = 0 ; j < charger->num_cables ; j++) {
-			struct charger_cable *cable = &charger->cables[j];
-
-			ret = charger_extcon_init(cm, cable);
-			if (ret < 0) {
-				dev_err(&pdev->dev, "Cannot find charger(%s)n",
-						charger->regulator_name);
-				goto err_extcon;
-			}
-			cable->charger = charger;
-			cable->cm = cm;
-		}
-
-		/* Create sysfs entry to control charger(regulator) */
-		snprintf(buf, 10, "charger.%d", i);
-		str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
-		if (!str) {
-			for (i--; i >= 0; i--) {
-				charger = &desc->charger_regulators[i];
-				kfree(charger->attr_g.name);
-			}
-			ret = -ENOMEM;
-
-			goto err_extcon;
-		}
-		strcpy(str, buf);
-
-		charger->attrs[0] = &charger->attr_name.attr;
-		charger->attrs[1] = &charger->attr_state.attr;
-		charger->attrs[2] = &charger->attr_externally_control.attr;
-		charger->attrs[3] = NULL;
-		charger->attr_g.name = str;
-		charger->attr_g.attrs = charger->attrs;
-
-		sysfs_attr_init(&charger->attr_name.attr);
-		charger->attr_name.attr.name = "name";
-		charger->attr_name.attr.mode = 0444;
-		charger->attr_name.show = charger_name_show;
-
-		sysfs_attr_init(&charger->attr_state.attr);
-		charger->attr_state.attr.name = "state";
-		charger->attr_state.attr.mode = 0444;
-		charger->attr_state.show = charger_state_show;
-
-		sysfs_attr_init(&charger->attr_externally_control.attr);
-		charger->attr_externally_control.attr.name
-				= "externally_control";
-		charger->attr_externally_control.attr.mode = 0644;
-		charger->attr_externally_control.show
-				= charger_externally_control_show;
-		charger->attr_externally_control.store
-				= charger_externally_control_store;
-
-		if (!desc->charger_regulators[i].externally_control ||
-				!chargers_externally_control) {
-			chargers_externally_control = 0;
-		}
-		dev_info(&pdev->dev, "'%s' regulator's externally_control"
-				"is %d\n", charger->regulator_name,
-				charger->externally_control);
-
-		ret = sysfs_create_group(&cm->charger_psy.dev->kobj,
-				&charger->attr_g);
-		if (ret < 0) {
-			dev_info(&pdev->dev, "Cannot create sysfs entry"
-					"of %s regulator\n",
-					charger->regulator_name);
-		}
-	}
-
-	if (chargers_externally_control) {
-		dev_err(&pdev->dev, "Cannot register regulator because "
-				"charger-manager must need at least "
-				"one charger for charging battery\n");
-
-		ret = -EINVAL;
-		goto err_chg_enable;
+	/* Register extcon device for charger cable */
+	ret = charger_manager_register_extcon(cm);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Cannot initialize extcon device\n");
+		goto err_reg_extcon;
 	}
 
-	ret = try_charger_enable(cm, true);
-	if (ret) {
-		dev_err(&pdev->dev, "Cannot enable charger regulators\n");
-		goto err_chg_enable;
+	/* Register sysfs entry for charger(regulator) */
+	ret = charger_manager_register_sysfs(cm);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"Cannot initialize sysfs entry of regulator\n");
+		goto err_reg_sysfs;
 	}
 
 	/* Add to the list */
@@ -1613,27 +1673,28 @@ static int charger_manager_probe(struct platform_device *pdev)
 
 	return 0;
 
-err_chg_enable:
+err_reg_sysfs:
 	for (i = 0; i < desc->num_charger_regulators; i++) {
 		struct charger_regulator *charger;
 
 		charger = &desc->charger_regulators[i];
 		sysfs_remove_group(&cm->charger_psy.dev->kobj,
 				&charger->attr_g);
+
 		kfree(charger->attr_g.name);
 	}
-err_extcon:
-	for (i = 0 ; i < desc->num_charger_regulators ; i++) {
-		struct charger_regulator *charger
-				= &desc->charger_regulators[i];
-		for (j = 0 ; j < charger->num_cables ; j++) {
+err_reg_extcon:
+	for (i = 0; i < desc->num_charger_regulators; i++) {
+		struct charger_regulator *charger;
+
+		charger = &desc->charger_regulators[i];
+		for (j = 0; j < charger->num_cables; j++) {
 			struct charger_cable *cable = &charger->cables[j];
 			extcon_unregister_interest(&cable->extcon_dev);
 		}
-	}
-err_chg_get:
-	for (i = 0 ; i < desc->num_charger_regulators ; i++)
+
 		regulator_put(desc->charger_regulators[i].consumer);
+	}
 
 	power_supply_unregister(&cm->charger_psy);
 err_register:
@@ -1661,10 +1722,8 @@ static int charger_manager_remove(struct platform_device *pdev)
 	list_del(&cm->entry);
 	mutex_unlock(&cm_list_mtx);
 
-	if (work_pending(&setup_polling))
-		cancel_work_sync(&setup_polling);
-	if (delayed_work_pending(&cm_monitor_work))
-		cancel_delayed_work_sync(&cm_monitor_work);
+	cancel_work_sync(&setup_polling);
+	cancel_delayed_work_sync(&cm_monitor_work);
 
 	for (i = 0 ; i < desc->num_charger_regulators ; i++) {
 		struct charger_regulator *charger
@@ -1733,8 +1792,7 @@ static int cm_suspend_prepare(struct device *dev)
 		cm_suspended = true;
 	}
 
-	if (delayed_work_pending(&cm->fullbatt_vchk_work))
-		cancel_delayed_work(&cm->fullbatt_vchk_work);
+	cancel_delayed_work(&cm->fullbatt_vchk_work);
 	cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
 	cm->status_save_batt = is_batt_present(cm);
 

+ 1 - 0
drivers/power/da9030_battery.c

@@ -22,6 +22,7 @@
 
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
+#include <linux/notifier.h>
 
 #define DA9030_FAULT_LOG		0x0a
 #define DA9030_FAULT_LOG_OVER_TEMP	(1 << 7)

+ 1 - 1
drivers/power/da9052-battery.c

@@ -337,7 +337,7 @@ static unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp)
 	if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1])
 		return DA9052_VC_TBL_REF_SZ - 1;
 
-	for (i = 0; i < DA9052_VC_TBL_REF_SZ; i++) {
+	for (i = 0; i < DA9052_VC_TBL_REF_SZ - 1; i++) {
 		if ((adc_temp > vc_tbl_ref[i]) &&
 		    (adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])))
 				return i;

+ 69 - 0
drivers/power/ds2782_battery.c

@@ -7,6 +7,8 @@
  *
  * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
  *
+ * UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
@@ -19,6 +21,7 @@
 #include <linux/errno.h>
 #include <linux/swab.h>
 #include <linux/i2c.h>
+#include <linux/delay.h>
 #include <linux/idr.h>
 #include <linux/power_supply.h>
 #include <linux/slab.h>
@@ -40,6 +43,8 @@
 
 #define DS2786_CURRENT_UNITS	25
 
+#define DS278x_DELAY		1000
+
 struct ds278x_info;
 
 struct ds278x_battery_ops {
@@ -54,8 +59,11 @@ struct ds278x_info {
 	struct i2c_client	*client;
 	struct power_supply	battery;
 	struct ds278x_battery_ops  *ops;
+	struct delayed_work	bat_work;
 	int			id;
 	int                     rsns;
+	int			capacity;
+	int			status;		/* State Of Charge */
 };
 
 static DEFINE_IDR(battery_id);
@@ -220,6 +228,8 @@ static int ds278x_get_status(struct ds278x_info *info, int *status)
 	if (err)
 		return err;
 
+	info->capacity = capacity;
+
 	if (capacity == 100)
 		*status = POWER_SUPPLY_STATUS_FULL;
 	else if (current_uA == 0)
@@ -267,6 +277,27 @@ static int ds278x_battery_get_property(struct power_supply *psy,
 	return ret;
 }
 
+static void ds278x_bat_update(struct ds278x_info *info)
+{
+	int old_status = info->status;
+	int old_capacity = info->capacity;
+
+	ds278x_get_status(info, &info->status);
+
+	if ((old_status != info->status) || (old_capacity != info->capacity))
+		power_supply_changed(&info->battery);
+}
+
+static void ds278x_bat_work(struct work_struct *work)
+{
+	struct ds278x_info *info;
+
+	info = container_of(work, struct ds278x_info, bat_work.work);
+	ds278x_bat_update(info);
+
+	schedule_delayed_work(&info->bat_work, DS278x_DELAY);
+}
+
 static enum power_supply_property ds278x_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_CAPACITY,
@@ -295,10 +326,39 @@ static int ds278x_battery_remove(struct i2c_client *client)
 	idr_remove(&battery_id, info->id);
 	mutex_unlock(&battery_lock);
 
+	cancel_delayed_work(&info->bat_work);
+
 	kfree(info);
 	return 0;
 }
 
+#ifdef CONFIG_PM
+
+static int ds278x_suspend(struct i2c_client *client,
+		pm_message_t state)
+{
+	struct ds278x_info *info = i2c_get_clientdata(client);
+
+	cancel_delayed_work(&info->bat_work);
+	return 0;
+}
+
+static int ds278x_resume(struct i2c_client *client)
+{
+	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 */
+
+
 enum ds278x_num_id {
 	DS2782 = 0,
 	DS2786,
@@ -368,10 +428,17 @@ static int ds278x_battery_probe(struct i2c_client *client,
 	info->ops  = &ds278x_ops[id->driver_data];
 	ds278x_power_supply_init(&info->battery);
 
+	info->capacity = 100;
+	info->status = POWER_SUPPLY_STATUS_FULL;
+
+	INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work);
+
 	ret = power_supply_register(&client->dev, &info->battery);
 	if (ret) {
 		dev_err(&client->dev, "failed to register battery\n");
 		goto fail_register;
+	} else {
+		schedule_delayed_work(&info->bat_work, DS278x_DELAY);
 	}
 
 	return 0;
@@ -401,6 +468,8 @@ static struct i2c_driver ds278x_battery_driver = {
 	},
 	.probe		= ds278x_battery_probe,
 	.remove		= ds278x_battery_remove,
+	.suspend	= ds278x_suspend,
+	.resume		= ds278x_resume,
 	.id_table	= ds278x_id,
 };
 module_i2c_driver(ds278x_battery_driver);

+ 9 - 7
drivers/power/generic-adc-battery.c

@@ -263,9 +263,6 @@ static int gab_probe(struct platform_device *pdev)
 	psy->external_power_changed = gab_ext_power_changed;
 	adc_bat->pdata = pdata;
 
-	/* calculate the total number of channels */
-	chan = ARRAY_SIZE(gab_chan_name);
-
 	/*
 	 * copying the static properties and allocating extra memory for holding
 	 * the extra configurable properties received from platform data.
@@ -291,6 +288,7 @@ static int gab_probe(struct platform_device *pdev)
 						gab_chan_name[chan]);
 		if (IS_ERR(adc_bat->channel[chan])) {
 			ret = PTR_ERR(adc_bat->channel[chan]);
+			adc_bat->channel[chan] = NULL;
 		} else {
 			/* copying properties for supported channels only */
 			memcpy(properties + sizeof(*(psy->properties)) * index,
@@ -344,8 +342,10 @@ err_gpio:
 gpio_req_fail:
 	power_supply_unregister(psy);
 err_reg_fail:
-	for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
-		iio_channel_release(adc_bat->channel[chan]);
+	for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
+		if (adc_bat->channel[chan])
+			iio_channel_release(adc_bat->channel[chan]);
+	}
 second_mem_fail:
 	kfree(psy->properties);
 first_mem_fail:
@@ -365,8 +365,10 @@ static int gab_remove(struct platform_device *pdev)
 		gpio_free(pdata->gpio_charge_finished);
 	}
 
-	for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
-		iio_channel_release(adc_bat->channel[chan]);
+	for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
+		if (adc_bat->channel[chan])
+			iio_channel_release(adc_bat->channel[chan]);
+	}
 
 	kfree(adc_bat->psy.properties);
 	cancel_delayed_work(&adc_bat->bat_work);

+ 236 - 0
drivers/power/goldfish_battery.c

@@ -0,0 +1,236 @@
+/*
+ * Power supply driver for the goldfish emulator
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ * Copyright (C) 2013 Intel, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+struct goldfish_battery_data {
+	void __iomem *reg_base;
+	int irq;
+	spinlock_t lock;
+
+	struct power_supply battery;
+	struct power_supply ac;
+};
+
+#define GOLDFISH_BATTERY_READ(data, addr) \
+	(readl(data->reg_base + addr))
+#define GOLDFISH_BATTERY_WRITE(data, addr, x) \
+	(writel(x, data->reg_base + addr))
+
+/*
+ * Temporary variable used between goldfish_battery_probe() and
+ * goldfish_battery_open().
+ */
+static struct goldfish_battery_data *battery_data;
+
+enum {
+	/* status register */
+	BATTERY_INT_STATUS	    = 0x00,
+	/* set this to enable IRQ */
+	BATTERY_INT_ENABLE	    = 0x04,
+
+	BATTERY_AC_ONLINE       = 0x08,
+	BATTERY_STATUS          = 0x0C,
+	BATTERY_HEALTH          = 0x10,
+	BATTERY_PRESENT         = 0x14,
+	BATTERY_CAPACITY        = 0x18,
+
+	BATTERY_STATUS_CHANGED	= 1U << 0,
+	AC_STATUS_CHANGED	= 1U << 1,
+	BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
+};
+
+
+static int goldfish_ac_get_property(struct power_supply *psy,
+			enum power_supply_property psp,
+			union power_supply_propval *val)
+{
+	struct goldfish_battery_data *data = container_of(psy,
+		struct goldfish_battery_data, ac);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int goldfish_battery_get_property(struct power_supply *psy,
+				 enum power_supply_property psp,
+				 union power_supply_propval *val)
+{
+	struct goldfish_battery_data *data = container_of(psy,
+		struct goldfish_battery_data, battery);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property goldfish_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property goldfish_ac_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
+{
+	unsigned long irq_flags;
+	struct goldfish_battery_data *data = dev_id;
+	uint32_t status;
+
+	spin_lock_irqsave(&data->lock, irq_flags);
+
+	/* read status flags, which will clear the interrupt */
+	status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
+	status &= BATTERY_INT_MASK;
+
+	if (status & BATTERY_STATUS_CHANGED)
+		power_supply_changed(&data->battery);
+	if (status & AC_STATUS_CHANGED)
+		power_supply_changed(&data->ac);
+
+	spin_unlock_irqrestore(&data->lock, irq_flags);
+	return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+
+static int goldfish_battery_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *r;
+	struct goldfish_battery_data *data;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (data == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&data->lock);
+
+	data->battery.properties = goldfish_battery_props;
+	data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
+	data->battery.get_property = goldfish_battery_get_property;
+	data->battery.name = "battery";
+	data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+
+	data->ac.properties = goldfish_ac_props;
+	data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props);
+	data->ac.get_property = goldfish_ac_get_property;
+	data->ac.name = "ac";
+	data->ac.type = POWER_SUPPLY_TYPE_MAINS;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "platform_get_resource failed\n");
+		return -ENODEV;
+	}
+
+	data->reg_base = devm_ioremap(&pdev->dev, r->start, r->end - r->start + 1);
+	if (data->reg_base == NULL) {
+		dev_err(&pdev->dev, "unable to remap MMIO\n");
+		return -ENOMEM;
+	}
+
+	data->irq = platform_get_irq(pdev, 0);
+	if (data->irq < 0) {
+		dev_err(&pdev->dev, "platform_get_irq failed\n");
+		return -ENODEV;
+	}
+
+	ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt,
+						IRQF_SHARED, pdev->name, data);
+	if (ret)
+		return ret;
+
+	ret = power_supply_register(&pdev->dev, &data->ac);
+	if (ret)
+		return ret;
+
+	ret = power_supply_register(&pdev->dev, &data->battery);
+	if (ret) {
+		power_supply_unregister(&data->ac);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, data);
+	battery_data = data;
+
+	GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
+	return 0;
+}
+
+static int goldfish_battery_remove(struct platform_device *pdev)
+{
+	struct goldfish_battery_data *data = platform_get_drvdata(pdev);
+
+	power_supply_unregister(&data->battery);
+	power_supply_unregister(&data->ac);
+	battery_data = NULL;
+	return 0;
+}
+
+static struct platform_driver goldfish_battery_device = {
+	.probe		= goldfish_battery_probe,
+	.remove		= goldfish_battery_remove,
+	.driver = {
+		.name = "goldfish-battery"
+	}
+};
+module_platform_driver(goldfish_battery_device);
+
+MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");

+ 4 - 4
drivers/power/lp8727_charger.c

@@ -367,28 +367,28 @@ static int lp8727_battery_get_property(struct power_supply *psy,
 			return -EINVAL;
 
 		if (pdata->get_batt_present)
-			val->intval = pchg->pdata->get_batt_present();
+			val->intval = pdata->get_batt_present();
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 		if (!pdata)
 			return -EINVAL;
 
 		if (pdata->get_batt_level)
-			val->intval = pchg->pdata->get_batt_level();
+			val->intval = pdata->get_batt_level();
 		break;
 	case POWER_SUPPLY_PROP_CAPACITY:
 		if (!pdata)
 			return -EINVAL;
 
 		if (pdata->get_batt_capacity)
-			val->intval = pchg->pdata->get_batt_capacity();
+			val->intval = pdata->get_batt_capacity();
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
 		if (!pdata)
 			return -EINVAL;
 
 		if (pdata->get_batt_temp)
-			val->intval = pchg->pdata->get_batt_temp();
+			val->intval = pdata->get_batt_temp();
 		break;
 	default:
 		break;

+ 9 - 8
drivers/power/lp8788-charger.c

@@ -367,7 +367,8 @@ static inline bool lp8788_is_valid_charger_register(u8 addr)
 	return addr >= LP8788_CHG_START && addr <= LP8788_CHG_END;
 }
 
-static int lp8788_update_charger_params(struct lp8788_charger *pchg)
+static int lp8788_update_charger_params(struct platform_device *pdev,
+					struct lp8788_charger *pchg)
 {
 	struct lp8788 *lp = pchg->lp;
 	struct lp8788_charger_platform_data *pdata = pchg->pdata;
@@ -376,7 +377,7 @@ static int lp8788_update_charger_params(struct lp8788_charger *pchg)
 	int ret;
 
 	if (!pdata || !pdata->chg_params) {
-		dev_info(lp->dev, "skip updating charger parameters\n");
+		dev_info(&pdev->dev, "skip updating charger parameters\n");
 		return 0;
 	}
 
@@ -537,7 +538,6 @@ err_free_irq:
 static int lp8788_irq_register(struct platform_device *pdev,
 				struct lp8788_charger *pchg)
 {
-	struct lp8788 *lp = pchg->lp;
 	const char *name[] = {
 		LP8788_CHG_IRQ, LP8788_PRSW_IRQ, LP8788_BATT_IRQ
 	};
@@ -550,13 +550,13 @@ static int lp8788_irq_register(struct platform_device *pdev,
 	for (i = 0; i < ARRAY_SIZE(name); i++) {
 		ret = lp8788_set_irqs(pdev, pchg, name[i]);
 		if (ret) {
-			dev_warn(lp->dev, "irq setup failed: %s\n", name[i]);
+			dev_warn(&pdev->dev, "irq setup failed: %s\n", name[i]);
 			return ret;
 		}
 	}
 
 	if (pchg->num_irqs > LP8788_MAX_CHG_IRQS) {
-		dev_err(lp->dev, "invalid total number of irqs: %d\n",
+		dev_err(&pdev->dev, "invalid total number of irqs: %d\n",
 			pchg->num_irqs);
 		return -EINVAL;
 	}
@@ -690,9 +690,10 @@ static int lp8788_charger_probe(struct platform_device *pdev)
 {
 	struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
 	struct lp8788_charger *pchg;
+	struct device *dev = &pdev->dev;
 	int ret;
 
-	pchg = devm_kzalloc(lp->dev, sizeof(struct lp8788_charger), GFP_KERNEL);
+	pchg = devm_kzalloc(dev, sizeof(struct lp8788_charger), GFP_KERNEL);
 	if (!pchg)
 		return -ENOMEM;
 
@@ -700,7 +701,7 @@ static int lp8788_charger_probe(struct platform_device *pdev)
 	pchg->pdata = lp->pdata ? lp->pdata->chg_pdata : NULL;
 	platform_set_drvdata(pdev, pchg);
 
-	ret = lp8788_update_charger_params(pchg);
+	ret = lp8788_update_charger_params(pdev, pchg);
 	if (ret)
 		return ret;
 
@@ -718,7 +719,7 @@ static int lp8788_charger_probe(struct platform_device *pdev)
 
 	ret = lp8788_irq_register(pdev, pchg);
 	if (ret)
-		dev_warn(lp->dev, "failed to register charger irq: %d\n", ret);
+		dev_warn(dev, "failed to register charger irq: %d\n", ret);
 
 	return 0;
 }

+ 1 - 3
drivers/power/max17040_battery.c

@@ -207,7 +207,7 @@ static int max17040_probe(struct i2c_client *client,
 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
 		return -EIO;
 
-	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip)
 		return -ENOMEM;
 
@@ -225,7 +225,6 @@ static int max17040_probe(struct i2c_client *client,
 	ret = power_supply_register(&client->dev, &chip->battery);
 	if (ret) {
 		dev_err(&client->dev, "failed: power supply register\n");
-		kfree(chip);
 		return ret;
 	}
 
@@ -244,7 +243,6 @@ static int max17040_remove(struct i2c_client *client)
 
 	power_supply_unregister(&chip->battery);
 	cancel_delayed_work(&chip->work);
-	kfree(chip);
 	return 0;
 }
 

+ 1088 - 0
drivers/power/pm2301_charger.c

@@ -0,0 +1,1088 @@
+/*
+ * Copyright 2012 ST Ericsson.
+ *
+ * Power supply driver for ST Ericsson pm2xxx_charger charger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/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 "pm2301_charger.h"
+
+#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
+		struct pm2xxx_charger, ac_chg)
+
+static int pm2xxx_interrupt_registers[] = {
+	PM2XXX_REG_INT1,
+	PM2XXX_REG_INT2,
+	PM2XXX_REG_INT3,
+	PM2XXX_REG_INT4,
+	PM2XXX_REG_INT5,
+	PM2XXX_REG_INT6,
+};
+
+static enum power_supply_property pm2xxx_charger_ac_props[] = {
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_AVG,
+};
+
+static int pm2xxx_charger_voltage_map[] = {
+	3500,
+	3525,
+	3550,
+	3575,
+	3600,
+	3625,
+	3650,
+	3675,
+	3700,
+	3725,
+	3750,
+	3775,
+	3800,
+	3825,
+	3850,
+	3875,
+	3900,
+	3925,
+	3950,
+	3975,
+	4000,
+	4025,
+	4050,
+	4075,
+	4100,
+	4125,
+	4150,
+	4175,
+	4200,
+	4225,
+	4250,
+	4275,
+	4300,
+};
+
+static int pm2xxx_charger_current_map[] = {
+	200,
+	200,
+	400,
+	600,
+	800,
+	1000,
+	1200,
+	1400,
+	1600,
+	1800,
+	2000,
+	2200,
+	2400,
+	2600,
+	2800,
+	3000,
+};
+
+static const struct i2c_device_id pm2xxx_ident[] = {
+	{ "pm2301", 0 },
+	{ }
+};
+
+static void set_lpn_pin(struct pm2xxx_charger *pm2)
+{
+	if (pm2->ac.charger_connected)
+		return;
+	gpio_set_value(pm2->lpn_pin, 1);
+
+	return;
+}
+
+static void clear_lpn_pin(struct pm2xxx_charger *pm2)
+{
+	if (pm2->ac.charger_connected)
+		return;
+	gpio_set_value(pm2->lpn_pin, 0);
+
+	return;
+}
+
+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);
+
+	ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
+				1, val);
+	if (ret < 0)
+		dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
+	else
+		ret = 0;
+	clear_lpn_pin(pm2);
+	mutex_unlock(&pm2->lock);
+
+	return ret;
+}
+
+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);
+
+	ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
+				1, &val);
+	if (ret < 0)
+		dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
+	else
+		ret = 0;
+	clear_lpn_pin(pm2);
+	mutex_unlock(&pm2->lock);
+
+	return ret;
+}
+
+static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2)
+{
+	int ret;
+
+	/* Enable charging */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
+			(PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA));
+
+	return ret;
+}
+
+static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2)
+{
+	int ret;
+
+	/* Disable charging */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
+			(PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS));
+
+	return ret;
+}
+
+static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
+{
+	queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
+
+	return 0;
+}
+
+
+int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
+{
+	queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
+
+	return 0;
+}
+
+static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val)
+{
+	int ret = 0;
+
+	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);
+	}
+
+out:
+	return ret;
+}
+
+static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
+{
+	dev_dbg(pm2->dev , "20 minutes watchdog occured\n");
+
+	pm2->ac.wd_expired = true;
+	power_supply_changed(&pm2->ac_chg.psy);
+
+	return 0;
+}
+
+static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val)
+{
+	switch (val) {
+	case PM2XXX_INT1_ITVBATLOWR:
+		dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n");
+		break;
+
+	case PM2XXX_INT1_ITVBATLOWF:
+		dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n");
+		break;
+
+	default:
+		dev_err(pm2->dev, "Unknown VBAT level\n");
+	}
+
+	return 0;
+}
+
+static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val)
+{
+	dev_dbg(pm2->dev, "battery disconnected\n");
+
+	return 0;
+}
+
+static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
+{
+	int ret;
+
+	ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val);
+
+	if (ret < 0) {
+		dev_err(pm2->dev, "Charger detection failed\n");
+		goto out;
+	}
+
+	*val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG);
+
+out:
+	return ret;
+}
+
+static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val)
+{
+
+	int ret;
+	u8 read_val;
+
+	/*
+	 * Since we can't be sure that the events are received
+	 * synchronously, we have the check if the main charger is
+	 * connected by reading the interrupt source register.
+	 */
+	ret = pm2xxx_charger_detection(pm2, &read_val);
+
+	if ((ret == 0) && read_val) {
+		pm2->ac.charger_connected = 1;
+		pm2->ac_conn = true;
+		queue_work(pm2->charger_wq, &pm2->ac_work);
+	}
+
+
+	return ret;
+}
+
+static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2,
+								int val)
+{
+	pm2->ac.charger_connected = 0;
+	queue_work(pm2->charger_wq, &pm2->ac_work);
+
+	return 0;
+}
+
+static int pm2_int_reg0(void *pm2_data, int val)
+{
+	struct pm2xxx_charger *pm2 = pm2_data;
+	int ret = 0;
+
+	if (val & (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
+		ret = pm2xxx_charger_vbat_lsig_mngt(pm2, val &
+			(PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF));
+	}
+
+	if (val & PM2XXX_INT1_ITVBATDISCONNECT) {
+		ret = pm2xxx_charger_bat_disc_mngt(pm2,
+				PM2XXX_INT1_ITVBATDISCONNECT);
+	}
+
+	return ret;
+}
+
+static int pm2_int_reg1(void *pm2_data, int val)
+{
+	struct pm2xxx_charger *pm2 = pm2_data;
+	int ret = 0;
+
+	if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
+		dev_dbg(pm2->dev , "Main charger plugged\n");
+		ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val &
+			(PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG));
+	}
+
+	if (val &
+		(PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) {
+		dev_dbg(pm2->dev , "Main charger unplugged\n");
+		ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val &
+						(PM2XXX_INT2_ITVPWR1UNPLUG |
+						PM2XXX_INT2_ITVPWR2UNPLUG));
+	}
+
+	return ret;
+}
+
+static int pm2_int_reg2(void *pm2_data, int val)
+{
+	struct pm2xxx_charger *pm2 = pm2_data;
+	int ret = 0;
+
+	if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD)
+		ret = pm2xxx_charger_wd_exp_mngt(pm2, val);
+
+	if (val & (PM2XXX_INT3_ITCHPRECHARGEWD |
+				PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) {
+		dev_dbg(pm2->dev,
+			"Watchdog occured for precharge, CC and CV charge\n");
+	}
+
+	return ret;
+}
+
+static int pm2_int_reg3(void *pm2_data, int val)
+{
+	struct pm2xxx_charger *pm2 = pm2_data;
+	int ret = 0;
+
+	if (val & (PM2XXX_INT4_ITCHARGINGON)) {
+		dev_dbg(pm2->dev ,
+			"chargind operation has started\n");
+	}
+
+	if (val & (PM2XXX_INT4_ITVRESUME)) {
+		dev_dbg(pm2->dev,
+			"battery discharged down to VResume threshold\n");
+	}
+
+	if (val & (PM2XXX_INT4_ITBATTFULL)) {
+		dev_dbg(pm2->dev , "battery fully detected\n");
+	}
+
+	if (val & (PM2XXX_INT4_ITCVPHASE)) {
+		dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n");
+	}
+
+	if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
+		pm2->failure_case = VPWR_OVV;
+		ret = pm2xxx_charger_ovv_mngt(pm2, val &
+			(PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV));
+		dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n");
+	}
+
+	if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD |
+				PM2XXX_INT4_S_ITBATTEMPHOT)) {
+		ret = pm2xxx_charger_batt_therm_mngt(pm2, val &
+			(PM2XXX_INT4_S_ITBATTEMPCOLD |
+			PM2XXX_INT4_S_ITBATTEMPHOT));
+		dev_dbg(pm2->dev, "BTEMP is too Low/High\n");
+	}
+
+	return ret;
+}
+
+static int pm2_int_reg4(void *pm2_data, int val)
+{
+	struct pm2xxx_charger *pm2 = pm2_data;
+	int ret = 0;
+
+	if (val & PM2XXX_INT5_ITVSYSTEMOVV) {
+		pm2->failure_case = VSYSTEM_OVV;
+		ret = pm2xxx_charger_ovv_mngt(pm2, val &
+						PM2XXX_INT5_ITVSYSTEMOVV);
+		dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n");
+	}
+
+	if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL |
+				PM2XXX_INT5_ITTHERMALWARNINGRISE |
+				PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
+				PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) {
+		dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n");
+		ret = pm2xxx_charger_die_therm_mngt(pm2, val &
+			(PM2XXX_INT5_ITTHERMALWARNINGFALL |
+			PM2XXX_INT5_ITTHERMALWARNINGRISE |
+			PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
+			PM2XXX_INT5_ITTHERMALSHUTDOWNRISE));
+	}
+
+	return ret;
+}
+
+static int pm2_int_reg5(void *pm2_data, int val)
+{
+	struct pm2xxx_charger *pm2 = pm2_data;
+	int ret = 0;
+
+
+	if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
+		dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
+	}
+
+	if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE |
+			PM2XXX_INT6_ITVPWR1VALIDRISE |
+			PM2XXX_INT6_ITVPWR2VALIDFALL |
+			PM2XXX_INT6_ITVPWR1VALIDFALL)) {
+		dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n");
+	}
+
+	return ret;
+}
+
+static irqreturn_t  pm2xxx_irq_int(int irq, void *data)
+{
+	struct pm2xxx_charger *pm2 = data;
+	struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
+	int i;
+
+	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]);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2)
+{
+	int ret = 0;
+	u8 val;
+
+	if (pm2->ac.charger_connected && pm2->ac.charger_online) {
+
+		ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val);
+		if (ret < 0) {
+			dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
+			goto out;
+		}
+
+		if (val & PM2XXX_INT4_S_ITCVPHASE)
+			ret = PM2XXX_CONST_VOLT;
+		else
+			ret = PM2XXX_CONST_CURR;
+	}
+out:
+	return ret;
+}
+
+static int pm2xxx_current_to_regval(int curr)
+{
+	int i;
+
+	if (curr < pm2xxx_charger_current_map[0])
+		return 0;
+
+	for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) {
+		if (curr < pm2xxx_charger_current_map[i])
+			return (i - 1);
+	}
+
+	i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1;
+	if (curr == pm2xxx_charger_current_map[i])
+		return i;
+	else
+		return -EINVAL;
+}
+
+static int pm2xxx_voltage_to_regval(int curr)
+{
+	int i;
+
+	if (curr < pm2xxx_charger_voltage_map[0])
+		return 0;
+
+	for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) {
+		if (curr < pm2xxx_charger_voltage_map[i])
+			return i - 1;
+	}
+
+	i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1;
+	if (curr == pm2xxx_charger_voltage_map[i])
+		return i;
+	else
+		return -EINVAL;
+}
+
+static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger,
+		int ich_out)
+{
+	int ret;
+	int curr_index;
+	struct pm2xxx_charger *pm2;
+	u8 val;
+
+	if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+		pm2 = to_pm2xxx_charger_ac_device_info(charger);
+	else
+		return -ENXIO;
+
+	curr_index = pm2xxx_current_to_regval(ich_out);
+	if (curr_index < 0) {
+		dev_err(pm2->dev,
+			"Charger current too high, charging not started\n");
+		return -ENXIO;
+	}
+
+	ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
+	if (ret >= 0) {
+		val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK;
+		val |= curr_index;
+		ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val);
+		if (ret < 0) {
+			dev_err(pm2->dev,
+				"%s write failed\n", __func__);
+		}
+	}
+	else
+		dev_err(pm2->dev, "%s read failed\n", __func__);
+
+	return ret;
+}
+
+static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
+	enum power_supply_property psp,
+	union power_supply_propval *val)
+{
+	struct pm2xxx_charger *pm2;
+
+	pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy));
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_HEALTH:
+		if (pm2->flags.mainextchnotok)
+			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+		else if (pm2->ac.wd_expired)
+			val->intval = POWER_SUPPLY_HEALTH_DEAD;
+		else if (pm2->flags.main_thermal_prot)
+			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+		else
+			val->intval = POWER_SUPPLY_HEALTH_GOOD;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = pm2->ac.charger_online;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = pm2->ac.charger_connected;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+		pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2);
+		val->intval = pm2->ac.cv_active;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
+{
+	int ret = 0;
+
+	/* enable CC and CV watchdog */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3,
+		(PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN));
+	if( ret < 0)
+		return ret;
+
+	/* enable precharge watchdog */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4,
+					PM2XXX_CH_WD_PRECH_PHASE_60MIN);
+
+	/* Disable auto timeout */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG5,
+					PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN);
+
+	/*
+     * EOC current level = 100mA
+	 * Precharge current level = 100mA
+	 * CC current level = 1000mA
+	 */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6,
+		(PM2XXX_DIR_CH_CC_CURRENT_1000MA |
+		PM2XXX_CH_PRECH_CURRENT_100MA |
+		PM2XXX_CH_EOC_CURRENT_100MA));
+
+	/*
+     * recharge threshold = 3.8V
+	 * Precharge to CC threshold = 2.9V
+	 */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG7,
+		(PM2XXX_CH_PRECH_VOL_2_9 | PM2XXX_CH_VRESUME_VOL_3_8));
+
+	/* float voltage charger level = 4.2V */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8,
+		PM2XXX_CH_VOLT_4_2);
+
+	/* Voltage drop between VBAT and VSYS in HW charging = 300mV */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG9,
+		(PM2XXX_CH_150MV_DROP_300MV | PM2XXX_CHARCHING_INFO_DIS |
+		PM2XXX_CH_CC_REDUCED_CURRENT_IDENT |
+		PM2XXX_CH_CC_MODEDROP_DIS));
+
+	/* Input charger level of over voltage = 10V */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR2,
+					PM2XXX_VPWR2_OVV_10);
+	ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR1,
+					PM2XXX_VPWR1_OVV_10);
+
+	/* Input charger drop */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR2,
+		(PM2XXX_VPWR2_HW_OPT_DIS | PM2XXX_VPWR2_VALID_DIS |
+		PM2XXX_VPWR2_DROP_DIS));
+	ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR1,
+		(PM2XXX_VPWR1_HW_OPT_DIS | PM2XXX_VPWR1_VALID_DIS |
+		PM2XXX_VPWR1_DROP_DIS));
+
+	/* Disable battery low monitoring */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG,
+		PM2XXX_VBAT_LOW_MONITORING_ENA);
+
+	/* Disable LED */
+	ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
+		PM2XXX_LED_SELECT_DIS);
+
+	return ret;
+}
+
+static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
+	int enable, int vset, int iset)
+{
+	int ret;
+	int volt_index;
+	int curr_index;
+	u8 val;
+
+	struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger);
+
+	if (enable) {
+		if (!pm2->ac.charger_connected) {
+			dev_dbg(pm2->dev, "AC charger not connected\n");
+			return -ENXIO;
+		}
+
+		dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset);
+		if (!pm2->vddadc_en_ac) {
+			regulator_enable(pm2->regu);
+			pm2->vddadc_en_ac = true;
+		}
+
+		ret = pm2xxx_charging_init(pm2);
+		if (ret < 0) {
+			dev_err(pm2->dev, "%s charging init failed\n",
+					__func__);
+			goto error_occured;
+		}
+
+		volt_index = pm2xxx_voltage_to_regval(vset);
+		curr_index = pm2xxx_current_to_regval(iset);
+
+		if (volt_index < 0 || curr_index < 0) {
+			dev_err(pm2->dev,
+				"Charger voltage or current too high, "
+				"charging not started\n");
+			return -ENXIO;
+		}
+
+		ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val);
+		if (ret < 0) {
+			dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
+			goto error_occured;
+		}
+		val &= ~PM2XXX_CH_VOLT_MASK;
+		val |= volt_index;
+		ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val);
+		if (ret < 0) {
+			dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
+			goto error_occured;
+		}
+
+		ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
+		if (ret < 0) {
+			dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
+			goto error_occured;
+		}
+		val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK;
+		val |= curr_index;
+		ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val);
+		if (ret < 0) {
+			dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__);
+			goto error_occured;
+		}
+
+		if (!pm2->bat->enable_overshoot) {
+			ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val);
+			if (ret < 0) {
+				dev_err(pm2->dev, "%s pm2xxx read failed\n",
+								__func__);
+				goto error_occured;
+			}
+			val |= PM2XXX_ANTI_OVERSHOOT_EN;
+			ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, val);
+			if (ret < 0) {
+				dev_err(pm2->dev, "%s pm2xxx write failed\n",
+								__func__);
+				goto error_occured;
+			}
+		}
+
+		ret = pm2xxx_charging_enable_mngt(pm2);
+		if (ret < 0) {
+			dev_err(pm2->dev, "Failed to enable"
+						"pm2xxx ac charger\n");
+			goto error_occured;
+		}
+
+		pm2->ac.charger_online = 1;
+	} else {
+		pm2->ac.charger_online = 0;
+		pm2->ac.wd_expired = false;
+
+		/* Disable regulator if enabled */
+		if (pm2->vddadc_en_ac) {
+			regulator_disable(pm2->regu);
+			pm2->vddadc_en_ac = false;
+		}
+
+		ret = pm2xxx_charging_disable_mngt(pm2);
+		if (ret < 0) {
+			dev_err(pm2->dev, "failed to disable"
+						"pm2xxx ac charger\n");
+			goto error_occured;
+		}
+
+		dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n");
+	}
+	power_supply_changed(&pm2->ac_chg.psy);
+
+error_occured:
+	return ret;
+}
+
+static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger)
+{
+	int ret;
+	struct pm2xxx_charger *pm2;
+
+	if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+		pm2 = to_pm2xxx_charger_ac_device_info(charger);
+	else
+		return -ENXIO;
+
+	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER);
+	if (ret)
+		dev_err(pm2->dev, "Failed to kick WD!\n");
+
+	return ret;
+}
+
+static void pm2xxx_charger_ac_work(struct work_struct *work)
+{
+	struct pm2xxx_charger *pm2 = container_of(work,
+		struct pm2xxx_charger, ac_work);
+
+
+	power_supply_changed(&pm2->ac_chg.psy);
+	sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
+};
+
+static void pm2xxx_charger_check_main_thermal_prot_work(
+	struct work_struct *work)
+{
+};
+
+static struct pm2xxx_interrupts pm2xxx_int = {
+	.handler[0] = pm2_int_reg0,
+	.handler[1] = pm2_int_reg1,
+	.handler[2] = pm2_int_reg2,
+	.handler[3] = pm2_int_reg3,
+	.handler[4] = pm2_int_reg4,
+	.handler[5] = pm2_int_reg5,
+};
+
+static struct pm2xxx_irq pm2xxx_charger_irq[] = {
+	{"PM2XXX_IRQ_INT", pm2xxx_irq_int},
+};
+
+static int pm2xxx_wall_charger_resume(struct i2c_client *i2c_client)
+{
+	return 0;
+}
+
+static int pm2xxx_wall_charger_suspend(struct i2c_client *i2c_client,
+	pm_message_t state)
+{
+	return 0;
+}
+
+static int __devinit 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;
+
+	pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL);
+	if (!pm2) {
+		dev_err(pm2->dev, "pm2xxx_charger allocation failed\n");
+		return -ENOMEM;
+	}
+
+	/* get parent data */
+	pm2->dev = &i2c_client->dev;
+	pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+
+	pm2->pm2_int = &pm2xxx_int;
+
+	/* get charger spcific platform data */
+	if (!pl_data->wall_charger) {
+		dev_err(pm2->dev, "no charger platform data supplied\n");
+		ret = -EINVAL;
+		goto free_device_info;
+	}
+
+	pm2->pdata = pl_data->wall_charger;
+
+	/* get battery specific platform data */
+	if (!pl_data->battery) {
+		dev_err(pm2->dev, "no battery platform data supplied\n");
+		ret = -EINVAL;
+		goto free_device_info;
+	}
+
+	pm2->bat = pl_data->battery;
+
+	/*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)) {
+		ret = -ENODEV;
+		dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n");
+		goto free_device_info;
+	}
+
+	pm2->config.pm2xxx_i2c = i2c_client;
+	pm2->config.pm2xxx_id = (struct i2c_device_id *) id;
+	i2c_set_clientdata(i2c_client, pm2);
+
+	/* AC supply */
+	/* power_supply base class */
+	pm2->ac_chg.psy.name = pm2->pdata->label;
+	pm2->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS;
+	pm2->ac_chg.psy.properties = pm2xxx_charger_ac_props;
+	pm2->ac_chg.psy.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props);
+	pm2->ac_chg.psy.get_property = pm2xxx_charger_ac_get_property;
+	pm2->ac_chg.psy.supplied_to = pm2->pdata->supplied_to;
+	pm2->ac_chg.psy.num_supplicants = pm2->pdata->num_supplicants;
+	/* pm2xxx_charger sub-class */
+	pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en;
+	pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick;
+	pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current;
+	pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[
+		ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1];
+	pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[
+		ARRAY_SIZE(pm2xxx_charger_current_map) - 1];
+	pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL;
+	pm2->ac_chg.enabled = true;
+	pm2->ac_chg.external = true;
+
+	/* Create a work queue for the charger */
+	pm2->charger_wq =
+		create_singlethread_workqueue("pm2xxx_charger_wq");
+	if (pm2->charger_wq == NULL) {
+		dev_err(pm2->dev, "failed to create work queue\n");
+		goto free_device_info;
+	}
+
+	/* Init work for charger detection */
+	INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work);
+
+	/* Init work for checking HW status */
+	INIT_WORK(&pm2->check_main_thermal_prot_work,
+		pm2xxx_charger_check_main_thermal_prot_work);
+
+	/*
+	 * VDD ADC supply needs to be enabled from this driver when there
+	 * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
+	 * interrupts during charging
+	 */
+	pm2->regu = regulator_get(pm2->dev, "vddadc");
+	if (IS_ERR(pm2->regu)) {
+		ret = PTR_ERR(pm2->regu);
+		dev_err(pm2->dev, "failed to get vddadc regulator\n");
+		goto free_charger_wq;
+	}
+
+	/* Register AC charger class */
+	ret = power_supply_register(pm2->dev, &pm2->ac_chg.psy);
+	if (ret) {
+		dev_err(pm2->dev, "failed to register AC charger\n");
+		goto free_regulator;
+	}
+
+	/* Register interrupts */
+	ret = request_threaded_irq(pm2->pdata->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);
+		goto unregister_pm2xxx_charger;
+	}
+
+	/*Initialize lock*/
+	mutex_init(&pm2->lock);
+
+	/*
+	 * 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;
+	}
+	ret = gpio_direction_output(pm2->lpn_pin, 0);
+	if (ret < 0) {
+		dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n");
+		goto free_gpio;
+	}
+
+	ret = pm2xxx_charger_detection(pm2, &val);
+
+	if ((ret == 0) && val) {
+		pm2->ac.charger_connected = 1;
+		pm2->ac_conn = true;
+		power_supply_changed(&pm2->ac_chg.psy);
+		sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
+	}
+
+	return 0;
+
+free_gpio:
+	gpio_free(pm2->lpn_pin);
+unregister_pm2xxx_charger:
+	/* unregister power supply */
+	power_supply_unregister(&pm2->ac_chg.psy);
+free_regulator:
+	/* disable the regulator */
+	regulator_put(pm2->regu);
+free_charger_wq:
+	destroy_workqueue(pm2->charger_wq);
+free_device_info:
+	kfree(pm2);
+	return ret;
+}
+
+static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
+{
+	struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client);
+
+	/* Disable AC charging */
+	pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0);
+
+	/* Disable interrupts */
+	free_irq(pm2->pdata->irq_number, pm2);
+
+	/* Delete the work queue */
+	destroy_workqueue(pm2->charger_wq);
+
+	flush_scheduled_work();
+
+	/* disable the regulator */
+	regulator_put(pm2->regu);
+
+	power_supply_unregister(&pm2->ac_chg.psy);
+
+	/*Free GPIO60*/
+	gpio_free(pm2->lpn_pin);
+
+	kfree(pm2);
+
+	return 0;
+}
+
+static const struct i2c_device_id pm2xxx_id[] = {
+	{ "pm2301", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
+
+static struct i2c_driver pm2xxx_charger_driver = {
+	.probe = pm2xxx_wall_charger_probe,
+	.remove = __devexit_p(pm2xxx_wall_charger_remove),
+	.suspend = pm2xxx_wall_charger_suspend,
+	.resume = pm2xxx_wall_charger_resume,
+	.driver = {
+		.name = "pm2xxx-wall_charger",
+		.owner = THIS_MODULE,
+	},
+	.id_table = pm2xxx_id,
+};
+
+static int __init pm2xxx_charger_init(void)
+{
+	return i2c_add_driver(&pm2xxx_charger_driver);
+}
+
+static void __exit pm2xxx_charger_exit(void)
+{
+	i2c_del_driver(&pm2xxx_charger_driver);
+}
+
+subsys_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");
+

+ 513 - 0
drivers/power/pm2301_charger.h

@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * PM2301 power supply interface
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef PM2301_CHARGER_H
+#define PM2301_CHARGER_H
+
+#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)
+
+#define PM2XXX_NUM_INT_REG		0x6
+
+/* Constant voltage/current */
+#define PM2XXX_CONST_CURR		0x0
+#define PM2XXX_CONST_VOLT		0x1
+
+/* Lowest charger voltage is 3.39V -> 0x4E */
+#define LOW_VOLT_REG			0x4E
+
+#define PM2XXX_BATT_CTRL_REG1		0x00
+#define PM2XXX_BATT_CTRL_REG2		0x01
+#define PM2XXX_BATT_CTRL_REG3		0x02
+#define PM2XXX_BATT_CTRL_REG4		0x03
+#define PM2XXX_BATT_CTRL_REG5		0x04
+#define PM2XXX_BATT_CTRL_REG6		0x05
+#define PM2XXX_BATT_CTRL_REG7		0x06
+#define PM2XXX_BATT_CTRL_REG8		0x07
+#define PM2XXX_NTC_CTRL_REG1		0x08
+#define PM2XXX_NTC_CTRL_REG2		0x09
+#define PM2XXX_BATT_CTRL_REG9		0x0A
+#define PM2XXX_BATT_STAT_REG1		0x0B
+#define PM2XXX_INP_VOLT_VPWR2		0x11
+#define PM2XXX_INP_DROP_VPWR2		0x13
+#define PM2XXX_INP_VOLT_VPWR1		0x15
+#define PM2XXX_INP_DROP_VPWR1		0x17
+#define PM2XXX_INP_MODE_VPWR		0x18
+#define PM2XXX_BATT_WD_KICK		0x70
+#define PM2XXX_DEV_VER_STAT		0x0C
+#define PM2XXX_THERM_WARN_CTRL_REG	0x20
+#define PM2XXX_BATT_DISC_REG		0x21
+#define PM2XXX_BATT_LOW_LEV_COMP_REG	0x22
+#define PM2XXX_BATT_LOW_LEV_VAL_REG	0x23
+#define PM2XXX_I2C_PAD_CTRL_REG		0x24
+#define PM2XXX_SW_CTRL_REG		0x26
+#define PM2XXX_LED_CTRL_REG		0x28
+
+#define PM2XXX_REG_INT1			0x40
+#define PM2XXX_MASK_REG_INT1		0x50
+#define PM2XXX_SRCE_REG_INT1		0x60
+#define PM2XXX_REG_INT2			0x41
+#define PM2XXX_MASK_REG_INT2		0x51
+#define PM2XXX_SRCE_REG_INT2		0x61
+#define PM2XXX_REG_INT3			0x42
+#define PM2XXX_MASK_REG_INT3		0x52
+#define PM2XXX_SRCE_REG_INT3		0x62
+#define PM2XXX_REG_INT4			0x43
+#define PM2XXX_MASK_REG_INT4		0x53
+#define PM2XXX_SRCE_REG_INT4		0x63
+#define PM2XXX_REG_INT5			0x44
+#define PM2XXX_MASK_REG_INT5		0x54
+#define PM2XXX_SRCE_REG_INT5		0x64
+#define PM2XXX_REG_INT6			0x45
+#define PM2XXX_MASK_REG_INT6		0x55
+#define PM2XXX_SRCE_REG_INT6		0x65
+
+#define VPWR_OVV			0x0
+#define VSYSTEM_OVV			0x1
+
+/* control Reg 1 */
+#define PM2XXX_CH_RESUME_EN		0x1
+#define PM2XXX_CH_RESUME_DIS		0x0
+
+/* control Reg 2 */
+#define PM2XXX_CH_AUTO_RESUME_EN	0X2
+#define PM2XXX_CH_AUTO_RESUME_DIS	0X0
+#define PM2XXX_CHARGER_ENA		0x4
+#define PM2XXX_CHARGER_DIS		0x0
+
+/* control Reg 3 */
+#define PM2XXX_CH_WD_CC_PHASE_OFF	0x0
+#define PM2XXX_CH_WD_CC_PHASE_5MIN	0x1
+#define PM2XXX_CH_WD_CC_PHASE_10MIN	0x2
+#define PM2XXX_CH_WD_CC_PHASE_30MIN	0x3
+#define PM2XXX_CH_WD_CC_PHASE_60MIN	0x4
+#define PM2XXX_CH_WD_CC_PHASE_120MIN	0x5
+#define PM2XXX_CH_WD_CC_PHASE_240MIN	0x6
+#define PM2XXX_CH_WD_CC_PHASE_360MIN	0x7
+
+#define PM2XXX_CH_WD_CV_PHASE_OFF	(0x0<<3)
+#define PM2XXX_CH_WD_CV_PHASE_5MIN	(0x1<<3)
+#define PM2XXX_CH_WD_CV_PHASE_10MIN	(0x2<<3)
+#define PM2XXX_CH_WD_CV_PHASE_30MIN	(0x3<<3)
+#define PM2XXX_CH_WD_CV_PHASE_60MIN	(0x4<<3)
+#define PM2XXX_CH_WD_CV_PHASE_120MIN	(0x5<<3)
+#define PM2XXX_CH_WD_CV_PHASE_240MIN	(0x6<<3)
+#define PM2XXX_CH_WD_CV_PHASE_360MIN	(0x7<<3)
+
+/* control Reg 4 */
+#define PM2XXX_CH_WD_PRECH_PHASE_OFF	0x0
+#define PM2XXX_CH_WD_PRECH_PHASE_1MIN	0x1
+#define PM2XXX_CH_WD_PRECH_PHASE_5MIN	0x2
+#define PM2XXX_CH_WD_PRECH_PHASE_10MIN	0x3
+#define PM2XXX_CH_WD_PRECH_PHASE_30MIN	0x4
+#define PM2XXX_CH_WD_PRECH_PHASE_60MIN	0x5
+#define PM2XXX_CH_WD_PRECH_PHASE_120MIN	0x6
+#define PM2XXX_CH_WD_PRECH_PHASE_240MIN	0x7
+
+/* control Reg 5 */
+#define PM2XXX_CH_WD_AUTO_TIMEOUT_NONE	0x0
+#define PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN	0x1
+
+/* control Reg 6 */
+#define PM2XXX_DIR_CH_CC_CURRENT_MASK	0x0F
+#define PM2XXX_DIR_CH_CC_CURRENT_200MA	0x0
+#define PM2XXX_DIR_CH_CC_CURRENT_400MA	0x2
+#define PM2XXX_DIR_CH_CC_CURRENT_600MA	0x3
+#define PM2XXX_DIR_CH_CC_CURRENT_800MA	0x4
+#define PM2XXX_DIR_CH_CC_CURRENT_1000MA	0x5
+#define PM2XXX_DIR_CH_CC_CURRENT_1200MA	0x6
+#define PM2XXX_DIR_CH_CC_CURRENT_1400MA	0x7
+#define PM2XXX_DIR_CH_CC_CURRENT_1600MA	0x8
+#define PM2XXX_DIR_CH_CC_CURRENT_1800MA	0x9
+#define PM2XXX_DIR_CH_CC_CURRENT_2000MA	0xA
+#define PM2XXX_DIR_CH_CC_CURRENT_2200MA	0xB
+#define PM2XXX_DIR_CH_CC_CURRENT_2400MA	0xC
+#define PM2XXX_DIR_CH_CC_CURRENT_2600MA	0xD
+#define PM2XXX_DIR_CH_CC_CURRENT_2800MA	0xE
+#define PM2XXX_DIR_CH_CC_CURRENT_3000MA	0xF
+
+#define PM2XXX_CH_PRECH_CURRENT_MASK	0x30
+#define PM2XXX_CH_PRECH_CURRENT_25MA	(0x0<<4)
+#define PM2XXX_CH_PRECH_CURRENT_50MA	(0x1<<4)
+#define PM2XXX_CH_PRECH_CURRENT_75MA	(0x2<<4)
+#define PM2XXX_CH_PRECH_CURRENT_100MA	(0x3<<4)
+
+#define PM2XXX_CH_EOC_CURRENT_MASK	0xC0
+#define PM2XXX_CH_EOC_CURRENT_100MA	(0x0<<6)
+#define PM2XXX_CH_EOC_CURRENT_150MA	(0x1<<6)
+#define PM2XXX_CH_EOC_CURRENT_300MA	(0x2<<6)
+#define PM2XXX_CH_EOC_CURRENT_400MA	(0x3<<6)
+
+/* control Reg 7 */
+#define PM2XXX_CH_PRECH_VOL_2_5		0x0
+#define PM2XXX_CH_PRECH_VOL_2_7		0x1
+#define PM2XXX_CH_PRECH_VOL_2_9		0x2
+#define PM2XXX_CH_PRECH_VOL_3_1		0x3
+
+#define PM2XXX_CH_VRESUME_VOL_3_2	(0x0<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_4	(0x1<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_6	(0x2<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_8	(0x3<<2)
+
+/* control Reg 8 */
+#define PM2XXX_CH_VOLT_MASK		0x3F
+#define PM2XXX_CH_VOLT_3_5		0x0
+#define PM2XXX_CH_VOLT_3_5225		0x1
+#define PM2XXX_CH_VOLT_3_6		0x4
+#define PM2XXX_CH_VOLT_3_7		0x8
+#define PM2XXX_CH_VOLT_4_0		0x14
+#define PM2XXX_CH_VOLT_4_175		0x1B
+#define PM2XXX_CH_VOLT_4_2		0x1C
+#define PM2XXX_CH_VOLT_4_275		0x1F
+#define PM2XXX_CH_VOLT_4_3		0x20
+
+/*NTC control register 1*/
+#define PM2XXX_BTEMP_HIGH_TH_45		0x0
+#define PM2XXX_BTEMP_HIGH_TH_50		0x1
+#define PM2XXX_BTEMP_HIGH_TH_55		0x2
+#define PM2XXX_BTEMP_HIGH_TH_60		0x3
+#define PM2XXX_BTEMP_HIGH_TH_65		0x4
+
+#define PM2XXX_BTEMP_LOW_TH_N5		(0x0<<3)
+#define PM2XXX_BTEMP_LOW_TH_0		(0x1<<3)
+#define PM2XXX_BTEMP_LOW_TH_5		(0x2<<3)
+#define PM2XXX_BTEMP_LOW_TH_10		(0x3<<3)
+
+/*NTC control register 2*/
+#define PM2XXX_NTC_BETA_COEFF_3477	0x0
+#define PM2XXX_NTC_BETA_COEFF_3964	0x1
+
+#define PM2XXX_NTC_RES_10K		(0x0<<2)
+#define PM2XXX_NTC_RES_47K		(0x1<<2)
+#define PM2XXX_NTC_RES_100K		(0x2<<2)
+#define PM2XXX_NTC_RES_NO_NTC		(0x3<<2)
+
+/* control Reg 9 */
+#define PM2XXX_CH_CC_MODEDROP_EN	1
+#define PM2XXX_CH_CC_MODEDROP_DIS	0
+
+#define PM2XXX_CH_CC_REDUCED_CURRENT_100MA	(0x0<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_200MA	(0x1<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_400MA	(0x2<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_IDENT	(0x3<<1)
+
+#define PM2XXX_CHARCHING_INFO_DIS	(0<<3)
+#define PM2XXX_CHARCHING_INFO_EN	(1<<3)
+
+#define PM2XXX_CH_150MV_DROP_300MV	(0<<4)
+#define PM2XXX_CH_150MV_DROP_150MV	(1<<4)
+
+
+/* charger status register */
+#define PM2XXX_CHG_STATUS_OFF		0x0
+#define PM2XXX_CHG_STATUS_ON		0x1
+#define PM2XXX_CHG_STATUS_FULL		0x2
+#define PM2XXX_CHG_STATUS_ERR		0x3
+#define PM2XXX_CHG_STATUS_WAIT		0x4
+#define PM2XXX_CHG_STATUS_NOBAT		0x5
+
+/* Input charger voltage VPWR2 */
+#define PM2XXX_VPWR2_OVV_6_0		0x0
+#define PM2XXX_VPWR2_OVV_6_3		0x1
+#define PM2XXX_VPWR2_OVV_10		0x2
+#define PM2XXX_VPWR2_OVV_NONE		0x3
+
+/* Input charger drop VPWR2 */
+#define PM2XXX_VPWR2_HW_OPT_EN		(0x1<<4)
+#define PM2XXX_VPWR2_HW_OPT_DIS		(0x0<<4)
+
+#define PM2XXX_VPWR2_VALID_EN		(0x1<<3)
+#define PM2XXX_VPWR2_VALID_DIS		(0x0<<3)
+
+#define PM2XXX_VPWR2_DROP_EN		(0x1<<2)
+#define PM2XXX_VPWR2_DROP_DIS		(0x0<<2)
+
+/* Input charger voltage VPWR1 */
+#define PM2XXX_VPWR1_OVV_6_0		0x0
+#define PM2XXX_VPWR1_OVV_6_3		0x1
+#define PM2XXX_VPWR1_OVV_10		0x2
+#define PM2XXX_VPWR1_OVV_NONE		0x3
+
+/* Input charger drop VPWR1 */
+#define PM2XXX_VPWR1_HW_OPT_EN		(0x1<<4)
+#define PM2XXX_VPWR1_HW_OPT_DIS		(0x0<<4)
+
+#define PM2XXX_VPWR1_VALID_EN		(0x1<<3)
+#define PM2XXX_VPWR1_VALID_DIS		(0x0<<3)
+
+#define PM2XXX_VPWR1_DROP_EN		(0x1<<2)
+#define PM2XXX_VPWR1_DROP_DIS		(0x0<<2)
+
+/* Battery low level comparator control register */
+#define PM2XXX_VBAT_LOW_MONITORING_DIS	0x0
+#define PM2XXX_VBAT_LOW_MONITORING_ENA	0x1
+
+/* Battery low level value control register */
+#define PM2XXX_VBAT_LOW_LEVEL_2_3	0x0
+#define PM2XXX_VBAT_LOW_LEVEL_2_4	0x1
+#define PM2XXX_VBAT_LOW_LEVEL_2_5	0x2
+#define PM2XXX_VBAT_LOW_LEVEL_2_6	0x3
+#define PM2XXX_VBAT_LOW_LEVEL_2_7	0x4
+#define PM2XXX_VBAT_LOW_LEVEL_2_8	0x5
+#define PM2XXX_VBAT_LOW_LEVEL_2_9	0x6
+#define PM2XXX_VBAT_LOW_LEVEL_3_0	0x7
+#define PM2XXX_VBAT_LOW_LEVEL_3_1	0x8
+#define PM2XXX_VBAT_LOW_LEVEL_3_2	0x9
+#define PM2XXX_VBAT_LOW_LEVEL_3_3	0xA
+#define PM2XXX_VBAT_LOW_LEVEL_3_4	0xB
+#define PM2XXX_VBAT_LOW_LEVEL_3_5	0xC
+#define PM2XXX_VBAT_LOW_LEVEL_3_6	0xD
+#define PM2XXX_VBAT_LOW_LEVEL_3_7	0xE
+#define PM2XXX_VBAT_LOW_LEVEL_3_8	0xF
+#define PM2XXX_VBAT_LOW_LEVEL_3_9	0x10
+#define PM2XXX_VBAT_LOW_LEVEL_4_0	0x11
+#define PM2XXX_VBAT_LOW_LEVEL_4_1	0x12
+#define PM2XXX_VBAT_LOW_LEVEL_4_2	0x13
+
+/* SW CTRL */
+#define PM2XXX_SWCTRL_HW		0x0
+#define PM2XXX_SWCTRL_SW		0x1
+
+
+/* LED Driver Control */
+#define PM2XXX_LED_CURRENT_MASK		0x0C
+#define PM2XXX_LED_CURRENT_2_5MA	(0X0<<2)
+#define PM2XXX_LED_CURRENT_1MA		(0X1<<2)
+#define PM2XXX_LED_CURRENT_5MA		(0X2<<2)
+#define PM2XXX_LED_CURRENT_10MA		(0X3<<2)
+
+#define PM2XXX_LED_SELECT_MASK		0x02
+#define PM2XXX_LED_SELECT_EN		(0X0<<1)
+#define PM2XXX_LED_SELECT_DIS		(0X1<<1)
+
+#define PM2XXX_ANTI_OVERSHOOT_MASK	0x01
+#define PM2XXX_ANTI_OVERSHOOT_DIS	0X0
+#define PM2XXX_ANTI_OVERSHOOT_EN	0X1
+
+enum pm2xxx_reg_int1 {
+	PM2XXX_INT1_ITVBATDISCONNECT	= 0x02,
+	PM2XXX_INT1_ITVBATLOWR		= 0x04,
+	PM2XXX_INT1_ITVBATLOWF		= 0x08,
+};
+
+enum pm2xxx_mask_reg_int1 {
+	PM2XXX_INT1_M_ITVBATDISCONNECT	= 0x02,
+	PM2XXX_INT1_M_ITVBATLOWR	= 0x04,
+	PM2XXX_INT1_M_ITVBATLOWF	= 0x08,
+};
+
+enum pm2xxx_source_reg_int1 {
+	PM2XXX_INT1_S_ITVBATDISCONNECT	= 0x02,
+	PM2XXX_INT1_S_ITVBATLOWR	= 0x04,
+	PM2XXX_INT1_S_ITVBATLOWF	= 0x08,
+};
+
+enum pm2xxx_reg_int2 {
+	PM2XXX_INT2_ITVPWR2PLUG		= 0x01,
+	PM2XXX_INT2_ITVPWR2UNPLUG	= 0x02,
+	PM2XXX_INT2_ITVPWR1PLUG		= 0x04,
+	PM2XXX_INT2_ITVPWR1UNPLUG	= 0x08,
+};
+
+enum pm2xxx_mask_reg_int2 {
+	PM2XXX_INT2_M_ITVPWR2PLUG	= 0x01,
+	PM2XXX_INT2_M_ITVPWR2UNPLUG	= 0x02,
+	PM2XXX_INT2_M_ITVPWR1PLUG	= 0x04,
+	PM2XXX_INT2_M_ITVPWR1UNPLUG	= 0x08,
+};
+
+enum pm2xxx_source_reg_int2 {
+	PM2XXX_INT2_S_ITVPWR2PLUG	= 0x03,
+	PM2XXX_INT2_S_ITVPWR1PLUG	= 0x0c,
+};
+
+enum pm2xxx_reg_int3 {
+	PM2XXX_INT3_ITCHPRECHARGEWD	= 0x01,
+	PM2XXX_INT3_ITCHCCWD		= 0x02,
+	PM2XXX_INT3_ITCHCVWD		= 0x04,
+	PM2XXX_INT3_ITAUTOTIMEOUTWD	= 0x08,
+};
+
+enum pm2xxx_mask_reg_int3 {
+	PM2XXX_INT3_M_ITCHPRECHARGEWD	= 0x01,
+	PM2XXX_INT3_M_ITCHCCWD		= 0x02,
+	PM2XXX_INT3_M_ITCHCVWD		= 0x04,
+	PM2XXX_INT3_M_ITAUTOTIMEOUTWD	= 0x08,
+};
+
+enum pm2xxx_source_reg_int3 {
+	PM2XXX_INT3_S_ITCHPRECHARGEWD	= 0x01,
+	PM2XXX_INT3_S_ITCHCCWD		= 0x02,
+	PM2XXX_INT3_S_ITCHCVWD		= 0x04,
+	PM2XXX_INT3_S_ITAUTOTIMEOUTWD	= 0x08,
+};
+
+enum pm2xxx_reg_int4 {
+	PM2XXX_INT4_ITBATTEMPCOLD	= 0x01,
+	PM2XXX_INT4_ITBATTEMPHOT	= 0x02,
+	PM2XXX_INT4_ITVPWR2OVV		= 0x04,
+	PM2XXX_INT4_ITVPWR1OVV		= 0x08,
+	PM2XXX_INT4_ITCHARGINGON	= 0x10,
+	PM2XXX_INT4_ITVRESUME		= 0x20,
+	PM2XXX_INT4_ITBATTFULL		= 0x40,
+	PM2XXX_INT4_ITCVPHASE		= 0x80,
+};
+
+enum pm2xxx_mask_reg_int4 {
+	PM2XXX_INT4_M_ITBATTEMPCOLD	= 0x01,
+	PM2XXX_INT4_M_ITBATTEMPHOT	= 0x02,
+	PM2XXX_INT4_M_ITVPWR2OVV	= 0x04,
+	PM2XXX_INT4_M_ITVPWR1OVV	= 0x08,
+	PM2XXX_INT4_M_ITCHARGINGON	= 0x10,
+	PM2XXX_INT4_M_ITVRESUME		= 0x20,
+	PM2XXX_INT4_M_ITBATTFULL	= 0x40,
+	PM2XXX_INT4_M_ITCVPHASE		= 0x80,
+};
+
+enum pm2xxx_source_reg_int4 {
+	PM2XXX_INT4_S_ITBATTEMPCOLD	= 0x01,
+	PM2XXX_INT4_S_ITBATTEMPHOT	= 0x02,
+	PM2XXX_INT4_S_ITVPWR2OVV	= 0x04,
+	PM2XXX_INT4_S_ITVPWR1OVV	= 0x08,
+	PM2XXX_INT4_S_ITCHARGINGON	= 0x10,
+	PM2XXX_INT4_S_ITVRESUME		= 0x20,
+	PM2XXX_INT4_S_ITBATTFULL	= 0x40,
+	PM2XXX_INT4_S_ITCVPHASE		= 0x80,
+};
+
+enum pm2xxx_reg_int5 {
+	PM2XXX_INT5_ITTHERMALSHUTDOWNRISE	= 0x01,
+	PM2XXX_INT5_ITTHERMALSHUTDOWNFALL	= 0x02,
+	PM2XXX_INT5_ITTHERMALWARNINGRISE	= 0x04,
+	PM2XXX_INT5_ITTHERMALWARNINGFALL	= 0x08,
+	PM2XXX_INT5_ITVSYSTEMOVV		= 0x10,
+};
+
+enum pm2xxx_mask_reg_int5 {
+	PM2XXX_INT5_M_ITTHERMALSHUTDOWNRISE	= 0x01,
+	PM2XXX_INT5_M_ITTHERMALSHUTDOWNFALL	= 0x02,
+	PM2XXX_INT5_M_ITTHERMALWARNINGRISE	= 0x04,
+	PM2XXX_INT5_M_ITTHERMALWARNINGFALL	= 0x08,
+	PM2XXX_INT5_M_ITVSYSTEMOVV		= 0x10,
+};
+
+enum pm2xxx_source_reg_int5 {
+	PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE	= 0x01,
+	PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL	= 0x02,
+	PM2XXX_INT5_S_ITTHERMALWARNINGRISE	= 0x04,
+	PM2XXX_INT5_S_ITTHERMALWARNINGFALL	= 0x08,
+	PM2XXX_INT5_S_ITVSYSTEMOVV		= 0x10,
+};
+
+enum pm2xxx_reg_int6 {
+	PM2XXX_INT6_ITVPWR2DROP		= 0x01,
+	PM2XXX_INT6_ITVPWR1DROP		= 0x02,
+	PM2XXX_INT6_ITVPWR2VALIDRISE	= 0x04,
+	PM2XXX_INT6_ITVPWR2VALIDFALL	= 0x08,
+	PM2XXX_INT6_ITVPWR1VALIDRISE	= 0x10,
+	PM2XXX_INT6_ITVPWR1VALIDFALL	= 0x20,
+};
+
+enum pm2xxx_mask_reg_int6 {
+	PM2XXX_INT6_M_ITVPWR2DROP	= 0x01,
+	PM2XXX_INT6_M_ITVPWR1DROP	= 0x02,
+	PM2XXX_INT6_M_ITVPWR2VALIDRISE	= 0x04,
+	PM2XXX_INT6_M_ITVPWR2VALIDFALL	= 0x08,
+	PM2XXX_INT6_M_ITVPWR1VALIDRISE	= 0x10,
+	PM2XXX_INT6_M_ITVPWR1VALIDFALL	= 0x20,
+};
+
+enum pm2xxx_source_reg_int6 {
+	PM2XXX_INT6_S_ITVPWR2DROP	= 0x01,
+	PM2XXX_INT6_S_ITVPWR1DROP	= 0x02,
+	PM2XXX_INT6_S_ITVPWR2VALIDRISE	= 0x04,
+	PM2XXX_INT6_S_ITVPWR2VALIDFALL	= 0x08,
+	PM2XXX_INT6_S_ITVPWR1VALIDRISE	= 0x10,
+	PM2XXX_INT6_S_ITVPWR1VALIDFALL	= 0x20,
+};
+
+struct pm2xxx_charger_info {
+	int charger_connected;
+	int charger_online;
+	int cv_active;
+	bool wd_expired;
+};
+
+struct pm2xxx_charger_event_flags {
+	bool mainextchnotok;
+	bool main_thermal_prot;
+	bool ovv;
+	bool chgwdexp;
+};
+
+struct pm2xxx_interrupts {
+	u8 reg[PM2XXX_NUM_INT_REG];
+	int (*handler[PM2XXX_NUM_INT_REG])(void *, int);
+};
+
+struct pm2xxx_config {
+	struct i2c_client *pm2xxx_i2c;
+	struct i2c_device_id *pm2xxx_id;
+};
+
+struct pm2xxx_irq {
+	char *name;
+	irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct pm2xxx_charger {
+	struct device *dev;
+	u8 chip_id;
+	bool vddadc_en_ac;
+	struct pm2xxx_config config;
+	bool ac_conn;
+	unsigned int gpio_irq;
+	int vbat;
+	int old_vbat;
+	int failure_case;
+	int failure_input_ovv;
+	unsigned int lpn_pin;
+	struct pm2xxx_interrupts *pm2_int;
+	struct ab8500_gpadc *gpadc;
+	struct regulator *regu;
+	struct pm2xxx_bm_data *bat;
+	struct mutex lock;
+	struct ab8500 *parent;
+	struct pm2xxx_charger_info ac;
+	struct pm2xxx_charger_platform_data *pdata;
+	struct workqueue_struct *charger_wq;
+	struct delayed_work check_vbat_work;
+	struct work_struct ac_work;
+	struct work_struct check_main_thermal_prot_work;
+	struct ux500_charger ac_chg;
+	struct pm2xxx_charger_event_flags flags;
+};
+
+#endif /* PM2301_CHARGER_H */

+ 2 - 1
drivers/power/power_supply_sysfs.c

@@ -55,7 +55,8 @@ static ssize_t power_supply_show_property(struct device *dev,
 	};
 	static char *health_text[] = {
 		"Unknown", "Good", "Overheat", "Dead", "Over voltage",
-		"Unspecified failure", "Cold",
+		"Unspecified failure", "Cold", "Watchdog timer expire",
+		"Safety timer expire"
 	};
 	static char *technology_text[] = {
 		"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",

+ 17 - 0
drivers/power/reset/Kconfig

@@ -13,3 +13,20 @@ config POWER_RESET_GPIO
 	  This driver supports turning off your board via a GPIO line.
 	  If your board needs a GPIO high/low to power down, say Y and
 	  create a binding in your devicetree.
+
+config POWER_RESET_QNAP
+	bool "QNAP power-off driver"
+	depends on OF_GPIO && POWER_RESET && PLAT_ORION
+	help
+	  This driver supports turning off QNAP NAS devices by sending
+	  commands to the microcontroller which controls the main power.
+
+	  Say Y if you have a QNAP NAS.
+
+config POWER_RESET_RESTART
+	bool "Restart power-off driver"
+	depends on ARM
+	help
+	  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.

+ 2 - 0
drivers/power/reset/Makefile

@@ -1 +1,3 @@
 obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
+obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
+obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o

+ 116 - 0
drivers/power/reset/qnap-poweroff.c

@@ -0,0 +1,116 @@
+/*
+ * QNAP Turbo NAS Board power off
+ *
+ * Copyright (C) 2012 Andrew Lunn <andrew@lunn.ch>
+ *
+ * Based on the code from:
+ *
+ * Copyright (C) 2009  Martin Michlmayr <tbm@cyrius.com>
+ * Copyright (C) 2008  Byron Bradley <byron.bbradley@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial_reg.h>
+#include <linux/kallsyms.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#define UART1_REG(x)	(base + ((UART_##x) << 2))
+
+static void __iomem *base;
+static unsigned long tclk;
+
+static void qnap_power_off(void)
+{
+	/* 19200 baud divisor */
+	const unsigned divisor = ((tclk + (8 * 19200)) / (16 * 19200));
+
+	pr_err("%s: triggering power-off...\n", __func__);
+
+	/* hijack UART1 and reset into sane state (19200,8n1) */
+	writel(0x83, UART1_REG(LCR));
+	writel(divisor & 0xff, UART1_REG(DLL));
+	writel((divisor >> 8) & 0xff, UART1_REG(DLM));
+	writel(0x03, UART1_REG(LCR));
+	writel(0x00, UART1_REG(IER));
+	writel(0x00, UART1_REG(FCR));
+	writel(0x00, UART1_REG(MCR));
+
+	/* send the power-off command 'A' to PIC */
+	writel('A', UART1_REG(TX));
+}
+
+static int qnap_power_off_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct clk *clk;
+	char symname[KSYM_NAME_LEN];
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Missing resource");
+		return -EINVAL;
+	}
+
+	base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!base) {
+		dev_err(&pdev->dev, "Unable to map resource");
+		return -EINVAL;
+	}
+
+	/* We need to know tclk in order to calculate the UART divisor */
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Clk missing");
+		return PTR_ERR(clk);
+	}
+
+	tclk = clk_get_rate(clk);
+
+	/* Check that nothing else has already setup a handler */
+	if (pm_power_off) {
+		lookup_symbol_name((ulong)pm_power_off, symname);
+		dev_err(&pdev->dev,
+			"pm_power_off already claimed %p %s",
+			pm_power_off, symname);
+		return -EBUSY;
+	}
+	pm_power_off = qnap_power_off;
+
+	return 0;
+}
+
+static int qnap_power_off_remove(struct platform_device *pdev)
+{
+	pm_power_off = NULL;
+	return 0;
+}
+
+static const struct of_device_id qnap_power_off_of_match_table[] = {
+	{ .compatible = "qnap,power-off", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table);
+
+static struct platform_driver qnap_power_off_driver = {
+	.probe	= qnap_power_off_probe,
+	.remove	= qnap_power_off_remove,
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "qnap_power_off",
+		.of_match_table = of_match_ptr(qnap_power_off_of_match_table),
+	},
+};
+module_platform_driver(qnap_power_off_driver);
+
+MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
+MODULE_DESCRIPTION("QNAP Power off driver");
+MODULE_LICENSE("GPL v2");

+ 65 - 0
drivers/power/reset/restart-poweroff.c

@@ -0,0 +1,65 @@
+/*
+ * Power off by restarting and let u-boot keep hold of the machine
+ * until the user presses a button for example.
+ *
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * Copyright (C) 2012 Andrew Lunn
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <asm/system_misc.h>
+
+static void restart_poweroff_do_poweroff(void)
+{
+	arm_pm_restart('h', NULL);
+}
+
+static int restart_poweroff_probe(struct platform_device *pdev)
+{
+	/* If a pm_power_off function has already been added, leave it alone */
+	if (pm_power_off != NULL) {
+		dev_err(&pdev->dev,
+			"pm_power_off function already registered");
+		return -EBUSY;
+	}
+
+	pm_power_off = &restart_poweroff_do_poweroff;
+	return 0;
+}
+
+static int restart_poweroff_remove(struct platform_device *pdev)
+{
+	if (pm_power_off == &restart_poweroff_do_poweroff)
+		pm_power_off = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id of_restart_poweroff_match[] = {
+	{ .compatible = "restart-poweroff", },
+	{},
+};
+
+static struct platform_driver restart_poweroff_driver = {
+	.probe = restart_poweroff_probe,
+	.remove = restart_poweroff_remove,
+	.driver = {
+		.name = "poweroff-restart",
+		.owner = THIS_MODULE,
+		.of_match_table = of_restart_poweroff_match,
+	},
+};
+module_platform_driver(restart_poweroff_driver);
+
+MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch");
+MODULE_DESCRIPTION("restart poweroff driver");
+MODULE_LICENSE("GPLv2");
+MODULE_ALIAS("platform:poweroff-restart");

+ 10 - 5
include/linux/mfd/abx500.h

@@ -131,7 +131,7 @@ struct abx500_maxim_parameters {
  * @nominal_voltage:		Nominal voltage of the battery in mV
  * @termination_vol:		max voltage upto which battery can be charged
  * @termination_curr		battery charging termination current in mA
- * @recharge_vol		battery voltage limit that will trigger a new
+ * @recharge_cap		battery capacity limit that will trigger a new
  *				full charging cycle in the case where maintenan-
  *				-ce charging has been disabled
  * @normal_cur_lvl:		charger current in normal state in mA
@@ -160,7 +160,7 @@ struct abx500_battery_type {
 	int nominal_voltage;
 	int termination_vol;
 	int termination_curr;
-	int recharge_vol;
+	int recharge_cap;
 	int normal_cur_lvl;
 	int normal_vol_lvl;
 	int maint_a_cur_lvl;
@@ -224,6 +224,7 @@ struct abx500_bm_charger_parameters {
  * @bkup_bat_v		voltage which we charge the backup battery with
  * @bkup_bat_i		current which we charge the backup battery with
  * @no_maintenance	indicates that maintenance charging is disabled
+ * @capacity_scaling    indicates whether capacity scaling is to be used
  * @abx500_adc_therm	placement of thermistor, batctrl or battemp adc
  * @chg_unknown_bat	flag to enable charging of unknown batteries
  * @enable_overshoot	flag to enable VBAT overshoot control
@@ -253,7 +254,11 @@ struct abx500_bm_data {
 	int usb_safety_tmr_h;
 	int bkup_bat_v;
 	int bkup_bat_i;
+	bool autopower_cfg;
+	bool ac_enabled;
+	bool usb_enabled;
 	bool no_maintenance;
+	bool capacity_scaling;
 	bool chg_unknown_bat;
 	bool enable_overshoot;
 	bool auto_trig;
@@ -277,9 +282,9 @@ enum {
 	NTC_INTERNAL,
 };
 
-int bmdevs_of_probe(struct device *dev,
-		struct device_node *np,
-		struct abx500_bm_data **battery);
+int ab8500_bm_of_probe(struct device *dev,
+		       struct device_node *np,
+		       struct abx500_bm_data *bm);
 
 int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
 	u8 value);

+ 6 - 31
include/linux/mfd/abx500/ab8500-bm.h

@@ -23,6 +23,7 @@
  * Bank : 0x5
  */
 #define AB8500_USB_LINE_STAT_REG	0x80
+#define AB8500_USB_LINK1_STAT_REG	0x94
 
 /*
  * Charger / status register offfsets
@@ -225,6 +226,8 @@
 /* BatCtrl Current Source Constants */
 #define BAT_CTRL_7U_ENA			0x01
 #define BAT_CTRL_20U_ENA		0x02
+#define BAT_CTRL_18U_ENA		0x01
+#define BAT_CTRL_16U_ENA		0x02
 #define BAT_CTRL_CMP_ENA		0x04
 #define FORCE_BAT_CTRL_CMP_HIGH		0x08
 #define BAT_CTRL_PULL_UP_ENA		0x10
@@ -355,6 +358,7 @@ struct ab8500_bm_charger_parameters {
  * @bkup_bat_v		voltage which we charge the backup battery with
  * @bkup_bat_i		current which we charge the backup battery with
  * @no_maintenance	indicates that maintenance charging is disabled
+ * @capacity_scaling    indicates whether capacity scaling is to be used
  * @adc_therm		placement of thermistor, batctrl or battemp adc
  * @chg_unknown_bat	flag to enable charging of unknown batteries
  * @enable_overshoot	flag to enable VBAT overshoot control
@@ -383,6 +387,7 @@ struct ab8500_bm_data {
 	int bkup_bat_v;
 	int bkup_bat_i;
 	bool no_maintenance;
+	bool capacity_scaling;
 	bool chg_unknown_bat;
 	bool enable_overshoot;
 	enum abx500_adc_therm adc_therm;
@@ -399,26 +404,6 @@ struct ab8500_bm_data {
 	const struct ab8500_fg_parameters *fg_params;
 };
 
-struct ab8500_charger_platform_data {
-	char **supplied_to;
-	size_t num_supplicants;
-	bool autopower_cfg;
-};
-
-struct ab8500_btemp_platform_data {
-	char **supplied_to;
-	size_t num_supplicants;
-};
-
-struct ab8500_fg_platform_data {
-	char **supplied_to;
-	size_t num_supplicants;
-};
-
-struct ab8500_chargalg_platform_data {
-	char **supplied_to;
-	size_t num_supplicants;
-};
 struct ab8500_btemp;
 struct ab8500_gpadc;
 struct ab8500_fg;
@@ -434,20 +419,10 @@ 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);
 int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
+int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
 int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
 
 #else
 static struct abx500_bm_data ab8500_bm_data;
-
-static inline int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
-{
-	return -ENODEV;
-}
-
-static inline int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
-{
-	return -ENODEV;
-}
-
 #endif
 #endif /* _AB8500_BM_H */

+ 5 - 0
include/linux/mfd/abx500/ux500_chargalg.h

@@ -27,12 +27,17 @@ struct ux500_charger_ops {
  * @ops			ux500 charger operations
  * @max_out_volt	maximum output charger voltage in mV
  * @max_out_curr	maximum output charger current in mA
+ * @enabled		indicates if this charger is used or not
+ * @external		external charger unit (pm2xxx)
  */
 struct ux500_charger {
 	struct power_supply psy;
 	struct ux500_charger_ops ops;
 	int max_out_volt;
 	int max_out_curr;
+	int wdt_refresh;
+	bool enabled;
+	bool external;
 };
 
 #endif

+ 61 - 0
include/linux/pm2301_charger.h

@@ -0,0 +1,61 @@
+/*
+ * PM2301 charger driver.
+ *
+ * Copyright (C) 2012 ST Ericsson Corporation
+ *
+ * Contact: Olivier LAUNAY (olivier.launay@stericsson.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_PM2301_H
+#define __LINUX_PM2301_H
+
+/**
+ * struct pm2xxx_bm_charger_parameters - Charger specific parameters
+ * @ac_volt_max:	maximum allowed AC charger voltage in mV
+ * @ac_curr_max:	maximum allowed AC charger current in mA
+ */
+struct pm2xxx_bm_charger_parameters {
+	int ac_volt_max;
+	int ac_curr_max;
+};
+
+/**
+ * struct pm2xxx_bm_data - pm2xxx battery management data
+ * @enable_overshoot    flag to enable VBAT overshoot control
+ * @chg_params	  charger parameters
+ */
+struct pm2xxx_bm_data {
+	bool enable_overshoot;
+	const struct pm2xxx_bm_charger_parameters *chg_params;
+};
+
+struct pm2xxx_charger_platform_data {
+	char **supplied_to;
+	size_t num_supplicants;
+	int i2c_bus;
+	const char *label;
+	int irq_number;
+	unsigned int lpn_gpio;
+	int irq_type;
+};
+
+struct pm2xxx_platform_data {
+	struct pm2xxx_charger_platform_data *wall_charger;
+	struct pm2xxx_bm_data *battery;
+};
+
+#endif /* __LINUX_PM2301_H */

+ 2 - 1
include/linux/power/bq2415x_charger.h

@@ -75,7 +75,8 @@
 
 /* Supported modes with maximal current limit */
 enum bq2415x_mode {
-	BQ2415X_MODE_NONE,		/* unknown or no charger (100mA) */
+	BQ2415X_MODE_OFF,		/* offline mode (charger disabled) */
+	BQ2415X_MODE_NONE,		/* unknown charger (100mA) */
 	BQ2415X_MODE_HOST_CHARGER,	/* usb host/hub charger (500mA) */
 	BQ2415X_MODE_DEDICATED_CHARGER, /* dedicated charger (unlimited) */
 	BQ2415X_MODE_BOOST,		/* boost mode (charging disabled) */

+ 2 - 0
include/linux/power_supply.h

@@ -54,6 +54,8 @@ enum {
 	POWER_SUPPLY_HEALTH_OVERVOLTAGE,
 	POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
 	POWER_SUPPLY_HEALTH_COLD,
+	POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,
+	POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
 };
 
 enum {

Some files were not shown because too many files changed in this diff