factoryToolV4.0 support on rk3168,lcd,bL,codec,tp,usbwifi,battery control by parameter
[firefly-linux-kernel-4.4.55.git] / drivers / power / wm831x_power.c
old mode 100644 (file)
new mode 100755 (executable)
index ddf8cf5..fdf5c74
 #include <linux/mfd/wm831x/auxadc.h>
 #include <linux/mfd/wm831x/pmu.h>
 #include <linux/mfd/wm831x/pdata.h>
+#include <linux/mfd/wm831x/irq.h>
+
+#define WM831X_DEBUG
+#undef  WM831X_DEBUG
+
+#ifdef WM831X_DEBUG
+#define        WM_BATT_DBG(x...) printk(KERN_INFO x)
+#else
+#define        WM_BATT_DBG(x...)  do {} while (0)
+#endif
+
+#define WM831X_CHG_SYSLO_SHIFT   4
+#define WM831X_CHG_SYSOK_SHIFT   0
+#define WM831X_CHG_SYSLO_MASK  ~(0x7 << 4)
+#define WM831X_CHG_SYSOK_MASK  ~(0x7 << 0)
+
+#define batt_num   52
+
+static int batt_step_table[batt_num] = {
+       3400,3420,3440,3475,3505,3525,
+       3540,3557,3570,3580,3610,
+       3630,3640,3652,3662,3672,
+       3680,3687,3693,3699,3705,
+       3710,3714,3718,3722,3726,
+       3730,3734,3738,3742,3746,
+       3750,3756,3764,3774,3786,
+       3800,3808,3817,3827,3845,
+       3950,3964,3982,4002,4026,
+       4030,4034,4055,4070,4085,4120
+};
+
+static int batt_disp_table[batt_num] = {
+       0,1,2,3,5,7,
+       9,11,13,15,17,
+       19,21,23,25,27,
+       29,31,33,35,37,
+       39,41,43,45,47,
+       49,51,53,55,57,
+       59,61,63,65,67,
+       69,71,73,75,77,
+       79,81,83,85,87,
+       89,91,93,95,97,100
+};
+
+static int batt_chg_step_table[batt_num] = {
+       3520,3525,3575,3600,3620,3644,//+160
+       3662,3670,3684,3700,3715,//+150
+       3720,3748,3756,3775,3790,//+140
+       3810,3814,3818,3822,3825,//+130
+       3830,3832,3834,3836,3837,//+120
+       3839,3841,3842,3844,3844,//+110
+       3855,3860,3864,3871,3890,//+100
+       3910,3930,3952,3977,3997,//+90
+       4030,4047,4064,4080,4096,//+80
+       4132,4144,4150,4170,4195,4200//+70
+};
+
+
+
+#define TIMER_MS_COUNTS 1000
+struct wm_batt_priv_data {
+       int online;
+       int status;
+       int health;
+       int level;
+       int temp;
+       int voltage;
+};
 
 struct wm831x_power {
        struct wm831x *wm831x;
        struct power_supply wall;
        struct power_supply usb;
        struct power_supply battery;
+       struct work_struct batt_work;
+       struct timer_list timer;
+       struct wm_batt_priv_data batt_info;
+       struct wake_lock        syslo_wake;
+       int interval;
 };
 
+struct wm831x_power *g_wm831x_power;
+
+static int power_test_sysfs_init(void);
+extern void wm831x_batt_vol_level(struct wm831x_power *power, int batt_vol, int *level);
+static DEFINE_MUTEX(charging_mutex);
+static struct wake_lock batt_wake_lock;
+
+int wm831x_read_on_pin_status(void)
+{
+       int ret;
+       
+       if(!g_wm831x_power)
+       {
+               printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
+               return -1;
+       }
+       
+       ret = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_ON_PIN_CONTROL);
+       if (ret < 0)
+               return ret;
+
+       return !(ret & WM831X_ON_PIN_STS) ? 1 : 0;
+}
+
 static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
                                     union power_supply_propval *val)
 {
@@ -43,18 +140,54 @@ static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
        return 0;
 }
 
