diff options
author | Nancy Chen <Nancy.Chen@freescale.com> | 2010-10-26 12:10:05 -0500 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2010-12-13 16:10:38 -0500 |
commit | fac9f29e1c46729830c2fbcf62d73d0273c9c1c6 (patch) | |
tree | 207a52b3bd33b5355f50b4222f4a168eb8147f9b /drivers | |
parent | 0a9fe99e2ee692c292b209f878a9ef3b43e347d7 (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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mxc/pmic/mc13892/pmic_adc.c | 77 | ||||
-rw-r--r-- | drivers/mxc/pmic/mc13892/pmic_battery.c | 163 |
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(); |