blob: 5b4821593201047cd3791ba2adabec4a8bd7bd8a [file] [log] [blame]
Lars-Peter Clausenf6a21382010-06-19 04:08:29 +00001/*
2 * Battery measurement code for Ingenic JZ SOC.
3 *
4 * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com>
5 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
6 *
7 * based on tosa_battery.c
8 *
9 * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
10*
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 *
15 */
16
17#include <linux/interrupt.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22
23#include <linux/delay.h>
24#include <linux/gpio.h>
25#include <linux/mfd/core.h>
26#include <linux/power_supply.h>
27
28#include <linux/power/jz4740-battery.h>
29#include <linux/jz4740-adc.h>
30
31struct jz_battery {
32 struct jz_battery_platform_data *pdata;
33 struct platform_device *pdev;
34
35 struct resource *mem;
36 void __iomem *base;
37
38 int irq;
39 int charge_irq;
40
41 struct mfd_cell *cell;
42
43 int status;
44 long voltage;
45
46 struct completion read_completion;
47
48 struct power_supply battery;
49 struct delayed_work work;
Lars-Peter Clausen8ec98fe2010-11-11 19:00:52 +010050
51 struct mutex lock;
Lars-Peter Clausenf6a21382010-06-19 04:08:29 +000052};
53
54static inline struct jz_battery *psy_to_jz_battery(struct power_supply *psy)
55{
56 return container_of(psy, struct jz_battery, battery);
57}
58
59static irqreturn_t jz_battery_irq_handler(int irq, void *devid)
60{
61 struct jz_battery *battery = devid;
62
63 complete(&battery->read_completion);
64 return IRQ_HANDLED;
65}
66
67static long jz_battery_read_voltage(struct jz_battery *battery)
68{
69 unsigned long t;
70 unsigned long val;
71 long voltage;
72
Lars-Peter Clausen8ec98fe2010-11-11 19:00:52 +010073 mutex_lock(&battery->lock);
74
Lars-Peter Clausenf6a21382010-06-19 04:08:29 +000075 INIT_COMPLETION(battery->read_completion);
76
77 enable_irq(battery->irq);
78 battery->cell->enable(battery->pdev);
79
80 t = wait_for_completion_interruptible_timeout(&battery->read_completion,
81 HZ);
82
83 if (t > 0) {
84 val = readw(battery->base) & 0xfff;
85
86 if (battery->pdata->info.voltage_max_design <= 2500000)
87 val = (val * 78125UL) >> 7UL;
88 else
89 val = ((val * 924375UL) >> 9UL) + 33000;
90 voltage = (long)val;
91 } else {
92 voltage = t ? t : -ETIMEDOUT;
93 }
94
95 battery->cell->disable(battery->pdev);
96 disable_irq(battery->irq);
97
Lars-Peter Clausen8ec98fe2010-11-11 19:00:52 +010098 mutex_unlock(&battery->lock);
99
Lars-Peter Clausenf6a21382010-06-19 04:08:29 +0000100 return voltage;
101}
102
103static int jz_battery_get_capacity(struct power_supply *psy)
104{
105 struct jz_battery *jz_battery = psy_to_jz_battery(psy);
106 struct power_supply_info *info = &jz_battery->pdata->info;
107 long voltage;
108 int ret;
109 int voltage_span;
110
111 voltage = jz_battery_read_voltage(jz_battery);
112
113 if (voltage < 0)
114 return voltage;
115
116 voltage_span = info->voltage_max_design - info->voltage_min_design;
117 ret = ((voltage - info->voltage_min_design) * 100) / voltage_span;
118
119 if (ret > 100)
120 ret = 100;
121 else if (ret < 0)
122 ret = 0;
123
124 return ret;
125}
126
127static int jz_battery_get_property(struct power_supply *psy,
128 enum power_supply_property psp, union power_supply_propval *val)
129{
130 struct jz_battery *jz_battery = psy_to_jz_battery(psy);
131 struct power_supply_info *info = &jz_battery->pdata->info;
132 long voltage;
133
134 switch (psp) {
135 case POWER_SUPPLY_PROP_STATUS:
136 val->intval = jz_battery->status;
137 break;
138 case POWER_SUPPLY_PROP_TECHNOLOGY:
139 val->intval = jz_battery->pdata->info.technology;
140 break;
141 case POWER_SUPPLY_PROP_HEALTH:
142 voltage = jz_battery_read_voltage(jz_battery);
143 if (voltage < info->voltage_min_design)
144 val->intval = POWER_SUPPLY_HEALTH_DEAD;
145 else
146 val->intval = POWER_SUPPLY_HEALTH_GOOD;
147 break;
148 case POWER_SUPPLY_PROP_CAPACITY:
149 val->intval = jz_battery_get_capacity(psy);
150 break;
151 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
152 val->intval = jz_battery_read_voltage(jz_battery);
153 if (val->intval < 0)
154 return val->intval;
155 break;
156 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
157 val->intval = info->voltage_max_design;
158 break;
159 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
160 val->intval = info->voltage_min_design;
161 break;
162 case POWER_SUPPLY_PROP_PRESENT:
163 val->intval = 1;
164 break;
165 default:
166 return -EINVAL;
167 }
168 return 0;
169}
170
171static void jz_battery_external_power_changed(struct power_supply *psy)
172{
173 struct jz_battery *jz_battery = psy_to_jz_battery(psy);
174
175 cancel_delayed_work(&jz_battery->work);
176 schedule_delayed_work(&jz_battery->work, 0);
177}
178
179static irqreturn_t jz_battery_charge_irq(int irq, void *data)
180{
181 struct jz_battery *jz_battery = data;
182
183 cancel_delayed_work(&jz_battery->work);
184 schedule_delayed_work(&jz_battery->work, 0);
185
186 return IRQ_HANDLED;
187}
188
189static void jz_battery_update(struct jz_battery *jz_battery)
190{
191 int status;
192 long voltage;
193 bool has_changed = false;
194 int is_charging;
195
196 if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
197 is_charging = gpio_get_value(jz_battery->pdata->gpio_charge);
198 is_charging ^= jz_battery->pdata->gpio_charge_active_low;
199 if (is_charging)
200 status = POWER_SUPPLY_STATUS_CHARGING;
201 else
202 status = POWER_SUPPLY_STATUS_NOT_CHARGING;
203
204 if (status != jz_battery->status) {
205 jz_battery->status = status;
206 has_changed = true;
207 }
208 }
209
210 voltage = jz_battery_read_voltage(jz_battery);
211 if (abs(voltage - jz_battery->voltage) < 50000) {
212 jz_battery->voltage = voltage;
213 has_changed = true;
214 }
215
216 if (has_changed)
217 power_supply_changed(&jz_battery->battery);
218}
219
220static enum power_supply_property jz_battery_properties[] = {
221 POWER_SUPPLY_PROP_STATUS,
222 POWER_SUPPLY_PROP_TECHNOLOGY,
223 POWER_SUPPLY_PROP_HEALTH,
224 POWER_SUPPLY_PROP_CAPACITY,
225 POWER_SUPPLY_PROP_VOLTAGE_NOW,
226 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
227 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
228 POWER_SUPPLY_PROP_PRESENT,
229};
230
231static void jz_battery_work(struct work_struct *work)
232{
233 /* Too small interval will increase system workload */
234 const int interval = HZ * 30;
235 struct jz_battery *jz_battery = container_of(work, struct jz_battery,
236 work.work);
237
238 jz_battery_update(jz_battery);
239 schedule_delayed_work(&jz_battery->work, interval);
240}
241
242static int __devinit jz_battery_probe(struct platform_device *pdev)
243{
244 int ret = 0;
245 struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data;
246 struct jz_battery *jz_battery;
247 struct power_supply *battery;
248
Lars-Peter Clausen8477ced2010-11-11 19:00:53 +0100249 if (!pdata) {
250 dev_err(&pdev->dev, "No platform_data supplied\n");
251 return -ENXIO;
252 }
253
Lars-Peter Clausenf6a21382010-06-19 04:08:29 +0000254 jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL);
255 if (!jz_battery) {
256 dev_err(&pdev->dev, "Failed to allocate driver structure\n");
257 return -ENOMEM;
258 }
259
260 jz_battery->cell = pdev->dev.platform_data;
261
262 jz_battery->irq = platform_get_irq(pdev, 0);
263 if (jz_battery->irq < 0) {
264 ret = jz_battery->irq;
265 dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
266 goto err_free;
267 }
268
269 jz_battery->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
270 if (!jz_battery->mem) {
271 ret = -ENOENT;
272 dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
273 goto err_free;
274 }
275
276 jz_battery->mem = request_mem_region(jz_battery->mem->start,
277 resource_size(jz_battery->mem), pdev->name);
278 if (!jz_battery->mem) {
279 ret = -EBUSY;
280 dev_err(&pdev->dev, "Failed to request mmio memory region\n");
281 goto err_free;
282 }
283
284 jz_battery->base = ioremap_nocache(jz_battery->mem->start,
285 resource_size(jz_battery->mem));
286 if (!jz_battery->base) {
287 ret = -EBUSY;
288 dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
289 goto err_release_mem_region;
290 }
291
292 battery = &jz_battery->battery;
293 battery->name = pdata->info.name;
294 battery->type = POWER_SUPPLY_TYPE_BATTERY;
295 battery->properties = jz_battery_properties;
296 battery->num_properties = ARRAY_SIZE(jz_battery_properties);
297 battery->get_property = jz_battery_get_property;
298 battery->external_power_changed = jz_battery_external_power_changed;
299 battery->use_for_apm = 1;
300
301 jz_battery->pdata = pdata;
302 jz_battery->pdev = pdev;
303
304 init_completion(&jz_battery->read_completion);
Lars-Peter Clausen8ec98fe2010-11-11 19:00:52 +0100305 mutex_init(&jz_battery->lock);
Lars-Peter Clausenf6a21382010-06-19 04:08:29 +0000306
307 INIT_DELAYED_WORK(&jz_battery->work, jz_battery_work);
308
309 ret = request_irq(jz_battery->irq, jz_battery_irq_handler, 0, pdev->name,
310 jz_battery);
311 if (ret) {
312 dev_err(&pdev->dev, "Failed to request irq %d\n", ret);
313 goto err_iounmap;
314 }
315 disable_irq(jz_battery->irq);
316
317 if (gpio_is_valid(pdata->gpio_charge)) {
318 ret = gpio_request(pdata->gpio_charge, dev_name(&pdev->dev));
319 if (ret) {
320 dev_err(&pdev->dev, "charger state gpio request failed.\n");
321 goto err_free_irq;
322 }
323 ret = gpio_direction_input(pdata->gpio_charge);
324 if (ret) {
325 dev_err(&pdev->dev, "charger state gpio set direction failed.\n");
326 goto err_free_gpio;
327 }
328
329 jz_battery->charge_irq = gpio_to_irq(pdata->gpio_charge);
330
331 if (jz_battery->charge_irq >= 0) {
332 ret = request_irq(jz_battery->charge_irq,
333 jz_battery_charge_irq,
334 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
335 dev_name(&pdev->dev), jz_battery);
336 if (ret) {
337 dev_err(&pdev->dev, "Failed to request charge irq: %d\n", ret);
338 goto err_free_gpio;
339 }
340 }
341 } else {
342 jz_battery->charge_irq = -1;
343 }
344
345 if (jz_battery->pdata->info.voltage_max_design <= 2500000)
346 jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB,
347 JZ_ADC_CONFIG_BAT_MB);
348 else
349 jz4740_adc_set_config(pdev->dev.parent, JZ_ADC_CONFIG_BAT_MB, 0);
350
351 ret = power_supply_register(&pdev->dev, &jz_battery->battery);
352 if (ret) {
353 dev_err(&pdev->dev, "power supply battery register failed.\n");
354 goto err_free_charge_irq;
355 }
356
357 platform_set_drvdata(pdev, jz_battery);
358 schedule_delayed_work(&jz_battery->work, 0);
359
360 return 0;
361
362err_free_charge_irq:
363 if (jz_battery->charge_irq >= 0)
364 free_irq(jz_battery->charge_irq, jz_battery);
365err_free_gpio:
366 if (gpio_is_valid(pdata->gpio_charge))
367 gpio_free(jz_battery->pdata->gpio_charge);
368err_free_irq:
369 free_irq(jz_battery->irq, jz_battery);
370err_iounmap:
371 platform_set_drvdata(pdev, NULL);
372 iounmap(jz_battery->base);
373err_release_mem_region:
374 release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem));
375err_free:
376 kfree(jz_battery);
377 return ret;
378}
379
380static int __devexit jz_battery_remove(struct platform_device *pdev)
381{
382 struct jz_battery *jz_battery = platform_get_drvdata(pdev);
383
384 cancel_delayed_work_sync(&jz_battery->work);
385
386 if (gpio_is_valid(jz_battery->pdata->gpio_charge)) {
387 if (jz_battery->charge_irq >= 0)
388 free_irq(jz_battery->charge_irq, jz_battery);
389 gpio_free(jz_battery->pdata->gpio_charge);
390 }
391
392 power_supply_unregister(&jz_battery->battery);
393
394 free_irq(jz_battery->irq, jz_battery);
395
396 iounmap(jz_battery->base);
397 release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem));
Axel Lin5f298952010-09-27 15:08:31 +0800398 kfree(jz_battery);
Lars-Peter Clausenf6a21382010-06-19 04:08:29 +0000399
400 return 0;
401}
402
403#ifdef CONFIG_PM
404static int jz_battery_suspend(struct device *dev)
405{
406 struct jz_battery *jz_battery = dev_get_drvdata(dev);
407
408 cancel_delayed_work_sync(&jz_battery->work);
409 jz_battery->status = POWER_SUPPLY_STATUS_UNKNOWN;
410
411 return 0;
412}
413
414static int jz_battery_resume(struct device *dev)
415{
416 struct jz_battery *jz_battery = dev_get_drvdata(dev);
417
418 schedule_delayed_work(&jz_battery->work, 0);
419
420 return 0;
421}
422
423static const struct dev_pm_ops jz_battery_pm_ops = {
424 .suspend = jz_battery_suspend,
425 .resume = jz_battery_resume,
426};
427
428#define JZ_BATTERY_PM_OPS (&jz_battery_pm_ops)
429#else
430#define JZ_BATTERY_PM_OPS NULL
431#endif
432
433static struct platform_driver jz_battery_driver = {
434 .probe = jz_battery_probe,
435 .remove = __devexit_p(jz_battery_remove),
436 .driver = {
437 .name = "jz4740-battery",
438 .owner = THIS_MODULE,
439 .pm = JZ_BATTERY_PM_OPS,
440 },
441};
442
443static int __init jz_battery_init(void)
444{
445 return platform_driver_register(&jz_battery_driver);
446}
447module_init(jz_battery_init);
448
449static void __exit jz_battery_exit(void)
450{
451 platform_driver_unregister(&jz_battery_driver);
452}
453module_exit(jz_battery_exit);
454
455MODULE_ALIAS("platform:jz4740-battery");
456MODULE_LICENSE("GPL");
457MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
458MODULE_DESCRIPTION("JZ4740 SoC battery driver");