From 5122c4ae79584cdbce89d793eba37783f4c56aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E7=9B=9B=E9=A3=9E?= Date: Wed, 4 Jan 2012 19:51:31 +0800 Subject: [PATCH 1/5] RTC: system suspend, RTC can auto wake up the system --- drivers/rtc/Kconfig | 8 +++ drivers/rtc/Makefile | 2 + drivers/rtc/auto-wake.c | 128 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100755 drivers/rtc/auto-wake.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 68a0c106eb6a..a567a8e131e7 100755 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -123,6 +123,14 @@ config RTC_INTF_ALARM_DEV help Exports the alarm interface to user-space. +config AUTO_WAKE_UP + tristate "Support auto wake up" + depends on RTC_INTF_ALARM_DEV + +config AUTO_WAKE_UP_PERIOD + int "auto wake up period(sec)" + depends on AUTO_WAKE_UP + default 3600 config RTC_DRV_TEST tristate "Test driver/device" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index a488d45cf72f..3f5e0685673b 100755 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -15,6 +15,8 @@ rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o +obj-$(CONFIG_AUTO_WAKE_UP) += auto-wake.o + # Keep the list ordered. obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o diff --git a/drivers/rtc/auto-wake.c b/drivers/rtc/auto-wake.c new file mode 100755 index 000000000000..4642867c6ea0 --- /dev/null +++ b/drivers/rtc/auto-wake.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct auto_wake +{ + struct alarm alarm; + struct timespec timer; + //struct notifier_block pm_nb; +}; + +static struct auto_wake auto_wake; +struct timespec set_atuo_wake_time( struct timespec timer) +{ + struct timespec new_time; + struct timespec tmp_time; + + tmp_time =ktime_to_timespec(alarm_get_elapsed_realtime()); + + printk("nowtime = %ld \n",tmp_time.tv_sec); + + new_time.tv_nsec = timer.tv_nsec+ tmp_time.tv_nsec; + new_time.tv_sec = timer.tv_sec+ tmp_time.tv_sec; + + return new_time; +} + +static void auto_wake_update(struct auto_wake *auto_wake) +{ + + struct timespec new_alarm_time; + +// printk("auto_wake_update\n"); + new_alarm_time = set_atuo_wake_time(auto_wake->timer); + alarm_start_range(&auto_wake->alarm, + timespec_to_ktime(new_alarm_time), + timespec_to_ktime(new_alarm_time)); +} + +static void atuo_wake_trigger(struct alarm *alarm) +{ + + struct auto_wake *auto_wake = container_of(alarm, struct auto_wake, + alarm); + + auto_wake_update(auto_wake); +} + + +#if 0 +static void auto_wake_cancel(struct auto_wake *auto_wake) +{ + alarm_cancel(&auto_wake->alarm); +} + + + +static int auto_wake_callback(struct notifier_block *nfb, + unsigned long action, + void *ignored) +{ + + struct auto_wake *auto_wake = container_of(nfb, struct auto_wake, + pm_nb); + + switch (action) + { + case PM_SUSPEND_PREPARE: + { + printk("PM_SUSPEND_PREPARExsf \n"); + auto_wake_update(auto_wake); + return NOTIFY_OK; + } + case PM_POST_SUSPEND: + { + printk("PM_POST_SUSPENDxsf \n"); + // auto_wake_cancel(auto_wake); + return NOTIFY_OK; + } + } + + return NOTIFY_DONE; +} + + +static struct notifier_block auto_wake_pm_notifier = { + .notifier_call = auto_wake_callback, + .priority = 0, +}; + +#endif + +void auto_wake_init(struct auto_wake *auto_wake,struct timespec timer) +{ +// auto_wake->pm_nb = auto_wake_pm_notifier; + auto_wake->timer = timer; + + alarm_init(&auto_wake->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, atuo_wake_trigger); + + //register_pm_notifier(&auto_wake->pm_nb);// xsf + +} + +static int __init start_auto_wake(void) +{ + + struct timespec timer; + + printk("CONFIG_AUTO_WAKE_UP_PERIOD = %d\n", CONFIG_AUTO_WAKE_UP_PERIOD); + timer.tv_sec = CONFIG_AUTO_WAKE_UP_PERIOD; + timer.tv_nsec = 0; + + auto_wake_init(&auto_wake,timer); + auto_wake_update(&auto_wake); + return 0; +} + +late_initcall_sync(start_auto_wake); From 525611c6dc539e7d737c6c0aecd5e0e5bcab7ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E7=9B=9B=E9=A3=9E?= Date: Wed, 4 Jan 2012 21:03:46 +0800 Subject: [PATCH 2/5] power_supply: add rk29 adc battery driver --- arch/arm/mach-rk29/include/mach/board.h | 17 + drivers/power/Kconfig | 12 + drivers/power/Makefile | 1 + drivers/power/rk29_adc_battery.c | 1234 +++++++++++++++++++++++ 4 files changed, 1264 insertions(+) create mode 100755 drivers/power/rk29_adc_battery.c diff --git a/arch/arm/mach-rk29/include/mach/board.h b/arch/arm/mach-rk29/include/mach/board.h index a64901ab3c0d..8383843e7e76 100755 --- a/arch/arm/mach-rk29/include/mach/board.h +++ b/arch/arm/mach-rk29/include/mach/board.h @@ -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); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 935b5c17c481..54c3ed608409 100755 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -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 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index ecd0e194da8f..5e24f30321ad 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -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 index 000000000000..26ec079b6e5f --- /dev/null +++ b/drivers/power/rk29_adc_battery.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 + +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插入,但是无法识别是否是充电器 + { //通过延时检测PC识别标志,如果超时检测不到,说明是充电 + 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"); From b1310216ec9568edc24d047a4002103e953e760c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E7=9B=9B=E9=A3=9E?= Date: Wed, 4 Jan 2012 21:04:31 +0800 Subject: [PATCH 3/5] rk29: ddr3sdk: add adc battery device --- arch/arm/mach-rk29/board-rk29-ddr3sdk.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/arm/mach-rk29/board-rk29-ddr3sdk.c b/arch/arm/mach-rk29/board-rk29-ddr3sdk.c index 6e7dff1801d4..c357c58e344f 100755 --- a/arch/arm/mach-rk29/board-rk29-ddr3sdk.c +++ b/arch/arm/mach-rk29/board-rk29-ddr3sdk.c @@ -775,6 +775,27 @@ struct bq27541_platform_data bq27541_info = { .bat_check_pin = BAT_LOW, }; #endif + +#ifdef CONFIG_BATTERY_RK29_ADC +static struct rk29_adc_battery_platform_data rk29_adc_battery_platdata = { + .dc_det_pin = RK29_PIN4_PA1, + .batt_low_pin = RK29_PIN4_PA2, + .charge_set_pin = INVALID_GPIO, + .charge_ok_pin = RK29_PIN4_PA3, + + .dc_det_level = GPIO_LOW, + .charge_ok_level = GPIO_HIGH, +}; + +static struct platform_device rk29_device_adc_battery = { + .name = "rk2918-battery", + .id = -1, + .dev = { + .platform_data = &rk29_adc_battery_platdata, + }, +}; +#endif + static struct android_pmem_platform_data android_pmem_pdata = { .name = "pmem", .start = PMEM_UI_BASE, @@ -2629,6 +2650,9 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_ADC_RK29 &rk29_device_adc, #endif +#ifdef CONFIG_BATTERY_RK29_ADC + &rk29_device_adc_battery, +#endif #ifdef CONFIG_I2C0_RK29 &rk29_device_i2c0, #endif From 161540c8ccc67144f21ab9df1dfd7d1ba982b944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E7=9B=9B=E9=A3=9E?= Date: Wed, 4 Jan 2012 21:28:24 +0800 Subject: [PATCH 4/5] power_supply: add charger display --- drivers/power/Kconfig | 3 + drivers/power/Makefile | 1 + drivers/power/power_supply_core.c | 7 ++ drivers/power/rk29_charger_display.c | 137 +++++++++++++++++++++++++++ include/linux/power_supply.h | 4 + 5 files changed, 152 insertions(+) create mode 100755 drivers/power/rk29_charger_display.c diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 54c3ed608409..7342bdde7967 100755 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -304,4 +304,7 @@ config BATTERY_RK29_AC_CHARGE help say Y to enable suspport for the AC battery charge +config POWER_ON_CHARGER_DISPLAY + bool "Support charger display" + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 5e24f30321ad..213a799bdae4 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -42,3 +42,4 @@ 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 +obj-$(CONFIG_POWER_ON_CHARGER_DISPLAY) += rk29_charger_display.o diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 03810ce5633f..f76f0bcbf606 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -19,6 +19,9 @@ #include #include "power_supply.h" +#ifdef CONFIG_POWER_ON_CHARGER_DISPLAY +extern struct list_head rk_psy_head; +#endif /* exported for the APM Power driver, APM emulation */ struct class *power_supply_class; EXPORT_SYMBOL_GPL(power_supply_class); @@ -181,6 +184,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy) device_initialize(dev); +#ifdef CONFIG_POWER_ON_CHARGER_DISPLAY + list_add(&psy->rk_psy_node, &rk_psy_head); +#endif + dev->class = power_supply_class; dev->type = &power_supply_dev_type; dev->parent = parent; diff --git a/drivers/power/rk29_charger_display.c b/drivers/power/rk29_charger_display.c new file mode 100755 index 000000000000..4afe3f9f7da3 --- /dev/null +++ b/drivers/power/rk29_charger_display.c @@ -0,0 +1,137 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + +//#define RK29_PLAY_ON_PIN RK29_PIN6_PA7 +//#define MAX_PRE_CNT 2 +//#define DET_CNT 5 +#define PWR_ON_THRESHD 5 //power on threshd of capacity +//unsigned int pre_cnt = 0; //for long press counter +//int charge_disp_mode = 0; +int pwr_on_thrsd = 5; //power on capcity threshold + +//extern int board_boot_mode(void); +//extern int boot_mode_init(char * s); + +extern void kernel_power_off(void); + +static int __init pwr_on_thrsd_setup(char *str) +{ + + pwr_on_thrsd = simple_strtol(str,NULL,10); + printk(KERN_INFO "power on threshold:%d",pwr_on_thrsd); + return 0; +} + +__setup("pwr_on_thrsd=", pwr_on_thrsd_setup); + + +LIST_HEAD(rk_psy_head); //add by yxj for charge logo display boot_command_line +//int charger_mode=0; //1:charge,0:not charge +static void add_bootmode_charger_to_cmdline(void) +{ + char *pmode=" androidboot.mode=charger"; + //int off = strlen(saved_command_line); + char *new_command_line = kzalloc(strlen(saved_command_line) + strlen(pmode) + 1, GFP_KERNEL); + sprintf(new_command_line, "%s%s", saved_command_line, pmode); + saved_command_line = new_command_line; + //strcpy(saved_command_line+off,pmode); + + //int off = strlen(boot_command_line); + //strcpy(boot_command_line+off,pmode); + + printk("Kernel command line: %s\n", saved_command_line); +} + +//display charger logo in kernel CAPACITY + + +static int __init start_charge_logo_display(void) +{ + union power_supply_propval val_status = {POWER_SUPPLY_STATUS_DISCHARGING}; + union power_supply_propval val_capacity ={ 100} ; + struct power_supply *psy; + int online = 0; + + printk("start_charge_logo_display\n"); + + if(board_boot_mode() == BOOT_MODE_RECOVERY) //recovery mode + { + printk("recovery mode \n"); + return 0; + + } + + list_for_each_entry(psy, &rk_psy_head, rk_psy_node) + { + psy->get_property(psy,POWER_SUPPLY_PROP_ONLINE,&val_status); + + online += val_status.intval; + + psy->get_property(psy,POWER_SUPPLY_PROP_CAPACITY,&val_capacity); + } + + if(online >= 1) + val_status.intval = POWER_SUPPLY_STATUS_CHARGING; + + // low power and discharging + + if((val_capacity.intval < pwr_on_thrsd )&&(val_status.intval != POWER_SUPPLY_STATUS_CHARGING)) + { + printk("low power\n"); + kernel_power_off(); + while(1); + return 0; + } + +/* + //low power and charging + if((val_capacity.intval < pwr_on_thrsd )&&(val_status.intval == POWER_SUPPLY_STATUS_CHARGING)) + { + while((val_capacity.intval < pwr_on_thrsd )) + { + list_for_each_entry(psy, &rk_psy_head, rk_psy_node) + { + psy->get_property(psy,POWER_SUPPLY_PROP_CAPACITY,&val_capacity); + } + + //printk("charging ... \n"); + } + } +*/ + + + if(val_status.intval == POWER_SUPPLY_STATUS_CHARGING) + { + if(board_boot_mode() != BOOT_MODE_REBOOT) //do not enter power on charge mode when soft reset + { + add_bootmode_charger_to_cmdline(); + //boot_mode_init("charge"); + printk("power in charge mode\n"); + } + } + + return 0; +} + +subsys_initcall_sync(start_charge_logo_display); + diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 2287c3214138..0cc26c7e5704 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -147,6 +147,10 @@ struct power_supply { char **supplied_to; size_t num_supplicants; +#ifdef CONFIG_POWER_ON_CHARGER_DISPLAY + struct list_head rk_psy_node; +#endif + int (*get_property)(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); From 7483ee6232bfec42cbe21ef852788aee1cd7e5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E7=9B=9B=E9=A3=9E?= Date: Wed, 4 Jan 2012 21:42:05 +0800 Subject: [PATCH 5/5] wm831x_power: add charger display support --- drivers/power/wm831x_power.c | 63 +++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c index 51f9b54baa40..6c99beff2174 100755 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c @@ -104,6 +104,7 @@ 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) { @@ -614,7 +615,7 @@ static int wm831x_bat_get_prop(struct power_supply *psy, //val->intval = wm831x_power->batt_info.status; break; case POWER_SUPPLY_PROP_PRESENT: - case POWER_SUPPLY_PROP_ONLINE: + //case POWER_SUPPLY_PROP_ONLINE: //ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT, val); val->intval = wm831x_power->batt_info.online; break; @@ -710,6 +711,7 @@ 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. */ @@ -1004,6 +1006,56 @@ static void wm831x_batt_work(struct work_struct *work) } +#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); @@ -1028,6 +1080,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; @@ -1107,6 +1160,10 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) 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; @@ -1192,7 +1249,11 @@ 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) {