+int wm831x_read_chg_status(void)
+{
+       int ret, usb_chg = 0, wall_chg = 0;
+       
+       if(!g_wm831x_power)
+       {
+               printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
+               return -1;
+       }
+       
+       ret = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_SYSTEM_STATUS);
+       if (ret < 0)
+               return ret;
+
+       if (ret & WM831X_PWR_USB)
+               usb_chg = 1;
+       if (ret & WM831X_PWR_WALL)
+               wall_chg = 1;
+
+       return ((usb_chg | wall_chg) ? 1 : 0);
+}
+
 static int wm831x_power_read_voltage(struct wm831x *wm831x,
                                     enum wm831x_auxadc src,
                                     union power_supply_propval *val)
 {
        int ret;
-
        ret = wm831x_auxadc_read_uv(wm831x, src);
        if (ret >= 0)
                val->intval = ret;
+       
+       return ret ;
+}
 
+int wm831x_read_batt_voltage(void)
+{
+       int ret = 0;
+       
+       if(!g_wm831x_power)
+       {
+               printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
+               return -1;
+       }
+       
+       ret = wm831x_auxadc_read_uv(g_wm831x_power->wm831x, WM831X_AUX_BATT);
        return ret;
 }
+//EXPORT_SYMBOL_GPL(wm831x_get_batt_voltage);
 
 /*********************************************************************
  *             WALL Power
@@ -190,13 +323,34 @@ static struct chg_map chg_times[] = {
        { 510, 15 << WM831X_CHG_TIME_SHIFT },
 };
 
+static struct chg_map chg_syslos[] = {
+       { 2800, 0 << WM831X_CHG_SYSLO_SHIFT},
+       { 2900, 1 << WM831X_CHG_SYSLO_SHIFT},
+       { 3000, 2 << WM831X_CHG_SYSLO_SHIFT},
+       { 3100, 3 << WM831X_CHG_SYSLO_SHIFT},
+       { 3200, 4 << WM831X_CHG_SYSLO_SHIFT},
+       { 3300, 5 << WM831X_CHG_SYSLO_SHIFT},
+       { 3400, 6 << WM831X_CHG_SYSLO_SHIFT},
+       { 3500, 7 << WM831X_CHG_SYSLO_SHIFT},
+};
+
+static struct chg_map chg_sysoks[] = {
+       { 2800, 0 << WM831X_CHG_SYSOK_SHIFT},
+       { 2900, 1 << WM831X_CHG_SYSOK_SHIFT},
+       { 3000, 2 << WM831X_CHG_SYSOK_SHIFT},
+       { 3100, 3 << WM831X_CHG_SYSOK_SHIFT},
+       { 3200, 4 << WM831X_CHG_SYSOK_SHIFT},
+       { 3300, 5 << WM831X_CHG_SYSOK_SHIFT},
+       { 3400, 6 << WM831X_CHG_SYSOK_SHIFT},
+       { 3500, 7 << WM831X_CHG_SYSOK_SHIFT},
+};
+
 static void wm831x_battey_apply_config(struct wm831x *wm831x,
                                       struct chg_map *map, int count, int val,
                                       int *reg, const char *name,
                                       const char *units)
 {
        int i;
-
        for (i = 0; i < count; i++)
                if (val == map[i].val)
                        break;
@@ -213,7 +367,7 @@ static void wm831x_config_battery(struct wm831x *wm831x)
 {
        struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
        struct wm831x_battery_pdata *pdata;
-       int ret, reg1, reg2;
+       int ret, reg1, reg2, reg3;
 
        if (!wm831x_pdata || !wm831x_pdata->battery) {
                dev_warn(wm831x->dev,
@@ -225,6 +379,7 @@ static void wm831x_config_battery(struct wm831x *wm831x)
 
        reg1 = 0;
        reg2 = 0;
+       reg3 = 0;
 
        if (!pdata->enable) {
                dev_info(wm831x->dev, "Battery charger disabled\n");
@@ -258,6 +413,14 @@ static void wm831x_config_battery(struct wm831x *wm831x)
                                   pdata->timeout, &reg2,
                                   "charger timeout", "min");
 
+       wm831x_battey_apply_config(wm831x, chg_syslos, ARRAY_SIZE(chg_syslos),
+                                  pdata->syslo, &reg3,
+                                  "syslo voltage", "mV");
+
+       wm831x_battey_apply_config(wm831x, chg_sysoks, ARRAY_SIZE(chg_sysoks),
+                                  pdata->sysok, &reg3,
+                                  "sysok voltage", "mV");
+
        ret = wm831x_reg_unlock(wm831x);
        if (ret != 0) {
                dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
@@ -269,10 +432,10 @@ static void wm831x_config_battery(struct wm831x *wm831x)
                              WM831X_CHG_FAST_MASK |
                              WM831X_CHG_ITERM_MASK,
                              reg1);
-       if (ret != 0)
+       if (ret != 0) {
                dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
                        ret);
-
+       }
        ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
                              WM831X_CHG_OFF_MSK |
                              WM831X_CHG_TIME_MASK |
@@ -280,9 +443,18 @@ static void wm831x_config_battery(struct wm831x *wm831x)
                              WM831X_CHG_TRKL_ILIM_MASK |
                              WM831X_CHG_VSEL_MASK,
                              reg2);
-       if (ret != 0)
+       if (ret != 0) {
                dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
                        ret);
+       }
+
+       ret = wm831x_set_bits(wm831x, WM831X_SYSVDD_CONTROL,
+                                         WM831X_CHG_SYSLO_MASK |
+                                                 WM831X_CHG_SYSOK_MASK,
+                                                 reg3);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to set sysvdd control reg: %d\n",ret);
+       }
 
        wm831x_reg_lock(wm831x);
 }
@@ -304,6 +476,7 @@ static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
        if (ret < 0)
                return ret;
 
+
        switch (ret & WM831X_CHG_STATE_MASK) {
        case WM831X_CHG_STATE_OFF:
                *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
@@ -320,10 +493,53 @@ static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
 
        return 0;
 }
+int wm831x_read_bat_charging_status(void)
+{
+       int ret, status;
+       
+       if(!g_wm831x_power)
+       {
+               printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
+               return -1;
+       }
+       
+       ret = wm831x_bat_check_status(g_wm831x_power->wm831x, &status);
+       if (ret < 0)
+               return ret;
+       if (status == POWER_SUPPLY_STATUS_CHARGING) 
+               return 1;
+       return 0;
+}
 
 static int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
 {
        int ret;
+#ifdef WM831X_DEBUG_0 
+       ret = wm831x_reg_read(wm831x, WM831X_POWER_STATE);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x power status %#x\n", __FUNCTION__, ret);
+
+       ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x system status %#x\n", __FUNCTION__, ret);
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x charger control1 %#x\n", __FUNCTION__, ret);
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_2);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x charger control2 %#x\n", __FUNCTION__, ret);
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x charger status %#x\n\n", __FUNCTION__, ret);
+#endif
 
        ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
        if (ret < 0)
@@ -391,25 +607,41 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
 {
        struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
        struct wm831x *wm831x = wm831x_power->wm831x;
-       int ret = 0;
+       int  ret = 0;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
                ret = wm831x_bat_check_status(wm831x, &val->intval);
+               //val->intval = wm831x_power->batt_info.status;
                break;
-       case POWER_SUPPLY_PROP_ONLINE:
-               ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
-                                               val);
+       case POWER_SUPPLY_PROP_PRESENT:
+       //case POWER_SUPPLY_PROP_ONLINE:
+               //ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT, val);
+               val->intval = wm831x_power->batt_info.online;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
+               //ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
+               val->intval = wm831x_power->batt_info.voltage;//uV
                break;
        case POWER_SUPPLY_PROP_HEALTH:
-               ret = wm831x_bat_check_health(wm831x, &val->intval);
+               //ret = wm831x_bat_check_health(wm831x, &val->intval);
+               val->intval = wm831x_power->batt_info.health;
                break;
        case POWER_SUPPLY_PROP_CHARGE_TYPE:
                ret = wm831x_bat_check_type(wm831x, &val->intval);
                break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               //ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
+               //wm831x_batt_vol_level(wm831x_power, val->intval, &level);
+               //val->intval = level;
+               val->intval = wm831x_power->batt_info.level;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = 0;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
        default:
                ret = -EINVAL;
                break;
@@ -420,12 +652,16 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
 
 static enum power_supply_property wm831x_bat_props[] = {
        POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_TEMP,
        POWER_SUPPLY_PROP_CHARGE_TYPE,
 };
 
+#ifdef CONFIG_WM831X_WITH_BATTERY
 static const char *wm831x_bat_irqs[] = {
        "BATT HOT",
        "BATT COLD",
@@ -441,16 +677,18 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data)
 {
        struct wm831x_power *wm831x_power = data;
        struct wm831x *wm831x = wm831x_power->wm831x;
-
-       dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
-
+       int irq0;
+       
+       irq0 = wm831x->irq_base + WM831X_IRQ_CHG_BATT_HOT + 1;
+       dev_crit(wm831x->dev, "battery changed: i=%d\n", irq-irq0);
+                       
        /* The battery charger is autonomous so we don't need to do
         * anything except kick user space */
        power_supply_changed(&wm831x_power->battery);
 
        return IRQ_HANDLED;
 }
