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 bat
;
29 struct jz_batt_info
*pdata
;
30 struct mutex work_lock
;
31 struct workqueue_struct
*monitor_wqueue
;
32 struct delayed_work bat_work
;
35 #define ps_to_jz_battery(x) container_of((x), struct jz_battery_info, bat);
37 /*********************************************************************
39 *********************************************************************/
41 static long jz_read_bat(struct power_supply
*psy
)
43 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
);
44 enum jz_adc_battery_scale scale
;
46 if (bat_info
->pdata
->max_voltag
> 2500000)
47 scale
= JZ_ADC_BATTERY_SCALE_7V5
;
49 scale
= JZ_ADC_BATTERY_SCALE_2V5
;
51 return jz4740_adc_read_battery_voltage(psy
->dev
->parent
->parent
, scale
);
54 static int jz_bat_get_capacity(struct power_supply
*psy
)
57 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
);
59 ret
= jz_read_bat(psy
);
64 ret
= (ret
- bat_info
->pdata
->min_voltag
) * 100
65 / (bat_info
->pdata
->max_voltag
- bat_info
->pdata
->min_voltag
);
75 static int jz_bat_get_property(struct power_supply
*psy
,
76 enum power_supply_property psp
,
77 union power_supply_propval
*val
)
79 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
)
82 case POWER_SUPPLY_PROP_STATUS
:
83 val
->intval
= bat_info
->bat_status
;
85 case POWER_SUPPLY_PROP_TECHNOLOGY
:
86 val
->intval
= bat_info
->pdata
->batt_tech
;
88 case POWER_SUPPLY_PROP_HEALTH
:
89 if(jz_read_bat(psy
) < bat_info
->pdata
->min_voltag
) {
90 dev_dbg(psy
->dev
, "%s: battery is dead,"
91 "voltage too low!\n", __func__
);
92 val
->intval
= POWER_SUPPLY_HEALTH_DEAD
;
94 dev_dbg(psy
->dev
, "%s: battery is good,"
95 "voltage normal.\n", __func__
);
96 val
->intval
= POWER_SUPPLY_HEALTH_GOOD
;
99 case POWER_SUPPLY_PROP_CAPACITY
:
100 val
->intval
= jz_bat_get_capacity(psy
);
101 dev_dbg(psy
->dev
, "%s: battery_capacity = %d\n",
102 __func__
, val
->intval
);
104 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
105 val
->intval
= jz_read_bat(psy
);
109 case POWER_SUPPLY_PROP_VOLTAGE_MAX
:
110 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
:
111 val
->intval
= bat_info
->pdata
->max_voltag
;
113 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
:
114 val
->intval
= bat_info
->pdata
->min_voltag
;
116 case POWER_SUPPLY_PROP_PRESENT
:
125 static void jz_bat_external_power_changed(struct power_supply
*psy
)
127 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
);
129 cancel_delayed_work(&bat_info
->bat_work
);
130 queue_delayed_work(bat_info
->monitor_wqueue
, &bat_info
->bat_work
, HZ
/ 8);
133 static char *status_text
[] = {
134 [POWER_SUPPLY_STATUS_UNKNOWN
] = "Unknown",
135 [POWER_SUPPLY_STATUS_CHARGING
] = "Charging",
136 [POWER_SUPPLY_STATUS_DISCHARGING
] = "Discharging",
137 [POWER_SUPPLY_STATUS_NOT_CHARGING
] = "Not charging",
140 static void jz_bat_update(struct power_supply
*psy
)
142 struct jz_battery_info
*bat_info
= ps_to_jz_battery(psy
);
144 int old_status
= bat_info
->bat_status
;
145 static unsigned long old_batt_vol
= 0;
146 unsigned long batt_vol
= jz_read_bat(psy
);
148 mutex_lock(&bat_info
->work_lock
);
150 if (gpio_is_valid(bat_info
->pdata
->charg_stat_gpio
)) {
151 if(!gpio_get_value(bat_info
->pdata
->charg_stat_gpio
))
152 bat_info
->bat_status
= POWER_SUPPLY_STATUS_CHARGING
;
154 bat_info
->bat_status
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
155 dev_dbg(psy
->dev
, "%s: battery status=%s\n",
156 __func__
, status_text
[bat_info
->bat_status
]);
158 if (old_status
!= bat_info
->bat_status
) {
159 dev_dbg(psy
->dev
, "%s %s -> %s\n",
161 status_text
[old_status
],
162 status_text
[bat_info
->bat_status
]);
164 power_supply_changed(psy
);
168 if (old_batt_vol
- batt_vol
> 50000) {
169 dev_dbg(psy
->dev
, "voltage change : %ld -> %ld\n",
170 old_batt_vol
, batt_vol
);
171 power_supply_changed(psy
);
172 old_batt_vol
= batt_vol
;
175 mutex_unlock(&bat_info
->work_lock
);
178 static enum power_supply_property jz_bat_main_props
[] = {
179 POWER_SUPPLY_PROP_STATUS
,
180 POWER_SUPPLY_PROP_TECHNOLOGY
,
181 POWER_SUPPLY_PROP_HEALTH
,
182 POWER_SUPPLY_PROP_CAPACITY
, /* in percents! */
183 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
184 POWER_SUPPLY_PROP_VOLTAGE_MAX
,
185 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
186 POWER_SUPPLY_PROP_PRESENT
,
189 struct power_supply bat_ps
= {
191 .type
= POWER_SUPPLY_TYPE_BATTERY
,
192 .properties
= jz_bat_main_props
,
193 .num_properties
= ARRAY_SIZE(jz_bat_main_props
),
194 .get_property
= jz_bat_get_property
,
195 .external_power_changed
= jz_bat_external_power_changed
,
199 static void jz_bat_work(struct work_struct
*work
)
201 /* query interval too small will increase system workload*/
202 const int interval
= HZ
* 30;
203 struct jz_battery_info
*bat_info
= container_of(work
,struct jz_battery_info
, bat_work
.work
);
205 jz_bat_update(&bat_info
->bat
);
206 queue_delayed_work(bat_info
->monitor_wqueue
,
207 &bat_info
->bat_work
, interval
);
211 static int jz_bat_suspend(struct platform_device
*pdev
, pm_message_t state
)
213 struct jz_battery_info
*bat_info
= platform_get_drvdata(pdev
);
215 bat_info
->bat_status
= POWER_SUPPLY_STATUS_UNKNOWN
;
220 static int jz_bat_resume(struct platform_device
*pdev
)
222 struct jz_battery_info
*bat_info
= platform_get_drvdata(pdev
);
224 bat_info
->bat_status
= POWER_SUPPLY_STATUS_UNKNOWN
;
226 cancel_delayed_work(&bat_info
->bat_work
);
227 queue_delayed_work(bat_info
->monitor_wqueue
, &bat_info
->bat_work
, HZ
/10);
232 #define jz_bat_suspend NULL
233 #define jz_bat_resume NULL
236 static int jz_bat_probe(struct platform_device
*pdev
)
239 struct jz_battery_info
*bat_info
;
241 if (!pdev
->dev
.platform_data
) {
242 dev_err(&pdev
->dev
, "Please set battery info\n");
246 bat_info
= kzalloc(sizeof(struct jz_battery_info
), GFP_KERNEL
);
252 platform_set_drvdata(pdev
, bat_info
);
253 bat_info
->pdata
= pdev
->dev
.platform_data
;
254 bat_info
->bat
= bat_ps
;
255 mutex_init(&bat_info
->work_lock
);
256 INIT_DELAYED_WORK(&bat_info
->bat_work
, jz_bat_work
);
258 if (gpio_is_valid(bat_info
->pdata
->charg_stat_gpio
)) {
259 ret
= gpio_request(bat_info
->pdata
->charg_stat_gpio
, "CHARG STAT");
261 dev_err(&pdev
->dev
, "charger state gpio request failed.\n");
262 goto err_charg_gpio_request
;
264 ret
= gpio_direction_input(bat_info
->pdata
->charg_stat_gpio
);
266 dev_err(&pdev
->dev
, "charger state gpio set direction failed.\n");
267 goto err_charg_gpio_direction
;
270 ret
= power_supply_register(&pdev
->dev
, &bat_info
->bat
);
272 dev_err(&pdev
->dev
, "power supply battery register failed.\n");
273 goto err_power_register_bat
;
275 bat_info
->monitor_wqueue
= create_singlethread_workqueue("jz_battery");
276 if (!bat_info
->monitor_wqueue
) {
279 queue_delayed_work(bat_info
->monitor_wqueue
, &bat_info
->bat_work
, HZ
* 1);
282 printk(KERN_INFO
"jz_bat init success.\n");
285 err_power_register_bat
:
286 err_charg_gpio_direction
:
287 gpio_free(bat_info
->pdata
->charg_stat_gpio
);
288 err_charg_gpio_request
:
293 static int jz_bat_remove(struct platform_device
*pdev
)
295 struct jz_battery_info
*bat_info
= platform_get_drvdata(pdev
);
297 if (bat_info
->pdata
) {
298 if (gpio_is_valid(bat_info
->pdata
->charg_stat_gpio
))
299 gpio_free(bat_info
->pdata
->charg_stat_gpio
);
302 power_supply_unregister(&bat_ps
);
307 static struct platform_driver jz_bat_driver
= {
308 .probe
= jz_bat_probe
,
309 .remove
= __devexit_p(jz_bat_remove
),
310 .suspend
= jz_bat_suspend
,
311 .resume
= jz_bat_resume
,
313 .name
= "jz4740-battery",
314 .owner
= THIS_MODULE
,
318 static int __init
jz_bat_init(void)
320 return platform_driver_register(&jz_bat_driver
);
322 module_init(jz_bat_init
);
324 static void __exit
jz_bat_exit(void)
326 platform_driver_unregister(&jz_bat_driver
);
328 module_exit(jz_bat_exit
);
330 MODULE_LICENSE("GPL");
331 MODULE_AUTHOR("Jiejing Zhang <kzjeef@gmail.com>");
332 MODULE_DESCRIPTION("JZ4720/JZ4740 SoC battery driver");