* [PATCH 01/24] pm2301: Provide u9540 support for the pm2301 charger
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 02/24] ab8500-charger: AB workaround for invalid charger Lee Jones
` (22 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Michel JAOUEN <michel.jaouen@stericsson.com>
AC charger driver for the DB9540 based platforms.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
---
drivers/power/Kconfig | 7 +
drivers/power/Makefile | 1 +
drivers/power/ab8500_charger.c | 36 +-
drivers/power/pm2301_charger.c | 1455 +++++++++++++++++++++++++++++
include/linux/mfd/abx500/ab8500-bm.h | 2 +
include/linux/mfd/abx500/ux500_chargalg.h | 2 +
include/linux/pm2301_charger.h | 60 ++
7 files changed, 1550 insertions(+), 13 deletions(-)
create mode 100644 drivers/power/pm2301_charger.c
create mode 100644 include/linux/pm2301_charger.h
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 9f45e2f7..4811b59 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -346,6 +346,13 @@ config AB8500_BM
help
Say Y to include support for AB8500 battery management.
+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
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b11e0c7..aa966e8 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -46,6 +46,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
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index d5a8bda..43ec82b 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2830,8 +2830,11 @@ static int ab8500_charger_remove(struct platform_device *pdev)
destroy_workqueue(di->charger_wq);
flush_scheduled_work();
- power_supply_unregister(&di->usb_chg.psy);
- power_supply_unregister(&di->ac_chg.psy);
+ if(di->usb_chg.enabled)
+ power_supply_unregister(&di->usb_chg.psy);
+ if(di->ac_chg.enabled)
+ power_supply_unregister(&di->ac_chg.psy);
+
platform_set_drvdata(pdev, NULL);
return 0;
@@ -2899,6 +2902,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
di->ac_chg.max_out_curr = ab8500_charger_current_map[
ARRAY_SIZE(ab8500_charger_current_map) - 1];
+ di->ac_chg.enabled = di->pdata->ac_enabled;
/* USB supply */
/* power_supply base class */
@@ -2917,7 +2921,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
di->usb_chg.max_out_curr = ab8500_charger_current_map[
ARRAY_SIZE(ab8500_charger_current_map) - 1];
-
+ di->usb_chg.enabled = di->pdata->usb_enabled;
/* Create a work queue for the charger */
di->charger_wq =
@@ -2995,17 +2999,21 @@ static int ab8500_charger_probe(struct platform_device *pdev)
}
/* Register AC charger class */
- ret = power_supply_register(di->dev, &di->ac_chg.psy);
- if (ret) {
- dev_err(di->dev, "failed to register AC charger\n");
- goto free_charger_wq;
+ if(di->ac_chg.enabled) {
+ ret = power_supply_register(di->dev, &di->ac_chg.psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register AC charger\n");
+ goto free_charger_wq;
+ }
}
/* Register USB charger class */
- ret = power_supply_register(di->dev, &di->usb_chg.psy);
- if (ret) {
- dev_err(di->dev, "failed to register USB charger\n");
- goto free_ac;
+ if(di->usb_chg.enabled) {
+ ret = power_supply_register(di->dev, &di->usb_chg.psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register USB charger\n");
+ goto free_ac;
+ }
}
di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
@@ -3085,9 +3093,11 @@ free_irq:
put_usb_phy:
usb_put_phy(di->usb_phy);
free_usb:
- power_supply_unregister(&di->usb_chg.psy);
+ if(di->usb_chg.enabled)
+ power_supply_unregister(&di->usb_chg.psy);
free_ac:
- power_supply_unregister(&di->ac_chg.psy);
+ if(di->ac_chg.enabled)
+ power_supply_unregister(&di->ac_chg.psy);
free_charger_wq:
destroy_workqueue(di->charger_wq);
return ret;
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
new file mode 100644
index 0000000..c470066
--- /dev/null
+++ b/drivers/power/pm2301_charger.c
@@ -0,0 +1,1455 @@
+/*
+ * Power supply driver for ST Ericsson pm2xxx_charger charger
+ *
+ * Copyright 2012 ST Ericsson.
+ *
+ * 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/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/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 (60 * HZ)
+
+/* 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 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
+
+/* 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
+
+#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,
+};
+
+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,
+};
+
+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_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+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,
+};
+
+struct pm2xxx_irq {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct pm2xxx_charger_info {
+ int charger_connected;
+ int charger_online;
+ int charger_voltage;
+ int cv_active;
+ bool wd_expired;
+};
+
+struct pm2xxx_charger_event_flags {
+ bool mainextchnotok;
+ bool main_thermal_prot;
+ bool ovv;
+ bool chgwdexp;
+};
+
+struct pm2xxx_config {
+ struct i2c_client *pm2xxx_i2c;
+ struct i2c_device_id *pm2xxx_id;
+};
+
+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;
+ u8 pm2_int[6];
+ 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;
+};
+
+static const struct i2c_device_id pm2xxx_ident[] = {
+ { "pm2301", 0 },
+ { }
+};
+
+static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
+{
+ int ret;
+
+ 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);
+
+ return ret;
+}
+
+static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
+{
+ int ret;
+
+ 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);
+
+ 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 (pm2xxx_charging_disable_mngt(pm2));
+}
+
+static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
+{
+ int ret = 0;
+
+ 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(struct pm2xxx_charger *pm2)
+{
+ int ret = 0;
+
+ if (pm2->pm2_int[0] &
+ (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
+ ret = pm2xxx_charger_vbat_lsig_mngt(pm2, pm2->pm2_int[0] &
+ (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF));
+ }
+
+ if (pm2->pm2_int[0] & PM2XXX_INT1_ITVBATDISCONNECT) {
+ ret = pm2xxx_charger_bat_disc_mngt(pm2,
+ PM2XXX_INT1_ITVBATDISCONNECT);
+ }
+
+ return ret;
+}
+
+static int pm2_int_reg1(struct pm2xxx_charger *pm2)
+{
+ int ret = 0;
+
+ if (pm2->pm2_int[1] &
+ (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
+ dev_dbg(pm2->dev , "Main charger plugged\n");
+ ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, pm2->pm2_int[1] &
+ (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG));
+ }
+
+ if (pm2->pm2_int[1] &
+ (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) {
+ dev_dbg(pm2->dev , "Main charger unplugged\n");
+ ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, pm2->pm2_int[1] &
+ (PM2XXX_INT2_ITVPWR1UNPLUG |
+ PM2XXX_INT2_ITVPWR2UNPLUG));
+ }
+
+ return ret;
+}
+
+static int pm2_int_reg2(struct pm2xxx_charger *pm2)
+{
+ int ret = 0;
+
+ if (pm2->pm2_int[2] & PM2XXX_INT3_ITAUTOTIMEOUTWD)
+ ret = pm2xxx_charger_wd_exp_mngt(pm2, pm2->pm2_int[2]);
+
+ if (pm2->pm2_int[2] & (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(struct pm2xxx_charger *pm2)
+{
+ int ret = 0;
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_ITCHARGINGON)) {
+ dev_dbg(pm2->dev ,
+ "chargind operation has started\n");
+ }
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_ITVRESUME)) {
+ dev_dbg(pm2->dev,
+ "battery discharged down to VResume threshold\n");
+ }
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_ITBATTFULL)) {
+ dev_dbg(pm2->dev , "battery fully detected\n");
+ }
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_ITCVPHASE)) {
+ dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n");
+ }
+
+ if (pm2->pm2_int[3] &
+ (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
+ pm2->failure_case = VPWR_OVV;
+ ret = pm2xxx_charger_ovv_mngt(pm2, pm2->pm2_int[3] &
+ (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV));
+ dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n");
+ }
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_S_ITBATTEMPCOLD |
+ PM2XXX_INT4_S_ITBATTEMPHOT)) {
+ ret = pm2xxx_charger_batt_therm_mngt(pm2,
+ pm2->pm2_int[3] & (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(struct pm2xxx_charger *pm2)
+{
+ int ret = 0;
+
+ if (pm2->pm2_int[4] & PM2XXX_INT5_ITVSYSTEMOVV) {
+ pm2->failure_case = VSYSTEM_OVV;
+ ret = pm2xxx_charger_ovv_mngt(pm2, pm2->pm2_int[4] &
+ PM2XXX_INT5_ITVSYSTEMOVV);
+ dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n");
+ }
+
+ if (pm2->pm2_int[4] & (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, pm2->pm2_int[4] &
+ (PM2XXX_INT5_ITTHERMALWARNINGFALL |
+ PM2XXX_INT5_ITTHERMALWARNINGRISE |
+ PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
+ PM2XXX_INT5_ITTHERMALSHUTDOWNRISE));
+ }
+
+ return ret;
+}
+
+static int pm2_int_reg5(struct pm2xxx_charger *pm2)
+{
+
+ if (pm2->pm2_int[5]
+ & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
+ dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
+ }
+
+ if (pm2->pm2_int[5] & (PM2XXX_INT6_ITVPWR2VALIDRISE |
+ PM2XXX_INT6_ITVPWR1VALIDRISE |
+ PM2XXX_INT6_ITVPWR2VALIDFALL |
+ PM2XXX_INT6_ITVPWR1VALIDFALL)) {
+ dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n");
+ }
+
+ return 0;
+}
+
+static irqreturn_t pm2xxx_irq_int(int irq, void *data)
+{
+ struct pm2xxx_charger *pm2 = data;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(pm2->pm2_int); i++) {
+ ret = pm2xxx_reg_read(pm2, pm2xxx_interrupt_registers[i],
+ &(pm2->pm2_int[i]));
+ }
+
+ pm2_int_reg0(pm2);
+ pm2_int_reg1(pm2);
+ pm2_int_reg2(pm2);
+ pm2_int_reg3(pm2);
+ pm2_int_reg4(pm2);
+ pm2_int_reg5(pm2);
+
+ return IRQ_HANDLED;
+}
+
+static int pm2xxx_charger_get_ac_voltage(struct pm2xxx_charger *pm2)
+{
+ int vch = 0;
+
+ if (pm2->ac.charger_connected) {
+ vch = ab8500_gpadc_convert(pm2->gpadc, MAIN_CHARGER_V);
+ if (vch < 0)
+ dev_err(pm2->dev, "%s gpadc conv failed,\n", __func__);
+ }
+
+ return vch;
+}
+
+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_charger_get_ac_current(struct pm2xxx_charger *pm2)
+{
+ int ich = 0;
+
+ if (pm2->ac.charger_online) {
+ ich = ab8500_gpadc_convert(pm2->gpadc, MAIN_CHARGER_C);
+ if (ich < 0)
+ dev_err(pm2->dev, "%s gpadc conv failed\n", __func__);
+ }
+
+ return ich;
+}
+
+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_NOW:
+ pm2->ac.charger_voltage = pm2xxx_charger_get_ac_voltage(pm2);
+ val->intval = pm2->ac.charger_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2);
+ val->intval = pm2->ac.cv_active;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = pm2xxx_charger_get_ac_current(pm2) * 1000;
+ 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);
+
+ 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) {
+ 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 write failed\n", __func__);
+ goto error_occured;
+ }
+ else
+ dev_err(pm2->dev, "%s read failed\n", __func__);
+ }
+
+ 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__);
+ goto error_occured;
+ }
+ else
+ dev_err(pm2->dev, "%s read failed\n", __func__);
+ }
+
+ if (!pm2->bat->enable_overshoot) {
+ ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val);
+ if (ret >= 0) {
+ val |= PM2XXX_ANTI_OVERSHOOT_EN;
+ ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
+ val);
+ if (ret < 0){
+ dev_err(pm2->dev, "%s write failed\n",
+ __func__);
+ goto error_occured;
+ }
+ }
+ else
+ dev_err(pm2->dev, "%s read failed\n", __func__);
+ }
+
+ ret = pm2xxx_charging_enable_mngt(pm2);
+ if (ret) {
+ dev_err(pm2->dev, "%s write failed\n", __func__);
+ 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) {
+ dev_err(pm2->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ 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_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();
+
+ /* get charger spcific platform data */
+ if (!pl_data->wall_charger) {
+ dev_err(pm2->dev, "no charger platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ pm2->pdata = pl_data->wall_charger;
+
+ /* get battery specific platform data */
+ if (!pl_data->battery) {
+ dev_err(pm2->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ pm2->bat = pl_data->battery;
+
+ if (!i2c_check_functionality(i2c_client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ ret = -ENODEV;
+ dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n");
+ goto free_device_info;
+ }
+
+ pm2->config.pm2xxx_i2c = i2c_client;
+ pm2->config.pm2xxx_id = (struct i2c_device_id *) id;
+ i2c_set_clientdata(i2c_client, pm2);
+
+ /* AC supply */
+ /* power_supply base class */
+ pm2->ac_chg.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];
+
+ /* 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;
+ }
+
+ /*
+ * I2C Read/Write will fail, if AC adaptor is not connected.
+ * fix the charger detection mechanism.
+ */
+ 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;
+
+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);
+
+ 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");
+
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index e2a1e6d..a03d4fd 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -406,6 +406,8 @@ struct ab8500_charger_platform_data {
char **supplied_to;
size_t num_supplicants;
bool autopower_cfg;
+ bool ac_enabled;
+ bool usb_enabled;
};
struct ab8500_btemp_platform_data {
diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h
index 9b07725..5b77a61 100644
--- a/include/linux/mfd/abx500/ux500_chargalg.h
+++ b/include/linux/mfd/abx500/ux500_chargalg.h
@@ -27,12 +27,14 @@ 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
*/
struct ux500_charger {
struct power_supply psy;
struct ux500_charger_ops ops;
int max_out_volt;
int max_out_curr;
+ bool enabled;
};
#endif
diff --git a/include/linux/pm2301_charger.h b/include/linux/pm2301_charger.h
new file mode 100644
index 0000000..16bb1d3
--- /dev/null
+++ b/include/linux/pm2301_charger.h
@@ -0,0 +1,60 @@
+/*
+ * PM2301 charger driver.
+ *
+ * Copyright (C) 2012 ST Ericsson Corporation
+ *
+ * Contact: Olivier LAUNAY (olivier.launay at 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;
+ int irq_type;
+};
+
+struct pm2xxx_platform_data {
+ struct pm2xxx_charger_platform_data *wall_charger;
+ struct pm2xxx_bm_data *battery;
+};
+
+#endif /* __LINUX_PM2301_H */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 02/24] ab8500-charger: AB workaround for invalid charger
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
2013-01-21 12:03 ` [PATCH 01/24] pm2301: Provide u9540 support for the pm2301 charger Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 03/24] ab8500-fg: Adjust for RF bursts voltage drops Lee Jones
` (21 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Henrik S?lver <henrik.solver@stericsson.com>
AB8500 refuses to start charging when some types of non standard
chargers are connected. This change force the AB to start charging.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Henrik S?lver <henrik.solver@stericsson.com>
Reviewed-by: Yvan FILLION <yvan.fillion@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Yvan FILLION <yvan.fillion@stericsson.com>
---
drivers/power/ab8500_charger.c | 59 +++++++++++++++++++++++++++++++++++++---
1 file changed, 55 insertions(+), 4 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 43ec82b..4c66172 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -207,6 +207,7 @@ struct ab8500_charger_usb_state {
* @usb_device_is_unrecognised USB device is unrecognised by the hardware
* @autopower Indicate if we should have automatic pwron after pwrloss
* @autopower_cfg platform specific power config support for "pwron after pwrloss"
+ * @invalid_charger_detect_state State when forcing AB to use invalid charger
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @bm: Platform specific battery management information
@@ -251,6 +252,7 @@ struct ab8500_charger {
bool usb_device_is_unrecognised;
bool autopower;
bool autopower_cfg;
+ int invalid_charger_detect_state;
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
struct abx500_bm_data *bm;
@@ -659,7 +661,6 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
break;
}
case USB_STAT_HM_IDGND:
- case USB_STAT_NOT_VALID_LINK:
dev_err(di->dev, "USB Type - Charging not allowed\n");
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
ret = -ENXIO;
@@ -688,6 +689,9 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
di->max_usb_in_curr);
+ case USB_STAT_NOT_VALID_LINK:
+ dev_err(di->dev, "USB Type invalid - try charging anyway\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
break;
default:
@@ -1957,7 +1961,9 @@ static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
*/
static void ab8500_charger_usb_link_status_work(struct work_struct *work)
{
+ int detected_chargers;
int ret;
+ u8 val;
struct ab8500_charger *di = container_of(work,
struct ab8500_charger, usb_link_status_work);
@@ -1967,11 +1973,55 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
* synchronously, we have the check if is
* connected by reading the status register
*/
- ret = ab8500_charger_detect_chargers(di);
- if (ret < 0)
+ detected_chargers = ab8500_charger_detect_chargers(di);
+ if (detected_chargers < 0)
return;
- if (!(ret & USB_PW_CONN)) {
+ /*
+ * Some chargers that breaks the USB spec is
+ * identified as invalid by AB8500 and it refuse
+ * to start the charging process. but by jumping
+ * thru a few hoops it can be forced to start.
+ */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+ AB8500_USB_LINE_STAT_REG, &val);
+ if (ret >= 0)
+ dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
+ else
+ dev_dbg(di->dev, "Error reading USB link status\n");
+
+ if (detected_chargers & USB_PW_CONN) {
+ if (((val & AB8500_USB_LINK_STATUS) >> 3) == USB_STAT_NOT_VALID_LINK &&
+ di->invalid_charger_detect_state == 0) {
+ dev_dbg(di->dev, "Invalid charger detected, state= 0\n");
+ /*Enable charger*/
+ abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_USBCH_CTRL1_REG, 0x01, 0x01);
+ /*Enable charger detection*/
+ abx500_mask_and_set_register_interruptible(di->dev, AB8500_USB,
+ AB8500_MCH_IPT_CURLVL_REG, 0x01, 0x01);
+ di->invalid_charger_detect_state = 1;
+ /*exit and wait for new link status interrupt.*/
+ return;
+
+ }
+ if (di->invalid_charger_detect_state == 1) {
+ dev_dbg(di->dev, "Invalid charger detected, state= 1\n");
+ /*Stop charger detection*/
+ abx500_mask_and_set_register_interruptible(di->dev, AB8500_USB,
+ AB8500_MCH_IPT_CURLVL_REG, 0x01, 0x00);
+ /*Check link status*/
+ ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
+ AB8500_USB_LINE_STAT_REG, &val);
+ dev_dbg(di->dev, "USB link status= 0x%02x\n",
+ (val & AB8500_USB_LINK_STATUS) >> 3);
+ di->invalid_charger_detect_state = 2;
+ }
+ } else {
+ di->invalid_charger_detect_state = 0;
+ }
+
+ if (!(detected_chargers & USB_PW_CONN)) {
di->vbus_detected = 0;
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, &di->usb_chg.psy);
@@ -2884,6 +2934,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
spin_lock_init(&di->usb_state.usb_lock);
di->autopower = false;
+ di->invalid_charger_detect_state = 0;
/* AC supply */
/* power_supply base class */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 03/24] ab8500-fg: Adjust for RF bursts voltage drops
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
2013-01-21 12:03 ` [PATCH 01/24] pm2301: Provide u9540 support for the pm2301 charger Lee Jones
2013-01-21 12:03 ` [PATCH 02/24] ab8500-charger: AB workaround for invalid charger Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 04/24] ab8500-btemp: Adaptation to AB8505 and AB9540 platforms Lee Jones
` (20 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Hakan Berg <hakan.berg@stericsson.com>
Changed conditions for restarting low battery measurements counter
and adjusted the interval between measurements to avoid RF burst
induced voltage drops, and to shorten time to decide to shut down.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Hakan Berg <hakan.berg@stericsson.com>
Signed-off-by: Martin Bergstrom <martin.bergstrom@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
---
drivers/power/ab8500_fg.c | 41 +++++++++++++++++++++++++++--------------
1 file changed, 27 insertions(+), 14 deletions(-)
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index cd03549..a0cbbd3 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -43,7 +43,7 @@
#define NBR_AVG_SAMPLES 20
-#define LOW_BAT_CHECK_INTERVAL (2 * HZ)
+#define LOW_BAT_CHECK_INTERVAL (HZ / 16) /* 62.5 ms */
#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */
#define BATT_OK_MIN 2360 /* mV */
@@ -169,6 +169,7 @@ struct inst_curr_result_list {
* @recovery_cnt: Counter for recovery mode
* @high_curr_cnt: Counter for high current mode
* @init_cnt: Counter for init mode
+ * @low_bat_cnt Counter for number of consecutive low battery measures
* @nbr_cceoc_irq_cnt Counter for number of CCEOC irqs received since enabled
* @recovery_needed: Indicate if recovery is needed
* @high_curr_mode: Indicate if we're in high current mode
@@ -210,6 +211,7 @@ struct ab8500_fg {
int recovery_cnt;
int high_curr_cnt;
int init_cnt;
+ int low_bat_cnt;
int nbr_cceoc_irq_cnt;
bool recovery_needed;
bool high_curr_mode;
@@ -1879,25 +1881,29 @@ static void ab8500_fg_low_bat_work(struct work_struct *work)
/* Check if LOW_BAT still fulfilled */
if (vbat < di->bm->fg_params->lowbat_threshold) {
- di->flags.low_bat = true;
- dev_warn(di->dev, "Battery voltage still LOW\n");
-
- /*
- * We need to re-schedule this check to be able to detect
- * if the voltage increases again during charging
- */
- queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
- round_jiffies(LOW_BAT_CHECK_INTERVAL));
+ /* Is it time to shut down? */
+ if (di->low_bat_cnt < 1) {
+ di->flags.low_bat = true;
+ dev_warn(di->dev, "Shut down pending...\n");
+ } else {
+ /*
+ * Else we need to re-schedule this check to be able to detect
+ * if the voltage increases again during charging or
+ * due to decreasing load.
+ */
+ di->low_bat_cnt--;
+ dev_warn(di->dev, "Battery voltage still LOW\n");
+ queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+ round_jiffies(LOW_BAT_CHECK_INTERVAL));
+ }
} else {
- di->flags.low_bat = false;
+ di->flags.low_bat_delay = false;
+ di->low_bat_cnt = 10;
dev_warn(di->dev, "Battery voltage OK again\n");
}
/* This is needed to dispatch LOW_BAT */
ab8500_fg_check_capacity_limits(di, false);
-
- /* Set this flag to check if LOW_BAT IRQ still occurs */
- di->flags.low_bat_delay = false;
}
/**
@@ -2056,6 +2062,7 @@ static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di)
{
struct ab8500_fg *di = _di;
+ /* Initiate handling in ab8500_fg_low_bat_work() if not already initiated. */
if (!di->flags.low_bat_delay) {
dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
di->flags.low_bat_delay = true;
@@ -2698,6 +2705,12 @@ static int ab8500_fg_probe(struct platform_device *pdev)
INIT_DEFERRABLE_WORK(&di->fg_check_hw_failure_work,
ab8500_fg_check_hw_failure_work);
+ /* Reset battery low voltage flag */
+ di->flags.low_bat = false;
+
+ /* Initialize low battery counter */
+ di->low_bat_cnt = 10;
+
/* Initialize OVV, and other registers */
ret = ab8500_fg_init_hw_registers(di);
if (ret) {
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 04/24] ab8500-btemp: Adaptation to AB8505 and AB9540 platforms
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (2 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 03/24] ab8500-fg: Adjust for RF bursts voltage drops Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 05/24] ab8500-charger: Kick watchdog Lee Jones
` (19 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Michel JAOUEN <michel.jaouen@stericsson.com>
Add AB9540 and AB8505 support to ABx500 BTEMP driver.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/ab8500_btemp.c | 67 +++++++++++++++++++++++++++-------
include/linux/mfd/abx500/ab8500-bm.h | 2 +
2 files changed, 55 insertions(+), 14 deletions(-)
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 8ccf359..301a9ad 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/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);
@@ -212,10 +215,18 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
/* Only do this for batteries with internal NTC */
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
- if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
- curr = BAT_CTRL_7U_ENA;
- else
- curr = BAT_CTRL_20U_ENA;
+
+ 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);
@@ -246,11 +257,22 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
} else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
dev_dbg(di->dev, "Disable BATCTRL curr source\n");
- /* 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__);
@@ -292,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__);
@@ -510,8 +541,11 @@ 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->bm->batt_id = BATTERY_UNKNOWN;
res = ab8500_btemp_get_batctrl_res(di);
@@ -549,8 +583,13 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
*/
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
di->bm->batt_id == 1) {
- dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
- di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+ if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
+ dev_dbg(di->dev, "Set BATCTRL current source to 16uA\n");
+ di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
+ } else {
+ dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
+ di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
+ }
}
return di->bm->batt_id;
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index a03d4fd..ec796c7 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -226,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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 05/24] ab8500-charger: Kick watchdog
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (3 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 04/24] ab8500-btemp: Adaptation to AB8505 and AB9540 platforms Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 06/24] ab8500-chargalg: Update battery health on safety timer exp Lee Jones
` (18 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
Kicks the watchdog so charging will not stop.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/abx500_chargalg.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 78b6235..830b2dd 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -603,6 +603,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,
@@ -620,8 +622,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);
}
/**
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 06/24] ab8500-chargalg: Update battery health on safety timer exp
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (4 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 05/24] ab8500-charger: Kick watchdog Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 07/24] pm2301: Add deep debug Lee Jones
` (17 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
When the charging safety timer elapses, the battery health is
shown as "Good". This is misleading and also makes it difficult
to distinguish issues relating to discharging despite the fact
that the charger is still connected.
When in actual fact a safety timer elapse is an indication of a
fault in the battery. Here we make this clearer by reporting
POWER_SUPPLY_HEALTH_UNSPEC_FAILURE instead.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/abx500_chargalg.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 830b2dd..2463fa0 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1628,6 +1628,9 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
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;
}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 07/24] pm2301: Add deep debug
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (5 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 06/24] ab8500-chargalg: Update battery health on safety timer exp Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 08/24] pm2301: Clean-up PM2301 interrupt management Lee Jones
` (16 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Loic Pallardy <loic.pallardy@stericsson.com>
Register access for HATS testing purposes
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
---
drivers/power/Kconfig | 9 +
drivers/power/Makefile | 1 +
drivers/power/pm2301_charger.c | 492 ++----------------------------------
drivers/power/pm2301_charger.h | 508 ++++++++++++++++++++++++++++++++++++++
drivers/power/pm2301_deepdebug.c | 131 ++++++++++
5 files changed, 664 insertions(+), 477 deletions(-)
create mode 100644 drivers/power/pm2301_charger.h
create mode 100644 drivers/power/pm2301_deepdebug.c
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 4811b59..b9de00d 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -353,6 +353,15 @@ config CHARGER_PM2301
Say Y to include support for PM2301 charger driver.
Depends on AB8500 battery management core.
+config PM2XXX_DEEP_DEBUG
+ bool "PM2XXX Deep Debug"
+ depends on DEEP_DEBUG && CHARGER_PM2301
+ help
+ Deep Debug interface provides an access to all registers.
+ It allows to read or write directly a register.
+ Say Y to include support for Deep Debug.
+ If unsure, say N.
+
source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index aa966e8..ef1e79c 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -47,6 +47,7 @@ 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_PM2XXX_DEEP_DEBUG) += pm2301_deepdebug.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
index c470066..c196de7 100644
--- a/drivers/power/pm2301_charger.c
+++ b/drivers/power/pm2301_charger.c
@@ -1,8 +1,8 @@
/*
- * Power supply driver for ST Ericsson pm2xxx_charger charger
- *
* 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.
@@ -29,285 +29,7 @@
#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/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 (60 * HZ)
-
-/* 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 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
-
-/* 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
+#include "pm2301_charger.h"
#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
struct pm2xxx_charger, ac_chg)
@@ -321,148 +43,6 @@ static int pm2xxx_interrupt_registers[] = {
PM2XXX_REG_INT6,
};
-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,
-};
-
static enum power_supply_property pm2xxx_charger_ac_props[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
@@ -527,64 +107,12 @@ static int pm2xxx_charger_current_map[] = {
3000,
};
-struct pm2xxx_irq {
- char *name;
- irqreturn_t (*isr)(int irq, void *data);
-};
-
-struct pm2xxx_charger_info {
- int charger_connected;
- int charger_online;
- int charger_voltage;
- int cv_active;
- bool wd_expired;
-};
-
-struct pm2xxx_charger_event_flags {
- bool mainextchnotok;
- bool main_thermal_prot;
- bool ovv;
- bool chgwdexp;
-};
-
-struct pm2xxx_config {
- struct i2c_client *pm2xxx_i2c;
- struct i2c_device_id *pm2xxx_id;
-};
-
-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;
- u8 pm2_int[6];
- 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;
-};
-
static const struct i2c_device_id pm2xxx_ident[] = {
{ "pm2301", 0 },
{ }
};
-static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
+int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
{
int ret;
@@ -596,7 +124,7 @@ static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
return ret;
}
-static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
+int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
{
int ret;
@@ -1376,8 +904,16 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
}
+ ret = pm2xxx_ddbg_init(pm2);
+ if (ret)
+ goto err_deep_debug;
+
return 0;
+err_deep_debug:
+ /* disable interrupt */
+ free_irq(pm2->pdata->irq_number, pm2);
+
unregister_pm2xxx_charger:
/* unregister power supply */
power_supply_unregister(&pm2->ac_chg.psy);
@@ -1404,6 +940,8 @@ static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
/* Delete the work queue */
destroy_workqueue(pm2->charger_wq);
+ pm2xxx_ddbg_exit();
+
flush_scheduled_work();
/* disable the regulator */
diff --git a/drivers/power/pm2301_charger.h b/drivers/power/pm2301_charger.h
new file mode 100644
index 0000000..27bf931
--- /dev/null
+++ b/drivers/power/pm2301_charger.h
@@ -0,0 +1,508 @@
+/*
+ * 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 (60 * HZ)
+
+/* 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 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
+
+/* 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 charger_voltage;
+ int cv_active;
+ bool wd_expired;
+};
+
+struct pm2xxx_charger_event_flags {
+ bool mainextchnotok;
+ bool main_thermal_prot;
+ bool ovv;
+ bool chgwdexp;
+};
+
+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;
+ u8 pm2_int[6];
+ 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;
+};
+
+int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val);
+int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val);
+
+#ifdef CONFIG_PM2XXX_DEEP_DEBUG
+
+int pm2xxx_ddbg_init(struct pm2xxx_charger *pm2);
+int pm2xxx_ddbg_exit(void);
+
+#else /* CONFIG_PM2XXX_DEEP_DEBUG */
+
+static inline int pm2xxx_ddbg_init(struct pm2xxx_charger *pm2)
+{
+ return 0;
+}
+
+static inline int pm2xxx_ddbg_exit(void)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PM2XXX_DEEP_DEBUG */
+
+#endif /* PM2301_CHARGER_H */
diff --git a/drivers/power/pm2301_deepdebug.c b/drivers/power/pm2301_deepdebug.c
new file mode 100644
index 0000000..6132006
--- /dev/null
+++ b/drivers/power/pm2301_deepdebug.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2012 ST Ericsson.
+ *
+ * Deepdebug support 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/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/debugfs.h>
+#include <linux/ux500_deepdebug.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/pm2301_charger.h>
+
+#include "pm2301_charger.h"
+
+/* PM2XXX registers list */
+static struct ddbg_register ddbg_pm2xxx_registers[] = {
+ DDBG_REG("PM2XXX_BATT_CTRL_REG1", PM2XXX_BATT_CTRL_REG1, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG2", PM2XXX_BATT_CTRL_REG2, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG3", PM2XXX_BATT_CTRL_REG3, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG4", PM2XXX_BATT_CTRL_REG4, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG5", PM2XXX_BATT_CTRL_REG5, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG6", PM2XXX_BATT_CTRL_REG6, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG7", PM2XXX_BATT_CTRL_REG7, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG8", PM2XXX_BATT_CTRL_REG8, DDBG_RW),
+ DDBG_REG("PM2XXX_NTC_CTRL_REG1", PM2XXX_NTC_CTRL_REG1, DDBG_RW),
+ DDBG_REG("PM2XXX_NTC_CTRL_REG2", PM2XXX_NTC_CTRL_REG2, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG9", PM2XXX_BATT_CTRL_REG9, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_STAT_REG1", PM2XXX_BATT_STAT_REG1, DDBG_RO),
+ DDBG_REG("PM2XXX_INP_VOLT_VPWR2", PM2XXX_INP_VOLT_VPWR2, DDBG_RW),
+ DDBG_REG("PM2XXX_INP_DROP_VPWR2", PM2XXX_INP_DROP_VPWR2, DDBG_RW),
+ DDBG_REG("PM2XXX_INP_VOLT_VPWR1", PM2XXX_INP_VOLT_VPWR1, DDBG_RW),
+ DDBG_REG("PM2XXX_INP_DROP_VPWR1", PM2XXX_INP_DROP_VPWR1, DDBG_RW),
+ DDBG_REG("PM2XXX_INP_MODE_VPWR", PM2XXX_INP_MODE_VPWR, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_WD_KICK", PM2XXX_BATT_WD_KICK, DDBG_RW),
+ DDBG_REG("PM2XXX_DEV_VER_STAT", PM2XXX_DEV_VER_STAT, DDBG_RO),
+ DDBG_REG("PM2XXX_THERM_WARN_CTRL", PM2XXX_THERM_WARN_CTRL_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_BAT_DISC_REG", PM2XXX_BATT_DISC_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_BAT_LWLEV_CMP", PM2XXX_BATT_LOW_LEV_COMP_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_BAT_LWLEV_VAL", PM2XXX_BATT_LOW_LEV_VAL_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_I2C_PAD_CTRL_REG", PM2XXX_I2C_PAD_CTRL_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_SW_CTRL_REG", PM2XXX_SW_CTRL_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_LED_CTRL_REG", PM2XXX_LED_CTRL_REG, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT1", PM2XXX_REG_INT1, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT1", PM2XXX_MASK_REG_INT1, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT1", PM2XXX_SRCE_REG_INT1, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT2", PM2XXX_REG_INT2, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT2", PM2XXX_MASK_REG_INT2, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT2", PM2XXX_SRCE_REG_INT2, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT3", PM2XXX_REG_INT3, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT3", PM2XXX_MASK_REG_INT3, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT3", PM2XXX_SRCE_REG_INT3, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT4", PM2XXX_REG_INT4, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT4", PM2XXX_MASK_REG_INT4, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT4", PM2XXX_SRCE_REG_INT4, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT5", PM2XXX_REG_INT5, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT5", PM2XXX_MASK_REG_INT5, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT5", PM2XXX_SRCE_REG_INT5, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT6", PM2XXX_REG_INT6, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT6", PM2XXX_MASK_REG_INT6, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT6", PM2XXX_SRCE_REG_INT6, DDBG_RO),
+ DDBG_REG_NULL,
+};
+
+static struct pm2xxx_charger *pm2xxx_ddbg;
+
+static int pm2xxx_ddbg_write(u32 addr, u32 val)
+{
+ int err;
+
+ err = pm2xxx_reg_write(pm2xxx_ddbg, addr, val);
+
+ if (err < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int pm2xxx_ddbg_read(u32 addr, u32 *val)
+{
+ int ret = pm2xxx_reg_read(pm2xxx_ddbg, addr, (u8 *)val);
+
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static struct ddbg_target ddbg_pm2xxx = {
+ .name = "pm2xxx",
+ .phyaddr = 0,
+ .reg = ddbg_pm2xxx_registers,
+ .read_reg = pm2xxx_ddbg_read,
+ .write_reg = pm2xxx_ddbg_write,
+};
+
+int __init pm2xxx_ddbg_init(struct pm2xxx_charger *pm2)
+{
+ int ret;
+ pm2xxx_ddbg = pm2;
+ ret = deep_debug_regaccess_register(&ddbg_pm2xxx);
+ if(ret)
+ pr_err("failed to create debugfs entries.\n");
+
+ return ret;
+}
+
+int __exit pm2xxx_ddbg_exit(void)
+{
+ return (deep_debug_regaccess_unregister(&ddbg_pm2xxx));
+}
+
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 08/24] pm2301: Clean-up PM2301 interrupt management
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (6 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 07/24] pm2301: Add deep debug Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 09/24] pm2301: Remove volt_now & curr_now properties Lee Jones
` (15 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Olivier Clergeaud <olivier.clergeaud@stericsson.com>
Fix the way interrupts are handled within the PM2301 charging driver.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
---
drivers/power/pm2301_charger.c | 173 ++++++++++++++++++++++++++++------------
drivers/power/pm2301_charger.h | 29 ++++++-
2 files changed, 148 insertions(+), 54 deletions(-)
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
index c196de7..316d5f0 100644
--- a/drivers/power/pm2301_charger.c
+++ b/drivers/power/pm2301_charger.c
@@ -224,7 +224,7 @@ static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val)
{
dev_dbg(pm2->dev, "battery disconnected\n");
- return (pm2xxx_charging_disable_mngt(pm2));
+ return 0;
}
static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
@@ -275,17 +275,17 @@ static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2,
return 0;
}
-static int pm2_int_reg0(struct pm2xxx_charger *pm2)
+static int pm2_int_reg0(void *pm2_data, int val)
{
+ struct pm2xxx_charger *pm2 = pm2_data;
int ret = 0;
- if (pm2->pm2_int[0] &
- (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
- ret = pm2xxx_charger_vbat_lsig_mngt(pm2, pm2->pm2_int[0] &
+ if (val & (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
+ ret = pm2xxx_charger_vbat_lsig_mngt(pm2, val &
(PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF));
}
- if (pm2->pm2_int[0] & PM2XXX_INT1_ITVBATDISCONNECT) {
+ if (val & PM2XXX_INT1_ITVBATDISCONNECT) {
ret = pm2xxx_charger_bat_disc_mngt(pm2,
PM2XXX_INT1_ITVBATDISCONNECT);
}
@@ -293,21 +293,21 @@ static int pm2_int_reg0(struct pm2xxx_charger *pm2)
return ret;
}
-static int pm2_int_reg1(struct pm2xxx_charger *pm2)
+static int pm2_int_reg1(void *pm2_data, int val)
{
+ struct pm2xxx_charger *pm2 = pm2_data;
int ret = 0;
- if (pm2->pm2_int[1] &
- (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
+ if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
dev_dbg(pm2->dev , "Main charger plugged\n");
- ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, pm2->pm2_int[1] &
+ ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val &
(PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG));
}
- if (pm2->pm2_int[1] &
+ if (val &
(PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) {
dev_dbg(pm2->dev , "Main charger unplugged\n");
- ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, pm2->pm2_int[1] &
+ ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val &
(PM2XXX_INT2_ITVPWR1UNPLUG |
PM2XXX_INT2_ITVPWR2UNPLUG));
}
@@ -315,14 +315,15 @@ static int pm2_int_reg1(struct pm2xxx_charger *pm2)
return ret;
}
-static int pm2_int_reg2(struct pm2xxx_charger *pm2)
+static int pm2_int_reg2(void *pm2_data, int val)
{
+ struct pm2xxx_charger *pm2 = pm2_data;
int ret = 0;
- if (pm2->pm2_int[2] & PM2XXX_INT3_ITAUTOTIMEOUTWD)
- ret = pm2xxx_charger_wd_exp_mngt(pm2, pm2->pm2_int[2]);
+ if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD)
+ ret = pm2xxx_charger_wd_exp_mngt(pm2, val);
- if (pm2->pm2_int[2] & (PM2XXX_INT3_ITCHPRECHARGEWD |
+ if (val & (PM2XXX_INT3_ITCHPRECHARGEWD |
PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) {
dev_dbg(pm2->dev,
"Watchdog occured for precharge, CC and CV charge\n");
@@ -331,64 +332,65 @@ static int pm2_int_reg2(struct pm2xxx_charger *pm2)
return ret;
}
-static int pm2_int_reg3(struct pm2xxx_charger *pm2)
+static int pm2_int_reg3(void *pm2_data, int val)
{
+ struct pm2xxx_charger *pm2 = pm2_data;
int ret = 0;
- if (pm2->pm2_int[3] & (PM2XXX_INT4_ITCHARGINGON)) {
+ if (val & (PM2XXX_INT4_ITCHARGINGON)) {
dev_dbg(pm2->dev ,
"chargind operation has started\n");
}
- if (pm2->pm2_int[3] & (PM2XXX_INT4_ITVRESUME)) {
+ if (val & (PM2XXX_INT4_ITVRESUME)) {
dev_dbg(pm2->dev,
"battery discharged down to VResume threshold\n");
}
- if (pm2->pm2_int[3] & (PM2XXX_INT4_ITBATTFULL)) {
+ if (val & (PM2XXX_INT4_ITBATTFULL)) {
dev_dbg(pm2->dev , "battery fully detected\n");
}
- if (pm2->pm2_int[3] & (PM2XXX_INT4_ITCVPHASE)) {
+ if (val & (PM2XXX_INT4_ITCVPHASE)) {
dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n");
}
- if (pm2->pm2_int[3] &
- (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
+ if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
pm2->failure_case = VPWR_OVV;
- ret = pm2xxx_charger_ovv_mngt(pm2, pm2->pm2_int[3] &
+ ret = pm2xxx_charger_ovv_mngt(pm2, val &
(PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV));
dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n");
}
- if (pm2->pm2_int[3] & (PM2XXX_INT4_S_ITBATTEMPCOLD |
+ if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD |
PM2XXX_INT4_S_ITBATTEMPHOT)) {
- ret = pm2xxx_charger_batt_therm_mngt(pm2,
- pm2->pm2_int[3] & (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(struct pm2xxx_charger *pm2)
+static int pm2_int_reg4(void *pm2_data, int val)
{
+ struct pm2xxx_charger *pm2 = pm2_data;
int ret = 0;
- if (pm2->pm2_int[4] & PM2XXX_INT5_ITVSYSTEMOVV) {
+ if (val & PM2XXX_INT5_ITVSYSTEMOVV) {
pm2->failure_case = VSYSTEM_OVV;
- ret = pm2xxx_charger_ovv_mngt(pm2, pm2->pm2_int[4] &
+ ret = pm2xxx_charger_ovv_mngt(pm2, val &
PM2XXX_INT5_ITVSYSTEMOVV);
dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n");
}
- if (pm2->pm2_int[4] & (PM2XXX_INT5_ITTHERMALWARNINGFALL |
+ 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, pm2->pm2_int[4] &
+ ret = pm2xxx_charger_die_therm_mngt(pm2, val &
(PM2XXX_INT5_ITTHERMALWARNINGFALL |
PM2XXX_INT5_ITTHERMALWARNINGRISE |
PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
@@ -398,40 +400,40 @@ static int pm2_int_reg4(struct pm2xxx_charger *pm2)
return ret;
}
-static int pm2_int_reg5(struct pm2xxx_charger *pm2)
+static int pm2_int_reg5(void *pm2_data, int val)
{
+ struct pm2xxx_charger *pm2 = pm2_data;
+ int ret = 0;
+
- if (pm2->pm2_int[5]
- & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
+ if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
}
- if (pm2->pm2_int[5] & (PM2XXX_INT6_ITVPWR2VALIDRISE |
- PM2XXX_INT6_ITVPWR1VALIDRISE |
- PM2XXX_INT6_ITVPWR2VALIDFALL |
- PM2XXX_INT6_ITVPWR1VALIDFALL)) {
+ 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 0;
+ return ret;
}
static irqreturn_t pm2xxx_irq_int(int irq, void *data)
{
struct pm2xxx_charger *pm2 = data;
- int ret, i;
+ struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
+ int i;
- for (i = 0; i < ARRAY_SIZE(pm2->pm2_int); i++) {
- ret = pm2xxx_reg_read(pm2, pm2xxx_interrupt_registers[i],
- &(pm2->pm2_int[i]));
- }
+ for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
+ pm2xxx_reg_read(pm2,
+ pm2xxx_interrupt_registers[i],
+ &(interrupt->reg[i]));
- pm2_int_reg0(pm2);
- pm2_int_reg1(pm2);
- pm2_int_reg2(pm2);
- pm2_int_reg3(pm2);
- pm2_int_reg4(pm2);
- pm2_int_reg5(pm2);
+ if (interrupt->reg[i] > 0)
+ interrupt->handler[i](pm2, interrupt->reg[i]);
+ }
return IRQ_HANDLED;
}
@@ -538,7 +540,7 @@ static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger,
curr_index = pm2xxx_current_to_regval(ich_out);
if (curr_index < 0) {
dev_err(pm2->dev,
- "Charger current too high: charging not started\n");
+ "Charger current too high, charging not started\n");
return -ENXIO;
}
@@ -614,6 +616,59 @@ static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
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_DIS);
+
+ /* Disable LED */
+ ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
+ PM2XXX_LED_SELECT_DIS);
+
return ret;
}
@@ -764,6 +819,15 @@ static void pm2xxx_charger_check_main_thermal_prot_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},
};
@@ -797,6 +861,8 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
pm2->dev = &i2c_client->dev;
pm2->gpadc = ab8500_gpadc_get();
+ 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");
@@ -844,6 +910,7 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
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.enabled = true;
/* Create a work queue for the charger */
pm2->charger_wq =
diff --git a/drivers/power/pm2301_charger.h b/drivers/power/pm2301_charger.h
index 27bf931..cc401d7 100644
--- a/drivers/power/pm2301_charger.h
+++ b/drivers/power/pm2301_charger.h
@@ -34,6 +34,8 @@
#define WD_TIMER 0x30 /* 4min */
#define WD_KICK_INTERVAL (60 * HZ)
+#define PM2XXX_NUM_INT_REG 0x6
+
/* Constant voltage/current */
#define PM2XXX_CONST_CURR 0x0
#define PM2XXX_CONST_VOLT 0x1
@@ -237,12 +239,32 @@
#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
@@ -446,6 +468,11 @@ struct pm2xxx_charger_event_flags {
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;
@@ -467,7 +494,7 @@ struct pm2xxx_charger {
int old_vbat;
int failure_case;
int failure_input_ovv;
- u8 pm2_int[6];
+ struct pm2xxx_interrupts *pm2_int;
struct ab8500_gpadc *gpadc;
struct regulator *regu;
struct pm2xxx_bm_data *bat;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 09/24] pm2301: Remove volt_now & curr_now properties
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (7 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 08/24] pm2301: Clean-up PM2301 interrupt management Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 10/24] ab8500-chargalg: Only root should have write permission on sysfs file Lee Jones
` (14 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Loic Pallardy <loic.pallardy@stericsson.com>
There is no support to measure the main charger voltage and
current using AB9540 gpadc. Therefore this has been removed
from the driver.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
---
drivers/power/pm2301_charger.c | 35 -----------------------------------
drivers/power/pm2301_charger.h | 1 -
2 files changed, 36 deletions(-)
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
index 316d5f0..16d8cfe 100644
--- a/drivers/power/pm2301_charger.c
+++ b/drivers/power/pm2301_charger.c
@@ -47,9 +47,7 @@ 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_NOW,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
- POWER_SUPPLY_PROP_CURRENT_NOW,
};
static int pm2xxx_charger_voltage_map[] = {
@@ -438,19 +436,6 @@ static irqreturn_t pm2xxx_irq_int(int irq, void *data)
return IRQ_HANDLED;
}
-static int pm2xxx_charger_get_ac_voltage(struct pm2xxx_charger *pm2)
-{
- int vch = 0;
-
- if (pm2->ac.charger_connected) {
- vch = ab8500_gpadc_convert(pm2->gpadc, MAIN_CHARGER_V);
- if (vch < 0)
- dev_err(pm2->dev, "%s gpadc conv failed,\n", __func__);
- }
-
- return vch;
-}
-
static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2)
{
int ret = 0;
@@ -473,19 +458,6 @@ out:
return ret;
}
-static int pm2xxx_charger_get_ac_current(struct pm2xxx_charger *pm2)
-{
- int ich = 0;
-
- if (pm2->ac.charger_online) {
- ich = ab8500_gpadc_convert(pm2->gpadc, MAIN_CHARGER_C);
- if (ich < 0)
- dev_err(pm2->dev, "%s gpadc conv failed\n", __func__);
- }
-
- return ich;
-}
-
static int pm2xxx_current_to_regval(int curr)
{
int i;
@@ -585,17 +557,10 @@ static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_PRESENT:
val->intval = pm2->ac.charger_connected;
break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- pm2->ac.charger_voltage = pm2xxx_charger_get_ac_voltage(pm2);
- val->intval = pm2->ac.charger_voltage * 1000;
- break;
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2);
val->intval = pm2->ac.cv_active;
break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = pm2xxx_charger_get_ac_current(pm2) * 1000;
- break;
default:
return -EINVAL;
}
diff --git a/drivers/power/pm2301_charger.h b/drivers/power/pm2301_charger.h
index cc401d7..4bace67 100644
--- a/drivers/power/pm2301_charger.h
+++ b/drivers/power/pm2301_charger.h
@@ -456,7 +456,6 @@ enum pm2xxx_source_reg_int6 {
struct pm2xxx_charger_info {
int charger_connected;
int charger_online;
- int charger_voltage;
int cv_active;
bool wd_expired;
};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 10/24] ab8500-chargalg: Only root should have write permission on sysfs file
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (8 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 09/24] pm2301: Remove volt_now & curr_now properties Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 19:17 ` Anton Vorontsov
2013-01-21 12:03 ` [PATCH 11/24] pm2301: Update watchdog for pm2xxx support Lee Jones
` (13 subsequent siblings)
23 siblings, 1 reply; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
Only root should have write permission on sysfs file ab8500_chargalg/chargalg.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/abx500_chargalg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 2463fa0..f59bc02 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1711,7 +1711,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_IWUSR,
};
static struct attribute *abx500_chargalg_chg[] = {
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 10/24] ab8500-chargalg: Only root should have write permission on sysfs file
2013-01-21 12:03 ` [PATCH 10/24] ab8500-chargalg: Only root should have write permission on sysfs file Lee Jones
@ 2013-01-21 19:17 ` Anton Vorontsov
0 siblings, 0 replies; 33+ messages in thread
From: Anton Vorontsov @ 2013-01-21 19:17 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jan 21, 2013 at 12:03:46PM +0000, Lee Jones wrote:
> Only root should have write permission on sysfs file ab8500_chargalg/chargalg.
>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
I guess it is a candidate for -stable?
> ---
> drivers/power/abx500_chargalg.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index 2463fa0..f59bc02 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -1711,7 +1711,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_IWUSR,
> };
>
> static struct attribute *abx500_chargalg_chg[] = {
> --
> 1.7.9.5
>
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 11/24] pm2301: Update watchdog for pm2xxx support
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (9 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 10/24] ab8500-chargalg: Only root should have write permission on sysfs file Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 12/24] ab8500-fg: Add test interface for u9540 Lee Jones
` (12 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Loic Pallardy <loic.pallardy@stericsson.com>
AB and PMxxx doesn't have same watchdog refresh period. Add watchdog
to refresh period parameters in x500 charger structure, this should
kick watchdog every 30sec. The AC charging should also kick both
pm2xxx and the AB charger watchdog.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/ab8500_charger.c | 6 ++++++
drivers/power/abx500_chargalg.c | 12 +++++++++++-
drivers/power/pm2301_charger.c | 2 ++
drivers/power/pm2301_charger.h | 2 +-
include/linux/mfd/abx500/ux500_chargalg.h | 3 +++
5 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 4c66172..0483e7c 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -93,6 +93,8 @@
#define CHARGER_STATUS_POLL 10 /* in ms */
+#define CHG_WD_INTERVAL (60 * HZ)
+
/* UsbLineStatus register - usb types */
enum ab8500_charger_link_status {
USB_STAT_NOT_CONFIGURED,
@@ -2953,7 +2955,9 @@ static int ab8500_charger_probe(struct platform_device *pdev)
ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
di->ac_chg.max_out_curr = ab8500_charger_current_map[
ARRAY_SIZE(ab8500_charger_current_map) - 1];
+ di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
di->ac_chg.enabled = di->pdata->ac_enabled;
+ di->ac_chg.external = false;
/* USB supply */
/* power_supply base class */
@@ -2972,7 +2976,9 @@ static int ab8500_charger_probe(struct platform_device *pdev)
ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
di->usb_chg.max_out_curr = ab8500_charger_current_map[
ARRAY_SIZE(ab8500_charger_current_map) - 1];
+ di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
di->usb_chg.enabled = di->pdata->usb_enabled;
+ di->usb_chg.external = false;
/* Create a work queue for the charger */
di->charger_wq =
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index f59bc02..7defb3e 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -445,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);
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
index 16d8cfe..0e41168 100644
--- a/drivers/power/pm2301_charger.c
+++ b/drivers/power/pm2301_charger.c
@@ -875,7 +875,9 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
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 =
diff --git a/drivers/power/pm2301_charger.h b/drivers/power/pm2301_charger.h
index 4bace67..5ae3573 100644
--- a/drivers/power/pm2301_charger.h
+++ b/drivers/power/pm2301_charger.h
@@ -32,7 +32,7 @@
/* Watchdog timeout constant */
#define WD_TIMER 0x30 /* 4min */
-#define WD_KICK_INTERVAL (60 * HZ)
+#define WD_KICK_INTERVAL (30 * HZ)
#define PM2XXX_NUM_INT_REG 0x6
diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h
index 5b77a61..d43ac0f 100644
--- a/include/linux/mfd/abx500/ux500_chargalg.h
+++ b/include/linux/mfd/abx500/ux500_chargalg.h
@@ -28,13 +28,16 @@ struct ux500_charger_ops {
* @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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 12/24] ab8500-fg: Add test interface for u9540
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (10 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 11/24] pm2301: Update watchdog for pm2xxx support Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 13/24] abx500-chargalg: Add new sysfs interface to get current charge status Lee Jones
` (11 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Michel JAOUEN <michel.jaouen@stericsson.com>
Add dedicated test functions for HATS framework. This patch
allows validating fuel gauge HW IP in all possible modes
supported by HW. Services are accessible through DebugFS
interface.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/Kconfig | 10 +
drivers/power/Makefile | 1 +
drivers/power/ab8500_fg.c | 271 +++--------
drivers/power/ab8500_fg.h | 242 ++++++++++
drivers/power/ab8500_fg_deepdebug.c | 823 ++++++++++++++++++++++++++++++++++
include/linux/mfd/abx500/ab8500-bm.h | 164 +++++++
6 files changed, 1297 insertions(+), 214 deletions(-)
create mode 100644 drivers/power/ab8500_fg.h
create mode 100644 drivers/power/ab8500_fg_deepdebug.c
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index b9de00d..16b4869 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -346,6 +346,15 @@ config AB8500_BM
help
Say Y to include support for AB8500 battery management.
+config AB8500_BM_DEEP_DEBUG
+ bool "AB8500 Battery Management Deep Debug"
+ depends on (AB8500_BM && DEEP_DEBUG)
+ default y
+ help
+ Say Y to include support for Deep Debug interface
+ for battery management.
+ If unsure, say N.
+
config CHARGER_PM2301
bool "PM2301 Battery Charger Driver"
depends on AB8500_BM
@@ -356,6 +365,7 @@ config CHARGER_PM2301
config PM2XXX_DEEP_DEBUG
bool "PM2XXX Deep Debug"
depends on DEEP_DEBUG && CHARGER_PM2301
+ default n
help
Deep Debug interface provides an access to all registers.
It allows to read or write directly a register.
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ef1e79c..476668d 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
+obj-$(CONFIG_AB8500_BM_DEEP_DEBUG) += ab8500_fg_deepdebug.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index a0cbbd3..238eeee 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -33,25 +33,12 @@
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/kernel.h>
+#include "ab8500_fg.h"
-#define MILLI_TO_MICRO 1000
-#define FG_LSB_IN_MA 1627
-#define QLSB_NANO_AMP_HOURS_X10 1129
-#define INS_CURR_TIMEOUT (3 * HZ)
-
-#define SEC_TO_SAMPLE(S) (S * 4)
-
-#define NBR_AVG_SAMPLES 20
-
-#define LOW_BAT_CHECK_INTERVAL (HZ / 16) /* 62.5 ms */
-
-#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */
-#define BATT_OK_MIN 2360 /* mV */
-#define BATT_OK_INCREMENT 50 /* mV */
-#define BATT_OK_MAX_NR_INCREMENTS 0xE
-
-/* FG constants */
-#define BATT_OVV 0x01
+char *charge_state[] = {
+ "CHARGE_INIT",
+ "CHARGE_READOUT",
+};
#define interpolate(x, x1, y1, x2, y2) \
((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
@@ -59,186 +46,6 @@
#define to_ab8500_fg_device_info(x) container_of((x), \
struct ab8500_fg, fg_psy);
-/**
- * struct ab8500_fg_interrupts - ab8500 fg interupts
- * @name: name of the interrupt
- * @isr function pointer to the isr
- */
-struct ab8500_fg_interrupts {
- char *name;
- irqreturn_t (*isr)(int irq, void *data);
-};
-
-enum ab8500_fg_discharge_state {
- AB8500_FG_DISCHARGE_INIT,
- AB8500_FG_DISCHARGE_INITMEASURING,
- AB8500_FG_DISCHARGE_INIT_RECOVERY,
- AB8500_FG_DISCHARGE_RECOVERY,
- AB8500_FG_DISCHARGE_READOUT_INIT,
- AB8500_FG_DISCHARGE_READOUT,
- AB8500_FG_DISCHARGE_WAKEUP,
-};
-
-static char *discharge_state[] = {
- "DISCHARGE_INIT",
- "DISCHARGE_INITMEASURING",
- "DISCHARGE_INIT_RECOVERY",
- "DISCHARGE_RECOVERY",
- "DISCHARGE_READOUT_INIT",
- "DISCHARGE_READOUT",
- "DISCHARGE_WAKEUP",
-};
-
-enum ab8500_fg_charge_state {
- AB8500_FG_CHARGE_INIT,
- AB8500_FG_CHARGE_READOUT,
-};
-
-static char *charge_state[] = {
- "CHARGE_INIT",
- "CHARGE_READOUT",
-};
-
-enum ab8500_fg_calibration_state {
- AB8500_FG_CALIB_INIT,
- AB8500_FG_CALIB_WAIT,
- AB8500_FG_CALIB_END,
-};
-
-struct ab8500_fg_avg_cap {
- int avg;
- int samples[NBR_AVG_SAMPLES];
- __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
- int pos;
- int nbr_samples;
- int sum;
-};
-
-struct ab8500_fg_cap_scaling {
- bool enable;
- int cap_to_scale[2];
- int disable_cap_level;
- int scaled_cap;
-};
-
-struct ab8500_fg_battery_capacity {
- int max_mah_design;
- int max_mah;
- int mah;
- int permille;
- int level;
- int prev_mah;
- int prev_percent;
- int prev_level;
- int user_mah;
- struct ab8500_fg_cap_scaling cap_scale;
-};
-
-struct ab8500_fg_flags {
- bool fg_enabled;
- bool conv_done;
- bool charging;
- bool fully_charged;
- bool force_full;
- bool low_bat_delay;
- bool low_bat;
- bool bat_ovv;
- bool batt_unknown;
- bool calibrate;
- bool user_cap;
- bool batt_id_received;
-};
-
-struct inst_curr_result_list {
- struct list_head list;
- int *result;
-};
-
-/**
- * struct ab8500_fg - ab8500 FG device information
- * @dev: Pointer to the structure device
- * @node: a list of AB8500 FGs, hence prepared for reentrance
- * @irq holds the CCEOC interrupt number
- * @vbat: Battery voltage in mV
- * @vbat_nom: Nominal battery voltage in mV
- * @inst_curr: Instantenous battery current in mA
- * @avg_curr: Average battery current in mA
- * @bat_temp battery temperature
- * @fg_samples: Number of samples used in the FG accumulation
- * @accu_charge: Accumulated charge from the last conversion
- * @recovery_cnt: Counter for recovery mode
- * @high_curr_cnt: Counter for high current mode
- * @init_cnt: Counter for init mode
- * @low_bat_cnt Counter for number of consecutive low battery measures
- * @nbr_cceoc_irq_cnt Counter for number of CCEOC irqs received since enabled
- * @recovery_needed: Indicate if recovery is needed
- * @high_curr_mode: Indicate if we're in high current mode
- * @init_capacity: Indicate if initial capacity measuring should be done
- * @turn_off_fg: True if fg was off before current measurement
- * @calib_state State during offset calibration
- * @discharge_state: Current discharge state
- * @charge_state: Current charge state
- * @ab8500_fg_started Completion struct used for the instant current start
- * @ab8500_fg_complete Completion struct used for the instant current reading
- * @flags: Structure for information about events triggered
- * @bat_cap: Structure for battery capacity specific parameters
- * @avg_cap: Average capacity filter
- * @parent: Pointer to the struct ab8500
- * @gpadc: Pointer to the struct gpadc
- * @bm: Platform specific battery management information
- * @fg_psy: Structure that holds the FG specific battery properties
- * @fg_wq: Work queue for running the FG algorithm
- * @fg_periodic_work: Work to run the FG algorithm periodically
- * @fg_low_bat_work: Work to check low bat condition
- * @fg_reinit_work Work used to reset and reinitialise the FG algorithm
- * @fg_work: Work to run the FG algorithm instantly
- * @fg_acc_cur_work: Work to read the FG accumulator
- * @fg_check_hw_failure_work: Work for checking HW state
- * @cc_lock: Mutex for locking the CC
- * @fg_kobject: Structure of type kobject
- */
-struct ab8500_fg {
- struct device *dev;
- struct list_head node;
- int irq;
- int vbat;
- int vbat_nom;
- int inst_curr;
- int avg_curr;
- int bat_temp;
- int fg_samples;
- int accu_charge;
- int recovery_cnt;
- int high_curr_cnt;
- int init_cnt;
- int low_bat_cnt;
- int nbr_cceoc_irq_cnt;
- bool recovery_needed;
- bool high_curr_mode;
- bool init_capacity;
- bool turn_off_fg;
- enum ab8500_fg_calibration_state calib_state;
- enum ab8500_fg_discharge_state discharge_state;
- enum ab8500_fg_charge_state charge_state;
- struct completion ab8500_fg_started;
- struct completion ab8500_fg_complete;
- struct ab8500_fg_flags flags;
- struct ab8500_fg_battery_capacity bat_cap;
- struct ab8500_fg_avg_cap avg_cap;
- struct ab8500 *parent;
- struct ab8500_gpadc *gpadc;
- struct abx500_bm_data *bm;
- struct power_supply fg_psy;
- struct workqueue_struct *fg_wq;
- struct delayed_work fg_periodic_work;
- struct delayed_work fg_low_bat_work;
- struct delayed_work fg_reinit_work;
- struct work_struct fg_work;
- struct work_struct fg_acc_cur_work;
- struct delayed_work fg_check_hw_failure_work;
- struct mutex cc_lock;
- struct kobject fg_kobject;
-};
static LIST_HEAD(ab8500_fg_list);
/**
@@ -470,7 +277,7 @@ static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
* Enable/Disable coulomb counter.
* On failure returns negative value.
*/
-static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
+int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
{
int ret = 0;
mutex_lock(&di->cc_lock);
@@ -483,11 +290,13 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
goto cc_err;
/* Program the samples */
- ret = abx500_set_register_interruptible(di->dev,
- AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
- di->fg_samples);
- if (ret)
- goto cc_err;
+ if (!di->test.enable) {
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU,
+ di->fg_samples);
+ if (ret)
+ goto cc_err;
+ }
/* Start the CC */
ret = abx500_set_register_interruptible(di->dev, AB8500_RTC,
@@ -1403,7 +1212,7 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
}
}
-static void ab8500_fg_charge_state_to(struct ab8500_fg *di,
+void ab8500_fg_charge_state_to(struct ab8500_fg *di,
enum ab8500_fg_charge_state new_state)
{
dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n",
@@ -1991,12 +1800,17 @@ static void ab8500_fg_instant_work(struct work_struct *work)
static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
{
struct ab8500_fg *di = _di;
- if (!di->nbr_cceoc_irq_cnt) {
- di->nbr_cceoc_irq_cnt++;
- complete(&di->ab8500_fg_started);
- } else {
- di->nbr_cceoc_irq_cnt = 0;
- complete(&di->ab8500_fg_complete);
+
+ if (di->test.enable)
+ complete(&di->test.cceoc_complete);
+ else {
+ if (!di->nbr_cceoc_irq_cnt) {
+ di->nbr_cceoc_irq_cnt++;
+ complete(&di->ab8500_fg_started);
+ } else {
+ di->nbr_cceoc_irq_cnt = 0;
+ complete(&di->ab8500_fg_complete);
+ }
}
return IRQ_HANDLED;
}
@@ -2011,8 +1825,31 @@ static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
static irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di)
{
struct ab8500_fg *di = _di;
- di->calib_state = AB8500_FG_CALIB_END;
- queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+ if (di->test.enable) {
+ complete(&di->test.cc_int_calib_complete);
+ } else {
+ di->calib_state = AB8500_FG_CALIB_END;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab8500_fg_cceoc_handler() - end of conversion isr.
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_fg structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+
+static irqreturn_t ab8500_fg_cceoc_handler(int irq, void *_di)
+{
+ struct ab8500_fg *di = _di;
+
+ if (di->test.enable)
+ complete(&di->test.cceoc_complete);
+
return IRQ_HANDLED;
}
@@ -2027,7 +1864,10 @@ static irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di)
{
struct ab8500_fg *di = _di;
- queue_work(di->fg_wq, &di->fg_acc_cur_work);
+ if (di->test.enable)
+ complete(&di->test.nconv_accu_complete);
+ else
+ queue_work(di->fg_wq, &di->fg_acc_cur_work);
return IRQ_HANDLED;
}
@@ -2613,6 +2453,7 @@ static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
{"LOW_BAT_F", ab8500_fg_lowbatf_handler},
{"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler},
{"CCEOC", ab8500_fg_cc_data_end_handler},
+ {"CCEOC", ab8500_fg_cceoc_handler},
};
static char *supply_interface[] = {
@@ -2676,6 +2517,8 @@ static int ab8500_fg_probe(struct platform_device *pdev)
ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+ ab8500_fg_test_init(di);
+
/* Create a work queue for running the FG algorithm */
di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
if (di->fg_wq == NULL) {
diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
new file mode 100644
index 0000000..946840b
--- /dev/null
+++ b/drivers/power/ab8500_fg.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2012
+ *
+ * Main and Back-up battery management driver.
+ *
+ * Note: Backup battery management is required in case of Li-Ion battery and not
+ * for capacitive battery. HREF boards have capacitive battery and hence backup
+ * battery management is not used and the supported code is available in this
+ * driver.
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ * Author: Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#define MILLI_TO_MICRO 1000
+#define FG_LSB_IN_MA 1627
+#define QLSB_NANO_AMP_HOURS_X10 1129
+#define INS_CURR_TIMEOUT (3 * HZ)
+
+#define SEC_TO_SAMPLE(S) (S * 4)
+
+#define NBR_AVG_SAMPLES 20
+
+#define LOW_BAT_CHECK_INTERVAL (HZ / 16) /* 62.5 ms */
+
+#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */
+#define BATT_OK_MIN 2360 /* mV */
+#define BATT_OK_INCREMENT 50 /* mV */
+#define BATT_OK_MAX_NR_INCREMENTS 0xE
+
+/* FG constants */
+#define BATT_OVV 0x01
+
+/**
+ * struct ab8500_fg_interrupts - ab8500 fg interupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab8500_fg_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+enum ab8500_fg_discharge_state {
+ AB8500_FG_DISCHARGE_INIT,
+ AB8500_FG_DISCHARGE_INITMEASURING,
+ AB8500_FG_DISCHARGE_INIT_RECOVERY,
+ AB8500_FG_DISCHARGE_RECOVERY,
+ AB8500_FG_DISCHARGE_READOUT_INIT,
+ AB8500_FG_DISCHARGE_READOUT,
+ AB8500_FG_DISCHARGE_WAKEUP,
+};
+
+enum ab8500_fg_charge_state {
+ AB8500_FG_CHARGE_INIT,
+ AB8500_FG_CHARGE_READOUT,
+};
+
+enum ab8500_fg_calibration_state {
+ AB8500_FG_CALIB_INIT,
+ AB8500_FG_CALIB_WAIT,
+ AB8500_FG_CALIB_END,
+};
+
+struct ab8500_fg_avg_cap {
+ int avg;
+ int samples[NBR_AVG_SAMPLES];
+ __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+ int pos;
+ int nbr_samples;
+ int sum;
+};
+
+struct ab8500_fg_cap_scaling {
+ bool enable;
+ int cap_to_scale[2];
+ int disable_cap_level;
+ int scaled_cap;
+};
+
+struct ab8500_fg_battery_capacity {
+ int max_mah_design;
+ int max_mah;
+ int mah;
+ int permille;
+ int level;
+ int prev_mah;
+ int prev_percent;
+ int prev_level;
+ int user_mah;
+ struct ab8500_fg_cap_scaling cap_scale;
+};
+
+struct ab8500_fg_flags {
+ bool fg_enabled;
+ bool conv_done;
+ bool charging;
+ bool fully_charged;
+ bool force_full;
+ bool low_bat_delay;
+ bool low_bat;
+ bool bat_ovv;
+ bool batt_unknown;
+ bool calibrate;
+ bool user_cap;
+ bool batt_id_received;
+};
+
+struct inst_curr_result_list {
+ struct list_head list;
+ int *result;
+};
+
+/**
+ * struct ab8500_fg_test - ab8500 FG device information in test mode
+ * @enable: true if fg in test mode else false
+ * @cc_int_offset: offset for internal calibration
+ * @cc_soft_offset: offset for software calibration
+ * @cc_sample_conv: sample read
+ * @cc_sample_conv_calib_uA: sample converted in uA
+ * @cceoc_complete: pointer to the struct completion, to indicate
+ * the completion of internal calibration and
+ * one sample reading
+ * @nconv_accu_complete: pointer to the struct completion, to indicate
+ * the completion of sample to accumulate
+ * @cc_int_calib_complete: pointer to the struct completion, to indicate
+ * the completion of internal calibration
+ * @lock: Mutex for locking the CC
+ */
+struct ab8500_fg_test {
+ bool enable;
+ u8 cc_int_offset;
+ u8 cc_soft_offset;
+ u16 cc_sample_conv;
+ int cc_sample_conv_calib_uA;
+ struct completion cceoc_complete;
+ struct completion nconv_accu_complete;
+ struct completion cc_int_calib_complete;
+ struct mutex lock;
+};
+
+/**
+ * struct ab8500_fg - ab8500 FG device information
+ * @dev: Pointer to the structure device
+ * @node: a list of AB8500 FGs, hence prepared for reentrance
+ * @irq holds the CCEOC interrupt number
+ * @vbat: Battery voltage in mV
+ * @vbat_nom: Nominal battery voltage in mV
+ * @inst_curr: Instantenous battery current in mA
+ * @avg_curr: Average battery current in mA
+ * @bat_temp battery temperature
+ * @fg_samples: Number of samples used in the FG accumulation
+ * @accu_charge: Accumulated charge from the last conversion
+ * @recovery_cnt: Counter for recovery mode
+ * @high_curr_cnt: Counter for high current mode
+ * @init_cnt: Counter for init mode
+ * @low_bat_cnt Counter for number of consecutive low battery measures
+ * @nbr_cceoc_irq_cnt Counter for number of CCEOC irqs received since enabled
+ * @recovery_needed: Indicate if recovery is needed
+ * @high_curr_mode: Indicate if we're in high current mode
+ * @init_capacity: Indicate if initial capacity measuring should be done
+ * @turn_off_fg: True if fg was off before current measurement
+ * @calib_state State during offset calibration
+ * @discharge_state: Current discharge state
+ * @charge_state: Current charge state
+ * @ab8500_fg_started Completion struct used for the instant current start
+ * @ab8500_fg_complete Completion struct used for the instant current reading
+ * @flags: Structure for information about events triggered
+ * @bat_cap: Structure for battery capacity specific parameters
+ * @avg_cap: Average capacity filter
+ * @parent: Pointer to the struct ab8500
+ * @gpadc: Pointer to the struct gpadc
+ * @bm: Platform specific battery management information
+ * @fg_psy: Structure that holds the FG specific battery properties
+ * @fg_wq: Work queue for running the FG algorithm
+ * @fg_periodic_work: Work to run the FG algorithm periodically
+ * @fg_low_bat_work: Work to check low bat condition
+ * @fg_reinit_work Work used to reset and reinitialise the FG algorithm
+ * @fg_work: Work to run the FG algorithm instantly
+ * @fg_acc_cur_work: Work to read the FG accumulator
+ * @fg_check_hw_failure_work: Work for checking HW state
+ * @cc_lock: Mutex for locking the CC
+ * @fg_kobject: Structure of type kobject
+ */
+struct ab8500_fg {
+ struct device *dev;
+ struct list_head node;
+ int irq;
+ int vbat;
+ int vbat_nom;
+ int inst_curr;
+ int avg_curr;
+ int bat_temp;
+ int fg_samples;
+ int accu_charge;
+ int recovery_cnt;
+ int high_curr_cnt;
+ int init_cnt;
+ int low_bat_cnt;
+ int nbr_cceoc_irq_cnt;
+ bool recovery_needed;
+ bool high_curr_mode;
+ bool init_capacity;
+ bool turn_off_fg;
+ enum ab8500_fg_calibration_state calib_state;
+ enum ab8500_fg_discharge_state discharge_state;
+ enum ab8500_fg_charge_state charge_state;
+ struct completion ab8500_fg_started;
+ struct completion ab8500_fg_complete;
+ struct ab8500_fg_flags flags;
+ struct ab8500_fg_battery_capacity bat_cap;
+ struct ab8500_fg_avg_cap avg_cap;
+ struct ab8500 *parent;
+ struct ab8500_gpadc *gpadc;
+ struct abx500_bm_data *bm;
+ struct power_supply fg_psy;
+ struct workqueue_struct *fg_wq;
+ struct delayed_work fg_periodic_work;
+ struct delayed_work fg_low_bat_work;
+ struct delayed_work fg_reinit_work;
+ struct work_struct fg_work;
+ struct work_struct fg_acc_cur_work;
+ struct delayed_work fg_check_hw_failure_work;
+ struct mutex cc_lock;
+ struct kobject fg_kobject;
+};
+
+extern char *discharge_state[];
+extern char *charge_state[];
+
+int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable);
+void ab8500_fg_charge_state_to(struct ab8500_fg *di,
+ enum ab8500_fg_charge_state new_state);
+void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
+ enum ab8500_fg_charge_state new_state);
+/* test initialization */
+#ifdef CONFIG_AB8500_BM_DEEP_DEBUG
+void ab8500_fg_test_init(struct ab8500_fg *di);
+#else
+void ab8500_fg_test_init(struct ab8500_fg *di) {return; }
+#endif
diff --git a/drivers/power/ab8500_fg_deepdebug.c b/drivers/power/ab8500_fg_deepdebug.c
new file mode 100644
index 0000000..8845de6
--- /dev/null
+++ b/drivers/power/ab8500_fg_deepdebug.c
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2012
+ *
+ * Battery Management Deep debug support
+ *
+ * Note: Deep debug features are needed to perform the
+ * HW validation of the platform
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Cedric Madianga <cedric.madianga@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+#include "ab8500_fg.h"
+
+/* Exposure to the debugfs interface for test purpose only */
+
+/**
+ * ab8500_fg_test_algorithm_en() - enable or disable gas gauge test mode
+ * @di: pointer to the ab8500_fg structure
+ * @enable: enable/disable gas gaude test mode
+ *
+ * Return 0 or error code
+ * Only used for test purpose
+ */
+int ab8500_fg_test_algorithm_en(struct ab8500_fg *di, bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ /* Set coulomb counter in test mode. */
+ dev_dbg(di->dev, "Try to put gas gauge in test mode\n");
+ cancel_delayed_work_sync(&di->fg_periodic_work);
+ if (di->flags.fg_enabled) {
+ ret = ab8500_fg_coulomb_counter(di, false);
+ if (ret)
+ return ret;
+ }
+ di->test.enable = true;
+ dev_dbg(di->dev, "Gas gauge in test mode\n");
+ } else {
+ /* Set coulomb counter in normal mode. */
+ dev_dbg(di->dev, "Try to put gas gauge in normal mode\n");
+ if (di->flags.fg_enabled) {
+ ret = ab8500_fg_coulomb_counter(di, false);
+ if (ret)
+ return ret;
+ }
+
+ di->init_capacity = true;
+ ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT);
+ ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT);
+
+ di->flags.batt_unknown = true;
+ di->flags.batt_id_received = false;
+
+ di->test.enable = false;
+ ab8500_fg_coulomb_counter(di, true);
+
+ di->flags.calibrate = true;
+ di->calib_state = AB8500_FG_CALIB_INIT;
+ dev_dbg(di->dev, "Gas gauge in normal mode\n");
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_is_test_is_algorithm_en() -
+ * Return 1 if fg algorithm is enable 0 else
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Only used for test purpose
+ */
+bool ab8500_fg_test_is_algorithm_en(struct ab8500_fg *di)
+{
+ return di->test.enable;
+}
+
+/**
+ * ab8500_fg_test_en() - enable coulomb counter
+ * @di: pointer to the ab8500_fg structure
+ * @enable: enable/disable
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+
+int ab8500_fg_test_en(struct ab8500_fg *di, bool enable)
+{
+ return ab8500_fg_coulomb_counter(di, enable);
+}
+
+/**
+ * ab8500_fg_test_is_en() - Return 1 if fg is enabled 0 else
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Only used for test purpose
+ */
+bool ab8500_fg_test_is_en(struct ab8500_fg *di)
+{
+ return di->flags.fg_enabled;
+}
+
+/**
+ * ab8500_fg_test_set_cc_int_n_avg() - set number of conversion to average for
+ * internal calibration
+ * @di: pointer to the ab8500_fg structure
+ * @val: number of conversion to average
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_cc_int_n_avg(struct ab8500_fg *di, u8 val)
+{
+ int ret;
+ u8 cc_int_n_avg = 0;
+
+ switch (val) {
+ case 4:
+ cc_int_n_avg = CC_INT_CAL_SAMPLES_4;
+ break;
+ case 8:
+ cc_int_n_avg = CC_INT_CAL_SAMPLES_8;
+ break;
+ case 16:
+ cc_int_n_avg = CC_INT_CAL_SAMPLES_16;
+ break;
+ default:
+ dev_err(di->dev,
+ "incorrect sample values\n"
+ "correct sample values should be 4, 8 or 16\n");
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_INT_CAL_N_AVG_MASK, cc_int_n_avg);
+ if (ret < 0)
+ dev_err(di->dev,
+ "set number of conversion to average failed\n");
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_get_cc_int_n_avg() - get number of conversion to average for
+ * internal calibration
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return number of conversion to average or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_cc_int_n_avg(struct ab8500_fg *di)
+{
+ int ret;
+ u8 val = 0;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_CTRL_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "get number of conversion to average failed\n");
+ return ret;
+ }
+
+ switch (val & CC_INT_CAL_N_AVG_MASK) {
+ case CC_INT_CAL_SAMPLES_4:
+ ret = 4;
+ break;
+ case CC_INT_CAL_SAMPLES_8:
+ ret = 8;
+ break;
+ case CC_INT_CAL_SAMPLES_16:
+ ret = 16;
+ break;
+ case CC_INT_CAL_N_AVG_MASK:
+ ret = 16;
+ break;
+ default:
+ dev_err(di->dev,
+ "incorrect val read in AB8500_GASG_CC_CTRL_REG");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_int_calib() - launch internal calibration
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return result of calibration or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_int_calib(struct ab8500_fg *di)
+{
+ int ret;
+ u8 val;
+
+ mutex_lock(&di->test.lock);
+ dev_dbg(di->dev, "Internal calibration ongoing...\n");
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "enabling offset average computation failed\n");
+ goto err;
+ }
+
+ /* wait for completion of calibration */
+ if (!wait_for_completion_timeout(&di->test.cc_int_calib_complete,
+ 5*HZ)) {
+ dev_err(di->dev,
+ "timeout: didn't receive CCIntCalib interrupt\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_CNTR_AVGOFF_REG, &val);
+ if (ret < 0)
+ goto err;
+
+ di->test.cc_int_offset = val;
+ dev_dbg(di->dev, "Internal Calibration done...\n");
+ mutex_unlock(&di->test.lock);
+
+ return di->test.cc_int_offset;
+
+err:
+ mutex_unlock(&di->test.lock);
+ dev_err(di->dev, "Internal calibration failure\n");
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_soft_calib() - launch software calibration
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return result of calibration or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_soft_calib(struct ab8500_fg *di)
+{
+ int ret;
+ u8 low_data, high_data;
+
+ mutex_lock(&di->test.lock);
+ dev_dbg(di->dev, "Software calibration ongoing...\n");
+
+ /* Set ADconverter in calibration mode */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_CALIB, CC_CALIB);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "set ADconverter in calibration mode failed\n");
+ goto err;
+ }
+
+ /* wait for completion of calibration */
+ if (!wait_for_completion_timeout(&di->test.cceoc_complete, 1*HZ)) {
+ dev_err(di->dev,
+ "timeout: didn't receive CCEOC interrupt\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (!wait_for_completion_timeout(&di->test.cceoc_complete, 1*HZ)) {
+ dev_err(di->dev,
+ "timeout: didn't receive CCEOC interrupt\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Don't set ADConverter in calibration mode */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_CALIB, 0x00);
+ if (ret < 0) {
+ dev_err(di->dev, "stopping calibration mode failed\n");
+ goto err;
+ }
+
+ /* Transfer sample and accumulator values */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ READ_REQ, READ_REQ);
+ if (ret < 0) {
+ dev_err(di->dev, "transfer accumulator data failed\n");
+ goto err;
+ }
+
+ /* Retrieve sample conversion */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_SMPL_CNVL_REG, &low_data);
+ if (ret < 0) {
+ dev_err(di->dev, "read low byte sample conversion failed\n");
+ goto err;
+ }
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_SMPL_CNVH_REG, &high_data);
+ if (ret < 0) {
+ dev_err(di->dev, "read high byte sample conversion failed\n");
+ goto err;
+ }
+
+ di->test.cc_soft_offset = (high_data << 8) | low_data;
+ dev_dbg(di->dev, "Software Calibration done...\n");
+ mutex_unlock(&di->test.lock);
+
+ return di->test.cc_soft_offset;
+
+err:
+ mutex_unlock(&di->test.lock);
+ dev_err(di->dev, "Software calibration failure\n");
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_set_cc_soft_offset() - set software offset into register
+ * @di: pointer to the ab8500_fg structure
+ * @enable: manual offset to be stored
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_cc_soft_offset(struct ab8500_fg *di, u8 val)
+{
+ int ret;
+
+ ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_OFFSET_REG, val);
+ if (ret < 0)
+ dev_err(di->dev,
+ "set software offset failed\n");
+ else
+ di->test.cc_soft_offset = val;
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_get_cc_soft_offset() - get software offset into register
+ * @di: pointer to the ab8500_fg structure
+ * @enable: manual offset to be stored
+ *
+ * Return software offset or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_cc_soft_offset(struct ab8500_fg *di, u8 *val)
+{
+ int ret;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_OFFSET_REG, val);
+ if (ret < 0)
+ dev_err(di->dev,
+ "get software offset failed\n");
+ else
+ di->test.cc_soft_offset = *val;
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_set_rst_accu_sample_counter() - set reset accumulator
+ * sample counter bit
+ * @di: pointer to the ab8500_fg structure
+ * @enable: enable/disable reset acc
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_rst_accu_sample_counter(struct ab8500_fg *di,
+ bool enable)
+{
+ int ret;
+ u8 val = 0;
+
+ if (enable)
+ val = RESET_ACCU;
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ RESET_ACCU, val);
+ if (ret < 0)
+ dev_err(di->dev,
+ "set accumulator sample counter reset bit failed\n");
+
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_get_rst_accu_sample_counter() - get reset accumulator
+ * sample counter bit
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return reset accumulator sample counter bit or error code
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_rst_accu_sample_counter(struct ab8500_fg *di)
+{
+ u8 val = 0;
+ int ret;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_CTRL_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "get accumulator sample counter reset bit failed\n");
+ return ret;
+ }
+
+ if (val & RESET_ACCU)
+ ret = 1;
+ else
+ ret = 0;
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_set_cc_mux_offset() - set coumlomb counter offset
+ * @di: pointer to the ab8500_fg structure
+ * @enable: enable/disable offset
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_cc_mux_offset(struct ab8500_fg *di, bool enable)
+{
+ int ret;
+ u8 val = 0;
+
+ if (enable)
+ val = CC_MUXOFFSET;
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ CC_MUXOFFSET, val);
+ if (ret < 0)
+ dev_err(di->dev,
+ "set mux offset failed\n");
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_get_cc_mux_offset() - get coulomb counter mux offset
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Get mux offset or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_cc_mux_offset(struct ab8500_fg *di)
+{
+ u8 val = 0;
+ int ret;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_CTRL_REG, &val);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "get mux offset failed\n");
+ return ret;
+ }
+
+ if (val & CC_MUXOFFSET)
+ ret = 1;
+ else
+ ret = 0;
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_read_sample() - read one sample
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return sample or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_read_sample(struct ab8500_fg *di)
+{
+ int ret;
+ u8 low_data, high_data;
+
+ mutex_lock(&di->test.lock);
+ dev_dbg(di->dev, "Sample reading ongoing...\n");
+
+ /* wait for completion of calibration */
+ if (!wait_for_completion_timeout(&di->test.cceoc_complete, 1*HZ)) {
+ dev_err(di->dev,
+ "timeout: didn't receive CCEOC interrupt\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (!wait_for_completion_timeout(&di->test.cceoc_complete, 1*HZ)) {
+ dev_err(di->dev,
+ "timeout: didn't receive CCEOC interrupt\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Transfer sample and accumulator values */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ READ_REQ, READ_REQ);
+ if (ret < 0) {
+ dev_err(di->dev, "transfer accumulator data failed\n");
+ goto err;
+ }
+
+ /* Retrieve sample conversion */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_SMPL_CNVL_REG, &low_data);
+ if (ret < 0) {
+ dev_err(di->dev, "read low byte sample conversion failed\n");
+ goto err;
+ }
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_SMPL_CNVH_REG, &high_data);
+ if (ret < 0) {
+ dev_err(di->dev, "read high byte sample conversion failed\n");
+ goto err;
+ }
+
+ di->test.cc_sample_conv = (high_data << 8) | low_data;
+
+ dev_dbg(di->dev, "Sample reading done...\n");
+ mutex_unlock(&di->test.lock);
+
+ return di->test.cc_sample_conv;
+
+err:
+ mutex_unlock(&di->test.lock);
+ dev_err(di->dev, "Sample reading failure\n");
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_sample_calibrate() - compute sample calibrated data
+ * @di: pointer to the ab8500_fg structure
+ * @val: raw sample
+ *
+ * Return sample calibrated value
+ * Only used for test purpose
+ */
+int ab8500_fg_test_sample_calibrate(struct ab8500_fg *di, int val)
+{
+ int ret;
+
+ ret = ab8500_fg_test_get_cc_mux_offset(di);
+ if (ret < 0)
+ return ret;
+
+ if (ret)
+ return val - di->test.cc_int_offset;
+ else
+ return val - di->test.cc_soft_offset;
+}
+
+/**
+ * ab8500_fg_test_sample_calibrate_to_uA() - convert sample calibrated data
+ * to nuAH
+ * @di: pointer to the ab8500_fg structure
+ * @val: calibrate sample
+ *
+ * Return sample calibrated value
+ * Only used for test purpose
+ */
+int ab8500_fg_test_sample_calibrate_to_uA(struct ab8500_fg *di, int val)
+{
+ di->test.cc_sample_conv_calib_uA = val * QLSB_NANO_AMP_HOURS_X10;
+ return di->test.cc_sample_conv_calib_uA;
+}
+
+/**
+ * ab8500_fg_test_get_nconv_accu() - get number of conversion accumulated
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return umber of conversion accumulated or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_nconv_accu(struct ab8500_fg *di, u8 *val)
+{
+ int ret;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU, val);
+ if (ret < 0)
+ dev_err(di->dev,
+ "get nb samples to be accumulated failed\n");
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_get_nconv_accu_to_uA() - get number of conversion accumulated
+ * in uA
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return umber of conversion accumulated or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_nconv_accu_to_uA(struct ab8500_fg *di, int val)
+{
+ return val * di->test.cc_sample_conv_calib_uA;
+}
+
+/**
+ * ab8500_fg_test_set_rst_nconv_accu() - allows to reset the 21bits accumulator data
+ * @di: pointer to the ab8500_fg structure
+ * @enable: enable/disable to reset the 21bits accumulator data
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_rst_nconv_accu(struct ab8500_fg *di,
+ bool enable)
+{
+ int ret;
+ u8 val = 0;
+
+ if (enable)
+ val = RESET_ACCU;
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL,
+ RESET_ACCU, val);
+ if (ret < 0)
+ dev_err(di->dev,
+ "set accumulator reset bit failed\n");
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_get_rst_nconv_accu() - get staus of ResetNconvAccu bit
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return accumulator reset bit or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_rst_nconv_accu(struct ab8500_fg *di)
+{
+ u8 val = 0;
+ int ret;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_CTRL, &val);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "get accumulator reset bit failedd\n");
+ goto out;
+ }
+
+ if (val & RESET_ACCU)
+ ret = 1;
+ else
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_set_nconv_accu_nb_sample() - set number of sample conversion
+ * to be accumulated in 21bits accumulator
+ * @di: pointer to the ab8500_fg structure
+ * @nb_sample: number of samples
+ *
+ * Return 0 or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_set_nconv_accu_nb_sample(struct ab8500_fg *di, u8 val)
+{
+ int ret;
+
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU, val);
+ if (ret < 0)
+ dev_err(di->dev,
+ "set number of samples to accumulated failed\n");
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_get_nconv_accu_nb_sample() - get number of sample conversion
+ * to be accumulated in 21bits accumulator
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return number of samples to be accumulated or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_get_nconv_accu_nb_sample(struct ab8500_fg *di, u8 *val)
+{
+ int ret;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU, val);
+ if (ret < 0)
+ dev_err(di->dev,
+ "get number of samples to accumulated failed\n");
+
+ return ret;
+}
+
+/**
+ * ab8500_fg_test_read_nconv_accu_sample() - read of accumulator after N samples
+ * @di: pointer to the ab8500_fg structure
+ *
+ * Return sample or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_read_nconv_accu_sample(struct ab8500_fg *di)
+{
+ int ret;
+ int nb_sample;
+ u8 low_data, med_data, high_data;
+
+ /* Get nb sample to average */
+ ret = ab8500_fg_test_get_nconv_accu_nb_sample(di, &nb_sample);
+ if (ret < 0)
+ goto out;
+
+ mutex_lock(&di->test.lock);
+ dev_dbg(di->dev, "N Samples reading ongoing...\n");
+
+ /* Launch measure */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL,
+ RD_NCONV_ACCU_REQ, RD_NCONV_ACCU_REQ);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "launch measure failed\n");
+ goto err;
+ }
+
+ /* wait for completion of measure */
+ if (!wait_for_completion_timeout(&di->test.nconv_accu_complete,
+ nb_sample*(HZ/4))) {
+ dev_err(di->dev,
+ "timeout: didn't receive NCONV_ACCU interrupt\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Retrieve samples */
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_LOW, &low_data);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "read low data failed\n");
+ goto err;
+ }
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_MED, &med_data);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "read med data failed\n");
+ goto err;
+ }
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE,
+ AB8500_GASG_CC_NCOV_ACCU_HIGH, &high_data);
+ if (ret < 0) {
+ dev_err(di->dev,
+ "read high data failed\n");
+ goto err;
+ }
+
+ dev_dbg(di->dev, "N Samples reading done...\n");
+ mutex_unlock(&di->test.lock);
+
+ return (high_data << 16) | (med_data << 8) | low_data;
+
+err:
+ mutex_unlock(&di->test.lock);
+ dev_err(di->dev, "Sample reading failure\n");
+out:
+ return ret;
+
+}
+
+/**
+ * ab8500_fg_test_read_nconv_accu_sample_to_uA - convert accu read in uA
+ * @di: pointer to the ab8500_fg structure
+ * @val: accu read
+ *
+ * Return sample or error code on failure
+ * Only used for test purpose
+ */
+int ab8500_fg_test_read_nconv_accu_sample_to_uA(struct ab8500_fg *di, int val)
+{
+ return val * QLSB_NANO_AMP_HOURS_X10;
+}
+
+void __devinit ab8500_fg_test_init(struct ab8500_fg *di)
+{
+ /* Initialize objects need for test purpose. */
+ di->test.enable = false;
+ di->test.cc_int_offset = 0;
+ di->test.cc_soft_offset = 0;
+ di->test.cc_sample_conv = 0;
+ di->test.cc_sample_conv_calib_uA = 0;
+ init_completion(&di->test.cceoc_complete);
+ init_completion(&di->test.nconv_accu_complete);
+ init_completion(&di->test.cc_int_calib_complete);
+ mutex_init(&di->test.lock);
+}
+
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index ec796c7..b800332 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -485,4 +485,168 @@ static inline int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
}
#endif
+
+#ifdef CONFIG_AB8500_BM_DEEP_DEBUG
+int ab8500_fg_test_algorithm_en(struct ab8500_fg *di, bool enable);
+bool ab8500_fg_test_is_algorithm_en(struct ab8500_fg *di);
+int ab8500_fg_test_en(struct ab8500_fg *di, bool enable);
+bool ab8500_fg_test_is_en(struct ab8500_fg *di);
+int ab8500_fg_test_set_cc_int_n_avg(struct ab8500_fg *di, u8 val);
+int ab8500_fg_test_get_cc_int_n_avg(struct ab8500_fg *di);
+int ab8500_fg_test_int_calib(struct ab8500_fg *di);
+int ab8500_fg_test_soft_calib(struct ab8500_fg *di);
+int ab8500_fg_test_set_cc_soft_offset(struct ab8500_fg *di, u8 val);
+int ab8500_fg_test_get_cc_soft_offset(struct ab8500_fg *di, u8 *val);
+int ab8500_fg_test_set_rst_accu_sample_counter(struct ab8500_fg *di,
+ bool enable);
+int ab8500_fg_test_get_rst_accu_sample_counter(struct ab8500_fg *di);
+int ab8500_fg_test_set_cc_mux_offset(struct ab8500_fg *di, bool enable);
+int ab8500_fg_test_get_cc_mux_offset(struct ab8500_fg *di);
+int ab8500_fg_test_read_sample(struct ab8500_fg *di);
+int ab8500_fg_test_sample_calibrate(struct ab8500_fg *di, int val);
+int ab8500_fg_test_sample_calibrate_to_uA(struct ab8500_fg *di, int val);
+int ab8500_fg_test_get_nconv_accu(struct ab8500_fg *di, u8 *val);
+int ab8500_fg_test_get_nconv_accu_to_uA(struct ab8500_fg *di, int val);
+int ab8500_fg_test_set_rst_nconv_accu(struct ab8500_fg *di,
+ bool enable);
+int ab8500_fg_test_get_rst_nconv_accu(struct ab8500_fg *di);
+int ab8500_fg_test_set_nconv_accu_nb_sample(struct ab8500_fg *di, u8 val);
+int ab8500_fg_test_get_nconv_accu_nb_sample(struct ab8500_fg *di, u8 *val);
+int ab8500_fg_test_read_nconv_accu_sample(struct ab8500_fg *di);
+int ab8500_fg_test_read_nconv_accu_sample_to_uA(struct ab8500_fg *di, int val);
+#else
+static inline int ab8500_fg_test_algorithm_en(struct ab8500_fg *di, bool enable)
+{
+ return -ENODEV;
+}
+
+static inline bool ab8500_fg_test_is_algorithm_en(struct ab8500_fg *di)
+{
+ return false;
+}
+
+static inline int ab8500_fg_test_en(struct ab8500_fg *di, bool enable)
+{
+ return -ENODEV;
+}
+
+static inline bool ab8500_fg_test_is_en(struct ab8500_fg *di)
+{
+ return false;
+}
+
+static inline int ab8500_fg_test_set_cc_int_n_avg(struct ab8500_fg *di, u8 val)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_get_cc_int_n_avg(struct ab8500_fg *di)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_int_calib(struct ab8500_fg *di)
+{
+ return -ENODEV;
+}
+
+static inline int ab8500_fg_test_soft_calib(struct ab8500_fg *di)
+{
+ return -ENODEV;
+}
+
+static inline int ab8500_fg_test_set_cc_soft_offset(struct ab8500_fg *di,
+ u8 val)
+{
+ return 0;
+}
+static inline int ab8500_fg_test_get_cc_soft_offset(struct ab8500_fg *di)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_set_rst_accu_sample_counter(struct ab8500_fg
+ *di, bool enable)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_get_rst_accu_sample_counter(struct ab8500_fg
+ *di)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_set_cc_mux_offset(struct ab8500_fg *di,
+ bool enable)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_get_cc_mux_offset(struct ab8500_fg *di)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_read_sample(struct ab8500_fg *di)
+{
+ return -ENODEV;
+}
+
+static inline int ab8500_fg_test_sample_calibrate(struct ab8500_fg *di, int val)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_sample_calibrate_to_uA(struct ab8500_fg *di,
+ int val)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_get_nconv_accu(struct ab8500_fg *di)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_get_nconv_accu_to_uA(struct ab8500_fg *di,
+ int val)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_set_rst_nconv_accu(struct ab8500_fg *di,
+ bool enable)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_get_rst_nconv_accu(struct ab8500_fg *di)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_set_nconv_accu_nb_sample(struct ab8500_fg *di,
+ u8 val)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_get_nconv_accu_nb_sample(struct ab8500_fg *di)
+{
+ return 0;
+}
+
+static inline int ab8500_fg_test_read_nconv_accu_sample(struct ab8500_fg *di)
+{
+ return -ENODEV;
+}
+
+static inline int ab8500_fg_test_read_nconv_accu_sample_to_uA(struct ab8500_fg
+ *di, int val)
+{
+ return 0;
+}
+
+#endif
#endif /* _AB8500_BM_H */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 13/24] abx500-chargalg: Add new sysfs interface to get current charge status
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (11 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 12/24] ab8500-fg: Add test interface for u9540 Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 14/24] ab8500-charger: Add support for autopower on AB8505 and AB9540 Lee Jones
` (10 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
Allow a user to check on AB8500 charging status from debugfs.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/ab8500_charger.c | 3 +++
drivers/power/abx500_chargalg.c | 22 +++++++++++++++++++++-
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 0483e7c..d834566 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2764,6 +2764,9 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
enum ab8500_usb_state bm_usb_state;
unsigned mA = *((unsigned *)power);
+ if (!di)
+ return NOTIFY_DONE;
+
if (event != USB_EVENT_VBUS) {
dev_dbg(di->dev, "not a standard host, returning\n");
return NOTIFY_DONE;
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 7defb3e..694f592 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1654,6 +1654,25 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
/* Exposure to the sysfs interface */
/**
+ * ab8500_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 ab8500_chargalg_sysfs_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct ab8500_chargalg *di = container_of(kobj,
+ struct ab8500_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
* @attr: pointer to the struct attribute
@@ -1721,7 +1740,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
static struct attribute abx500_chargalg_en_charger = \
{
.name = "chargalg",
- .mode = S_IWUSR,
+ .mode = S_IRUGO | S_IWUSR,
};
static struct attribute *abx500_chargalg_chg[] = {
@@ -1730,6 +1749,7 @@ static struct attribute *abx500_chargalg_chg[] = {
};
static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
+ .show = ab8500_chargalg_sysfs_show,
.store = abx500_chargalg_sysfs_charger,
};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 14/24] ab8500-charger: Add support for autopower on AB8505 and AB9540
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (12 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 13/24] abx500-chargalg: Add new sysfs interface to get current charge status Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 15/24] ab8500-fg: Go to INIT_RECOVERY when charger removed Lee Jones
` (9 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Nicolas Guion <nicolas.guion@stericsson.com>
Accessing autopower register fails on the AB8505 and ab9540 as
the fallback software control register has moved.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Reviewed-by: Mattias WALLIN <mattias.wallin@stericsson.com>
Reviewed-by: Nicolas GUION <nicolas.guion@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/ab8500_charger.c | 52 +++++++++++++++++++++++++++-------------
1 file changed, 35 insertions(+), 17 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index d834566..da965ee 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -95,6 +95,8 @@
#define CHG_WD_INTERVAL (60 * HZ)
+#define AB8500_SW_CONTROL_FALLBACK 0x03
+
/* UsbLineStatus register - usb types */
enum ab8500_charger_link_status {
USB_STAT_NOT_CONFIGURED,
@@ -312,42 +314,58 @@ static enum power_supply_property ab8500_charger_usb_props[] = {
static void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
bool fallback)
{
+ u8 val;
u8 reg;
+ u8 bank;
+ u8 bit;
int ret;
dev_dbg(di->dev, "SW Fallback: %d\n", fallback);
+ if (is_ab8500(di->parent)) {
+ bank = 0x15;
+ reg = 0x0;
+ bit = 3;
+ } else {
+ bank = AB8500_SYS_CTRL1_BLOCK;
+ reg = AB8500_SW_CONTROL_FALLBACK;
+ bit = 0;
+ }
+
/* read the register containing fallback bit */
- ret = abx500_get_register_interruptible(di->dev, 0x15, 0x00, ®);
- if (ret) {
- dev_err(di->dev, "%d write failed\n", __LINE__);
+ ret = abx500_get_register_interruptible(di->dev, bank, reg, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%d read failed\n", __LINE__);
return;
}
- /* enable the OPT emulation registers */
- ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x2);
- if (ret) {
- dev_err(di->dev, "%d write failed\n", __LINE__);
- return;
+ if (is_ab8500(di->parent)) {
+ /* enable the OPT emulation registers */
+ ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x2);
+ if (ret) {
+ dev_err(di->dev, "%d write failed\n", __LINE__);
+ goto disable_otp;
+ }
}
if (fallback)
- reg |= 0x8;
+ val |= (1 << bit);
else
- reg &= ~0x8;
+ val &= ~(1 << bit);
/* write back the changed fallback bit value to register */
- ret = abx500_set_register_interruptible(di->dev, 0x15, 0x00, reg);
+ ret = abx500_set_register_interruptible(di->dev, bank, reg, val);
if (ret) {
dev_err(di->dev, "%d write failed\n", __LINE__);
- return;
}
- /* disable the set OTP registers again */
- ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x0);
- if (ret) {
- dev_err(di->dev, "%d write failed\n", __LINE__);
- return;
+disable_otp:
+ if (is_ab8500(di->parent)) {
+ /* disable the set OTP registers again */
+ ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x0);
+ if (ret) {
+ dev_err(di->dev, "%d write failed\n", __LINE__);
+ }
}
}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 15/24] ab8500-fg: Go to INIT_RECOVERY when charger removed
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (13 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 14/24] ab8500-charger: Add support for autopower on AB8505 and AB9540 Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 16/24] ab8500-bm: Flush all work queues before suspending Lee Jones
` (8 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Martin Bergstr?m <martin.bergstrom@stericsson.com>
When the charger is removed we need to go to INIT_RECOVERY
state instead of directly to RECOVERY state.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Martin Bergstrom <martin.bergstrom@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/ab8500_fg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 238eeee..0378aab 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -1450,7 +1450,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
if (di->recovery_needed) {
ab8500_fg_discharge_state_to(di,
- AB8500_FG_DISCHARGE_RECOVERY);
+ AB8500_FG_DISCHARGE_INIT_RECOVERY);
queue_delayed_work(di->fg_wq,
&di->fg_periodic_work, 0);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 16/24] ab8500-bm: Flush all work queues before suspending
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (14 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 15/24] ab8500-fg: Go to INIT_RECOVERY when charger removed Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 19:14 ` Anton Vorontsov
2013-01-21 12:03 ` [PATCH 17/24] ab8500-fg-deepdebug: Create Deep Debug interface Lee Jones
` (7 subsequent siblings)
23 siblings, 1 reply; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Jonas Aaberg <jonas.aberg@stericsson.com>
Flush all workqueues at suspend time to avoid suspending during work.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/61915
---
drivers/power/ab8500_charger.c | 11 +++++++++++
drivers/power/ab8500_fg.c | 5 +++++
2 files changed, 16 insertions(+)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index da965ee..a632b94 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2866,6 +2866,17 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
if (delayed_work_pending(&di->check_hw_failure_work))
cancel_delayed_work(&di->check_hw_failure_work);
+ flush_delayed_work(&di->attach_work);
+ flush_delayed_work(&di->usb_charger_attached_work);
+ flush_delayed_work(&di->ac_charger_attached_work);
+ flush_delayed_work(&di->check_usbchgnotok_work);
+ flush_delayed_work(&di->check_vbat_work);
+ flush_delayed_work(&di->kick_wd_work);
+
+ flush_work(&di->usb_link_status_work);
+ flush_work(&di->ac_work);
+ flush_work(&di->detect_usb_type_work);
+
return 0;
}
#else
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 0378aab..1a78116 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2410,6 +2410,11 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
struct ab8500_fg *di = platform_get_drvdata(pdev);
flush_delayed_work(&di->fg_periodic_work);
+ flush_work(&di->fg_work);
+ flush_work(&di->fg_acc_cur_work);
+ flush_delayed_work(&di->fg_reinit_work);
+ flush_delayed_work(&di->fg_low_bat_work);
+ flush_delayed_work(&di->fg_check_hw_failure_work);
/*
* If the FG is enabled we will disable it before going to suspend
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 16/24] ab8500-bm: Flush all work queues before suspending
2013-01-21 12:03 ` [PATCH 16/24] ab8500-bm: Flush all work queues before suspending Lee Jones
@ 2013-01-21 19:14 ` Anton Vorontsov
2013-01-22 8:43 ` Lee Jones
0 siblings, 1 reply; 33+ messages in thread
From: Anton Vorontsov @ 2013-01-21 19:14 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jan 21, 2013 at 12:03:52PM +0000, Lee Jones wrote:
> From: Jonas Aaberg <jonas.aberg@stericsson.com>
>
> Flush all workqueues at suspend time to avoid suspending during work.
>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
> Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/61915
I do remeber I was commenting on that patch, and I was asking to merge the
changes into the patches that introduce the workqueues (aka problems).
Now I see that "ab8500_charger: Detect charger removal" commit already in
the mainline, i.e. I merged the patch with the *known* possible
regression. The issue was known to me during the first review cycle, and I
pointed you to my review comments. But the patch sneaked in anyway...
> ---
> drivers/power/ab8500_charger.c | 11 +++++++++++
> drivers/power/ab8500_fg.c | 5 +++++
> 2 files changed, 16 insertions(+)
>
> diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> index da965ee..a632b94 100644
> --- a/drivers/power/ab8500_charger.c
> +++ b/drivers/power/ab8500_charger.c
> @@ -2866,6 +2866,17 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
> if (delayed_work_pending(&di->check_hw_failure_work))
> cancel_delayed_work(&di->check_hw_failure_work);
>
> + flush_delayed_work(&di->attach_work);
> + flush_delayed_work(&di->usb_charger_attached_work);
> + flush_delayed_work(&di->ac_charger_attached_work);
> + flush_delayed_work(&di->check_usbchgnotok_work);
> + flush_delayed_work(&di->check_vbat_work);
> + flush_delayed_work(&di->kick_wd_work);
> +
> + flush_work(&di->usb_link_status_work);
> + flush_work(&di->ac_work);
> + flush_work(&di->detect_usb_type_work);
> +
> return 0;
> }
> #else
> diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> index 0378aab..1a78116 100644
> --- a/drivers/power/ab8500_fg.c
> +++ b/drivers/power/ab8500_fg.c
> @@ -2410,6 +2410,11 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
> struct ab8500_fg *di = platform_get_drvdata(pdev);
>
> flush_delayed_work(&di->fg_periodic_work);
> + flush_work(&di->fg_work);
> + flush_work(&di->fg_acc_cur_work);
> + flush_delayed_work(&di->fg_reinit_work);
> + flush_delayed_work(&di->fg_low_bat_work);
> + flush_delayed_work(&di->fg_check_hw_failure_work);
>
> /*
> * If the FG is enabled we will disable it before going to suspend
> --
> 1.7.9.5
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 16/24] ab8500-bm: Flush all work queues before suspending
2013-01-21 19:14 ` Anton Vorontsov
@ 2013-01-22 8:43 ` Lee Jones
0 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-22 8:43 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 21 Jan 2013, Anton Vorontsov wrote:
> On Mon, Jan 21, 2013 at 12:03:52PM +0000, Lee Jones wrote:
> > From: Jonas Aaberg <jonas.aberg@stericsson.com>
> >
> > Flush all workqueues at suspend time to avoid suspending during work.
> >
> > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> > Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
> > Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
> > Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/61915
>
> I do remeber I was commenting on that patch, and I was asking to merge the
> changes into the patches that introduce the workqueues (aka problems).
>
> Now I see that "ab8500_charger: Detect charger removal" commit already in
> the mainline, i.e. I merged the patch with the *known* possible
> regression. The issue was known to me during the first review cycle, and I
> pointed you to my review comments. But the patch sneaked in anyway...
I assure you I didn't _sneak_ any patch in. I'm working on small, simplified
patch-sets for your convenience. I didn't look at the review comments
for this patch until I placed it in this patch-set. By the time I did
look, the offending patch was already in -next. To solve this type of
situation, I can either send you the whole lot in one go with
everything fixed-up and squashed, or we can continue dealing with more
manageable patch-sets where situations like this are bound to happen
occasionally.
Even when working directly with Mainline, we still have patches which
fixup issues which were introduced earlier in the development
cycle. I'm not sure on your view of it exactly, but I don't think it's
the end of the world?
> > ---
> > drivers/power/ab8500_charger.c | 11 +++++++++++
> > drivers/power/ab8500_fg.c | 5 +++++
> > 2 files changed, 16 insertions(+)
> >
> > diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
> > index da965ee..a632b94 100644
> > --- a/drivers/power/ab8500_charger.c
> > +++ b/drivers/power/ab8500_charger.c
> > @@ -2866,6 +2866,17 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
> > if (delayed_work_pending(&di->check_hw_failure_work))
> > cancel_delayed_work(&di->check_hw_failure_work);
> >
> > + flush_delayed_work(&di->attach_work);
> > + flush_delayed_work(&di->usb_charger_attached_work);
> > + flush_delayed_work(&di->ac_charger_attached_work);
> > + flush_delayed_work(&di->check_usbchgnotok_work);
> > + flush_delayed_work(&di->check_vbat_work);
> > + flush_delayed_work(&di->kick_wd_work);
> > +
> > + flush_work(&di->usb_link_status_work);
> > + flush_work(&di->ac_work);
> > + flush_work(&di->detect_usb_type_work);
> > +
> > return 0;
> > }
> > #else
> > diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
> > index 0378aab..1a78116 100644
> > --- a/drivers/power/ab8500_fg.c
> > +++ b/drivers/power/ab8500_fg.c
> > @@ -2410,6 +2410,11 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
> > struct ab8500_fg *di = platform_get_drvdata(pdev);
> >
> > flush_delayed_work(&di->fg_periodic_work);
> > + flush_work(&di->fg_work);
> > + flush_work(&di->fg_acc_cur_work);
> > + flush_delayed_work(&di->fg_reinit_work);
> > + flush_delayed_work(&di->fg_low_bat_work);
> > + flush_delayed_work(&di->fg_check_hw_failure_work);
> >
> > /*
> > * If the FG is enabled we will disable it before going to suspend
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 17/24] ab8500-fg-deepdebug: Create Deep Debug interface
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (15 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 16/24] ab8500-bm: Flush all work queues before suspending Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 18/24] pm2301: Enable vbat low monitoring Lee Jones
` (6 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Michel JAOUEN <michel.jaouen@stericsson.com>
This patch creates "fg" debugfs directory under existing ab8500
directory to access fuel gauge test services from userspace
(HATS framework).
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Signed-off-by: Cedric Madianga <cedric.madianga@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/ab8500_fg_deepdebug.c | 873 ++++++++++++++++++++++++++++++++++-
1 file changed, 871 insertions(+), 2 deletions(-)
diff --git a/drivers/power/ab8500_fg_deepdebug.c b/drivers/power/ab8500_fg_deepdebug.c
index 8845de6..37c5f12 100644
--- a/drivers/power/ab8500_fg_deepdebug.c
+++ b/drivers/power/ab8500_fg_deepdebug.c
@@ -11,13 +11,28 @@
*/
#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/device.h>
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
#include <linux/power_supply.h>
+#include <linux/kobject.h>
#include <linux/mfd/ab8500.h>
#include <linux/mfd/abx500.h>
+#include <linux/slab.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/delay.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500.h>
#include <linux/time.h>
+#include <linux/completion.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/ux500_deepdebug.h>
+#include <linux/uaccess.h>
#include "ab8500_fg.h"
@@ -725,8 +740,8 @@ int ab8500_fg_test_get_nconv_accu_nb_sample(struct ab8500_fg *di, u8 *val)
*/
int ab8500_fg_test_read_nconv_accu_sample(struct ab8500_fg *di)
{
- int ret;
- int nb_sample;
+ int ret = 0;
+ u8 nb_sample;
u8 low_data, med_data, high_data;
/* Get nb sample to average */
@@ -807,6 +822,858 @@ int ab8500_fg_test_read_nconv_accu_sample_to_uA(struct ab8500_fg *di, int val)
return val * QLSB_NANO_AMP_HOURS_X10;
}
+/*
+ * DebugFS interface
+ */
+
+#define AB8500_FG_NAME_STRING "fg"
+
+static int ab8500_fg_test_algorithm_en_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ bool status;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ status = ab8500_fg_test_is_algorithm_en(di);
+
+ return seq_printf(s, "%d\n", status);
+}
+
+static int ab8500_fg_test_algorithm_en_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_algorithm_en_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_fg_test_algorithm_en_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev;
+ struct ab8500_fg *di;
+ unsigned long user_enable;
+ bool enable;
+ int err;
+
+ dev = ((struct seq_file *)(file->private_data))->private;
+ if (!dev)
+ return -ENOMEM;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ /* Get userspace string and assure termination */
+ err = kstrtoul_from_user(user_buf, count, 0, &user_enable);
+ if (err)
+ return -EINVAL;
+
+ if ((user_enable == 0) || (user_enable == 1)) {
+ enable = (bool) user_enable;
+ err = ab8500_fg_test_algorithm_en(di, enable);
+ if (err)
+ return err;
+ } else {
+ dev_err(di->dev, "Wrong input\n"
+ "Enter 0 => Disable Gas Gauge test mode\n"
+ "Enter 1 => Enable Gas Gauge test mode\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations ab8500_fg_test_algorithm_en_fops = {
+ .open = ab8500_fg_test_algorithm_en_open,
+ .read = seq_read,
+ .write = ab8500_fg_test_algorithm_en_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_en_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ bool status;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ status = ab8500_fg_test_is_en(di);
+
+ return seq_printf(s, "%d\n", status);
+}
+
+static int ab8500_fg_test_en_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_fg_test_en_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_fg_test_en_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev;
+ struct ab8500_fg *di;
+ unsigned long user_enable;
+ bool enable;
+ int err;
+
+ dev = ((struct seq_file *)(file->private_data))->private;
+ if (!dev)
+ return -ENOMEM;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ /* Get userspace string and assure termination */
+ err = kstrtoul_from_user(user_buf, count, 0, &user_enable);
+ if (err)
+ return -EINVAL;
+
+ if ((user_enable == 0) || (user_enable == 1)) {
+ enable = (bool) user_enable;
+ err = ab8500_fg_test_en(di, enable);
+ if (err)
+ return err;
+ } else {
+ dev_err(di->dev, "Wrong input\n"
+ "Enter 0 => Disable Gas Gauge\n"
+ "Enter 1 => Enable Gas Gauge\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct file_operations ab8500_fg_test_en_fops = {
+ .open = ab8500_fg_test_en_open,
+ .read = seq_read,
+ .write = ab8500_fg_test_en_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_cc_int_n_avg_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ int result;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ result = ab8500_fg_test_get_cc_int_n_avg(di);
+ if (result < 0)
+ return result;
+
+ return seq_printf(s, "%d\n", result);
+}
+
+static int ab8500_fg_test_cc_int_n_avg_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_cc_int_n_avg_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_fg_test_cc_int_n_avg_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab8500_fg *di;
+ char buf[32];
+ int buf_size;
+ unsigned long user_cc_int_n_avg;
+ u8 cc_int_n_avg;
+ int err;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_cc_int_n_avg);
+ if (err)
+ return -EINVAL;
+
+ cc_int_n_avg = (u8) user_cc_int_n_avg;
+ err = ab8500_fg_test_set_cc_int_n_avg(di, cc_int_n_avg);
+ if (err)
+ return err;
+
+ return buf_size;
+}
+
+static const struct file_operations ab8500_fg_test_cc_int_n_avg_fops = {
+ .open = ab8500_fg_test_cc_int_n_avg_open,
+ .read = seq_read,
+ .write = ab8500_fg_test_cc_int_n_avg_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_int_calib_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ int result;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ result = ab8500_fg_test_int_calib(di);
+ if (result < 0)
+ return result;
+
+ return seq_printf(s, "%d\n", result);
+}
+
+static int ab8500_fg_test_int_calib_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_fg_test_int_calib_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_fg_test_int_calib_fops = {
+ .open = ab8500_fg_test_int_calib_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_soft_calib_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ int result;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ result = ab8500_fg_test_soft_calib(di);
+ if (result < 0)
+ return result;
+
+ return seq_printf(s, "%d\n", result);
+}
+
+static int ab8500_fg_test_soft_calib_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_soft_calib_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_fg_test_soft_calib_fops = {
+ .open = ab8500_fg_test_soft_calib_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_soft_offset_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ int ret;
+ u8 result;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ ret = ab8500_fg_test_get_cc_soft_offset(di, &result);
+ if (ret < 0)
+ return ret;
+
+ return seq_printf(s, "%d\n", result);
+}
+
+static int ab8500_fg_test_soft_offset_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_soft_offset_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_fg_test_soft_offset_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab8500_fg *di;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ u8 val;
+ int err;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+
+ val = (u8) user_val;
+ err = ab8500_fg_test_set_cc_soft_offset(di, val);
+ if (err)
+ return err;
+
+ return buf_size;
+}
+
+static const struct file_operations ab8500_fg_test_soft_offset_fops = {
+ .open = ab8500_fg_test_soft_offset_open,
+ .read = seq_read,
+ .write = ab8500_fg_test_soft_offset_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+
+static int ab8500_fg_test_rst_accu_sample_counter_print(struct seq_file *s,
+ void *p)
+{
+ struct ab8500_fg *di;
+ int result;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ result = ab8500_fg_test_get_rst_accu_sample_counter(di);
+
+ return seq_printf(s, "%d\n", result);
+}
+
+static int ab8500_fg_test_rst_accu_sample_counter_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_rst_accu_sample_counter_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_fg_test_rst_accu_sample_counter_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev;
+ struct ab8500_fg *di;
+ char buf[32];
+ int buf_size;
+ unsigned long user_enable;
+ bool enable;
+ int err;
+
+ dev = ((struct seq_file *)(file->private_data))->private;
+ if (!dev)
+ return -ENOMEM;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_enable);
+ if (err)
+ return -EINVAL;
+ if ((user_enable == 0) || (user_enable == 1)) {
+ enable = (bool) user_enable;
+ err = ab8500_fg_test_set_rst_accu_sample_counter(di, enable);
+ if (err)
+ return err;
+ } else {
+ dev_err(di->dev, "Wrong input\n"
+ "Enter 0. => Disable Reset Acc\n"
+ "Enter 1. => Enable Reset Acc\n");
+ return -EINVAL;
+ }
+ return buf_size;
+}
+
+static const struct file_operations ab8500_fg_test_rst_accu_sample_fops = {
+ .open = ab8500_fg_test_rst_accu_sample_counter_open,
+ .read = seq_read,
+ .write = ab8500_fg_test_rst_accu_sample_counter_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_cc_mux_offset_print(struct seq_file *s,
+ void *p)
+{
+ struct ab8500_fg *di;
+ int result;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ result = ab8500_fg_test_get_cc_mux_offset(di);
+ if (result < 0)
+ return result;
+
+ return seq_printf(s, "%d\n", result);
+}
+
+static int ab8500_fg_test_cc_mux_offset_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_cc_mux_offset_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_fg_test_cc_mux_offset_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev;
+ struct ab8500_fg *di;
+ char buf[32];
+ int buf_size;
+ unsigned long user_enable;
+ bool enable;
+ int err;
+
+ dev = ((struct seq_file *)(file->private_data))->private;
+ if (!dev)
+ return -ENOMEM;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_enable);
+ if (err)
+ return -EINVAL;
+ if ((user_enable == 0) || (user_enable == 1)) {
+ enable = (bool) user_enable;
+ err = ab8500_fg_test_set_cc_mux_offset(di, enable);
+ if (err)
+ return err;
+ } else {
+ dev_err(di->dev, "Wrong input\n"
+ "Enter 0. => Manual offset\n"
+ "Enter 1. => Internal offset\n");
+ return -EINVAL;
+ }
+ return buf_size;
+}
+
+static const struct file_operations ab8500_fg_test_cc_mux_offset_fops = {
+ .open = ab8500_fg_test_cc_mux_offset_open,
+ .read = seq_read,
+ .write = ab8500_fg_test_cc_mux_offset_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_read_sample_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ int result, cc_sample_calibrate, cc_sample_calibrate_uA;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ result = ab8500_fg_test_read_sample(di);
+ if (result < 0)
+ return result;
+
+ cc_sample_calibrate = ab8500_fg_test_sample_calibrate(di, result);
+ if (cc_sample_calibrate < 0)
+ return cc_sample_calibrate;
+
+ cc_sample_calibrate_uA = ab8500_fg_test_sample_calibrate_to_uA(di,
+ cc_sample_calibrate);
+
+ return seq_printf(s, "0x%X,%d,%d\n", result, cc_sample_calibrate,
+ cc_sample_calibrate_uA);
+}
+
+static int ab8500_fg_test_read_sample_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_read_sample_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_fg_test_read_sample_fops = {
+ .open = ab8500_fg_test_read_sample_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_get_nconv_accu_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ int ret, nconv_accu_uA;
+ u8 val;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ ret = ab8500_fg_test_get_nconv_accu(di, &val);
+ if (ret < 0)
+ return ret;
+
+ nconv_accu_uA = ab8500_fg_test_get_nconv_accu_to_uA(di, (int)val);
+
+ return seq_printf(s, "%d,%d\n", val, nconv_accu_uA);
+}
+
+static int ab8500_fg_test_get_nconv_accu_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_get_nconv_accu_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_fg_test_get_nconv_accu_fops = {
+ .open = ab8500_fg_test_get_nconv_accu_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_rst_nconv_accu_print(struct seq_file *s,
+ void *p)
+{
+ struct ab8500_fg *di;
+ int result;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ result = ab8500_fg_test_get_rst_nconv_accu(di);
+ if (result < 0)
+ return result;
+
+ return seq_printf(s, "%d\n", result);
+}
+
+static int ab8500_fg_test_rst_nconv_accu_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_rst_nconv_accu_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_fg_test_rst_nconv_accu_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev;
+ struct ab8500_fg *di;
+ char buf[32];
+ int buf_size;
+ unsigned long user_enable;
+ bool enable;
+ int err;
+
+ dev = ((struct seq_file *)(file->private_data))->private;
+ if (!dev)
+ return -ENOMEM;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_enable);
+ if (err)
+ return -EINVAL;
+ if ((user_enable == 0) || (user_enable == 1)) {
+ enable = (bool) user_enable;
+ err = ab8500_fg_test_set_rst_nconv_accu(di, enable);
+ if (err)
+ return err;
+ } else {
+ dev_err(di->dev, "Wrong input\n"
+ "Enter 0. => Disable Reset Acc\n"
+ "Enter 1. => Enable Reset Acc\n");
+ return -EINVAL;
+ }
+ return buf_size;
+}
+
+static const struct file_operations ab8500_fg_test_rst_nconv_accu_fops = {
+ .open = ab8500_fg_test_rst_nconv_accu_open,
+ .read = seq_read,
+ .write = ab8500_fg_test_rst_nconv_accu_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_nconv_accu_nb_sample_print(struct seq_file *s,
+ void *p)
+{
+ struct ab8500_fg *di;
+ u8 result;
+ int ret;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ ret = ab8500_fg_test_get_nconv_accu_nb_sample(di, &result);
+ if (ret < 0)
+ return ret;
+
+ return seq_printf(s, "%d\n", result);
+}
+
+static int ab8500_fg_test_nconv_accu_nb_sample_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_nconv_accu_nb_sample_print,
+ inode->i_private);
+}
+
+static ssize_t ab8500_fg_test_nconv_accu_nb_sample_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ab8500_fg *di;
+ char buf[32];
+ int buf_size;
+ unsigned long user_nb_sample;
+ u8 nb_sample;
+ int err;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_nb_sample);
+ if (err)
+ return -EINVAL;
+
+ nb_sample = (u8) user_nb_sample;
+ err = ab8500_fg_test_set_nconv_accu_nb_sample(di, nb_sample);
+ if (err)
+ return err;
+
+ return buf_size;
+}
+
+static const struct file_operations ab8500_fg_test_nconv_accu_nb_sample_fops = {
+ .open = ab8500_fg_test_nconv_accu_nb_sample_open,
+ .read = seq_read,
+ .write = ab8500_fg_test_nconv_accu_nb_sample_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_fg_test_nconv_accu_sample_print(struct seq_file *s, void *p)
+{
+ struct ab8500_fg *di;
+ int result, nconv_accu_sample_uA;
+
+ di = ab8500_fg_get();
+ if (!di)
+ return -ENOMEM;
+
+ result = ab8500_fg_test_read_nconv_accu_sample(di);
+ if (result < 0)
+ return result;
+
+ nconv_accu_sample_uA = ab8500_fg_test_read_nconv_accu_sample_to_uA(di,
+ result);
+
+ return seq_printf(s, "0x%X,%d\n", result, nconv_accu_sample_uA);
+}
+
+static int ab8500_fg_test_nconv_accu_sample_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_fg_test_nconv_accu_sample_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_fg_test_nconv_accu_sample_fops = {
+ .open = ab8500_fg_test_nconv_accu_sample_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab8500_fg_dir;
+
+int ab8500_bm_deepdebug_probe(struct ddbg_service *data,
+ struct dentry *parent)
+{
+ struct dentry *file;
+ struct dentry *dir;
+ int ret = -ENOMEM;
+
+ ab8500_fg_dir = debugfs_create_dir(AB8500_FG_NAME_STRING,
+ parent);
+ if (!ab8500_fg_dir)
+ goto err;
+
+ file = debugfs_create_file("fg_algo_enable", (S_IRUGO | S_IWUGO),
+ ab8500_fg_dir, data, &ab8500_fg_test_algorithm_en_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("fg_enable", (S_IRUGO | S_IWUGO),
+ ab8500_fg_dir, data, &ab8500_fg_test_en_fops);
+ if (!file)
+ goto err;
+
+ dir = debugfs_create_dir("internal_calibration", ab8500_fg_dir);
+ if (!dir)
+ goto err;
+
+ file = debugfs_create_file("cc_int_n_avg", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_cc_int_n_avg_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("cc_int_offset", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_int_calib_fops);
+ if (!file)
+ goto err;
+
+ dir = debugfs_create_dir("software_calibration", ab8500_fg_dir);
+ if (!dir)
+ goto err;
+
+ file = debugfs_create_file("cc_sample_conv", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_soft_calib_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("cc_soft_offset", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_soft_offset_fops);
+ if (!file)
+ goto err;
+
+ dir = debugfs_create_dir("cc_one_sample", ab8500_fg_dir);
+ if (!dir)
+ goto err;
+
+ file = debugfs_create_file("cc_rst_accu_sample", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_rst_accu_sample_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("cc_mux_offset", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_cc_mux_offset_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("cc_one_sample", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_read_sample_fops);
+ if (!file)
+ goto err;
+ file = debugfs_create_file("cc_nconv_accu", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_get_nconv_accu_fops);
+ if (!file)
+ goto err;
+
+ dir = debugfs_create_dir("read_n_samples", ab8500_fg_dir);
+ if (!dir)
+ goto err;
+
+ file = debugfs_create_file("cc_rst_nconv_accu", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_rst_nconv_accu_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("cc_mux_offset", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_cc_mux_offset_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("cc_nb_samples_to_average",
+ (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_nconv_accu_nb_sample_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("cc_retrieve_samples", (S_IRUGO | S_IWUGO),
+ dir, data, &ab8500_fg_test_nconv_accu_sample_fops);
+ if (!file)
+ goto err;
+
+ return 0;
+
+err:
+ if (ab8500_fg_dir)
+ debugfs_remove_recursive(ab8500_fg_dir);
+ pr_err("failed to create debugfs entries.\n");
+
+ return ret;
+}
+
+static struct ddbg_service ab8500_fg_ddbg_services = {
+ .name = AB8500_FG_NAME_STRING,
+ .probe = ab8500_bm_deepdebug_probe,
+};
+
+/*
+ * Initialization
+ */
+
void __devinit ab8500_fg_test_init(struct ab8500_fg *di)
{
/* Initialize objects need for test purpose. */
@@ -819,5 +1686,7 @@ void __devinit ab8500_fg_test_init(struct ab8500_fg *di)
init_completion(&di->test.nconv_accu_complete);
init_completion(&di->test.cc_int_calib_complete);
mutex_init(&di->test.lock);
+
+ deep_debug_service_access_register(&ab8500_fg_ddbg_services);
}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 18/24] pm2301: Enable vbat low monitoring
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (16 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 17/24] ab8500-fg-deepdebug: Create Deep Debug interface Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 19/24] pm2301: LPN mode control support Lee Jones
` (5 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Enable support for low battery checking.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/pm2301_charger.c | 86 +++++++++++++++++++++-------------------
1 file changed, 45 insertions(+), 41 deletions(-)
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
index 0e41168..c7d92b3 100644
--- a/drivers/power/pm2301_charger.c
+++ b/drivers/power/pm2301_charger.c
@@ -118,6 +118,8 @@ int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
1, val);
if (ret < 0)
dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
+ else
+ ret = 0;
return ret;
}
@@ -130,6 +132,8 @@ int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
1, &val);
if (ret < 0)
dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
+ else
+ ret = 0;
return ret;
}
@@ -227,7 +231,7 @@ static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val)
static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
{
- int ret = 0;
+ int ret;
ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val);
@@ -237,6 +241,7 @@ static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
}
*val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG);
+
out:
return ret;
}
@@ -628,7 +633,7 @@ static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
/* Disable battery low monitoring */
ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG,
- PM2XXX_VBAT_LOW_MONITORING_DIS);
+ PM2XXX_VBAT_LOW_MONITORING_ENA);
/* Disable LED */
ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
@@ -677,53 +682,51 @@ static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
}
ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val);
- if (ret >= 0) {
- 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 write failed\n", __func__);
- goto error_occured;
- }
- else
- dev_err(pm2->dev, "%s read failed\n", __func__);
+ 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) {
- 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__);
- goto error_occured;
- }
- else
- dev_err(pm2->dev, "%s read failed\n", __func__);
+ 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) {
- val |= PM2XXX_ANTI_OVERSHOOT_EN;
- ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
- val);
- if (ret < 0){
- dev_err(pm2->dev, "%s write failed\n",
- __func__);
- goto error_occured;
- }
+ 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;
}
- else
- dev_err(pm2->dev, "%s read failed\n", __func__);
}
ret = pm2xxx_charging_enable_mngt(pm2);
- if (ret) {
- dev_err(pm2->dev, "%s write failed\n", __func__);
+ if (ret < 0) {
+ dev_err(pm2->dev, "Failed to enable"
+ "pm2xxx ac charger\n");
goto error_occured;
}
@@ -739,9 +742,10 @@ static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
}
ret = pm2xxx_charging_disable_mngt(pm2);
- if (ret) {
- dev_err(pm2->dev, "%s write failed\n", __func__);
- return ret;
+ 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");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 19/24] pm2301: LPN mode control support
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (17 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 18/24] pm2301: Enable vbat low monitoring Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 20/24] ab8500-fg: Use correct battery charge full design Lee Jones
` (4 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Rupesh Kumar <rupesh.kumar@stericsson.com>
The AC charger plug-in detection while booting causes I2C read
failure if AC charger is not connected. Now the LPN pin is enabled
for every PM2301 register access, which solves the issue.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rupesh Kumar <rupesh.kumar@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Vijaya Kumar K-1 <vijay.kilari@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/pm2301_charger.c | 72 ++++++++++++++++++++++++++++++++++++++--
drivers/power/pm2301_charger.h | 1 +
include/linux/pm2301_charger.h | 1 +
3 files changed, 71 insertions(+), 3 deletions(-)
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
index c7d92b3..14e37b2 100644
--- a/drivers/power/pm2301_charger.c
+++ b/drivers/power/pm2301_charger.c
@@ -28,6 +28,7 @@
#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"
@@ -110,9 +111,35 @@ static const struct i2c_device_id pm2xxx_ident[] = {
{ }
};
+static void set_lpn_pin(struct pm2xxx_charger *pm2)
+{
+ if (pm2->ac.charger_connected)
+ return;
+ gpio_set_value(pm2->lpn_pin, 1);
+
+ return;
+}
+
+static void clear_lpn_pin(struct pm2xxx_charger *pm2)
+{
+ if (pm2->ac.charger_connected)
+ return;
+ gpio_set_value(pm2->lpn_pin, 0);
+
+ return;
+}
+
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);
@@ -120,6 +147,8 @@ int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
dev_err(pm2->dev, "Error reading register at 0x%x\n", reg);
else
ret = 0;
+ clear_lpn_pin(pm2);
+ mutex_unlock(&pm2->lock);
return ret;
}
@@ -127,6 +156,14 @@ int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
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);
@@ -134,6 +171,8 @@ int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
dev_err(pm2->dev, "Error writing register at 0x%x\n", reg);
else
ret = 0;
+ clear_lpn_pin(pm2);
+ mutex_unlock(&pm2->lock);
return ret;
}
@@ -850,6 +889,14 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
pm2->bat = pl_data->battery;
+ /*get lpn GPIO from platform data*/
+ if (!pm2->pdata->lpn_gpio) {
+ dev_err(pm2->dev, "no lpn gpio data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ pm2->lpn_pin = pm2->pdata->lpn_gpio;
+
if (!i2c_check_functionality(i2c_client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
@@ -929,10 +976,25 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
goto unregister_pm2xxx_charger;
}
+ /*Initialize lock*/
+ mutex_init(&pm2->lock);
+
/*
- * I2C Read/Write will fail, if AC adaptor is not connected.
- * fix the charger detection mechanism.
+ * 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) {
@@ -951,7 +1013,8 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
err_deep_debug:
/* disable interrupt */
free_irq(pm2->pdata->irq_number, pm2);
-
+free_gpio:
+ gpio_free(pm2->lpn_pin);
unregister_pm2xxx_charger:
/* unregister power supply */
power_supply_unregister(&pm2->ac_chg.psy);
@@ -987,6 +1050,9 @@ static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
power_supply_unregister(&pm2->ac_chg.psy);
+ /*Free GPIO60*/
+ gpio_free(pm2->lpn_pin);
+
kfree(pm2);
return 0;
diff --git a/drivers/power/pm2301_charger.h b/drivers/power/pm2301_charger.h
index 5ae3573..c28babc 100644
--- a/drivers/power/pm2301_charger.h
+++ b/drivers/power/pm2301_charger.h
@@ -493,6 +493,7 @@ struct pm2xxx_charger {
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;
diff --git a/include/linux/pm2301_charger.h b/include/linux/pm2301_charger.h
index 16bb1d3..fc3f026 100644
--- a/include/linux/pm2301_charger.h
+++ b/include/linux/pm2301_charger.h
@@ -49,6 +49,7 @@ struct pm2xxx_charger_platform_data {
int i2c_bus;
const char *label;
int irq_number;
+ unsigned int lpn_gpio;
int irq_type;
};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 20/24] ab8500-fg: Use correct battery charge full design
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (18 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 19/24] pm2301: LPN mode control support Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 21/24] ab8500-charger: Do not touch VBUSOVV bits Lee Jones
` (3 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
If battery is not identified while fg probe, mah_max_design gets
initialized with unknown battery's charge full design. Reinitialize
mah_max_design if battery is identified after fg probe.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Reviewed-by: Vijaya Kumar K-1 <vijay.kilari@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
Reviewed-by: Arun MURTHY <arun.murthy@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
Tested-by: Rupesh KUMAR <rupesh.kumar@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/ab8500_fg.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 1a78116..0780b60 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2090,7 +2090,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
case POWER_SUPPLY_PROP_TECHNOLOGY:
switch (ext->type) {
case POWER_SUPPLY_TYPE_BATTERY:
- if (!di->flags.batt_id_received) {
+ if (!di->flags.batt_id_received &&
+ di->bm->batt_id != BATTERY_UNKNOWN) {
const struct abx500_battery_type *b;
b = &(di->bm->bat_type[di->bm->batt_id]);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 21/24] ab8500-charger: Do not touch VBUSOVV bits
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (19 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 20/24] ab8500-fg: Use correct battery charge full design Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 22/24] ab8500-bm: Remove individual [charger|btemp|fg|chargalg] pdata structures Lee Jones
` (2 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
From: Jonas Aaberg <jonas.aberg@stericsson.com>
Do not touch the VBUSOVV in USBCHTRL2 when running on AB8505.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
---
drivers/power/ab8500_charger.c | 22 ++++++++++++++++------
include/linux/mfd/abx500/ab8500.h | 19 +++++++++++++++++++
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index a632b94..871bf5a 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2671,13 +2671,23 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
}
}
- /* VBUS OVV set to 6.3V and enable automatic current limitiation */
- ret = abx500_set_register_interruptible(di->dev,
- AB8500_CHARGER,
- AB8500_USBCH_CTRL2_REG,
- VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
+ if (is_ab9540_2p0(di->parent) || is_ab8505_2p0(di->parent))
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL2_REG,
+ VBUS_AUTO_IN_CURR_LIM_ENA,
+ VBUS_AUTO_IN_CURR_LIM_ENA);
+ else
+ /*
+ * VBUS OVV set to 6.3V and enable automatic current limitation
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL2_REG,
+ VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
if (ret) {
- dev_err(di->dev, "failed to set VBUS OVV\n");
+ dev_err(di->dev,
+ "failed to set automatic current limitation\n");
goto out;
}
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index 9dd9b99..b9a6a84 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -346,4 +346,23 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
}
+static inline int is_ab8505_1p0_or_earlier(struct ab8500 *ab)
+{
+ return (is_ab8505(ab) && (ab->chip_id <= AB8500_CUT1P0));
+}
+
+static inline int is_ab8505_2p0(struct ab8500 *ab)
+{
+ return (is_ab8505(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
+
+static inline int is_ab9540_1p0_or_earlier(struct ab8500 *ab)
+{
+ return (is_ab9540(ab) && (ab->chip_id <= AB8500_CUT1P0));
+}
+
+static inline int is_ab9540_2p0(struct ab8500 *ab)
+{
+ return (is_ab9540(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
#endif /* MFD_AB8500_H */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 22/24] ab8500-bm: Remove individual [charger|btemp|fg|chargalg] pdata structures
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (20 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 21/24] ab8500-charger: Do not touch VBUSOVV bits Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 12:03 ` [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing Lee Jones
2013-01-21 12:04 ` [PATCH 24/24] u8500-charger: Delay for USB enumeration Lee Jones
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
None of the aforementioned components have their own dedicated
platform data structures anymore. Instead they have all been
merged into one big Battery Management container. Let's remove
them and place all the nice newly added attributes into the core
container.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/ab8500_charger.c | 4 ++--
include/linux/mfd/abx500.h | 3 +++
include/linux/mfd/abx500/ab8500-bm.h | 22 ----------------------
3 files changed, 5 insertions(+), 24 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 871bf5a..432f6bc 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2998,7 +2998,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->ac_chg.max_out_curr = ab8500_charger_current_map[
ARRAY_SIZE(ab8500_charger_current_map) - 1];
di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
- di->ac_chg.enabled = di->pdata->ac_enabled;
+ di->ac_chg.enabled = di->bm->ac_enabled;
di->ac_chg.external = false;
/* USB supply */
@@ -3019,7 +3019,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->usb_chg.max_out_curr = ab8500_charger_current_map[
ARRAY_SIZE(ab8500_charger_current_map) - 1];
di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
- di->usb_chg.enabled = di->pdata->usb_enabled;
+ di->usb_chg.enabled = di->bm->usb_enabled;
di->usb_chg.external = false;
/* Create a work queue for the charger */
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index 0e6e90b..1beaa05 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -254,6 +254,9 @@ 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;
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index b800332..802521a 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -404,28 +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;
- bool ac_enabled;
- bool usb_enabled;
-};
-
-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;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (21 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 22/24] ab8500-bm: Remove individual [charger|btemp|fg|chargalg] pdata structures Lee Jones
@ 2013-01-21 12:03 ` Lee Jones
2013-01-21 19:13 ` Anton Vorontsov
2013-01-21 12:04 ` [PATCH 24/24] u8500-charger: Delay for USB enumeration Lee Jones
23 siblings, 1 reply; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
When compile testing the new AB8500 Battery Management changes
due for inclusion into upstream, there were a few minor niggles
which required repairing, or adapting for use against the
Mainline kernel. This patch is a collection of them all.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/ab8500_fg.h | 6 ++++--
drivers/power/abx500_chargalg.c | 4 ++--
drivers/power/pm2301_charger.c | 4 ++--
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
index 946840b..bb78dc9 100644
--- a/drivers/power/ab8500_fg.h
+++ b/drivers/power/ab8500_fg.h
@@ -182,6 +182,7 @@ struct ab8500_fg_test {
* @fg_check_hw_failure_work: Work for checking HW state
* @cc_lock: Mutex for locking the CC
* @fg_kobject: Structure of type kobject
+ * @test: Structure of type ab8500_fg_test for test purpose
*/
struct ab8500_fg {
struct device *dev;
@@ -224,6 +225,7 @@ struct ab8500_fg {
struct delayed_work fg_check_hw_failure_work;
struct mutex cc_lock;
struct kobject fg_kobject;
+ struct ab8500_fg_test test;
};
extern char *discharge_state[];
@@ -232,8 +234,8 @@ extern char *charge_state[];
int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable);
void ab8500_fg_charge_state_to(struct ab8500_fg *di,
enum ab8500_fg_charge_state new_state);
-void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
- enum ab8500_fg_charge_state new_state);
+static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
+ enum ab8500_fg_discharge_state new_state);
/* test initialization */
#ifdef CONFIG_AB8500_BM_DEEP_DEBUG
void ab8500_fg_test_init(struct ab8500_fg *di);
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 694f592..cacf512 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1664,8 +1664,8 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
- struct ab8500_chargalg *di = container_of(kobj,
- struct ab8500_chargalg, chargalg_kobject);
+ struct abx500_chargalg *di = container_of(kobj,
+ struct abx500_chargalg, chargalg_kobject);
return sprintf(buf, "%d\n",
di->susp_status.ac_suspended &&
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
index 14e37b2..5ebae88 100644
--- a/drivers/power/pm2301_charger.c
+++ b/drivers/power/pm2301_charger.c
@@ -22,8 +22,8 @@
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
-#include <linux/mfd/ab8500.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>
@@ -867,7 +867,7 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
/* get parent data */
pm2->dev = &i2c_client->dev;
- pm2->gpadc = ab8500_gpadc_get();
+ pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
pm2->pm2_int = &pm2xxx_int;
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing
2013-01-21 12:03 ` [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing Lee Jones
@ 2013-01-21 19:13 ` Anton Vorontsov
2013-01-22 8:44 ` Lee Jones
2013-01-22 9:27 ` Lee Jones
0 siblings, 2 replies; 33+ messages in thread
From: Anton Vorontsov @ 2013-01-21 19:13 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Jan 21, 2013 at 12:03:59PM +0000, Lee Jones wrote:
> When compile testing the new AB8500 Battery Management changes
> due for inclusion into upstream, there were a few minor niggles
> which required repairing, or adapting for use against the
> Mainline kernel. This patch is a collection of them all.
No. This is the third time I'm saying it: the last time I checked, this is
not how we do development in the mainline.
1. One logical change at a time;
2. Every patch in a sequence must be bisectable, compilable;
3. The patches must not "break things temporary" 'because it is easier to
do development that way'. If you introduce a new feature, make all
possible to not introduce regressions.
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
> drivers/power/ab8500_fg.h | 6 ++++--
> drivers/power/abx500_chargalg.c | 4 ++--
> drivers/power/pm2301_charger.c | 4 ++--
> 3 files changed, 8 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
> index 946840b..bb78dc9 100644
> --- a/drivers/power/ab8500_fg.h
> +++ b/drivers/power/ab8500_fg.h
> @@ -182,6 +182,7 @@ struct ab8500_fg_test {
> * @fg_check_hw_failure_work: Work for checking HW state
> * @cc_lock: Mutex for locking the CC
> * @fg_kobject: Structure of type kobject
> + * @test: Structure of type ab8500_fg_test for test purpose
> */
> struct ab8500_fg {
> struct device *dev;
> @@ -224,6 +225,7 @@ struct ab8500_fg {
> struct delayed_work fg_check_hw_failure_work;
> struct mutex cc_lock;
> struct kobject fg_kobject;
> + struct ab8500_fg_test test;
You are introducing it, but I don't see any users.
> };
>
> extern char *discharge_state[];
> @@ -232,8 +234,8 @@ extern char *charge_state[];
> int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable);
> void ab8500_fg_charge_state_to(struct ab8500_fg *di,
> enum ab8500_fg_charge_state new_state);
> -void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
> - enum ab8500_fg_charge_state new_state);
> +static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
> + enum ab8500_fg_discharge_state new_state);
> /* test initialization */
> #ifdef CONFIG_AB8500_BM_DEEP_DEBUG
> void ab8500_fg_test_init(struct ab8500_fg *di);
> diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> index 694f592..cacf512 100644
> --- a/drivers/power/abx500_chargalg.c
> +++ b/drivers/power/abx500_chargalg.c
> @@ -1664,8 +1664,8 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
> static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj,
> struct attribute *attr, char *buf)
> {
> - struct ab8500_chargalg *di = container_of(kobj,
> - struct ab8500_chargalg, chargalg_kobject);
> + struct abx500_chargalg *di = container_of(kobj,
> + struct abx500_chargalg, chargalg_kobject);
Spaces.
>
> return sprintf(buf, "%d\n",
> di->susp_status.ac_suspended &&
> diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
> index 14e37b2..5ebae88 100644
> --- a/drivers/power/pm2301_charger.c
> +++ b/drivers/power/pm2301_charger.c
> @@ -22,8 +22,8 @@
> #include <linux/i2c.h>
> #include <linux/workqueue.h>
> #include <linux/kobject.h>
> -#include <linux/mfd/ab8500.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>
> @@ -867,7 +867,7 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
>
> /* get parent data */
> pm2->dev = &i2c_client->dev;
> - pm2->gpadc = ab8500_gpadc_get();
> + pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
Was the driver even compilable before? You've just introduced the new
driver in this exact series. :-/
Since pm2301 is a new driver, please merge all pm2301 stuff into one
patch.
Or, if you want to preserve the development history of the new driver,
this is also fine, but then I'd prefer to take it via a pull-request with
only this driver.
Thanks,
Anton
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing
2013-01-21 19:13 ` Anton Vorontsov
@ 2013-01-22 8:44 ` Lee Jones
2013-01-22 9:27 ` Lee Jones
1 sibling, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-22 8:44 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 21 Jan 2013, Anton Vorontsov wrote:
> On Mon, Jan 21, 2013 at 12:03:59PM +0000, Lee Jones wrote:
> > When compile testing the new AB8500 Battery Management changes
> > due for inclusion into upstream, there were a few minor niggles
> > which required repairing, or adapting for use against the
> > Mainline kernel. This patch is a collection of them all.
>
> No. This is the third time I'm saying it: the last time I checked, this is
> not how we do development in the mainline.
>
> 1. One logical change at a time;
> 2. Every patch in a sequence must be bisectable, compilable;
> 3. The patches must not "break things temporary" 'because it is easier to
> do development that way'. If you introduce a new feature, make all
> possible to not introduce regressions.
Okay, I'll fix them up instead.
> > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> > ---
> > drivers/power/ab8500_fg.h | 6 ++++--
> > drivers/power/abx500_chargalg.c | 4 ++--
> > drivers/power/pm2301_charger.c | 4 ++--
> > 3 files changed, 8 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
> > index 946840b..bb78dc9 100644
> > --- a/drivers/power/ab8500_fg.h
> > +++ b/drivers/power/ab8500_fg.h
> > @@ -182,6 +182,7 @@ struct ab8500_fg_test {
> > * @fg_check_hw_failure_work: Work for checking HW state
> > * @cc_lock: Mutex for locking the CC
> > * @fg_kobject: Structure of type kobject
> > + * @test: Structure of type ab8500_fg_test for test purpose
> > */
> > struct ab8500_fg {
> > struct device *dev;
> > @@ -224,6 +225,7 @@ struct ab8500_fg {
> > struct delayed_work fg_check_hw_failure_work;
> > struct mutex cc_lock;
> > struct kobject fg_kobject;
> > + struct ab8500_fg_test test;
>
> You are introducing it, but I don't see any users.
>
> > };
> >
> > extern char *discharge_state[];
> > @@ -232,8 +234,8 @@ extern char *charge_state[];
> > int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable);
> > void ab8500_fg_charge_state_to(struct ab8500_fg *di,
> > enum ab8500_fg_charge_state new_state);
> > -void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
> > - enum ab8500_fg_charge_state new_state);
> > +static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
> > + enum ab8500_fg_discharge_state new_state);
> > /* test initialization */
> > #ifdef CONFIG_AB8500_BM_DEEP_DEBUG
> > void ab8500_fg_test_init(struct ab8500_fg *di);
> > diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> > index 694f592..cacf512 100644
> > --- a/drivers/power/abx500_chargalg.c
> > +++ b/drivers/power/abx500_chargalg.c
> > @@ -1664,8 +1664,8 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
> > static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj,
> > struct attribute *attr, char *buf)
> > {
> > - struct ab8500_chargalg *di = container_of(kobj,
> > - struct ab8500_chargalg, chargalg_kobject);
> > + struct abx500_chargalg *di = container_of(kobj,
> > + struct abx500_chargalg, chargalg_kobject);
>
> Spaces.
>
> >
> > return sprintf(buf, "%d\n",
> > di->susp_status.ac_suspended &&
> > diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
> > index 14e37b2..5ebae88 100644
> > --- a/drivers/power/pm2301_charger.c
> > +++ b/drivers/power/pm2301_charger.c
> > @@ -22,8 +22,8 @@
> > #include <linux/i2c.h>
> > #include <linux/workqueue.h>
> > #include <linux/kobject.h>
> > -#include <linux/mfd/ab8500.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>
> > @@ -867,7 +867,7 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
> >
> > /* get parent data */
> > pm2->dev = &i2c_client->dev;
> > - pm2->gpadc = ab8500_gpadc_get();
> > + pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
>
> Was the driver even compilable before? You've just introduced the new
> driver in this exact series. :-/
>
> Since pm2301 is a new driver, please merge all pm2301 stuff into one
> patch.
>
> Or, if you want to preserve the development history of the new driver,
> this is also fine, but then I'd prefer to take it via a pull-request with
> only this driver.
>
> Thanks,
> Anton
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing
2013-01-21 19:13 ` Anton Vorontsov
2013-01-22 8:44 ` Lee Jones
@ 2013-01-22 9:27 ` Lee Jones
2013-01-23 6:25 ` Anton Vorontsov
1 sibling, 1 reply; 33+ messages in thread
From: Lee Jones @ 2013-01-22 9:27 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 21 Jan 2013, Anton Vorontsov wrote:
> On Mon, Jan 21, 2013 at 12:03:59PM +0000, Lee Jones wrote:
> > When compile testing the new AB8500 Battery Management changes
> > due for inclusion into upstream, there were a few minor niggles
> > which required repairing, or adapting for use against the
> > Mainline kernel. This patch is a collection of them all.
>
> No. This is the third time I'm saying it: the last time I checked, this is
> not how we do development in the mainline.
Okay, all of these changes have now been fixed-up into the patches
which caused the issues. I have also added Stable to the patch you
requested it on.
How would you like to proceed? Do you want a pull-request, or for me
to send the patches to the list again?
> > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> > ---
> > drivers/power/ab8500_fg.h | 6 ++++--
> > drivers/power/abx500_chargalg.c | 4 ++--
> > drivers/power/pm2301_charger.c | 4 ++--
> > 3 files changed, 8 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
> > index 946840b..bb78dc9 100644
> > --- a/drivers/power/ab8500_fg.h
> > +++ b/drivers/power/ab8500_fg.h
> > @@ -182,6 +182,7 @@ struct ab8500_fg_test {
> > * @fg_check_hw_failure_work: Work for checking HW state
> > * @cc_lock: Mutex for locking the CC
> > * @fg_kobject: Structure of type kobject
> > + * @test: Structure of type ab8500_fg_test for test purpose
> > */
> > struct ab8500_fg {
> > struct device *dev;
> > @@ -224,6 +225,7 @@ struct ab8500_fg {
> > struct delayed_work fg_check_hw_failure_work;
> > struct mutex cc_lock;
> > struct kobject fg_kobject;
> > + struct ab8500_fg_test test;
>
> You are introducing it, but I don't see any users.
>
> > };
> >
> > extern char *discharge_state[];
> > @@ -232,8 +234,8 @@ extern char *charge_state[];
> > int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable);
> > void ab8500_fg_charge_state_to(struct ab8500_fg *di,
> > enum ab8500_fg_charge_state new_state);
> > -void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
> > - enum ab8500_fg_charge_state new_state);
> > +static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
> > + enum ab8500_fg_discharge_state new_state);
> > /* test initialization */
> > #ifdef CONFIG_AB8500_BM_DEEP_DEBUG
> > void ab8500_fg_test_init(struct ab8500_fg *di);
> > diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
> > index 694f592..cacf512 100644
> > --- a/drivers/power/abx500_chargalg.c
> > +++ b/drivers/power/abx500_chargalg.c
> > @@ -1664,8 +1664,8 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
> > static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj,
> > struct attribute *attr, char *buf)
> > {
> > - struct ab8500_chargalg *di = container_of(kobj,
> > - struct ab8500_chargalg, chargalg_kobject);
> > + struct abx500_chargalg *di = container_of(kobj,
> > + struct abx500_chargalg, chargalg_kobject);
>
> Spaces.
>
> >
> > return sprintf(buf, "%d\n",
> > di->susp_status.ac_suspended &&
> > diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
> > index 14e37b2..5ebae88 100644
> > --- a/drivers/power/pm2301_charger.c
> > +++ b/drivers/power/pm2301_charger.c
> > @@ -22,8 +22,8 @@
> > #include <linux/i2c.h>
> > #include <linux/workqueue.h>
> > #include <linux/kobject.h>
> > -#include <linux/mfd/ab8500.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>
> > @@ -867,7 +867,7 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
> >
> > /* get parent data */
> > pm2->dev = &i2c_client->dev;
> > - pm2->gpadc = ab8500_gpadc_get();
> > + pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
>
> Was the driver even compilable before? You've just introduced the new
> driver in this exact series. :-/
>
> Since pm2301 is a new driver, please merge all pm2301 stuff into one
> patch.
>
> Or, if you want to preserve the development history of the new driver,
> this is also fine, but then I'd prefer to take it via a pull-request with
> only this driver.
>
> Thanks,
> Anton
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing
2013-01-22 9:27 ` Lee Jones
@ 2013-01-23 6:25 ` Anton Vorontsov
2013-01-23 8:16 ` Lee Jones
0 siblings, 1 reply; 33+ messages in thread
From: Anton Vorontsov @ 2013-01-23 6:25 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 22, 2013 at 09:27:17AM +0000, Lee Jones wrote:
> On Mon, 21 Jan 2013, Anton Vorontsov wrote:
>
> > On Mon, Jan 21, 2013 at 12:03:59PM +0000, Lee Jones wrote:
> > > When compile testing the new AB8500 Battery Management changes
> > > due for inclusion into upstream, there were a few minor niggles
> > > which required repairing, or adapting for use against the
> > > Mainline kernel. This patch is a collection of them all.
> >
> > No. This is the third time I'm saying it: the last time I checked, this is
> > not how we do development in the mainline.
>
> Okay, all of these changes have now been fixed-up into the patches
> which caused the issues. I have also added Stable to the patch you
> requested it on.
>
> How would you like to proceed? Do you want a pull-request, or for me
> to send the patches to the list again?
Either way will work.
Thanks!
Anton
p.s. I took another look at the patches, and noticed one issue that is
constantly there: you place your signed-off-by line on top of everyones',
but that's wrong. Sign offs are going downwards, not upwards. This is
quite important since the lines encode how the code "travels" between
companies and submitters. Currently, one might assume that the origin of
the code is always Linaro, which is not quite true. (And 'From:' doesn't
always mean the author, fwiw.)
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing
2013-01-23 6:25 ` Anton Vorontsov
@ 2013-01-23 8:16 ` Lee Jones
0 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-23 8:16 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, 22 Jan 2013, Anton Vorontsov wrote:
> On Tue, Jan 22, 2013 at 09:27:17AM +0000, Lee Jones wrote:
> > On Mon, 21 Jan 2013, Anton Vorontsov wrote:
> >
> > > On Mon, Jan 21, 2013 at 12:03:59PM +0000, Lee Jones wrote:
> > > > When compile testing the new AB8500 Battery Management changes
> > > > due for inclusion into upstream, there were a few minor niggles
> > > > which required repairing, or adapting for use against the
> > > > Mainline kernel. This patch is a collection of them all.
> > >
> > > No. This is the third time I'm saying it: the last time I checked, this is
> > > not how we do development in the mainline.
> >
> > Okay, all of these changes have now been fixed-up into the patches
> > which caused the issues. I have also added Stable to the patch you
> > requested it on.
> >
> > How would you like to proceed? Do you want a pull-request, or for me
> > to send the patches to the list again?
>
> Either way will work.
>
> Thanks!
>
> Anton
>
> p.s. I took another look at the patches, and noticed one issue that is
> constantly there: you place your signed-off-by line on top of everyones',
> but that's wrong. Sign offs are going downwards, not upwards. This is
> quite important since the lines encode how the code "travels" between
> companies and submitters. Currently, one might assume that the origin of
> the code is always Linaro, which is not quite true. (And 'From:' doesn't
> always mean the author, fwiw.)
Thanks for the info. I'll move them all and re-submit.
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 24/24] u8500-charger: Delay for USB enumeration
2013-01-21 12:03 [PATCH 00/24] Power: Next batch of AB8500 Battery Management patches Lee Jones
` (22 preceding siblings ...)
2013-01-21 12:03 ` [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing Lee Jones
@ 2013-01-21 12:04 ` Lee Jones
23 siblings, 0 replies; 33+ messages in thread
From: Lee Jones @ 2013-01-21 12:04 UTC (permalink / raw)
To: linux-arm-kernel
From: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>
If charging is started before USB enumeration of an Accessory
Charger Adapter has finished, the AB8500 will generate a
VBUS_ERROR. This in turn results in timeouts and delays the
enumeration with around 15 seconds. This patch delays the
charging and then ramps currents slowly to avoid VBUS errors.
The delay allows the enumeration to have finished before
charging is turned on.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Martin Sjoblom <martin.w.sjoblom@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/mfd/ab8500-core.c | 6 +
drivers/power/ab8500_charger.c | 444 ++++++++++++++++++++++++++++++----------
2 files changed, 343 insertions(+), 107 deletions(-)
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 30b9265..4c4aa19 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -749,6 +749,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[] = {
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 432f6bc..000c8cf 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -55,6 +55,7 @@
#define MAIN_CH_INPUT_CURR_SHIFT 4
#define VBUS_IN_CURR_LIM_SHIFT 4
+#define AUTO_VBUS_IN_CURR_LIM_SHIFT 4
#define LED_INDICATOR_PWM_ENA 0x01
#define LED_INDICATOR_PWM_DIS 0x00
@@ -88,14 +89,13 @@
/* Step up/down delay in us */
#define STEP_UDELAY 1000
-/* Wait for enumeration before charging in ms */
-#define WAIT_FOR_USB_ENUMERATION 5 * 1000
-
#define CHARGER_STATUS_POLL 10 /* in ms */
#define CHG_WD_INTERVAL (60 * HZ)
#define AB8500_SW_CONTROL_FALLBACK 0x03
+/* Wait for enumeration before charing in us */
+#define WAIT_ACA_RID_ENUMERATION (5 * 1000)
/* UsbLineStatus register - usb types */
enum ab8500_charger_link_status {
@@ -185,12 +185,14 @@ struct ab8500_charger_event_flags {
bool usbchargernotok;
bool chgwdexp;
bool vbus_collapse;
+ bool vbus_drop_end;
};
struct ab8500_charger_usb_state {
- bool usb_changed;
int usb_current;
+ int usb_current_tmp;
enum ab8500_usb_state state;
+ enum ab8500_usb_state state_tmp;
spinlock_t usb_lock;
};
@@ -212,6 +214,10 @@ struct ab8500_charger_usb_state {
* @autopower Indicate if we should have automatic pwron after pwrloss
* @autopower_cfg platform specific power config support for "pwron after pwrloss"
* @invalid_charger_detect_state State when forcing AB to use invalid charger
+ * @is_usb_host: Indicate if last detected USB type is host
+ * @is_aca_rid: Incicate if accessory is ACA type
+ * @current_stepping_sessions:
+ * Counter for current stepping sessions
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @bm: Platform specific battery management information
@@ -223,12 +229,13 @@ struct ab8500_charger_usb_state {
* @usb: Structure that holds the USB charger properties
* @regu: Pointer to the struct regulator
* @charger_wq: Work queue for the IRQs and checking HW state
+ * @usb_ipt_crnt_lock: Lock to protect VBUS input current setting from mutuals
+ * @pm_lock: Lock to prevent system to suspend
* @check_vbat_work Work for checking vbat threshold to adjust vbus current
* @check_hw_failure_work: Work for checking HW state
* @check_usbchgnotok_work: Work for checking USB charger not ok status
* @kick_wd_work: Work for kicking the charger watchdog in case
* of ABB rev 1.* due to the watchog logic bug
- * @attach_work: Work for checking the usb enumeration
* @ac_charger_attached_work: Work for checking if AC charger is still
* connected
* @usb_charger_attached_work: Work for checking if USB charger is still
@@ -237,6 +244,8 @@ struct ab8500_charger_usb_state {
* @detect_usb_type_work: Work for detecting the USB type connected
* @usb_link_status_work: Work for checking the new USB link status
* @usb_state_changed_work: Work for checking USB state
+ * @attach_work: Work for detecting USB type
+ * @vbus_drop_end_work: Work for detecting VBUS drop end
* @check_main_thermal_prot_work:
* Work for checking Main thermal status
* @check_usb_thermal_prot_work:
@@ -257,6 +266,9 @@ struct ab8500_charger {
bool autopower;
bool autopower_cfg;
int invalid_charger_detect_state;
+ bool is_usb_host;
+ int is_aca_rid;
+ atomic_t current_stepping_sessions;
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
struct abx500_bm_data *bm;
@@ -268,17 +280,20 @@ struct ab8500_charger {
struct ab8500_charger_info usb;
struct regulator *regu;
struct workqueue_struct *charger_wq;
+ struct mutex usb_ipt_crnt_lock;
+ struct wake_lock pm_lock;
struct delayed_work check_vbat_work;
struct delayed_work check_hw_failure_work;
struct delayed_work check_usbchgnotok_work;
struct delayed_work kick_wd_work;
+ struct delayed_work usb_state_changed_work;
struct delayed_work attach_work;
struct delayed_work ac_charger_attached_work;
struct delayed_work usb_charger_attached_work;
+ struct delayed_work vbus_drop_end_work;
struct work_struct ac_work;
struct work_struct detect_usb_type_work;
struct work_struct usb_link_status_work;
- struct work_struct usb_state_changed_work;
struct work_struct check_main_thermal_prot_work;
struct work_struct check_usb_thermal_prot_work;
struct usb_phy *usb_phy;
@@ -568,6 +583,7 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
/**
* ab8500_charger_detect_chargers() - Detect the connected chargers
* @di: pointer to the ab8500_charger structure
+ * @probe: if probe, don't delay and wait for HW
*
* Returns the type of charger connected.
* For USB it will not mean we can actually charge from it
@@ -581,7 +597,7 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
* USB_PW_CONN if the USB power supply is connected
* AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
*/
-static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
+static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
{
int result = NO_PW_CONN;
int ret;
@@ -599,13 +615,25 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
result = AC_PW_CONN;
/* Check for USB charger */
+
+ if (!probe) {
+ /*
+ * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
+ * when disconnecting ACA even though no
+ * charger was connected. Try waiting a little
+ * longer than the 100 ms of VBUS_DET_DBNC100...
+ */
+ msleep(110);
+ }
ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_CH_USBCH_STAT1_REG, &val);
if (ret < 0) {
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
return ret;
}
-
+ dev_dbg(di->dev,
+ "%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__,
+ val);
if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
result |= USB_PW_CONN;
@@ -628,33 +656,47 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
di->usb_device_is_unrecognised = false;
+ /*
+ * Platform only supports USB 2.0.
+ * This means that charging current from USB source
+ * is maximum 500 mA. Every occurence of USB_STAT_*_HOST_*
+ * should set USB_CH_IP_CUR_LVL_0P5.
+ */
+
switch (link_status) {
case USB_STAT_STD_HOST_NC:
case USB_STAT_STD_HOST_C_NS:
case USB_STAT_STD_HOST_C_S:
dev_dbg(di->dev, "USB Type - Standard host is "
- "detected through USB driver\n");
- di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+ "detected through USB driver\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ di->is_usb_host = true;
+ di->is_aca_rid = 0;
break;
case USB_STAT_HOST_CHG_HS_CHIRP:
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
- dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
- di->max_usb_in_curr);
+ di->is_usb_host = true;
+ di->is_aca_rid = 0;
break;
case USB_STAT_HOST_CHG_HS:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ di->is_usb_host = true;
+ di->is_aca_rid = 0;
+ break;
case USB_STAT_ACA_RID_C_HS:
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
- dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
- di->max_usb_in_curr);
+ di->is_usb_host = false;
+ di->is_aca_rid = 0;
break;
case USB_STAT_ACA_RID_A:
/*
* Dedicated charger level minus maximum current accessory
- * can consume (300mA). Closest level is 1100mA
+ * can consume (900mA). Closest level is 500mA
*/
- di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
- dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
- di->max_usb_in_curr);
+ dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ di->is_usb_host = false;
+ di->is_aca_rid = 1;
break;
case USB_STAT_ACA_RID_B:
/*
@@ -664,14 +706,24 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
di->max_usb_in_curr);
+ di->is_usb_host = false;
+ di->is_aca_rid = 1;
break;
case USB_STAT_HOST_CHG_NM:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ di->is_usb_host = true;
+ di->is_aca_rid = 0;
+ break;
case USB_STAT_DEDICATED_CHG:
- case USB_STAT_ACA_RID_C_NM:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+ di->is_usb_host = false;
+ di->is_aca_rid = 0;
+ break;
case USB_STAT_ACA_RID_C_HS_CHIRP:
+ case USB_STAT_ACA_RID_C_NM:
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
- dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
- di->max_usb_in_curr);
+ di->is_usb_host = false;
+ di->is_aca_rid = 1;
break;
case USB_STAT_NOT_CONFIGURED:
if (di->vbus_detected) {
@@ -788,6 +840,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
ret = abx500_get_register_interruptible(di->dev,
AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
&val);
+ dev_dbg(di->dev, "%s AB8500_IT_SOURCE21_REG %x\n",
+ __func__, val);
if (ret < 0) {
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
return ret;
@@ -803,6 +857,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
return ret;
}
+ dev_dbg(di->dev, "%s AB8500_USB_LINE_STAT_REG %x\n", __func__,
+ val);
/*
* Until the IT source register is read the UsbLineStatus
* register is not updated, hence doing the same
@@ -1062,69 +1118,125 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
static int ab8500_charger_set_current(struct ab8500_charger *di,
int ich, int reg)
{
- int ret, i;
- int curr_index, prev_curr_index, shift_value;
+ int ret = 0;
+ int auto_curr_index, curr_index, prev_curr_index, shift_value, i;
u8 reg_value;
+ u32 step_udelay;
+ bool no_stepping = false;
+
+ atomic_inc(&di->current_stepping_sessions);
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ reg, ®_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s read failed\n", __func__);
+ goto exit_set_current;
+ }
switch (reg) {
case AB8500_MCH_IPT_CURLVL_REG:
shift_value = MAIN_CH_INPUT_CURR_SHIFT;
+ prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_current_to_regval(ich);
+ step_udelay = STEP_UDELAY;
+ if (!di->ac.charger_connected)
+ no_stepping = true;
break;
case AB8500_USBCH_IPT_CRNTLVL_REG:
shift_value = VBUS_IN_CURR_LIM_SHIFT;
+ prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_vbus_in_curr_to_regval(ich);
+ step_udelay = STEP_UDELAY * 100;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT2_REG, ®_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s read failed\n", __func__);
+ goto exit_set_current;
+ }
+ auto_curr_index =
+ reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT;
+
+ dev_dbg(di->dev, "%s Auto VBUS curr is %d mA\n",
+ __func__,
+ ab8500_charger_vbus_in_curr_map[auto_curr_index]);
+
+ prev_curr_index = min(prev_curr_index, auto_curr_index);
+
+ if (!di->usb.charger_connected)
+ no_stepping = true;
break;
case AB8500_CH_OPT_CRNTLVL_REG:
shift_value = 0;
+ prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_current_to_regval(ich);
+ step_udelay = STEP_UDELAY;
+ if (curr_index && (curr_index - prev_curr_index) > 1)
+ step_udelay *= 100;
+
+ if (!di->usb.charger_connected && !di->ac.charger_connected)
+ no_stepping = true;
+
break;
default:
dev_err(di->dev, "%s current register not valid\n", __func__);
- return -ENXIO;
+ ret = -ENXIO;
+ goto exit_set_current;
}
if (curr_index < 0) {
dev_err(di->dev, "requested current limit out-of-range\n");
- return -ENXIO;
- }
-
- ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
- reg, ®_value);
- if (ret < 0) {
- dev_err(di->dev, "%s read failed\n", __func__);
- return ret;
+ ret = -ENXIO;
+ goto exit_set_current;
}
- prev_curr_index = (reg_value >> shift_value);
/* only update current if it's been changed */
- if (prev_curr_index == curr_index)
- return 0;
+ if (prev_curr_index == curr_index) {
+ dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
+ __func__, reg);
+ ret = 0;
+ goto exit_set_current;
+ }
dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
__func__, ich, reg);
- if (prev_curr_index > curr_index) {
+ if (no_stepping) {
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ reg, (u8)curr_index << shift_value);
+ if (ret)
+ dev_err(di->dev, "%s write failed\n", __func__);
+ } else if (prev_curr_index > curr_index) {
for (i = prev_curr_index - 1; i >= curr_index; i--) {
+ dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
+ (u8) i << shift_value, reg);
ret = abx500_set_register_interruptible(di->dev,
- AB8500_CHARGER, reg, (u8) i << shift_value);
+ AB8500_CHARGER, reg, (u8)i << shift_value);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
- return ret;
+ goto exit_set_current;
}
- usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+ if (i != curr_index)
+ usleep_range(step_udelay, step_udelay * 2);
}
} else {
for (i = prev_curr_index + 1; i <= curr_index; i++) {
+ dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
+ (u8)i << shift_value, reg);
ret = abx500_set_register_interruptible(di->dev,
- AB8500_CHARGER, reg, (u8) i << shift_value);
+ AB8500_CHARGER, reg, (u8)i << shift_value);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
- return ret;
+ goto exit_set_current;
}
- usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+ if (i != curr_index)
+ usleep_range(step_udelay, step_udelay * 2);
}
}
+
+exit_set_current:
+ atomic_dec(&di->current_stepping_sessions);
+
return ret;
}
@@ -1140,6 +1252,7 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
int ich_in)
{
int min_value;
+ int ret;
/* We should always use to lowest current limit */
min_value = min(di->bm->chg_params->usb_curr_max, ich_in);
@@ -1157,8 +1270,14 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
break;
}
- return ab8500_charger_set_current(di, min_value,
+ dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value);
+
+ mutex_lock(&di->usb_ipt_crnt_lock);
+ ret = ab8500_charger_set_current(di, min_value,
AB8500_USBCH_IPT_CRNTLVL_REG);
+ mutex_unlock(&di->usb_ipt_crnt_lock);
+
+ return ret;
}
/**
@@ -1469,25 +1588,13 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
dev_err(di->dev, "%s write failed\n", __func__);
return ret;
}
- /* USBChInputCurr: current that can be drawn from the usb */
- ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
- if (ret) {
- dev_err(di->dev, "setting USBChInputCurr failed\n");
- return ret;
- }
- /* ChOutputCurentLevel: protected output current */
- ret = ab8500_charger_set_output_curr(di, ich_out);
- if (ret) {
- dev_err(di->dev, "%s "
- "Failed to set ChOutputCurentLevel\n",
- __func__);
- return ret;
- }
/* Check if VBAT overshoot control should be enabled */
if (!di->bm->enable_overshoot)
overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
/* Enable USB Charger */
+ dev_dbg(di->dev,
+ "Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
if (ret) {
@@ -1500,11 +1607,29 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
if (ret < 0)
dev_err(di->dev, "failed to enable LED\n");
+ di->usb.charger_online = 1;
+
+ /* USBChInputCurr: current that can be drawn from the usb */
+ ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+ if (ret) {
+ dev_err(di->dev, "setting USBChInputCurr failed\n");
+ return ret;
+ }
+
+ /* ChOutputCurentLevel: protected output current */
+ ret = ab8500_charger_set_output_curr(di, ich_out);
+ if (ret) {
+ dev_err(di->dev, "%s "
+ "Failed to set ChOutputCurentLevel\n",
+ __func__);
+ return ret;
+ }
+
queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
- di->usb.charger_online = 1;
} else {
/* Disable USB charging */
+ dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
ret = abx500_set_register_interruptible(di->dev,
AB8500_CHARGER,
AB8500_USBCH_CTRL1_REG, 0);
@@ -1517,7 +1642,21 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
ret = ab8500_charger_led_en(di, false);
if (ret < 0)
dev_err(di->dev, "failed to disable LED\n");
+ /* USBChInputCurr: current that can be drawn from the usb */
+ ret = ab8500_charger_set_vbus_in_curr(di, 0);
+ if (ret) {
+ dev_err(di->dev, "setting USBChInputCurr failed\n");
+ return ret;
+ }
+ /* ChOutputCurentLevel: protected output current */
+ ret = ab8500_charger_set_output_curr(di, 0);
+ if (ret) {
+ dev_err(di->dev, "%s "
+ "Failed to reset ChOutputCurentLevel\n",
+ __func__);
+ return ret;
+ }
di->usb.charger_online = 0;
di->usb.wd_expired = false;
@@ -1800,7 +1939,7 @@ static void ab8500_charger_ac_work(struct work_struct *work)
* synchronously, we have the check if the main charger is
* connected by reading the status register
*/
- ret = ab8500_charger_detect_chargers(di);
+ ret = ab8500_charger_detect_chargers(di, false);
if (ret < 0)
return;
@@ -1911,16 +2050,18 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
* synchronously, we have the check if is
* connected by reading the status register
*/
- ret = ab8500_charger_detect_chargers(di);
+ ret = ab8500_charger_detect_chargers(di, false);
if (ret < 0)
return;
if (!(ret & USB_PW_CONN)) {
- di->vbus_detected = 0;
+ dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__);
+ di->vbus_detected = false;
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, &di->usb_chg.psy);
} else {
- di->vbus_detected = 1;
+ dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
+ di->vbus_detected = true;
if (is_ab8500_1p1_or_earlier(di->parent)) {
ret = ab8500_charger_detect_usb_type(di);
@@ -1930,7 +2071,8 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
&di->usb_chg.psy);
}
} else {
- /* For ABB cut2.0 and onwards we have an IRQ,
+ /*
+ * For ABB cut2.0 and onwards we have an IRQ,
* USB_LINK_STATUS that will be triggered when the USB
* link status changes. The exception is USB connected
* during startup. Then we don't get a
@@ -1951,7 +2093,7 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
}
/**
- * ab8500_charger_usb_link_attach_work() - delayd work to detect USB type
+ * ab8500_charger_usb_link_attach_work() - work to detect USB type
* @work: pointer to the work_struct structure
*
* Detect the type of USB plugged
@@ -1993,7 +2135,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
* synchronously, we have the check if is
* connected by reading the status register
*/
- detected_chargers = ab8500_charger_detect_chargers(di);
+ detected_chargers = ab8500_charger_detect_chargers(di, false);
if (detected_chargers < 0)
return;
@@ -2042,32 +2184,46 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
}
if (!(detected_chargers & USB_PW_CONN)) {
- di->vbus_detected = 0;
+ di->vbus_detected = false;
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, &di->usb_chg.psy);
return;
}
- di->vbus_detected = 1;
+ dev_dbg(di->dev,"%s di->vbus_detected = true\n",__func__);
+ di->vbus_detected = true;
ret = ab8500_charger_read_usb_type(di);
- if (!ret) {
- if (di->usb_device_is_unrecognised) {
- dev_dbg(di->dev,
- "Potential Legacy Charger device. "
- "Delay work for %d msec for USB enum "
- "to finish",
- WAIT_FOR_USB_ENUMERATION);
- queue_delayed_work(di->charger_wq,
- &di->attach_work,
- msecs_to_jiffies(WAIT_FOR_USB_ENUMERATION));
- } else {
- queue_delayed_work(di->charger_wq,
- &di->attach_work, 0);
+ if (ret) {
+ if (ret == -ENXIO) {
+ /* No valid charger type detected */
+ ab8500_charger_set_usb_connected(di, false);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
}
- } else if (ret == -ENXIO) {
- /* No valid charger type detected */
- ab8500_charger_set_usb_connected(di, false);
- ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ return;
+ }
+
+ if (di->usb_device_is_unrecognised) {
+ dev_dbg(di->dev,
+ "Potential Legacy Charger device. "
+ "Delay work for %d msec for USB enum "
+ "to finish",
+ WAIT_ACA_RID_ENUMERATION);
+ queue_delayed_work(di->charger_wq,
+ &di->attach_work,
+ msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
+ } else if (di->is_aca_rid == 1) {
+ /* Only wait once */
+ di->is_aca_rid++;
+ dev_dbg(di->dev,
+ "%s Wait %d msec for USB enum to finish",
+ __func__, WAIT_ACA_RID_ENUMERATION);
+ queue_delayed_work(di->charger_wq,
+ &di->attach_work,
+ msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
+ } else {
+ queue_delayed_work(di->charger_wq,
+ &di->attach_work,
+ 0);
}
}
@@ -2077,24 +2233,20 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
unsigned long flags;
struct ab8500_charger *di = container_of(work,
- struct ab8500_charger, usb_state_changed_work);
+ struct ab8500_charger, usb_state_changed_work.work);
- if (!di->vbus_detected)
+ if (!di->vbus_detected) {
+ dev_dbg(di->dev,
+ "%s !di->vbus_detected\n",
+ __func__);
return;
+ }
spin_lock_irqsave(&di->usb_state.usb_lock, flags);
- di->usb_state.usb_changed = false;
+ di->usb_state.state = di->usb_state.state_tmp;
+ di->usb_state.usb_current = di->usb_state.usb_current_tmp;
spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
- /*
- * wait for some time until you get updates from the usb stack
- * and negotiations are completed
- */
- msleep(250);
-
- if (di->usb_state.usb_changed)
- return;
-
dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
__func__, di->usb_state.state, di->usb_state.usb_current);
@@ -2336,6 +2488,21 @@ static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
return IRQ_HANDLED;
}
+static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
+{
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, vbus_drop_end_work.work);
+
+ di->flags.vbus_drop_end = false;
+
+ /* Reset the drop counter */
+ abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
+
+ if (di->usb.charger_connected)
+ ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+}
+
/**
* ab8500_charger_vbusdetf_handler() - VBUS falling detected
* @irq: interrupt number
@@ -2347,6 +2514,7 @@ static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
{
struct ab8500_charger *di = _di;
+ di->vbus_detected = false;
dev_dbg(di->dev, "VBUS falling detected\n");
queue_work(di->charger_wq, &di->detect_usb_type_work);
@@ -2366,6 +2534,16 @@ static irqreturn_t ab8500_charger_vbusdetr_handler(int irq, void *_di)
di->vbus_detected = true;
dev_dbg(di->dev, "VBUS rising detected\n");
+
+ /*
+ * When already called suspend handler we can not guarantee that
+ * USB detect type work is able to run complete.
+ * Need to wake lock with timeout to make sure work is starting to
+ * execute. Upon suspend and work is not complete it will be handled
+ * by flushing the work.
+ */
+ wake_lock_timeout(&di->pm_lock, HZ / 2);
+
queue_work(di->charger_wq, &di->detect_usb_type_work);
return IRQ_HANDLED;
@@ -2474,6 +2652,25 @@ static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
}
/**
+ * ab8500_charger_vbuschdropend_handler() - VBUS drop removed
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "VBUS charger drop ended\n");
+ di->flags.vbus_drop_end = true;
+ queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
+ round_jiffies(30 * HZ));
+
+ return IRQ_HANDLED;
+}
+
+/**
* ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
* @irq: interrupt number
* @_di: pointer to the ab8500_charger structure
@@ -2743,6 +2940,20 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
goto out;
}
+ /* Set charger watchdog timeout */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_WD_TIMER_REG, WD_TIMER);
+ if (ret) {
+ dev_err(di->dev, "failed to set charger watchdog timeout\n");
+ goto out;
+ }
+
+ ret = ab8500_charger_led_en(di, false);
+ if (ret < 0) {
+ dev_err(di->dev, "failed to disable LED\n");
+ goto out;
+ }
+
/* Backup battery voltage and current */
ret = abx500_set_register_interruptible(di->dev,
AB8500_RTC,
@@ -2782,6 +2993,7 @@ static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
{"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
{"VBUS_OVV", ab8500_charger_vbusovv_handler},
{"CH_WD_EXP", ab8500_charger_chwdexp_handler},
+ {"VBUS_CH_DROP_END", ab8500_charger_vbuschdropend_handler},
};
static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
@@ -2818,13 +3030,15 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
__func__, bm_usb_state, mA);
spin_lock(&di->usb_state.usb_lock);
- di->usb_state.usb_changed = true;
+ di->usb_state.state_tmp = bm_usb_state;
+ di->usb_state.usb_current_tmp = mA;
spin_unlock(&di->usb_state.usb_lock);
- di->usb_state.state = bm_usb_state;
- di->usb_state.usb_current = mA;
-
- queue_work(di->charger_wq, &di->usb_state_changed_work);
+ /*
+ * wait for some time until you get updates from the usb stack
+ * and negotiations are completed
+ */
+ queue_delayed_work(di->charger_wq, &di->usb_state_changed_work, HZ/2);
return NOTIFY_OK;
}
@@ -2864,6 +3078,9 @@ static int ab8500_charger_resume(struct platform_device *pdev)
&di->check_hw_failure_work, 0);
}
+ if (di->flags.vbus_drop_end)
+ queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work, 0);
+
return 0;
}
@@ -2876,6 +3093,9 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
if (delayed_work_pending(&di->check_hw_failure_work))
cancel_delayed_work(&di->check_hw_failure_work);
+ if (delayed_work_pending(&di->vbus_drop_end_work))
+ cancel_delayed_work(&di->vbus_drop_end_work);
+
flush_delayed_work(&di->attach_work);
flush_delayed_work(&di->usb_charger_attached_work);
flush_delayed_work(&di->ac_charger_attached_work);
@@ -2887,6 +3107,9 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
flush_work(&di->ac_work);
flush_work(&di->detect_usb_type_work);
+ if (atomic_read(&di->current_stepping_sessions))
+ return -EAGAIN;
+
return 0;
}
#else
@@ -2924,11 +3147,13 @@ static int ab8500_charger_remove(struct platform_device *pdev)
destroy_workqueue(di->charger_wq);
flush_scheduled_work();
+ wake_lock_destroy(&di->pm_lock);
if(di->usb_chg.enabled)
power_supply_unregister(&di->usb_chg.psy);
+#if !defined(CONFIG_CHARGER_PM2301)
if(di->ac_chg.enabled)
power_supply_unregister(&di->ac_chg.psy);
-
+#endif
platform_set_drvdata(pdev, NULL);
return 0;
@@ -2976,6 +3201,8 @@ static int ab8500_charger_probe(struct platform_device *pdev)
/* initialize lock */
spin_lock_init(&di->usb_state.usb_lock);
+ mutex_init(&di->usb_ipt_crnt_lock);
+ wake_lock_init(&di->pm_lock, WAKE_LOCK_SUSPEND, "ab8500-charger");
di->autopower = false;
di->invalid_charger_detect_state = 0;
@@ -3061,6 +3288,12 @@ static int ab8500_charger_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&di->attach_work,
ab8500_charger_usb_link_attach_work);
+ INIT_DELAYED_WORK(&di->usb_state_changed_work,
+ ab8500_charger_usb_state_changed_work);
+
+ INIT_DELAYED_WORK(&di->vbus_drop_end_work,
+ ab8500_charger_vbus_drop_end_work);
+
/* Init work for charger detection */
INIT_WORK(&di->usb_link_status_work,
ab8500_charger_usb_link_status_work);
@@ -3068,9 +3301,6 @@ static int ab8500_charger_probe(struct platform_device *pdev)
INIT_WORK(&di->detect_usb_type_work,
ab8500_charger_detect_usb_type_work);
- INIT_WORK(&di->usb_state_changed_work,
- ab8500_charger_usb_state_changed_work);
-
/* Init work for checking HW status */
INIT_WORK(&di->check_main_thermal_prot_work,
ab8500_charger_check_main_thermal_prot_work);
@@ -3129,7 +3359,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
}
/* Identify the connected charger types during startup */
- charger_status = ab8500_charger_detect_chargers(di);
+ charger_status = ab8500_charger_detect_chargers(di, true);
if (charger_status & AC_PW_CONN) {
di->ac.charger_connected = 1;
di->ac_conn = true;
@@ -3164,7 +3394,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
mutex_lock(&di->charger_attached_mutex);
- ch_stat = ab8500_charger_detect_chargers(di);
+ ch_stat = ab8500_charger_detect_chargers(di, false);
if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
queue_delayed_work(di->charger_wq,
--
1.7.9.5
^ permalink raw reply related [flat|nested] 33+ messages in thread