-
+#endif
 
 /*********************************************************************
  *             Initialisation
@@ -463,8 +701,8 @@ static irqreturn_t wm831x_syslo_irq(int irq, void *data)
 
        /* Not much we can actually *do* but tell people for
         * posterity, we're probably about to run out of power. */
-       dev_crit(wm831x->dev, "SYSVDD under voltage\n");
-
+       dev_crit(wm831x->dev, "SYSVDD under voltage and wake lock 60s\n");
+       wake_lock_timeout(&wm831x_power->syslo_wake,60*HZ);//wait for android closing system
        return IRQ_HANDLED;
 }
 
@@ -473,16 +711,340 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
        struct wm831x_power *wm831x_power = data;
        struct wm831x *wm831x = wm831x_power->wm831x;
 
+       wake_lock_timeout(&batt_wake_lock, 30 * HZ);
        dev_dbg(wm831x->dev, "Power source changed\n");
-
+       WM_BATT_DBG("%s:Power source changed\n", __FUNCTION__); 
        /* Just notify for everything - little harm in overnotifying. */
        power_supply_changed(&wm831x_power->battery);
        power_supply_changed(&wm831x_power->usb);
        power_supply_changed(&wm831x_power->wall);
 
+
        return IRQ_HANDLED;
 }
 
