2 * Copyright (C) 2013 Spreadtrum Communications Inc.
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/interrupt.h>
16 #include <linux/err.h>
17 #include <soc/sprd/hardware.h>
18 #include <soc/sprd/sci.h>
19 #include <soc/sprd/sci_glb_regs.h>
21 #include <soc/sprd/adi.h>
22 #include <linux/wakelock.h>
23 #include <linux/hrtimer.h>
24 #include <linux/delay.h>
25 #include <linux/device.h>
26 #include <linux/platform_device.h>
27 #include <linux/power_supply.h>
28 #include <linux/battery/sec_fuelgauge.h>
29 #include <linux/sprd_battery_common.h>
31 #define SPRDFGU_TEMP_COMP_SOC
32 int sprdbat_interpolate(int x, int n, struct sprdbat_table_data *tab)
39 else if (x <= tab[n - 1].x)
43 for (index = 1; index < n; index++)
47 y = (tab[index - 1].y - tab[index].y) * (x - tab[index].x)
48 * 2 / (tab[index - 1].x - tab[index].x);
54 static void print_pdata(struct sprd_battery_platform_data *pdata)
56 #define PDATA_LOG(format, arg...) printk("sprdbat pdata: " format, ## arg)
60 PDATA_LOG("chg_bat_safety_vol:%d\n", pdata->chg_bat_safety_vol);
62 PDATA_LOG("irq_fgu:%d\n", pdata->irq_fgu);
63 PDATA_LOG("fgu_mode:%d\n", pdata->fgu_mode);
64 PDATA_LOG("alm_soc:%d\n", pdata->alm_soc);
65 PDATA_LOG("alm_vol:%d\n", pdata->alm_vol);
66 PDATA_LOG("soft_vbat_uvlo:%d\n", pdata->soft_vbat_uvlo);
67 PDATA_LOG("soft_vbat_ovp:%d\n", pdata->soft_vbat_ovp);
68 PDATA_LOG("rint:%d\n", pdata->rint);
69 PDATA_LOG("cnom:%d\n", pdata->cnom);
70 PDATA_LOG("rsense_real:%d\n", pdata->rsense_real);
71 PDATA_LOG("rsense_spec:%d\n", pdata->rsense_spec);
72 PDATA_LOG("relax_current:%d\n", pdata->relax_current);
73 PDATA_LOG("fgu_cal_ajust:%d\n", pdata->fgu_cal_ajust);
74 PDATA_LOG("qmax_update_period:%d\n", pdata->qmax_update_period);
75 PDATA_LOG("ocv_tab_size:%d\n", pdata->ocv_tab_size);
76 for (i = 0; i < pdata->ocv_tab_size; i++) {
77 PDATA_LOG("ocv_tab i=%d x:%d,y:%d\n", i, pdata->ocv_tab[i].x,
80 for (i = 0; i < pdata->temp_tab_size; i++) {
81 PDATA_LOG("temp_tab_size i=%d x:%d,y:%d\n", i,
82 pdata->temp_tab[i].x, pdata->temp_tab[i].y);
84 for (i = 0; i < pdata->cnom_temp_tab_size; i++) {
85 PDATA_LOG("cnom_temp_tab i=%d x:%d,y:%d\n", i, pdata->cnom_temp_tab[i].x,
86 pdata->cnom_temp_tab[i].y);
88 for (i = 0; i < pdata->rint_temp_tab_size; i++) {
89 PDATA_LOG("rint_temp_tab i=%d x:%d,y:%d\n", i, pdata->rint_temp_tab[i].x,
90 pdata->rint_temp_tab[i].y);
95 static uint32_t init_flag = 0;
96 static uint32_t init_temp_flag = 0;
98 #define SPRDBAT_ONE_PERCENT_TIME (40)
99 #define SPRDBAT_AVOID_JUMPING_TEMI (SPRDBAT_ONE_PERCENT_TIME)
101 static unsigned long sprdbat_update_capacity_time;
102 static uint32_t update_capacity;
104 static unsigned long sprdbat_last_query_time;
105 struct sprd_battery_platform_data *bat_pdata;
107 bool sec_hal_fg_init(fuelgauge_variable_t * fg_var)
109 struct sprd_battery_platform_data *pdata = (&get_battery_data(fg_var))->pdata ;
111 pr_info("register sec_hal_fg_init\n");
112 print_pdata(pdata); //debug
115 struct timespec cur_time;
116 get_monotonic_boottime(&cur_time);
117 sprdbat_update_capacity_time = cur_time.tv_sec;
118 update_capacity = sprdfgu_poweron_capacity();
124 static uint32_t sprdbat_update_capacty(void)
126 uint32_t fgu_capacity = sprdfgu_read_capacity();
127 uint32_t flush_time = 0;
128 uint32_t period_time = 0;
129 struct timespec cur_time;
130 union power_supply_propval value;
132 union power_supply_propval temp_value;
134 psy_do_property("battery", get, POWER_SUPPLY_PROP_TEMP, temp_value);
135 temp_degree = temp_value.intval / 10;
137 psy_do_property("battery", get, POWER_SUPPLY_PROP_STATUS, value);
139 get_monotonic_boottime(&cur_time);
140 flush_time = cur_time.tv_sec - sprdbat_update_capacity_time;
141 period_time = cur_time.tv_sec - sprdbat_last_query_time;
142 sprdbat_last_query_time = cur_time.tv_sec;
144 pr_info("fgu_capacity: = %d,flush_time: = %d,period_time:=%d\n",
145 fgu_capacity, flush_time, period_time);
146 if ((!init_temp_flag) && (temp_degree < 16)) {
147 pr_info("temp init skip\n");
151 switch (value.intval) {
152 case POWER_SUPPLY_STATUS_CHARGING:
153 #ifdef SPRDFGU_TEMP_COMP_SOC
154 if ((fgu_capacity < update_capacity) && ((temp_degree > 15) || \
155 (update_capacity >= 2))) {
157 if (fgu_capacity < update_capacity) {
159 if (sprdfgu_read_batcurrent() > 0) {
160 pr_info("avoid vol jumping\n");
161 fgu_capacity = update_capacity;
163 if (period_time <= SPRDBAT_AVOID_JUMPING_TEMI) {
164 fgu_capacity =update_capacity - 1;
165 pr_info("avoid jumping! fgu_capacity: = %d\n", fgu_capacity);
167 if ((update_capacity - fgu_capacity) >=
168 (flush_time / SPRDBAT_ONE_PERCENT_TIME)) {
171 flush_time / SPRDBAT_ONE_PERCENT_TIME;
174 } else if (fgu_capacity > update_capacity) {
175 if (period_time <= SPRDBAT_AVOID_JUMPING_TEMI) {
178 pr_info("avoid jumping! fgu_capacity: = %d\n",fgu_capacity);
180 if ((fgu_capacity - update_capacity) >=
181 (flush_time / SPRDBAT_ONE_PERCENT_TIME)) {
184 flush_time / SPRDBAT_ONE_PERCENT_TIME;
187 /*when soc=100 and adp plugin occur, keep on 100, */
188 if (100 == update_capacity) {
189 sprdbat_update_capacity_time = cur_time.tv_sec;
193 /* skip this code to avoid not full in charger when current comsuption is high */
195 if (fgu_capacity >= 100) {
202 case POWER_SUPPLY_STATUS_NOT_CHARGING:
203 case POWER_SUPPLY_STATUS_DISCHARGING:
204 if (fgu_capacity >= update_capacity) {
205 fgu_capacity = update_capacity;
207 if (period_time <= SPRDBAT_AVOID_JUMPING_TEMI) {
211 ("avoid jumping! fgu_capacity: = %d\n",
214 if ((update_capacity - fgu_capacity) >=
215 (flush_time / SPRDBAT_ONE_PERCENT_TIME)) {
218 flush_time / SPRDBAT_ONE_PERCENT_TIME;
222 if (sprdfgu_read_vbat_vol() < bat_pdata->soft_vbat_uvlo) {
223 pr_info("soft uvlo vol 0...\n");
228 case POWER_SUPPLY_STATUS_FULL:
229 sprdbat_update_capacity_time = cur_time.tv_sec;
230 if (fgu_capacity != 100) {
238 if (fgu_capacity != update_capacity) {
239 update_capacity = fgu_capacity;
240 sprdbat_update_capacity_time = cur_time.tv_sec;
242 sprdfgu_record_cap(update_capacity);
243 return update_capacity;
247 bool sec_hal_fg_reset(fuelgauge_variable_t * fg_var)
249 msleep(1000); //wait a moment,insure voltage REG of FG is updated after power supply voltage be changed
252 struct timespec cur_time;
253 get_monotonic_boottime(&cur_time);
254 sprdbat_update_capacity_time = cur_time.tv_sec; //time need to be updated,too.
255 update_capacity = sprdfgu_poweron_capacity(); //same as your change
259 bool sec_hal_fg_suspend(fuelgauge_variable_t * fg_var)
265 bool sec_hal_fg_resume(fuelgauge_variable_t * fg_var)
270 bool sec_hal_fg_fuelalert_init(fuelgauge_variable_t * fg_var , int val)
274 bool sec_hal_fg_is_fuelalerted(fuelgauge_variable_t * fg_var)
278 bool sec_hal_fg_full_charged(fuelgauge_variable_t * fg_var)
283 bool sec_hal_fg_fuelalert_process(void *irq_data, bool is_fuel_alerted)
288 bool sec_hal_fg_get_property(fuelgauge_variable_t * fg_var,
289 enum power_supply_property psp,
290 union power_supply_propval *val)
295 /* Cell voltage (VCELL, mV) */
296 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
297 val->intval = sprdfgu_read_vbat_vol();
300 /* Additional Voltage Information (mV) */
301 case POWER_SUPPLY_PROP_VOLTAGE_AVG:
302 switch (val->intval) {
303 case SEC_BATTEY_VOLTAGE_AVERAGE:
304 val->intval = sprdfgu_read_vbat_vol();
306 case SEC_BATTEY_VOLTAGE_OCV:
307 val->intval = sprdfgu_read_vbat_ocv();
313 case POWER_SUPPLY_PROP_CURRENT_NOW:
314 val->intval = sprdfgu_read_batcurrent();
317 /* Average Current (mA) */
318 case POWER_SUPPLY_PROP_CURRENT_AVG:
319 val->intval = sprdfgu_read_batcurrent();
323 case POWER_SUPPLY_PROP_CAPACITY:
326 int cap = sprdfgu_read_capacity();
328 union power_supply_propval value;
329 psy_do_property("battery", get,
330 POWER_SUPPLY_PROP_STATUS,
332 if (POWER_SUPPLY_STATUS_CHARGING ==
335 ("sec_hal_fg_get_property soc == 100, but is charging\n",
341 int cap = sprdbat_update_capacty();
343 val->intval = cap * 10;
349 case POWER_SUPPLY_PROP_PRESENT:
353 case POWER_SUPPLY_PROP_ENERGY_NOW:
357 /* Battery Temperature */
358 case POWER_SUPPLY_PROP_TEMP:
362 /* Target Temperature */
363 case POWER_SUPPLY_PROP_TEMP_AMBIENT:
370 pr_debug("sec_hal_fg_get_property-%d,val->intval = %d\n", psp,
375 bool sec_hal_fg_set_property(fuelgauge_variable_t * fg_var,
376 enum power_supply_property psp,
377 const union power_supply_propval *val)
381 case POWER_SUPPLY_PROP_ONLINE:
382 if (val->intval == POWER_SUPPLY_TYPE_BATTERY) {
383 sprdfgu_adp_status_set(0);
385 sprdfgu_adp_status_set(1);
388 /* Battery Temperature */
389 case POWER_SUPPLY_PROP_TEMP:
391 /* Target Temperature */
392 case POWER_SUPPLY_PROP_TEMP_AMBIENT: