2 * Battery measurement code for Ingenic JZ SOC.
4 * based on tosa_battery.c
6 * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
7 * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/power_supply.h>
17 #include <linux/delay.h>
18 #include <linux/spinlock.h>
19 #include <linux/interrupt.h>
20 #include <linux/platform_device.h>
21 #include <linux/gpio.h>
23 #include <linux/power/jz4740-battery.h>
24 #include <linux/jz4740-adc.h>
26 struct jz_battery_info
{
27 struct power_supply usb
;
28 struct power_supply bat
;
29 struct power_supply ac
;
31 struct jz_batt_info
*pdata
;
32 struct mutex work_lock
;
33 struct workqueue_struct
*monitor_wqueue
;
34 struct delayed_work bat_work
;
37 #define ps_to_jz_battery(x) container_of((x), struct jz_battery_info, bat);
39 /*********************************************************************
41 *********************************************************************/
44 static int jz_get_power_prop(struct jz_battery_info
*bat_info
,
45 struct power_supply
*psy
,
46 enum power_supply_property psp
,
47 union power_supply_propval
*val
)
51 if (bat_info
== 0 || bat_info
->pdata
== 0)
53 gpio
= (psy
->type
== POWER_SUPPLY_TYPE_MAINS
) ?
54 bat_info
->pdata
->dc_dect_gpio
:
55 bat_info
->pdata
->usb_dect_gpio
;
56 if (!gpio_is_valid(gpio
))
59 case POWER_SUPPLY_PROP_ONLINE
:
60 val
->intval
= !gpio_get_value(gpio
);
69 static int jz_usb_get_power_prop(struct power_supply
*psy
,
70 enum power_supply_property psp
,
71 union power_supply_propval
*val
)
73 struct jz_battery_info
*bat_info
= container_of(psy
, struct jz_battery_info
, usb
);
74 return jz_get_power_prop(bat_info
, psy
, psp
, val
);
77 static int jz_ac_get_power_prop(struct power_supply
*psy
,
78 enum power_supply_property psp
,
79 union power_supply_propval
*val
)
81 struct jz_battery_info
*bat_info
= container_of(psy
, struct jz_battery_info
, ac
);
82 return jz_get_power_prop(bat_info
, psy
, psp
, val
);
86 static enum power_supply_property jz_power_props
[] = {
87 POWER_SUPPLY_PROP_ONLINE
,
90 static struct power_supply jz_ac
= {
92 .type
= POWER_SUPPLY_TYPE_MAINS
,
93 .properties
= jz_power_props
,
94 .num_properties
= ARRAY_SIZE(jz_power_props
),
95 .get_property
= jz_ac_get_power_prop
,
98 static struct power_supply jz_usb
= {
100 .type
= POWER_SUPPLY_TYPE_USB
,
101 .properties
= jz_power_props
,
102 .num_properties
= ARRAY_SIZE(jz_power_props
),
103 .get_property
= jz_usb_get_power_prop
,
107 /*********************************************************************
109 *********************************************************************/
111 static long jz_read_bat(struct power_supply
*psy
)
113 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
);
114 enum jz_adc_battery_scale scale
;
116 if (bat_info
->pdata
->max_voltag
> 2500000)
117 scale
= JZ_ADC_BATTERY_SCALE_7V5
;
119 scale
= JZ_ADC_BATTERY_SCALE_2V5
;
121 return jz4740_adc_read_battery_voltage(psy
->dev
->parent
->parent
, scale
);
124 static int jz_bat_get_capacity(struct power_supply
*psy
)
127 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
);
129 ret
= jz_read_bat(psy
);
134 ret
= (ret
- bat_info
->pdata
->min_voltag
) * 100
135 / (bat_info
->pdata
->max_voltag
- bat_info
->pdata
->min_voltag
);
145 static int jz_bat_get_property(struct power_supply
*psy
,
146 enum power_supply_property psp
,
147 union power_supply_propval
*val
)
149 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
)
152 case POWER_SUPPLY_PROP_STATUS
:
153 val
->intval
= bat_info
->bat_status
;
155 case POWER_SUPPLY_PROP_TECHNOLOGY
:
156 val
->intval
= bat_info
->pdata
->batt_tech
;
158 case POWER_SUPPLY_PROP_HEALTH
:
159 if(jz_read_bat(psy
) < bat_info
->pdata
->min_voltag
) {
160 dev_dbg(psy
->dev
, "%s: battery is dead,"
161 "voltage too low!\n", __func__
);
162 val
->intval
= POWER_SUPPLY_HEALTH_DEAD
;
164 dev_dbg(psy
->dev
, "%s: battery is good,"
165 "voltage normal.\n", __func__
);
166 val
->intval
= POWER_SUPPLY_HEALTH_GOOD
;
169 case POWER_SUPPLY_PROP_CAPACITY
:
170 val
->intval
= jz_bat_get_capacity(psy
);
171 dev_dbg(psy
->dev
, "%s: battery_capacity = %d\n",
172 __func__
, val
->intval
);
174 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
175 val
->intval
= jz_read_bat(psy
);
179 case POWER_SUPPLY_PROP_VOLTAGE_MAX
:
180 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
:
181 val
->intval
= bat_info
->pdata
->max_voltag
;
183 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
:
184 val
->intval
= bat_info
->pdata
->min_voltag
;
186 case POWER_SUPPLY_PROP_PRESENT
:
195 static void jz_bat_external_power_changed(struct power_supply
*psy
)
197 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
);
199 cancel_delayed_work(&bat_info
->bat_work
);
200 queue_delayed_work(bat_info
->monitor_wqueue
, &bat_info
->bat_work
, HZ
/ 8);
203 static char *status_text
[] = {
204 [POWER_SUPPLY_STATUS_UNKNOWN
] = "Unknown",
205 [POWER_SUPPLY_STATUS_CHARGING
] = "Charging",
206 [POWER_SUPPLY_STATUS_DISCHARGING
] = "Discharging",
207 [POWER_SUPPLY_STATUS_NOT_CHARGING
] = "Not charging",
210 static void jz_bat_update(struct power_supply
*psy
)
212 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
);
214 int old_status
= bat_info
->bat_status
;
215 static unsigned long old_batt_vol
= 0;
216 unsigned long batt_vol
= jz_read_bat(psy
);
218 mutex_lock(&bat_info
->work_lock
);
220 if (gpio_is_valid(bat_info
->pdata
->charg_stat_gpio
)) {
221 if(!gpio_get_value(bat_info
->pdata
->charg_stat_gpio
))
222 bat_info
->bat_status
= POWER_SUPPLY_STATUS_CHARGING
;
224 bat_info
->bat_status
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
225 dev_dbg(psy
->dev
, "%s: battery status=%s\n",
226 __func__
, status_text
[bat_info
->bat_status
]);
228 if (old_status
!= bat_info
->bat_status
) {
229 dev_dbg(psy
->dev
, "%s %s -> %s\n",
231 status_text
[old_status
],
232 status_text
[bat_info
->bat_status
]);
234 power_supply_changed(psy
);
238 if (old_batt_vol
- batt_vol
> 50000) {
239 dev_dbg(psy
->dev
, "voltage change : %ld -> %ld\n",
240 old_batt_vol
, batt_vol
);
241 power_supply_changed(psy
);
242 old_batt_vol
= batt_vol
;
245 mutex_unlock(&bat_info
->work_lock
);
248 static enum power_supply_property jz_bat_main_props
[] = {
249 POWER_SUPPLY_PROP_STATUS
,
250 POWER_SUPPLY_PROP_TECHNOLOGY
,
251 POWER_SUPPLY_PROP_HEALTH
,
252 POWER_SUPPLY_PROP_CAPACITY
, /* in percents! */
253 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
254 POWER_SUPPLY_PROP_VOLTAGE_MAX
,
255 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
256 POWER_SUPPLY_PROP_PRESENT
,
259 struct power_supply bat_ps
= {
261 .type
= POWER_SUPPLY_TYPE_BATTERY
,
262 .properties
= jz_bat_main_props
,
263 .num_properties
= ARRAY_SIZE(jz_bat_main_props
),
264 .get_property
= jz_bat_get_property
,
265 .external_power_changed
= jz_bat_external_power_changed
,
269 static void jz_bat_work(struct work_struct
*work
)
271 /* query interval too small will increase system workload*/
272 const int interval
= HZ
* 30;
273 struct jz_battery_info
*bat_info
= container_of(work
,struct jz_battery_info
, bat_work
.work
);
275 jz_bat_update(&bat_info
->bat
);
276 queue_delayed_work(bat_info
->monitor_wqueue
,
277 &bat_info
->bat_work
, interval
);
281 static int jz_bat_suspend(struct platform_device
*pdev
, pm_message_t state
)
283 struct jz_battery_info
*bat_info
= platform_get_drvdata(pdev
);
285 bat_info
->bat_status
= POWER_SUPPLY_STATUS_UNKNOWN
;
290 static int jz_bat_resume(struct platform_device
*pdev
)
292 struct jz_battery_info
*bat_info
= platform_get_drvdata(pdev
);
294 bat_info
->bat_status
= POWER_SUPPLY_STATUS_UNKNOWN
;
296 cancel_delayed_work(&bat_info
->bat_work
);
297 queue_delayed_work(bat_info
->monitor_wqueue
, &bat_info
->bat_work
, HZ
/10);
302 #define jz_bat_suspend NULL
303 #define jz_bat_resume NULL
306 static int jz_bat_probe(struct platform_device
*pdev
)
309 struct jz_battery_info
*bat_info
;
311 bat_info
= kzalloc(sizeof(struct jz_battery_info
), GFP_KERNEL
);
317 if (!pdev
->dev
.platform_data
) {
318 dev_err(&pdev
->dev
, "Please set battery info\n");
320 goto err_platform_data
;
322 platform_set_drvdata(pdev
, bat_info
);
323 bat_info
->pdata
= pdev
->dev
.platform_data
;
324 bat_info
->bat
= bat_ps
;
325 bat_info
->usb
= jz_usb
;
326 bat_info
->ac
= jz_ac
;
327 mutex_init(&bat_info
->work_lock
);
328 INIT_DELAYED_WORK(&bat_info
->bat_work
, jz_bat_work
);
330 if (gpio_is_valid(bat_info
->pdata
->dc_dect_gpio
)) {
331 ret
= gpio_request(bat_info
->pdata
->dc_dect_gpio
, "AC/DC DECT");
333 dev_err(&pdev
->dev
, "ac/dc dect gpio request failed.\n");
335 goto err_dc_gpio_request
;
337 ret
= gpio_direction_input(bat_info
->pdata
->dc_dect_gpio
);
339 dev_err(&pdev
->dev
, "ac/dc dect gpio direction failed.\n");
341 goto err_dc_gpio_direction
;
345 if (gpio_is_valid(bat_info
->pdata
->usb_dect_gpio
)) {
346 ret
= gpio_request(bat_info
->pdata
->usb_dect_gpio
, "USB DECT");
348 dev_err(&pdev
->dev
, "usb dect gpio request failed.\n");
350 goto err_usb_gpio_request
;
352 ret
= gpio_direction_input(bat_info
->pdata
->usb_dect_gpio
);
354 dev_err(&pdev
->dev
, "usb dect gpio set direction failed.\n");
355 goto err_usb_gpio_direction
;
358 jz_gpio_disable_pullup(bat_info
->pdata
->usb_dect_gpio
);
359 /* TODO: Use generic gpio is better */
362 if (gpio_is_valid(bat_info
->pdata
->charg_stat_gpio
)) {
363 ret
= gpio_request(bat_info
->pdata
->charg_stat_gpio
, "CHARG STAT");
365 dev_err(&pdev
->dev
, "charger state gpio request failed.\n");
366 goto err_charg_gpio_request
;
368 ret
= gpio_direction_input(bat_info
->pdata
->charg_stat_gpio
);
370 dev_err(&pdev
->dev
, "charger state gpio set direction failed.\n");
371 goto err_charg_gpio_direction
;
375 if (gpio_is_valid(bat_info
->pdata
->dc_dect_gpio
)) {
376 ret
= power_supply_register(&pdev
->dev
, &bat_info
->ac
);
378 dev_err(&pdev
->dev
, "power supply ac/dc register failed.\n");
379 goto err_power_register_ac
;
383 if (gpio_is_valid(bat_info
->pdata
->usb_dect_gpio
)) {
384 ret
= power_supply_register(&pdev
->dev
, &bat_info
->usb
);
386 dev_err(&pdev
->dev
, "power supply usb register failed.\n");
387 goto err_power_register_usb
;
391 if (gpio_is_valid(bat_info
->pdata
->charg_stat_gpio
)) {
392 ret
= power_supply_register(&pdev
->dev
, &bat_info
->bat
);
394 dev_err(&pdev
->dev
, "power supply battery register failed.\n");
395 goto err_power_register_bat
;
397 bat_info
->monitor_wqueue
= create_singlethread_workqueue("jz_battery");
398 if (!bat_info
->monitor_wqueue
) {
401 queue_delayed_work(bat_info
->monitor_wqueue
, &bat_info
->bat_work
, HZ
* 1);
404 printk(KERN_INFO
"jz_bat init success.\n");
407 err_power_register_bat
:
408 power_supply_unregister(&bat_info
->usb
);
409 err_power_register_usb
:
410 power_supply_unregister(&bat_info
->ac
);
411 err_power_register_ac
:
412 err_charg_gpio_direction
:
413 gpio_free(bat_info
->pdata
->charg_stat_gpio
);
414 err_charg_gpio_request
:
415 err_usb_gpio_direction
:
416 gpio_free(bat_info
->pdata
->usb_dect_gpio
);
417 err_usb_gpio_request
:
418 err_dc_gpio_direction
:
419 gpio_free(bat_info
->pdata
->dc_dect_gpio
);
426 static int jz_bat_remove(struct platform_device
*pdev
)
428 struct jz_battery_info
*bat_info
= platform_get_drvdata(pdev
);
430 if (bat_info
->pdata
) {
431 if (gpio_is_valid(bat_info
->pdata
->dc_dect_gpio
))
432 gpio_free(bat_info
->pdata
->dc_dect_gpio
);
433 if (gpio_is_valid(bat_info
->pdata
->usb_dect_gpio
))
434 gpio_free(bat_info
->pdata
->usb_dect_gpio
);
435 if (gpio_is_valid(bat_info
->pdata
->charg_stat_gpio
))
436 gpio_free(bat_info
->pdata
->charg_stat_gpio
);
439 power_supply_unregister(&bat_ps
);
440 power_supply_unregister(&jz_ac
);
441 power_supply_unregister(&jz_usb
);
446 static struct platform_driver jz_bat_driver
= {
447 .probe
= jz_bat_probe
,
448 .remove
= __devexit_p(jz_bat_remove
),
449 .suspend
= jz_bat_suspend
,
450 .resume
= jz_bat_resume
,
452 .name
= "jz4740-battery",
453 .owner
= THIS_MODULE
,
457 static int __init
jz_bat_init(void)
459 return platform_driver_register(&jz_bat_driver
);
461 module_init(jz_bat_init
);
463 static void __exit
jz_bat_exit(void)
465 platform_driver_unregister(&jz_bat_driver
);
467 module_exit(jz_bat_exit
);
469 MODULE_LICENSE("GPL");
470 MODULE_AUTHOR("Jiejing Zhang <kzjeef@gmail.com>");
471 MODULE_DESCRIPTION("JZ4720/JZ4740 SoC battery driver");