summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNancy Chen <Nancy.Chen@freescale.com>2010-10-26 12:10:05 -0500
committerLily Zhang <r58066@freescale.com>2010-10-27 15:58:51 +0800
commit080a813ae04b421d4e006964afc55762b94a6cfd (patch)
treeda5c41e788040db469dd94f5f9ed462265d195b7
parent022ab5ca2504b5f36cf91b79bdbab52e0290c5cc (diff)
ENGR00131644 PMIC: Charger removal may not be detected
PMIC: Charger removal may not always be detected by MC13892. Signed-off-by: Nancy Chen <Nancy.Chen@freescale.com> (cherry picked from commit 958a3b70a70ecd0824177875d76b09c2370257a3)
-rw-r--r--drivers/mxc/pmic/mc13892/pmic_adc.c77
-rw-r--r--drivers/mxc/pmic/mc13892/pmic_battery.c163
2 files changed, 228 insertions, 12 deletions
diff --git a/drivers/mxc/pmic/mc13892/pmic_adc.c b/drivers/mxc/pmic/mc13892/pmic_adc.c
index a24446c22003..201494440ae1 100644
--- a/drivers/mxc/pmic/mc13892/pmic_adc.c
+++ b/drivers/mxc/pmic/mc13892/pmic_adc.c
@@ -43,6 +43,7 @@
/*
* ADC 0
*/
+#define ADC_CHRGICON 0x000002
#define ADC_WAIT_TSI_0 0x001400
#define ADC_INC 0x030000
@@ -55,7 +56,9 @@
#define ADC_EN 0x000001
#define ADC_SGL_CH 0x000002
+#define ADC_ADCCAL 0x000004
#define ADC_ADSEL 0x000008
+#define ADC_TRIGMASK 0x000010
#define ADC_CH_0_POS 5
#define ADC_CH_0_MASK 0x0000E0
#define ADC_CH_1_POS 8
@@ -400,8 +403,7 @@ int mc13892_adc_init_param(t_adc_param *adc_param)
PMIC_STATUS mc13892_adc_convert(t_adc_param *adc_param)
{
bool use_bis = false;
- unsigned int adc_0_reg = 0, adc_1_reg = 0, reg_1 = 0, result_reg =
- 0, i = 0;
+ unsigned int adc_0_reg = 0, adc_1_reg = 0, result_reg = 0, i = 0;
unsigned int result = 0, temp = 0;
pmic_version_t mc13892_ver;
pr_debug("mc13892 ADC - mc13892_adc_convert ....\n");
@@ -465,11 +467,15 @@ PMIC_STATUS mc13892_adc_convert(t_adc_param *adc_param)
} else {
adc_0_reg = 0x002400 | (ADC_BIS * use_bis) | ADC_INC;
}
- pr_debug("Write Reg %i = %x\n", REG_ADC0, adc_0_reg);
+
+ if (adc_param->channel_0 == channel_num[CHARGE_CURRENT])
+ adc_0_reg |= ADC_CHRGICON;
+
/*Change has been made here */
CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg,
ADC_INC | ADC_BIS | ADC_CHRGRAW_D5 |
0xfff00ff));
+
/* CONFIGURE ADC REG 1 */
if (adc_param->read_ts == false) {
adc_1_reg |= ADC_NO_ADTRIG;
@@ -485,7 +491,47 @@ PMIC_STATUS mc13892_adc_convert(t_adc_param *adc_param)
/* set ATOx = 5, it could be better for ts ADC */
adc_1_reg |= 0x002800;
}
- reg_1 = adc_1_reg;
+
+ if (adc_param->channel_0 == channel_num[CHARGE_CURRENT]) {
+ adc_param->channel_1 = channel_num[CHARGE_CURRENT];
+ adc_1_reg &= ~(ADC_CH_0_MASK | ADC_CH_1_MASK | ADC_NO_ADTRIG |
+ ADC_TRIGMASK | ADC_EN | ADC_SGL_CH | ADC_ADCCAL);
+ adc_1_reg |= ((adc_param->channel_0 << ADC_CH_0_POS) |
+ (adc_param->channel_1 << ADC_CH_1_POS));
+ adc_1_reg |= (ADC_EN | ADC_SGL_CH | ADC_ADCCAL);
+
+ if (use_bis == 0) {
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+ 0xFFFFFF));
+ } else {
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+ 0xFFFFFF));
+ temp = 0x800000;
+ CHECK_ERROR(pmic_write_reg(REG_ADC3, temp, 0xFFFFFF));
+ }
+
+ adc_1_reg &= ~(ADC_NO_ADTRIG | ASC_ADC | ADC_ADCCAL);
+ adc_1_reg |= (ADC_NO_ADTRIG | ASC_ADC);
+ if (use_bis == 0) {
+ data_ready_adc_1 = true;
+ INIT_COMPLETION(adcdone_it);
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+ 0xFFFFFF));
+ pr_debug("wait adc done\n");
+ wait_for_completion_interruptible(&adcdone_it);
+ data_ready_adc_1 = false;
+ } else {
+ data_ready_adc_2 = true;
+ INIT_COMPLETION(adcbisdone_it);
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+ 0xFFFFFF));
+ temp = 0x800000;
+ CHECK_ERROR(pmic_write_reg(REG_ADC3, temp, 0xFFFFFF));
+ pr_debug("wait adc done bis\n");
+ wait_for_completion_interruptible(&adcbisdone_it);
+ data_ready_adc_2 = false;
+ }
+ } else {
if (use_bis == 0) {
data_ready_adc_1 = false;
adc_1_reg |= ASC_ADC;
@@ -493,11 +539,13 @@ PMIC_STATUS mc13892_adc_convert(t_adc_param *adc_param)
pr_debug("Write Reg %i = %x\n", REG_ADC1, adc_1_reg);
INIT_COMPLETION(adcdone_it);
CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
- ADC_SGL_CH | ADC_ATO | ADC_ADSEL
- | ADC_CH_0_MASK | ADC_CH_1_MASK |
+ ADC_SGL_CH | ADC_ATO |
+ ADC_ADSEL | ADC_CH_0_MASK |
+ ADC_CH_1_MASK |
ADC_NO_ADTRIG | ADC_EN |
- ADC_DELAY_MASK | ASC_ADC | ADC_BIS));
- pr_debug("wait adc done \n");
+ ADC_DELAY_MASK | ASC_ADC |
+ ADC_BIS));
+ pr_debug("wait adc done\n");
wait_for_completion_interruptible(&adcdone_it);
data_ready_adc_1 = false;
} else {
@@ -505,13 +553,17 @@ PMIC_STATUS mc13892_adc_convert(t_adc_param *adc_param)
adc_1_reg |= ASC_ADC;
data_ready_adc_2 = true;
INIT_COMPLETION(adcbisdone_it);
- CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, 0xFFFFFF));
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+ 0xFFFFFF));
+
temp = 0x800000;
CHECK_ERROR(pmic_write_reg(REG_ADC3, temp, 0xFFFFFF));
pr_debug("wait adc done bis\n");
wait_for_completion_interruptible(&adcbisdone_it);
data_ready_adc_2 = false;
}
+ }
+
/* read result and store in adc_param */
result = 0;
if (use_bis == 0)
@@ -575,6 +627,7 @@ PMIC_STATUS pmic_adc_convert(t_channel channel, unsigned short *result)
{
t_adc_param adc_param;
PMIC_STATUS ret;
+ unsigned int i;
if (suspend_flag == 1)
return -EBUSY;
@@ -597,7 +650,9 @@ PMIC_STATUS pmic_adc_convert(t_channel channel, unsigned short *result)
return PMIC_PARAMETER_ERROR;
ret = mc13892_adc_convert(&adc_param);
- *result = adc_param.value[0];
+ for (i = 0; i <= 7; i++)
+ result[i] = adc_param.value[i];
+
return ret;
}
@@ -941,7 +996,7 @@ static int pmic_adc_module_probe(struct platform_device *pdev)
pr_debug("PMIC ADC successfully probed\n");
return 0;
- rm_dev_file:
+rm_dev_file:
device_remove_file(&(pdev->dev), &dev_attr_adc);
return ret;
}
diff --git a/drivers/mxc/pmic/mc13892/pmic_battery.c b/drivers/mxc/pmic/mc13892/pmic_battery.c
index 8cd979725719..6518072dfa59 100644
--- a/drivers/mxc/pmic/mc13892/pmic_battery.c
+++ b/drivers/mxc/pmic/mc13892/pmic_battery.c
@@ -14,6 +14,7 @@
/*
* Includes
*/
+#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/delay.h>
@@ -104,6 +105,14 @@ enum chg_setting {
VI_PROGRAM_EN
};
+/* Flag used to indicate if Charger workaround is active. */
+int chg_wa_is_active;
+/* Flag used to indicate if Charger workaround timer is on. */
+int chg_wa_timer;
+int disable_chg_timer;
+struct workqueue_struct *chg_wq;
+struct delayed_work chg_work;
+
static int pmic_set_chg_current(unsigned short curr)
{
unsigned int mask;
@@ -291,6 +300,7 @@ static int pmic_restart_charging(void)
pmic_set_chg_misc(VI_PROGRAM_EN, 1);
pmic_set_chg_current(0x8);
pmic_set_chg_misc(RESTART_CHG_STAT, 1);
+ pmic_set_chg_misc(PLIM_DIS, 3);
return 0;
}
@@ -329,6 +339,88 @@ static enum power_supply_property mc13892_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
+static int pmic_get_chg_value(unsigned int *value)
+{
+ t_channel channel;
+ unsigned short result[8], max1 = 0, min1 = 0, max2 = 0, min2 = 0, i;
+ unsigned int average = 0, average1 = 0, average2 = 0;
+
+ channel = CHARGE_CURRENT;
+ CHECK_ERROR(pmic_adc_convert(channel, result));
+
+
+ for (i = 0; i < 8; i++) {
+ if ((result[i] & 0x200) != 0) {
+ result[i] = 0x400 - result[i];
+ average2 += result[i];
+ if ((max2 == 0) || (max2 < result[i]))
+ max2 = result[i];
+ if ((min2 == 0) || (min2 > result[i]))
+ min2 = result[i];
+ } else {
+ average1 += result[i];
+ if ((max1 == 0) || (max1 < result[i]))
+ max1 = result[i];
+ if ((min1 == 0) || (min1 > result[i]))
+ min1 = result[i];
+ }
+ }
+
+ if (max1 != 0) {
+ average1 -= max1;
+ if (max2 != 0)
+ average2 -= max2;
+ else
+ average1 -= min1;
+ } else
+ average2 -= max2 + min2;
+
+ if (average1 >= average2) {
+ average = (average1 - average2) / 6;
+ *value = average;
+ } else {
+ average = (average2 - average1) / 6;
+ *value = ((~average) + 1) & 0x3FF;
+ }
+
+ return 0;
+}
+
+static void chg_thread(struct work_struct *work)
+{
+ int ret;
+ unsigned int value = 0;
+ int dets;
+
+ if (disable_chg_timer) {
+ disable_chg_timer = 0;
+ pmic_set_chg_current(0x8);
+ queue_delayed_work(chg_wq, &chg_work, 100);
+ chg_wa_timer = 1;
+ return;
+ }
+
+ ret = pmic_read_reg(REG_INT_SENSE0, &value, BITFMASK(BIT_CHG_DETS));
+
+ if (ret == 0) {
+ dets = BITFEXT(value, BIT_CHG_DETS);
+ pr_debug("dets=%d\n", dets);
+
+ if (dets == 1) {
+ pmic_get_chg_value(&value);
+ pr_debug("average value=%d\n", value);
+ if ((value <= 3) | ((value & 0x200) != 0)) {
+ pr_debug("%s: Disable the charger\n", __func__);
+ pmic_set_chg_current(0);
+ disable_chg_timer = 1;
+ }
+
+ queue_delayed_work(chg_wq, &chg_work, 100);
+ chg_wa_timer = 1;
+ }
+ }
+}
+
static int mc13892_charger_update_status(struct mc13892_dev_info *di)
{
int ret;
@@ -351,10 +443,15 @@ static int mc13892_charger_update_status(struct mc13892_dev_info *di)
if (online) {
pmic_start_coulomb_counter();
pmic_restart_charging();
- } else
+ queue_delayed_work(chg_wq, &chg_work, 100);
+ chg_wa_timer = 1;
+ } else {
+ cancel_delayed_work(&chg_work);
+ chg_wa_timer = 0;
pmic_stop_coulomb_counter();
}
}
+ }
return ret;
}
@@ -504,6 +601,46 @@ static int mc13892_battery_get_property(struct power_supply *psy,
return 0;
}
+static ssize_t chg_wa_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (chg_wa_is_active & chg_wa_timer)
+ return sprintf(buf, "Charger LED workaround timer is on\n");
+ else
+ return sprintf(buf, "Charger LED workaround timer is off\n");
+}
+
+static ssize_t chg_wa_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "1") != NULL) {
+ if (chg_wa_is_active) {
+ if (chg_wa_timer)
+ printk(KERN_INFO "Charger timer is already on\n");
+ else {
+ queue_delayed_work(chg_wq, &chg_work, 100);
+ chg_wa_timer = 1;
+ printk(KERN_INFO "Turned on the timer\n");
+ }
+ }
+ } else if (strstr(buf, "0") != NULL) {
+ if (chg_wa_is_active) {
+ if (chg_wa_timer) {
+ cancel_delayed_work(&chg_work);
+ chg_wa_timer = 0;
+ printk(KERN_INFO "Turned off charger timer\n");
+ } else {
+ printk(KERN_INFO "The Charger workaround timer is off\n");
+ }
+ }
+ }
+
+ return size;
+}
+
+static DEVICE_ATTR(enable, 0644, chg_wa_enable_show, chg_wa_enable_store);
+
static int pmic_battery_remove(struct platform_device *pdev)
{
pmic_event_callback_t bat_event_callback;
@@ -515,7 +652,13 @@ static int pmic_battery_remove(struct platform_device *pdev)
cancel_rearming_delayed_workqueue(di->monitor_wqueue,
&di->monitor_work);
+ cancel_rearming_delayed_workqueue(chg_wq,
+ &chg_work);
destroy_workqueue(di->monitor_wqueue);
+ destroy_workqueue(chg_wq);
+ chg_wa_timer = 0;
+ chg_wa_is_active = 0;
+ disable_chg_timer = 0;
power_supply_unregister(&di->bat);
power_supply_unregister(&di->charger);
@@ -560,6 +703,14 @@ static int pmic_battery_probe(struct platform_device *pdev)
dev_err(di->dev, "failed to register charger\n");
goto charger_failed;
}
+
+ INIT_DELAYED_WORK(&chg_work, chg_thread);
+ chg_wq = create_singlethread_workqueue("mxc_chg");
+ if (!chg_wq) {
+ retval = -ESRCH;
+ goto workqueue_failed;
+ }
+
INIT_DELAYED_WORK(&di->monitor_work, mc13892_battery_work);
di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
if (!di->monitor_wqueue) {
@@ -587,6 +738,16 @@ static int pmic_battery_probe(struct platform_device *pdev)
bat_event_callback.func = charger_online_event_callback;
bat_event_callback.param = (void *) di;
pmic_event_subscribe(EVENT_CHGDETI, bat_event_callback);
+ retval = sysfs_create_file(&pdev->dev.kobj, &dev_attr_enable.attr);
+
+ if (retval) {
+ printk(KERN_ERR
+ "Battery: Unable to register sysdev entry for Battery");
+ goto workqueue_failed;
+ }
+ chg_wa_is_active = 1;
+ chg_wa_timer = 0;
+ disable_chg_timer = 0;
pmic_stop_coulomb_counter();
pmic_calibrate_coulomb_counter();