From 55ca1d277a7b7a99ed02e8e14b5e36f0c5e9e86d Mon Sep 17 00:00:00 2001 From: Markinus Date: Tue, 26 Oct 2010 17:41:48 +0200 Subject: [PATCH] htcleo: add new battery driver This driver is comming from desire hd and is modificated for htcleo --- arch/arm/configs/htcleo_defconfig | 4 +- arch/arm/mach-msm/Makefile | 4 +- arch/arm/mach-msm/board-htcleo-battery.c | 728 ---------- arch/arm/mach-msm/board-htcleo-power.c | 214 --- arch/arm/mach-msm/board-htcleo.c | 53 +- arch/arm/mach-msm/htc_battery.c | 238 +++- arch/arm/mach-msm/include/mach/htc_battery.h | 14 +- drivers/power/ds2746_battery.c | 1248 ++++++++++++++++++ drivers/power/ds2746_param.c | 852 ++++++++++++ include/linux/ds2746_battery.h | 147 +++ include/linux/ds2746_battery_config.h | 44 + include/linux/ds2746_param.h | 70 + include/linux/ds2746_param_config.h | 274 ++++ include/linux/tps65200.h | 23 + include/linux/wrapper_types.h | 92 ++ 15 files changed, 3008 insertions(+), 997 deletions(-) delete mode 100644 arch/arm/mach-msm/board-htcleo-battery.c delete mode 100644 arch/arm/mach-msm/board-htcleo-power.c create mode 100644 drivers/power/ds2746_battery.c create mode 100644 drivers/power/ds2746_param.c create mode 100644 include/linux/ds2746_battery.h create mode 100644 include/linux/ds2746_battery_config.h create mode 100644 include/linux/ds2746_param.h create mode 100644 include/linux/ds2746_param_config.h create mode 100644 include/linux/tps65200.h create mode 100644 include/linux/wrapper_types.h diff --git a/arch/arm/configs/htcleo_defconfig b/arch/arm/configs/htcleo_defconfig index c6697304..4320b561 100644 --- a/arch/arm/configs/htcleo_defconfig +++ b/arch/arm/configs/htcleo_defconfig @@ -236,7 +236,7 @@ CONFIG_MACH_HTCLEO=y # CONFIG_MACH_INCREDIBLE is not set # CONFIG_MACH_INCREDIBLEC is not set # CONFIG_MACH_SUPERSONIC is not set -# CONFIG_HTC_BATTCHG is not set +CONFIG_HTC_BATTCHG=y # CONFIG_HTC_PWRSINK is not set CONFIG_HTC_SLEEP_MODE_GPIO_DUMP=y # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set @@ -1161,7 +1161,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_DS2760 is not set # CONFIG_BATTERY_DS2782 is not set # CONFIG_BATTERY_DS2784 is not set -# CONFIG_BATTERY_DS2746 is not set +CONFIG_BATTERY_DS2746=y # CONFIG_BATTERY_BQ27x00 is not set # CONFIG_BATTERY_MAX17040 is not set # CONFIG_HWMON is not set diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 847d9811..5f3a5b58 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -115,9 +115,9 @@ obj-$(CONFIG_MACH_BRAVOC) += board-bravoc-wifi.o htc_awb_cal.o obj-$(CONFIG_MACH_BRAVOC) += board-bravoc-microp.o clock.o obj-$(CONFIG_MACH_HTCLEO) += board-htcleo.o board-htcleo-panel.o board-htcleo-keypad.o -obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-ts.o board-htcleo-mmc.o ieee754-df.o board-htcleo-power.o +obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-ts.o board-htcleo-mmc.o ieee754-df.o -obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-battery.o board-htcleo-log.o board-htcleo-audio.o board-htcleo-acoustic.o +obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-audio.o board-htcleo-acoustic.o obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-rfkill.o board-htcleo-wifi.o board-htcleo-microp.o obj-$(CONFIG_MACH_HTCLEO) += board-htcleo-proximity.o board-htcleo-leds.o board-htcleo-ls.o obj-$(CONFIG_MACH_HTCLEO) += clock-wince.o diff --git a/arch/arm/mach-msm/board-htcleo-battery.c b/arch/arm/mach-msm/board-htcleo-battery.c deleted file mode 100644 index 20488aa9..00000000 --- a/arch/arm/mach-msm/board-htcleo-battery.c +++ /dev/null @@ -1,728 +0,0 @@ -/* arch/arm/mach-msm/board-htcleo-battery.h -* -* Copyright (C) 2010 Markinus -* Copyright (C) 2009 HTC Corporation -* Copyright (C) 2009 Google, Inc. -* -* 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. -* -*/ - -/* - -actually this file called "board-htcleo-battery.c", so it's HTC LEO specific only -most values are hard coded here really. -this driver designed for android. - -proper ds2745 driver located at "driver/i2c/chips/ds2745.c" -I don't want and have not resources to support any "proper" linux drivers coding. -("proper" for them, not for me ofcourse) - -my primary target was to make high quality battery support in Android for HTC LEO. kefir with us! - -*/ -//#define DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "gpio_chip.h" -#include "board-htcleo.h" - -static enum power_supply_property battery_properties[] = -{ - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CURRENT_AVG, - POWER_SUPPLY_PROP_CHARGE_COUNTER, -}; - -static int battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); -static void htcleo_program_alarm(struct htcleo_device_info *di, int seconds); -static void battery_ext_power_changed(struct power_supply *psy); - -#define BATTERY_LOG_MAX 1024 -#define BATTERY_LOG_MASK (BATTERY_LOG_MAX - 1) - -static DEFINE_MUTEX(battery_log_lock); -static struct battery_status battery_log[BATTERY_LOG_MAX]; -static unsigned battery_log_head; -static unsigned battery_log_tail; - -void battery_log_status(struct battery_status *s) -{ - unsigned n; - mutex_lock(&battery_log_lock); - n = battery_log_head; - memcpy(battery_log + n, s, sizeof(struct battery_status)); - n = (n + 1) & BATTERY_LOG_MASK; - if (n == battery_log_tail) - battery_log_tail = (battery_log_tail + 1) & BATTERY_LOG_MASK; - battery_log_head = n; - mutex_unlock(&battery_log_lock); -} - -static const char *battery_source[3] = { "none", " usb", " ac" }; -static const char *battery_mode[4] = { " off", "slow", "fast", "full" }; - -static int battery_log_print(struct seq_file *sf, void *private) -{ - unsigned n; - mutex_lock(&battery_log_lock); - seq_printf(sf, "timestamp mV mA avg mA uAh dC %% src mode reg full\n"); - for (n = battery_log_tail; n != battery_log_head; n = (n + 1) & BATTERY_LOG_MASK) - { - struct battery_status *s = battery_log + n; - seq_printf(sf, "%9d %5d %6d %6d %8d %4d %3d %s %s 0x%02x %d\n", - s->timestamp, s->voltage_uV / 1000, - s->current_uA / 1000, s->current_avg_uA / 1000, - s->charge_uAh, s->temp_C, - s->percentage, - battery_source[s->charge_source], - battery_mode[s->charge_mode], - s->status_reg, s->battery_full); - } - mutex_unlock(&battery_log_lock); - return 0; -} - -static int I2C_Read_Status(struct htcleo_device_info *di) -{ - uint8_t i2c_msg[1]; - uint8_t i2c_data[2]; - - i2c_msg[0] = 0x01; //status reg - - i2c_master_send(di->client, i2c_msg, 1); - i2c_master_recv(di->client, i2c_data, 2); - - dev_dbg(&di->client->dev, "I2C_Read_Status() = %08X!\n", i2c_data[0]); - di->raw_status = i2c_data[0]; - return 0; -} - -static int I2C_Read_Data(struct htcleo_device_info *di) -{ - uint8_t i2c_msg[1]; - uint8_t i2c_data[10]; - - i2c_msg[0] = 0x08; // AUX0 AUX1 VOLTAGE CURRENT ACR - - i2c_master_send(di->client, i2c_msg, 1); - i2c_master_recv(di->client, i2c_data, 10); - - dev_dbg(&di->client->dev, "I2C_Read_Data() %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X!\n", - i2c_data[0], i2c_data[1], i2c_data[2], i2c_data[3], i2c_data[4], - i2c_data[5], i2c_data[6], i2c_data[7], i2c_data[8], i2c_data[9]); - memcpy(di->raw, i2c_data, 10); - return 0; -} - -static int I2C_Write_ACR(struct htcleo_device_info *di, uint16_t val) -{ - uint8_t i2c_msg[3]; - - i2c_msg[0] = 0x10; - i2c_msg[1] = (val >> 8) & 0xFF; - i2c_msg[2] = (val >> 0) & 0xFF; - -// dev_dbg(&di->client->dev, "I2C_Write_ACR() = %04X!\n", val); - i2c_master_send(di->client, i2c_msg, 3); - return 0; -} - -////////////////////////////////////////////////////////////////////////// - -static int htcleo_charge(int on, int fast) -{ - gpio_direction_output(HTCLEO_GPIO_BATTERY_CHARGER_CURRENT, !!fast); - gpio_direction_output(HTCLEO_GPIO_BATTERY_CHARGER_ENABLE, !on); - return 0; -} - -static void htcleo_parse_data(uint32_t raw_status, u8 *raw, struct battery_status *s) -{ - short n; - uint32_t n32; - uint32_t FL, ACR, ACR_EMPTY; - - /* Get status reg */ - s->status_reg = raw_status; - - /* Get Level */ - // TODO: FL too wrong (?) - ACR = ((raw[8] << 8) | raw[9]); - FL = (LEO_BATTERY_CAPACITY * 1570) / 625; - ACR_EMPTY = (LEO_BATTERY_EMPTY * 1570) / 625; - s->percentage = (100 * (ACR - ACR_EMPTY)) / FL; - - s->charge_uAh = 1000 * (((ACR - ACR_EMPTY) * 625) / 1570); - - if (s->percentage < 0 ) s->percentage = 0; - if (s->percentage > 100 ) s->percentage = 100; - - - /* Get Voltage */ - n32 = (((raw[4] << 8) | raw[5]) / 16); - //s->voltage_uV = (n32 * 244) / 100; - s->voltage_uV = 1000 * ((n32 * 312) >> 7); // div to 128 - - /* Get Current */ - n = ((raw[6]) << 8) | raw[7]; - s->current_uA = 1000 * (((n/4) * 625) / 1570); - - printk("ACR=%d CURR=%d VOL=%d RAAC=%d\n", ACR, s->current_uA, s->voltage_uV, s->percentage); - - // average current not supported by DS2746 - s->current_avg_uA = s->current_uA; - - /* Get Temperature */ - n = ((raw[0] << 8) | (raw[1])); - n /= 16; - - // printk("temp = %x\n", n); - if (n > 2047 || n == 0) - { - s->temp_C = 250; - } - else - { - double v = 0.021277 * (300.0 / (2047.0 / n - 1.0)); - s->temp_C = 10 * (1.0 / ((log(v) * 0.000290698) + 0.003354016) - 273.15); - } - if (s->temp_C < -250) - { - s->temp_C = -250; - } -} - -static int htcleo_battery_read_status(struct htcleo_device_info *di) -{ - // read status - I2C_Read_Status(di); - I2C_Read_Data(di); - - // printk("status = %04X\n", di->raw_status); - htcleo_parse_data(di->raw_status, di->raw, &di->status); - - dev_dbg(&di->client->dev, "batt: %3d%%, %d mV, %d mA (%d avg), %d.%d C, %d mAh\n", - di->status.percentage, - di->status.voltage_uV / 1000, di->status.current_uA / 1000, - di->status.current_avg_uA / 1000, - di->status.temp_C / 10, di->status.temp_C % 10, - di->status.charge_uAh / 1000); - - return 0; -} - -////////////////////////////////////////////////////////////////////////// - -static int htcleo_set_cc(struct htcleo_device_info *di, bool enable) -{ - // not supported for DS2745, there no CE bit - return 0; -} - - -static int battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) -{ - struct htcleo_device_info *di = psy_to_dev_info(psy); - - switch (psp) - { - case POWER_SUPPLY_PROP_STATUS: - switch (di->status.charge_source) - { - case CHARGE_OFF: - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; - break; - case CHARGE_FAST: - case CHARGE_SLOW: - if (di->status.battery_full) - val->intval = POWER_SUPPLY_STATUS_FULL; - else if (di->status.charge_mode == CHARGE_OFF) - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; - else - val->intval = POWER_SUPPLY_STATUS_CHARGING; - break; - default: - val->intval = POWER_SUPPLY_STATUS_UNKNOWN; - break; - } - break; - case POWER_SUPPLY_PROP_HEALTH: - if (di->status.temp_C >= TEMP_HOT) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - else - val->intval = POWER_SUPPLY_HEALTH_GOOD; - break; - case POWER_SUPPLY_PROP_PRESENT: - /* XXX todo */ - val->intval = 1; - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - if (di->dummy) - val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; - else - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case POWER_SUPPLY_PROP_CAPACITY: - if (di->dummy) - val->intval = 75; - else - val->intval = di->status.percentage; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = di->status.voltage_uV; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = di->status.temp_C; - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = di->status.current_uA; - break; - case POWER_SUPPLY_PROP_CURRENT_AVG: - val->intval = di->status.current_avg_uA; - break; - case POWER_SUPPLY_PROP_CHARGE_COUNTER: - val->intval = di->status.charge_uAh; - break; - default: - return -EINVAL; - } - - return 0; -} - -static void htcleo_battery_update_status(struct htcleo_device_info *di) -{ - u8 last_level; - last_level = di->status.percentage; - - htcleo_battery_read_status(di); - - // CotullaTODO: add ACR = FL at top point here - - if ((last_level != di->status.percentage) || (di->status.temp_C > 450)) - { - power_supply_changed(&di->bat); - } -} - -static DEFINE_MUTEX(charge_state_lock); - -static bool check_timeout(ktime_t now, ktime_t last, int seconds) -{ - ktime_t timeout = ktime_add(last, ktime_set(seconds, 0)); - return ktime_sub(timeout, now).tv64 < 0; -} - -static int battery_adjust_charge_state(struct htcleo_device_info *di) -{ - unsigned source; - int rc = 0; - int temp, volt, curr, perc; - int ovp; - u8 charge_mode; - bool charge_timeout = false; - static int lastval=0xffff; - const int min_curr=5000; - - mutex_lock(&charge_state_lock); - - curr = di->status.current_uA; - perc = di->status.percentage; - temp = di->status.temp_C; - volt = di->status.voltage_uV / 1000; - - source = di->status.charge_source; - - /* initially our charge mode matches our source: - * NONE:OFF, USB:SLOW, AC:FAST - */ - charge_mode = source; - - // Check whether the bat is full and change the ACR to right 100% value - if(curr >=0 && curr < min_curr && lastval < min_curr && perc > 95 && source) { - uint32_t FL = (LEO_BATTERY_CAPACITY * 1570) / 625; - uint32_t ACR_EMPTY = (LEO_BATTERY_EMPTY * 1570) / 625; - I2C_Write_ACR(di, FL+ACR_EMPTY); - di->status.battery_full = 1; - di->status.percentage = 100; - charge_mode = CHARGE_OFF; - } - else { - di->status.battery_full = 0; - } - if(curr >=0) lastval=curr; - else lastval=0xffff; - - ovp = gpio_get_value(HTCLEO_GPIO_BATTERY_OVER_CHG); - if (ovp) - { - printk("Battery overpowered!\n"); - } - - if (temp >= TEMP_HOT || ovp) - { - if (temp >= TEMP_CRITICAL) - { - charge_mode = CHARGE_OFF; - } - - /* once we charge to max voltage when hot, disable - * charging until the temp drops or the voltage drops - */ - if (volt >= TEMP_HOT_MAX_MV) - { - di->status.cooldown = 1; - } - } - - /* when the battery is warm, only charge in slow charge mode */ - if ((temp >= TEMP_WARM) && (charge_mode == CHARGE_FAST)) - charge_mode = CHARGE_SLOW; - - if (di->status.cooldown) - { - if ((temp < TEMP_WARM) || (volt <= TEMP_HOT_MIN_MV)) - di->status.cooldown = 0; - else - charge_mode = CHARGE_OFF; - } - - if (di->status.current_uA > 1024) - { - di->last_charge_seen = di->last_poll; - } - else if (di->last_charge_mode != CHARGE_OFF && check_timeout(di->last_poll, di->last_charge_seen, 60 * 60)) - { - /* The charger has probably stopped charging. Turn it - * off until the next sample period. - */ - charge_timeout = true; - charge_mode = CHARGE_OFF; - } - - if (source == CHARGE_OFF) - charge_mode = CHARGE_OFF; - - - if (di->last_charge_mode == charge_mode) - goto done; - - di->last_charge_mode = charge_mode; - di->status.charge_mode = charge_mode; - - switch (charge_mode) - { - case CHARGE_OFF: - htcleo_charge(0, 0); - htcleo_set_cc(di, true); - if (temp >= TEMP_CRITICAL) - pr_info("batt: charging OFF [OVERTEMP]\n"); - else if (di->status.cooldown) - pr_info("batt: charging OFF [COOLDOWN]\n"); - else if (di->status.battery_full) - pr_info("batt: charging OFF [FULL]\n"); - else if (charge_timeout) - pr_info("batt: charging OFF [TIMEOUT]\n"); - else - pr_info("batt: charging OFF\n"); - break; - case CHARGE_SLOW: - di->last_charge_seen = di->last_poll; - htcleo_set_cc(di, true); - htcleo_charge(1, 0); - pr_info("batt: charging SLOW\n"); - break; - case CHARGE_FAST: - di->last_charge_seen = di->last_poll; - htcleo_set_cc(di, true); - htcleo_charge(1, 1); - pr_info("batt: charging FAST\n"); - break; - } - rc = 1; - done: - mutex_unlock(&charge_state_lock); - return rc; -} - - - -static void htcleo_battery_work(struct work_struct *work) -{ - struct htcleo_device_info *di = container_of(work, struct htcleo_device_info, monitor_work); - struct timespec ts; - unsigned long flags; - - htcleo_battery_update_status(di); - - di->last_poll = alarm_get_elapsed_realtime(); - - if (battery_adjust_charge_state(di)) - { - power_supply_changed(&di->bat); - } - - ts = ktime_to_timespec(di->last_poll); - di->status.timestamp = ts.tv_sec; - battery_log_status(&di->status); - - /* prevent suspend before starting the alarm */ - local_irq_save(flags); - wake_unlock(&di->work_wake_lock); - htcleo_program_alarm(di, FAST_POLL); - local_irq_restore(flags); -} - -////////////////////////////////////////////////////////////////////////// - -static void htcleo_program_alarm(struct htcleo_device_info *di, int seconds) -{ - ktime_t low_interval = ktime_set(seconds - 10, 0); - ktime_t slack = ktime_set(20, 0); - ktime_t next; - - next = ktime_add(di->last_poll, low_interval); - - alarm_start_range(&di->alarm, next, ktime_add(next, slack)); -} - -static void htcleo_battery_alarm(struct alarm *alarm) -{ - struct htcleo_device_info *di = container_of(alarm, struct htcleo_device_info, alarm); - wake_lock(&di->work_wake_lock); - queue_work(di->monitor_wqueue, &di->monitor_work); -} - -static void battery_ext_power_changed(struct power_supply *psy) -{ - struct htcleo_device_info *di; - int got_power; - - di = psy_to_dev_info(psy); - got_power = power_supply_am_i_supplied(psy); - - if (got_power) - { - if (is_ac_power_supplied()) - di->status.charge_source = SOURCE_AC; - else - di->status.charge_source = SOURCE_USB; - wake_lock(&vbus_wake_lock); - } - else - { - di->status.charge_source = SOURCE_NONE; - /* give userspace some time to see the uevent and update - * LED state or whatnot... - */ - wake_lock_timeout(&vbus_wake_lock, HZ / 2); - } - battery_adjust_charge_state(di); - power_supply_changed(psy); -} - -static int htcleo_battery_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - int rc; - struct htcleo_device_info *di; - - printk("%s\n", __func__); - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) - { - return -EIO; - } - - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) - { - return -ENOMEM; - } - - mydi=di; - - di->client = client; - - i2c_set_clientdata(client, di); - - gpio_request(HTCLEO_GPIO_BATTERY_CHARGER_CURRENT, "charger_current"); - gpio_request(HTCLEO_GPIO_BATTERY_CHARGER_ENABLE, "charger_enable"); - gpio_request(HTCLEO_GPIO_BATTERY_OVER_CHG, "charger_over_chg"); - - gpio_direction_output(HTCLEO_GPIO_BATTERY_CHARGER_CURRENT, 1); - gpio_direction_output(HTCLEO_GPIO_BATTERY_CHARGER_ENABLE, 1); - gpio_direction_input(HTCLEO_GPIO_BATTERY_OVER_CHG); - - - di->bat.name = "battery"; - di->bat.type = POWER_SUPPLY_TYPE_BATTERY; - di->bat.properties = battery_properties; - di->bat.num_properties = ARRAY_SIZE(battery_properties); - di->bat.external_power_changed = battery_ext_power_changed; - di->bat.get_property = battery_get_property; - di->last_charge_mode = 0xff; - - rc = power_supply_register(&client->dev, &di->bat); - if (rc) - { - goto fail_register; - } - - INIT_WORK(&di->monitor_work, htcleo_battery_work); - di->monitor_wqueue = create_freezeable_workqueue(dev_name(&client->dev)); - - /* init to something sane */ - di->last_poll = alarm_get_elapsed_realtime(); - - if (!di->monitor_wqueue) - { - rc = -ESRCH; - goto fail_workqueue; - } - wake_lock_init(&di->work_wake_lock, WAKE_LOCK_SUSPEND, "htcleo-battery"); - printk("alarm init\n"); - alarm_init(&di->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, htcleo_battery_alarm); - wake_lock(&di->work_wake_lock); - queue_work(di->monitor_wqueue, &di->monitor_work); - printk("%s: done!\n", __func__); - return 0; - - fail_workqueue: - power_supply_unregister(&di->bat); - fail_register: - - - gpio_free(HTCLEO_GPIO_BATTERY_CHARGER_CURRENT); - gpio_free(HTCLEO_GPIO_BATTERY_CHARGER_ENABLE); - gpio_free(HTCLEO_GPIO_BATTERY_OVER_CHG); - - kfree(di); - return rc; -} - -static int htcleo_suspend(struct device *dev) -{ - struct htcleo_device_info *di = dev_get_drvdata(dev); - - /* If we are on battery, reduce our update rate until - * we next resume. - */ - if (di->status.charge_source == SOURCE_NONE) - { - htcleo_program_alarm(di, SLOW_POLL); - di->slow_poll = 1; - } - return 0; -} - -static int htcleo_resume(struct device *dev) -{ - struct htcleo_device_info *di = dev_get_drvdata(dev); - - /* We might be on a slow sample cycle. If we're - * resuming we should resample the battery state - * if it's been over a minute since we last did - * so, and move back to sampling every minute until - * we suspend again. - */ - if (di->slow_poll) - { - htcleo_program_alarm(di, FAST_POLL); - di->slow_poll = 0; - } - return 0; -} - -static struct dev_pm_ops htcleo_pm_ops = -{ - .suspend = htcleo_suspend, - .resume = htcleo_resume, -}; - - -static const struct i2c_device_id htcleo_battery_id[] = -{ - { "htcleo-battery", 0 }, - { } -}; - - -static struct i2c_driver htcleo_battery_driver = -{ - .driver = - { - .name = "htcleo-battery", - .owner = THIS_MODULE, - .pm = &htcleo_pm_ops, - }, - .id_table = htcleo_battery_id, - .probe = htcleo_battery_probe, -#ifndef CONFIG_HAS_EARLYSUSPEND - /* .suspend = htcleo_suspend, - .resume = htcleo_resume,*/ -#endif -}; - -static int battery_log_open(struct inode *inode, struct file *file) -{ - return single_open(file, battery_log_print, NULL); -} - -static struct file_operations battery_log_fops = -{ - .open = battery_log_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int __init htcleo_battery_init(void) -{ - debugfs_create_file("battery_log", 0444, NULL, NULL, &battery_log_fops); - wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); - return i2c_add_driver(&htcleo_battery_driver); -} - -//module_init(htcleo_battery_init); -late_initcall(htcleo_battery_init); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Cotulla"); -MODULE_DESCRIPTION("htcleo battery driver"); -// END OF FILE diff --git a/arch/arm/mach-msm/board-htcleo-power.c b/arch/arm/mach-msm/board-htcleo-power.c deleted file mode 100644 index fcb9d8da..00000000 --- a/arch/arm/mach-msm/board-htcleo-power.c +++ /dev/null @@ -1,214 +0,0 @@ -/* arch/arm/mach-msm/board-htcleo-power.c -* -* Copyright (C) 2010 Cotulla -* Copyright (C) 2008 HTC Corporation. -* Copyright (C) 2008 Google, Inc. -* -* 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. -* -*/ - -// -// calls seq: -// -// notify_vbus_change_intr -> vbus_work_func -> msm_hsusb_set_vbus_state -> USB -> notify_usb_connected -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "board-htcleo.h" - -static char *supply_list[] = -{ - "battery", -}; - -static int inited; -static int vbus_present; -static int usb_status; -static struct work_struct vbus_work; - -#ifdef CONFIG_USB_ANDROID -void notify_usb_connected(int status); -static struct t_usb_status_notifier usb_status_notifier = { - .name = "htc_battery", - .func = notify_usb_connected, -}; -#endif - -static int power_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - if (psp != POWER_SUPPLY_PROP_ONLINE) - return -EINVAL; - - if (psy->type == POWER_SUPPLY_TYPE_MAINS) - { - val->intval = (usb_status == 2); - } - else - { - val->intval = vbus_present; - } - return 0; -} - -static enum power_supply_property power_properties[] = -{ - POWER_SUPPLY_PROP_ONLINE, -}; - -static struct power_supply ac_supply = -{ - .name = "ac", - .type = POWER_SUPPLY_TYPE_MAINS, - .supplied_to = supply_list, - .num_supplicants = ARRAY_SIZE(supply_list), - .properties = power_properties, - .num_properties = ARRAY_SIZE(power_properties), - .get_property = power_get_property, -}; - -static struct power_supply usb_supply = -{ - .name = "usb", - .type = POWER_SUPPLY_TYPE_USB, - .supplied_to = supply_list, - .num_supplicants = ARRAY_SIZE(supply_list), - .properties = power_properties, - .num_properties = ARRAY_SIZE(power_properties), - .get_property = power_get_property, -}; - -static int get_vbus_state(void) -{ - if (readl(MSM_SHARED_RAM_BASE + 0xEF20C)) - return 1; - else - return 0; -} - -void notify_cable_status(int status) -{ - pr_info("### notify_cable_status(%d) ###\n", status); - vbus_present = status; - msm_hsusb_set_vbus_state(vbus_present); - power_supply_changed(&ac_supply); - power_supply_changed(&usb_supply); - if(mydi) - power_supply_changed(&mydi->bat); -} - -// called from USB driver -void notify_usb_connected(int status) -{ - if (!inited) return; - pr_info("### notify_usb_connected(%d) ###\n", status); - usb_status = status; - power_supply_changed(&ac_supply); - power_supply_changed(&usb_supply); - if(mydi) - power_supply_changed(&mydi->bat); -} - -// called from DEX intrrupt -void notify_vbus_change_intr(void) -{ - if (!inited) return; - schedule_work(&vbus_work); -} - -// used by battery driver -int is_ac_power_supplied(void) -{ - return (usb_status == 2); -} - -static void vbus_work_func(struct work_struct *work) -{ - int vbus = get_vbus_state(); - printk(" new vbus = %d\n", vbus); - #ifdef CONFIG_USB_EHCI_HCD - if (vbus) - return 0; - else - return 0; - #else - if (vbus) - gpio_set_value(HTCLEO_GPIO_POWER_USB, 0); - else - gpio_set_value(HTCLEO_GPIO_POWER_USB, 1); - #endif - notify_cable_status(vbus); -} - - -static int htcleo_power_probe(struct platform_device *pdev) -{ - printk("$$$ htcleo_power_probe $$$\n"); - - INIT_WORK(&vbus_work, vbus_work_func); - - gpio_request(HTCLEO_GPIO_POWER_USB, "power_usb"); - gpio_direction_output(HTCLEO_GPIO_POWER_USB, 0); - - power_supply_register(&pdev->dev, &ac_supply); - power_supply_register(&pdev->dev, &usb_supply); - - inited = 1; - // init VBUS state - notify_vbus_change_intr(); - return 0; -} - -//#define APP_BATT_PDEV_NAME "rs30100001:00000000" -#define APP_BATT_PDEV_NAME "htcleo_power" - -static struct platform_driver htcleo_power_driver = -{ - .probe = htcleo_power_probe, - .driver = - { - .name = APP_BATT_PDEV_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init htcleo_power_init(void) -{ - printk("htcleo_power_init\n"); - platform_driver_register(&htcleo_power_driver); - #ifdef CONFIG_USB_ANDROID - usb_register_notifier(&usb_status_notifier); - #endif - return 0; -} - -//module_init(htcleo_power_init); -late_initcall(htcleo_power_init); -MODULE_DESCRIPTION("HTCLEO Power Driver"); -MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-htcleo.c b/arch/arm/mach-msm/board-htcleo.c index 47632913..f4424801 100644 --- a/arch/arm/mach-msm/board-htcleo.c +++ b/arch/arm/mach-msm/board-htcleo.c @@ -34,6 +34,7 @@ #endif #include #include <../../../drivers/staging/android/timed_gpio.h> +#include #include #include @@ -231,9 +232,6 @@ static struct microp_i2c_platform_data microp_data = { static struct i2c_board_info base_i2c_devices[] = { - { - I2C_BOARD_INFO("htcleo-battery", 0x26), - }, { I2C_BOARD_INFO(LEO_TOUCH_DRV_NAME, 0), }, @@ -716,11 +714,45 @@ static struct platform_device ram_console_device = { // Power/Battery /////////////////////////////////////////////////////////////////////// -static struct platform_device htcleo_power = -{ - .name = "htcleo_power", - .id = -1, +static struct htc_battery_platform_data htc_battery_pdev_data = { + .func_show_batt_attr = htc_battery_show_attr, + .gpio_mbat_in = -1, + .gpio_mchg_en_n = HTCLEO_GPIO_BATTERY_CHARGER_ENABLE, + .gpio_iset = HTCLEO_GPIO_BATTERY_CHARGER_CURRENT, + .guage_driver = GUAGE_DS2746, + .charger = LINEAR_CHARGER, + .m2a_cable_detect = 0, + .force_no_rpc = 1, + .int_data = { + .chg_int = HTCLEO_GPIO_BATTERY_OVER_CHG, + }, }; + +static struct platform_device htc_battery_pdev = { + .name = "htc_battery", + .id = -1, + .dev = { + .platform_data = &htc_battery_pdev_data, + }, +}; + +static int get_thermal_id(void) +{ + return THERMAL_300; +} + +static struct ds2746_platform_data ds2746_pdev_data = { + .func_get_thermal_id = get_thermal_id, +}; + +static struct platform_device ds2746_battery_pdev = { + .name = "ds2746-battery", + .id = -1, + .dev = { + .platform_data = &ds2746_pdev_data, + }, +}; + /////////////////////////////////////////////////////////////////////// // Real Time Clock /////////////////////////////////////////////////////////////////////// @@ -751,10 +783,11 @@ static struct platform_device *devices[] __initdata = &android_pmem_adsp_device, &android_pmem_venc_device, &msm_device_i2c, + &ds2746_battery_pdev, + &htc_battery_pdev, &msm_kgsl_device, &msm_camera_sensor_s5k3e2fx, &htcleo_flashlight_device, - &htcleo_power, &qsd_device_spi, &htc_headset_mgr, &htc_headset_gpio, @@ -905,10 +938,6 @@ static void __init htcleo_init(void) platform_device_register(&htcleo_timed_gpios); -#ifdef CONFIG_USB_ANDROID - msm_hsusb_set_vbus_state(htcleo_get_vbus_state()); -#endif - /* Blink the camera LED shortly to show that we're alive! */ #ifdef CONFIG_HTCLEO_BLINK_AT_BOOT bank6_in = (unsigned int*)(MSM_GPIO1_BASE + 0x0864); diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index b201cce5..b6dd0633 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -30,21 +30,23 @@ #include /* Jay, to register display notifier */ #include #include +#include +#include #ifdef CONFIG_HTC_BATTCHG_SMEM #include "smd_private.h" #endif +#include +#include #if defined(CONFIG_TROUT_BATTCHG_DOCK) #include #endif #ifdef CONFIG_BATTERY_DS2784 #include -#elif CONFIG_BATTERY_DS2746 +#elif defined(CONFIG_BATTERY_DS2746) #include #endif -#include - static struct wake_lock vbus_wake_lock; enum { @@ -114,6 +116,7 @@ struct htc_battery_info { int gpio_iset; int guage_driver; int m2a_cable_detect; + int force_no_rpc; int charger; }; @@ -204,6 +207,37 @@ int unregister_notifier_cable_status(struct notifier_block *nb) return blocking_notifier_chain_unregister(&cable_status_notifier_list, nb); } +/* -------------------------------------------------------------------------- */ +/* HTCLeo Dex Functions. */ +#if defined(CONFIG_MACH_HTCLEO) + +static int get_vbus_state(void) +{ + if (readl(MSM_SHARED_RAM_BASE + 0xEF20C)) + return 1; + else + return 0; +} + +void notify_cable_status(int status) +{ + pr_info("notify_cable_status(%d)\n", status); + msm_hsusb_set_vbus_state(status); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); +} + +// called from DEX intrrupt +void notify_vbus_change_intr(void) +{ + int vbus; + if (!htc_battery_initial) return; + vbus = get_vbus_state(); + notify_cable_status(vbus); +} + +#endif /* -------------------------------------------------------------------------- */ /* For sleep charging screen. */ static int zcharge_enabled; @@ -328,7 +362,6 @@ device_initcall(batt_debug_init); static int init_batt_gpio(void) { - if (htc_batt_info.gpio_mbat_in > 0 && gpio_request(htc_batt_info.gpio_mbat_in, "batt_detect") < 0) goto gpio_failed; @@ -355,6 +388,7 @@ int battery_charging_ctrl(enum batt_ctl_t ctl) { int result = 0; + if (!htc_battery_initial) return 0; switch (ctl) { case DISABLE: BATT_LOG("charger OFF"); @@ -611,9 +645,13 @@ static void usb_status_notifier_func(int online) blocking_notifier_call_chain(&cable_status_notifier_list, htc_batt_info.rep.charging_source, NULL); - power_supply_changed(&htc_power_supplies[CHARGER_AC]); - power_supply_changed(&htc_power_supplies[CHARGER_USB]); - power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + if (htc_battery_initial) { + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + } else { + pr_err("\n\n ### htc_battery_code is not inited yet! ###\n\n"); + } update_wake_lock(htc_batt_info.rep.charging_source); #else mutex_lock(&htc_batt_info.lock); @@ -981,13 +1019,17 @@ static int htc_battery_get_charging_status(void) break; case CHARGER_USB: case CHARGER_AC: +#if !defined(CONFIG_BATTERY_DS2746) if ((htc_charge_full) && (htc_batt_info.rep.full_level == 100)) { htc_batt_info.rep.level = 100; } - +#endif level = htc_batt_info.rep.level; if (level == 100){ htc_charge_full = 1;} + else + htc_charge_full = 0; + if (htc_charge_full) ret = POWER_SUPPLY_STATUS_FULL; else if (htc_batt_info.rep.charging_enabled != 0) @@ -1014,12 +1056,20 @@ static int htc_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_HEALTH: val->intval = POWER_SUPPLY_HEALTH_GOOD; - if (machine_is_paradise() && (htc_batt_info.rep.batt_temp >= 500 || - htc_batt_info.rep.batt_temp <= 0)) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - else if (!machine_is_paradise() && (htc_batt_info.rep.batt_temp >= 480 || - htc_batt_info.rep.batt_temp <= 0)) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + if (machine_is_paradise()) { + if (htc_batt_info.rep.batt_temp >= 500 || + htc_batt_info.rep.batt_temp <= 0) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (machine_is_spade()) { + if (htc_batt_info.rep.batt_temp >= 450 || + htc_batt_info.rep.batt_temp <= 0) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + } else { + if (htc_batt_info.rep.batt_temp >= 480 || + htc_batt_info.rep.batt_temp <= 0) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + } + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) BATT_LOG("%s: %s: health=%d", __func__, psy->name, val->intval); break; @@ -1070,9 +1120,8 @@ static struct device_attribute htc_battery_attrs[] = { #ifdef CONFIG_HTC_BATTCHG_SMEM __ATTR(smem_raw, S_IRUGO, htc_battery_show_smem, NULL), __ATTR(smem_text, S_IRUGO, htc_battery_show_smem, NULL), -#else - __ATTR(batt_attr_text, S_IRUGO, htc_battery_show_batt_attr, NULL), #endif + __ATTR(batt_attr_text, S_IRUGO, htc_battery_show_batt_attr, NULL), }; enum { @@ -1194,7 +1243,6 @@ static ssize_t htc_battery_set_full_level(struct device *dev, { int rc = 0; unsigned long percent = 100; - unsigned long param = 0; percent = simple_strtoul(buf, NULL, 10); @@ -1214,9 +1262,8 @@ static ssize_t htc_battery_set_full_level(struct device *dev, mutex_lock(&htc_batt_info.lock); htc_full_level_flag = 1; htc_batt_info.rep.full_level = percent; - param = percent; blocking_notifier_call_chain(&cable_status_notifier_list, - 0xff, (void *) ¶m); + 0xff, (void *) &htc_batt_info.rep.full_level); mutex_unlock(&htc_batt_info.lock); } rc = 0; @@ -1288,7 +1335,7 @@ static int update_batt_info(void) ret = -1; } break; -#elif CONFIG_BATTERY_DS2746 +#elif defined(CONFIG_BATTERY_DS2746) case GUAGE_DS2746: if (ds2746_get_battery_info(&htc_batt_info.rep)) { BATT_ERR("%s: ds2746 read failed!!!", __func__); @@ -1378,10 +1425,93 @@ dont_need_update: return i; } +static irqreturn_t tps65200_int_detection(int irq, void *data) +{ + struct htc_battery_tps65200_int *ip = data; + + BATT_LOG("%s: over voltage is detected.", __func__); + + disable_irq_nosync(ip->chg_int); + + ip->tps65200_reg = 0; + + schedule_delayed_work(&ip->int_work, msecs_to_jiffies(200)); + + return IRQ_HANDLED; +} + +static void htc_battery_tps65200_int_func(struct work_struct *work) +{ + struct htc_battery_tps65200_int *ip; + int fault_bit; + ip = container_of(work, struct htc_battery_tps65200_int, + int_work.work); + + switch (ip->tps65200_reg) { + case CHECK_INT1: + /* read twice. First read to trigger TPS65200 clear fault bit + on INT1. Second read to make sure that fault bit is cleared + and call off ovp function.*/ + fault_bit = tps_set_charger_ctrl(CHECK_INT1); + BATT_LOG("INT1 value: %d", fault_bit); + fault_bit = tps_set_charger_ctrl(CHECK_INT1); + + if (fault_bit) { +#ifdef CONFIG_HTC_BATTCHG_SMEM + smem_batt_info->over_vchg = 1; +#else + htc_batt_info.rep.over_vchg = 1; +#endif + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + schedule_delayed_work(&ip->int_work, + msecs_to_jiffies(5000)); + BATT_LOG("OVER_VOLTAGE: " + "over voltage fault bit on TPS65200 is raised:" + " %d", fault_bit); + } else { +#ifdef CONFIG_HTC_BATTCHG_SMEM + smem_batt_info->over_vchg = 0; +#else + htc_batt_info.rep.over_vchg = 0; +#endif + cancel_delayed_work(&ip->int_work); + enable_irq(ip->chg_int); + } + break; + default: + fault_bit = tps_set_charger_ctrl(CHECK_INT2); + BATT_LOG("Read TPS65200 INT2 register value: %x", fault_bit); + if (fault_bit) { + fault_bit = tps_set_charger_ctrl(CHECK_INT2); + BATT_LOG("Read TPS65200 INT2 register value: %x" + , fault_bit); + fault_bit = tps_set_charger_ctrl(CHECK_INT2); + BATT_LOG("Read TPS65200 INT2 register value: %x" + , fault_bit); + fault_bit = tps_set_charger_ctrl(CHECK_CONTROL); +#ifdef CONFIG_HTC_BATTCHG_SMEM + smem_batt_info->reserve4 = 1; +#endif + cancel_delayed_work(&ip->int_work); + enable_irq(ip->chg_int); + } else { + fault_bit = tps_set_charger_ctrl(CHECK_INT1); + BATT_LOG("Read TPS65200 INT1 register value: %x" + , fault_bit); + if (fault_bit) { + ip->tps65200_reg = CHECK_INT1; + schedule_delayed_work(&ip->int_work, + msecs_to_jiffies(200)); + } + } + break; + } +} + static int htc_battery_core_probe(struct platform_device *pdev) { int i, rc; - + pr_info("%s\n", __func__); /* init battery gpio */ if (htc_batt_info.charger == LINEAR_CHARGER) { if ((rc = init_batt_gpio()) < 0) { @@ -1397,12 +1527,14 @@ static int htc_battery_core_probe(struct platform_device *pdev) */ htc_batt_info.present = 1; - /* init rpc */ - endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); - if (IS_ERR(endpoint)) { - BATT_ERR("%s: init rpc failed! rc = %ld", - __func__, PTR_ERR(endpoint)); - return -EINVAL; + if(!htc_batt_info.force_no_rpc) { + /* init rpc */ + endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); + if (IS_ERR(endpoint)) { + BATT_ERR("%s: init rpc failed! rc = %ld", + __func__, PTR_ERR(endpoint)); + return -EINVAL; + } } /* init power supplier framework */ @@ -1419,16 +1551,21 @@ static int htc_battery_core_probe(struct platform_device *pdev) * the battery status in case of we lost some info */ htc_battery_initial = 1; + + if(htc_batt_info.force_no_rpc) { + update_batt_info(); + } + else { + mutex_lock(&htc_batt_info.rpc_lock); + htc_batt_info.rep.charging_source = CHARGER_BATTERY; + if (htc_get_batt_info(&htc_batt_info.rep) < 0) + BATT_ERR("%s: get info failed", __func__); - mutex_lock(&htc_batt_info.rpc_lock); - htc_batt_info.rep.charging_source = CHARGER_BATTERY; - if (htc_get_batt_info(&htc_batt_info.rep) < 0) - BATT_ERR("%s: get info failed", __func__); - - if (htc_rpc_set_delta(1) < 0) - BATT_ERR("%s: set delta failed", __func__); - htc_batt_info.update_time = jiffies; - mutex_unlock(&htc_batt_info.rpc_lock); + if (htc_rpc_set_delta(1) < 0) + BATT_ERR("%s: set delta failed", __func__); + htc_batt_info.update_time = jiffies; + mutex_unlock(&htc_batt_info.rpc_lock); + } return 0; } @@ -1526,7 +1663,6 @@ static int ds2784_notifier_func(struct notifier_block *nfb, unsigned long action, void *param) { u8 arg = 0; - if (param) arg = *(u8 *)param; @@ -1560,12 +1696,14 @@ static struct notifier_block ds2784_notifier = { static int htc_battery_probe(struct platform_device *pdev) { + int rc = 0; struct htc_battery_platform_data *pdata = pdev->dev.platform_data; htc_batt_info.device_id = pdev->id; htc_batt_info.gpio_usb_id = pdata->gpio_usb_id; htc_batt_info.guage_driver = pdata->guage_driver; htc_batt_info.m2a_cable_detect = pdata->m2a_cable_detect; + htc_batt_info.force_no_rpc = pdata->force_no_rpc; htc_batt_info.func_show_batt_attr = pdata->func_show_batt_attr; htc_batt_info.charger = pdata->charger; htc_batt_info.rep.full_level = 100; @@ -1582,11 +1720,35 @@ static int htc_battery_probe(struct platform_device *pdev) #ifdef CONFIG_BATTERY_DS2784 if (pdata->guage_driver == GUAGE_DS2784) ds2784_register_notifier(&ds2784_notifier); -#elif CONFIG_BATTERY_DS2746 +#elif defined(CONFIG_BATTERY_DS2746) if (pdata->guage_driver == GUAGE_DS2746) ds2746_register_notifier(&ds2784_notifier); #endif + if (system_rev >= 1 || machine_is_htcleo()) { + if (pdata->int_data.chg_int) { + BATT_LOG("init over voltage interrupt detection."); + INIT_DELAYED_WORK(&pdata->int_data.int_work, + htc_battery_tps65200_int_func); + + rc = request_irq(pdata->int_data.chg_int, + tps65200_int_detection, + IRQF_TRIGGER_LOW, + "over_voltage_interrupt", + &pdata->int_data); + + if (rc) { + BATT_LOG("request irq failed"); + return rc; + } + } + } +#if defined(CONFIG_MACH_HTCLEO) + if(pdata->force_no_rpc) { + htc_battery_core_probe(pdev); + htc_cable_status_update(get_vbus_state()); + } +#endif return 0; } diff --git a/arch/arm/mach-msm/include/mach/htc_battery.h b/arch/arm/mach-msm/include/mach/htc_battery.h index 9b1b4d0e..d4e758dc 100644 --- a/arch/arm/mach-msm/include/mach/htc_battery.h +++ b/arch/arm/mach-msm/include/mach/htc_battery.h @@ -22,7 +22,10 @@ #define SET_ICL500 0X65 #define SET_ICL100 0X66 #define CHECK_INT2 0X67 - +#define OVERTEMP_VREG_4060 0XC8 +#define NORMALTEMP_VREG_4200 0XC9 +#define CHECK_INT1 0XCA +#define CHECK_CONTROL 0xCB /* information about the system we're running on */ extern unsigned int system_rev; @@ -67,6 +70,13 @@ struct battery_info_reply { u32 over_vchg; /* 0:normal, 1:over voltage charger */ s32 eval_current; /* System loading current from ADC */ }; + +struct htc_battery_tps65200_int { + int chg_int; + int tps65200_reg; + struct delayed_work int_work; +}; + struct htc_battery_platform_data { int (*func_show_batt_attr)(struct device_attribute *attr, char *buf); @@ -77,6 +87,8 @@ struct htc_battery_platform_data { int guage_driver; int m2a_cable_detect; int charger; + struct htc_battery_tps65200_int int_data; + int force_no_rpc; }; #if CONFIG_HTC_BATTCHG diff --git a/drivers/power/ds2746_battery.c b/drivers/power/ds2746_battery.c new file mode 100644 index 00000000..56fbcc66 --- /dev/null +++ b/drivers/power/ds2746_battery.c @@ -0,0 +1,1248 @@ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (c) 2010 High Tech Computer Corporation + +Module Name: + + ds2746_battery.c + +Abstract: + + This module implements the power algorithm, including below concepts: + 1. Charging function control. + 2. Charging full condition. + 3. Recharge control. + 4. Battery capacity maintainance. + 5. Battery full capacity calibration. + +Original Auther: + + Andy.YS Wang June-01-2010 +---------------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include +#include +#include +#include +#include "../../arch/arm/mach-msm/proc_comm.h" +#include /* for i2c_adapter, i2c_client define*/ +/*#include "../w1/w1.h"*/ +/*#include "../w1/slaves/w1_ds2784.h"*/ +#include +#include + +struct ds2746_device_info { + + struct device *dev; + struct device *w1_dev; + struct workqueue_struct *monitor_wqueue; + struct work_struct monitor_work; + /* lock to protect the battery info */ + struct mutex lock; + /* DS2784 data, valid after calling ds2784_battery_read_status() */ + unsigned long update_time; /* jiffies when data read */ + struct alarm alarm; + struct wake_lock work_wake_lock; + u8 slow_poll; + ktime_t last_poll; +}; +static struct wake_lock vbus_wake_lock; + +/*======================================================================================== + +HTC power algorithm helper member and functions + +========================================================================================*/ + +static struct poweralg_type poweralg = {0}; +static struct poweralg_config_type config = {0}; +static struct poweralg_config_type debug_config = {0}; + +#define FAST_POLL (1 * 60) +#define SLOW_POLL (10 * 60) +#define PREDIC_POLL 20 + +#define SOURCE_NONE 0 +#define SOURCE_USB 1 +#define SOURCE_AC 2 + +#define CHARGE_OFF 0 +#define CHARGE_SLOW 1 +#define CHARGE_FAST 2 + +#define BATTERY_ID_UNKNOWN 0 +#define HTC_BATTERY_DS2746_DEBUG_ENABLE 0 + +/* DS2746 I2C BUS*/ +#define DS2746_I2C_BUS_ID 0 +#define DS2746_I2C_SLAVE_ADDR 0x26 + +/*======================================================================================== + +IC dependent defines + +========================================================================================*/ + +/* DS2746 I2C register address*/ +#define DS2746_STATUS_REG 0x01 +#define DS2746_AUX0_MSB 0x08 +#define DS2746_AUX0_LSB 0x09 +#define DS2746_AUX1_MSB 0x0A +#define DS2746_AUX1_LSB 0x0B +#define DS2746_VOLT_MSB 0x0C +#define DS2746_VOLT_LSB 0x0D +#define DS2746_CURRENT_MSB 0x0E +#define DS2746_CURRENT_LSB 0x0F +#define DS2746_ACR_MSB 0x10 +#define DS2746_ACR_LSB 0x11 + +/* DS2746 I2C I/O*/ +static struct i2c_adapter *i2c2 = NULL; +static struct i2c_client *ds2746_i2c = NULL; +static int htc_battery_initial = 0; + +int ds2746_i2c_write_u8(u8 value, u8 reg) +{ + int ret; + u8 buf[2]; + struct i2c_msg *msg; + struct i2c_msg xfer_msg[1]; + + /* [MSG1] fill the register address data and fill the data Tx buffer */ + msg = &xfer_msg[0]; + msg->addr = ds2746_i2c->addr; + msg->len = 2; + msg->flags = 0; /* Read the register value */ + msg->buf = buf; + + buf[0] = reg; + buf[1] = value; + + ret = i2c_transfer(ds2746_i2c->adapter, xfer_msg, 1); + if (ret <= 0){ + printk(DRIVER_ZONE "[%s] fail.\n", __func__); + } + +#if HTC_BATTERY_DS2746_DEBUG_ENABLE + printk(DRIVER_ZONE "[%s] ds2746[0x%x]<-0x%x.\n", __func__, reg, value); +#endif + + return ret; +} + +int ds2746_i2c_read_u8(u8 *value, u8 reg) +{ + int ret; + struct i2c_msg *msg; + struct i2c_msg xfer_msg[2]; + + /* [MSG1] fill the register address data */ + msg = &xfer_msg[0]; + msg->addr = ds2746_i2c->addr; + msg->len = 1; + msg->flags = 0; /* Read the register value */ + msg->buf = ® + /* [MSG2] fill the data rx buffer */ + msg = &xfer_msg[1]; + msg->addr = ds2746_i2c->addr; + msg->len = 1; + msg->flags = I2C_M_RD; /* Read the register value */ + msg->buf = value; + + ret = i2c_transfer(ds2746_i2c->adapter, xfer_msg, 2); + if (ret <= 0){ + printk(DRIVER_ZONE "[%s] fail.\n", __func__); + } + +#if HTC_BATTERY_DS2746_DEBUG_ENABLE + printk(DRIVER_ZONE "[%s] ds2746[0x%x]=0x%x.\n", __func__, reg, *value); +#endif + + return ret; +} + +static void ds2746_i2c_exit(void) +{ + if (ds2746_i2c != NULL){ + kfree(ds2746_i2c); + ds2746_i2c = NULL; + } + + if (i2c2 != NULL){ + i2c_put_adapter(i2c2); + i2c2 = NULL; + } +} + +static int ds2746_i2c_init(void) +{ + i2c2 = i2c_get_adapter(DS2746_I2C_BUS_ID); + ds2746_i2c = kzalloc(sizeof(*ds2746_i2c), GFP_KERNEL); + + if (i2c2 == NULL || ds2746_i2c == NULL){ + printk(DRIVER_ZONE "[%s] fail (0x%x, 0x%x).\n", + __func__, + (int) i2c2, + (int) ds2746_i2c); + ds2746_i2c_exit(); + return -ENOMEM; + } + + ds2746_i2c->adapter = i2c2; + ds2746_i2c->addr = DS2746_I2C_SLAVE_ADDR; + + return 0; +} + +/*======================================================================================== + + HTC supporting MFG testing member and functions + +=========================================================================================*/ + +static BOOL b_is_charge_off_by_bounding = FALSE; +static void bounding_fullly_charged_level(int upperbd) +{ + static int pingpong = 1; + int lowerbd; + int current_level; + b_is_charge_off_by_bounding = FALSE; + if (upperbd <= 0) + return; /* doesn't activated this function */ + lowerbd = upperbd - 5; /* 5% range */ + + if (lowerbd < 0) + lowerbd = 0; + current_level = CEILING(poweralg.capacity_01p, 10); + + if (pingpong == 1 && upperbd <= current_level) { + printk(DRIVER_ZONE "MFG: lowerbd=%d, upperbd=%d, current=%d, pingpong:1->0 turn off\n", lowerbd, upperbd, current_level); + b_is_charge_off_by_bounding = TRUE; + pingpong = 0; + } else if (pingpong == 0 && lowerbd < current_level) { + printk(DRIVER_ZONE "MFG: lowerbd=%d, upperbd=%d, current=%d, toward 0, turn off\n", lowerbd, upperbd, current_level); + b_is_charge_off_by_bounding = TRUE; + } else if (pingpong == 0 && current_level <= lowerbd) { + printk(DRIVER_ZONE "MFG: lowerbd=%d, upperbd=%d, current=%d, pingpong:0->1 turn on\n", lowerbd, upperbd, current_level); + pingpong = 1; + } else { + printk(DRIVER_ZONE "MFG: lowerbd=%d, upperbd=%d, current=%d, toward %d, turn on\n", lowerbd, upperbd, current_level, pingpong); + } + +} + +static BOOL is_charge_off_by_bounding_condition(void) +{ + return b_is_charge_off_by_bounding; +} + +void calibrate_id_ohm(struct battery_type *battery) +{ + if (!poweralg.charging_source || !poweralg.charging_enable){ + battery->id_ohm += 500; /* If device is in discharge mode, Rid=Rid_1 + 0.5Kohm*/ + } + else if (poweralg.charging_source == 2 && battery->current_mA >= 400 && battery->id_ohm >= 1500){ + battery->id_ohm -= 1500; /* If device is in charge mode and ISET=1 (charge current is <800mA), Rid=Rid_1 - 1.5Kohm*/ + } + else if (battery->id_ohm >= 700){ + battery->id_ohm -= 700; /* If device is in charge mode and ISET=0 (charge current is <400mA), Rid=Rid_1 - 0.7Kohm*/ + } +} + +static BOOL is_charging_avaiable(void) +{ + if (poweralg.is_software_charger_timeout) return FALSE; + if (!poweralg.protect_flags.is_charging_enable_available)return FALSE; + if (!poweralg.is_cable_in) return FALSE; + if (poweralg.charge_state == CHARGE_STATE_PENDING) return FALSE; + if (poweralg.charge_state == CHARGE_STATE_FULL_PENDING) return FALSE; + if (poweralg.charge_state == CHARGE_STATE_PREDICTION) return FALSE; + if (is_charge_off_by_bounding_condition()) return FALSE; + return TRUE; /* CHARGE_STATE_UNKNOWN, SET_LED_BATTERY_CHARGING is available to be charged by default*/ +} + +static BOOL is_high_current_charging_avaialable(void) +{ + if (!poweralg.protect_flags.is_charging_high_current_avaialble) return FALSE; + if (!poweralg.is_china_ac_in) return FALSE; + if (poweralg.charge_state == CHARGE_STATE_UNKNOWN) return FALSE; + return TRUE; +} + +static void update_next_charge_state(void) +{ + static UINT32 count_charging_full_condition; + static UINT32 count_charge_over_load; + int next_charge_state; + int i; + + /* unknown -> prediction -> unknown -> discharge/charging/pending + charging -> full-wait-stable -> full-charging -> full-pending + full-pending -> full-charging -> charging + *(cable in group) -> discharge, charge-pending, dead + *(cable out group), full-wait-stable, charge-pending, dead -> charging*/ + + for (i = 0; i < 25; i++) /* maximun 25 times state transition to prevent from busy loop; ideally the transition time shall be less than 5 times.*/ + { + next_charge_state = poweralg.charge_state; + + /* 0. enter prediction state or not*/ + if (poweralg.charge_state == CHARGE_STATE_UNKNOWN){ + if (poweralg.battery.is_power_on_reset || config.debug_always_predict){ + if (poweralg.protect_flags.is_battery_dead){ + /* keep poweralg.charge_state unchanged, set capacity to 0% directly*/ + printk(DRIVER_ZONE " dead battery, \ + p=0%%\n"); + poweralg.capacity_01p = 0; + battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); + + poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; + poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; + } + else{ + /* battery replaced, recalculate capacity based on battery voltage*/ + printk(DRIVER_ZONE " start predict discharge...\n"); + next_charge_state = CHARGE_STATE_PREDICTION; + } + + config.debug_always_predict = FALSE; + } + } + + if (next_charge_state == poweralg.charge_state){ + /*---------------------------------------------------------------------------------------------------*/ + /* 1. cable in group*/ + if (poweralg.charge_state == CHARGE_STATE_UNKNOWN || + poweralg.charge_state == CHARGE_STATE_CHARGING || + poweralg.charge_state == CHARGE_STATE_PENDING || + poweralg.charge_state == CHARGE_STATE_FULL_WAIT_STABLE || + poweralg.charge_state == CHARGE_STATE_FULL_CHARGING || + poweralg.charge_state == CHARGE_STATE_FULL_PENDING){ + if (!poweralg.is_cable_in){ + next_charge_state = CHARGE_STATE_DISCHARGE; + } + else if (!poweralg.protect_flags.is_charging_enable_available){ + next_charge_state = CHARGE_STATE_PENDING; + } + } + + /*---------------------------------------------------------------------------------------------------*/ + /* 2. cable out group*/ + if (poweralg.charge_state == CHARGE_STATE_UNKNOWN || + poweralg.charge_state == CHARGE_STATE_DISCHARGE){ + if (poweralg.is_cable_in){ + next_charge_state = CHARGE_STATE_CHARGING; + } + } + } + + /*---------------------------------------------------------------------------------------------------*/ + /* 3. state handler/transition, if the charge state is not changed due to cable/protect flags*/ + if (next_charge_state == poweralg.charge_state){ + switch (poweralg.charge_state){ + case CHARGE_STATE_PREDICTION: + { + UINT32 end_time_ms = BAHW_MyGetMSecs(); + + if (end_time_ms - poweralg.state_start_time_ms >= + config.predict_timeout_sec * 1000){ + + printk(DRIVER_ZONE "predict done [%d->%d]\n", poweralg.state_start_time_ms, + end_time_ms); + next_charge_state = CHARGE_STATE_UNKNOWN; + } + } + break; + case CHARGE_STATE_CHARGING: + if (!poweralg.battery.is_power_on_reset){ + /* -> full-charging, pending, dead*/ + if (poweralg.capacity_01p > 990){ + /* only ever charge-full, the capacity can be larger than 99.0%*/ + next_charge_state = CHARGE_STATE_FULL_CHARGING; + } + else if (poweralg.battery.voltage_mV >= config.full_charging_mv && + poweralg.battery.current_mA >= 0 && + poweralg.battery.current_mA <= config.full_charging_ma){ + /* meet charge full terminate condition, check again*/ + next_charge_state = CHARGE_STATE_FULL_WAIT_STABLE; + } + } + + if (poweralg.battery.current_mA <= 0){ + /* count_charge_over_load is 5 as max*/ + if (count_charge_over_load < 5) + count_charge_over_load++; + else + poweralg.is_charge_over_load = TRUE; + } + else{ + count_charge_over_load = 0; + poweralg.is_charge_over_load = FALSE; + } + + /* is_software_charger_timeout: only triggered when AC adapter in*/ + if (config.software_charger_timeout_sec && poweralg.is_china_ac_in){ + /* software charger timer is enabled; for AC charge only*/ + UINT32 end_time_ms = BAHW_MyGetMSecs(); + + if (end_time_ms - poweralg.state_start_time_ms >= + config.software_charger_timeout_sec * 1000){ + + printk(DRIVER_ZONE "software charger timer timeout [%d->%d]\n", + poweralg.state_start_time_ms, + end_time_ms); + poweralg.is_software_charger_timeout = TRUE; + } + } + break; + case CHARGE_STATE_FULL_WAIT_STABLE: + { + /* -> full-charging, pending, dead*/ + if (poweralg.battery.voltage_mV >= config.full_charging_mv && + poweralg.battery.current_mA >= 0 && + poweralg.battery.current_mA <= config.full_charging_ma){ + + count_charging_full_condition++; + } + else{ + count_charging_full_condition = 0; + next_charge_state = CHARGE_STATE_CHARGING; + } + + if (count_charging_full_condition >= 3){ + + poweralg.capacity_01p = 1000; + battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); + + next_charge_state = CHARGE_STATE_FULL_CHARGING; + } + } + break; + case CHARGE_STATE_FULL_CHARGING: + { + /* -> full-pending, charging*/ + UINT32 end_time_ms = BAHW_MyGetMSecs(); + + if (poweralg.battery.voltage_mV < config.voltage_exit_full_mv){ + if (poweralg.capacity_01p > 990) + poweralg.capacity_01p = 990; + next_charge_state = CHARGE_STATE_CHARGING; + } + else if (config.full_pending_ma != 0 && + poweralg.battery.current_mA >= 0 && + poweralg.battery.current_mA <= config.full_pending_ma){ + + printk(DRIVER_ZONE " charge-full pending(%dmA)(%d:%d)\n", + poweralg.battery.current_mA, + poweralg.state_start_time_ms, + end_time_ms); + + next_charge_state = CHARGE_STATE_FULL_PENDING; + } + else if (end_time_ms - poweralg.state_start_time_ms >= + config.full_charging_timeout_sec * 1000){ + + printk(DRIVER_ZONE " charge-full (expect:%dsec)(%d:%d)\n", + config.full_charging_timeout_sec, + poweralg.state_start_time_ms, + end_time_ms); + next_charge_state = CHARGE_STATE_FULL_PENDING; + } + } + break; + case CHARGE_STATE_FULL_PENDING: + if ((poweralg.battery.voltage_mV >= 0 && + poweralg.battery.voltage_mV < config.voltage_recharge_mv) || + (poweralg.battery.RARC_01p >= 0 && + poweralg.battery.RARC_01p <= config.capacity_recharge_p * 10)){ + /* -> full-charging*/ + next_charge_state = CHARGE_STATE_FULL_CHARGING; + } + break; + case CHARGE_STATE_PENDING: + case CHARGE_STATE_DISCHARGE: + { + UINT32 end_time_ms = BAHW_MyGetMSecs(); + + if (!poweralg.is_voltage_stable){ + if (end_time_ms - poweralg.state_start_time_ms >= + config.wait_votlage_statble_sec * 1000){ + + printk(DRIVER_ZONE " voltage stable\n"); + poweralg.is_voltage_stable = TRUE; + } + } + } + + if (poweralg.is_cable_in && + poweralg.protect_flags.is_charging_enable_available){ + /* -> charging*/ + next_charge_state = CHARGE_STATE_CHARGING; + } + break; + } + } + + /*---------------------------------------------------------------------------------------------------*/ + /* 4. state transition*/ + if (next_charge_state != poweralg.charge_state){ + /* state exit*/ + switch (poweralg.charge_state){ + case CHARGE_STATE_UNKNOWN: + poweralg.capacity_01p = poweralg.battery.RARC_01p; + if (poweralg.capacity_01p > 990) + poweralg.capacity_01p = 990; + if (poweralg.capacity_01p < 0) + poweralg.capacity_01p = 0; + + poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; + poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; + break; + case CHARGE_STATE_PREDICTION: + battery_param_update(&poweralg.battery, + &poweralg.protect_flags); + + poweralg.capacity_01p = poweralg.battery.KADC_01p; + if (poweralg.capacity_01p > 990) + poweralg.capacity_01p = 990; + if (poweralg.capacity_01p < 0) + poweralg.capacity_01p = 0; + battery_capacity_update(&poweralg.battery, + poweralg.capacity_01p); + + poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; + poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; + break; + } + + /* state init*/ + poweralg.state_start_time_ms = BAHW_MyGetMSecs(); + + switch (next_charge_state){ + case CHARGE_STATE_DISCHARGE: + case CHARGE_STATE_PENDING: + /*! star_lee 20100426 - always set ACR=FULL when discharge starts and ACR>FULL*/ + if (poweralg.battery.RARC_01p > 1000) + battery_capacity_update(&poweralg.battery, 1000); + + poweralg.is_need_calibrate_at_49p = TRUE; + poweralg.is_need_calibrate_at_14p = TRUE; + poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; + poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; + poweralg.is_voltage_stable = FALSE; + + break; + case CHARGE_STATE_CHARGING: + poweralg.is_software_charger_timeout = FALSE; /* reset software charger timer every time when charging re-starts*/ + poweralg.is_charge_over_load = FALSE; + count_charge_over_load = 0; + poweralg.battery.charge_full_real_mAh = poweralg.battery.charge_full_design_mAh; + battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); + break; + case CHARGE_STATE_FULL_WAIT_STABLE: + /* set to 0 first; the cournter will be add to 1 soon in CHARGE_STATE_FULL_WAIT_STABLE state handler*/ + count_charging_full_condition = 0; + break; + } + + printk(DRIVER_ZONE " state change(%d->%d), full count=%d, over load count=%d [%d]\n", + poweralg.charge_state, + next_charge_state, + count_charging_full_condition, + count_charge_over_load, + poweralg.state_start_time_ms); + + poweralg.charge_state = next_charge_state; + continue; + } + + break; + } +} + +static void __update_capacity(void) +{ + INT32 next_capacity_01p; + + pr_info("ds2746_batt:__update_capacity start\n"); + if (poweralg.charge_state == CHARGE_STATE_PREDICTION || + poweralg.charge_state == CHARGE_STATE_UNKNOWN){ + + /*! star_lee 20100429 - return 99%~25% when in prediction mode*/ + poweralg.capacity_01p = max(min(990, poweralg.battery.KADC_01p), 250); + printk(DRIVER_ZONE "fake percentage (%d) during prediction.\n", + poweralg.capacity_01p); + } + else if (poweralg.charge_state == CHARGE_STATE_FULL_CHARGING || + poweralg.charge_state == CHARGE_STATE_FULL_PENDING){ + + poweralg.capacity_01p = 1000; + } + else if (!is_charging_avaiable() && poweralg.is_voltage_stable){ + /* DISCHARGE ALG: capacity is based on KADC/RARC; only do this after cable in 3 minutes later*/ + if (poweralg.battery.KADC_01p <= 0){ + if (poweralg.capacity_01p > 0) + poweralg.capacity_01p -= 10; + if (poweralg.capacity_01p > 0){ + /* capacity is still not 0 when KADC is 0; record capacity for next boot time*/ + battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); + } + } + else{ + if ((config.enable_weight_percentage) && (poweralg.capacity_01p <150 || + poweralg.battery.RARC_01p> poweralg.battery.KADC_01p)){ + +#define Padc 200 +#define Pw 5 +/* 500= 1000) + next_capacity_01p = 1000; + if (next_capacity_01p < 0) + next_capacity_01p = 0; + + if (next_capacity_01p < poweralg.capacity_01p){ + poweralg.capacity_01p -= min(10, poweralg.capacity_01p-next_capacity_01p); + } + } + + if (config.enable_full_calibration){ + if (poweralg.is_need_calibrate_at_49p && + poweralg.capacity_01p <= 500 && + poweralg.fst_discharge_capacity_01p >= 600){ + + poweralg.battery.charge_full_real_mAh = (poweralg.fst_discharge_acr_mAh-poweralg.battery.charge_counter_mAh)*1000/ + (poweralg.fst_discharge_capacity_01p-poweralg.capacity_01p); + + battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); + + poweralg.is_need_calibrate_at_49p = FALSE; + poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; + poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; + + printk(DRIVER_ZONE " 1.full calibrate: full=%d\n", + poweralg.battery.charge_full_real_mAh); + } + else if (poweralg.is_need_calibrate_at_14p && + poweralg.capacity_01p <= 150 && + poweralg.fst_discharge_capacity_01p >= 250){ + poweralg.battery.charge_full_real_mAh = (poweralg.fst_discharge_acr_mAh-poweralg.battery.charge_counter_mAh)*1000/ + (poweralg.fst_discharge_capacity_01p - poweralg.capacity_01p); + + battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); + + poweralg.is_need_calibrate_at_14p = FALSE; + poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; + poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; + + printk(DRIVER_ZONE " 2.full calibrate: full=%d\n", + poweralg.battery.charge_full_real_mAh); + } + } + } + else{ + /* CHARGE ALG: capacity is always based on ACR + 1. plus 1% as max when charge, if the orignal capacity is <= 99%, the result is no more than 99% + 2. minus 1% as max when discharge, not less than 0%*/ + next_capacity_01p = poweralg.battery.RARC_01p; + + if (next_capacity_01p > 1000) + next_capacity_01p = 1000; + if (next_capacity_01p < 0) + next_capacity_01p = 0; + + if (next_capacity_01p > poweralg.capacity_01p){ + /* charge case*/ + next_capacity_01p = poweralg.capacity_01p + min(next_capacity_01p - poweralg.capacity_01p, 10); + if (poweralg.capacity_01p > 990) + poweralg.capacity_01p = next_capacity_01p; + else + poweralg.capacity_01p = min(next_capacity_01p, 990); + } + else if (next_capacity_01p < poweralg.capacity_01p){ + /* discharge case*/ + poweralg.capacity_01p -= min(poweralg.capacity_01p - next_capacity_01p, 10); + if (poweralg.capacity_01p < 0) + poweralg.capacity_01p = 0; + } + } +} + +/*======================================================================================== + +HTC power algorithm implemetation + +========================================================================================*/ + +int get_state_check_interval_min_sec(void) +{ + /*the minimal check interval of each states in seconds + reserve for change polling rate + UINT32 elapse_time_ms = BAHW_MyGetMSecs() - poweralg.state_start_time_ms; + switch (poweralg.charge_state) + { + case CHARGE_STATE_FULL_WAIT_STABLE: + //! star_lee 20100429 - takes 30 seconds(10 seconds*3 times) to confirm charge full condition + return 10; + case CHARGE_STATE_PREDICTION: + return min(config.predict_timeout_sec, max((INT32)(config.predict_timeout_sec - elapse_time_ms/1000), (INT32)1)); + default: + if ( BAHW_IsChargeSourceIn() ) return config.polling_time_in_charging_sec; + else return config.polling_time_in_discharging_sec; + } + */ + return 0; +} + +BOOL do_power_alg(BOOL is_event_triggered) +{ + /* is_event_triggered - TRUE: handle event only, do not update capacity; FALSE; always update capacity*/ + static BOOL s_bFirstEntry = TRUE; + static UINT32 s_pre_time_ms; + static INT32 s_level; + + UINT32 now_time_ms = BAHW_MyGetMSecs(); + + /*------------------------------------------------------ + 1 get battery data and update charge state*/ + if (!battery_param_update(&poweralg.battery, &poweralg.protect_flags)){ + printk(DRIVER_ZONE "battery_param_update fail, please retry next time.\n"); + return FALSE; + } + + update_next_charge_state(); + + /*----------------------------------------------------- + 2 calculate battery capacity (predict if necessary)*/ + if (s_bFirstEntry || now_time_ms - s_pre_time_ms > 10000 || !is_event_triggered){ + /* DO not update capacity when plug/unplug cable less than 10 seconds*/ + __update_capacity(); + + s_bFirstEntry = FALSE; + s_pre_time_ms = now_time_ms; + } + + if (config.debug_disable_shutdown){ + if (poweralg.capacity_01p <= 0){ + poweralg.capacity_01p = 1; + } + } + + s_level = CEILING(poweralg.capacity_01p, 10); + if (CEILING(poweralg.last_capacity_01p, 10) != s_level || + poweralg.battery.last_temp_01c != poweralg.battery.temp_01c) { + + poweralg.battery.last_temp_01c = poweralg.battery.temp_01c; + poweralg.last_capacity_01p = poweralg.capacity_01p; + ds2746_blocking_notify(DS2784_LEVEL_UPDATE, &s_level); + } + + bounding_fullly_charged_level(config.full_level); + + /*------------------------------------------------------ + 3 charging function change*/ + if (is_charging_avaiable()){ + if (is_high_current_charging_avaialable()){ + ds2746_charger_control(CHARGE_FAST); + } + else{ + ds2746_charger_control(CHARGE_SLOW); + } + } + else{ + ds2746_charger_control(CHARGE_OFF); + } + + if (config.debug_disable_hw_timer && poweralg.is_charge_over_load){ + ds2746_charger_control(CHARGE_OFF); + printk(DRIVER_ZONE "Toggle charger due to HW disable charger.\n"); + } + + /*------------------------------------------------------ + 4 debug messages and update os battery status*/ + + /*powerlog_to_file(&poweralg); + update_os_batt_status(&poweralg);*/ + + printk(DRIVER_ZONE "[%d] P=%d cable=%d%d flags=%d%d%d debug=%d%d%d%d fst_discharge=%d/%d [%u]\n", + poweralg.charge_state, + poweralg.capacity_01p, + poweralg.is_cable_in, + poweralg.is_china_ac_in, + poweralg.protect_flags.is_charging_enable_available, + poweralg.protect_flags.is_charging_high_current_avaialble, + poweralg.protect_flags.is_battery_dead, + config.debug_disable_shutdown, + config.debug_fake_room_temp, + config.debug_disable_hw_timer, + config.debug_always_predict, + poweralg.fst_discharge_capacity_01p, + poweralg.fst_discharge_acr_mAh, + BAHW_MyGetMSecs()); + + return TRUE; +} + +void power_alg_init(struct poweralg_config_type *debug_config) +{ + /*------------------------------------------------------------- + 1. setup default poweralg data*/ + poweralg.charge_state = CHARGE_STATE_UNKNOWN; + poweralg.capacity_01p = 990; + poweralg.last_capacity_01p = poweralg.capacity_01p; + poweralg.fst_discharge_capacity_01p = 0; + poweralg.fst_discharge_acr_mAh = 0; + poweralg.is_need_calibrate_at_49p = TRUE; + poweralg.is_need_calibrate_at_14p = TRUE; + poweralg.is_charge_over_load = FALSE; + poweralg.is_china_ac_in = FALSE; + poweralg.is_cable_in = FALSE; + poweralg.is_voltage_stable = FALSE; + poweralg.is_software_charger_timeout = FALSE; + poweralg.state_start_time_ms = 0; + + if(get_cable_status() == SOURCE_USB) + { + poweralg.is_cable_in = TRUE; + poweralg.charging_source = SOURCE_USB; + ds2746_charger_control(CHARGE_SLOW); + } + else if (get_cable_status() == SOURCE_AC) + { + poweralg.is_cable_in = TRUE; + poweralg.is_china_ac_in = TRUE; + poweralg.charging_source = SOURCE_AC; + ds2746_charger_control(CHARGE_FAST); + } else{ + poweralg.charging_source = SOURCE_NONE; + } + /*------------------------------------------------------------- + 2. setup default config flags (board dependent)*/ + poweralg_config_init(&config); + + if (debug_config){ + config.debug_disable_shutdown = debug_config->debug_disable_shutdown; + config.debug_fake_room_temp = debug_config->debug_fake_room_temp; + config.debug_disable_hw_timer = debug_config->debug_disable_hw_timer; + config.debug_always_predict = debug_config->debug_always_predict; + } + + /* if ( BAHW_IsTestMode() ) + { + config.debug_disable_shutdown = TRUE; + config.debug_fake_room_temp = TRUE; + config.debug_disable_hw_timer = TRUE; + }*/ + + /*------------------------------------------------------------- + 3. setup default protect flags*/ + poweralg.protect_flags.is_charging_enable_available = TRUE; + poweralg.protect_flags.is_battery_dead = FALSE; + poweralg.protect_flags.is_charging_high_current_avaialble = FALSE; + poweralg.protect_flags.is_fake_room_temp = config.debug_fake_room_temp; + + /*------------------------------------------------------------- + 4. setup default battery structure*/ + battery_param_init(&poweralg.battery); + + /*pr_info("power alg inited with board name <%s>\n", HTC_BATT_BOARD_NAME);*/ +} + +void power_alg_preinit(void) +{ + /* make sure cable and battery is in when off mode charging*/ +} + +static BLOCKING_NOTIFIER_HEAD(ds2746_notifier_list); +int ds2746_register_notifier(struct notifier_block *nb) +{ + pr_info("%s\n", __func__); + return blocking_notifier_chain_register(&ds2746_notifier_list, nb); +} + +int ds2746_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&ds2746_notifier_list, nb); +} + + +int ds2746_blocking_notify(unsigned long val, void *v) +{ + int chg_ctl; + pr_info("%s\n", __func__); + + if (val == DS2784_CHARGING_CONTROL){ + chg_ctl = *(int *) v; + if (machine_is_passionc()){ + pr_info("[ds2746_batt] Switch charging %d\n", chg_ctl); + if (chg_ctl <= 2){ + gpio_direction_output(22, !(!!chg_ctl));/*PNC*/ + set_charger_ctrl(chg_ctl); + } + return 0; + } + else if (poweralg.battery.id_index != BATTERY_ID_UNKNOWN){ + /* only notify at changes */ + if (poweralg.charging_enable == chg_ctl) + return 0; + else + poweralg.charging_enable = chg_ctl; + } + else{ + /* poweralg.charging_enable = DISABLE; + v = DISABLE; + pr_info("[HTC_BATT] Unknow battery\n");*/ + if (poweralg.charging_enable == chg_ctl) + return 0; + else + poweralg.charging_enable = chg_ctl; + } + } + return blocking_notifier_call_chain(&ds2746_notifier_list, val, v); +} + + +int ds2746_get_battery_info(struct battery_info_reply *batt_info) +{ + batt_info->batt_id = poweralg.battery.id_index; /*Mbat ID*/ + batt_info->batt_vol = poweralg.battery.voltage_mV; /*VMbat*/ + batt_info->batt_temp = poweralg.battery.temp_01c; /*Temperature*/ + batt_info->batt_current = poweralg.battery.current_mA; /*Current*/ + batt_info->level = CEILING(poweralg.capacity_01p, 10); /*last_show%*/ + batt_info->charging_source = poweralg.charging_source; + batt_info->charging_enabled = poweralg.charging_enable; + batt_info->full_bat = poweralg.battery.charge_full_real_mAh; + return 0; +} +ssize_t htc_battery_show_attr(struct device_attribute *attr, char *buf) +{ + int len = 0; + pr_info("%s\n", __func__); + if (!strcmp(attr->attr.name, "batt_attr_text")){ + len += scnprintf(buf + + len, + PAGE_SIZE - + len, + "Percentage(%%): %d;\n" + "KADC(%%): %d;\n" + "RARC(%%): %d;\n" + "V_MBAT(mV): %d;\n" + "Main_battery_ID(Kohm): %d;\n" + "pd_M: %d;\n" + "Current(mA): %d;\n" + "Temp: %d;\n" + "Charging_source: %d;\n" + "ACR(mAh): %d;\n" + "FULL(mAh): %d;\n" + "1st_dis_percentage(%%): %d;\n" + "1st_dis_ACR: %d;\n", + CEILING(poweralg.capacity_01p, 10), + CEILING(poweralg.battery.KADC_01p, 10), + CEILING(poweralg.battery.RARC_01p, 10), + poweralg.battery.voltage_mV, + poweralg.battery.id_ohm, + poweralg.battery.pd_m, + poweralg.battery.current_mA, + CEILING(poweralg.battery.temp_01c, 10), + poweralg.charging_source, + poweralg.battery.charge_counter_mAh, + poweralg.battery.charge_full_real_mAh, + CEILING(poweralg.fst_discharge_capacity_01p, 10), + poweralg.fst_discharge_acr_mAh + ); + } + return len; +} + + +static int cable_status_handler_func(struct notifier_block *nfb, + unsigned long action, void *param) +{ + u32 cable_type = (u32) action; + pr_info("[ds2746_batt] cable change to %d\n", cable_type); + /* When the cable plug out, reset all the related flag, + Let algorithm machine to judge latest state */ + if (cable_type == 0){ + poweralg.is_cable_in = 0; + poweralg.is_china_ac_in = 0; + /*htc_batt_info.rep.OTP_Flag = 0; + htc_batt_info.rep.charging_sts_flag = 0; + htc_batt_info.full_charge_count = 0;*/ + } + else if (cable_type == 1){ + poweralg.is_cable_in = 1; + poweralg.is_china_ac_in = 0; + } + else if (cable_type == 2){ + poweralg.is_cable_in = 1; + poweralg.is_china_ac_in = 1; + } + else if (cable_type == 0xff){ + if (param) + config.full_level = *(INT32 *)param; + pr_info("[ds2746_batt] Set the full level to %d\n", config.full_level); + return NOTIFY_OK; + } + else if (cable_type == 0x10){ + poweralg.protect_flags.is_fake_room_temp = TRUE; + pr_info("[ds2746_batt] enable fake temp mode\n"); + return NOTIFY_OK; + } + + if (cable_type <= 2){ + poweralg.charging_source = cable_type; + ds2746_blocking_notify(DS2784_CHARGING_CONTROL, + &poweralg.charging_source); + } + return NOTIFY_OK; +} + +static struct notifier_block cable_status_handler = +{ + .notifier_call = cable_status_handler_func, +}; + +void ds2746_charger_control(int type) +{ + int chg_ctl = DISABLE; + int charge_type = type; + + switch (charge_type){ + case CHARGE_OFF: + /* CHARGER_EN is active low. Set to 1 to disable. */ + chg_ctl = DISABLE; + ds2746_blocking_notify(DS2784_CHARGING_CONTROL, &chg_ctl); + /*if (temp >= TEMP_CRITICAL) + pr_info("batt: charging OFF [OVERTEMP]\n"); + else if (htc_batt_info.rep.cooldown) + pr_info("batt: charging OFF [COOLDOWN]\n"); + else if (htc_batt_info.rep.battery_full) + pr_info("batt: charging OFF [FULL]\n"); + else*/ + pr_info("batt: charging OFF\n"); + break; + case CHARGE_SLOW: + chg_ctl = ENABLE_SLOW_CHG; + ds2746_blocking_notify(DS2784_CHARGING_CONTROL, &chg_ctl); + pr_info("batt: charging SLOW\n"); + break; + case CHARGE_FAST: + chg_ctl = ENABLE_FAST_CHG; + ds2746_blocking_notify(DS2784_CHARGING_CONTROL, &chg_ctl); + pr_info("batt: charging FAST\n"); + break; + } +} + +static void ds2746_program_alarm(struct ds2746_device_info *di, int seconds) +{ + ktime_t low_interval = ktime_set(seconds - 10, 0); + ktime_t slack = ktime_set(20, 0); + ktime_t next; + + next = ktime_add(di->last_poll, low_interval); + + alarm_start_range(&di->alarm, next, ktime_add(next, slack)); +} + +static void ds2746_battery_work(struct work_struct *work) +{ + if (!htc_battery_initial) return; + struct ds2746_device_info *di = container_of(work, + struct ds2746_device_info, monitor_work); + unsigned long flags; + + pr_info("[ds2746_batt] ds2746_battery_work*\n"); + do_power_alg(0); + get_state_check_interval_min_sec(); + di->last_poll = alarm_get_elapsed_realtime(); + + /* prevent suspend before starting the alarm */ + local_irq_save(flags); + + wake_unlock(&di->work_wake_lock); + if (poweralg.battery.is_power_on_reset) + ds2746_program_alarm(di, PREDIC_POLL); + else + ds2746_program_alarm(di, FAST_POLL); + + local_irq_restore(flags); +} + +static void ds2746_battery_alarm(struct alarm *alarm) +{ + if (!htc_battery_initial) return; + struct ds2746_device_info *di = container_of(alarm, struct ds2746_device_info, alarm); + wake_lock(&di->work_wake_lock); + queue_work(di->monitor_wqueue, &di->monitor_work); +} + +static int ds2746_battery_probe(struct platform_device *pdev) +{ + int rc; + struct ds2746_device_info *di; + struct ds2746_platform_data *pdata = pdev->dev.platform_data; + + pr_info("[ds2746_batt] ds2746_battery_prob\n"); + + poweralg.battery.thermal_id = pdata->func_get_thermal_id(); + + power_alg_preinit(); + power_alg_init(&debug_config); + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di){ + rc = -ENOMEM; + goto fail_register; + } + + di->update_time = jiffies; + platform_set_drvdata(pdev, di); + + di->dev = &pdev->dev; + + INIT_WORK(&di->monitor_work, ds2746_battery_work); + di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev)); + + /* init to something sane */ + di->last_poll = alarm_get_elapsed_realtime(); + + if (!di->monitor_wqueue){ + rc = -ESRCH; + goto fail_workqueue; + } + wake_lock_init(&di->work_wake_lock, WAKE_LOCK_SUSPEND, "ds2746-battery"); + alarm_init(&di->alarm, + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, + ds2746_battery_alarm); + wake_lock(&di->work_wake_lock); + queue_work(di->monitor_wqueue, &di->monitor_work); + htc_battery_initial = 1; + return 0; + + fail_workqueue : fail_register : kfree(di); + return rc; +} + + +static int ds2746_battery_remove(struct platform_device *pdev) +{ + struct ds2746_device_info *di = platform_get_drvdata(pdev); + + cancel_work_sync(&di->monitor_work); + destroy_workqueue(di->monitor_wqueue); + + return 0; +} + +/* FIXME: power down DQ master when not in use. */ +static int ds2746_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ds2746_device_info *di = platform_get_drvdata(pdev); + unsigned long flags; + pr_info("ds2746_batt:ds2746_suspend\n"); + /* If we are on battery, reduce our update rate until + * we next resume.*/ + if (poweralg.charging_source == SOURCE_NONE){ + local_irq_save(flags); + ds2746_program_alarm(di, SLOW_POLL); + di->slow_poll = 1; + local_irq_restore(flags); + } + /*gpio_direction_output(87, 0);*/ + return 0; +} +static void ds2746_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ds2746_device_info *di = platform_get_drvdata(pdev); + unsigned long flags; + + /* We might be on a slow sample cycle. If we're + * resuming we should resample the battery state + * if it's been over a minute since we last did + * so, and move back to sampling every minute until + * we suspend again.*/ + /*gpio_direction_output(87, 1);*/ + ndelay(100 * 1000); + pr_info("ds2746_batt:ds2746_resume\n"); + if (di->slow_poll){ + local_irq_save(flags); + ds2746_program_alarm(di, FAST_POLL); + di->slow_poll = 0; + local_irq_restore(flags); + } +} + +static struct dev_pm_ops ds2746_pm_ops = { + .prepare = ds2746_suspend, + .complete = ds2746_resume, +}; + +MODULE_ALIAS("platform:ds2746-battery"); +static struct platform_driver ds2746_battery_driver = +{ + .driver = { + .name = "ds2746-battery", + .pm = &ds2746_pm_ops, + }, + .probe = ds2746_battery_probe, + .remove = ds2746_battery_remove, +}; + +static int __init ds2746_battery_init(void) +{ + int ret; + + pr_info("[ds2746_batt]ds2746_battery_init"); + wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); + register_notifier_cable_status(&cable_status_handler); + + ret = ds2746_i2c_init(); + if (ret < 0){ + return ret; + } + + /*mutex_init(&htc_batt_info.lock);*/ + return platform_driver_register(&ds2746_battery_driver); +} + +static void __exit ds2746_battery_exit(void) +{ + ds2746_i2c_exit(); + platform_driver_unregister(&ds2746_battery_driver); +} + +module_init(ds2746_battery_init); +module_exit(ds2746_battery_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andy.YS Wang "); +MODULE_DESCRIPTION("ds2746 battery driver"); + diff --git a/drivers/power/ds2746_param.c b/drivers/power/ds2746_param.c new file mode 100644 index 00000000..db462c66 --- /dev/null +++ b/drivers/power/ds2746_param.c @@ -0,0 +1,852 @@ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (c) 2010 High Tech Computer Corporation + +Module Name: + + ds2784_param.c + +Abstract: + + This module implements the battery formula based on power spec, including below concepts: + 1. adc converter + 2. voltage mapping to capacity + 3. over temperature algorithm + 4. id range algorithm + 5. ACR maintainance + + Add from TPE PMA: + 1. temperature index + 2. pd_m_coef_boot + 3. preserved_capacity_by_temp + Remove from TAO PMA: + 1. pd_temp + + To adapt different PMA/projects, we need to modify below tables: + 1. ID_RANGE: which battery is used in the project? + 2. FL_25: the full capacity in temp 25C. + 3. pd_m_bias_mA: the discharge current threshold to calculating pd_m + 4. M_PARAMTER_TABLE: the voltage-capacity mapping table + 5. TEMP_RANGE: how many temp condition we need to consider + 6. PD_M_COEF_TABLE(BOOT)/PD_M_RESL_TABLE(BOOT): voltage compensation based on current + 7. PD_T_COEF: voltage compensation based on temp + 8. CAPACITY_DEDUCTION_01p: the capacity deduction due to low temperature + +Original Auther: + + Andy.ys Wang June-01-2010 + +---------------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include + +/*======================================================================================== + +build flags + +========================================================================================*/ + +#define HTC_ENABLE_POWER_DEBUG 0 +#define HTC_ENABLE_DUMMY_BATTERY 0 + +/*======================================================================================== + +battery common parameter defines (independent on battery id yet...) + +========================================================================================*/ + +#define BATTERY_VOLTAGE_MIN 2000 +#define BATTERY_VOLTAGE_MAX 20000 + +/*======================================================================================== + +battery parameter helper functions + +========================================================================================*/ + +static INT32 get_id_index(struct battery_type *battery) +{ + int i; + + for (i = 0; i < BATTERY_ID_NUM - 1; i++) { + /* minus 1, unknown battery is not in ID_RANGE + [min, max)*/ + UINT32 resister_min = ID_RANGE[i*2]; + UINT32 resister_max = ID_RANGE[i*2 + 1]; + + if (resister_min <= battery->id_ohm && resister_max > battery->id_ohm) { + return i + 1; + } + } + + return BATTERY_ID_UNKNOWN; +} + +static INT32 get_temp_index(struct battery_type *battery) +{ + int i; + + for (i = 0; i < 255; i++) { + /* the table size shall not be greater then 255, to ensure no infinite looping + [min, max)*/ + INT32 temp = TEMP_RANGE[i]; + if (battery->temp_01c >= temp) + return i; + } + + printk(DRIVER_ZONE " invalid batt_temp (%d) or temp range mapping.\n", battery->temp_01c); + return -1; +} + +/*======================================================================================== + +temperature formula definitions + +========================================================================================*/ + +static INT32 get_temp_01c(struct battery_type *battery) +{ + int current_index = battery->temp_check_index; + int search_direction = 0; + + if (battery->last_temp_adc > battery->temp_adc) { + search_direction = -1; + } else { + search_direction = 1; + } + + while (current_index >= 0 && current_index < TEMP_NUM-1) { + + UINT32 temp_min = TEMP_MAP[current_index]; + UINT32 temp_max = TEMP_MAP[current_index + 1]; + + if (temp_max > battery->temp_adc && temp_min <= battery->temp_adc) { + battery->temp_check_index = current_index; + battery->last_temp_adc = battery->temp_adc; + return (TEMP_MAX-current_index)*10; + } + current_index += search_direction; + } + + return (TEMP_MIN-1)*10; +} + +/*======================================================================================== + +over temperature protection + +========================================================================================*/ + +static BOOL is_over_temp(struct battery_type *battery) +{ + /* stop charging*/ + if (battery->temp_01c < over_low_temp_lock_01c || battery->temp_01c >= over_high_temp_lock_01c) { + return TRUE; + } + + return FALSE; +} + +static BOOL is_not_over_temp(struct battery_type *battery) +{ + /* start charging*/ + if (battery->temp_01c >= over_low_temp_release_01c && + battery->temp_01c < over_high_temp_release_01c) { + return TRUE; + } + + return FALSE; +} + +static void __protect_flags_update(struct battery_type *battery, + struct protect_flags_type *flags) +{ + /* Flags: + is_charging_enable_available - Over temperature, need to stop charging + is_charging_high_current_avaialble - Temperature is too high so that we have to slow charge*/ + + if (is_over_temp(battery)) { + /* Ex: T<0 or T>45 */ + flags->is_charging_enable_available = FALSE; + flags->is_charging_high_current_avaialble = FALSE; +#if 0 + flags->is_battery_overtemp = TRUE; +#endif + } else if (is_not_over_temp(battery)) { + /* Ex: T<42 or T>3*/ + flags->is_charging_enable_available = TRUE; + flags->is_charging_high_current_avaialble = TRUE; +#if 0 + flags->is_battery_overtemp = FALSE; +#endif + } + + /* Flags: + is_battery_dead - If battery is dead, show special indicator for it*/ + if (battery->voltage_mV < BATTERY_DEAD_VOLTAGE_LEVEL) { + flags->is_battery_dead = TRUE; + } + else if (battery->voltage_mV > BATTERY_DEAD_VOLTAGE_RELEASE) { + flags->is_battery_dead = FALSE; + } +} + +/*======================================================================================== + +Voltage-Percentage mapping + +========================================================================================*/ + +/*------------------------------------------------------------------------ + Example: + p0 = (4200, 10000); 4.2V for 100% + p1 = (3900, 8000); 3.9V for 80% + p2 = (3700, 2000); 3.7V for 20% + p3 = (3300, 0); 3.3V for 0% + + if V = 4000, (3900<4000<4200) + P = (4000-3900) * (10000-8000)/(4200-3900) + 8000 = 8666*/ + +#define NUM_SAMPLED_POINTS_MAX 12 + +struct sampled_point_type { + + DWORD voltage; + DWORD capacity; +}; + +struct voltage_curve_translator { + + DWORD voltage_min; + DWORD voltage_max; + DWORD capacity_min; + DWORD capacity_max; + int sampled_point_count; + struct sampled_point_type sampled_points[NUM_SAMPLED_POINTS_MAX]; +}; + +static void voltage_curve_translator_init(struct voltage_curve_translator *t) +{ + memset(t, 0, sizeof(*t)); +} + +static void voltage_curve_translator_add(struct voltage_curve_translator *t, DWORD voltage, DWORD capacity) +{ + struct sampled_point_type *pt; + + if (t->sampled_point_count >= NUM_SAMPLED_POINTS_MAX) { + return; + } + + t->sampled_points[t->sampled_point_count].voltage = voltage; + t->sampled_points[t->sampled_point_count].capacity = capacity; + pt = &t->sampled_points[t->sampled_point_count]; + + t->sampled_point_count++; + + if (pt->voltage > t->voltage_max) + t->voltage_max = pt->voltage; + if (pt->voltage < t->voltage_min) + t->voltage_min = pt->voltage; + if (pt->capacity > t->capacity_max) + t->capacity_max = pt->capacity; + if (pt->capacity < t->capacity_min) + t->capacity_min = pt->capacity; + +#if HTC_ENABLE_POWER_DEBUG + printk(DRIVER_ZONE " kadc t: capacity=%d voltage=%d\n", capacity, voltage); +#endif /* HTC_ENABLE_POWER_DEBUG*/ +} + +static INT32 voltage_curve_translator_get(struct voltage_curve_translator *t, DWORD voltage) +{ + struct sampled_point_type *p0, *p1; + INT32 capacity; + int i; + + if (voltage > t->voltage_max) + voltage = t->voltage_max; + if (voltage < t->voltage_min) + voltage = t->voltage_min; + + p0 = &t->sampled_points[0]; + p1 = p0 + 1; + for (i = 0; i < t->sampled_point_count - 1 && voltage < p1->voltage; i++) { + p0++; + p1++; + } + + /* DIV ZERO check*/ + if (p0->voltage - p1->voltage == 0) { + return 0; + } + + /* INT32 overflow check: mv(4200) * capacity(10000), shall be no problem at all...*/ + capacity = (voltage - p1->voltage) * (p0->capacity - p1->capacity) / (p0->voltage - p1->voltage) + p1->capacity; + if (capacity > t->capacity_max) { + capacity = t->capacity_max; + } + if (capacity < t->capacity_min) { + capacity = t->capacity_min; + } + return capacity; +} + +/*======================================================================================== + +KADC mapping functions + +========================================================================================*/ + +static struct voltage_curve_translator __get_kadc_t; + +static INT32 get_kadc_001p(struct battery_type *battery) +{ + INT32 pd_m = 0; + INT32 pd_temp = 0; + + INT32 temp_01c = battery->temp_01c; + INT32 current_mA = battery->current_mA; + + UINT32 *m_paramtable; + + INT32 pd_m_coef; + INT32 pd_m_resl; + + INT32 capacity_deduction_01p = CAPACITY_DEDUCTION_01p[battery->temp_index]; + INT32 capacity_predict_001p; + /* 1. INT32 overflow check: assert abs(iChgCurrent_ma) <= 3000, iBattTemp_01c>-250, pd_t_coef <= 1000 + when calculating pd_temp: 0x7FFFFFFF / (500 * 3000 * 1000) =:= 1.4*/ + + if (battery->current_mA > 3000) + current_mA = 3000; + else if (battery->current_mA < -3000) + current_mA = -3000; + if (battery->temp_01c <= -250) + temp_01c = -250; + + /* 2. calculate pd_m and pd_temp*/ + + if (battery->is_power_on_reset) { + pd_m_coef = PD_M_COEF_TABLE_BOOT[battery->temp_index][battery->id_index]; + pd_m_resl = PD_M_RESL_TABLE_BOOT[battery->temp_index][battery->id_index]; + } + else{ + pd_m_coef = PD_M_COEF_TABLE[battery->temp_index][battery->id_index]; + pd_m_resl = PD_M_RESL_TABLE[battery->temp_index][battery->id_index]; + } + + if (battery->current_mA < -pd_m_bias_mA) { + /* ex: -150mA < -130mA*/ + pd_m = (abs(battery->current_mA) - pd_m_bias_mA) * pd_m_coef /pd_m_resl; + } + + if (battery->temp_01c < 250) { + pd_temp = ((250 - battery->temp_01c) * (abs(battery->current_mA) * PD_T_COEF[battery->id_index])) / (10 * 10000); + } + battery->pd_m = pd_m; + + /* 3. calculate KADC using M_PARAMTER_TABLE*/ + + m_paramtable = M_PARAMTER_TABLE[battery->id_index]; + if (m_paramtable) { + int i = 0; /* assume that m_paramtable has at least 2 items...the last capacity item must be 0 to end the loop...*/ + + voltage_curve_translator_init(&__get_kadc_t); + + while (1) { + INT32 capacity = m_paramtable[i]; + INT32 voltage = m_paramtable[i + 1]; + if (capacity == 10000) { + /* full capacity, no need to fix voltage level*/ + voltage_curve_translator_add(&__get_kadc_t, voltage, capacity); + } + else { + voltage_curve_translator_add(&__get_kadc_t, voltage - pd_temp, capacity); + } + + if (capacity == 0) + break; + + i += 2; + } + +#if HTC_ENABLE_POWER_DEBUG + printk(DRIVER_ZONE " pd_m=%d, pd_temp=%d\n", pd_m, pd_temp); +#endif /* HTC_ENABLE_POWER_DEBUG*/ + + capacity_predict_001p = voltage_curve_translator_get(&__get_kadc_t, battery->voltage_mV + pd_m); + } + else{ + capacity_predict_001p = (battery->voltage_mV - 3400) * 10000 / (4200 - 3400); + } + + return (capacity_predict_001p - capacity_deduction_01p * 10) * 10000 / (10000 - capacity_deduction_01p * 10); +} + +/*======================================================================================== + +coulomb counter+curve tracer + +========================================================================================*/ + +static INT32 get_software_acr_revise(struct battery_type *battery, UINT32 ms) +{ + INT32 kadc_01p = battery->KADC_01p; + INT32 ccbi_01p = battery->RARC_01p; + INT32 delta_01p = kadc_01p - ccbi_01p; + + DWORD C = 5; /* KADC = 15%~100%*/ + if (kadc_01p <= 150) { + C = 5; + } /* KADC = 0%~15%*/ + + if (delta_01p < 0) { + /* if KADC is less than RARC, p shall be lower*/ + return -(INT32) (C * ms * delta_01p * delta_01p) / 1000; + } + else{ + /* if KADC is larger than RARC, p shall be higher*/ + return (INT32) (C * ms * delta_01p * delta_01p) / 1000; + } +} + +/*======================================================================================== + +ds2746 gauge ic functions, to access ds2746 registers and convert ADC to battery param + +========================================================================================*/ + +static void __ds2746_clear_porf(void) +{ + UINT8 reg_data; + if (!ds2746_i2c_read_u8(®_data, 0x01)) { + printk(DRIVER_ZONE " clear porf error in read.\n"); + return; + } + + if (!ds2746_i2c_write_u8((reg_data & (~DS2746_STATUS_PORF)), 0x01)) { + printk(DRIVER_ZONE " clear porf error in write.\n"); + return; + } +} + +static void __ds2746_acr_update(struct battery_type *battery, int capacity_01p) +{ + printk(DRIVER_ZONE " acr update: P=%d, C=%d.\n", + capacity_01p, + battery->charge_counter_adc); + + ds2746_i2c_write_u8((battery->charge_counter_adc & 0xFF00) >> 8, 0x10); + ds2746_i2c_write_u8((battery->charge_counter_adc & 0x00FF), 0x11); + + if (battery->is_power_on_reset) { + __ds2746_clear_porf(); + } +} + +static void __ds2746_init_config(struct battery_type *battery) +{ + UINT8 reg_data; + + if (!ds2746_i2c_read_u8(®_data, 0x01)) { + printk(DRIVER_ZONE " init config error in read.\n"); + return; + } + + /* Erase SMOD and NBEN value in DS2746 status/config register*/ + reg_data &= ~(DS2746_STATUS_SMOD | DS2746_STATUS_NBEN); + if (!ds2746_i2c_write_u8(reg_data, 0x01)) { + printk(DRIVER_ZONE " init config error in write.\n"); + return; + } +} + +static BOOL __ds2746_get_reg_data(UINT8 *reg) +{ + memset(reg, 0, 12); + + if (!ds2746_i2c_read_u8(®[0], 0x01)) + return FALSE; + if (!ds2746_i2c_read_u8(®[2], 0x08)) + return FALSE; + if (!ds2746_i2c_read_u8(®[3], 0x09)) + return FALSE; + if (!ds2746_i2c_read_u8(®[4], 0x0a)) + return FALSE; + if (!ds2746_i2c_read_u8(®[5], 0x0b)) + return FALSE; + if (!ds2746_i2c_read_u8(®[6], 0x0c)) + return FALSE; + if (!ds2746_i2c_read_u8(®[7], 0x0d)) + return FALSE; + if (!ds2746_i2c_read_u8(®[8], 0x0e)) + return FALSE; + if (!ds2746_i2c_read_u8(®[9], 0x0f)) + return FALSE; + if (!ds2746_i2c_read_u8(®[10], 0x10)) + return FALSE; + if (!ds2746_i2c_read_u8(®[11], 0x11)) + return FALSE; + + return TRUE; +} + +static BOOL __ds2746_battery_adc_udpate(struct battery_type *battery) +{ + UINT8 reg[12]; + + if (!__ds2746_get_reg_data((UINT8 *) ®)) { + printk(DRIVER_ZONE " get ds2746 data failed...\n"); + return FALSE; + } + + printk(DRIVER_ZONE " [x0]%x [x8]%x %x %x %x %x %x %x %x %x %x\n", + reg[0], + reg[2], + reg[3], + reg[4], + reg[5], + reg[6], + reg[7], + reg[8], + reg[9], + reg[10], + reg[11]); + + if (!(reg[0] & DS2746_STATUS_AIN0) || !(reg[0] & DS2746_STATUS_AIN1)) { + printk(DRIVER_ZONE " AIN not ready...\n"); + return FALSE; + } + + if (reg[0] & DS2746_STATUS_PORF) { + battery->is_power_on_reset = TRUE; + } + else{ + battery->is_power_on_reset = FALSE; + } + + /* adc register value*/ + battery->voltage_adc = MAKEWORD(reg[7], reg[6]) >> 4; + battery->current_adc = MAKEWORD(reg[9], reg[8]); + if (battery->current_adc & 0x8000) { + battery->current_adc = -(0x10000 - battery->current_adc); + } + battery->current_adc /= 4; + battery->charge_counter_adc = MAKEWORD(reg[11], reg[10]); + if (battery->charge_counter_adc & 0x8000) { + battery->charge_counter_adc = -(0x10000 - battery->charge_counter_adc); + } + battery->id_adc = MAKEWORD(reg[5], reg[4]) >> 4; + battery->temp_adc = MAKEWORD(reg[3], reg[2]) >> 4; + if (support_ds2746_gauge_ic) { + /* we preserve 500mAh for capacity lower than 0%, however the 500mAh is still drained out...we need to do predict for correct ACR*/ + if ((battery->charge_counter_adc & 0xFFFF) >= 0xF000){ + printk(DRIVER_ZONE " ACR out of range (x%x)...\n", + battery->charge_counter_adc); + battery->is_power_on_reset = TRUE; + } + } + + return TRUE; +} + +/*======================================================================================== + +softwar acr functions, to accumulate ACR by software and revise by battery parameter + +========================================================================================*/ + +static void __software_charge_counter_update(struct battery_type *battery, UINT32 ms) +{ + /* if the charge counter is maintained by sw, batt_alg shall use this routine to update charge counter and related parameters*/ + INT32 capacity_deduction_01p = CAPACITY_DEDUCTION_01p[battery->temp_index]; + /* AEL(mAh): A low temp unusable battery capacity, calculated in runtime*/ + INT32 ael_mAh = capacity_deduction_01p *battery->charge_full_real_mAh / 1000; +#if HTC_ENABLE_POWER_DEBUG + printk(DRIVER_ZONE "chgctr update: I=%d ms=%d.\n", battery->current_mA, ms); +#endif /* HTC_ENABLE_POWER_DEBUG*/ + + /* ACRt(mAh): The total capacity battery owns, stored in battery->charge_counter_mAh*/ + battery->software_charge_counter_mAms += (INT32) (battery->current_mA * ms); + battery->charge_counter_mAh += (battery->software_charge_counter_mAms / 3600000); + battery->software_charge_counter_mAms -= (battery->software_charge_counter_mAms / 3600000) * 3600000; + + /* CCBI(0.1%): A software RARC*/ + battery->RARC_01p = (battery->charge_counter_mAh - ael_mAh) * 1000 / (battery->charge_full_real_mAh - ael_mAh); + /* store back the battery->charge_counter_mAh to battery->charge_counter_adc*/ + battery->charge_counter_adc = (battery->charge_counter_mAh + charge_counter_zero_base_mAh) * acr_adc_to_mv_coef / acr_adc_to_mv_resl; +} + +static void __software_charge_counter_revise(struct battery_type *battery, UINT32 ms) +{ + if (battery->current_mA < 0) { +#if HTC_ENABLE_POWER_DEBUG + printk(DRIVER_ZONE "chgctr revise: delta=%d.\n", get_software_acr_revise(battery, ms)); +#endif /* HTC_ENABLE_POWER_DEBUG*/ + + /* revise software charge counter by coulomb counter+curve tracer*/ + battery->software_charge_counter_mAms += get_software_acr_revise(battery, ms); + battery->charge_counter_mAh += (battery->software_charge_counter_mAms / 3600000); + battery->software_charge_counter_mAms -= (battery->software_charge_counter_mAms / 3600000) * 3600000; + /* store back the battery->charge_counter_mAh to battery->charge_counter_adc*/ + battery->charge_counter_adc = (battery->charge_counter_mAh + charge_counter_zero_base_mAh) * acr_adc_to_mv_coef / acr_adc_to_mv_resl; + } +} + +static void __software_acr_update(struct battery_type *battery) +{ + static BOOL s_bFirstEntry = TRUE; + static DWORD last_time_ms; + DWORD now_time_ms = BAHW_MyGetMSecs(); + + if (s_bFirstEntry) { + s_bFirstEntry = FALSE; + last_time_ms = now_time_ms; + } + +#if HTC_ENABLE_POWER_DEBUG + printk(DRIVER_ZONE "+acr update: adc=%d C=%d mams=%d.\n", + battery->charge_counter_adc, + battery->charge_counter_mAh, + battery->software_charge_counter_mAms); +#endif /* HTC_ENABLE_POWER_DEBUG*/ + + __software_charge_counter_update(battery, now_time_ms - last_time_ms); + __software_charge_counter_revise(battery, now_time_ms - last_time_ms); + +#if HTC_ENABLE_POWER_DEBUG + printk(DRIVER_ZONE "-acr update: adc=%d C=%d mams=%d.\n", + battery->charge_counter_adc, + battery->charge_counter_mAh, + battery->software_charge_counter_mAms); +#endif /* HTC_ENABLE_POWER_DEBUG*/ + + last_time_ms = now_time_ms; +} + +/*======================================================================================== + +battery param update, the coef are referenced from power spec + +========================================================================================*/ + +static BOOL __battery_param_udpate(struct battery_type *battery) +{ + static int batt_id_stable_counter = 0; + INT32 batt_id_index; + INT32 temp_01c; + + if (support_ds2746_gauge_ic) { + /* adc register value are read from __ds2746_battery_adc_udpate()*/ + if (!__ds2746_battery_adc_udpate(battery)) + return FALSE; + } + else{ + /* adc register value are read from BAHW_get_batt_info_all() + if ( !BAHW_get_batt_info_all(battery) ) return FALSE;*/ + } + + /*real physical value*/ + battery->voltage_mV = (battery->voltage_adc * voltage_adc_to_mv_coef / voltage_adc_to_mv_resl); + battery->current_mA = (battery->current_adc * current_adc_to_mv_coef / current_adc_to_mv_resl); + battery->discharge_mA = (battery->discharge_adc * discharge_adc_to_mv_coef / discharge_adc_to_mv_resl); + battery->charge_counter_mAh = (battery->charge_counter_adc * acr_adc_to_mv_coef / acr_adc_to_mv_resl) - charge_counter_zero_base_mAh; + battery->current_mA = battery->current_mA - battery->discharge_mA; + /* prevent from adc out of range*/ + if (battery->id_adc >= id_adc_resl) { + battery->id_adc = id_adc_resl - 1; + } + if (battery->id_adc <= 0) { + battery->id_adc = 1; + } + if (battery->temp_adc >= temp_adc_resl) { + battery->temp_adc = temp_adc_resl - 1; + } + if (battery->temp_adc <= 0) { + battery->temp_adc = 1; + } + + /* battery ID shall be ready first for temp/kadc calculation*/ + // if ( id_conversion ) battery->id_ohm = ((float)id_R_kohm / ((float)id_adc_resl/battery->id_adc - 1)) * 1000; // kohm -> ohm + // else battery->id_ohm = battery->id_adc; + battery->id_ohm = battery->id_adc; + calibrate_id_ohm(battery); + + batt_id_index = get_id_index(battery); + + if (is_allow_batt_id_change) { + /*! TODO: batt_id changes immediately; may need to modify in future*/ + if (batt_id_stable_counter >= 3 && batt_id_index != battery->id_index){ + /* if batt_id is stable but is different from previous one*/ + batt_id_stable_counter = 0; /* reset stable counter and set batt_id to new one*/ + } + } + + if (batt_id_stable_counter < 3) { + if (batt_id_stable_counter == 0) { + /* first time to get the batt id*/ + battery->id_index = batt_id_index; + battery->charge_full_design_mAh = FL_25[battery->id_index]; + battery->charge_full_real_mAh = battery->charge_full_design_mAh; + batt_id_stable_counter = 1; + } + else{ + /* 2nd and further time to get the batt id*/ + if (batt_id_index == battery->id_index) + batt_id_stable_counter++; + else + batt_id_stable_counter = 0; + } + } + + /* calculate temperature*/ + // battery->temp_01c = get_temp_c((float)temp_R_kohm / ((float)temp_adc_resl/battery->temp_adc - 1))*10; + temp_01c = get_temp_01c(battery); + if (temp_01c >= TEMP_MIN*10) + battery->temp_01c = temp_01c; + else + printk(DRIVER_ZONE " get temp_01c(%d) failed...\n", temp_01c); + battery->temp_index = get_temp_index(battery); + + /* calculate KADC and RARC*/ + battery->KADC_01p = CEILING(get_kadc_001p(battery), 10); + battery->RARC_01p = CEILING(10000 * battery->charge_counter_mAh / battery->charge_full_real_mAh, 10); + if (!support_ds2746_gauge_ic) { + __software_acr_update(battery); + } + + if (battery->voltage_mV voltage_mV> BATTERY_VOLTAGE_MAX) { + printk(DRIVER_ZONE " invalid V(%d).\n", battery->voltage_mV); + return FALSE; + } + + /*! star_lee 20100426 - minimum RARC is 0%*/ + if (battery->RARC_01p <= 0) { + battery->RARC_01p = 0; + } + + printk(DRIVER_ZONE " V=%d(%x) I=%d(%x) C=%d.%d/%d(%x) id=%d(%x) T=%d(%x) KADC=%d\n", + battery->voltage_mV, + battery->voltage_adc, + battery->current_mA, + battery->current_adc, + battery->charge_counter_mAh, + battery->software_charge_counter_mAms, + battery->charge_full_real_mAh, + battery->charge_counter_adc, + battery->id_index, + battery->id_adc, + battery->temp_01c, + battery->temp_adc, + battery->KADC_01p); + + return TRUE; +} + +/*======================================================================================== + +time functions + +========================================================================================*/ + +DWORD BAHW_MyGetMSecs(void) +{ + struct timespec now; + getnstimeofday(&now); + /*struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_MONOTONIC, &t);*/ + return now.tv_sec * 1000 + now.tv_nsec / 1000000; +} + +/*======================================================================================== + +battery param public function + +========================================================================================*/ + +void battery_capacity_update(struct battery_type *battery, int capacity_01p) +{ + + /* ACR 500~500+FULL mapping to capacity 0~FULL*/ + battery->charge_counter_mAh = capacity_01p * battery->charge_full_real_mAh / 1000; + battery->charge_counter_adc = (battery->charge_counter_mAh + charge_counter_zero_base_mAh) * acr_adc_to_mv_resl / acr_adc_to_mv_coef; + battery->RARC_01p = capacity_01p; + if (support_ds2746_gauge_ic) { + __ds2746_acr_update(battery, capacity_01p); + } + + printk(DRIVER_ZONE "new RARC=%d C=%dmAh adc=%d.\n", + battery->RARC_01p, + battery->charge_counter_mAh, + battery->charge_counter_adc); + battery->is_power_on_reset = FALSE; +} + +BOOL battery_param_update(struct battery_type *battery, struct protect_flags_type *flags) +{ + if (!__battery_param_udpate(battery)) { + return FALSE; + } + + if (flags->is_fake_room_temp) { + battery->temp_01c = 250; + printk(DRIVER_ZONE "fake temp=%d(%x)\n", + battery->temp_01c, + battery->temp_adc); + } + __protect_flags_update(battery, flags); + +#if ! HTC_ENABLE_DUMMY_BATTERY + if (battery->id_index == BATTERY_ID_UNKNOWN) { + flags->is_charging_enable_available = FALSE; + } +#else /* HTC_ENABLE_DUMMY_BATTERY*/ + /* do not disable charging for debug stage*/ + flags->is_charging_enable_available = TRUE; +#endif /* HTC_ENABLE_DUMMY_BATTERY*/ + + return TRUE; +} + +void battery_param_init(struct battery_type *battery) +{ + /* set battery id to unknown to get battery id and related characters*/ + battery->id_index = BATTERY_ID_UNKNOWN; + + /* default to 25C unless we can get valid battery temp from adc*/ + battery->temp_01c = 250; + battery->last_temp_01c = battery->temp_01c; + battery->temp_check_index = 0; + battery->last_temp_adc = 0; + + battery->voltage_mV = 3800; + + /* this is used when accumulate current by software; initial it as 0mAs*/ + battery->software_charge_counter_mAms = 0; + + /* set POR at first by software; gauge ic will has correct value*/ + battery->is_power_on_reset = TRUE; + + if (support_ds2746_gauge_ic) { + __ds2746_init_config(battery); + } + + if (battery->thermal_id == THERMAL_1000) { + TEMP_MAP = TEMP_MAP_1000K; + printk(DRIVER_ZONE "Use 1000 Kohm thermal resistance"); + } else { + printk(DRIVER_ZONE "Use default(300 Kohm) thermal resistance"); + } + + /*printk(DRIVER_ZONE "battery param inited with board name <%s>\n", HTC_BATT_BOARD_NAME);*/ +} + diff --git a/include/linux/ds2746_battery.h b/include/linux/ds2746_battery.h new file mode 100644 index 00000000..84353816 --- /dev/null +++ b/include/linux/ds2746_battery.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2007 HTC Incorporated + * + * 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. + */ +#ifndef _DS2746_BATTERY_H_ +#define _DS2746_BATTERY_H_ +#include +#include +#include +#include + +enum ds2784_notify_evt_t { + DS2784_CHARGING_CONTROL = 0, + DS2784_LEVEL_UPDATE, + DS2784_BATTERY_FAULT, + DS2784_OVER_TEMP, + DS2784_NUM_EVENTS, +}; + +/* battery charging state*/ + +enum { + CHARGE_STATE_UNKNOWN, /* before anything is ready, we are in this state. shall default low current charge and show charging LED*/ + CHARGE_STATE_PREDICTION, /* in normal case, we need to enter prediction for 10 seconds for 1st KADC*/ + CHARGE_STATE_DISCHARGE, /* cable out state*/ + CHARGE_STATE_CHARGING, /* charging state*/ + CHARGE_STATE_PENDING, /* charging state but no good*/ + CHARGE_STATE_FULL_WAIT_STABLE, /* charging state but going full*/ + CHARGE_STATE_FULL_CHARGING, /* charging full, keep charging*/ + CHARGE_STATE_FULL_PENDING, /* charging full, stop charging*/ +}; + +enum { + THERMAL_300, + THERMAL_1000, +}; + +/* power algorithm data structure and config data structure*/ + +struct poweralg_type +{ + int charge_state; + int capacity_01p; + int last_capacity_01p; + int fst_discharge_capacity_01p; + int fst_discharge_acr_mAh; + int charging_source; + int charging_enable; + BOOL is_need_calibrate_at_49p; + BOOL is_need_calibrate_at_14p; + BOOL is_charge_over_load; + struct battery_type battery; + struct protect_flags_type protect_flags; + BOOL is_china_ac_in; + BOOL is_cable_in; + BOOL is_voltage_stable; + BOOL is_software_charger_timeout; + UINT32 state_start_time_ms; +}; + +struct poweralg_config_type +{ + INT32 full_charging_mv; + INT32 full_charging_ma; + INT32 full_pending_ma; /* 0 to disable*/ + INT32 full_charging_timeout_sec; /* 0 to disable*/ + INT32 voltage_recharge_mv; /* 0 to disable*/ + INT32 capacity_recharge_p; /* 0 to disable*/ + INT32 voltage_exit_full_mv; /* 0 to disable*/ + INT32 wait_votlage_statble_sec; + INT32 predict_timeout_sec; + INT32 polling_time_in_charging_sec; + INT32 polling_time_in_discharging_sec; + + BOOL enable_full_calibration; + BOOL enable_weight_percentage; + INT32 software_charger_timeout_sec; /* 0 to disable*/ + + BOOL debug_disable_shutdown; + BOOL debug_fake_room_temp; + BOOL debug_disable_hw_timer; + BOOL debug_always_predict; + INT32 full_level; /* 0 to disable*/ +}; + +struct ds2746_platform_data { + int (*func_get_thermal_id)(void); +}; + +/* battery behavior constant*/ + +#define BATTERY_PERCENTAGE_UNKNOWN 0xFF +#define BATTERY_LOW_PERCENTAGE 10 /* in 1%*/ +#define BATTERY_CRITICAL_PERCENTAGE 5 /* in 1%*/ +#define BATTERY_EMPTY_PERCENTAGE 0 /* in 1%*/ + +/* battery algorithm public functions*/ + +int get_state_check_interval_min_sec( void); +BOOL do_power_alg( BOOL is_event_triggered); +void power_alg_init( struct poweralg_config_type *debug_config); +void power_alg_preinit( void); +int ds2746_blocking_notify( unsigned long val, void *v); +void ds2746_charger_control( int type); +int ds2746_i2c_write_u8( u8 value, u8 reg); +int ds2746_i2c_read_u8( u8* value, u8 reg); +void calibrate_id_ohm( struct battery_type *battery); +/* external function implemented by upper layer*/ + +/*extern void powerlog_to_file(struct poweralg_type* poweralg);*/ +/*extern void update_os_batt_status(struct poweralg_type* poweralg);*/ + +#ifdef CONFIG_BATTERY_DS2746 +extern int ds2746_register_notifier( struct notifier_block *nb); +extern int ds2746_unregister_notifier( struct notifier_block *nb); +extern int ds2746_get_battery_info( struct battery_info_reply *batt_info); +extern ssize_t htc_battery_show_attr( struct device_attribute *attr, char *buf); +#else +static int ds2746_register_notifier( struct notifier_block *nb) { + + return 0; +} +static int ds2746_unregister_notifier( struct notifier_block *nb) { + + return 0; +} +static int ds2746_get_battery_info( struct battery_info_reply *batt_info) { + + batt_info->level = 10; + return 0; +} +extern ssize_t htc_battery_show_attr( struct device_attribute *attr, char *buf) { + + return 0; +} + +#endif + +#endif diff --git a/include/linux/ds2746_battery_config.h b/include/linux/ds2746_battery_config.h new file mode 100644 index 00000000..2195d450 --- /dev/null +++ b/include/linux/ds2746_battery_config.h @@ -0,0 +1,44 @@ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (c) 2010 High Tech Computer Corporation + +Module Name: + + batt_alg_config.c +Abstract: + + This module tells batt_alg.c module how to config power alg. + +Original Auther: + + Star Lee (star_lee) Apr-12-2010 + +---------------------------------------------------------------------------------*/ + +#define HTC_BATT_BOARD_NAME "ACE" + +static void poweralg_config_init(struct poweralg_config_type *config) +{ + config->full_charging_mv = 4110; + config->full_charging_ma = 50; + config->full_pending_ma = 0; /* disabled*/ + config->full_charging_timeout_sec = 60 * 60; + config->voltage_recharge_mv = 4150; + config->capacity_recharge_p = 0; /* disabled*/ + config->voltage_exit_full_mv = 4100; + config->wait_votlage_statble_sec = 1 * 60; + config->predict_timeout_sec = 10; + config->polling_time_in_charging_sec = 30; + config->polling_time_in_discharging_sec = 30; + + config->enable_full_calibration = TRUE; + config->enable_weight_percentage = TRUE; + config->software_charger_timeout_sec = 0; /* disabled*/ + + config->debug_disable_shutdown = FALSE; + config->debug_fake_room_temp = FALSE; + config->debug_disable_hw_timer = FALSE; + config->debug_always_predict = FALSE; + config->full_level = 0; +} + diff --git a/include/linux/ds2746_param.h b/include/linux/ds2746_param.h new file mode 100644 index 00000000..49733165 --- /dev/null +++ b/include/linux/ds2746_param.h @@ -0,0 +1,70 @@ +#ifndef __BATT_PARAM_H__ +#define __BATT_PARAM_H__ + +/* battery status and charging information*/ + +struct battery_type{ + + BOOL is_power_on_reset; + + INT32 voltage_mV; + INT32 current_mA; + INT32 discharge_mA; + INT32 charge_counter_mAh; + INT32 temp_01c; + INT32 last_temp_01c; + INT32 id_ohm; + INT32 vref_mv; + + INT32 voltage_adc; + INT32 current_adc; + INT32 discharge_adc; + INT32 charge_counter_adc; + INT32 temp_adc; + INT32 last_temp_adc; + INT32 id_adc; + INT32 vref_adc; + + INT32 id_index; + INT32 charge_full_design_mAh; + INT32 charge_full_real_mAh; + + INT32 temp_index; + INT32 temp_check_index; + + INT32 KADC_01p; + INT32 RARC_01p; + INT32 pd_m; + + INT32 software_charge_counter_mAms; + INT32 thermal_id; +}; + +struct protect_flags_type{ + + BOOL is_charging_enable_available; + BOOL is_charging_high_current_avaialble; + BOOL is_charging_indicator_available; + BOOL is_battery_dead; +#if 0 + BOOL is_battery_overtemp; +#endif + BOOL is_fake_room_temp; +}; + +/* ds2746 register definition*/ + +#define DS2746_STATUS_PORF (1 << 6) /* write to 0 as power-up sequence ready*/ +#define DS2746_STATUS_SMOD (1 << 5) /* write to 0 to disable DS2746 sleep mode*/ +#define DS2746_STATUS_NBEN (1 << 4) /* write to 0 to disable blanking of negative currents*/ +#define DS2746_STATUS_AIN0 (1 << 0) +#define DS2746_STATUS_AIN1 (1 << 1) + +/* function prototypes*/ + +void battery_capacity_update(struct battery_type *battery, int capacity_01p); +BOOL battery_param_update(struct battery_type *battery, struct protect_flags_type *flags); +DWORD BAHW_MyGetMSecs(void); +void battery_param_init(struct battery_type *battery); + +#endif /* __BATT_PARAM_H__*/ diff --git a/include/linux/ds2746_param_config.h b/include/linux/ds2746_param_config.h new file mode 100644 index 00000000..ff77356e --- /dev/null +++ b/include/linux/ds2746_param_config.h @@ -0,0 +1,274 @@ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Copyright (c) 2010 High Tech Computer Corporation + +Module Name: + + ds2746_param_config.c + +Abstract: + + This module tells batt_param.c module what the battery characteristic is. + And also which charger/gauge component hardware uses, to decide if software/hardware function is available. + +Original Auther: + + Andy.ys Wang June-01-2010 + +---------------------------------------------------------------------------------*/ + + +#ifndef __BATT_PARAM_CONFIG_H__ +#define __BATT_PARAM_CONFIG_H__ +#define HTC_BATT_BOARD_NAME "ACE" + +/*======================================================================================== + +battery parameter defines (depend on board design) + +========================================================================================*/ + +static BOOL support_ds2746_gauge_ic = TRUE; + +enum BATTERY_ID_ENUM { + BATTERY_ID_UNKNOWN = 0, + BATTERY_ID_SONY_1300MAH_FORMOSA, + BATTERY_ID_SONY_1300MAH_HTE, + BATTERY_ID_SANYO_1300MAH_HTE, + BATTERY_ID_SANYO_1300MAH_TWS, + BATTERY_ID_NUM /* include unknown battery*/ +}; + +UINT32 ID_RANGE[] = +{ + /* id resister range = [min, max)*/ + 7150, 15500, /* Sony 1300mAh (Formosa) */ /* 7.1k~15k */ + 27500, 49500, /* Sony 1300mAh (HTE) */ /* 28k~49.5k */ + 15500, 27500, /* Sanyo 1300mAh (HTE) */ /* 16k~27k */ + 0, 7150, /* Samsung 1230mAh */ /* 0k~7k */ +}; + +/*This table is calculated according to temp formula for temp mapping. +If temp_adc is located on 0-95, then the temp_01c is 700. + 96-99, then the temp_01c is 690. + 100-102, then the temp_01c is 680. + ... + 1117777777-1145, then the temp_01c is 0. + 1146-1175, then the temp_01c is -1. + ... + 1433-2046, then the temp_01c is -11. +*/ +UINT32 TEMP_MAP_300K[] = +{ +0, 96, 100, 103, 107, 111, 115, 119, 123, 127, +133, 137, 143, 148, 154, 159, 165, 172, 178, 185, +192, 199, 207, 215, 223, 232, 241, 250, 260, 270, +280, 291, 301, 314, 326, 339, 352, 366, 380, 394, +410, 425, 442, 458, 476, 494, 512, 531, 551, 571, +592, 614, 636, 659, 682, 706, 730, 755, 780, 806, +833, 860, 887, 942, 943, 971, 1000, 1028, 1058, 1087, +1117, 1146, 1176, 1205, 1234, 1264, 1293, 1321, 1350, 1378, +1406, 1433, 2047, +}; + +UINT32 TEMP_MAP_1000K[] = +{ +0, 30, 31, 32, 34, 35, 36, 38, 39, 40, +42, 44, 45, 47, 49, 51, 53, 55, 57, 60, +62, 64, 67, 70, 73, 76, 79, 82, 86, 89, +93, 97, 101, 106, 111, 115, 120, 126, 131, 137, +143, 150, 156, 163, 171, 178, 187, 195, 204, 213, +223, 233, 244, 255, 267, 279, 292, 306, 320, 334, +350, 365, 382, 399, 418, 436, 456, 476, 497, 519, +542, 566, 590, 615, 641, 668, 695, 723, 752, 782, +812, 843, 2047, +}; + +UINT32 *TEMP_MAP = TEMP_MAP_300K; + +UINT32 FL_25[] = +{ + 1280, /* Unknown battery */ + 1280, /* Sony 1300mAh (Formosa) */ + 1280, /* Sony 1300mAh (HTE) */ + 1250, /* Sanyo 1300mAh (HTE) */ + 1230, /* Samsung 1230mAh */ +}; + +UINT32 PD_M_COEF[] = +{ + /* for TAO PMA, this is defined as PD_M; for TPE PMA, this is defined as FT or t*/ + 24, /* Unknown battery */ + 24, /* Sony 1300mAh (Formosa) */ + 24, /* Sony 1300mAh (HTE) */ + 27, /* Sanyo 1300mAh (HTE) */ + 30, /* Samsung 1230mAh */ +}; + +UINT32 PD_M_RESL[] = +{ + /* for TAO PMA, this is defined as PD_M; for TPE PMA, this is defined as FT or t*/ + 100, /* Unknown battery */ + 100, /* Sony 1300mAh (Formosa) */ + 100, /* Sony 1300mAh (HTE) */ + 100, /* Sanyo 1300mAh (HTE) */ + 100, /* Samsung 1230mAh */ +}; + +UINT32 PD_T_COEF[] = +{ + /* Ex: 140 -> 0.014, 156 -> 0.0156*/ + 140, /* Unknown battery */ + 140, /* Sony 1300mAh (Formosa) */ + 140, /* Sony 1300mAh (HTE) */ + 156, /* Sanyo 1300mAh (HTE) */ + 250, /* Samsung 1230mAh */ +}; + +/*! star_lee 20100426 - update KADC discharge parameter */ +UINT32 M_PARAMETER_SONY_1300MAH_FORMOSA[] = +{ + /* capacity (in 0.01%) -> voltage (in mV)*/ + 10000, 4100, 5500, 3839, 2400, 3759, 400, 3667, 0, 3397, +}; + +UINT32 M_PARAMETER_Samsung_1230MAH_FORMOSA[] = +{ + /* capacity (in 0.01%) -> voltage (in mV)*/ + 10000, 4135, 7500, 3960, 4700, 3800, 1700, 3727, 900, 3674, 300, 3640, 0, 3420, +}; + +UINT32 *M_PARAMTER_TABLE[] = +{ + /* Unknown battery */ + (UINT32 *) (M_PARAMETER_SONY_1300MAH_FORMOSA), + /* same as Sony 1300mAh (Formosa) currently... */ + /* Sony 1300mAh (Formosa) */ + (UINT32 *) (M_PARAMETER_SONY_1300MAH_FORMOSA), + /* Sony 1300mAh (HTE) */ + (UINT32 *) (M_PARAMETER_SONY_1300MAH_FORMOSA), + /* same as Sony 1300mAh (Formosa) currently... */ + /* Sanyo 1300mAh (HTE) */ + (UINT32 *) (M_PARAMETER_SONY_1300MAH_FORMOSA), + /* same as Sony 1300mAh (Formosa) currently... */ + /* Samsung 1230mAh */ + (UINT32 *) (M_PARAMETER_Samsung_1230MAH_FORMOSA), + /* same as Sony 1300mAh (Formosa) currently... */ +}; + +INT32 TEMP_RANGE[] = +{ + /* mapping temp to temp_index*/ + 200, /* ~20C */ + 100, /* 20~10C */ + 50, /* 10~5C */ + 0, /* 5~0C */ -5, /* 0~-5C */ -10, /* -5~-10C */ -3000, + /* -10C~ */ +}; + +UINT32 *PD_M_COEF_TABLE_BOOT[] = +{ + /* mapping current to pd_m_coef table when booting*/ + (UINT32 *) (PD_M_COEF), /* ~20C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* 20~10C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* 10~5C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* 5~0C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* 0~-5C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* -5~-10C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* -10C~, using PD_M_COEF */ +}; + +UINT32 *PD_M_COEF_TABLE[] = +{ + /* mapping current to pd_m_coef table*/ + (UINT32 *) (PD_M_COEF), /* ~20C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* 20~10C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* 10~5C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* 5~0C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* 0~-5C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* -5~-10C, using PD_M_COEF */ + (UINT32 *) (PD_M_COEF), /* -10C~, using PD_M_COEF */ +}; + +UINT32 *PD_M_RESL_TABLE_BOOT[] = +{ + /* mapping current to pd_m_coef table when booting*/ + (UINT32 *) (PD_M_RESL), /* ~20C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* 20~10C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* 10~5C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* 5~0C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* 0~-5C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* -5~-10C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* -10C~, using PD_M_RESL */ +}; + +UINT32 *PD_M_RESL_TABLE[] = +{ + /* mapping current to pd_m_coef table*/ + (UINT32 *) (PD_M_RESL), /* ~20C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* 20~10C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* 10~5C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* 5~0C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* 0~-5C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* -5~-10C, using PD_M_RESL */ + (UINT32 *) (PD_M_RESL), /* -10C~, using PD_M_RESL */ +}; + +UINT32 CAPACITY_DEDUCTION_01p[] = +{ + 0, /* ~20C, upper capacity 100 is usable */ + 0, /* 20~10C, upper capacity 95 is usable */ + 0, /* 10~5C, upper capacity 92 is usable */ + 0, /* 5~0C, upper capacity 90 is usable */ + 0, /* 0~-5C, upper capacity 87 is usable */ + 0, /* -5~-10C, upper capacity 85 is usable */ + 0, /* -10C~, upper capacity 85 is usable */ +}; + +/*======================================================================================== + +battery formula coef definition, can be re-programable + +========================================================================================*/ + +/* adc converter*/ + +static INT32 voltage_adc_to_mv_coef = 244; +static INT32 voltage_adc_to_mv_resl = 100; +static INT32 current_adc_to_mv_coef = 625; +static INT32 current_adc_to_mv_resl = 1580; +static INT32 discharge_adc_to_mv_coef = 625; +static INT32 discharge_adc_to_mv_resl = 1580; +static INT32 acr_adc_to_mv_coef = 625; +static INT32 acr_adc_to_mv_resl = 1580; +static INT32 charge_counter_zero_base_mAh = 500; + +static INT32 id_adc_resl = 2047; +static INT32 temp_adc_resl = 2047; + +/* kadc parameter*/ + +static INT32 pd_m_bias_mA; /* the bias current when calculating pd_m*/ + +/* over temperature algorithm*/ + +static INT32 over_high_temp_lock_01c = 450; +static INT32 over_high_temp_release_01c = 420; +static INT32 over_low_temp_lock_01c; /*over_low_temp_lock_01c = 0*/ +static INT32 over_low_temp_release_01c = 30; + +/* function config*/ + +static BOOL is_allow_batt_id_change = FALSE; + +/*boot up voltage*/ + +/*dead battery is voltage < M_0*/ +#define BATTERY_DEAD_VOLTAGE_LEVEL 3420 +#define BATTERY_DEAD_VOLTAGE_RELEASE 3450 + +#define TEMP_MAX 70 +#define TEMP_MIN -11 +#define TEMP_NUM 83 + +#endif diff --git a/include/linux/tps65200.h b/include/linux/tps65200.h new file mode 100644 index 00000000..6048cecb --- /dev/null +++ b/include/linux/tps65200.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2007 HTC Incorporated + * + * 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. + */ +#ifndef _TPS65200_H_ +#define _TPS65200_H_ +#include +#include + +#ifdef CONFIG_TPS65200 +extern int tps_set_charger_ctrl(u32 ctl); +#else +static int tps_set_charger_ctrl(u32 ctl) {return 0 ; } +#endif +#endif diff --git a/include/linux/wrapper_types.h b/include/linux/wrapper_types.h new file mode 100644 index 00000000..742755fe --- /dev/null +++ b/include/linux/wrapper_types.h @@ -0,0 +1,92 @@ +#ifndef __WRAPPER_TYPES_H_ +#define __WRAPPER_TYPES_H_ + +#define DRIVER_ZONE "[D:BATT]" +#define DRIVER_BATTERY (1<<9) + +/* The left side of these typedefs are machine and compiler dependent */ +typedef signed char INT8; +typedef unsigned char UINT8; +typedef signed short INT16; +typedef unsigned short UINT16; +typedef signed int INT32; +typedef unsigned int UINT32; +typedef struct _INT64{ + + UINT32 u0, u1, u2; INT32 u3; +} INT64; +typedef struct _UINT64{ + + UINT32 u0, u1, u2, u3; +} UINT64; +typedef struct _INT128{ + + UINT32 u0, u1, u2; INT32 u3; +} INT128; +typedef struct _UINT128{ + + UINT32 u0, u1, u2, u3; +} UINT128; + +typedef INT8 * PINT8; +typedef UINT8 *PUINT8; +typedef INT16 * PINT16; +typedef UINT16 * PUINT16; +typedef INT32 *PINT32; +typedef UINT32 *PUINT32; +typedef INT64 * PINT64; +typedef UINT64 * PUINT64; +typedef INT128 * PINT128; +typedef UINT128 * PUINT128; +typedef const void *PCVOID; +typedef void **PPVOID; +/*typedef unsigned char uchar;*/ +typedef void (*PFNVOID)( void); +typedef char CHAR; +typedef UINT8 BYTE; +typedef UINT8 UCHAR; +typedef UINT32 ULONG; +typedef INT32 LONG; +typedef UINT32 DWORD; +typedef UINT32 UINT; +typedef UINT32 BOOL; +typedef UINT16 WORD; +typedef UINT16 USHORT; +typedef INT32 INT; +typedef void *LPVOID; +typedef void *PVOID; +typedef void VOID; +typedef void *HLOCAL; +typedef CHAR * LPCHAR; +typedef CHAR * PBYTE; +typedef INT16 * LPCWSTR; +typedef UINT8 *LPTSTR; +typedef UINT8 *LPCSTR; +typedef UINT8 *LPCTSTR; +typedef BYTE * LPBYTE; +typedef DWORD *LPDWORD; +typedef DWORD *PDWORD; +typedef CHAR TCHAR; +typedef UINT32 HINSTANCE; +typedef UINT32 HKEY; +typedef UINT32 HMODULE; +typedef UINT32 FARPROC; +typedef UINT32 HRESULT; +typedef UINT32 HREGNOTIFY; +typedef UINT32 LPSTARTUPINFO; +typedef UINT32 LPPROCESS_INFORMATION; +typedef UINT32 REGISTRYNOTIFYCALLBACK; +typedef UINT32 NOTIFICATIONCONDITION; +typedef UINT32 REGSAM; +typedef UINT32 PHKEY; + +#define FALSE 0 +#define TRUE 1 +#define INFINITE 0x7fffffffffffffffL + +#define MAKEWORD(a, b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) << 8)) +#define LOBYTE(w) ((BYTE)(w)) +#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) +#define CEILING(n, precision) ((n+precision-1)/precision) /* ex: CEILING(989, 10)=99, means 98.9 mapping to 99*/ + +#endif