add preinit modularization work by Daniel Dickinson (cshore)
[openwrt.git] / target / linux / xburst / files-2.6.32 / drivers / power / jz4740-battery.c
1 /*
2 * Battery measurement code for Ingenic JZ SOC.
3 *
4 * based on tosa_battery.c
5 *
6 * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
7 * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com>
8 *
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.
12 *
13 */
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>
22
23 #include <linux/power/jz4740-battery.h>
24 #include <linux/jz4740-adc.h>
25
26 struct jz_battery_info {
27 struct power_supply usb;
28 struct power_supply bat;
29 struct power_supply ac;
30 int bat_status;
31 struct jz_batt_info *pdata;
32 struct mutex work_lock;
33 struct workqueue_struct *monitor_wqueue;
34 struct delayed_work bat_work;
35 };
36
37 #define ps_to_jz_battery(x) container_of((x), struct jz_battery_info, bat);
38
39 /*********************************************************************
40 * Power
41 *********************************************************************/
42
43
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)
48 {
49 int gpio;
50
51 if (bat_info == 0 || bat_info->pdata == 0)
52 return -EINVAL;
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))
57 return -EINVAL;
58 switch (psp) {
59 case POWER_SUPPLY_PROP_ONLINE:
60 val->intval = !gpio_get_value(gpio);
61 break;
62 default:
63 return -EINVAL;
64 }
65
66 return 0;
67 }
68
69 static int jz_usb_get_power_prop(struct power_supply *psy,
70 enum power_supply_property psp,
71 union power_supply_propval *val)
72 {
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);
75 }
76
77 static int jz_ac_get_power_prop(struct power_supply *psy,
78 enum power_supply_property psp,
79 union power_supply_propval *val)
80 {
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);
83 }
84
85
86 static enum power_supply_property jz_power_props[] = {
87 POWER_SUPPLY_PROP_ONLINE,
88 };
89
90 static struct power_supply jz_ac = {
91 .name = "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,
96 };
97
98 static struct power_supply jz_usb = {
99 .name = "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,
104 };
105
106
107 /*********************************************************************
108 * Battery properties
109 *********************************************************************/
110
111 static long jz_read_bat(struct power_supply *psy)
112 {
113 struct jz_battery_info *bat_info = ps_to_jz_battery(psy);
114 enum jz_adc_battery_scale scale;
115
116 if (bat_info->pdata->max_voltag > 2500000)
117 scale = JZ_ADC_BATTERY_SCALE_7V5;
118 else
119 scale = JZ_ADC_BATTERY_SCALE_2V5;
120
121 return jz4740_adc_read_battery_voltage(psy->dev->parent->parent, scale);
122 }
123
124 static int jz_bat_get_capacity(struct power_supply *psy)
125 {
126 int ret;
127 struct jz_battery_info *bat_info = ps_to_jz_battery(psy);
128
129 ret = jz_read_bat(psy);
130
131 if (ret < 0)
132 return ret;
133
134 ret = (ret - bat_info->pdata->min_voltag) * 100
135 / (bat_info->pdata->max_voltag - bat_info->pdata->min_voltag);
136
137 if (ret > 100)
138 ret = 100;
139 else if (ret < 0)
140 ret = 0;
141
142 return ret;
143 }
144
145 static int jz_bat_get_property(struct power_supply *psy,
146 enum power_supply_property psp,
147 union power_supply_propval *val)
148 {
149 struct jz_battery_info *bat_info = ps_to_jz_battery(psy)
150
151 switch (psp) {
152 case POWER_SUPPLY_PROP_STATUS:
153 val->intval = bat_info->bat_status;
154 break;
155 case POWER_SUPPLY_PROP_TECHNOLOGY:
156 val->intval = bat_info->pdata->batt_tech;
157 break;
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;
163 } else {
164 dev_dbg(psy->dev, "%s: battery is good,"
165 "voltage normal.\n", __func__);
166 val->intval = POWER_SUPPLY_HEALTH_GOOD;
167 }
168 break;
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);
173 break;
174 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
175 val->intval = jz_read_bat(psy);
176 if (val->intval < 0)
177 return val->intval;
178 break;
179 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
180 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
181 val->intval = bat_info->pdata->max_voltag;
182 break;
183 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
184 val->intval = bat_info->pdata->min_voltag;
185 break;
186 case POWER_SUPPLY_PROP_PRESENT:
187 val->intval = 1;
188 break;
189 default:
190 return -EINVAL;
191 }
192 return 0;
193 }
194
195 static void jz_bat_external_power_changed(struct power_supply *psy)
196 {
197 struct jz_battery_info *bat_info = ps_to_jz_battery(psy);
198
199 cancel_delayed_work(&bat_info->bat_work);
200 queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ / 8);
201 }
202
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",
208 };
209
210 static void jz_bat_update(struct power_supply *psy)
211 {
212 struct jz_battery_info *bat_info = ps_to_jz_battery(psy);
213
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);
217
218 mutex_lock(&bat_info->work_lock);
219
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;
223 else
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]);
227
228 if (old_status != bat_info->bat_status) {
229 dev_dbg(psy->dev, "%s %s -> %s\n",
230 psy->name,
231 status_text[old_status],
232 status_text[bat_info->bat_status]);
233
234 power_supply_changed(psy);
235 }
236 }
237
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;
243 }
244
245 mutex_unlock(&bat_info->work_lock);
246 }
247
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,
257 };
258
259 struct power_supply bat_ps = {
260 .name = "battery",
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,
266 .use_for_apm = 1,
267 };
268
269 static void jz_bat_work(struct work_struct *work)
270 {
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);
274
275 jz_bat_update(&bat_info->bat);
276 queue_delayed_work(bat_info->monitor_wqueue,
277 &bat_info->bat_work, interval);
278 }
279
280 #ifdef CONFIG_PM
281 static int jz_bat_suspend(struct platform_device *pdev, pm_message_t state)
282 {
283 struct jz_battery_info *bat_info = platform_get_drvdata(pdev);
284
285 bat_info->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
286
287 return 0;
288 }
289
290 static int jz_bat_resume(struct platform_device *pdev)
291 {
292 struct jz_battery_info *bat_info = platform_get_drvdata(pdev);
293
294 bat_info->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
295
296 cancel_delayed_work(&bat_info->bat_work);
297 queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ/10);
298
299 return 0;
300 }
301 #else
302 #define jz_bat_suspend NULL
303 #define jz_bat_resume NULL
304 #endif
305
306 static int jz_bat_probe(struct platform_device *pdev)
307 {
308 int ret = 0;
309 struct jz_battery_info *bat_info;
310
311 bat_info = kzalloc(sizeof(struct jz_battery_info), GFP_KERNEL);
312
313 if (!bat_info) {
314 return -ENOMEM;
315 }
316
317 if (!pdev->dev.platform_data) {
318 dev_err(&pdev->dev, "Please set battery info\n");
319 ret = -EINVAL;
320 goto err_platform_data;
321 }
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);
329
330 if (gpio_is_valid(bat_info->pdata->dc_dect_gpio)) {
331 ret = gpio_request(bat_info->pdata->dc_dect_gpio, "AC/DC DECT");
332 if (ret) {
333 dev_err(&pdev->dev, "ac/dc dect gpio request failed.\n");
334
335 goto err_dc_gpio_request;
336 }
337 ret = gpio_direction_input(bat_info->pdata->dc_dect_gpio);
338 if (ret) {
339 dev_err(&pdev->dev, "ac/dc dect gpio direction failed.\n");
340
341 goto err_dc_gpio_direction;
342 }
343 }
344
345 if (gpio_is_valid(bat_info->pdata->usb_dect_gpio)) {
346 ret = gpio_request(bat_info->pdata->usb_dect_gpio, "USB DECT");
347 if (ret) {
348 dev_err(&pdev->dev, "usb dect gpio request failed.\n");
349
350 goto err_usb_gpio_request;
351 }
352 ret = gpio_direction_input(bat_info->pdata->usb_dect_gpio);
353 if (ret) {
354 dev_err(&pdev->dev, "usb dect gpio set direction failed.\n");
355 goto err_usb_gpio_direction;
356 }
357
358 jz_gpio_disable_pullup(bat_info->pdata->usb_dect_gpio);
359 /* TODO: Use generic gpio is better */
360 }
361
362 if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) {
363 ret = gpio_request(bat_info->pdata->charg_stat_gpio, "CHARG STAT");
364 if (ret) {
365 dev_err(&pdev->dev, "charger state gpio request failed.\n");
366 goto err_charg_gpio_request;
367 }
368 ret = gpio_direction_input(bat_info->pdata->charg_stat_gpio);
369 if (ret) {
370 dev_err(&pdev->dev, "charger state gpio set direction failed.\n");
371 goto err_charg_gpio_direction;
372 }
373 }
374
375 if (gpio_is_valid(bat_info->pdata->dc_dect_gpio)) {
376 ret = power_supply_register(&pdev->dev, &bat_info->ac);
377 if (ret) {
378 dev_err(&pdev->dev, "power supply ac/dc register failed.\n");
379 goto err_power_register_ac;
380 }
381 }
382
383 if (gpio_is_valid(bat_info->pdata->usb_dect_gpio)) {
384 ret = power_supply_register(&pdev->dev, &bat_info->usb);
385 if (ret) {
386 dev_err(&pdev->dev, "power supply usb register failed.\n");
387 goto err_power_register_usb;
388 }
389 }
390
391 if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) {
392 ret = power_supply_register(&pdev->dev, &bat_info->bat);
393 if (ret) {
394 dev_err(&pdev->dev, "power supply battery register failed.\n");
395 goto err_power_register_bat;
396 } else {
397 bat_info->monitor_wqueue = create_singlethread_workqueue("jz_battery");
398 if (!bat_info->monitor_wqueue) {
399 return -ESRCH;
400 }
401 queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ * 1);
402 }
403 }
404 printk(KERN_INFO "jz_bat init success.\n");
405 return ret;
406
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);
420 err_dc_gpio_request:
421 err_platform_data:
422 kfree(bat_info);
423 return ret;
424 }
425
426 static int jz_bat_remove(struct platform_device *pdev)
427 {
428 struct jz_battery_info *bat_info = platform_get_drvdata(pdev);
429
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);
437 }
438
439 power_supply_unregister(&bat_ps);
440 power_supply_unregister(&jz_ac);
441 power_supply_unregister(&jz_usb);
442
443 return 0;
444 }
445
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,
451 .driver = {
452 .name = "jz4740-battery",
453 .owner = THIS_MODULE,
454 },
455 };
456
457 static int __init jz_bat_init(void)
458 {
459 return platform_driver_register(&jz_bat_driver);
460 }
461 module_init(jz_bat_init);
462
463 static void __exit jz_bat_exit(void)
464 {
465 platform_driver_unregister(&jz_bat_driver);
466 }
467 module_exit(jz_bat_exit);
468
469 MODULE_LICENSE("GPL");
470 MODULE_AUTHOR("Jiejing Zhang <kzjeef@gmail.com>");
471 MODULE_DESCRIPTION("JZ4720/JZ4740 SoC battery driver");
This page took 0.074616 seconds and 5 git commands to generate.