+static void wm831x_batt_timer_handler(unsigned long data)
+{
+       struct wm831x_power *wm831x_power = (struct wm831x_power*)data;
+       schedule_work(&wm831x_power->batt_work);
+       mod_timer(&wm831x_power->timer, jiffies + msecs_to_jiffies(wm831x_power->interval));
+}
+
+void wm831x_batt_vol_level(struct wm831x_power *wm831x_power, int batt_vol, int *level)
+{
+       int i, ret, status;
+       static int chg_plus = 1000;
+       static int chg_minus = 1000;
+       static int chg_curr = 0;
+       static int chg_num = 60;
+       static int disp_plus = 1000;
+       static int disp_minus = 1000;
+       static int disp_minus2 = 1000;
+       static int disp_curr = 0;
+       static int disp_num = 50;
+       static int batt_level_all = 0;
+       static int batt_level[20];
+       static int avr_num = 0;
+       static int avr_int = 0;
+
+
+       *level = wm831x_power->batt_info.level;
+       ret = wm831x_bat_check_status(wm831x_power->wm831x, &status);
+       if (ret < 0) {
+               printk("%s: check bat status failer...err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+
+       if (status == POWER_SUPPLY_STATUS_NOT_CHARGING 
+                       && batt_vol >= batt_step_table[batt_num-1]) {
+               *level = 100;
+               return;
+       }
+
+       if (status == POWER_SUPPLY_STATUS_CHARGING) 
+       {
+               disp_plus = 0;
+               disp_minus = 0;
+               disp_minus2 = 0;
+               disp_curr = 0;
+               for(i = 0; i < batt_num; i++){        
+                       if(batt_vol >= batt_chg_step_table[i] && 
+                                        batt_vol < batt_chg_step_table[i+1])
+                               break;     
+               }
+               if(batt_vol <= batt_chg_step_table[0])
+                       i = 0;
+               if(batt_vol >= batt_chg_step_table[batt_num-1])
+                       i = batt_num-1;
+               if(avr_int==0){
+                       batt_level[avr_num] = batt_disp_table[i];
+                       batt_level_all += batt_level[avr_num];
+                       avr_num++;
+                       if(avr_num >= 20)
+                       {
+                               avr_num = 0;
+                               avr_int = 1;
+                       }
+                       else
+                       {
+                               *level = batt_disp_table[i];
+                               return 0;
+                       }
+               }
+               else {
+                       batt_level_all -= batt_level[avr_num];
+                       batt_level[avr_num]=batt_disp_table[i];
+                       batt_level_all += batt_level[avr_num];
+                       avr_num++;
+               }
+               if(avr_num >= 20) 
+                       avr_num = 0;
+               *level = batt_level_all/20;
+               if ((chg_plus == 1000) && (chg_minus == 1000))
+               {
+                       *level = *level;
+                       chg_plus = 0;
+                       chg_minus = 0;
+                       chg_curr = 0;
+
+               }
+               else
+               {                       
+
+                       if (*level >= (wm831x_power->batt_info.level+1))
+                       {
+                               chg_minus = 0;
+                               chg_curr = 0;
+                               if(*level < 85)
+                                       chg_num =10;
+                               else
+                                       chg_num = 5;
+                               if (++chg_plus > chg_num)
+                               {
+                                       *level = wm831x_power->batt_info.level + 1;
+                                       chg_plus = 0;
+                               
+                               }
+                               else
+                               {
+                                       *level = wm831x_power->batt_info.level;
+                               }
+                       }
+                       else
+                       {
+                               chg_plus = 0;
+                               chg_minus = 0;
+                               chg_curr = 0;
+                               *level = wm831x_power->batt_info.level;
+                       }
+               }
+               
+
+               if (*level >= 100)
+                       *level = 100;
+               if (*level < 0)
+                       *level = 0;
+       }
+       else 
+       {
+               chg_plus = 0;
+               chg_minus = 0;
+               chg_curr = 0;
+               for(i = 0; i < batt_num; i++){        
+                       if(batt_vol >= batt_step_table[i] && 
+                                        batt_vol < batt_step_table[i+1])
+                               break;     
+               }
+               if(batt_vol <= batt_step_table[0])
+                       i = 0;
+               if(batt_vol >= batt_step_table[batt_num-1])
+                       i = batt_num-1;
+               if(avr_int==0){
+                       batt_level[avr_num] = batt_disp_table[i];
+                       batt_level_all += batt_level[avr_num];
+                       avr_num++;
+                       if(avr_num >= 20)
+                       {
+                               avr_num = 0;
+                               avr_int = 1;
+                       }
+                       else
+                       {
+                               *level = batt_disp_table[i];
+                               return 0;
+                       }
+               }
+               else {
+                       batt_level_all -= batt_level[avr_num];
+                       batt_level[avr_num]=batt_disp_table[i];
+                       batt_level_all += batt_level[avr_num];
+                       avr_num++;
+               }
+               if(avr_num >= 20) 
+                       avr_num = 0;
+               *level = batt_level_all/20;
+               if ((disp_plus == 1000) && (disp_minus == 1000))
+               {
+                       *level = *level;
+                       disp_plus = 0;
+                       disp_minus = 0;
+                       disp_minus2 =0;
+                       disp_curr = 0;
+               }
+               else
+               {       
+                       if(*level <= (wm831x_power ->batt_info.level -20))
+                       {
+                               disp_plus = 0;
+                               disp_curr = 0;
+                               disp_minus2 = 0;
+                               disp_num = 1;
+                                if (++disp_minus > disp_num)
+                               {
+                                       *level = wm831x_power->batt_info.level - 20;
+                                       disp_minus = 0;
+                               }
+                               else
+                               {
+                                       *level = wm831x_power->batt_info.level;
+                               }
+                       }
+                       else if (*level <= (wm831x_power->batt_info.level-1))   
+                       {
+                               disp_plus = 0;
+                               disp_curr = 0;
+                               disp_minus = 0;
+                               if((*level < 17) || (*level > 85))
+                                       disp_num = 30;
+                               else
+                                       disp_num = 80;
+               
+                                if (++disp_minus2 > disp_num)
+                               {
+                                       *level = wm831x_power->batt_info.level - 1;
+                                       disp_minus2 = 0;
+                               }
+                               else
+                               {
+
+                                       *level = wm831x_power->batt_info.level;
+                               }
+                       }
+                       else
+                       {
+                               disp_plus = 0;
+                               disp_minus = 0;
+                               disp_minus2 = 0;
+                               disp_curr = 0;
+                               *level = wm831x_power->batt_info.level;
+                       }
+               }
+
+               if (*level >= 100)
+                       *level = 100;
+               if (*level < 0)
+                       *level = 0;
+       }
+}
+
+static void wm831x_batt_work(struct work_struct *work)
+{
+       int online, status,health,level, ret; 
+    union power_supply_propval val;
+       struct wm831x_power *power = container_of(work, struct wm831x_power, batt_work);
+
+       ret = wm831x_power_check_online(power->wm831x, WM831X_PWR_SRC_BATT, &val);
+       if (ret < 0) {
+               printk("%s: check bat online failer...  err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+       online = val.intval;
+
+       ret = wm831x_bat_check_status(power->wm831x, &status);
+       if (ret < 0) {
+               printk("%s: check bat status failer...  err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+
+       ret = wm831x_bat_check_health(power->wm831x, &health);
+       if (ret < 0) {
+               printk("%s: check bat health failer...  err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+
+       ret = wm831x_power_read_voltage(power->wm831x, WM831X_AUX_BATT, &val);
+       if (ret < 0) {
+               printk("%s: read bat voltage failer...err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+       power->batt_info.voltage = val.intval;
+
+       wm831x_batt_vol_level(power, val.intval / 1000, &level);
+       //mod_timer(&power->timer, jiffies + msecs_to_jiffies(power->interval));
+
+       if (online != power->batt_info.online || status != power->batt_info.status
+                       || health != power->batt_info.health || level != power->batt_info.level)
+       {
+               power->batt_info.online = online;
+               power->batt_info.status = status;
+               power->batt_info.health = health;
+               power->batt_info.level  = level;
+
+               power_supply_changed(&power->battery);
+       }
+
+}
+
+#ifdef CONFIG_POWER_ON_CHARGER_DISPLAY
+static void wm831x_batt_check(struct wm831x_power *power)
+{
+       int online, status,health,level, ret; 
+       union power_supply_propval val;
+//     struct wm831x_power *power = container_of(work, struct wm831x_power, batt_work);
+
+       ret = wm831x_power_check_online(power->wm831x, WM831X_PWR_SRC_BATT, &val);
+       if (ret < 0) {
+               printk("%s: check bat online failer...  err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+       online = val.intval;
+
+       ret = wm831x_bat_check_status(power->wm831x, &status);
+       if (ret < 0) {
+               printk("%s: check bat status failer...  err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+
+       ret = wm831x_bat_check_health(power->wm831x, &health);
+       if (ret < 0) {
+               printk("%s: check bat health failer...  err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+
+       ret = wm831x_power_read_voltage(power->wm831x, WM831X_AUX_BATT, &val);
+       if (ret < 0) {
+               printk("%s: read bat voltage failer...err = %d\n", __FUNCTION__, ret);
+               return;
+       }
+       power->batt_info.voltage = val.intval;
+
+       wm831x_batt_vol_level(power, val.intval / 1000, &level);
+       //mod_timer(&power->timer, jiffies + msecs_to_jiffies(power->interval));
+
+       if (online != power->batt_info.online || status != power->batt_info.status
+                       || health != power->batt_info.health || level != power->batt_info.level)
+       {
+               power->batt_info.online = online;
+               power->batt_info.status = status;
+               power->batt_info.health = health;
+               power->batt_info.level  = level;
+
+               power_supply_changed(&power->battery);
+       }
+}
+#endif
+
+
 static __devinit int wm831x_power_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
@@ -490,7 +1052,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
        struct power_supply *usb;
        struct power_supply *battery;
        struct power_supply *wall;
-       int ret, irq, i;
+       int ret, irq;
 
        power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
        if (power == NULL)
@@ -507,6 +1069,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
         * the status without enabling the charger.
         */
        wm831x_config_battery(wm831x);
+       wake_lock_init(&batt_wake_lock, WAKE_LOCK_SUSPEND, "batt_lock");
 
        wall->name = "wm831x-wall";
        wall->type = POWER_SUPPLY_TYPE_MAINS;
@@ -555,12 +1118,15 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
                goto err_syslo;
        }
 
+#ifdef CONFIG_WM831X_WITH_BATTERY
+       int i;
        for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
                irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
                ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
                                           IRQF_TRIGGER_RISING,
                                           wm831x_bat_irqs[i],
                                           power);
+               WM_BATT_DBG("%s: %s irq no %d\n", __FUNCTION__, wm831x_bat_irqs[i], irq);
                if (ret != 0) {
                        dev_err(&pdev->dev,
                                "Failed to request %s IRQ %d: %d\n",
@@ -568,9 +1134,31 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
                        goto err_bat_irq;
                }
        }
-
+#endif
+
+       power->interval = TIMER_MS_COUNTS;
+       power->batt_info.level = 100;
+       power->batt_info.voltage   = 4200000;
+       power->batt_info.online    = 1;
+       power->batt_info.status    = POWER_SUPPLY_STATUS_DISCHARGING;
+       power->batt_info.health    = POWER_SUPPLY_HEALTH_GOOD;
+
+       wake_lock_init(&power->syslo_wake, WAKE_LOCK_SUSPEND, "wm831x_syslo_wake");
+       INIT_WORK(&power->batt_work, wm831x_batt_work);
+       setup_timer(&power->timer, wm831x_batt_timer_handler, (unsigned long)power);
+       power->timer.expires = jiffies + msecs_to_jiffies(1000);
+       add_timer(&power->timer);
+
+       g_wm831x_power = power;
+#ifdef CONFIG_POWER_ON_CHARGER_DISPLAY
+       wm831x_batt_check(power);//xsf
+#endif
+       
+       printk("%s:wm831x_power initialized\n",__FUNCTION__);
+       power_test_sysfs_init();
        return ret;
-
+       
+#ifdef CONFIG_WM831X_WITH_BATTERY
 err_bat_irq:
        for (; i >= 0; i--) {
                irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
@@ -578,6 +1166,8 @@ err_bat_irq:
        }
        irq = platform_get_irq_byname(pdev, "PWR SRC");
        free_irq(irq, power);
+#endif
+
 err_syslo:
        irq = platform_get_irq_byname(pdev, "SYSLO");
        free_irq(irq, power);
@@ -595,13 +1185,14 @@ err_kmalloc:
 static __devexit int wm831x_power_remove(struct platform_device *pdev)
 {
        struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
-       int irq, i;
-
+       int irq;
+#ifdef CONFIG_WM831X_WITH_BATTERY
+       int i;
        for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
                irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
                free_irq(irq, wm831x_power);
        }
-
+#endif
        irq = platform_get_irq_byname(pdev, "PWR SRC");
        free_irq(irq, wm831x_power);
 
@@ -615,19 +1206,45 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm831x_battery_suspend(struct platform_device *dev, pm_message_t state)
+{
+       struct wm831x_power *power = (struct wm831x_power *)platform_get_drvdata(dev);
+       flush_scheduled_work();
+       del_timer(&power->timer);
+       return 0;
+}
+
+static int wm831x_battery_resume(struct platform_device *dev)
+{
+       struct wm831x_power *power = (struct wm831x_power *)platform_get_drvdata(dev);
+       power->timer.expires = jiffies + msecs_to_jiffies(power->interval);
+       add_timer(&power->timer);
+       return 0;
+}
+#else
+#define wm831x_battery_suspend NULL
+#define wm831x_battery_resume  NULL
+#endif
+
 static struct platform_driver wm831x_power_driver = {
        .probe = wm831x_power_probe,
        .remove = __devexit_p(wm831x_power_remove),
+       .suspend = wm831x_battery_suspend,
+       .resume = wm831x_battery_resume,
        .driver = {
                .name = "wm831x-power",
        },
 };
-
 static int __init wm831x_power_init(void)
 {
        return platform_driver_register(&wm831x_power_driver);
 }
+#ifndef CONFIG_POWER_ON_CHARGER_DISPLAY
 module_init(wm831x_power_init);
+#else
+subsys_initcall(wm831x_power_init);
+#endif
 
 static void __exit wm831x_power_exit(void)
 {
@@ -635,6 +1252,96 @@ static void __exit wm831x_power_exit(void)
 }
 module_exit(wm831x_power_exit);
 
+
+static ssize_t power_prop_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       ssize_t ret = 0;
+       int level, power_status, system_status, chg_ctl1, chg_ctl2, chg_status;
+    union power_supply_propval val;
+
+       if (!g_wm831x_power)
+               return -1;
+       power_status = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_POWER_STATE);
+       if (power_status < 0)
+               return power_status;
+       //printk("wm831x power status %#x\n", ret);
+
+       system_status = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_SYSTEM_STATUS);
+       if (system_status < 0)
+               return system_status;
+       //printk("wm831x system status %#x\n", ret);
+
+       chg_ctl1 = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_CHARGER_CONTROL_1);
+       if (chg_ctl1 < 0)
+               return chg_ctl1;
+       //printk("wm831x charger control1 %#x\n", ret);
+
+       chg_ctl2 = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_CHARGER_CONTROL_2);
+       if (chg_ctl2 < 0)
+               return chg_ctl2;
+       //printk("wm831x charger control2 %#x\n", ret);
+
+       chg_status = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_CHARGER_STATUS);
+       if (chg_status < 0)
+               return chg_status;
+       //printk("wm831x charger status %#x\n", ret);
+
+       ret = wm831x_power_read_voltage(g_wm831x_power->wm831x, WM831X_AUX_BATT, &val);
+       if (ret < 0)
+               return ret;
+       wm831x_batt_vol_level(g_wm831x_power, val.intval / 1000, &level);
+       //printk("batt_vol = %d batt_level = %d\n", val.intval, level);
+       //
+       sprintf(buf, "power_status=%#x\n"
+                            "system_status=%#x\n"
+                                "chg_ctl1=%#x\n"
+                                "chg_ctl2=%#x\n"
+                                "chg_status=%#x\n"
+                                "batt_vol=%d\n"
+                                "batt_level=%d%%\n",
+                                power_status,
+                                system_status,
+                                chg_ctl1,
+                                chg_ctl2,
+                                chg_status,
+                                val.intval,
+                                level);
+       ret = strlen(buf) + 1;
+       return ret;
+}
+
+static DEVICE_ATTR(prop, 0444, power_prop_show, NULL);
+
+static struct kobject *power_test_kobj;
+
+static int power_test_sysfs_init(void)
+{
+       int ret ;
+       power_test_kobj = kobject_create_and_add("power_test_prop", NULL);
+       if (power_test_kobj == NULL) {
+               printk(KERN_ERR
+                      "power_test_sysfs_init:"\
+                      "subsystem_register failed\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+       ret = sysfs_create_file(power_test_kobj, &dev_attr_prop.attr);
+       if (ret) {
+               printk(KERN_ERR
+                      "power_test_sysfs_init:"\
+                      "sysfs_create_group failed\n");
+               goto err1;
+       }
+
+       return 0 ;
+err1:
+       kobject_del(power_test_kobj);
+err:
+       return ret ;
+}
+
+
 MODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");