power_supply: add rk29 adc battery driver
author许盛飞 <xsf@rock-chips.com>
Wed, 4 Jan 2012 13:03:46 +0000 (21:03 +0800)
committer许盛飞 <xsf@rock-chips.com>
Wed, 4 Jan 2012 13:03:46 +0000 (21:03 +0800)
arch/arm/mach-rk29/include/mach/board.h
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/rk29_adc_battery.c [new file with mode: 0755]

index a64901ab3c0d0a287560900e022dc2fa8ee91205..8383843e7e76b6e1e5bfc5bf79482ff8b55949b1 100755 (executable)
@@ -44,6 +44,23 @@ struct hdmi_platform_data {
        int (*io_init)(void);
        int (*io_deinit)(void);
 };
+
+/* adc battery */
+struct rk29_adc_battery_platform_data {
+       int (*io_init)(void);
+       int (*io_deinit)(void);
+
+       int dc_det_pin;
+       int batt_low_pin;
+       int charge_ok_pin;
+       int charge_set_pin;
+
+       int dc_det_level;
+       int batt_low_level;
+       int charge_ok_level;
+       int charge_set_level;
+};
+
 struct irda_info{
     u32 intr_pin;
     int (*iomux_init)(void);
index 935b5c17c481dad37667e5784867af070af30e76..54c3ed608409863975c3a131354d4dece6e62ab4 100755 (executable)
@@ -292,4 +292,16 @@ config CHARGER_GPIO
          This driver can be build as a module. If so, the module will be
          called gpio-charger.
 
+config BATTERY_RK29_ADC
+       tristate "RK29 ADC Battery"
+       depends on ADC_RK29
+       help
+         Say Y to enable support for the battery on the RK2918.
+
+config BATTERY_RK29_AC_CHARGE
+       tristate "RK29 AC CHARGE"
+       depends on BATTERY_RK29_ADC
+       help
+         say Y to enable suspport for the AC battery charge
+
 endif # POWER_SUPPLY
index ecd0e194da8f9ddeca24ff23120bab4172f297b5..5e24f30321ad3f1976c2f6ab35cf683265bd3102 100644 (file)
@@ -41,3 +41,4 @@ obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
 obj-$(CONFIG_CHARGER_MAX8903)  += max8903_charger.o
 obj-$(CONFIG_CHARGER_TWL4030)  += twl4030_charger.o
 obj-$(CONFIG_CHARGER_GPIO)     += gpio-charger.o
+obj-$(CONFIG_BATTERY_RK29_ADC) += rk29_adc_battery.o
diff --git a/drivers/power/rk29_adc_battery.c b/drivers/power/rk29_adc_battery.c
new file mode 100755 (executable)
index 0000000..26ec079
--- /dev/null
@@ -0,0 +1,1234 @@
+/* drivers/power/rk29_adc_battery.c
+ *
+ * battery detect driver for the rk2918 
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <mach/gpio.h>
+#include <linux/adc.h>
+#include <mach/iomux.h>
+#include <mach/board.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+
+#include <linux/wakelock.h>
+
+static struct wake_lock batt_wake_lock;
+
+#if 0
+#define DBG(x...)   printk(x)
+#else
+#define DBG(x...)
+#endif
+
+int rk29_battery_dbg_level = 0;
+module_param_named(dbg_level, rk29_battery_dbg_level, int, 0644);
+
+/*******************ÒÔϲÎÊý¿ÉÒÔÐÞ¸Ä******************************/
+#define        TIMER_MS_COUNTS                     50  //¶¨Ê±Æ÷µÄ³¤¶Èms
+//ÒÔϲÎÊýÐèÒª¸ù¾Ýʵ¼Ê²âÊÔµ÷Õû
+#define        SLOPE_SECOND_COUNTS                 15  //ͳ¼ÆµçѹбÂʵÄʱ¼ä¼ä¸ôs
+#define        DISCHARGE_MIN_SECOND            45      //×î¿ì·Åµçµç1%ʱ¼ä
+#define        CHARGE_MIN_SECOND                   45  //×î¿ì³äµçµç1%ʱ¼ä
+#define        CHARGE_MID_SECOND                   90  //ÆÕͨ³äµçµç1%ʱ¼ä
+#define        CHARGE_MAX_SECOND                   250 //×³äµçµç1%ʱ¼ä
+#define CHARGE_FULL_DELAY_TIMES         10  //³äµçÂú¼ì²â·À¶¶Ê±¼ä
+#define USBCHARGE_IDENTIFY_TIMES        5   //²åÈëUSB»ìÁ÷£¬pcʶ±ð¼ì²âʱ¼ä
+
+#define        NUM_VOLTAGE_SAMPLE                  ((SLOPE_SECOND_COUNTS * 1000) / TIMER_MS_COUNTS)    //´æ´¢µÄ²ÉÑùµã¸öÊý
+#define        NUM_DISCHARGE_MIN_SAMPLE            ((DISCHARGE_MIN_SECOND * 1000) / TIMER_MS_COUNTS)   //´æ´¢µÄ²ÉÑùµã¸öÊý
+#define        NUM_CHARGE_MIN_SAMPLE           ((CHARGE_MIN_SECOND * 1000) / TIMER_MS_COUNTS)      //´æ´¢µÄ²ÉÑùµã¸öÊý
+#define        NUM_CHARGE_MID_SAMPLE           ((CHARGE_MID_SECOND * 1000) / TIMER_MS_COUNTS)      //´æ´¢µÄ²ÉÑùµã¸öÊý
+#define        NUM_CHARGE_MAX_SAMPLE           ((CHARGE_MAX_SECOND * 1000) / TIMER_MS_COUNTS)      //´æ´¢µÄ²ÉÑùµã¸öÊý
+#define NUM_CHARGE_FULL_DELAY_TIMES     ((CHARGE_FULL_DELAY_TIMES * 1000) / TIMER_MS_COUNTS)   //³äµçÂú״̬³ÖÐøʱ¼ä³¤¶È
+#define NUM_USBCHARGE_IDENTIFY_TIMES    ((USBCHARGE_IDENTIFY_TIMES * 1000) / TIMER_MS_COUNTS)  //³äµçÂú״̬³ÖÐøʱ¼ä³¤¶È
+
+#define BAT_2V5_VALUE                  2500
+#define BATT_MAX_VOL_VALUE               8284 //   4180                //ÂúµçʱµÄµç³Øµçѹ       FOR A7
+#define BATT_ZERO_VOL_VALUE            6800 //    3500                 //¹Ø»úʱµÄµç³Øµçѹ
+#define BATT_NOMAL_VOL_VALUE             7600 //  3800               
+
+//¶¨ÒåADC²ÉÑù·Öѹµç×裬ÒÔʵ¼ÊֵΪ׼£¬µ¥Î»K
+#define BAT_PULL_UP_R                  300  ////200
+
+#define BAT_PULL_DOWN_R                100// 200
+#define BAT_ADC_TABLE_LEN               11
+#define adc_to_voltage(adc_val) ((adc_val * BAT_2V5_VALUE * (BAT_PULL_UP_R + BAT_PULL_DOWN_R)) / (1024 * BAT_PULL_DOWN_R))
+
+static int adc_raw_table_bat[BAT_ADC_TABLE_LEN] = 
+{
+//    3490, 3597, 3628, 3641, 3660, 3697, 3747, 3809, 3879, 3945, 4165
+ 6800,7242,7332,7404,7470,7520,7610,7744,7848,8016,8284
+
+};
+
+static int adc_raw_table_ac[BAT_ADC_TABLE_LEN] = 
+{
+ //   3600, 3760, 3800, 3827, 3845, 3885, 3950, 4007, 4078, 4140, 4200
+ 7630, 7754, 7852, 7908, 7956, 8024, 8112, 8220, 8306, 8318, 8328
+
+};
+
+extern int dwc_vbus_status(void);
+extern int get_msc_connect_flag(void);
+
+struct rk29_adc_battery_data {
+       int irq;
+       
+       struct timer_list       timer;
+       struct work_struct          timer_work;
+       struct work_struct          dcwakeup_work;
+       struct work_struct          resume_work;
+       
+       struct rk29_adc_battery_platform_data *pdata;
+
+       int                     full_times;
+       
+       struct adc_client       *client; 
+       int                     adc_val;
+       int                     adc_samples[NUM_VOLTAGE_SAMPLE+2];
+       
+       int                     bat_status;
+       int                     bat_status_cnt;
+       int                     bat_health;
+       int                     bat_present;
+       int                     bat_voltage;
+       int                     bat_capacity;
+       int                     bat_change;
+};
+static struct rk29_adc_battery_data *gBatteryData;
+
+enum {
+       BATTERY_STATUS          = 0,
+       BATTERY_HEALTH          = 1,
+       BATTERY_PRESENT         = 2,
+       BATTERY_CAPACITY        = 3,
+       BATTERY_AC_ONLINE       = 4,
+       BATTERY_STATUS_CHANGED  = 5,
+       AC_STATUS_CHANGED       = 6,
+       BATTERY_INT_STATUS          = 7,
+       BATTERY_INT_ENABLE          = 8,
+};
+
+typedef enum {
+       CHARGER_BATTERY = 0,
+       CHARGER_USB,
+       CHARGER_AC
+} charger_type_t;
+
+
+#define BATT_FILENAME "/data/bat_last_capacity.dat"
+#include <linux/fs.h>
+
+static void rk29_adc_battery_capacity_samples(struct rk29_adc_battery_data *bat);
+static int rk29_adc_battery_voltage_to_capacity(struct rk29_adc_battery_data *bat, int BatVoltage);
+static struct power_supply rk29_battery_supply;
+
+static int rk29_adc_battery_load_capacity(void)
+{
+    char value[4];
+       int* p = (int *)value;
+    long fd = sys_open(BATT_FILENAME,O_RDONLY,0);
+    
+       if(fd < 0)
+    {
+               printk("rk29_adc_battery_load_capacity: open file /data/bat_last_capacity.dat failed\n");
+               return -1;
+       }
+       
+       sys_read(fd,(char __user *)value,4);
+       
+    sys_close(fd);
+    
+       return (*p);
+}
+
+static void rk29_adc_battery_put_capacity(int loadcapacity)
+{
+    char value[4];
+       int* p = (int *)value;
+    long fd = sys_open(BATT_FILENAME,O_CREAT | O_RDWR,0);
+    
+       if(fd < 0)
+    {
+               printk("rk29_adc_battery_put_capacity: open file /data/bat_last_capacity.dat failed\n");
+               return;
+       }
+    *p = loadcapacity;
+       sys_write(fd, (const char __user *)value, 4);
+       
+    sys_close(fd);
+}
+
+static void rk29_adc_battery_charge_enable(struct rk29_adc_battery_data *bat)
+{
+    struct rk29_adc_battery_platform_data *pdata = bat->pdata;
+    
+    if (pdata->charge_set_pin != INVALID_GPIO)
+    {
+        gpio_direction_output(pdata->charge_set_pin, pdata->charge_set_level);
+    }
+}
+
+static void rk29_adc_battery_charge_disable(struct rk29_adc_battery_data *bat)
+{
+    struct rk29_adc_battery_platform_data *pdata = bat->pdata;
+    
+    if (pdata->charge_set_pin != INVALID_GPIO)
+    {
+        gpio_direction_output(pdata->charge_set_pin, 1 - pdata->charge_set_level);
+    }
+}
+
+extern int suspend_flag;
+static int rk29_adc_battery_get_charge_level(struct rk29_adc_battery_data *bat)
+{
+    int charge_on = 0;
+    struct rk29_adc_battery_platform_data *pdata = bat->pdata;
+    
+#if defined(CONFIG_BATTERY_RK29_AC_CHARGE)
+    if (pdata->dc_det_pin != INVALID_GPIO)
+    {
+        if (gpio_get_value (pdata->dc_det_pin) == pdata->dc_det_level)
+        {
+            charge_on = 1;
+        }
+    }
+#endif
+    
+#if defined(CONFIG_BATTERY_RK29_USB_CHARGE)
+    if (charge_on == 0)
+    {
+        if (suspend_flag) return;
+            
+        if (1 == dwc_vbus_status())         //¼ì²âµ½USB²åÈ룬µ«ÊÇÎÞ·¨Ê¶±ðÊÇ·ñÊdzäµçÆ÷
+        {                                   //ͨ¹ýÑÓʱ¼ì²âPCʶ±ð±êÖ¾£¬Èç¹û³¬Ê±¼ì²â²»µ½£¬ËµÃ÷Êdzäµç
+            if (0 == get_msc_connect_flag())
+            {                               //²åÈë³äµçÆ÷ʱ¼ä´óÓÚÒ»¶¨Ê±¼äÖ®ºó£¬¿ªÊ¼½øÈë³äµç״̬
+                if (++gBatUsbChargeCnt >= NUM_USBCHARGE_IDENTIFY_TIMES)
+                {
+                    gBatUsbChargeCnt = NUM_USBCHARGE_IDENTIFY_TIMES + 1;
+                    charge_on = 1;
+                }
+            }                               //·ñÔò£¬²»½øÈë³äµçģʽ
+        }                   
+        else
+        {
+            gBatUsbChargeCnt = 0;
+            if (2 == dwc_vbus_status()) 
+            {
+                charge_on = 1;
+            }
+        }
+    }
+#endif
+
+    return charge_on;
+}
+
+int old_charge_level;
+static int rk29_adc_battery_status_samples(struct rk29_adc_battery_data *bat)
+{
+    int charge_level;
+    struct rk29_adc_battery_platform_data *pdata = bat->pdata;
+    
+    charge_level = rk29_adc_battery_get_charge_level(bat);
+    
+    //¼ì²â³äµç״̬±ä»¯Çé¿ö
+    if (charge_level != old_charge_level)
+    {
+        old_charge_level = charge_level;
+        bat->bat_change  = 1;
+        if(charge_level) 
+        {            
+            rk29_adc_battery_charge_enable(bat);
+        }
+        else
+        {
+            rk29_adc_battery_charge_disable(bat);
+        }
+        bat->bat_status_cnt = 0;        //״̬±ä»¯¿ªÊ¼¼ÆÊý
+    }
+    
+    //»ñÈ¡Îȶ¨µÄ³äµç״̬
+       if(charge_level == 0)
+       {   
+           //δ³äµç
+           bat->full_times = 0;
+        bat->bat_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       }
+       else
+       {
+           //³äµç          
+        if (pdata->charge_ok_pin == INVALID_GPIO)
+        {
+            //ûÓÐcharge_ok_pin£¬¼ì²âÈÝÁ¿
+            if (bat->bat_capacity == 100)
+            {
+                if (bat->bat_status != POWER_SUPPLY_STATUS_FULL)
+                {
+                    bat->bat_status = POWER_SUPPLY_STATUS_FULL;
+                    bat->bat_change  = 1;
+                }
+            }
+            else
+            {
+                bat->bat_status = POWER_SUPPLY_STATUS_CHARGING;
+            }
+        }
+        else
+        {
+            //Óгäµç¼ì²â½Ì
+            if (gpio_get_value(pdata->charge_ok_pin) != pdata->charge_ok_level)
+            {
+                //ûÓмì²âµ½³äµçÂúµçƽ±êÖ¾
+                bat->full_times = 0;
+                bat->bat_status = POWER_SUPPLY_STATUS_CHARGING;
+            }
+            else
+            {
+                //¼ì²âµ½³äµçÂúµçƽ±êÖ¾
+                bat->full_times++;
+                if (bat->full_times >= NUM_CHARGE_FULL_DELAY_TIMES) 
+                {
+                    bat->full_times = NUM_CHARGE_FULL_DELAY_TIMES + 1;
+                }
+
+                if ((bat->full_times >= NUM_CHARGE_FULL_DELAY_TIMES) && (bat->bat_capacity >= 99))
+                   {
+                       if (bat->bat_status != POWER_SUPPLY_STATUS_FULL)
+                    {
+                        bat->bat_status = POWER_SUPPLY_STATUS_FULL;
+                        bat->bat_capacity = 100;
+                        bat->bat_change  = 1;
+                    }
+                   }
+                   else
+                   {
+                       bat->bat_status = POWER_SUPPLY_STATUS_CHARGING;
+                   }
+            }
+        }
+    }
+    
+       return charge_level;
+}
+
+int AdcTestvalue = 0;
+static int gFlagLoop = 0;
+static int *pSamples;
+static void rk29_adc_battery_voltage_samples(struct rk29_adc_battery_data *bat)
+{
+       int value;
+       int i,*pStart = bat->adc_samples, num = 0;
+       
+       value = bat->adc_val;
+       AdcTestvalue = value;
+    adc_async_read(bat->client);
+    
+       *pSamples++ = adc_to_voltage(value);
+       
+       bat->bat_status_cnt++;
+       if (bat->bat_status_cnt > NUM_VOLTAGE_SAMPLE)  bat->bat_status_cnt = NUM_VOLTAGE_SAMPLE + 1;
+       
+       num = pSamples - pStart;
+       if (num >= NUM_VOLTAGE_SAMPLE)
+       {
+           pSamples = pStart;
+           gFlagLoop = 1;
+       }
+       if (gFlagLoop == 1)
+       {
+           num = NUM_VOLTAGE_SAMPLE;
+       }
+       value = 0;
+       for (i = 0; i < num; i++)
+       {
+           value += bat->adc_samples[i];
+       }
+       bat->bat_voltage = value / num;
+       
+       /*Ïû³ýë´Ìµçѹ*/
+       if(bat->bat_voltage >= BATT_MAX_VOL_VALUE + 10)
+               bat->bat_voltage = BATT_MAX_VOL_VALUE + 10;
+       else if(bat->bat_voltage <= BATT_ZERO_VOL_VALUE - 10)
+               bat->bat_voltage = BATT_ZERO_VOL_VALUE - 10;
+}
+
+int capacitytmp = 0;
+static int rk29_adc_battery_voltage_to_capacity(struct rk29_adc_battery_data *bat, int BatVoltage)
+{
+    int i = 0;
+       int capacity = 0;
+       int *p = adc_raw_table_bat;
+    
+    if (rk29_adc_battery_get_charge_level(bat))
+    {
+        p = adc_raw_table_ac;
+    }
+       
+       if(BatVoltage >= p[BAT_ADC_TABLE_LEN - 1])
+       {
+           //µ±µçѹ³¬¹ý×î´óÖµ
+           capacity = 100;
+       }       
+       else if(BatVoltage <= p[0])
+       {
+           //µ±µçѹµÍÓÚ×îСֵ
+           capacity = 0;
+       }
+       else
+       {
+       //¼ÆËãÈÝÁ¿
+       for(i = 0; i < BAT_ADC_TABLE_LEN - 1; i++)
+        {
+               
+               if((p[i] <= BatVoltage) && (BatVoltage < p[i+1]))
+               {
+                       capacity = i * 10 + ((BatVoltage - p[i]) * 10) / (p[i+1] - p[i]);
+                       break;
+               }
+       }
+    }  
+    return capacity;
+}
+
+static int gBatCapacityDisChargeCnt = 0;
+static int gBatCapacityChargeCnt    = 0;
+//static int rk29_adc_battery_get_capacity_ext(int BatVoltage)
+static void rk29_adc_battery_capacity_samples(struct rk29_adc_battery_data *bat)
+{
+       int capacity = 0;
+       struct rk29_adc_battery_platform_data *pdata = bat->pdata;
+       
+    //³ä·Åµç״̬±ä»¯ºó£¬BufferÌîÂú֮ǰ£¬²»¸üÐÂ
+       if (bat->bat_status_cnt < NUM_VOLTAGE_SAMPLE)  
+       {
+           gBatCapacityDisChargeCnt = 0;
+           gBatCapacityChargeCnt    = 0;
+           return;
+       }
+       
+    capacity = rk29_adc_battery_voltage_to_capacity(bat, bat->bat_voltage);
+           
+    if (rk29_adc_battery_get_charge_level(bat))
+    {
+        if (capacity > bat->bat_capacity)
+        {
+            //ʵ¼Ê²ÉÑùµ½µÄµçѹ±ÈÏÔʾµÄµçѹ´ó£¬Öð¼¶ÉÏÉý
+            if (++gBatCapacityDisChargeCnt >= NUM_CHARGE_MIN_SAMPLE)
+            {
+                gBatCapacityDisChargeCnt = 0;
+                if (bat->bat_capacity < 99)
+                {
+                    bat->bat_capacity++;
+                    bat->bat_change  = 1;
+                }
+            }
+            gBatCapacityChargeCnt = 0;
+        }
+        else
+        {
+            gBatCapacityDisChargeCnt = 0;
+            gBatCapacityChargeCnt++;
+            
+            if (pdata->charge_ok_pin != INVALID_GPIO)
+            {
+                if (gpio_get_value(pdata->charge_ok_pin) == pdata->charge_ok_level)
+                {
+                    //¼ì²âµ½µç³Ø³äÂú±êÖ¾£¬Í¬Ê±³¤Ê±¼äÄÚ³äµçµçѹÎޱ仯£¬¿ªÊ¼Æô¶¯¼Æʱ³äµç£¬¿ìËÙÉÏÉýÈÝÁ¿
+                    if (gBatCapacityChargeCnt >= NUM_CHARGE_MIN_SAMPLE)
+                    {
+                        gBatCapacityChargeCnt = 0;
+                        if (bat->bat_capacity < 99)
+                        {
+                            bat->bat_capacity++;
+                            bat->bat_change  = 1;
+                        }
+                    }
+                }
+                else
+                {
+                    if (capacity > capacitytmp)
+                    {
+                        //¹ý³ÌÖÐÈç¹ûµçѹÓÐÔö³¤£¬¶¨Ê±Æ÷¸´Î»£¬·ÀÖ¹¶¨Ê±Æ÷Ä£Äâ³äµç±Èʵ¼Ê³äµç¿ì
+                        gBatCapacityChargeCnt = 0;
+                    }
+                    if (/*(bat->bat_capacity >= 80) && */(gBatCapacityChargeCnt > NUM_CHARGE_MAX_SAMPLE))
+                    {
+                        gBatCapacityChargeCnt = (NUM_CHARGE_MAX_SAMPLE - NUM_CHARGE_MID_SAMPLE);
+                        if (bat->bat_capacity < 99)
+                        {
+                            bat->bat_capacity++;
+                            bat->bat_change  = 1;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                //ûÓгäµçÂú¼ì²â½Å£¬³¤Ê±¼äÄÚµçѹÎޱ仯£¬¶¨Ê±Æ÷Ä£Äâ³äµç
+                if (capacity > capacitytmp)
+                {
+                    //¹ý³ÌÖÐÈç¹ûµçѹÓÐÔö³¤£¬¶¨Ê±Æ÷¸´Î»£¬·ÀÖ¹¶¨Ê±Æ÷Ä£Äâ³äµç±Èʵ¼Ê³äµç¿ì
+                    gBatCapacityChargeCnt = 0;
+                }
+                if (gBatCapacityChargeCnt > NUM_CHARGE_MAX_SAMPLE)
+                {
+                    gBatCapacityChargeCnt = (NUM_CHARGE_MAX_SAMPLE - NUM_CHARGE_MID_SAMPLE);
+                    if (bat->bat_capacity < 100)
+                    {
+                        bat->bat_capacity++;
+                        bat->bat_change  = 1;
+                    }
+                }
+            }            
+        }
+    }    
+    else
+    {   
+        //·Åµçʱ,Ö»ÔÊÐíµçѹϽµ
+        if (capacity < bat->bat_capacity)
+        {
+            if (++gBatCapacityDisChargeCnt >= NUM_DISCHARGE_MIN_SAMPLE)
+            {
+                gBatCapacityDisChargeCnt = 0;
+                if (bat->bat_capacity > 0)
+                {
+                    bat->bat_capacity-- ;
+                    bat->bat_change  = 1;
+                }
+            }
+        }
+        else
+        {
+            gBatCapacityDisChargeCnt = 0;
+        }
+        
+        gBatCapacityChargeCnt = 0;
+    }
+       capacitytmp = capacity;
+}
+
+static int poweron_check = 0;
+static void rk29_adc_battery_poweron_capacity_check(void)
+{
+    int new_capacity, old_capacity;
+    
+    new_capacity = gBatteryData->bat_capacity;
+    old_capacity = rk29_adc_battery_load_capacity();
+    if ((old_capacity <= 0) || (old_capacity >= 100))
+    {
+        old_capacity = new_capacity;
+    }    
+    
+    if (gBatteryData->bat_status == POWER_SUPPLY_STATUS_FULL)
+    {
+        if (new_capacity > 80)
+        {
+            gBatteryData->bat_capacity = 100;
+        }
+    }
+    else if (gBatteryData->bat_status != POWER_SUPPLY_STATUS_NOT_CHARGING)
+    {
+        //chargeing state
+        //ÎÊÌ⣺
+        //1£©³¤Ê±¼ä¹Ø»ú·ÅÖú󣬿ª»úºó¶ÁÈ¡µÄÈÝÁ¿Ô¶Ô¶´óÓÚʵ¼ÊÈÝÁ¿Ôõô°ì£¿
+        //2£©Èç¹û²»ÕâÑù×ö£¬¶Ìʱ¼ä¹Ø»úÔÙ¿ª»ú£¬Ç°ºóÈÝÁ¿²»Ò»ÖÂÓÖ¸ÃÔõô°ì£¿
+        //3£©Ò»ÏÂÄÇÖÖ·½Ê½ºÏÊÊ£¿
+        //gBatteryData->bat_capacity = new_capacity;
+        gBatteryData->bat_capacity = (new_capacity > old_capacity) ? new_capacity : old_capacity;
+    }
+    else
+    {
+        gBatteryData->bat_capacity = (new_capacity < old_capacity) ? new_capacity : old_capacity;
+    }
+    
+    
+    printk("capacity = %d, new_capacity = %d, old_capacity = %d\n",gBatteryData->bat_capacity, new_capacity, old_capacity);
+    
+    gBatteryData->bat_change = 1;
+}
+
+unsigned long AdcTestCnt = 0;
+static void rk29_adc_battery_timer_work(struct work_struct *work)
+{      
+       rk29_adc_battery_status_samples(gBatteryData);
+       
+       if (poweron_check)
+       {   
+        poweron_check = 0;
+        rk29_adc_battery_poweron_capacity_check();
+       }
+       
+       rk29_adc_battery_voltage_samples(gBatteryData);
+       rk29_adc_battery_capacity_samples(gBatteryData);
+       
+       /*update battery parameter after adc and capacity has been changed*/
+       if(gBatteryData->bat_change)
+       {
+           gBatteryData->bat_change = 0;
+           rk29_adc_battery_put_capacity(gBatteryData->bat_capacity);
+               power_supply_changed(&rk29_battery_supply);
+       }
+
+       if (rk29_battery_dbg_level)
+       {
+       if (++AdcTestCnt >= 20)
+       {
+           AdcTestCnt = 0;
+           printk("Status = %d, RealAdcVal = %d, RealVol = %d,gBatVol = %d, gBatCap = %d, RealCapacity = %d, dischargecnt = %d, chargecnt = %d\n", 
+                   gBatteryData->bat_status, AdcTestvalue, adc_to_voltage(AdcTestvalue), 
+                   gBatteryData->bat_voltage, gBatteryData->bat_capacity, capacitytmp, gBatCapacityDisChargeCnt, gBatCapacityChargeCnt);
+       }
+    }
+       
+
+
+}
+
+static void rk29_adc_battery_scan_timer(unsigned long data)
+{
+    gBatteryData->timer.expires  = jiffies + msecs_to_jiffies(TIMER_MS_COUNTS);
+       add_timer(&gBatteryData->timer);
+       
+       schedule_work(&gBatteryData->timer_work);       
+}
+
+#if defined(CONFIG_BATTERY_RK29_USB_CHARGE)
+static int rk29_adc_battery_get_usb_property(struct power_supply *psy, 
+                                   enum power_supply_property psp,
+                                   union power_supply_propval *val)
+{
+       charger_type_t charger;
+       charger =  CHARGER_USB;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               if (psy->type == POWER_SUPPLY_TYPE_USB)
+                       val->intval = get_msc_connect_flag();
+               printk("%s:%d\n",__FUNCTION__,val->intval);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       
+       return 0;
+
+}
+
+static enum power_supply_property rk29_adc_battery_usb_props[] = {
+    
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static struct power_supply rk29_usb_supply = 
+{
+       .name = "usb",
+       .type = POWER_SUPPLY_TYPE_USB,
+
+       .get_property   = rk29_adc_battery_get_usb_property,
+
+    .properties     = rk29_adc_battery_usb_props,
+       .num_properties = ARRAY_SIZE(rk29_adc_battery_usb_props),
+};
+#endif
+
+#if defined(CONFIG_BATTERY_RK29_AC_CHARGE)
+static irqreturn_t rk29_adc_battery_dc_wakeup(int irq, void *dev_id)
+{   
+    schedule_work(&gBatteryData->dcwakeup_work);
+    return IRQ_HANDLED;
+}
+
+
+static int rk29_adc_battery_get_ac_property(struct power_supply *psy,
+                       enum power_supply_property psp,
+                       union power_supply_propval *val)
+{
+       int ret = 0;
+       charger_type_t charger;
+       charger =  CHARGER_USB;
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+               {
+                       printk("POWER_SUPPLY_TYPE_MAINS\n");
+                       if (rk29_adc_battery_get_charge_level(gBatteryData))
+                       {
+                               val->intval = 1;
+                               }
+                       else
+                               {
+                               val->intval = 0;        
+                               }
+               }
+               DBG("%s:%d\n",__FUNCTION__,val->intval);
+               break;
+               
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property rk29_adc_battery_ac_props[] = 
+{
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static struct power_supply rk29_ac_supply = 
+{
+       .name = "ac",
+       .type = POWER_SUPPLY_TYPE_MAINS,
+
+       .get_property   = rk29_adc_battery_get_ac_property,
+
+    .properties     = rk29_adc_battery_ac_props,
+       .num_properties = ARRAY_SIZE(rk29_adc_battery_ac_props),
+};
+
+static void rk29_adc_battery_dcdet_delaywork(struct work_struct *work)
+{
+    int ret;
+    struct rk29_adc_battery_platform_data *pdata = gBatteryData->pdata;
+    int irq      = gpio_to_irq(pdata->dc_det_pin);
+    int irq_flag = gpio_get_value (pdata->dc_det_pin) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+    
+    rk28_send_wakeup_key();
+    
+    free_irq(irq, NULL);
+    ret = request_irq(irq, rk29_adc_battery_dc_wakeup, irq_flag, "rk29_adc_battery", NULL);
+       if (ret) {
+               free_irq(irq, NULL);
+       }
+       
+       power_supply_changed(&rk29_ac_supply);
+
+    gBatteryData->bat_status_cnt = 0;        //״̬±ä»¯¿ªÊ¼¼ÆÊý
+
+               wake_lock_timeout(&batt_wake_lock, 30 * HZ);
+
+}
+
+
+#endif
+
+static int rk29_adc_battery_get_status(struct rk29_adc_battery_data *bat)
+{
+       return (bat->bat_status);
+}
+
+static int rk29_adc_battery_get_health(struct rk29_adc_battery_data *bat)
+{
+       return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static int rk29_adc_battery_get_present(struct rk29_adc_battery_data *bat)
+{
+       return (bat->bat_voltage < BATT_MAX_VOL_VALUE) ? 0 : 1;
+}
+
+static int rk29_adc_battery_get_voltage(struct rk29_adc_battery_data *bat)
+{
+       return (bat->bat_voltage );
+}
+
+static int rk29_adc_battery_get_capacity(struct rk29_adc_battery_data *bat)
+{
+       return (bat->bat_capacity);
+}
+
+static int rk29_adc_battery_get_property(struct power_supply *psy,
+                                enum power_supply_property psp,
+                                union power_supply_propval *val)
+{              
+       int ret = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = rk29_adc_battery_get_status(gBatteryData);
+               DBG("gBatStatus=%d\n",val->intval);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = rk29_adc_battery_get_health(gBatteryData);
+               DBG("gBatHealth=%d\n",val->intval);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = rk29_adc_battery_get_present(gBatteryData);
+               DBG("gBatPresent=%d\n",val->intval);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val ->intval = rk29_adc_battery_get_voltage(gBatteryData);
+               DBG("gBatVoltage=%d\n",val->intval);
+               break;
+//     case POWER_SUPPLY_PROP_CURRENT_NOW:
+//             val->intval = 1100;
+//             break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = rk29_adc_battery_get_capacity(gBatteryData);
+               DBG("gBatCapacity=%d%%\n",val->intval);
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;     
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = BATT_MAX_VOL_VALUE;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = BATT_ZERO_VOL_VALUE;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property rk29_adc_battery_props[] = {
+
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+//     POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+};
+
+static struct power_supply rk29_battery_supply = 
+{
+       .name = "battery",
+       .type = POWER_SUPPLY_TYPE_BATTERY,
+
+       .get_property   = rk29_adc_battery_get_property,
+       
+    .properties     = rk29_adc_battery_props,
+       .num_properties = ARRAY_SIZE(rk29_adc_battery_props),
+};
+
+
+#ifdef CONFIG_PM
+int suspend_capacity = 0;
+static void rk29_adc_battery_resume_check(struct work_struct *work)
+{
+    int i;
+    int level,oldlevel;
+    int new_capacity, old_capacity;
+    struct rk29_adc_battery_data *bat = gBatteryData;
+    
+    old_charge_level = -1;
+    pSamples = bat->adc_samples;
+    
+    adc_sync_read(bat->client);                             //start adc sample
+    level = oldlevel = rk29_adc_battery_status_samples(bat);//init charge status
+    
+    for (i = 0; i < NUM_VOLTAGE_SAMPLE; i++)                //0.3 s
+    {
+        mdelay(1);
+        rk29_adc_battery_voltage_samples(bat);              //get voltage
+        level = rk29_adc_battery_status_samples(bat);       //check charge status
+        if (oldlevel != level)
+        {
+            oldlevel = level;                               //if charge status changed, reset sample
+            i = 0;
+        }        
+    }
+    new_capacity = rk29_adc_battery_voltage_to_capacity(bat, bat->bat_voltage);
+    old_capacity = suspend_capacity;
+    
+    if (bat->bat_status != POWER_SUPPLY_STATUS_NOT_CHARGING)
+    {
+        //chargeing state
+        bat->bat_capacity = (new_capacity > old_capacity) ? new_capacity : old_capacity;
+    }
+    else
+    {
+        bat->bat_capacity = (new_capacity < old_capacity) ? new_capacity : old_capacity;
+    }
+    
+    printk("rk29_adc_battery_resume: status = %d, voltage = %d, capacity = %d, new_capacity = %d, old_capacity = %d\n",
+                                     bat->bat_status, bat->bat_voltage, bat->bat_capacity, new_capacity, old_capacity);
+    
+    //start timer scan
+       schedule_work(&bat->timer_work);
+    bat->timer.expires  = jiffies + 10;
+       add_timer(&bat->timer);
+}
+
+static int rk29_adc_battery_suspend(struct platform_device *dev, pm_message_t state)
+{
+       /* flush all pending status updates */
+       suspend_capacity = gBatteryData->bat_capacity;
+       del_timer(&gBatteryData->timer);
+       //flush_scheduled_work();
+       return 0;
+}
+
+static int rk29_adc_battery_resume(struct platform_device *dev)
+{
+       /* things may have changed while we were away */
+       schedule_work(&gBatteryData->resume_work);
+       return 0;
+}
+#else
+#define rk29_adc_battery_suspend NULL
+#define rk29_adc_battery_resume NULL
+#endif
+
+
+static int rk29_adc_battery_io_init(struct rk29_adc_battery_data *data, struct rk29_adc_battery_platform_data *pdata)
+{
+    int ret = 0;
+    
+    data->pdata = pdata;
+       
+       if (pdata->io_init) 
+       {
+               pdata->io_init();
+       }
+       
+       //charge control pin
+       if (pdata->charge_set_pin != INVALID_GPIO)
+       {
+       ret = gpio_request(pdata->charge_set_pin, NULL);
+       if (ret) {
+               printk("failed to request dc_det gpio\n");
+               goto error;
+       }
+       gpio_direction_output(pdata->charge_set_pin, 1 - pdata->charge_set_level);
+    }
+       
+       //dc charge detect pin
+       if (pdata->dc_det_pin != INVALID_GPIO)
+       {
+       ret = gpio_request(pdata->dc_det_pin, NULL);
+       if (ret) {
+               printk("failed to request dc_det gpio\n");
+               goto error;
+       }
+       
+       gpio_pull_updown(pdata->dc_det_pin, GPIOPullUp);//important
+       ret = gpio_direction_input(pdata->dc_det_pin);
+       if (ret) {
+               printk("failed to set gpio dc_det input\n");
+               goto error;
+       }
+    }
+       
+       //charge ok detect
+       if (pdata->charge_ok_pin != INVALID_GPIO)
+       {
+        ret = gpio_request(pdata->charge_ok_pin, NULL);
+       if (ret) {
+               printk("failed to request charge_ok gpio\n");
+               goto error;
+       }
+       
+       gpio_pull_updown(pdata->charge_ok_pin, GPIOPullUp);//important
+       ret = gpio_direction_input(pdata->charge_ok_pin);
+       if (ret) {
+               printk("failed to set gpio charge_ok input\n");
+               goto error;
+       }
+    }
+    
+    return 0;
+error:
+    return -1;
+}
+
+#define POWER_ON_PIN    RK29_PIN4_PA4
+static void rk29_adc_battery_lowpower_check(struct rk29_adc_battery_data *bat)
+{
+    int i;
+    int tmp = 0;
+    int level,oldlevel;
+    struct rk29_adc_battery_platform_data *pdata = bat->pdata;
+    
+    printk("%s--%d:\n",__FUNCTION__,__LINE__);
+    
+    old_charge_level = -1;
+    pSamples = bat->adc_samples;
+    
+    adc_sync_read(bat->client);                             //start adc sample
+    level = oldlevel = rk29_adc_battery_status_samples(bat);//init charge status
+    
+    bat->full_times = 0;
+    for (i = 0; i < NUM_VOLTAGE_SAMPLE; i++)                //0.3 s
+    {
+        mdelay(1);
+        rk29_adc_battery_voltage_samples(bat);              //get voltage
+        //level = rk29_adc_battery_status_samples(bat);       //check charge status
+        level = rk29_adc_battery_get_charge_level(bat);
+        if (oldlevel != level)
+        {
+            oldlevel = level;                               //if charge status changed, reset sample
+            i = 0;
+        }        
+    }
+    
+    bat->bat_capacity = rk29_adc_battery_voltage_to_capacity(bat, bat->bat_voltage);
+    bat->bat_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+    if (rk29_adc_battery_get_charge_level(bat))
+    {
+        bat->bat_status = POWER_SUPPLY_STATUS_CHARGING;
+        if (pdata->charge_ok_pin != INVALID_GPIO)
+        {
+            if (gpio_get_value(pdata->charge_ok_pin) == pdata->charge_ok_level)
+            {
+                bat->bat_status = POWER_SUPPLY_STATUS_FULL;
+                bat->bat_capacity = 100;
+            }
+        }
+    }
+    
+#if 0
+    rk29_adc_battery_poweron_capacity_check();
+#else
+    poweron_check = 1;
+#endif
+
+    
+    /*******************************************
+    //¿ª»ú²ÉÑùµ½µÄµçѹºÍÉϴιػú±£´æµçѹÏà²î½Ï´ó£¬Ôõô´¦Àí£¿
+    if (bat->bat_capacity > old_capacity)
+    {
+        if ((bat->bat_capacity - old_capacity) > 20)
+        {
+            
+        }
+    }
+    else if (bat->bat_capacity < old_capacity)
+    {
+        if ((old_capacity > bat->bat_capacity) > 20)
+        {
+            
+        }
+    }
+    *********************************************/
+    if (bat->bat_capacity == 0) bat->bat_capacity = 1;
+    
+    if (bat->bat_voltage <= BATT_ZERO_VOL_VALUE + 50)
+    {
+        printk("low battery: powerdown\n");
+        gpio_direction_output(POWER_ON_PIN, GPIO_LOW);
+        tmp = 0;
+        while(1)
+        {
+            if(gpio_get_value(POWER_ON_PIN) == GPIO_HIGH)
+                   {
+                           gpio_set_value(POWER_ON_PIN,GPIO_LOW);
+                   }
+                   mdelay(5);
+                   if (++tmp > 50) break;
+               }
+    }
+    gpio_direction_output(POWER_ON_PIN, GPIO_HIGH);
+}
+
+static void rk29_adc_battery_callback(struct adc_client *client, void *param, int result)
+{
+    gBatteryData->adc_val = result;
+       return;
+}
+
+static int rk29_adc_battery_probe(struct platform_device *pdev)
+{
+       int    ret;
+       int    irq;
+       int    irq_flag;
+       struct adc_client                   *client;
+       struct rk29_adc_battery_data          *data;
+       struct rk29_adc_battery_platform_data *pdata = pdev->dev.platform_data;
+       
+       printk("%s--%d:\n",__FUNCTION__,__LINE__);
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (data == NULL) {
+               ret = -ENOMEM;
+               goto err_data_alloc_failed;
+       }
+       gBatteryData = data;
+       platform_set_drvdata(pdev, data);
+
+       ret = rk29_adc_battery_io_init(data, pdata);
+    if (ret)
+    {
+        goto err_io_init;
+    }
+    
+    //register adc for battery sample
+       memset(data->adc_samples, 0, sizeof(int)*(NUM_VOLTAGE_SAMPLE + 2));
+    client = adc_register(0, rk29_adc_battery_callback, NULL);
+    if(!client)
+               goto err_adc_register_failed;
+    
+    //variable init
+       data->client  = client;
+       data->adc_val = adc_sync_read(client);
+       
+       //init a timer for adc sample
+       //init a delay work for adc timer work
+    setup_timer(&data->timer, rk29_adc_battery_scan_timer, (unsigned long)data);
+       data->timer.expires  = jiffies + 2000;
+       add_timer(&data->timer);
+
+       INIT_WORK(&data->timer_work, rk29_adc_battery_timer_work);
+       INIT_WORK(&data->resume_work, rk29_adc_battery_resume_check);
+       
+#if defined(CONFIG_BATTERY_RK29_AC_CHARGE)
+       //init dc dectet irq & delay work
+    if (pdata->dc_det_pin != INVALID_GPIO)
+    {
+        irq = gpio_to_irq(pdata->dc_det_pin);
+        
+        irq_flag = gpio_get_value (pdata->dc_det_pin) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
+       ret = request_irq(irq, rk29_adc_battery_dc_wakeup, irq_flag, "rk29_adc_battery", NULL);
+       if (ret) {
+               printk("failed to request dc det irq\n");
+               goto err_dcirq_failed;
+       }
+       enable_irq_wake(irq);
+       
+       INIT_WORK(&data->dcwakeup_work, rk29_adc_battery_dcdet_delaywork);
+    }
+#endif
+    
+    //Power on Battery detect
+       rk29_adc_battery_lowpower_check(data);
+    
+    //power supply register
+    wake_lock_init(&batt_wake_lock, WAKE_LOCK_SUSPEND, "batt_lock");
+
+       ret = power_supply_register(&pdev->dev, &rk29_battery_supply);
+       if (ret)
+       {
+               printk(KERN_INFO "fail to battery power_supply_register\n");
+               goto err_battery_failed;
+       }
+       
+#if defined(CONFIG_BATTERY_RK29_AC_CHARGE)
+       ret = power_supply_register(&pdev->dev, &rk29_ac_supply);
+       if (ret)
+       {
+               printk(KERN_INFO "fail to ac power_supply_register\n");
+               goto err_ac_failed;
+       }
+#endif
+
+#if defined(CONFIG_BATTERY_RK29_USB_CHARGE)
+       ret = power_supply_register(&pdev->dev, &rk29_usb_supply);
+       if (ret)
+       {
+               printk(KERN_INFO "fail to usb power_supply_register\n");
+               goto err_usb_failed;
+       }
+#endif
+       
+       printk(KERN_INFO "rk29_adc_battery: driver initialized\n");
+       
+       return 0;
+       
+#if defined(CONFIG_BATTERY_RK29_USB_CHARGE)
+err_usb_failed:
+       power_supply_unregister(&rk29_usb_supply);
+#endif
+
+err_ac_failed:
+#if defined(CONFIG_BATTERY_RK29_AC_CHARGE)
+       power_supply_unregister(&rk29_ac_supply);
+#endif
+
+err_battery_failed:
+       power_supply_unregister(&rk29_battery_supply);
+    
+err_dcirq_failed:
+    free_irq(gpio_to_irq(pdata->dc_det_pin), data);
+    
+err_adc_register_failed:
+err_io_init:    
+err_data_alloc_failed:
+       kfree(data);
+
+    printk("rk29_adc_battery: error!\n");
+    
+       return ret;
+}
+
+static int rk29_adc_battery_remove(struct platform_device *pdev)
+{
+       struct rk29_adc_battery_data *data = platform_get_drvdata(pdev);
+       struct rk29_adc_battery_platform_data *pdata = pdev->dev.platform_data;
+       
+#if defined(CONFIG_BATTERY_RK29_USB_CHARGE)
+       power_supply_unregister(&rk29_usb_supply);
+#endif
+#if defined(CONFIG_BATTERY_RK29_AC_CHARGE)
+       power_supply_unregister(&rk29_ac_supply);
+#endif
+       power_supply_unregister(&rk29_battery_supply);
+
+       free_irq(gpio_to_irq(pdata->dc_det_pin), data);
+
+       kfree(data);
+       
+       return 0;
+}
+
+static struct platform_driver rk29_adc_battery_driver = {
+       .probe          = rk29_adc_battery_probe,
+       .remove         = rk29_adc_battery_remove,
+       .suspend        = rk29_adc_battery_suspend,
+       .resume         = rk29_adc_battery_resume,
+       .driver = {
+               .name = "rk2918-battery",
+               .owner  = THIS_MODULE,
+       }
+};
+
+static int __init rk29_adc_battery_init(void)
+{
+       return platform_driver_register(&rk29_adc_battery_driver);
+}
+
+static void __exit rk29_adc_battery_exit(void)
+{
+       platform_driver_unregister(&rk29_adc_battery_driver);
+}
+
+subsys_initcall(rk29_adc_battery_init);
+module_exit(rk29_adc_battery_exit);
+
+MODULE_DESCRIPTION("Battery detect driver for the rk2918");
+MODULE_AUTHOR("luowei lw@rock-chips.com");
+MODULE_LICENSE("GPL");