.BATD_EN = 0,
},
},
- .fcc = 6200, //6200 mAh
+// .fcc = 6200, //6200 mAh
};
static struct rt5025_gpio_data rt5025_gpio_data = {
.TIMEOUT_PC = 1,
.CHVSREGI = 0,
.CHTREGI = 0,
- .CHRCHGI = 0,
+ .CHRCHGI = 1,
},
},
.irq_enable4 = {
},
};
+//temp unit: 'c*10 degree
+static int jeita_temp[4] = { 0, 150, 500, 600};
+static u8 jeita_scalar[4] = { 0x2d, 0x25, 0x12, 0x0e};
+//cc unit: xxx mA
+static int jeita_temp_cc[][5] = {{ 500, 500, 500, 500, 500}, // not plugin
+ { 0 , 250, 500, 250, 0}, // normal USB
+ { 0, 500, 1000, 500, 0}, // USB charger
+ { 0, 500, 1000, 500, 0}}; // AC Adapter
+//cv unit: xxx mV
+static int jeita_temp_cv[][5] = {{ 4200, 4200, 4200, 4200, 4200}, // not plugin
+ { 4200, 4200, 4200, 4200, 4200}, // normal USB
+ { 4200, 4200, 4200, 4200, 4200}, // USB charger
+ { 4200, 4200, 4200, 4200, 4200}}; // AC Adapter
+
+static struct rt5025_jeita_data rt5025_jeita_data = {
+ .temp = jeita_temp,
+ .temp_scalar = jeita_scalar,
+ .temp_cc = jeita_temp_cc,
+ .temp_cv = jeita_temp_cv,
+};
+
static void rt5025_charger_event_callback(uint32_t detected)
{
RTINFO("event detected = 0x%08x\n", detected);
.gpio_data = &rt5025_gpio_data,
.misc_data = &rt5025_misc_data,
.irq_data = &rt5025_irq_data,
+ .jeita_data = &rt5025_jeita_data,
.cb = &rt5025_event_callback,
.intr_pin = 81, //GPIO81
};
mutex_init(&ricoh619->io_lock);
ret = ricoh619_read(ricoh619->dev, 0x36, &control);
- if ((control < 0) || (control == 0xff)) {
+ if ((control < 0) || (control == 0xff) || (control == 0)) {
printk(KERN_INFO "The device is not ricoh619\n");
return 0;
}
.id = -1,
.num_resources = 0,
},
+{
+ .name = RT5025_DEVICE_NAME "-swjeita",
+ .id = -1,
+ .num_resources = 0,
+},
+{
+ .name = RT5025_DEVICE_NAME "-battery",
+ .id = -1,
+ .num_resources = 0,
+},
};
#endif /* CONFIG_POWER_RT5025 */
int ret = 0;
RTINFO("Start to initialize all device\n");
- printk("%s,line=%d\n", __func__,__LINE__);
#ifdef CONFIG_REGULATOR_RT5025
if (pdata && pdata->regulator[0]) {
#endif /* CONFIG_REGULATOR_RT5025 */
#ifdef CONFIG_POWER_RT5025
- if (pdata && pdata->power_data) {
+ if (pdata && pdata->power_data && pdata->jeita_data) {
RTINFO("mfd add power dev\n");
#if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0))
ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
}
}
#endif /* CONFIG_GPIO_RT5025 */
-\
+
#ifdef CONFIG_MFD_RT5025_MISC
if (pdata && pdata->misc_data) {
RTINFO("mfd add misc dev\n");
}
}
#endif /* CONFIG_MFD_RT5025_IRQ */
-#if 1
+
#ifdef CONFIG_MFD_RT5025_DEBUG
RTINFO("mfd add debug dev\n");
- printk("%s,line=%d\n", __func__,__LINE__);
#if (LINUX_VERSION_CODE>=KERNEL_VERSION(3,6,0))
ret = mfd_add_devices(chip->dev, 0, &debug_devs[0],
ARRAY_SIZE(debug_devs),
goto out_dev;
}
#endif /* CONFIG_MFD_RT5025_DEBUG */
-#endif
-
+
RTINFO("Initialize all device successfully\n");
return ret;
out_dev:
static struct dentry *debugfs_rt_dent;
static struct dentry *debugfs_peek;
static struct dentry *debugfs_poke;
+static struct dentry *debugfs_regs;
-static unsigned char read_data;
+static unsigned char read_data[10];
static int reg_debug_open(struct inode *inode, struct file *file)
{
return 0;
}
+#define LOG_FORMAT "0x%02x\n0x%02x\n0x%02x\n0x%02x\n0x%02x\n"
+
static ssize_t reg_debug_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *ppos)
{
- char lbuf[8];
-
- snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+ char *access_str = filp->private_data;
+ char lbuf[150];
+ if (!strcmp(access_str, "regs"))
+ {
+ RTINFO("read regs file\n");
+ /* read regs */
+ snprintf(lbuf, sizeof(lbuf), LOG_FORMAT LOG_FORMAT, read_data[0], \
+ read_data[1], read_data[2], read_data[3], read_data[4], read_data[5], \
+ read_data[6], read_data[7], read_data[8], read_data[9]);
+ }
+ else
+ snprintf(lbuf, sizeof(lbuf), "0x%02x\n", read_data[0]);
return simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf));
}
rc = get_parameters(lbuf, param, 1);
if ((param[0] <= 0xFF) && (rc == 0))
{
- read_data = rt5025_reg_read(client, param[0]);
+ read_data[0] = rt5025_reg_read(client, param[0]);
+ }
+ else
+ rc = -EINVAL;
+ } else if (!strcmp(access_str, "regs")) {
+ /* read */
+ rc = get_parameters(lbuf, param, 1);
+ if ((param[0] <= 0xFF) && (rc == 0))
+ {
+ rt5025_reg_block_read(client, param[0], 10, read_data);
+ RTINFO("regs 0 = 0x%02x\n", read_data[0]);
+ RTINFO("regs 1 = 0x%02x\n", read_data[1]);
+ RTINFO("regs 2 = 0x%02x\n", read_data[2]);
+ RTINFO("regs 3 = 0x%02x\n", read_data[3]);
+ RTINFO("regs 4 = 0x%02x\n", read_data[4]);
+ RTINFO("regs 5 = 0x%02x\n", read_data[5]);
+ RTINFO("regs 6 = 0x%02x\n", read_data[6]);
+ RTINFO("regs 7 = 0x%02x\n", read_data[7]);
+ RTINFO("regs 8 = 0x%02x\n", read_data[8]);
+ RTINFO("regs 9 = 0x%02x\n", read_data[9]);
}
else
rc = -EINVAL;
debugfs_poke = debugfs_create_file("poke",
S_IFREG | S_IRUGO, debugfs_rt_dent,
(void *) "poke", ®_debug_ops);
+
+ debugfs_regs = debugfs_create_file("regs",
+ S_IFREG | S_IRUGO, debugfs_rt_dent,
+ (void *) "regs", ®_debug_ops);
}
platform_set_drvdata(pdev, di);
{
struct delayed_work *delayed_work = (struct delayed_work *)container_of(work, struct delayed_work, work);
struct rt5025_irq_info *ii = (struct rt5025_irq_info *)container_of(delayed_work, struct rt5025_irq_info, delayed_work);
- unsigned char irq_stat[10] = {0};
+ unsigned char irq_stat[6] = {0};
uint32_t chg_event = 0, pwr_event = 0;
#ifdef CONFIG_POWER_RT5025
- if (!ii->chip->power_info)
+ if (!ii->chip->power_info || !ii->chip->jeita_info || !ii->chip->battery_info)
{
queue_delayed_work(ii->wq, &ii->delayed_work, msecs_to_jiffies(1));
return;
}
#endif
- //if (rt5025_reg_block_read(ii->i2c, RT5025_REG_IRQEN1, 10, irq_stat) >= 0)
+ #if 0
+ if (rt5025_reg_block_read(ii->i2c, RT5025_REG_IRQEN1, 10, irq_stat) >= 0)
{
- irq_stat[1] = rt5025_reg_read(ii->i2c, 0x31);
- irq_stat[3] = rt5025_reg_read(ii->i2c, 0x33);
- irq_stat[5] = rt5025_reg_read(ii->i2c, 0x35);
- irq_stat[7] = rt5025_reg_read(ii->i2c, 0x37);
- irq_stat[9] = rt5025_reg_read(ii->i2c, 0x39);
- RTINFO("irq1->%02x, irq2->%02x, irq3->%02x\n", irq_stat[1], irq_stat[3], irq_stat[5]);
- RTINFO("irq4->%02x, irq5->%02x\n", irq_stat[7], irq_stat[9]);
+ #endif
+ irq_stat[0] = rt5025_reg_read(ii->i2c, RT5025_REG_IRQSTATUS1);
+ irq_stat[1] = rt5025_reg_read(ii->i2c, RT5025_REG_IRQSTATUS2);
+ irq_stat[2] = rt5025_reg_read(ii->i2c, RT5025_REG_IRQSTATUS3);
+ irq_stat[3] = rt5025_reg_read(ii->i2c, RT5025_REG_IRQSTATUS4);
+ irq_stat[4] = rt5025_reg_read(ii->i2c, RT5025_REG_IRQSTATUS5);
+ irq_stat[5] = rt5025_reg_read(ii->i2c, RT5025_REG_GAUGEIRQFLG);
+ RTINFO("irq1->0x%02x, irq2->0x%02x, irq3->0x%02x\n", irq_stat[0], irq_stat[1], irq_stat[2]);
+ RTINFO("irq4->0x%02x, irq5->0x%02x, irq6->0x%02x\n", irq_stat[3], irq_stat[4], irq_stat[5]);
RTINFO("stat value = %02x\n", rt5025_reg_read(ii->i2c, RT5025_REG_CHGSTAT));
- chg_event = irq_stat[1]<<16 | irq_stat[3]<<8 | irq_stat[5];
- pwr_event = irq_stat[7]<<8 | irq_stat[9];
+ chg_event = irq_stat[0]<<16 | irq_stat[1]<<8 | irq_stat[2];
+ pwr_event = irq_stat[3]<<8 | irq_stat[4];
#ifdef CONFIG_POWER_RT5025
if (chg_event & CHARGER_DETECT_MASK)
+ {
+ if (chg_event & CHG_EVENT_CHTERMI)
+ ii->chip->power_info->chg_term++;
rt5025_power_charge_detect(ii->chip->power_info);
+ }
#endif /* CONFIG_POWER_RT5025 */
if (ii->event_cb)
{
if (pwr_event)
ii->event_cb->power_event_callkback(pwr_event);
}
- }
#if 0
+ }
else
dev_err(ii->dev, "read irq stat io fail\n");
#endif
#ifdef CONFIG_POWER_RT5025
- rt5025_power_passirq_to_gauge(ii->chip->power_info);
+ if (irq_stat[5] & RT5025_FLG_TEMP)
+ rt5025_swjeita_irq_handler(ii->chip->jeita_info, irq_stat[5] & RT5025_FLG_TEMP);
+ if (irq_stat[5] & RT5025_FLG_VOLT)
+ rt5025_gauge_irq_handler(ii->chip->battery_info, irq_stat[5] & RT5025_FLG_VOLT);
#endif /* CONFIG_POWER_RT5025 */
//enable_irq(ii->irq);
struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct rt5025_platform_data *pdata = chip->dev->platform_data;
struct rt5025_irq_info *ii;
- printk("%s,line=%d\n", __func__,__LINE__);
RTINFO("\n");
ii = kzalloc(sizeof(*ii), GFP_KERNEL);
return 0;
}
+static void rt5025_irq_shutdown(struct platform_device *pdev)
+{
+ struct rt5025_irq_info *ii = platform_get_drvdata(pdev);
+
+ if (ii->irq)
+ free_irq(ii->irq, ii);
+
+ if (ii->wq)
+ {
+ cancel_delayed_work_sync(&ii->delayed_work);
+ flush_workqueue(ii->wq);
+ }
+}
+
static struct platform_driver rt5025_irq_driver =
{
.driver = {
},
.probe = rt5025_irq_probe,
.remove = __devexit_p(rt5025_irq_remove),
+ .shutdown = rt5025_irq_shutdown,
};
static int __init rt5025_irq_init(void)
struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct rt5025_platform_data *pdata = chip->dev->platform_data;
struct rt5025_misc_info *mi;
- printk("%s,line=%d\n", __func__,__LINE__);
mi = kzalloc(sizeof(*mi), GFP_KERNEL);
if (!mi)
obj-$(CONFIG_BATTERY_RK30_ADC_FAC) += rk30_factory_adc_battery.o
obj-$(CONFIG_CW2015_BATTERY) += cw2015_battery.o
obj-$(CONFIG_BATTERY_RICOH619) += ricoh619-battery.o
-obj-$(CONFIG_POWER_RT5025) += rt5025-power.o rt5025-gauge.o
\ No newline at end of file
+obj-$(CONFIG_POWER_RT5025) += rt5025-power.o rt5025-battery.o rt5025-swjeita.o
--- /dev/null
+/* drivers/power/rt5025-battery.c
+ * I2C Driver for Richtek RT5025 PMIC
+ * Multi function device - multi functional baseband PMIC Battery part
+ *
+ * Copyright (C) 2013
+ * Author: Nick Hung <nick_hung@richtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/android_alarm.h>
+#include <linux/mfd/rt5025.h>
+#include <linux/power/rt5025-battery.h>
+
+#define VOLTAGE_ALERT 1
+#define TEMPERATURE_ALERT 0
+
+#define RT5025_CSV 0
+#define RT5025_B 1
+#define RT5025_TEST_WAKE_LOCK 1
+
+u8 irq_thres[LAST_TYPE];
+
+void rt5025_gauge_set_status(struct rt5025_battery_info *bi, int status)
+{
+ bi->status = status;
+ if (status == POWER_SUPPLY_STATUS_FULL)
+ bi->tp_flag = true;
+}
+EXPORT_SYMBOL(rt5025_gauge_set_status);
+
+void rt5025_gauge_set_online(struct rt5025_battery_info *bi, bool present)
+{
+ bi->online = present;
+}
+EXPORT_SYMBOL(rt5025_gauge_set_online);
+
+static int rt5025_read_reg(struct i2c_client *client,
+ u8 reg, u8 *data, u8 len)
+{
+ #if 1
+ return rt5025_reg_block_read(client, reg, len, data);
+ #else
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msgs[2];
+ int ret;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags;
+ msgs[0].len = 1;
+ msgs[0].buf = ®
+
+ msgs[1].addr = client->addr;
+ msgs[1].flags = client->flags | I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = data;
+
+ ret = i2c_transfer(adap, msgs, 2);
+
+ return (ret == 2)? len : ret;
+ #endif
+}
+
+static int rt5025_write_reg(struct i2c_client *client,
+ u8 reg, u8 *data, u8 len)
+{
+ #if 1
+ return rt5025_reg_block_write(client, reg, len, data);
+ #else
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg;
+ int ret;
+ char *tx_buf = (char *)kmalloc(len + 1, GFP_KERNEL);
+
+ if(!tx_buf)
+ return -ENOMEM;
+ tx_buf[0] = reg;
+ memcpy(tx_buf+1, data, len);
+
+ msg.addr = client->addr;
+ msg.flags = client->flags;
+ msg.len = len + 1;
+ msg.buf = (char *)tx_buf;
+
+ ret = i2c_transfer(adap, &msg, 1);
+ kfree(tx_buf);
+ return (ret == 1) ? len : ret;
+ #endif
+}
+
+static void rt5025_gauge_alarm(struct alarm *alarm)
+{
+ struct rt5025_battery_info *bi = (struct rt5025_battery_info *)container_of(alarm, struct rt5025_battery_info, wakeup_alarm);
+
+ wake_lock(&bi->monitor_wake_lock);
+ schedule_delayed_work(&bi->monitor_work, 0);
+}
+
+static void rt5025_program_alarm(struct rt5025_battery_info *bi)
+{
+ ktime_t low_interval = ktime_set(bi->update_time, 0);
+ ktime_t slack = ktime_set(20, 0);
+ ktime_t next;
+
+ next = ktime_add(bi->last_poll, low_interval);
+ alarm_start_range(&bi->wakeup_alarm, next, ktime_add(next, slack));
+}
+
+#if 0
+static void rt5025_run_time(struct rt5025_battery_info *bi)
+{
+ if(bi->curr <= 0)
+ {
+ bi->time_to_empty = bi->rm / (bi->curr*(-1));
+ }
+ else
+ {
+ bi->time_to_full = (bi->fcc * 3600 - bi->rm) / bi->curr;
+ }
+ RTINFO("RTTF = %d\n",bi->time_to_full);
+ RTINFO("RTTE = %d\n",bi->time_to_empty);
+}
+#endif
+
+static int rt5025_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct rt5025_battery_info *bi = dev_get_drvdata(psy->dev->parent);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = bi->status;
+ //val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = bi->health;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = bi->present;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = bi->ext_temp;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = bi->online;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bi->vcell * 1000; //uV
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = bi->curr * 1000; //uA
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = bi->soc;
+ //val->intval = 50;
+ if (val->intval > 100)
+ val->intval = 100;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ #if 0
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+ val->intval = bi->time_to_empty;
+ break;
+ case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
+ val->intval = bi->time_to_full;
+ #endif
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void rt5025_get_vcell(struct rt5025_battery_info *bi)
+{
+ u8 data[2];
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_VCELL_MSB, data, 2) < 0){
+ printk(KERN_ERR "%s: Failed to read Voltage\n", __func__);
+ }
+
+ if (bi->avg_flag)
+ bi->vcell = ((data[0] << 8) + data[1]) * 61 / 100;
+ else
+ bi->vcell = (bi->vcell + ((data[0] << 8) + data[1]) * 61 / 100) / 2;
+#if RT5025_B
+ bi->curr_offset = (15444 * bi->vcell - 27444000) / 10000;
+#else
+ if (37 * bi->vcell > 92000)
+ bi->curr_offset = (37 * bi->vcell - 92000) / 1000;
+ else
+ bi->curr_offset = 0;
+#endif
+
+#if RT5025_CSV
+ // if (!bi->avg_flag)
+ // pr_info("%d,%d,", bi->vcell, bi->curr_offset);
+#else
+ if (bi->avg_flag)
+ RTINFO("vcell_pre: %d, offset: %d\n", bi->vcell, bi->curr_offset);
+ else
+ RTINFO("vcell_avg: %d, offset: %d\n", bi->vcell, bi->curr_offset);
+#endif
+}
+
+static void rt5025_get_current(struct rt5025_battery_info *bi)
+{
+ u8 data[2];
+ s32 temp;
+ int sign = 0;
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_CURRENT_MSB, data, 2) < 0) {
+ printk(KERN_ERR "%s: Failed to read CURRENT\n", __func__);
+ }
+#if RT5025_B
+ temp = (data[0]<<8) | data[1];
+ bi->curr_raw = ((temp & 0x7FFF) * 3125) / 10000;
+
+ if (data[0] & (1 << 7)) {
+ sign = 1;
+ temp = (((temp & 0x7FFF) * 3125) / 10 + bi->curr_offset) / 1000;
+ }else{
+ if ((temp * 3125) / 10 > bi->curr_offset)
+ temp = ((temp * 3125) / 10 - bi->curr_offset) / 1000;
+ }
+
+ if (temp < DEADBAND)
+ temp = 0;
+
+ if (sign){
+ temp *= -1;
+ bi->curr_raw *= -1;
+ }
+#else
+ temp = (data[0]<<8) | data[1];
+ if (data[0] & (1 << 7)) {
+ sign = 1;
+ temp = temp & 0x7FFF;
+ if(temp > bi->curr_offset)
+ temp = temp - bi->curr_offset;
+ }else {
+ temp = temp + bi->curr_offset;
+ }
+
+ temp = (temp * 37375) / 100000; //Unit: 0.3125mA
+ if (temp < DEADBAND)
+ temp = 0;
+
+ if (sign)
+ temp *= -1;
+#endif
+
+ if (bi->avg_flag)
+ bi->curr = temp;
+ else
+ bi->curr = (bi->curr + temp) / 2;
+
+ if(bi->curr > 0)
+ bi->internal_status = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ bi->internal_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ RTINFO("current=%d, internal_status=%d\n", bi->curr, bi->internal_status);
+
+#if RT5025_CSV
+ // if (!bi->avg_flag)
+ // pr_info("%d,",bi->curr);
+#else
+ if (bi->avg_flag)
+ RTINFO("current_pre: %d\n", bi->curr);
+ else
+ RTINFO("current_avg: %d\n", bi->curr);
+#endif
+}
+
+static void rt5025_get_external_temp(struct rt5025_battery_info *bi)
+{
+ u8 data[2];
+ s32 temp;
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_EXT_TEMPERATUE_MSB, data, 2) < 0) {
+ printk(KERN_ERR "%s: Failed to read TEMPERATURE\n", __func__);
+ }
+ bi->ain_volt = (data[0] * 256 + data[1]) * 61 / 100;
+ if(bi->ain_volt < 1150)
+ {
+ bi->present = 1;
+ }
+ else
+ {
+ bi->present = 0;
+ }
+
+ temp = (bi->ain_volt * (-91738) + 81521000) / 100000;
+ bi->ext_temp = (int)temp;
+ //test
+ //bi->ext_temp = 250;
+
+ if (bi->ext_temp >= HIGH_TEMP_THRES) {
+ if (bi->health != POWER_SUPPLY_HEALTH_OVERHEAT)
+ bi->temp_high_cnt++;
+ } else if (bi->ext_temp <= HIGH_TEMP_RECOVER && bi->ext_temp >= LOW_TEMP_RECOVER) {
+ if (bi->health == POWER_SUPPLY_HEALTH_OVERHEAT ||
+ bi->health == POWER_SUPPLY_HEALTH_COLD)
+ bi->temp_recover_cnt++;
+ } else if (bi->ext_temp <= LOW_TEMP_THRES) {
+ if (bi->health != POWER_SUPPLY_HEALTH_COLD)
+ bi->temp_low_cnt++;
+ }else {
+ bi->temp_high_cnt = 0;
+ bi->temp_low_cnt = 0;
+ bi->temp_recover_cnt = 0;
+ }
+
+ if (bi->temp_high_cnt >= TEMP_ABNORMAL_COUNT) {
+ bi->health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ bi->temp_high_cnt = 0;
+ } else if (bi->temp_low_cnt >= TEMP_ABNORMAL_COUNT) {
+ bi->health = POWER_SUPPLY_HEALTH_COLD;
+ bi->temp_low_cnt = 0;
+ } else if (bi->temp_recover_cnt >= TEMP_ABNORMAL_COUNT) {
+ bi->health = POWER_SUPPLY_HEALTH_GOOD;
+ bi->temp_recover_cnt = 0;
+ }
+ //RTINFO("external temperature: %d\n", bi->ext_temp);
+}
+
+static void rt5025_clear_cc(struct rt5025_battery_info *bi, operation_mode mode)
+{
+ u8 data[2];
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_CHANNEL_MSB, data, 2) < 0){
+ pr_err("%s: failed to read channel\n", __func__);
+ }
+
+ if (mode == CHG)
+ data[0] = data[0] | CHANNEL_H_BIT_CLRQCHG;
+ else
+ data[0] = data[0] | CHANNEL_H_BIT_CLRQDCHG;
+
+ if (rt5025_write_reg(bi->client, RT5025_REG_CHANNEL_MSB, data, 2) < 0){
+ pr_err("%s: failed to write channel\n", __func__);
+ }
+}
+
+static void rt5025_get_chg_cc(struct rt5025_battery_info *bi)
+{
+ u8 data[4];
+ u32 qh_old,ql_old,qh_new,ql_new;
+ u32 cc_masec,offset;
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_QCHGH_MSB, data, 4) < 0){
+ pr_err("%s: Failed to read QCHG\n", __func__);
+ }
+ qh_old = (data[0]<<8) + data[1];
+ ql_old = (data[2]<<8) + data[3];
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_QCHGH_MSB, data, 4) < 0){
+ pr_err("%s: Failed to read QCHG\n", __func__);
+ }
+ qh_new = (data[0]<<8) + data[1];
+ ql_new = (data[2]<<8) + data[3];
+
+#if RT5025_B
+ if (qh_new > qh_old){
+ cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10;
+ }else if (qh_new == qh_old){
+ if (ql_new >= ql_old){
+ cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10;
+ }else {
+ cc_masec = (((qh_old<<16) + ql_old) * 50134) / 10;
+ }
+ }
+
+ offset = bi->curr_offset * bi->time_interval;
+
+ if (cc_masec > offset){
+ cc_masec = (cc_masec - offset) / 1000;
+ }
+#else
+ if (qh_new > qh_old){
+ cc_masec = (((qh_new<<16) + ql_new) * 5996) / 1000;
+ }else if (qh_new == qh_old){
+ if (ql_new >= ql_old){
+ cc_masec = (((qh_new<<16) + ql_new) * 5996) / 1000;
+ }else {
+ cc_masec = (((qh_old<<16) + ql_old) * 5996) / 1000;
+ }
+ }
+
+ offset = (bi->curr_offset * bi->time_interval * 37375) / 100000;
+
+ if (cc_masec != 0){
+ cc_masec = cc_masec - offset;
+ }
+#endif
+ if (cc_masec < (DEADBAND * bi->time_interval))
+ cc_masec = 0;
+
+#if RT5025_CSV
+ //pr_info("%d,\n", cc_masec);
+#else
+ RTINFO("chg_cc_mAsec: %d\n", cc_masec);
+#endif
+
+ bi->chg_cc = cc_masec;
+ //bi->chg_cc = (cc_masec + bi->chg_cc_unuse) / 3600;
+ //bi->chg_cc_unuse = (cc_masec + bi->chg_cc_unuse) % 3600;
+ rt5025_clear_cc(bi, CHG);
+}
+
+static void rt5025_get_dchg_cc(struct rt5025_battery_info *bi)
+{
+ u8 data[4];
+ u32 qh_old,ql_old,qh_new,ql_new;
+ u32 cc_masec,offset;
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_QDCHGH_MSB, data, 4) < 0){
+ printk(KERN_ERR "%s: Failed to read QDCHG\n", __func__);
+ }
+ qh_old = (data[0]<<8) + data[1];
+ ql_old = (data[2]<<8) + data[3];
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_QDCHGH_MSB, data, 4) < 0){
+ printk(KERN_ERR "%s: Failed to read QDCHG\n", __func__);
+ }
+ qh_new = (data[0]<<8) + data[1];
+ ql_new = (data[2]<<8) + data[3];
+
+#if RT5025_B
+ if (qh_new > qh_old){
+ cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10;
+ }else if (qh_new == qh_old){
+ if (ql_new >= ql_old){
+ cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10;
+ }else {
+ cc_masec = (((qh_old<<16) + ql_old) * 50134) / 10;
+ }
+ }
+
+ offset = bi->curr_offset * bi->time_interval;
+
+ if (cc_masec != 0){
+ cc_masec = (cc_masec + offset) / 1000;
+ }
+#else
+ if (qh_new > qh_old){
+ cc_masec = (((qh_new<<16) + ql_new) * 5996) / 1000;
+ }else if (qh_new == qh_old){
+ if (ql_new >= ql_old){
+ cc_masec = (((qh_new<<16) + ql_new) * 5996) / 1000;
+ }else {
+ cc_masec = (((qh_old<<16) + ql_old) * 5996) / 1000;
+ }
+ }
+
+ offset = (bi->curr_offset * bi->time_interval * 37375) / 100000;
+
+ if (cc_masec > offset){
+ cc_masec = cc_masec - offset;
+ }
+#endif
+ if (cc_masec < (DEADBAND * bi->time_interval))
+ cc_masec = 0;
+
+#if RT5025_CSV
+ //pr_info("%d,", cc_masec);
+#else
+ RTINFO("dchg_cc_mAsec: %d\n", cc_masec);
+#endif
+ bi->dchg_cc = cc_masec;
+ //bi->dchg_cc = (cc_masec + bi->dchg_cc_unuse) / 3600;
+ //bi->dchg_cc_unuse = (cc_masec + bi->dchg_cc_unuse) % 3600;
+ rt5025_clear_cc(bi, DCHG);
+
+}
+
+static void rt5025_cycle_count(struct rt5025_battery_info *bi)
+{
+ bi->acc_dchg_cap += bi->dchg_cc;
+ if(bi->acc_dchg_cap >= (bi->dc * 3600)){
+ bi->cycle_cnt++;
+ bi->acc_dchg_cap -= (bi->dc * 3600);
+ }
+}
+
+static void rt5025_get_irq_flag(struct rt5025_battery_info *bi, u8 flag)
+{
+#if 0
+ u8 data[1];
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_IRQ_FLAG, data, 1) < 0){
+ pr_err("%s: Failed to read irq_flag\n", __func__);
+ }
+#endif
+
+ bi->irq_flag = flag;
+ //RTINFO("IRQ_FLG 0x%x\n", bi->irq_flag);
+}
+
+static void rt5025_get_timer(struct rt5025_battery_info *bi)
+{
+ u8 data[2];
+ //frankie
+ //u16 gauge_timer;
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_TIMER, data, 2) < 0){
+ pr_err("%s: Failed to read Timer\n", __func__);
+ }
+
+ bi->gauge_timer = (data[0] << 8) + data[1];
+ if (!bi->device_suspend){
+ if (bi->gauge_timer > bi->pre_gauge_timer)
+ bi->time_interval = bi->gauge_timer - bi->pre_gauge_timer;
+ else
+ bi->time_interval = 65536 - bi->pre_gauge_timer + bi->gauge_timer;
+ }
+
+ bi->pre_gauge_timer = bi->gauge_timer;
+#if RT5025_CSV
+ // pr_info("%d,%d,", bi->gauge_timer,bi->time_interval);
+#else
+ RTINFO("timer %d , interval %d\n", bi->gauge_timer,bi->time_interval);
+#endif
+}
+
+static void rt5025_alert_setting(struct rt5025_battery_info *bi, alert_type type, bool enable)
+{
+ u8 data[1];
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_IRQ_CTL, data, 1) < 0){
+ printk(KERN_ERR "%s: Failed to read CONFIG\n", __func__);
+ }
+
+ if(enable){
+ switch(type){
+ case MAXTEMP:
+ data[0] |= IRQ_CTL_BIT_TMX; //Enable max temperature alert
+ bi->max_temp_irq = true;
+ //RTDBG("Enable min temperature alert");
+ break;
+ case MINTEMP:
+ data[0] |= IRQ_CTL_BIT_TMN; //Enable min temperature alert
+ bi->min_temp_irq = true;
+ //RTDBG("Enable max temperature alert");
+ break;
+ case MAXVOLT:
+ data[0] |= IRQ_CTL_BIT_VMX; //Enable max voltage alert
+ bi->max_volt_irq = true;
+ //RTDBG("Enable max voltage alert");
+ break;
+ case MINVOLT1:
+ data[0] |= IRQ_CTL_BIT_VMN1; //Enable min1 voltage alert
+ bi->min_volt1_irq = true;
+ //RTDBG("Enable min1 voltage alert");
+ break;
+ case MINVOLT2:
+ data[0] |= IRQ_CTL_BIT_VMN2; //Enable min2 voltage alert
+ bi->min_volt2_irq = true;
+ //RTDBG("Enable min2 voltage alert");
+ break;
+ default:
+ break;
+ }
+ }else{
+ switch(type){
+ case MAXTEMP:
+ data[0] = data[0] &~ IRQ_CTL_BIT_TMX; //Disable max temperature alert
+ bi->max_temp_irq = false;
+ //RTDBG("Disable min temperature alert");
+ break;
+ case MINTEMP:
+ data[0] = data[0] &~ IRQ_CTL_BIT_TMN; //Disable min temperature alert
+ bi->min_temp_irq = false;
+ //RTDBG("Disable max temperature alert");
+ break;
+ case MAXVOLT:
+ data[0] = data[0] &~ IRQ_CTL_BIT_VMX; //Disable max voltage alert
+ bi->max_volt_irq = false;
+ //RTDBG("Disable max voltage alert");
+ break;
+ case MINVOLT1:
+ data[0] = data[0] &~ IRQ_CTL_BIT_VMN1; //Disable min1 voltage alert
+ bi->min_volt1_irq = false;
+ //RTDBG("Disable min1 voltage alert");
+ break;
+ case MINVOLT2:
+ data[0] = data[0] &~ IRQ_CTL_BIT_VMN2; //Disable min2 voltage alert
+ bi->min_volt2_irq = false;
+ //RTDBG("Disable min2 voltage alert");
+ break;
+ default:
+ break;
+ }
+ }
+ if (rt5025_write_reg(bi->client, RT5025_REG_IRQ_CTL, data, 1) < 0)
+ pr_err("%s: failed to write IRQ control\n", __func__);
+}
+
+static void rt5025_alert_threshold_init(struct i2c_client *client)
+{
+ u8 data[1];
+
+ #if 0 //change the operating right to jeita driver
+ /* TALRT MAX threshold setting */
+ data[0] = irq_thres[MAXTEMP];
+ if (rt5025_write_reg(client, RT5025_REG_TALRT_MAXTH, data, 1) < 0)
+ printk(KERN_ERR "%s: failed to write TALRT MAX threshold\n", __func__);
+ /* TALRT MIN threshold setting */
+ data[0] = irq_thres[MINTEMP];
+ if (rt5025_write_reg(client, RT5025_REG_TALRT_MINTH, data, 1) < 0)
+ printk(KERN_ERR "%s: failed to write TALRT MIN threshold\n", __func__);
+ #endif
+ /* VALRT MAX threshold setting */
+ data[0] = irq_thres[MAXVOLT];
+ if (rt5025_write_reg(client, RT5025_REG_VALRT_MAXTH, data, 1) < 0)
+ printk(KERN_ERR "%s: failed to write VALRT MAX threshold\n", __func__);
+ /* VALRT MIN1 threshold setting */
+ data[0] = irq_thres[MINVOLT1];
+ if (rt5025_write_reg(client, RT5025_REG_VALRT_MIN1TH, data, 1) < 0)
+ printk(KERN_ERR "%s: failed to write VALRT MIN1 threshold\n", __func__);
+ /* VALRT MIN2 threshold setting */
+ data[0] = irq_thres[MINVOLT2];
+ if (rt5025_write_reg(client, RT5025_REG_VALRT_MIN2TH, data, 1) < 0)
+ printk(KERN_ERR "%s: failed to write VALRT MIN2 threshold\n", __func__);
+}
+
+static void rt5025_alert_init(struct rt5025_battery_info *bi)
+{
+
+ /* Set RT5025 gauge alert configuration */
+ rt5025_alert_threshold_init(bi->client);
+ /* Enable gauge alert function */
+ rt5025_alert_setting(bi, MINVOLT2,VOLTAGE_ALERT);
+}
+
+void rt5025_gauge_irq_handler(struct rt5025_battery_info *bi, u8 irq_flag)
+{
+ rt5025_get_irq_flag(bi, irq_flag);
+
+ if ((bi->irq_flag) & IRQ_FLG_BIT_TMX){
+ //printk(KERN_INFO "[RT5025]: Min temperature IRQ received\n");
+ rt5025_alert_setting(bi,MAXTEMP,false);
+ bi->max_temp_irq = false;
+ }
+ if ((bi->irq_flag) & IRQ_FLG_BIT_TMN){
+ //printk(KERN_INFO "[RT5025]: Max temperature IRQ received\n");
+ rt5025_alert_setting(bi,MINTEMP,false);
+ bi->min_temp_irq = false;
+ }
+ if ((bi->irq_flag) & IRQ_FLG_BIT_VMX){
+ //printk(KERN_INFO "[RT5025]: Max voltage IRQ received\n");
+ rt5025_alert_setting(bi,MAXVOLT,false);
+ bi->max_volt_irq = false;
+ }
+ if ((bi->irq_flag) & IRQ_FLG_BIT_VMN1){
+ //printk(KERN_INFO "[RT5025]: Min voltage1 IRQ received\n");
+ rt5025_alert_setting(bi,MINVOLT1,false);
+ bi->min_volt1_irq = false;
+ }
+ if ((bi->irq_flag) & IRQ_FLG_BIT_VMN2){
+ //printk(KERN_INFO "[RT5025]: Min voltage2 IRQ received\n");
+ rt5025_alert_setting(bi,MINVOLT2,false);
+ bi->min_volt2_irq = false;
+ bi->min_volt2_alert = true;
+ wake_lock_timeout(&bi->low_battery_wake_lock, msecs_to_jiffies(LOW_BAT_WAKE_LOK_TIME*MSEC_PER_SEC));
+ }
+
+ wake_lock(&bi->monitor_wake_lock);
+ schedule_delayed_work(&bi->monitor_work, 0);
+}
+EXPORT_SYMBOL(rt5025_gauge_irq_handler);
+
+static void rt5025_convert_masec_to_permille(struct rt5025_battery_info *bi)
+{
+ bi->permille = bi->rm / 3600 * 1000 / bi->fcc;
+ RTINFO("permille=%d\n", bi->permille);
+ return;
+}
+
+static void rt5025_convert_permille_to_masec(struct rt5025_battery_info *bi)
+{
+ bi->rm = bi->permille * bi->fcc / 1000 * 3600;
+ return;
+}
+
+static void rt5025_init_capacity(struct rt5025_battery_info *bi)
+{
+ int i = 1;
+ int size;
+ int slope, const_term;
+ int delta_y, delta_x;
+
+ size = ARRAY_SIZE(rt5025_battery_param1);
+ while((bi->vcell < rt5025_battery_param1[i].x) &&
+ (i < (size - 1))){
+ i++;
+ }
+
+ delta_x = rt5025_battery_param1[i-1].x - rt5025_battery_param1[i].x;
+ delta_y = (rt5025_battery_param1[i-1].y - rt5025_battery_param1[i].y);
+
+ slope = delta_y * 1000 / delta_x;
+
+ const_term = (rt5025_battery_param1[i].y) - ((rt5025_battery_param1[i].x * slope) / 1000);
+
+ if (bi->vcell >= rt5025_battery_param1[0].x)
+ bi->permille = rt5025_battery_param1[0].y;
+ else if (bi->vcell <= rt5025_battery_param1[size-1].x)
+ bi->permille = rt5025_battery_param1[size-1].y;
+ else
+ bi->permille = (bi->vcell * slope) / 1000 + const_term;
+ rt5025_convert_permille_to_masec(bi);
+ bi->soc = bi->rm /36/bi->fcc_aging;
+ bi->init_cap = false;
+
+ //pr_err("[rt5025] i=%d, delta_x=%d, delta_y=%d, slope=%d, const_term=%d\n", i, delta_x, delta_y, slope, const_term);
+ RTINFO("voltage=%d, permille=%d, soc=%d, rm=%d\n", bi->vcell, bi->permille, bi->soc, bi->rm);
+ return;
+}
+
+static void rt5025_smooth_soc(struct rt5025_battery_info *bi)
+{
+ if ((bi->internal_status == POWER_SUPPLY_STATUS_CHARGING) &&
+ (bi->soc < 100)){
+ bi->soc++;
+ }else if ((bi->internal_status == POWER_SUPPLY_STATUS_DISCHARGING) &&
+ (bi->soc > 0)){
+ bi->soc--;
+ }else{
+ bi->smooth_flag = false;
+ bi->update_time = NORMAL_POLL;
+ }
+}
+
+
+static void rt5025_soc_irreversible(struct rt5025_battery_info *bi)
+{
+ // Prevent inverse
+ //if (!bi->init_cap){
+ if (!bi->init_once){
+ if (bi->internal_status == POWER_SUPPLY_STATUS_CHARGING){
+ if (bi->soc < bi->pre_soc)
+ bi->soc = bi->pre_soc;
+ }else if (bi->internal_status == POWER_SUPPLY_STATUS_DISCHARGING){
+ if (bi->soc > bi->pre_soc)
+ bi->soc = bi->pre_soc;
+ }
+ }
+ else
+ bi->init_once = false;
+
+ bi->pre_soc = bi->soc;
+}
+
+static void rt5025_soc_lock(struct rt5025_battery_info *bi)
+{
+ // lock 99%
+ RTINFO("internal status=%d, tp_flag=%d, soc=%d\n", bi->internal_status, bi->tp_flag, bi->soc);
+ if ((bi->soc >= 99) && (bi->internal_status == POWER_SUPPLY_STATUS_CHARGING))
+ {
+ if (bi->tp_flag)
+ bi->soc = 100;
+ else
+ {
+ if (bi->status == POWER_SUPPLY_STATUS_FULL)
+ bi->soc = 100;
+ else
+ bi->soc = 99;
+ }
+ }
+ else if ((bi->soc < 99) && (bi->internal_status == POWER_SUPPLY_STATUS_CHARGING) && \
+ (bi->tp_flag))
+ {
+ bi->update_time = SMOOTH_POLL;
+ bi->smooth_flag = true;
+ rt5025_smooth_soc(bi);
+ }else
+ {
+ bi->tp_flag = false;
+ }
+
+ // lock 1%
+ if ((bi->soc <= 1) &&
+ (bi->internal_status == POWER_SUPPLY_STATUS_DISCHARGING)){
+ if (bi->edv_flag)
+ bi->soc = 0;
+ else
+ bi->soc = 1;
+ }else if ((bi->soc > 1) &&
+ (bi->internal_status == POWER_SUPPLY_STATUS_DISCHARGING) &&
+ (bi->edv_flag)){
+ bi->update_time = SMOOTH_POLL;
+ bi->smooth_flag = true;
+ rt5025_smooth_soc(bi);
+ }else{
+ bi->edv_flag = false;
+ }
+}
+
+static void rt5025_get_soc(struct rt5025_battery_info *bi)
+{
+ if (bi->smooth_flag){
+ bi->smooth_flag = false;
+ bi->update_time = NORMAL_POLL;
+ }
+ RTINFO("before rm=%d\n", bi->rm);
+ if ((!bi->tp_flag) &&
+ (!bi->edv_flag)){
+ bi->rm = (bi->rm + bi->chg_cc) > bi->dchg_cc ?
+ bi->rm + bi->chg_cc - bi->dchg_cc : 0;
+ if (bi->rm > (bi->fcc * 3600))
+ bi->rm = bi->fcc * 3600;
+ rt5025_convert_masec_to_permille(bi);
+ bi->soc = DIV_ROUND_UP(bi->permille, 10);
+#if RT5025_CSV
+ bi->temp_soc = bi->soc;
+ //pr_info("%d", bi->soc);
+#else
+ RTINFO("after rm=%d\n", bi->rm);
+ RTINFO("temp_soc=%d\n", bi->soc);
+#endif
+ }
+#if RT5025_CSV
+ // pr_info("%d,%d,%d,%d,%d", bi->soc,bi->permille,bi->rm,bi->fcc,bi->smooth_flag);
+#else
+ RTINFO("soc=%d, permille=%d, rm=%d, fcc=%d, smooth_flag=%d\n", bi->soc,bi->permille,bi->rm,bi->fcc,bi->smooth_flag);
+#endif
+ return;
+}
+
+static void rt5025_soc_relearn_check(struct rt5025_battery_info *bi)
+{
+ // TP relearn
+/* if ((bi->vcell >= TP_VOLT) &&
+ (bi->curr <= TP_CURR) &&
+ (bi->status == POWER_SUPPLY_STATUS_CHARGING) &&
+ (!bi->tp_flag)){
+ bi->tp_cnt++;
+ bi->update_time = TP_POLL;
+ }else {
+ bi->tp_cnt = 0;
+ bi->update_time = NORMAL_POLL;
+ }
+
+ if (bi->tp_cnt == TP_TOTAL_COUNT){
+ bi->tp_cnt = 0;
+ bi->tp_flag = true;
+ bi->rm = bi->fcc * 3600;
+ rt5025_convert_masec_to_permille();
+ bi->update_time = NORMAL_POLL;
+ }*/
+
+ // if EOC happened, the tp_flag should be set 1.
+ if(bi->tp_flag == true)
+ {
+ bi->rm = bi->fcc * 3600;
+ rt5025_convert_masec_to_permille(bi);
+ bi->update_time = NORMAL_POLL;
+ }
+
+ if(rt5025_battery_param2[4].x < bi->vcell && bi->vcell <= rt5025_battery_param2[4].x+300)
+ {
+ bi->update_time = EDV_POLL;
+ bi->edv_detection = true;
+ }
+ else if((bi->vcell >= rt5025_battery_param2[4].x + 300 +EDV_HYS) && (bi->edv_detection == true))
+ {
+ bi->update_time = NORMAL_POLL;
+ bi->edv_detection = false;
+ }
+ else if((bi->vcell <= rt5025_battery_param2[4].x) && (bi->min_volt2_alert == true))
+ {
+ bi->edv_flag = true;
+ bi->rm = 0;
+ rt5025_convert_masec_to_permille(bi);
+ bi->edv_detection = false;
+ bi->update_time = NORMAL_POLL;
+ }
+ else if((bi->vcell > rt5025_battery_param2[4].x + EDV_HYS) && (bi->min_volt2_alert == true))
+ {
+ bi->min_volt2_alert = false;
+ }
+
+ /*// EDV relearn
+ if (bi->vcell <= EDV_VOLT){
+ if ((bi->curr <= EDV_CURR) ||
+ (bi->vcell <= rt5025_battery_param2[4].x))
+ bi->edv_cnt++;
+ else
+ {
+ bi->edv_cnt = 0;
+ }
+ bi->edv_detection = true;
+ bi->update_time = EDV_POLL;
+ }else if ((bi->vcell > (EDV_VOLT + EDV_HYS)) &&
+ (bi->edv_detection)) {
+ bi->edv_cnt = 0;
+ bi->edv_detection = false;
+ bi->edv_flag = false;
+ bi->update_time = NORMAL_POLL;
+ }
+ else
+ {
+ bi->edv_cnt = 0;
+ }
+
+ if (bi->edv_cnt == EDV_TOTAL_COUNT){
+ bi->edv_cnt = 0;
+ bi->edv_flag = true;
+ bi->rm = 0;
+ rt5025_convert_masec_to_permille();
+ bi->edv_detection = false;
+ bi->update_time = NORMAL_POLL;
+ }
+ if(bi->edv_detection)
+ {
+ bi->update_time = EDV_POLL;
+ }*/
+ if (bi->internal_status == POWER_SUPPLY_STATUS_CHARGING)
+ bi->edv_flag = false;
+ else if (bi->internal_status == POWER_SUPPLY_STATUS_DISCHARGING)
+ bi->tp_flag = false;
+
+#if RT5025_CSV
+ //pr_err("%d,%d,%d,%d,%d",
+ // bi->tp_cnt,bi->tp_flag,bi->edv_detection,bi->edv_cnt,bi->edv_flag);
+#else
+ RTINFO("tp_cnt=%d, tp_flag=%d, edv_detection=%d, edv_cnt=%d, edv_flag=%d\n",
+ bi->tp_cnt,bi->tp_flag,bi->edv_detection,bi->edv_cnt,bi->edv_flag);
+#endif
+
+ return;
+}
+
+static void rt5025_channel_cc(struct rt5025_battery_info *bi, bool enable)
+{
+ u8 data[1];
+
+ if (rt5025_read_reg(bi->client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){
+ printk(KERN_INFO "%s: failed to read channel\n", __func__);
+ }
+
+ if (enable){
+ data[0] = data[0] | 0x80;
+ }else {
+ data[0] = data[0] & 0x7F;
+ }
+
+ if (rt5025_write_reg(bi->client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){
+ printk(KERN_INFO "%s: failed to write channel\n", __func__);
+ }
+}
+
+#if 0
+static void rt5025_pretrim(struct rt5025_battery_info *bi)
+{
+ u8 data0[2];
+ u8 data1[1];
+
+ data0[0] = 0x55;
+ data0[1] = 0xAA;
+ if (rt5025_write_reg(bi->client, 0xF0, data0, 2) < 0){
+ printk(KERN_ERR "%s: failed to write channel\n", __func__);
+ }
+ data0[0] = 0x07;
+ if (rt5025_write_reg(bi->client, 0xF1, data0, 1) < 0){
+ printk(KERN_ERR "%s: failed to write channel\n", __func__);
+ }
+ // write trim data D0
+ data0[0] = 0xDE;
+ if (rt5025_write_reg(bi->client, 0xD0, data0, 1) < 0){
+ printk(KERN_ERR "%s: failed to write channel\n", __func__);
+ }
+ // Read back to verify
+ if (rt5025_read_reg(bi->client, 0xD0, data1, 1) < 0){
+ printk(KERN_ERR "%s: failed to read channel\n", __func__);
+ }
+ if (data1[0] != data0[0])
+ printk(KERN_ERR "%s: 0xD0 write fail\n", __func__);
+ // write trim data D1
+ data0[0] = 0x01;
+ if (rt5025_write_reg(bi->client, 0xD1, data0, 1) < 0){
+ printk(KERN_ERR "%s: failed to write channel\n", __func__);
+ }
+ // Read back to verify
+ if (rt5025_read_reg(bi->client, 0xD1, data1, 1) < 0){
+ printk(KERN_ERR "%s: failed to read channel\n", __func__);
+ }
+ if (data1[0] != data0[0])
+ printk(KERN_ERR "%s: 0xD1 write fail\n", __func__);
+ // write trim data D2
+ data0[0] = 0x10;
+ if (rt5025_write_reg(bi->client, 0xD2, data0, 1) < 0){
+ printk(KERN_ERR "%s: failed to write channel\n", __func__);
+ }
+ // Read back to verify
+ if (rt5025_read_reg(bi->client, 0xD2, data1, 1) < 0){
+ printk(KERN_ERR "%s: failed to read channel\n", __func__);
+ }
+ if(data1[0] != data0[0])
+ printk(KERN_ERR "%s: 0xD2 write fail\n", __func__);
+ // write trim data D3
+ data0[0] = 0x89;
+ if (rt5025_write_reg(bi->client, 0xD3, data0, 1) < 0){
+ printk(KERN_ERR "%s: failed to write channel\n", __func__);
+ }
+ // Read back to verify
+ if (rt5025_read_reg(bi->client, 0xD3, data1, 1) < 0){
+ printk(KERN_ERR "%s: failed to read channel\n", __func__);
+ }
+ if(data1[0] != data0[0])
+ printk(KERN_ERR "%s: 0xD3 write fail\n", __func__);
+ // write trim data D4
+ data0[0] = 0xF2;
+ if (rt5025_write_reg(bi->client, 0xD4, data0, 1) < 0){
+ printk(KERN_ERR "%s: failed to write channel\n", __func__);
+ }
+ // Read back to verify
+ if (rt5025_read_reg(bi->client, 0xD4, data1, 1) < 0){
+ printk(KERN_ERR "%s: failed to read channel\n", __func__);
+ }
+ if (data1[0] != data0[0])
+ printk(KERN_ERR "%s: 0xD4 write fail\n", __func__);
+
+ data0[0] = 0x55;
+ data0[1] = 0x55;
+ if (rt5025_write_reg(bi->client, 0xF0, data0, 2) < 0){
+ printk(KERN_ERR "%s: failed to write channel\n", __func__);
+ }
+}
+#endif
+
+static int rt5025_battery_parameter_backup(struct rt5025_battery_info *bi)
+{
+ u8 data[4];
+ RTINFO("\n");
+ //backup fcc_aging, rm, cycle_count, acc_dchg_cap
+ //fcc_aging
+ data[0] = (bi->fcc_aging>>8)&0xff;
+ data[1] = (bi->fcc_aging)&0xff;
+ rt5025_write_reg(bi->client, 0x21, data, 2);
+ //rm
+ data[0] = (bi->rm>>24)&0xff;
+ data[1] = (bi->rm>>16)&0xff;
+ data[2] = (bi->rm>>8)&0xff;
+ data[3] = (bi->rm)&0xff;
+ rt5025_write_reg(bi->client, 0x23, data, 4);
+ //acc_dchg_cap
+ data[0] = (bi->acc_dchg_cap>>24)&0xff;
+ data[1] = (bi->acc_dchg_cap>>16)&0xff;
+ data[2] = (bi->acc_dchg_cap>>8)&0xff;
+ data[3] = (bi->acc_dchg_cap)&0xff;
+ rt5025_write_reg(bi->client, 0x27, data, 4);
+ //cycle_count
+ data[0] = (bi->cycle_cnt)&0xff;
+ rt5025_write_reg(bi->client, 0x2B, data, 1);
+ return 0;
+}
+
+static int rt5025_battery_parameter_restore(struct rt5025_battery_info *bi)
+{
+ u8 data[4];
+ RTINFO("\n");
+ //restore fcc_aging, rm ,cycle_count, acc_dchg_cap
+ //fcc_aging
+ rt5025_read_reg(bi->client, 0x21, data, 2);
+ bi->fcc = bi->fcc_aging = data[0]<<8 | data[1];
+ //rm
+ rt5025_read_reg(bi->client, 0x23, data, 4);
+ bi->rm = data[0]<<24 | data[1]<<16 | data[2]<<8 | data[3];
+ //acc_dchg_cap
+ rt5025_read_reg(bi->client, 0x27, data, 4);
+ bi->acc_dchg_cap = data[0]<<24 | data[1]<<16 | data[2]<<8 | data[3];
+ //cycle_count
+ rt5025_read_reg(bi->client, 0x2B, data, 1);
+ bi->cycle_cnt = data[0];
+
+ return 0;
+}
+
+
+// return value; 1-> initialized, 0-> no initial value
+static int rt5025_battery_parameter_initcheck(struct rt5025_battery_info *bi)
+{
+ u8 data[2];
+ u16 value;
+ int ret = 0;
+
+ if (rt5025_read_reg(bi->client, 0x21, data, 2) < 0)
+ {
+ pr_err("%s: check initial value error\n", __func__);
+ }
+ else
+ {
+ value = data[1]<<8 | data[0];
+ if (value)
+ ret = 1;
+ }
+ RTINFO("initial check = %d\n", ret);
+
+ return ret;
+}
+
+static void rt5025_register_init(struct rt5025_battery_info *bi)
+{
+ u8 data[1];
+
+ /* enable the channel of current,qc,ain,vbat and vadc */
+ if (rt5025_read_reg(bi->client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){
+ pr_err("%s: failed to read channel\n", __func__);
+ }
+ data[0] = data[0] | CHANNEL_L_BIT_CADC_EN | CHANNEL_L_BIT_AINCH | \
+ CHANNEL_L_BIT_VBATSCH | CHANNEL_L_BIT_VADC_EN;
+ if (rt5025_write_reg(bi->client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){
+ pr_err("%s: failed to write channel\n", __func__);
+ }
+ /* set the alert threshold value */
+ irq_thres[MINVOLT2] = VALRTMIN2_VALUE;
+ irq_thres[VOLT_RLS] = VRLS_VALUE;
+
+ bi->chg_cc_unuse = 0;
+ bi->dchg_cc_unuse = 0;
+ bi->pre_gauge_timer = 0;
+ bi->online = 1;
+ bi->status = bi->internal_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ bi->health = POWER_SUPPLY_HEALTH_GOOD;
+
+ bi->init_cap = true;
+ bi->avg_flag = true;
+
+ bi->fcc_aging = rt5025_battery_param2[4].y;
+ bi->fcc = rt5025_battery_param2[4].y;
+ bi->dc = rt5025_battery_param2[4].y;
+ bi->rm = 0;
+
+ bi->edv_cnt = 0;
+ bi->edv_flag = false;
+ bi->edv_detection = false;
+ bi->init_once = true;
+
+ bi->tp_cnt = 0;
+ bi->tp_flag = false;
+
+ bi->acc_dchg_cap = 0;
+ bi->cycle_cnt = 0;
+
+ // if has initial data, rewrite to the stored data
+ if (rt5025_battery_parameter_initcheck(bi))
+ {
+ bi->init_cap = false;
+ rt5025_battery_parameter_restore(bi);
+ bi->soc = bi->rm/36/bi->fcc_aging;
+ }
+
+ bi->update_time = NORMAL_POLL;
+ bi->device_suspend = false;
+ RTINFO("register initialized\n");
+}
+
+static void rt5025_soc_aging(struct rt5025_battery_info *bi)
+{
+ if (bi->cycle_cnt >= rt5025_battery_param2[3].x)
+ {
+ bi->fcc_aging = bi->fcc_aging*(1000-rt5025_battery_param2[3].y)/1000;
+ bi->rm = bi->rm*(1000-rt5025_battery_param2[3].y)/1000;
+ bi->cycle_cnt -= rt5025_battery_param2[3].x;
+ }
+ RTINFO("fcc_aging=%d, rm=%d, cycle_cnt=%d\n", bi->fcc_aging, bi->rm, bi->cycle_cnt);
+}
+
+static void rt5025_temp_comp(struct rt5025_battery_info *bi)
+{
+ int i = 1;
+ int size;
+ int slope, const_term;
+ int delta_y, delta_x;
+
+ size = 3;
+ while((bi->ext_temp < rt5025_battery_param2[i].x) &&
+ (i < (size - 1))){
+ i++;
+ }
+
+ delta_x = rt5025_battery_param2[i-1].x - rt5025_battery_param2[i].x;
+ delta_y = (rt5025_battery_param2[i-1].y - rt5025_battery_param2[i].y);
+
+ slope = delta_y * 1000 / delta_x;
+
+ const_term = (rt5025_battery_param2[i].y) - ((rt5025_battery_param2[i].x * slope) / 1000);
+
+ if (bi->ext_temp >= rt5025_battery_param2[0].x)
+ bi->tempcmp = rt5025_battery_param2[0].y;
+ else if (bi->ext_temp <= rt5025_battery_param2[size-1].x)
+ bi->tempcmp = rt5025_battery_param2[size-1].y;
+ else
+ bi->tempcmp = (bi->ext_temp * slope) / 1000 + const_term;
+
+ bi->fcc = bi->fcc_aging + bi->fcc_aging * bi->tempcmp /1000;
+ if (bi->fcc >= (bi->dc*3>>1))
+ bi->fcc = bi->dc*3>>1;
+ if (bi->fcc <= (bi->dc>>1))
+ bi->fcc = bi->dc>>1;
+ bi->rm = bi->fcc * bi->soc * 36;
+ //pr_err("[rt5025] i=%d, delta_x=%d, delta_y=%d, slope=%d, const_term=%d\n", i, delta_x, delta_y, slope, const_term);
+ RTINFO("tempcmp=%d, ext_temp=%d, fcc=%d, rm=%d\n", bi->tempcmp, bi->ext_temp, bi->fcc, bi->rm);
+ return;
+}
+
+static void rt5025_soc_temp_comp(struct rt5025_battery_info *bi)
+{
+ RTINFO("soc->%d++\n", bi->soc);
+ bi->temp_range_0_5 = false;
+ bi->temp_range_5_10 = false;
+ bi->temp_range_10_15 = false;
+ bi->temp_range_15_20 = false;
+ bi->temp_range_20_30 = false;
+ bi->temp_range_30_35 = false;
+ bi->temp_range_35_40 = false;
+ bi->temp_range_40_45 = false;
+ bi->temp_range_45_50 = false;
+
+ if (bi->ext_temp < 50)
+ bi->temp_range_0_5 = true;
+ else if (50 <= bi->ext_temp && bi->ext_temp < 100)
+ bi->temp_range_5_10 = true;
+ else if (100 <= bi->ext_temp && bi->ext_temp < 150)
+ bi->temp_range_10_15 = true;
+ else if (150 <= bi->ext_temp && bi->ext_temp < 200)
+ bi->temp_range_15_20 = true;
+ else if (200 <= bi->ext_temp && bi->ext_temp <= 300)
+ bi->temp_range_20_30 = true;
+ else if (300 < bi->ext_temp && bi->ext_temp <= 350)
+ bi->temp_range_30_35 = true;
+ else if (350 < bi->ext_temp && bi->ext_temp <= 400)
+ bi->temp_range_35_40 = true;
+ else if (400 < bi->ext_temp && bi->ext_temp <= 450)
+ bi->temp_range_40_45 = true;
+ else if (450 < bi->ext_temp)
+ bi->temp_range_45_50 = true;
+
+ if((bi->temp_range_0_5 == true) && (bi->range_0_5_done == false))
+ {
+ rt5025_temp_comp(bi);
+ bi->range_0_5_done = true;
+ bi->range_5_10_done = false;
+ bi->range_10_15_done = false;
+ bi->range_15_20_done = false;
+ bi->range_20_30_done = false;
+ bi->range_30_35_done = false;
+ bi->range_35_40_done = false;
+ bi->range_40_45_done = false;
+ bi->range_45_50_done = false;
+ }
+ else if((bi->temp_range_5_10 == true) && (bi->range_5_10_done == false))
+ {
+ rt5025_temp_comp(bi);
+ bi->range_0_5_done = false;
+ bi->range_5_10_done = true;
+ bi->range_10_15_done = false;
+ bi->range_15_20_done = false;
+ bi->range_20_30_done = false;
+ bi->range_30_35_done = false;
+ bi->range_35_40_done = false;
+ bi->range_40_45_done = false;
+ bi->range_45_50_done = false;
+ }
+ else if((bi->temp_range_10_15 == true) && (bi->range_10_15_done == false))
+ {
+ rt5025_temp_comp(bi);
+ bi->range_0_5_done = false;
+ bi->range_5_10_done = false;
+ bi->range_10_15_done = true;
+ bi->range_15_20_done = false;
+ bi->range_20_30_done = false;
+ bi->range_30_35_done = false;
+ bi->range_35_40_done = false;
+ bi->range_40_45_done = false;
+ bi->range_45_50_done = false;
+ }
+ else if((bi->temp_range_15_20 == true) && (bi->range_15_20_done == false))
+ {
+ rt5025_temp_comp(bi);
+ bi->range_0_5_done = false;
+ bi->range_5_10_done = false;
+ bi->range_10_15_done = false;
+ bi->range_15_20_done = true;
+ bi->range_20_30_done = false;
+ bi->range_30_35_done = false;
+ bi->range_35_40_done = false;
+ bi->range_40_45_done = false;
+ bi->range_45_50_done = false;
+ }
+ else if((bi->temp_range_20_30 == true) && (bi->range_20_30_done == false))
+ {
+ bi->fcc = bi->fcc_aging;
+ bi->rm = bi->fcc*bi->soc*36;
+ bi->range_0_5_done = false;
+ bi->range_5_10_done = false;
+ bi->range_10_15_done = false;
+ bi->range_15_20_done = false;
+ bi->range_20_30_done = true;
+ bi->range_30_35_done = false;
+ bi->range_35_40_done = false;
+ bi->range_40_45_done = false;
+ bi->range_45_50_done = false;
+ }
+ else if((bi->temp_range_30_35 == true) && (bi->range_30_35_done == false))
+ {
+ rt5025_temp_comp(bi);
+ bi->range_0_5_done = false;
+ bi->range_5_10_done = false;
+ bi->range_10_15_done = false;
+ bi->range_15_20_done = false;
+ bi->range_20_30_done = false;
+ bi->range_30_35_done = true;
+ bi->range_35_40_done = false;
+ bi->range_40_45_done = false;
+ bi->range_45_50_done = false;
+ }
+ else if((bi->temp_range_35_40 == true) && (bi->range_35_40_done == false))
+ {
+ rt5025_temp_comp(bi);
+ bi->range_0_5_done = false;
+ bi->range_5_10_done = false;
+ bi->range_10_15_done = false;
+ bi->range_15_20_done = false;
+ bi->range_20_30_done = false;
+ bi->range_30_35_done = false;
+ bi->range_35_40_done = true;
+ bi->range_40_45_done = false;
+ bi->range_45_50_done = false;
+ }
+ else if((bi->temp_range_40_45 == true) && (bi->range_40_45_done == false))
+ {
+ rt5025_temp_comp(bi);
+ bi->range_0_5_done = false;
+ bi->range_5_10_done = false;
+ bi->range_10_15_done = false;
+ bi->range_15_20_done = false;
+ bi->range_20_30_done = false;
+ bi->range_30_35_done = false;
+ bi->range_35_40_done = false;
+ bi->range_40_45_done = true;
+ bi->range_45_50_done = false;
+ }
+ else if((bi->temp_range_45_50 == true) && (bi->range_45_50_done == false))
+ {
+ rt5025_temp_comp(bi);
+ bi->range_0_5_done = false;
+ bi->range_5_10_done = false;
+ bi->range_10_15_done = false;
+ bi->range_15_20_done = false;
+ bi->range_20_30_done = false;
+ bi->range_30_35_done = false;
+ bi->range_35_40_done = false;
+ bi->range_40_45_done = false;
+ bi->range_45_50_done = true;
+ }
+ RTINFO("soc->%d--\n", bi->soc);
+}
+
+static void rt5025_update(struct rt5025_battery_info *bi)
+{
+ /* Update voltage */
+ rt5025_get_vcell(bi);
+ /* Update current */
+ rt5025_get_current(bi);
+
+ /* Update external temperature */
+ rt5025_get_external_temp(bi);
+ /* Read timer */
+ rt5025_get_timer(bi);
+ /* Update chg cc */
+ rt5025_get_chg_cc(bi);
+ /* Update dchg cc */
+ rt5025_get_dchg_cc(bi);
+ /* Update cycle count check */
+ rt5025_cycle_count(bi);
+ /* Calculate cycle count */
+ rt5025_soc_aging(bi);
+ /* calculate initial soc */
+ if (bi->init_cap){
+ rt5025_init_capacity(bi);
+#if RT5025_CSV
+ pr_info("vcell,offset,current,timer,interval,QCHG,QDCHG,tp_cnt,tp_flag,edv_det,edv_cnt,edv_flag,soc,permille,RM,FCC,smooth_flag,acc_QD,cycle,update_time\n");
+#endif
+ }
+
+ /* Relearn SOC */
+ rt5025_soc_relearn_check(bi);
+ /* SOC_Temp_Comp*/
+ rt5025_soc_temp_comp(bi);
+ /* Update SOC */
+ rt5025_get_soc(bi);
+
+ /* SOC Control Process */
+ rt5025_soc_lock(bi);
+ rt5025_soc_irreversible(bi);
+
+ /* Update RTTF or RTTE */
+ //rt5025_run_time(bi);
+
+#if TEMPERATURE_ALERT
+ if ((bi->max_temp_irq == false) &&
+ (((irq_thres[MAXTEMP] * IRQ_THRES_UNIT) / 100 - bi->ain_volt) > irq_thres[TEMP_RLS])){
+ rt5025_alert_setting(bi,MAXTEMP,true);
+ }else if ((bi->min_temp_irq == false) &&
+ ((bi->ain_volt - (irq_thres[MINTEMP] * IRQ_THRES_UNIT) / 100) > irq_thres[TEMP_RLS])){
+ rt5025_alert_setting(bi,MINTEMP,true);
+ }
+#endif
+#if 0
+ }else if ((bi->min_volt1_irq == false) &&
+ ((bi->vcell - ((irq_thres[MINVOLT1] * IRQ_THRES_UNIT) / 100)) > irq_thres[VOLT_RLS])){
+ rt5025_alert_setting(bi,MINVOLT1,true);
+ }else if ((bi->min_volt2_irq == false) &&
+ ((bi->vcell - ((irq_thres[MINVOLT2] * IRQ_THRES_UNIT) / 100)) > irq_thres[VOLT_RLS])){
+ rt5025_alert_setting(bi,MINVOLT2,true);
+ }
+#endif
+
+#if VOLTAGE_ALERT
+ if ((bi->min_volt2_irq == false) &&
+ (bi->vcell > (rt5025_battery_param2[4].x + EDV_HYS))){
+ rt5025_alert_setting(bi,MINVOLT2,true);
+ }
+#endif
+#if RT5025_CSV
+ printk(KERN_INFO "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",bi->vcell,bi->curr_offset,bi->curr,bi->gauge_timer,bi->time_interval,bi->chg_cc,
+ bi->dchg_cc,bi->tp_cnt,bi->tp_flag,bi->edv_detection,bi->edv_cnt,bi->edv_flag,bi->soc,
+ bi->permille,bi->rm,bi->fcc,bi->smooth_flag,bi->acc_dchg_cap,bi->cycle_cnt,bi->update_time);
+#else
+ pr_info("[RT5025] update_time=%d\n",bi->update_time);
+ pr_info("\n");
+#endif
+}
+
+static void rt5025_update_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work = (struct delayed_work *)container_of(work, struct delayed_work, work);
+ struct rt5025_battery_info *bi = (struct rt5025_battery_info *)container_of(delayed_work, struct rt5025_battery_info, monitor_work);
+ unsigned long flags;
+
+ rt5025_update(bi);
+
+ /* prevent suspend before starting the alarm */
+ local_irq_save(flags);
+ bi->last_poll = alarm_get_elapsed_realtime();
+ rt5025_program_alarm(bi);
+ local_irq_restore(flags);
+
+ wake_unlock(&bi->monitor_wake_lock);
+}
+
+static enum power_supply_property rt5025_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ #if 0
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ #endif
+};
+
+static int rt5025_battery_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct rt5025_battery_info *bi = platform_get_drvdata(pdev);
+ //bi->last_event = ktime_get();
+ bi->last_event = current_kernel_time();
+
+ //cy add for battery parameter backup
+ rt5025_battery_parameter_backup(bi);
+
+ rt5025_channel_cc(bi, false);
+ cancel_delayed_work(&bi->monitor_work);
+ rt5025_get_timer(bi);
+ bi->device_suspend = true;
+ //bi->update_time = SUSPEND_POLL;
+ RTINFO("RM=%d\n",bi->rm);
+ return 0;
+}
+
+static int rt5025_battery_resume(struct platform_device *pdev)
+{
+ struct rt5025_battery_info *bi = platform_get_drvdata(pdev);
+ //ktime_t now;
+ //struct timespec now = current_kernel_time();
+ //struct timeval tv;
+ //long time_interval;
+
+ //now = ktime_get();
+ //tv = ktime_to_timeval(ktime_sub(now, bi->last_event));
+ //RTINFO("Sleep time = %d\n",(u32)tv.tv_sec);
+ //bi->rm = bi->rm - ((u32)tv.tv_sec * SLEEP_CURRENT);
+
+ //time_interval = now.tv_sec - bi->last_event.tv_sec;
+ //bi->rm = bi->rm - (time_interval * SLEEP_CURRENT);
+ //RTINFO("Sleep time=%d, RM=%d",(int)time_interval,bi->rm);
+
+ rt5025_channel_cc(bi, true);
+ wake_lock(&bi->monitor_wake_lock);
+ schedule_delayed_work(&bi->monitor_work, 0);
+ bi->device_suspend = false;
+ //RTINFO("\n");
+ return 0;
+}
+
+static int rt5025_battery_remove(struct platform_device *pdev)
+{
+ struct rt5025_battery_info *bi = platform_get_drvdata(pdev);
+
+ power_supply_unregister(&bi->battery);
+ cancel_delayed_work(&bi->monitor_work);
+ wake_lock_destroy(&bi->monitor_wake_lock);
+ kfree(bi);
+ return 0;
+}
+
+static int rt5025_battery_probe(struct platform_device *pdev)
+{
+ struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct rt5025_battery_info *bi;
+ int ret;
+
+ bi = kzalloc(sizeof(*bi), GFP_KERNEL);
+ if (!bi)
+ return -ENOMEM;
+
+ bi->client = chip->i2c;
+ bi->chip = chip;
+
+
+ bi->last_poll = alarm_get_elapsed_realtime();
+ alarm_init(&bi->wakeup_alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+ rt5025_gauge_alarm);
+
+ INIT_DELAYED_WORK(&bi->monitor_work, rt5025_update_work);
+
+ wake_lock_init(&bi->monitor_wake_lock, WAKE_LOCK_SUSPEND, "rt-battery-monitor");
+ wake_lock_init(&bi->low_battery_wake_lock, WAKE_LOCK_SUSPEND, "low_battery_wake_lock");
+#if RT5025_TEST_WAKE_LOCK
+ wake_lock_init(&bi->test_wake_lock, WAKE_LOCK_SUSPEND, "rt-test");
+#endif
+ /* Write trimed data */
+ //rt5025_pretrim(client);
+ /* enable channel */
+ rt5025_register_init(bi);
+ /* enable gauge IRQ */
+ rt5025_alert_init(bi);
+
+ /* register callback functions */
+ /*
+ chip->cb.rt5025_gauge_irq_handler = rt5025_irq_handler;
+ chip->cb.rt5025_gauge_set_status = rt5025_set_status;
+ chip->cb.rt5025_gauge_set_online = rt5025_set_online;
+ chip->cb.rt5025_gauge_suspend = rt5025_gauge_suspend;
+ chip->cb.rt5025_gauge_resume = rt5025_gauge_resume;
+ chip->cb.rt5025_gauge_remove = rt5025_gauge_remove;
+
+ rt5025_register_gauge_callbacks(&chip->cb);
+ */
+
+ platform_set_drvdata(pdev, bi);
+
+ bi->battery.name = "rt5025-battery";
+ bi->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ bi->battery.get_property = rt5025_get_property;
+ bi->battery.properties = rt5025_battery_props;
+ bi->battery.num_properties = ARRAY_SIZE(rt5025_battery_props);
+
+ ret = power_supply_register(&pdev->dev, &bi->battery);
+ if (ret) {
+ printk(KERN_ERR "[RT5025] power supply register failed\n");
+ goto err_wake_lock;
+ }
+
+
+ wake_lock(&bi->monitor_wake_lock);
+#if RT5025_TEST_WAKE_LOCK
+ wake_lock(&bi->test_wake_lock);
+#endif
+ schedule_delayed_work(&bi->monitor_work, msecs_to_jiffies(INIT_POLL*MSEC_PER_SEC));
+ chip->battery_info = bi;
+
+ pr_info("rt5025-battery driver is successfully loaded\n");
+
+ return 0;
+
+err_wake_lock:
+ wake_lock_destroy(&bi->monitor_wake_lock);
+ kfree(bi);
+
+ return ret;
+}
+
+static void rt5025_battery_shutdown(struct platform_device *pdev)
+{
+ struct rt5025_battery_info *bi = platform_get_drvdata(pdev);
+
+ RTINFO("\n");
+ rt5025_battery_parameter_backup(bi);
+}
+
+static struct platform_driver rt5025_battery_driver =
+{
+ .driver = {
+ .name = RT5025_DEVICE_NAME "-battery",
+ .owner = THIS_MODULE,
+ },
+ .probe = rt5025_battery_probe,
+ .remove = __devexit_p(rt5025_battery_remove),
+ .shutdown = rt5025_battery_shutdown,
+ .suspend = rt5025_battery_suspend,
+ .resume = rt5025_battery_resume,
+};
+
+static int __init rt5025_battery_init(void)
+{
+ return platform_driver_register(&rt5025_battery_driver);
+}
+module_init(rt5025_battery_init);
+
+static void __exit rt5025_battery_exit(void)
+{
+ platform_driver_unregister(&rt5025_battery_driver);
+}
+module_exit(rt5025_battery_exit);
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Nick Hung <nick_hung@richtek.com");
+MODULE_DESCRIPTION("battery gauge driver for RT5025");
+MODULE_ALIAS("platform:" RT5025_DEVICE_NAME "-battery");
+++ /dev/null
-/*
- * rt5025_gauge.c
- * fuel-gauge driver
- * revision 0.1
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/slab.h>
-#include <linux/wakelock.h>
-#include <linux/workqueue.h>
-#include <linux/jiffies.h>
-#include <linux/timer.h>
-#include <linux/android_alarm.h>
-#include <linux/mfd/rt5025.h>
-#include <linux/power/rt5025-gauge.h>
-
-#define RT5025_REG_IRQ_CTL 0x50
-#define RT5025_REG_IRQ_FLAG 0x51
-#define RT5025_REG_VALRT_MAXTH 0x53
-#define RT5025_REG_VALRT_MIN1TH 0x54
-#define RT5025_REG_VALRT_MIN2TH 0x55
-#define RT5025_REG_TALRT_MAXTH 0x56
-#define RT5025_REG_TALRT_MINTH 0x57
-#define RT5025_REG_VCELL_MSB 0x58
-#define RT5025_REG_VCELL_LSB 0x59
-#define RT5025_REG_INT_TEMPERATUE_MSB 0x5B
-#define RT5025_REG_INT_TEMPERATUE_LSB 0x5C
-#define RT5025_REG_EXT_TEMPERATUE_MSB 0x5E
-#define RT5025_REG_EXT_TEMPERATUE_LSB 0x5F
-#define RT5025_REG_TIMER 0x60
-#define RT5025_REG_CHANNEL_MSB 0x62
-#define RT5025_REG_CHANNEL_LSB 0x63
-#define RT5025_REG_CURRENT_MSB 0x76
-#define RT5025_REG_CURRENT_LSB 0x77
-#define RT5025_REG_QCHGH_MSB 0x78
-#define RT5025_REG_QCHGH_LSB 0x79
-#define RT5025_REG_QCHGL_MSB 0x7A
-#define RT5025_REG_QCHGL_LSB 0x7B
-#define RT5025_REG_QDCHGH_MSB 0x7C
-#define RT5025_REG_QDCHGH_LSB 0x7D
-#define RT5025_REG_QDCHGL_MSB 0x7E
-#define RT5025_REG_QDCHGL_LSB 0x7F
-
-#define IRQ_CTL_BIT_TMX (1 << 5)
-#define IRQ_CTL_BIT_TMN (1 << 4)
-#define IRQ_CTL_BIT_VMX (1 << 2)
-#define IRQ_CTL_BIT_VMN1 (1 << 1)
-#define IRQ_CTL_BIT_VMN2 (1 << 0)
-
-#define IRQ_FLG_BIT_TMX (1 << 5)
-#define IRQ_FLG_BIT_TMN (1 << 4)
-#define IRQ_FLG_BIT_VMX (1 << 2)
-#define IRQ_FLG_BIT_VMN1 (1 << 1)
-#define IRQ_FLG_BIT_VMN2 (1 << 0)
-
-#define CHANNEL_H_BIT_CLRQDCHG (1 << 7)
-#define CHANNEL_H_BIT_CLRQCHG (1 << 6)
-
-#define CHANNEL_L_BIT_CADC_EN (1 << 7)
-#define CHANNEL_L_BIT_INTEMPCH (1 << 6)
-#define CHANNEL_L_BIT_AINCH (1 << 2)
-#define CHANNEL_L_BIT_VBATSCH (1 << 1)
-#define CHANNEL_L_BIT_VADC_EN (1 << 0)
-
-#define NORMAL_POLL 10 /* 10 sec */
-#define SUSPEND_POLL (30*60) /* 30 min */
-
-#define HIGH_TEMP_THRES 650
-#define HIGH_TEMP_RECOVER 430
-#define LOW_TEMP_THRES (-30)
-#define LOW_TEMP_RECOVER 0
-#define TEMP_ABNORMAL_COUNT 3
-
-#define TALRTMAX_VALUE 0x38 //65.39'C 0x9
-#define TALRTMIN_VALUE 0x11 //-18.75'C 0x17
-#define VALRTMAX_VALUE 0xDC //4297mV
-#define VALRTMIN1_VALUE 0xB8 //3600mV
-#define VALRTMIN2_VALUE 0x99 //3000mV
-#define TRLS_VALUE 55 //5'C
-#define VRLS_VALUE 100 //100mV
-
-#define IRQ_THRES_UNIT 1953
-
-struct rt5025_gauge_chip {
- struct i2c_client *client;
- struct rt5025_power_info *info;
- struct rt5025_gauge_callbacks cb;
-
- struct power_supply battery;
-
- struct delayed_work monitor_work;
- struct wake_lock monitor_wake_lock;
- struct alarm wakeup_alarm;
-
- bool suspend_poll;
- ktime_t last_poll;
-
- /* battery voltage */
- u16 vcell;
- /* battery current */
- s16 curr;
- /* battery current offset */
- u8 curr_offset;
- /* AIN voltage */
- u16 ain_volt;
- /* battery external temperature */
- s16 ext_temp;
- /* charge coulomb counter */
- u32 chg_cc;
- u32 chg_cc_unuse;
- //u32 chg_cc_raw; // JY: May not necessary
- /* discharge coulomb counter */
- u32 dchg_cc;
- u32 dchg_cc_unuse;
- //u32 dchg_cc_raw; // JY: May not necessary
-
- // JY add variable
- bool soc_init;
- u32 rm;
- u32 rm_old;
- u8 soc_old;
- // ---------------
-
- u32 fcc;
- /* battery capacity */
- u8 soc;
- u32 soc_precise;
- u16 time_interval;
- u16 pre_gauge_timer;
-
- u8 online;
- u8 status;
- u8 health;
-
- /* IRQ flag */
- u8 irq_flag;
-
- /* max voltage IRQ flag */
- bool max_volt_irq;
- /* min voltage1 IRQ flag */
- bool min_volt1_irq;
- /* min voltage2 IRQ flag */
- bool min_volt2_irq;
- /* max temperature IRQ flag */
- bool max_temp_irq;
- /* min temperature IRQ flag */
- bool min_temp_irq;
-
- u8 temp_high_cnt;
- u8 temp_low_cnt;
- u8 temp_recover_cnt;
-};
-
-static u32 battery_vcell_table[] = {3000, 3418, 3598, 3650, 3679, 3722, 3766, 3790, 3826, 3914, 3973, 4046, 4130, 4190};
-static u32 battery_soc_table[] = {0, 2, 5, 7, 14, 24, 40, 49, 58, 71, 80, 89, 98, 100};
-
-struct rt5025_gauge_chip *chip;
-u8 irq_thres[LAST_TYPE];
-
-void rt5025_set_status(int status)
-{
- chip->status = status;
-}
-
-static int get_vcell_segment_index(u32* pX, size_t size, u32 x)
-{
- unsigned int i;
- if (x <= *pX)
- return 0;
- for (i=0; i<size; i++)
- {
- if (x<=*(pX+i))
- break;
- }
- #if 0 // for linear interpolation
- #else
- if (i>=(size-2))
- return size-3;
- #endif
- return i;
-}
-
-static u32 rt5025_vcell2soc(u32* pX, u32* pY, size_t size, u32 _x)
-{
- #if 0 // Linear interpolation
- int index;
- int x1, x2;
- int y, y1, y2;
- index = get_vcell_segment_index(pX, size, _x);
- if (_x<*pX)
- return 0;
- if (_x>=*(pX+size-1))
- return 100;
- if (_x == *(pX+index))
- return *(pY+index);
- x1 = *(pX+index-1);
- x2 = *(pX+index);
- y1 = *(pY+index-1);
- y2 = *(pY+index);
-
- y = (_x-x1)*(y2-y1)*100/(x2-x1);
- y /= 100;
- y += y1;
-
- return y;
- #else // Lagrange interpolation
- int index;
- int32_t x1, x2, x3;
- int32_t y1, y2, y3;
- int32_t a1, a2, a3, b1, b2, b3;
- int32_t x = _x;
- if (_x<*pX)
- return 0;
- if (_x>=*(pX+size-1))
- return 100;
- index = get_vcell_segment_index(pX, size, _x);
- pX+=index;
- pY+=index;
- x1 = *pX;
- x2 = *(pX+1);
- x3 = *(pX+2);
- y1 = *pY;
- y2 = *(pY+1);
- y3 = *(pY+2);
- if (x == x1)
- return y1;
- if (x == x2)
- return y2;
- if (x == x3)
- return y3;
- a1 = y1*(x-x2)*(x-x3);
- a2 = y2*(x-x1)*(x-x3);
- a3 = y3*(x-x1)*(x-x2);
- b1 = (x1-x2)*(x1-x3);
- b2 = (x2-x1)*(x2-x3);
- b3 = (x3-x1)*(x3-x2);
-
- return (100*a1/b1+100*a2/b2+100*a3/b3)/100;
- #endif
-}
-
-static int rt5025_read_reg(struct i2c_client *client,
- u8 reg, u8 *data, u8 len)
-{
- #if 1
- int ret;
-
- ret = rt5025_reg_block_read(client, reg, len, data);
- #else
- struct i2c_adapter *adap = client->adapter;
- struct i2c_msg msgs[2];
- int ret;
-
- msgs[0].addr = client->addr;
- msgs[0].flags = client->flags;
- msgs[0].len = 1;
- msgs[0].buf = ®
- msgs[0].scl_rate = 200*1000;
-
- msgs[1].addr = client->addr;
- msgs[1].flags = client->flags | I2C_M_RD;
- msgs[1].len = len;
- msgs[1].buf = data;
- msgs[1].scl_rate = 200*1000;
-
- ret = i2c_transfer(adap, msgs, 2);
- #endif
- return (ret == 2)? len : ret;
-}
-
-static int rt5025_write_reg(struct i2c_client *client,
- u8 reg, u8 *data, u8 len)
-{
- #if 1
- int ret;
-
- ret = rt5025_reg_block_write(client, reg, len, data);
- #else
- struct i2c_adapter *adap = client->adapter;
- struct i2c_msg msg;
-
- int ret;
- char* tx_buf = (char *)kmalloc(len + 1, GFP_KERNEL);
-
- if (!tx_buf)
- return -ENOMEM;
- tx_buf[0] = reg;
- memcpy(tx_buf+1, data, len);
-
- msg.addr = client->addr;
- msg.flags = client->flags;
- msg.len = len + 1;
- msg.buf = (char *)tx_buf;
- msg.scl_rate = 200*1000;
- ret = i2c_transfer(adap, &msg, 1);
- kfree(tx_buf);
-
- #endif
- return (ret == 1) ? len : ret;
-}
-
-static void rt5025_gauge_alarm(struct alarm *alarm)
-{
- pr_info("%s: alarmed \n", __func__);
- wake_lock(&chip->monitor_wake_lock);
- schedule_delayed_work(&chip->monitor_work, 0);
-}
-
-static void rt5025_program_alarm(int seconds)
-{
- ktime_t low_interval = ktime_set(seconds, 0);
- ktime_t slack = ktime_set(20, 0);
- ktime_t next;
-
- next = ktime_add(chip->last_poll, low_interval);
- alarm_start_range(&chip->wakeup_alarm, next, ktime_add(next, slack));
-}
-
-static int rt5025_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = chip->status;
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = chip->health;
- break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = chip->online;
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = chip->ext_temp;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = 1;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = chip->vcell * 1000; //uV
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = chip->curr * 1000; //uA
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = chip->soc;
- if (val->intval > 100)
- val->intval = 100;
- break;
- case POWER_SUPPLY_PROP_TECHNOLOGY:
- val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static void rt5025_get_vcell(struct i2c_client *client)
-{
- u8 data[2];
-
- if (rt5025_read_reg(client, RT5025_REG_VCELL_MSB, data, 2) < 0){
- printk(KERN_ERR "%s: Failed to read Voltage\n", __func__);
- }
-
- chip->vcell = ((data[0] << 8) + data[1]) * 61 / 100;
- chip->curr_offset = (15444 * chip->vcell - 27444000) / 10000;
-
- RTINFO("[RT5025] vcell: %d, offset: %d\n", chip->vcell, chip->curr_offset);
-}
-
-static void rt5025_get_current(struct i2c_client *client)
-{
- u8 data[2];
- u16 temp;
- int sign = 0;
-
- if (rt5025_read_reg(client, RT5025_REG_CURRENT_MSB, data, 2) < 0) {
- printk(KERN_ERR "%s: Failed to read CURRENT\n", __func__);
- }
-
- temp = (data[0]<<8) | data[1];
- if (data[0] & (1 << 7)) {
- sign = 1;
- temp = (((temp & 0x7FFF) * 3125) / 10 + chip->curr_offset) / 1000;
- }else
- temp = ((temp * 3125) / 10 - chip->curr_offset) / 1000;
-
- if (sign)
- temp *= -1;
-
- chip->curr = temp;
- RTINFO("[RT5025] current: %d\n", chip->curr);
-}
-
-static void rt5025_get_external_temp(struct i2c_client *client)
-{
- u8 data[2];
- long int temp;
-
- if (rt5025_read_reg(client, RT5025_REG_EXT_TEMPERATUE_MSB, data, 2) < 0) {
- printk(KERN_ERR "%s: Failed to read TEMPERATURE\n", __func__);
- }
- chip->ain_volt = (data[0] * 256 + data[1]) * 61 / 100;
- /// Check battery present
- if (chip->ain_volt < 1150)
- chip->online = true;
- else
- chip->online = false;
- temp = (chip->ain_volt * (-91738) + 81521000) / 100000;
- chip->ext_temp = (int)temp;
-
- if (chip->ext_temp >= HIGH_TEMP_THRES) {
- if (chip->health != POWER_SUPPLY_HEALTH_OVERHEAT)
- chip->temp_high_cnt++;
- } else if (chip->ext_temp <= HIGH_TEMP_RECOVER && chip->ext_temp >= LOW_TEMP_RECOVER) {
- if (chip->health == POWER_SUPPLY_HEALTH_OVERHEAT ||
- chip->health == POWER_SUPPLY_HEALTH_COLD)
- chip->temp_recover_cnt++;
- } else if (chip->ext_temp <= LOW_TEMP_THRES) {
- if (chip->health != POWER_SUPPLY_HEALTH_COLD)
- chip->temp_low_cnt++;
- } else {
- chip->temp_high_cnt = 0;
- chip->temp_low_cnt = 0;
- chip->temp_recover_cnt = 0;
- }
-
- if (chip->temp_high_cnt >= TEMP_ABNORMAL_COUNT) {
- chip->health = POWER_SUPPLY_HEALTH_OVERHEAT;
- chip->temp_high_cnt = 0;
- } else if (chip->temp_low_cnt >= TEMP_ABNORMAL_COUNT) {
- chip->health = POWER_SUPPLY_HEALTH_COLD;
- chip->temp_low_cnt = 0;
- } else if (chip->temp_recover_cnt >= TEMP_ABNORMAL_COUNT) {
- chip->health = POWER_SUPPLY_HEALTH_GOOD;
- chip->temp_recover_cnt = 0;
- }
- RTINFO("[RT5025] external temperature: %d\n", chip->ext_temp);
-}
-
-static void rt5025_clear_cc(operation_mode mode)
-{
- u8 data[2];
-
- if (rt5025_read_reg(chip->client, RT5025_REG_CHANNEL_MSB, data, 2) < 0){
- printk(KERN_ERR "%s: failed to read channel\n", __func__);
- }
-
- if (mode == CHG)
- data[0] = data[0] | CHANNEL_H_BIT_CLRQCHG;
- else
- data[0] = data[0] | CHANNEL_H_BIT_CLRQDCHG;
-
- if (rt5025_write_reg(chip->client, RT5025_REG_CHANNEL_MSB, data, 2) < 0){
- printk(KERN_ERR "%s: failed to write channel\n", __func__);
- }
-}
-
-static void rt5025_get_chg_cc(struct i2c_client *client)
-{
- u8 data[4];
- u32 qh_old,ql_old,qh_new,ql_new;
- u32 cc_masec,offset;
-
- if (rt5025_read_reg(client, RT5025_REG_QCHGH_MSB, data, 4) < 0){
- printk(KERN_ERR "%s: Failed to read QCHG\n", __func__);
- }
- qh_old = (data[0]<<8) + data[1];
- ql_old = (data[2]<<8) + data[3];
- //pr_info("%s qh_old %04x ql_old %04x\n", __func__, qh_old, ql_old);
-
- if (rt5025_read_reg(client, RT5025_REG_QCHGH_MSB, data, 4) < 0){
- printk(KERN_ERR "%s: Failed to read QCHG\n", __func__);
- }
- qh_new = (data[0]<<8) + data[1];
- ql_new = (data[2]<<8) + data[3];
- //pr_info("%s qh_new %04x ql_new %04x\n", __func__, qh_new, ql_new);
-
- #if 0
- if (qh_new > qh_old){
- cc_masec = qh_new*91266 + ((ql_new*22)>>4);
- }else if (qh_new == qh_old){
- if (ql_new >= ql_old){
- cc_masec = qh_new*91266 + ((ql_new*22)>>4);
- }else {
- cc_masec = qh_old*91266 + ((ql_old*22)>>4);
- }
- }
- #else
- if (qh_new > qh_old){
- cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10;
- }else if (qh_new == qh_old){
- if (ql_new >= ql_old){
- cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10;
- }else {
- cc_masec = (((qh_old<<16) + ql_old) * 50134) / 10;
- }
- }
- #endif
-
- offset = chip->curr_offset * chip->time_interval;
-
- if (cc_masec != 0){
- #if 0
- cc_masec /= 1000;
- #else
- cc_masec = (cc_masec - offset) / 1000;
- #endif
- }
-
- RTINFO("[RT5025] chg_cc_mAsec: %d\n", cc_masec);
-
- #if 0
- chip->chg_cc = cc_masec;
- chip->chg_cc_unuse = 0;
- #else
- chip->chg_cc = (cc_masec + chip->chg_cc_unuse) / 3600;
- chip->chg_cc_unuse = (cc_masec + chip->chg_cc_unuse) % 3600;
- #endif
- RTINFO("[RT5025] chg_cc_mAH: %d\n", chip->chg_cc);
- rt5025_clear_cc(CHG);
-}
-
-static void rt5025_get_dchg_cc(struct i2c_client *client)
-{
- u8 data[4];
- u32 qh_old,ql_old,qh_new,ql_new;
- u32 cc_masec,offset;
-
- if (rt5025_read_reg(client, RT5025_REG_QDCHGH_MSB, data, 4) < 0){
- printk(KERN_ERR "%s: Failed to read QDCHG\n", __func__);
- }
- qh_old = (data[0]<<8) + data[1];
- ql_old = (data[2]<<8) + data[3];
- //pr_info("%s qh_old %04x ql_old %04x\n", __func__, qh_old, ql_old);
-
- if (rt5025_read_reg(client, RT5025_REG_QDCHGH_MSB, data, 4) < 0){
- printk(KERN_ERR "%s: Failed to read QDCHG\n", __func__);
- }
- qh_new = (data[0]<<8) + data[1];
- ql_new = (data[2]<<8) + data[3];
- // pr_info("%s qh_new %04x ql_new %04x\n", __func__, qh_new, ql_new);
-
-#if 0
- if (qh_new > qh_old){
- cc_masec = qh_new*91266 + ((ql_new*22)>>4);
- }else if (qh_new == qh_old){
- if (ql_new >= ql_old){
- cc_masec = qh_new*91266 + ((ql_new*22)>>4);
- }else {
- cc_masec = qh_old*91266 + ((ql_old*22)>>4);
- }
- }
-#else
- if (qh_new > qh_old){
- cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10;
- }else if (qh_new == qh_old){
- if (ql_new >= ql_old){
- cc_masec = (((qh_new<<16) + ql_new) * 50134) / 10;
- }else {
- cc_masec = (((qh_old<<16) + ql_old) * 50134) / 10;
- }
- }
-#endif
-
- offset = chip->curr_offset * chip->time_interval;
-
- if (cc_masec != 0){
- #if 0
- cc_masec /= 1000;
- #else
- cc_masec = (cc_masec - offset) / 1000;
- #endif
- }
-
- RTINFO("[RT5025] dchg_cc_mAsec: %d\n", cc_masec);
-
- #if 0
- chip->dchg_cc = cc_masec;
- chip->dchg_cc_unuse = 0;
- #else
- chip->dchg_cc = (cc_masec + chip->dchg_cc_unuse) / 3600;
- chip->dchg_cc_unuse = (cc_masec + chip->dchg_cc_unuse) % 3600;
- #endif
- RTINFO("[RT5025] dchg_cc_mAH: %d\n", chip->dchg_cc);
- rt5025_clear_cc(DCHG);
-}
-
-static void rt5025_get_irq_flag(struct i2c_client *client)
-{
- u8 data[1];
-
- if (rt5025_read_reg(client, RT5025_REG_IRQ_FLAG, data, 1) < 0){
- printk(KERN_ERR "%s: Failed to read irq_flag\n", __func__);
- }
-
- chip->irq_flag = data[0];
- RTINFO("[RT5025] IRQ_FLG 0x%x\n", chip->irq_flag);
-}
-
-static void rt5025_get_timer(struct i2c_client *client)
-{
- u8 data[2];
- u16 gauge_timer;
-
- if (rt5025_read_reg(client, RT5025_REG_TIMER, data, 2) < 0){
- printk(KERN_ERR "%s: Failed to read Timer\n", __func__);
- }
-
- gauge_timer = (data[0] << 8) + data[1];
- if (gauge_timer > chip->pre_gauge_timer)
- chip->time_interval = gauge_timer - chip->pre_gauge_timer;
- else
- chip->time_interval = 65536 - chip->pre_gauge_timer + gauge_timer;
-
- chip->pre_gauge_timer = gauge_timer;
- RTINFO("[RT5025] timer %d , interval %d\n", gauge_timer,chip->time_interval);
-}
-
-static void rt5025_gauge_init_soc(struct i2c_client *client)
-{
- /* Update voltage */
- rt5025_get_vcell(client);
- /* Update current */
- rt5025_get_current(client);
- /* Update external temperature */
- rt5025_get_external_temp(client);
- // JY add
- if (chip->soc_init)
- {
- chip->soc = chip->soc_old;
- chip->rm = chip->rm_old;
- }
- else
- {
- chip->soc = rt5025_vcell2soc(battery_vcell_table, battery_soc_table, ARRAY_SIZE(battery_vcell_table), chip->vcell);
- chip->soc_precise = (u32)(chip->soc);
- chip->soc_init = true;
- chip->soc_old = chip->soc;
- chip->rm = (chip->soc *chip->fcc)/100;
- chip->rm_old = chip->rm;
- }
- // ----------------
- /*
- chip->soc = rt5025_vcell2soc(battery_vcell_table, battery_soc_table, ARRAY_SIZE(battery_vcell_table), chip->vcell);
- chip->soc_precise = (u32)(chip->soc);
- */
- pr_info("%s: vcell = %d, soc = %d\n", __func__, chip->vcell, chip->soc);
- /* upsampling (extend more 12 bits)*/
-}
-
-static void rt5025_get_soc(struct i2c_client *client)
-{
- //fcc = full charged capacity (battery capacity)
- int chg_cc = chip->chg_cc;
- int dchg_cc = chip->dchg_cc;
-
- // JY new implement
- chip->rm = chip->rm_old + (chg_cc - dchg_cc);
- if (chip->rm < 0)
- {
- chip->rm = 0;
- chip->chg_cc = 0;
- chip->dchg_cc = 0;
- }
- else if (chip->rm > chip->fcc)
- {
- chip->rm = chip->fcc;
- chip->chg_cc = chip->fcc;
- chip->dchg_cc = 0;
- }
-
- chip->soc_precise = (chip->rm *100)/chip->fcc;
-
- chip->soc = (chip->soc_precise);
-
- if (chip->soc > chip->soc_old+1)
- {
- chip->soc = chip->soc_old + 1;
- }
- else if (chip->soc < chip->soc_old-1)
- {
- chip->soc = chip->soc_old -1;
- }
-
- if (chip->soc < 0)
- chip->soc = 0;
- else if (chip->soc > 100)
- chip->soc = 100;
-
- chip->rm_old = chip->rm;
- chip->soc_old = chip->soc;
- // --------------------
-
- /* JY mark it
-
- chip->soc_precise = chip->soc_precise + ((chg_cc-dchg_cc)*100)/chip->fcc;
-
- pr_info("%s chg_cc = %d\n", __func__, chg_cc);
- pr_info("%s dchg_cc = %d\n", __func__, dchg_cc);
- pr_info("%s soc_precise = %d\n", __func__, chip->soc_precise);
- chip->soc = (chip->soc_precise);
- if (chip->soc < 0)
- chip->soc = 0;
- else if (chip->soc > 100)
- chip->soc = 100;
- */
- //chip->soc = 50;
-}
-
-static void rt5025_channel_cc(bool enable)
-{
- u8 data[1];
-
- if (rt5025_read_reg(chip->client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){
- printk(KERN_ERR "%s: failed to read channel\n", __func__);
- }
-
- if (enable){
- data[0] = data[0] | 0x80;
- }else {
- data[0] = data[0] & 0x7F;
- }
-
- if (rt5025_write_reg(chip->client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){
- printk(KERN_ERR "%s: failed to write channel\n", __func__);
- }
-}
-
-static void rt5025_register_init(struct i2c_client *client)
-{
- u8 data[1];
-
- /* enable the channel of current,qc,ain,vbat and vadc */
- if (rt5025_read_reg(client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){
- printk("%s: failed to read channel\n", __func__);
- }
- data[0] = data[0] |
- CHANNEL_L_BIT_CADC_EN |
- CHANNEL_L_BIT_AINCH |
- CHANNEL_L_BIT_VBATSCH |
- CHANNEL_L_BIT_VADC_EN;
- if (rt5025_write_reg(client, RT5025_REG_CHANNEL_LSB, data, 1) < 0){
- printk("%s: failed to write channel\n", __func__);
- }
- /* set the alert threshold value */
- irq_thres[MAXTEMP] = TALRTMAX_VALUE;
- irq_thres[MINTEMP] = TALRTMIN_VALUE;
- irq_thres[MAXVOLT] = VALRTMAX_VALUE;
- irq_thres[MINVOLT1] = VALRTMIN1_VALUE;
- irq_thres[MINVOLT2] = VALRTMIN2_VALUE;
- irq_thres[TEMP_RLS] = TRLS_VALUE;
- irq_thres[VOLT_RLS] = VRLS_VALUE;
-
- chip->chg_cc_unuse = 0;
- chip->dchg_cc_unuse = 0;
- chip->pre_gauge_timer = 0;
- chip->online = 1;
- chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
- chip->health = POWER_SUPPLY_HEALTH_GOOD;
- RTINFO("[RT5025] register initialized\n");
-}
-
-static void rt5025_alert_setting(alert_type type, bool enable)
-{
- u8 data[1];
-
- if (rt5025_read_reg(chip->client, RT5025_REG_IRQ_CTL, data, 1) < 0){
- printk(KERN_ERR "%s: Failed to read CONFIG\n", __func__);
- }
-
- if(enable){
- switch(type){
- case MAXTEMP:
- data[0] |= IRQ_CTL_BIT_TMX; //Enable max temperature alert
- chip->max_temp_irq = true;
- RTINFO("Enable min temperature alert");
- break;
- case MINTEMP:
- data[0] |= IRQ_CTL_BIT_TMN; //Enable min temperature alert
- chip->min_temp_irq = true;
- RTINFO("Enable max temperature alert");
- break;
- case MAXVOLT:
- data[0] |= IRQ_CTL_BIT_VMX; //Enable max voltage alert
- chip->max_volt_irq = true;
- RTINFO("Enable max voltage alert");
- break;
- case MINVOLT1:
- data[0] |= IRQ_CTL_BIT_VMN1; //Enable min1 voltage alert
- chip->min_volt1_irq = true;
- RTINFO("Enable min1 voltage alert");
- break;
- case MINVOLT2:
- data[0] |= IRQ_CTL_BIT_VMN2; //Enable min2 voltage alert
- chip->min_volt2_irq = true;
- RTINFO("Enable min2 voltage alert");
- break;
- default:
- break;
- }
- }else{
- switch(type){
- case MAXTEMP:
- data[0] = data[0] &~ IRQ_CTL_BIT_TMX; //Disable max temperature alert
- chip->max_temp_irq = false;
- RTINFO("Disable min temperature alert");
- break;
- case MINTEMP:
- data[0] = data[0] &~ IRQ_CTL_BIT_TMN; //Disable min temperature alert
- chip->min_temp_irq = false;
- RTINFO("Disable max temperature alert");
- break;
- case MAXVOLT:
- data[0] = data[0] &~ IRQ_CTL_BIT_VMX; //Disable max voltage alert
- chip->max_volt_irq = false;
- RTINFO("Disable max voltage alert");
- break;
- case MINVOLT1:
- data[0] = data[0] &~ IRQ_CTL_BIT_VMN1; //Disable min1 voltage alert
- chip->min_volt1_irq = false;
- RTINFO("Disable min1 voltage alert");
- break;
- case MINVOLT2:
- data[0] = data[0] &~ IRQ_CTL_BIT_VMN2; //Disable min2 voltage alert
- chip->min_volt2_irq = false;
- RTINFO("Disable min2 voltage alert");
- break;
- default:
- break;
- }
- }
- if (rt5025_write_reg(chip->client, RT5025_REG_IRQ_CTL, data, 1) < 0)
- printk(KERN_ERR "%s: failed to write IRQ control\n", __func__);
-}
-static void rt5025_alert_threshold_init(struct i2c_client *client)
-{
- u8 data[1];
-
- /* TALRT MAX threshold setting */
- data[0] = irq_thres[MAXTEMP];
- if (rt5025_write_reg(client, RT5025_REG_TALRT_MAXTH, data, 1) < 0)
- printk(KERN_ERR "%s: failed to write TALRT MAX threshold\n", __func__);
- /* TALRT MIN threshold setting */
- data[0] = irq_thres[MINTEMP];
- if (rt5025_write_reg(client, RT5025_REG_TALRT_MINTH, data, 1) < 0)
- printk(KERN_ERR "%s: failed to write TALRT MIN threshold\n", __func__);
- /* VALRT MAX threshold setting */
- data[0] = irq_thres[MAXVOLT];
- if (rt5025_write_reg(client, RT5025_REG_VALRT_MAXTH, data, 1) < 0)
- printk(KERN_ERR "%s: failed to write VALRT MAX threshold\n", __func__);
- /* VALRT MIN1 threshold setting */
- data[0] = irq_thres[MINVOLT1];
- if (rt5025_write_reg(client, RT5025_REG_VALRT_MIN1TH, data, 1) < 0)
- printk(KERN_ERR "%s: failed to write VALRT MIN1 threshold\n", __func__);
- /* VALRT MIN2 threshold setting */
- data[0] = irq_thres[MINVOLT2];
- if (rt5025_write_reg(client, RT5025_REG_VALRT_MIN2TH, data, 1) < 0)
- printk(KERN_ERR "%s: failed to write VALRT MIN2 threshold\n", __func__);
-}
-
-static void rt5025_alert_init(struct i2c_client *client)
-{
- /* Set RT5025 gauge alert configuration */
- rt5025_alert_threshold_init(client);
- /* Enable gauge alert function */
- rt5025_alert_setting(MAXTEMP,true);
- rt5025_alert_setting(MINTEMP,true);
- rt5025_alert_setting(MAXVOLT,true);
- rt5025_alert_setting(MINVOLT1,true);
- rt5025_alert_setting(MINVOLT2,true);
-}
-
-void rt5025_irq_handler(void)
-{
- rt5025_get_irq_flag(chip->client);
-
- if ((chip->irq_flag) & IRQ_FLG_BIT_TMX){
- printk(KERN_INFO "[RT5025]: Min temperature IRQ received\n");
- rt5025_alert_setting(MAXTEMP,false);
- chip->max_temp_irq = false;
- }
- if ((chip->irq_flag) & IRQ_FLG_BIT_TMN){
- printk(KERN_INFO "[RT5025]: Max temperature IRQ received\n");
- rt5025_alert_setting(MINTEMP,false);
- chip->min_temp_irq = false;
- }
- if ((chip->irq_flag) & IRQ_FLG_BIT_VMX){
- printk(KERN_INFO "[RT5025]: Max voltage IRQ received\n");
- rt5025_alert_setting(MAXVOLT,false);
- chip->max_volt_irq = false;
- }
- if ((chip->irq_flag) & IRQ_FLG_BIT_VMN1){
- printk(KERN_INFO "[RT5025]: Min voltage1 IRQ received\n");
- rt5025_alert_setting(MINVOLT1,false);
- chip->min_volt1_irq = false;
- }
- if ((chip->irq_flag) & IRQ_FLG_BIT_VMN2){
- printk(KERN_INFO "[RT5025]: Min voltage2 IRQ received\n");
- rt5025_alert_setting(MINVOLT2,false);
- chip->min_volt2_irq = false;
- }
-
- wake_lock(&chip->monitor_wake_lock);
- schedule_delayed_work(&chip->monitor_work, 0);
-}
-
-static void rt5025_update(struct i2c_client *client)
-{
- /* Update voltage */
- rt5025_get_vcell(client);
- /* Update current */
- rt5025_get_current(client);
- /* Update external temperature */
- rt5025_get_external_temp(client);
- /* Read timer */
- rt5025_get_timer(client);
- /* Update chg cc */
- rt5025_get_chg_cc(client);
- /* Update dchg cc */
- rt5025_get_dchg_cc(client);
- /* Update SOC */
- rt5025_get_soc(client);
-
- if ((chip->max_temp_irq == false) &&
- (((irq_thres[MAXTEMP] * IRQ_THRES_UNIT) / 100 - chip->ain_volt) > irq_thres[TEMP_RLS])){
- rt5025_alert_setting(MAXTEMP,true);
- }else if ((chip->min_temp_irq == false) &&
- ((chip->ain_volt - (irq_thres[MINTEMP] * IRQ_THRES_UNIT) / 100) > irq_thres[TEMP_RLS])){
- rt5025_alert_setting(MINTEMP,true);
- }else if ((chip->max_volt_irq == false) &&
- ((((irq_thres[MAXVOLT] * IRQ_THRES_UNIT) / 100) - chip->vcell) > irq_thres[VOLT_RLS])){
- rt5025_alert_setting(MAXVOLT,true);
- }else if ((chip->min_volt1_irq == false) &&
- ((chip->vcell - ((irq_thres[MINVOLT1] * IRQ_THRES_UNIT) / 100)) > irq_thres[VOLT_RLS])){
- rt5025_alert_setting(MINVOLT1,true);
- }else if ((chip->min_volt2_irq == false) &&
- ((chip->vcell - ((irq_thres[MINVOLT2] * IRQ_THRES_UNIT) / 100)) > irq_thres[VOLT_RLS])){
- rt5025_alert_setting(MINVOLT2,true);
- }
-}
-
-static void rt5025_update_work(struct work_struct *work)
-{
- unsigned long flags;
-
- rt5025_update(chip->client);
-
- /* Update data to framework */
- power_supply_changed(&chip->battery);
-
- /* prevent suspend before starting the alarm */
- local_irq_save(flags);
- chip->last_poll = alarm_get_elapsed_realtime();
- rt5025_program_alarm(NORMAL_POLL);
- local_irq_restore(flags);
-
- wake_unlock(&chip->monitor_wake_lock);
-}
-
-static enum power_supply_property rt5025_battery_props[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_HEALTH,
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_TECHNOLOGY,
-};
-
-void rt5025_gauge_suspend(void)
-{
- rt5025_channel_cc(false);
- cancel_delayed_work(&chip->monitor_work);
-
- RTINFO("\n");
-}
-
-void rt5025_gauge_resume(void)
-{
- rt5025_channel_cc(true);
- wake_lock(&chip->monitor_wake_lock);
- schedule_delayed_work(&chip->monitor_work, 0);
- RTINFO("\n");
-}
-
-void rt5025_gauge_remove(void)
-{
- chip->info->event_callback = NULL;
- power_supply_unregister(&chip->battery);
- cancel_delayed_work(&chip->monitor_work);
- wake_lock_destroy(&chip->monitor_wake_lock);
- kfree(chip);
-}
-
-int rt5025_gauge_init(struct rt5025_power_info *info)
-{
- int ret;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- chip->client = info->i2c;
- chip->fcc = info->fcc;
- chip->info = info;
- // JY add
- chip->soc_init = false;
- chip->rm = chip->fcc/2;
- chip->rm_old = chip->rm;
- // ------------
- chip->battery.name = "rt5025-battery";
- chip->battery.type = POWER_SUPPLY_TYPE_BATTERY;
- chip->battery.get_property = rt5025_get_property;
- chip->battery.properties = rt5025_battery_props;
- chip->battery.num_properties = ARRAY_SIZE(rt5025_battery_props);
-
- ret = power_supply_register(info->dev, &chip->battery);
- if (ret) {
- printk(KERN_ERR "[RT5025] power supply register failed\n");
- goto err_wake_lock;
- }
-
- chip->last_poll = alarm_get_elapsed_realtime();
- alarm_init(&chip->wakeup_alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
- rt5025_gauge_alarm);
-
- INIT_DELAYED_WORK(&chip->monitor_work, rt5025_update_work);
-
- wake_lock_init(&chip->monitor_wake_lock, WAKE_LOCK_SUSPEND,
- "rt-battery-monitor");
- /* enable channel */
- rt5025_register_init(info->i2c);
- rt5025_gauge_init_soc(info->i2c);
-
- /* enable gauge IRQ */
- rt5025_alert_init(info->i2c);
-
- /* register callback functions */
- chip->cb.rt5025_gauge_irq_handler = rt5025_irq_handler;
- chip->cb.rt5025_gauge_set_status = rt5025_set_status;
- chip->cb.rt5025_gauge_suspend = rt5025_gauge_suspend;
- chip->cb.rt5025_gauge_resume = rt5025_gauge_resume;
- chip->cb.rt5025_gauge_remove = rt5025_gauge_remove;
- info->event_callback=&chip->cb;
-
- //rt_register_gauge_callbacks(info->i2c, &chip->cb);
-
- wake_lock(&chip->monitor_wake_lock);
- schedule_delayed_work(&chip->monitor_work, msecs_to_jiffies(1000));
-
- return 0;
-
-err_wake_lock:
- wake_lock_destroy(&chip->monitor_wake_lock);
- kfree(chip);
-
- return ret;
-}
#include <linux/workqueue.h>
#include <linux/mfd/rt5025.h>
#include <linux/power/rt5025-power.h>
-#include <linux/power/rt5025-gauge.h>
#include <linux/delay.h>
"rt5025-battery",
};
+
#if 0
static int rt5025_set_charging_current_switch (struct i2c_client *i2c, int onoff)
{
u8 data = 0;
//ICC Setting
+ #if 0
if (cur_value > 2000)
data |= 0x0f<<3;
else if (cur_value >= 500 && cur_value <= 2000)
data = (cur_value-500)/100;
data<<=3;
}
-
+ #endif
//AICR Setting
if (cur_value > 1000)
else if (cur_value > 100 && cur_value >= 500)
data |= 0x01<<1;
- rt5025_assign_bits(i2c, RT5025_REG_CHGCTL4, RT5025_CHGCC_MASK, data);
+ rt5025_assign_bits(i2c, RT5025_REG_CHGCTL4, RT5025_CHGAICR_MASK, data);
return ret;
}
rt5025_set_charging_buck(info->i2c, 1);
#endif
info->chg_stat = 0x00;
+ #if 1
+ if (info->chip->battery_info)
+ {
+ if (info->chg_term == 0)
+ rt5025_gauge_set_status(info->chip->battery_info, POWER_SUPPLY_STATUS_DISCHARGING);
+ else if (info->chg_term > 0)
+ {
+ rt5025_gauge_set_status(info->chip->battery_info, POWER_SUPPLY_STATUS_FULL);
+ info->chg_term = 0;
+ }
+
+ }
+ #else
if (info->event_callback)
info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_CHARGING);
+ #endif
break;
case 0x01:
//rt5025_set_charging_current_switch(info->i2c, 1);
info->chg_stat = 0x01;
+ #if 1
+ if (info->chip->battery_info)
+ {
+ rt5025_gauge_set_status(info->chip->battery_info, POWER_SUPPLY_STATUS_CHARGING);
+ info->chg_term = 0;
+ }
+ #else
if (info->event_callback)
info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_CHARGING);
+ #endif
break;
case 0x02:
#if 0
rt5025_set_charging_current_switch(info->i2c, 0);
#endif
info->chg_stat = 0x02;
+ #if 1
+ if (info->chip->battery_info)
+ {
+ rt5025_gauge_set_status(info->chip->battery_info, POWER_SUPPLY_STATUS_FULL);
+ info->chg_term = 0;
+ }
+ #else
if (info->event_callback)
info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_FULL);
+ #endif
break;
case 0x03:
#if 0
rt5025_set_charging_current_switch(info->i2c, 0);
#endif
info->chg_stat = 0x03;
+ #if 1
+ if (info->chip->battery_info)
+ {
+ if (info->chg_term == 0)
+ rt5025_gauge_set_status(info->chip->battery_info, POWER_SUPPLY_STATUS_CHARGING);
+ else if (info->chg_term > 1)
+ {
+ rt5025_gauge_set_status(info->chip->battery_info, POWER_SUPPLY_STATUS_FULL);
+ info->chg_term = 0;
+ }
+ }
+ #else
if (info->event_callback)
info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_DISCHARGING);
+ #endif
break;
default:
break;
return ret;
}
+#if 0
int rt5025_power_passirq_to_gauge(struct rt5025_power_info *info)
{
if (info->event_callback)
return 0;
}
EXPORT_SYMBOL(rt5025_power_passirq_to_gauge);
+#endif
int rt5025_power_charge_detect(struct rt5025_power_info *info)
{
schedule_delayed_work(&info->usb_detect_work, 0); //no delay
new_chgval = (chgstatval&RT5025_CHGSTAT_MASK)>>RT5025_CHGSTAT_SHIFT;
+
if (new_acval || new_usbval)
{
if (old_chgval != new_chgval)
rt5025_set_charging_current_switch(info->i2c, 0);
#endif
info->chg_stat = RT5025_CHGSTAT_UNKNOWN;
+ if (info->chip->jeita_info)
+ rt5025_notify_charging_cable(info->chip->jeita_info, JEITA_NO_CHARGE);
+ #if 1
+ if (info->chip->battery_info)
+ rt5025_gauge_set_status(info->chip->battery_info, POWER_SUPPLY_STATUS_NOT_CHARGING);
+ #else
if (info->event_callback)
info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_NOT_CHARGING);
+ #endif
}
return ret;
return 0;
}
+
extern int dwc_vbus_status(void);
+
static void usb_detect_work_func(struct work_struct *work)
{
struct delayed_work *delayed_work = (struct delayed_work *)container_of(work, struct delayed_work, work);
if (pi->ac_online)
{
rt5025_set_charging_current(pi->i2c, 1000);
+ rt5025_notify_charging_cable(pi->chip->jeita_info, JEITA_AC_ADAPTER);
pi->usb_cnt = 0;
}
else if (pi->usb_online)
{
case 2: // USB Wall charger
rt5025_set_charging_current(pi->i2c, 1000);
+ rt5025_notify_charging_cable(pi->chip->jeita_info, JEITA_USB_TA);
pr_info("rt5025: detect usb wall charger\n");
break;
case 1: //normal USB
default:
rt5025_set_charging_current(pi->i2c, 500);
+ rt5025_notify_charging_cable(pi->chip->jeita_info, JEITA_NORMAL_USB);
pr_info("rt5025: detect normal usb\n");
break;
}
{
//default to prevent over current charging
rt5025_set_charging_current(pi->i2c, 500);
+ rt5025_notify_charging_cable(pi->chip->jeita_info, JEITA_NO_CHARGE);
//reset usb_cnt;
pi->usb_cnt = 0;
}
static int __devinit rt5025_init_charger(struct rt5025_power_info *info, struct rt5025_power_data* pd)
{
- RTINFO("++\n");
info->ac_online = 0;
info->usb_online =0;
//init charger buckck & charger current en to disable stat
info->chg_stat = RT5025_CHGSTAT_UNKNOWN;
+ #if 0
if (info->event_callback)
info->event_callback->rt5025_gauge_set_status(POWER_SUPPLY_STATUS_DISCHARGING);
+ #endif
rt5025_set_bits(info->i2c, RT5025_REG_CHGCTL4, RT5025_CHGRST_MASK);
udelay(200);
//init register setting
rt5025_power_charge_detect(info);
- RTINFO("--\n");
return 0;
}
struct rt5025_platform_data *pdata = chip->dev->platform_data;
struct rt5025_power_info *pi;
int ret = 0;
- printk("%s,line=%d\n", __func__,__LINE__);
-
+
pi = kzalloc(sizeof(*pi), GFP_KERNEL);
if (!pi)
return -ENOMEM;
pi->i2c = chip->i2c;
pi->dev = &pdev->dev;
- pi->fcc = pdata->power_data->fcc;
+ pi->chip = chip;
mutex_init(&pi->var_lock);
INIT_DELAYED_WORK(&pi->usb_detect_work, usb_detect_work_func);
+ #if 0
ret = rt5025_gauge_init(pi);
if (ret)
goto out;
+ #endif
platform_set_drvdata(pdev, pi);
- pi->ac.name = "rt5025-ac";
+ pi->ac.name = "rt5025-dc";
pi->ac.type = POWER_SUPPLY_TYPE_MAINS;
pi->ac.supplied_to = rt5025_supply_list;
pi->ac.properties = rt5025_adap_props;
static int rt5025_power_suspend(struct platform_device *pdev, pm_message_t state)
{
+ #if 0
struct rt5025_power_info *pi = platform_get_drvdata(pdev);
if (pi->event_callback)
pi->event_callback->rt5025_gauge_suspend();
+ #endif
return 0;
}
static int rt5025_power_resume(struct platform_device *pdev)
{
+ #if 0
struct rt5025_power_info *pi = platform_get_drvdata(pdev);
+
if (pi->event_callback)
pi->event_callback->rt5025_gauge_resume();
+ #endif
return 0;
}
struct rt5025_power_info *pi = platform_get_drvdata(pdev);
struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ #if 0
if (pi->event_callback)
pi->event_callback->rt5025_gauge_remove();
+ #endif
power_supply_unregister(&pi->usb);
power_supply_unregister(&pi->ac);
chip->power_info = NULL;
--- /dev/null
+/* drivers/power/rt5025-swjeita.c
+ * swjeita Driver for Richtek RT5025 PMIC
+ * Multi function device - multi functional baseband PMIC swjeita part
+ *
+ * Copyright (C) 2013
+ * Author: CY Huang <cy_huang@richtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/mfd/rt5025.h>
+#include <linux/power/rt5025-swjeita.h>
+
+#define TEMP_TOLERANCE 10 // 'c*10 gap for tolerance
+
+static int rt5025_set_charging_cc_switch (struct i2c_client *i2c, int onoff)
+{
+ int ret;
+ RTINFO("onoff = %d\n", onoff);
+ if (onoff)
+ ret = rt5025_set_bits(i2c, RT5025_REG_CHGCTL7, RT5025_CHGCCEN_MASK);
+ else
+ ret = rt5025_clr_bits(i2c, RT5025_REG_CHGCTL7, RT5025_CHGCCEN_MASK);
+ return ret;
+}
+
+static int rt5025_set_charging_cc(struct i2c_client *i2c, int cur_value)
+{
+ int ret;
+ u8 data;
+
+ RTINFO("current value = %d\n", cur_value);
+ if (cur_value < 500)
+ data = 0;
+ else if (cur_value > 2000)
+ data = 0xf<<RT5025_CHGICC_SHIFT;
+ else
+ data = ((cur_value-500)/100)<<RT5025_CHGICC_SHIFT;
+
+ ret = rt5025_assign_bits(i2c, RT5025_REG_CHGCTL4, RT5025_CHGICC_MASK, data);
+
+ if (cur_value == 0)
+ rt5025_set_charging_cc_switch(i2c, 0);
+ else
+ rt5025_set_charging_cc_switch(i2c, 1);
+
+ return ret;
+}
+
+static int rt5025_set_charging_cv(struct i2c_client *i2c, int voltage)
+{
+ int ret;
+ u8 data;
+
+ RTINFO("voltage = %d\n", voltage);
+ if (voltage < 3500)
+ data = 0;
+ else if (voltage > 4440)
+ data = 0x2f<<RT5025_CHGCV_SHIFT;
+ else
+ data = ((voltage-3500)/20)<<RT5025_CHGCV_SHIFT;
+
+ ret = rt5025_assign_bits(i2c, RT5025_REG_CHGCTL3, RT5025_CHGCV_MASK, data);
+ return ret;
+}
+
+static int rt5025_sel_external_temp_index(struct rt5025_swjeita_info *swji)
+{
+ int temp = swji->cur_temp;
+ int sect_index;
+
+ RTINFO("\n");
+ if (temp < swji->temp[0])
+ sect_index = 0;
+ else if (temp >= swji->temp[0] && temp < swji->temp[1])
+ sect_index = 1;
+ else if (temp >= swji->temp[1] && temp < swji->temp[2])
+ sect_index = 2;
+ else if (temp >= swji->temp[2] && temp < swji->temp[3])
+ sect_index = 3;
+ else if (temp >= swji->temp[3])
+ sect_index = 4;
+
+ RTINFO("sect_index = %d\n", sect_index);
+ return sect_index;
+}
+
+static int rt5025_get_external_temp_index(struct rt5025_swjeita_info *swji)
+{
+ u8 data[2];
+ long int temp;
+ int sect_index;
+
+ RTINFO("\n");
+ if (rt5025_reg_block_read(swji->i2c, RT5025_REG_AINH, 2, data) < 0)
+ pr_err("%s: failed to read ext_temp register\n", __func__);
+
+ temp = (data[0]*256+data[1])*61/100;
+ temp = (temp * (-91738) +81521000)/100000;
+
+ swji->cur_temp = temp;
+
+ RTINFO("cur_section = %d, cur_temp = %d\n", swji->cur_section, swji->cur_temp);
+
+ switch (swji->cur_section)
+ {
+ case 0:
+ if (temp < swji->temp[0]+TEMP_TOLERANCE)
+ sect_index = rt5025_sel_external_temp_index(swji);
+ else
+ sect_index = swji->cur_section;
+ break;
+ case 1:
+ if (temp <= swji->temp[0]-TEMP_TOLERANCE || temp >= swji->temp[1]+TEMP_TOLERANCE)
+ sect_index = rt5025_sel_external_temp_index(swji);
+ else
+ sect_index = swji->cur_section;
+ break;
+ case 2:
+ if (temp <= swji->temp[1]-TEMP_TOLERANCE || temp >= swji->temp[2]+TEMP_TOLERANCE)
+ sect_index = rt5025_sel_external_temp_index(swji);
+ else
+ sect_index = swji->cur_section;
+ break;
+ case 3:
+ if (temp <= swji->temp[2]-TEMP_TOLERANCE || temp >= swji->temp[3]+TEMP_TOLERANCE)
+ sect_index = rt5025_sel_external_temp_index(swji);
+ else
+ sect_index = swji->cur_section;
+ break;
+ case 4:
+ if (temp <= swji->temp[3]-TEMP_TOLERANCE)
+ sect_index = rt5025_sel_external_temp_index(swji);
+ else
+ sect_index = swji->cur_section;
+ break;
+ default:
+ sect_index = swji->cur_section;
+ break;
+ }
+ RTINFO("sect_index = %d\n", sect_index);
+ return sect_index;
+}
+
+static inline int rt5025_set_ainadc_onoff(struct rt5025_swjeita_info *swji, int enable)
+{
+ int ret;
+
+ RTINFO("enable = %d\n", enable);
+ if (enable)
+ ret = rt5025_set_bits(swji->i2c, RT5025_REG_CHANNELL, RT5025_AINEN_MASK);
+ else
+ ret = rt5025_clr_bits(swji->i2c, RT5025_REG_CHANNELL, RT5025_AINEN_MASK);
+
+ return ret;
+}
+
+static int rt5025_set_exttemp_alert(struct rt5025_swjeita_info *swji, int index)
+{
+ int ret = 0;
+
+ RTINFO("index = %d\n", index);
+
+ switch (index)
+ {
+ case 0:
+ rt5025_reg_write(swji->i2c, RT5025_REG_TALRTMIN, swji->temp_scalar[0]);
+ break;
+ case 1:
+ rt5025_reg_write(swji->i2c, RT5025_REG_TALRTMAX, swji->temp_scalar[0]);
+ rt5025_reg_write(swji->i2c, RT5025_REG_TALRTMIN, swji->temp_scalar[1]);
+ break;
+ case 2:
+ rt5025_reg_write(swji->i2c, RT5025_REG_TALRTMAX, swji->temp_scalar[1]);
+ rt5025_reg_write(swji->i2c, RT5025_REG_TALRTMIN, swji->temp_scalar[2]);
+ break;
+ case 3:
+ rt5025_reg_write(swji->i2c, RT5025_REG_TALRTMAX, swji->temp_scalar[2]);
+ rt5025_reg_write(swji->i2c, RT5025_REG_TALRTMIN, swji->temp_scalar[3]);
+ break;
+ case 4:
+ rt5025_reg_write(swji->i2c, RT5025_REG_TALRTMAX, swji->temp_scalar[3]);
+ break;
+ }
+
+ return ret;
+}
+
+static int rt5025_exttemp_alert_switch(struct rt5025_swjeita_info *swji, int onoff)
+{
+ if (!onoff)
+ {
+ rt5025_clr_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMXEN_MASK);
+ rt5025_clr_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMNEN_MASK);
+ }
+ else
+ {
+ switch (swji->cur_section)
+ {
+ case 0:
+ rt5025_set_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMNEN_MASK);
+ break;
+ case 1:
+ rt5025_set_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMXEN_MASK);
+ rt5025_set_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMNEN_MASK);
+ break;
+ case 2:
+ rt5025_set_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMXEN_MASK);
+ rt5025_set_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMNEN_MASK);
+ break;
+ case 3:
+ rt5025_set_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMXEN_MASK);
+ rt5025_set_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMNEN_MASK);
+ break;
+ case 4:
+ rt5025_set_bits(swji->i2c, RT5025_REG_IRQCTL, RT5025_TMXEN_MASK);
+ break;
+ }
+ }
+
+ RTINFO("index=%d, onoff=%d\n", swji->cur_section, onoff);
+ return 0;
+}
+
+int rt5025_notify_charging_cable(struct rt5025_swjeita_info *swji, int cable_type)
+{
+ int sect_index;
+ int ret = 0;
+
+ RTINFO("cable_type = %d\n", cable_type);
+
+ rt5025_exttemp_alert_switch(swji, 0);
+
+ sect_index = rt5025_get_external_temp_index(swji);
+ if (swji->cur_section != sect_index || swji->init_once == 0)
+ {
+ rt5025_set_exttemp_alert(swji, sect_index);
+ swji->cur_section = sect_index;
+ swji->init_once = 1;
+ }
+
+ switch (cable_type)
+ {
+ case JEITA_NORMAL_USB:
+ rt5025_set_charging_cc(swji->i2c, swji->temp_cc[cable_type][swji->cur_section]);
+ rt5025_set_charging_cv(swji->i2c, swji->temp_cv[cable_type][swji->cur_section]);
+ break;
+ case JEITA_USB_TA:
+ rt5025_set_charging_cc(swji->i2c, swji->temp_cc[cable_type][swji->cur_section]);
+ rt5025_set_charging_cv(swji->i2c, swji->temp_cv[cable_type][swji->cur_section]);
+ break;
+ case JEITA_AC_ADAPTER:
+ rt5025_set_charging_cc(swji->i2c, swji->temp_cc[cable_type][swji->cur_section]);
+ rt5025_set_charging_cv(swji->i2c, swji->temp_cv[cable_type][swji->cur_section]);
+ break;
+ case JEITA_NO_CHARGE:
+ rt5025_set_charging_cc(swji->i2c, swji->temp_cc[cable_type][swji->cur_section]);
+ rt5025_set_charging_cv(swji->i2c, swji->temp_cv[cable_type][swji->cur_section]);
+ break;
+ }
+ swji->cur_cable = cable_type;
+
+ rt5025_exttemp_alert_switch(swji, 1);
+
+ return ret;
+}
+EXPORT_SYMBOL(rt5025_notify_charging_cable);
+
+int rt5025_swjeita_irq_handler(struct rt5025_swjeita_info *swji, unsigned char event)
+{
+ int ret = 0;
+ RTINFO("event = 0x%02x\n", event);
+
+ if (event&(RT5025_TMXEN_MASK|RT5025_TMNEN_MASK))
+ rt5025_notify_charging_cable(swji, swji->cur_cable);
+
+ return ret;
+}
+EXPORT_SYMBOL(rt5025_swjeita_irq_handler);
+
+static int __devinit rt5025_swjeita_probe(struct platform_device *pdev)
+{
+ struct rt5025_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct rt5025_platform_data *pdata = chip->dev->platform_data;
+ struct rt5025_swjeita_info *swji;
+ int ret = 0;
+
+ swji = kzalloc(sizeof(*swji), GFP_KERNEL);
+ if (!swji)
+ return -ENOMEM;
+
+ #if 0 // for debug pdata->jeita_data
+ for (ret=0; ret<4; ret++)
+ RTINFO("jeita temp value %d\n", pdata->jeita_data->temp[ret]);
+ for (ret=0; ret<4; ret++)
+ {
+ RTINFO("jeita temp_cc value %d, %d, %d, %d, %d\n", pdata->jeita_data->temp_cc[ret][0], \
+ pdata->jeita_data->temp_cc[ret][1], pdata->jeita_data->temp_cc[ret][2], \
+ pdata->jeita_data->temp_cc[ret][3], pdata->jeita_data->temp_cc[ret][4]);
+ }
+ for (ret=0; ret<4; ret++)
+ {
+ RTINFO("jeita temp_cv value %d, %d, %d, %d, %d\n", pdata->jeita_data->temp_cv[ret][0], \
+ pdata->jeita_data->temp_cv[ret][1], pdata->jeita_data->temp_cv[ret][2], \
+ pdata->jeita_data->temp_cv[ret][3], pdata->jeita_data->temp_cv[ret][4]);
+ }
+ for (ret=0; ret<4; ret++)
+ {
+ RTINFO("temp_scalar[%d] = 0x%02x\n", ret, pdata->jeita_data->temp_scalar[ret]);
+ }
+ ret = 0;
+ #endif /* #if 0 */
+
+ swji->i2c = chip->i2c;
+ swji->chip = chip;
+ swji->cur_section = 2; //initial as the normal temperature
+ swji->cur_cable = JEITA_NO_CHARGE;
+ swji->temp = pdata->jeita_data->temp;
+ swji->temp_scalar = pdata->jeita_data->temp_scalar;
+ swji->temp_cc = pdata->jeita_data->temp_cc;
+ swji->temp_cv = pdata->jeita_data->temp_cv;
+ platform_set_drvdata(pdev, swji);
+
+ rt5025_set_ainadc_onoff(swji, 1);
+ mdelay(100);
+ rt5025_notify_charging_cable(swji, swji->cur_cable);
+
+ chip->jeita_info = swji;
+ RTINFO("rt5025-swjeita driver is successfully loaded\n");
+ return ret;
+}
+
+static int __devexit rt5025_swjeita_remove(struct platform_device *pdev)
+{
+ struct rt5025_swjeita_info *swji = platform_get_drvdata(pdev);
+
+ swji->chip->jeita_info = NULL;
+ kfree(swji);
+ return 0;
+}
+
+static int rt5025_swjeita_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct rt5025_swjeita_info *swji = platform_get_drvdata(pdev);
+
+ swji->suspend = 1;
+ return 0;
+}
+
+static int rt5025_swjeita_resume(struct platform_device *pdev)
+{
+ struct rt5025_swjeita_info *swji = platform_get_drvdata(pdev);
+
+ swji->suspend = 0;
+ return 0;
+}
+
+static struct platform_driver rt5025_swjeita_driver =
+{
+ .driver = {
+ .name = RT5025_DEVICE_NAME "-swjeita",
+ .owner = THIS_MODULE,
+ },
+ .probe = rt5025_swjeita_probe,
+ .remove = __devexit_p(rt5025_swjeita_remove),
+ .suspend = rt5025_swjeita_suspend,
+ .resume = rt5025_swjeita_resume,
+};
+
+static int __init rt5025_swjeita_init(void)
+{
+ return platform_driver_register(&rt5025_swjeita_driver);
+}
+module_init(rt5025_swjeita_init);
+
+static void __exit rt5025_swjeita_exit(void)
+{
+ platform_driver_unregister(&rt5025_swjeita_driver);
+}
+module_exit(rt5025_swjeita_exit);
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("CY Huang <cy_huang@richtek.com");
+MODULE_DESCRIPTION("Swjeita driver for RT5025");
+MODULE_ALIAS("platform:" RT5025_DEVICE_NAME "-swjeita");
int vol_mask;
int enable_bit;
int enable_reg;
+ int mode_bit;
+ int mode_reg;
};
//for DCDC1
return (index>=info->vol_output_size)? \
-EINVAL: \
- info->vol_output_list[index ];
+ info->vol_output_list[index];
}
-//#if (LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38))
+#if 0 //(LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38))
static int rt5025_set_voltage_sel(struct regulator_dev *rdev, unsigned selector)
{
struct rt5025_regulator_info *info = rdev_get_drvdata(rdev);
return ret;
return (ret & info->vol_mask) >> info->vol_shift;
}
-//#else
+#else
static int rt5025_find_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
{
struct rt5025_regulator_info *info = rdev_get_drvdata(rdev);
unsigned char data;
+
if (check_range(info, min_uV, max_uV)) {
dev_err(info->chip->dev, "invalid voltage range (%d, %d) uV\n",
min_uV, max_uV);
}
data = rt5025_find_voltage(rdev,min_uV,max_uV);
data <<= info->vol_shift;
+
return rt5025_assign_bits(info->i2c, info->vol_reg, info->vol_mask, data);
}
static int rt5025_get_voltage(struct regulator_dev *rdev)
{
+ struct rt5025_regulator_info *info = rdev_get_drvdata(rdev);
int ret;
- ret = rt5025_get_voltage_sel(rdev);
+ ret = rt5025_reg_read(info->i2c, info->vol_reg);
if (ret < 0)
return ret;
- return rt5025_list_voltage(rdev, ret );
-
+ ret = (ret & info->vol_mask) >> info->vol_shift;
+ return rt5025_list_voltage(rdev, ret);
}
-//#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38) */
+#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38) */
static int rt5025_enable(struct regulator_dev *rdev)
{
return (ret & (info->enable_bit))?1:0;
}
-static int rt5025_dcdc_get_mode(struct regulator_dev *rdev)
+
+static int rt5025_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
struct rt5025_regulator_info *info = rdev_get_drvdata(rdev);
- int buck = rdev_get_id(rdev) - 0;
int ret;
- uint8_t control;
-
- ret = rt5025_reg_read(info->i2c, 0x0c);
- if (ret < 0) {
- return ret;
- }
- if (buck ==0){
- control =(ret & 0x80)>>7;
- }
- else if (buck ==1){
- control =(ret & 0x40)>>6;
- }
- else if (buck ==2){
- control =(ret & 0x20)>>5;
- }
- else{
- return -1;
- }
- switch (control) {
- case 0:
- return REGULATOR_MODE_FAST;
- case 1:
- return REGULATOR_MODE_NORMAL;
- default:
- return -1;
+ if (!info->mode_bit)
+ ret = 0;
+ else
+ {
+ switch (mode)
+ {
+ case REGULATOR_MODE_NORMAL:
+ ret = rt5025_set_bits(info->i2c, info->mode_reg, info->mode_bit);
+ break;
+ case REGULATOR_MODE_FAST:
+ ret = rt5025_clr_bits(info->i2c, info->mode_reg, info->mode_bit);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
}
-
+ return ret;
}
-static int rt5025_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode)
+
+static unsigned int rt5025_get_mode(struct regulator_dev *rdev)
{
struct rt5025_regulator_info *info = rdev_get_drvdata(rdev);
- int buck = rdev_get_id(rdev) - 0;
- int ret;
- uint8_t control;
-
- ret = rt5025_reg_read(info->i2c, 0x0c);
- if (buck ==0){
- control =ret &(~ (1 <<7));
- }
- else if (buck ==1){
- control =ret &(~ (1 <<6));
- }
- else if (buck ==2){
- control =ret &(~ (1 <<5));
- }
- else{
- return -1;
- }
-
- switch(mode)
+ unsigned int mode;
+ int data;
+
+ if (!info->mode_bit)
+ mode = REGULATOR_MODE_NORMAL;
+ else
{
- case REGULATOR_MODE_FAST:
- return rt5025_reg_write(info->i2c, 0x0c,control);
- case REGULATOR_MODE_NORMAL:
- return rt5025_reg_write(info->i2c, 0x0c,(control | (1 <<(7-buck))));
- default:
- printk("error:pmu_rt5025 only powersave pwm & auto mode\n");
- return -EINVAL;
+ data = rt5025_reg_read(info->i2c, info->mode_reg);
+ mode = (data & info->mode_bit)?REGULATOR_MODE_NORMAL:REGULATOR_MODE_FAST;
}
-}
-static int rt5025_dcdc_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector,
- unsigned int new_selector)
-{
- int old_volt, new_volt;
-
- old_volt = rt5025_list_voltage(rdev, old_selector);
- if (old_volt < 0)
- return old_volt;
-
- new_volt = rt5025_list_voltage(rdev, new_selector);
- if (new_volt < 0)
- return new_volt;
-
- return DIV_ROUND_UP(abs(old_volt - new_volt), 25000);
+ return mode;
}
static struct regulator_ops rt5025_regulator_ops = {
.list_voltage = rt5025_list_voltage,
-//#if (LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38))
-// .get_voltage_sel = rt5025_get_voltage_sel,
-// .set_voltage_sel = rt5025_set_voltage_sel,
-//#else
+#if 0 //(LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38))
+ .get_voltage_sel = rt5025_get_voltage_sel,
+ .set_voltage_sel = rt5025_set_voltage_sel,
+#else
.set_voltage = rt5025_set_voltage,
.get_voltage = rt5025_get_voltage,
-//#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38) */
+#endif /* LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,38) */
.enable = rt5025_enable,
.disable = rt5025_disable,
.is_enabled = rt5025_is_enabled,
- .get_mode = rt5025_dcdc_get_mode,
- .set_mode = rt5025_dcdc_set_mode,
- .set_voltage_time_sel = rt5025_dcdc_set_voltage_time_sel,
+ .set_mode = rt5025_set_mode,
+ .get_mode = rt5025_get_mode,
};
#define RT5025_DCDCVOUT_LIST1 rt5025_vol_output_list1
.vol_mask = RT5025_DCDCVOUT_MASK##_id, \
.enable_reg = RT5025_DCDC_OUTPUT_EN, \
.enable_bit = RT5025_DCDCEN_MASK##_id, \
+ .mode_reg = RT5025_REG_DCDCVRC, \
+ .mode_bit = RT5025_DCDCMODE_MASK##_id \
}
#define RT5025_LDO(_id, min, max) \
.vol_mask = RT5025_LDOVOUT_MASK##_id, \
.enable_reg = RT5025_LDO_OUTPUT_EN, \
.enable_bit = RT5025_LDOEN_MASK##_id, \
+ .mode_reg = RT5025_REG_LDOVRC, \
+ .mode_bit = RT5025_LDOMODE_MASK##_id, \
}
static struct rt5025_regulator_info rt5025_regulator_info[] =
#define RT5025_REG_CHGSTAT 0x01
#define RT5025_REG_IRQEN1 0x30
+#define RT5025_REG_IRQSTATUS1 0x31
#define RT5025_REG_IRQEN2 0x32
+#define RT5025_REG_IRQSTATUS2 0x33
#define RT5025_REG_IRQEN3 0x34
+#define RT5025_REG_IRQSTATUS3 0x35
#define RT5025_REG_IRQEN4 0x36
+#define RT5025_REG_IRQSTATUS4 0x37
#define RT5025_REG_IRQEN5 0x38
+#define RT5025_REG_IRQSTATUS5 0x39
+
+#define RT5025_REG_GAUGEIRQFLG 0x51
+#define RT5025_FLG_TEMP 0x30
+#define RT5025_FLG_VOLT 0x07
#endif /* #ifndef __LINUX_RT5025_IRQ_H */
#define __LINUX_MFD_RT5025_H
#include <linux/power_supply.h>
+#include <linux/android_alarm.h>
#define RT5025_DEVICE_NAME "RT5025"
}bitfield;
unsigned char val;
}CHGControl7;
- u32 fcc;
};
struct rt5025_gpio_data {
}irq_enable5;
};
+enum {
+ JEITA_NO_CHARGE,
+ JEITA_NORMAL_USB,
+ JEITA_USB_TA,
+ JEITA_AC_ADAPTER,
+ JEITA_CHARGER_MAX,
+};
+
+struct rt5025_jeita_data {
+ int* temp;
+ u8* temp_scalar;
+ int (*temp_cc)[5];
+ int (*temp_cv)[5];
+};
+
#define CHG_EVENT_INACOVP (0x80<<16)
#define CHG_EVENT_INAC_PLUGIN (0x40<<16)
#define CHG_EVENT_INUSBOVP (0x10<<16)
#define CHARGER_DETECT_MASK (CHG_EVENT_INAC_PLUGIN | CHG_EVENT_INUSB_PLUGIN | \
CHG_EVENT_CHSLPI_INAC | CHG_EVENT_CHSLPI_INUSB | \
- CHG_EVENT_CHBADI_INAC | CHG_EVENT_CHBADI_INUSB)
+ CHG_EVENT_CHBADI_INAC | CHG_EVENT_CHBADI_INUSB | \
+ CHG_EVENT_CHTERMI | CHG_EVENT_CHRCHGI)
+
#define PWR_EVENT_OTIQ (0x80<<8)
#define PWR_EVENT_DCDC1LV (0x40<<8)
#define PWR_EVENT_KPSHDN (0x80<<0)
#define PWR_EVNET_PWRONR (0x40<<0)
#define PWR_EVENT_PWRONF (0x20<<0)
-#define PWR_EVENT_RESETB (0x10<<0)
+#define PWR_EVENT_RESETB (0x10<<0)
#define PWR_EVENT_GPIO2IE (0x08<<0)
#define PWR_EVENT_GPIO1IE (0x04<<0)
#define PWR_EVENT_GPIO0IE (0x02<<0)
#endif
};
+struct rt5025_platform_data {
+ struct regulator_init_data* regulator[RT5025_MAX_REGULATOR];
+ struct rt5025_power_data* power_data;
+ struct rt5025_gpio_data* gpio_data;
+ struct rt5025_misc_data* misc_data;
+ struct rt5025_irq_data* irq_data;
+ struct rt5025_jeita_data* jeita_data;
+ struct rt5025_event_callback *cb;
+ int (*pre_init)(struct rt5025_chip *rt5025_chip);
+ /** Called after subdevices are set up */
+ int (*post_init)(void);
+ int intr_pin;
+};
+
struct rt5025_power_info {
struct i2c_client *i2c;
struct device *dev;
- struct rt5025_gauge_callbacks *event_callback;
+ struct rt5025_chip *chip;
+ //struct rt5025_gauge_callbacks *event_callback;
struct power_supply ac;
struct power_supply usb;
struct mutex var_lock;
struct delayed_work usb_detect_work;
int usb_cnt;
- u32 fcc;
+ int chg_term;
unsigned ac_online:1;
unsigned usb_online:1;
unsigned chg_stat:3;
};
+struct rt5025_swjeita_info {
+ struct i2c_client *i2c;
+ struct rt5025_chip *chip;
+ int *temp;
+ u8 *temp_scalar;
+ int (*temp_cc)[5];
+ int (*temp_cv)[5];
+ int cur_section;
+ int cur_cable;
+ int cur_temp;
+ int init_once;
+ int suspend;
+};
+
+struct rt5025_battery_info {
+ struct i2c_client *client;
+ struct rt5025_chip *chip;
+ //struct rt5025_gauge_callbacks cb;
+
+ struct power_supply battery;
+
+ struct delayed_work monitor_work;
+ struct wake_lock monitor_wake_lock;
+ struct wake_lock low_battery_wake_lock;
+//#if RT5025_TEST_WAKE_LOCK
+ struct wake_lock test_wake_lock;
+//#endif
+ struct alarm wakeup_alarm;
+
+ bool temp_range_0_5;
+ bool temp_range_5_10;
+ bool temp_range_10_15;
+ bool temp_range_15_20;
+ bool temp_range_20_30;
+ bool temp_range_30_35;
+ bool temp_range_35_40;
+ bool temp_range_40_45;
+ bool temp_range_45_50;
+
+ bool range_0_5_done;
+ bool range_5_10_done;
+ bool range_10_15_done;
+ bool range_15_20_done;
+ bool range_20_30_done;
+ bool range_30_35_done;
+ bool range_35_40_done;
+ bool range_40_45_done;
+ bool range_45_50_done;
+
+
+
+ bool suspend_poll;
+ ktime_t last_poll;
+// ktime_t last_event;
+ struct timespec last_event;
+
+ u16 update_time;
+
+ /* previous battery voltage */
+ u16 pre_vcell;
+ /* previous battery current */
+ s16 pre_curr;
+ /* battery voltage */
+ u16 vcell;
+ /* battery current */
+ s16 curr;
+ /* battery current offset */
+ u16 curr_offset;
+ /* AIN voltage */
+ u16 ain_volt;
+ /* battery external temperature */
+ s16 ext_temp;
+ /* charge coulomb counter */
+ u32 chg_cc;
+ u32 chg_cc_unuse;
+ /* discharge coulomb counter */
+ u32 dchg_cc;
+ u32 dchg_cc_unuse;
+ /* battery capacity */
+ u16 soc;
+ u16 temp_soc;
+ u16 pre_soc;
+
+ u16 time_interval;
+ u16 pre_gauge_timer;
+
+ u8 online;
+ u8 status;
+ u8 internal_status;
+ u8 health;
+ u8 present;
+
+ /* IRQ flag */
+ u8 irq_flag;
+
+ /* max voltage IRQ flag */
+ bool max_volt_irq;
+ /* min voltage1 IRQ flag */
+ bool min_volt1_irq;
+ /* min voltage2 IRQ flag */
+ bool min_volt2_irq;
+ /* max temperature IRQ flag */
+ bool max_temp_irq;
+ /* min temperature IRQ flag */
+ bool min_temp_irq;
+
+ bool min_volt2_alert;
+
+ u8 temp_high_cnt;
+ u8 temp_low_cnt;
+ u8 temp_recover_cnt;
+
+ bool init_cap;
+ bool avg_flag;
+
+ /* remain capacity */
+ u32 rm;
+ /* SOC permille */
+ u16 permille;
+ /* full capccity */
+ u16 fcc_aging;
+ u16 fcc;
+ u16 dc;
+ s16 tempcmp;
+ #if 0
+ u32 time_to_empty;
+ u32 time_to_full;
+ #endif
+
+ bool edv_flag;
+ bool edv_detection;
+ u8 edv_cnt;
+
+ bool tp_flag;
+ u8 tp_cnt;
+
+ u8 cycle_cnt;
+ u32 acc_dchg_cap;
+
+ bool smooth_flag;
+
+ u16 gauge_timer;
+ s16 curr_raw;
+
+ bool init_once;
+ bool device_suspend;
+ u8 test_temp;
+};
+
struct rt5025_chip {
struct i2c_client *i2c;
struct workqueue_struct *wq;
struct device *dev;
struct rt5025_power_info *power_info;
+ struct rt5025_swjeita_info *jeita_info;
+ struct rt5025_battery_info *battery_info;
int suspend;
int irq;
struct delayed_work delayed_work;
struct mutex io_lock;
};
-struct rt5025_platform_data {
- struct regulator_init_data* regulator[RT5025_MAX_REGULATOR];
- struct rt5025_power_data* power_data;
- struct rt5025_gpio_data* gpio_data;
- struct rt5025_misc_data* misc_data;
- struct rt5025_irq_data* irq_data;
- struct rt5025_event_callback *cb;
- int (*pre_init)(struct rt5025_chip *rt5025_chip);
- /** Called after subdevices are set up */
- int (*post_init)(void);
- int intr_pin;
-};
-
#ifdef CONFIG_MFD_RT5025_MISC
extern void rt5025_power_off(void);
#endif /* CONFIG_MFD_RT5025_MISC */
#ifdef CONFIG_POWER_RT5025
-extern int rt5025_gauge_init(struct rt5025_power_info *);
-extern int rt5025_power_passirq_to_gauge(struct rt5025_power_info *);
+extern void rt5025_gauge_set_status(struct rt5025_battery_info *, int);
+extern void rt5025_gauge_set_online(struct rt5025_battery_info *, bool);
+extern void rt5025_gauge_irq_handler(struct rt5025_battery_info *, u8);
extern int rt5025_power_charge_detect(struct rt5025_power_info *);
+extern int rt5025_notify_charging_cable(struct rt5025_swjeita_info *, int);
+extern int rt5025_swjeita_irq_handler(struct rt5025_swjeita_info *, unsigned char);
#endif /* CONFIG_POEWR_RT5025 */
extern int rt5025_reg_block_read(struct i2c_client *, int, int, void *);
--- /dev/null
+battery_graph_prop rt5025_battery_param1[] =
+{
+ {4190, 1000},
+ {4153, 980},
+ {4067, 890},
+ {3991, 800},
+ {3931, 710},
+ {3845, 580},
+ {3799, 490},
+ {3776, 400},
+ {3743, 240},
+ {3695, 140},
+ {3660, 70},
+ {3642, 50},
+ {3509, 20},
+ {3300, 0},
+};
+
+battery_graph_prop rt5025_battery_param2[] =
+{
+ {450,30},
+ {250, 0},
+ {50,-90},
+ {50, 10},
+ {3400, 6900},
+};
--- /dev/null
+/*
+ * include/linux/power/rt5025-battery.h
+ * Include header file for Richtek RT5025 battery Driver
+ *
+ * Copyright (C) 2013 Richtek Electronics
+ * cy_huang <cy_huang@richtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_RT5025_BATTERY_H
+#define __LINUX_RT5025_BATTERY_H
+
+#define ROCKCHIP_BATTERY_6200MAH
+#undef ROCKCHIP_BATTERY_2100MAH
+
+#define RT5025_REG_IRQ_CTL 0x50
+#define RT5025_REG_IRQ_FLAG 0x51
+#define RT5025_REG_VALRT_MAXTH 0x53
+#define RT5025_REG_VALRT_MIN1TH 0x54
+#define RT5025_REG_VALRT_MIN2TH 0x55
+#define RT5025_REG_TALRT_MAXTH 0x56
+#define RT5025_REG_TALRT_MINTH 0x57
+#define RT5025_REG_VCELL_MSB 0x58
+#define RT5025_REG_VCELL_LSB 0x59
+#define RT5025_REG_INT_TEMPERATUE_MSB 0x5B
+#define RT5025_REG_INT_TEMPERATUE_LSB 0x5C
+#define RT5025_REG_EXT_TEMPERATUE_MSB 0x5E
+#define RT5025_REG_EXT_TEMPERATUE_LSB 0x5F
+#define RT5025_REG_TIMER 0x60
+#define RT5025_REG_CHANNEL_MSB 0x62
+#define RT5025_REG_CHANNEL_LSB 0x63
+#define RT5025_REG_CURRENT_MSB 0x76
+#define RT5025_REG_CURRENT_LSB 0x77
+#define RT5025_REG_QCHGH_MSB 0x78
+#define RT5025_REG_QCHGH_LSB 0x79
+#define RT5025_REG_QCHGL_MSB 0x7A
+#define RT5025_REG_QCHGL_LSB 0x7B
+#define RT5025_REG_QDCHGH_MSB 0x7C
+#define RT5025_REG_QDCHGH_LSB 0x7D
+#define RT5025_REG_QDCHGL_MSB 0x7E
+#define RT5025_REG_QDCHGL_LSB 0x7F
+
+#define IRQ_CTL_BIT_TMX (1 << 5)
+#define IRQ_CTL_BIT_TMN (1 << 4)
+#define IRQ_CTL_BIT_VMX (1 << 2)
+#define IRQ_CTL_BIT_VMN1 (1 << 1)
+#define IRQ_CTL_BIT_VMN2 (1 << 0)
+
+#define IRQ_FLG_BIT_TMX (1 << 5)
+#define IRQ_FLG_BIT_TMN (1 << 4)
+#define IRQ_FLG_BIT_VMX (1 << 2)
+#define IRQ_FLG_BIT_VMN1 (1 << 1)
+#define IRQ_FLG_BIT_VMN2 (1 << 0)
+
+#define CHANNEL_H_BIT_CLRQDCHG (1 << 7)
+#define CHANNEL_H_BIT_CLRQCHG (1 << 6)
+
+#define CHANNEL_L_BIT_CADC_EN (1 << 7)
+#define CHANNEL_L_BIT_INTEMPCH (1 << 6)
+#define CHANNEL_L_BIT_AINCH (1 << 2)
+#define CHANNEL_L_BIT_VBATSCH (1 << 1)
+#define CHANNEL_L_BIT_VADC_EN (1 << 0)
+
+#define NORMAL_POLL 30 /* 30 sec */
+#define TP_POLL 5 /* 5 sec */
+#define EDV_POLL 1 /* 1 sec */
+#define SMOOTH_POLL 5 /* 5 sec */
+#define SUSPEND_POLL (30*60) /* 30 min */
+#define INIT_POLL 1
+#define LOW_BAT_WAKE_LOK_TIME 120
+
+#define HIGH_TEMP_THRES 650
+#define HIGH_TEMP_RECOVER 430
+#define LOW_TEMP_THRES (-30)
+#define LOW_TEMP_RECOVER 0
+#define TEMP_ABNORMAL_COUNT 3
+
+#define EDV_HYS 100
+#define IRQ_THRES_UNIT 1953
+
+#define TALRTMAX_VALUE 0x38 //65.39'C
+#define TALRTMIN_VALUE 0x9 //-18.75'C
+#define TRLS_VALUE 55 //5'C ; unit:mV
+#define VRLS_VALUE 100 //100mV
+
+
+#define DEADBAND 10
+
+//#define SLEEP_CURRENT 3 //mA
+
+typedef enum{
+ CHG,
+ DCHG
+}operation_mode;
+
+typedef struct{
+ int x;
+ int y;
+}battery_graph_prop;
+
+typedef enum {
+ MAXTEMP,
+ MINTEMP,
+ MAXVOLT,
+ MINVOLT1,
+ MINVOLT2,
+ TEMP_RLS,
+ VOLT_RLS,
+ LAST_TYPE,
+}alert_type;
+
+#if defined(ROCKCHIP_BATTERY_6200MAH)
+#include <linux/power/rockchip-6200ma-bat.h>
+#elif defined(ROCKCHIP_BATTERY_2100MAH)
+#include <linux/power/rockchip-2100ma-bat.h>
+#else
+#include <linux/power/rockchip-general-bat.h>
+#endif
+
+#define VALRTMIN2_VALUE (rt5025_battery_param2[4].x * 100 / IRQ_THRES_UNIT + 1) //EDV0 voltage
+
+#endif /* #ifndef __LINUX_RT5025_BATTERY_H */
+++ /dev/null
-/*
- * rt5025_gauge.h
- * fuel-gauge driver
- * revision 0.1
- */
-
-#ifndef __LINUX_RT5025_GAUGE_H_
-#define __LINUX_RT5025_GAUGE_H_
-
-#define GPIO_GAUGE_ALERT 4
-
-struct rt5025_gauge_callbacks {
- void (*rt5025_gauge_irq_handler)(void);
- void (*rt5025_gauge_set_status)(int status);
- void (*rt5025_gauge_suspend)(void);
- void (*rt5025_gauge_resume)(void);
- void (*rt5025_gauge_remove)(void);
-};
-
-
-typedef enum{
- CHG,
- DCHG
-}operation_mode;
-
-typedef enum{
- MAXTEMP,
- MINTEMP,
- MAXVOLT,
- MINVOLT1,
- MINVOLT2,
- TEMP_RLS,
- VOLT_RLS,
- LAST_TYPE
-}alert_type;
-
-#endif /* #ifndef __LINUX_RT5025_GAUGE_H_ */
#define RT5025_CHGBUCKEN_MASK 0x02
#define RT5025_CHGCEN_MASK 0x10
-#define RT5025_CHGCC_MASK 0x7E
+#define RT5025_CHGAICR_MASK 0x06
#define RT5025_CHGSTAT_MASK 0x30
#define RT5025_CHGSTAT_SHIFT 4
--- /dev/null
+/*
+ * include/linux/power/rt5025-swjeita.h
+ * Include header file for Richtek RT5025 Core Jeita Driver
+ *
+ * Copyright (C) 2013 Richtek Electronics
+ * cy_huang <cy_huang@richtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_RT5025_SWJEITA_H
+#define __LINUX_RT5025_SWJEITA_H
+
+#define RT5025_REG_CHGCTL3 0x03
+#define RT5025_REG_CHGCTL4 0x04
+#define RT5025_REG_CHGCTL7 0x07
+#define RT5025_REG_AINH 0x5E
+#define RT5025_REG_CHANNELL 0x63
+#define RT5025_REG_IRQCTL 0x50
+#define RT5025_REG_TALRTMAX 0x56
+#define RT5025_REG_TALRTMIN 0x57
+
+#define RT5025_CHGCCEN_MASK 0x10
+#define RT5025_CHGICC_SHIFT 3
+#define RT5025_CHGICC_MASK 0x78
+#define RT5025_CHGCV_SHIFT 2
+#define RT5025_CHGCV_MASK 0xFC
+#define RT5025_AINEN_MASK 0x04
+#define RT5025_TMXEN_MASK 0x20
+#define RT5025_TMNEN_MASK 0x10
+
+
+#endif /* #ifndef __LINUX_RT5025_SWJEITA_H */
#define RT5025_REG_LDOCTRL6 0x12
#define RT5025_REG_DCDCEN 0x17
#define RT5025_REG_LDOEN 0x18
+#define RT5025_REG_DCDCVRC 0x0B
+#define RT5025_REG_LDOVRC 0x00
#define RT5025_DCDCVOUT1 RT5025_REG_DCDCCTRL1
#define RT5025_DCDCVOUT2 RT5025_REG_DCDCCTRL2
#define RT5025_LDOEN_MASK5 0x10
#define RT5025_LDOEN_MASK6 0x20
+#define RT5025_DCDCMODE_MASK1 0x40
+#define RT5025_DCDCMODE_MASK2 0x20
+#define RT5025_DCDCMODE_MASK3 0x10
+#define RT5025_DCDCMODE_MASK4 0x00
+#define RT5025_LDOMODE_MASK1 0x00
+#define RT5025_LDOMODE_MASK2 0x00
+#define RT5025_LDOMODE_MASK3 0x00
+#define RT5025_LDOMODE_MASK4 0x00
+#define RT5025_LDOMODE_MASK5 0x00
+#define RT5025_LDOMODE_MASK6 0x00
+
#endif /* __LINUX_RT5025_REGULATOR_H */