2 * Battery measurement code for Ingenic JZ SOC.
4 * Copyright (C) 2009 Jiejing Zhang <kzjeef@gmail.com>
5 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
7 * based on tosa_battery.c
9 * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
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.
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/power_supply.h>
19 #include <linux/delay.h>
20 #include <linux/spinlock.h>
21 #include <linux/interrupt.h>
22 #include <linux/platform_device.h>
23 #include <linux/gpio.h>
24 #include <linux/interrupt.h>
26 #include <linux/power/jz4740-battery.h>
27 #include <linux/jz4740-adc.h>
30 struct jz_battery_platform_data
*pdata
;
37 struct power_supply battery
;
38 struct delayed_work work
;
41 static inline struct jz_battery
*psy_to_jz_battery(struct power_supply
*psy
)
43 return container_of(psy
, struct jz_battery
, battery
);
46 static long jz_battery_read_voltage(struct jz_battery
*jz_battery
)
48 struct device
*adc
= jz_battery
->battery
.dev
->parent
->parent
;
49 enum jz_adc_battery_scale scale
;
51 if (jz_battery
->pdata
->info
.voltage_max_design
> 2500000)
52 scale
= JZ_ADC_BATTERY_SCALE_7V5
;
54 scale
= JZ_ADC_BATTERY_SCALE_2V5
;
56 return jz4740_adc_read_battery_voltage(adc
, scale
);
59 static int jz_battery_get_capacity(struct power_supply
*psy
)
61 struct jz_battery
*jz_battery
= psy_to_jz_battery(psy
);
62 struct power_supply_info
*info
= &jz_battery
->pdata
->info
;
67 voltage
= jz_battery_read_voltage(jz_battery
);
72 voltage_span
= info
->voltage_max_design
- info
->voltage_min_design
;
73 ret
= ((voltage
- info
->voltage_min_design
) * 100) / voltage_span
;
83 static int jz_battery_get_property(struct power_supply
*psy
,
84 enum power_supply_property psp
,
85 union power_supply_propval
*val
)
87 struct jz_battery
*jz_battery
= psy_to_jz_battery(psy
);
88 struct power_supply_info
*info
= &jz_battery
->pdata
->info
;
92 case POWER_SUPPLY_PROP_STATUS
:
93 val
->intval
= jz_battery
->status
;
95 case POWER_SUPPLY_PROP_TECHNOLOGY
:
96 val
->intval
= jz_battery
->pdata
->info
.technology
;
98 case POWER_SUPPLY_PROP_HEALTH
:
99 voltage
= jz_battery_read_voltage(jz_battery
);
100 if (voltage
< info
->voltage_min_design
)
101 val
->intval
= POWER_SUPPLY_HEALTH_DEAD
;
103 val
->intval
= POWER_SUPPLY_HEALTH_GOOD
;
105 case POWER_SUPPLY_PROP_CAPACITY
:
106 val
->intval
= jz_battery_get_capacity(psy
);
108 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
109 val
->intval
= jz_battery_read_voltage(jz_battery
);
113 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
:
114 val
->intval
= info
->voltage_max_design
;
116 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
:
117 val
->intval
= info
->voltage_min_design
;
119 case POWER_SUPPLY_PROP_PRESENT
:
128 static void jz_battery_external_power_changed(struct power_supply
*psy
)
130 struct jz_battery
*jz_battery
= psy_to_jz_battery(psy
);
132 cancel_delayed_work(&jz_battery
->work
);
133 schedule_delayed_work(&jz_battery
->work
, 0);
136 static irqreturn_t
jz_battery_charge_irq(int irq
, void *data
)
138 struct jz_battery
*jz_battery
= data
;
140 cancel_delayed_work(&jz_battery
->work
);
141 schedule_delayed_work(&jz_battery
->work
, 0);
146 static void jz_battery_update(struct jz_battery
*jz_battery
)
150 long voltage_difference
;
151 bool has_changed
= 0;
153 if (gpio_is_valid(jz_battery
->pdata
->gpio_charge
)) {
156 is_charging
= gpio_get_value(jz_battery
->pdata
->gpio_charge
);
157 is_charging
^= jz_battery
->pdata
->gpio_charge_active_low
;
159 status
= POWER_SUPPLY_STATUS_CHARGING
;
161 status
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
163 if (status
!= jz_battery
->status
) {
164 jz_battery
->status
= status
;
169 voltage
= jz_battery_read_voltage(jz_battery
);
170 voltage_difference
= voltage
- jz_battery
->voltage
;
171 if (voltage_difference
> 50000 || voltage_difference
< 50000) {
172 jz_battery
->voltage
= voltage
;
176 power_supply_changed(&jz_battery
->battery
);
179 static enum power_supply_property jz_battery_properties
[] = {
180 POWER_SUPPLY_PROP_STATUS
,
181 POWER_SUPPLY_PROP_TECHNOLOGY
,
182 POWER_SUPPLY_PROP_HEALTH
,
183 POWER_SUPPLY_PROP_CAPACITY
,
184 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
185 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
,
186 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
187 POWER_SUPPLY_PROP_PRESENT
,
190 static void jz_battery_work(struct work_struct
*work
)
192 /* Too small interval will increase system workload */
193 const int interval
= HZ
* 30;
194 struct jz_battery
*jz_battery
= container_of(work
, struct jz_battery
,
197 jz_battery_update(jz_battery
);
198 schedule_delayed_work(&jz_battery
->work
, interval
);
201 static int jz_battery_probe(struct platform_device
*pdev
)
204 struct jz_battery_platform_data
*pdata
= pdev
->dev
.platform_data
;
205 struct jz_battery
*jz_battery
;
206 struct power_supply
*battery
;
208 if (!pdev
->dev
.platform_data
) {
209 dev_err(&pdev
->dev
, "No platform data\n");
213 jz_battery
= kzalloc(sizeof(*jz_battery
), GFP_KERNEL
);
216 dev_err(&pdev
->dev
, "Failed to allocate driver structure\n");
220 battery
= &jz_battery
->battery
;
221 battery
->name
= pdata
->info
.name
;
222 battery
->type
= POWER_SUPPLY_TYPE_BATTERY
;
223 battery
->properties
= jz_battery_properties
;
224 battery
->num_properties
= ARRAY_SIZE(jz_battery_properties
);
225 battery
->get_property
= jz_battery_get_property
;
226 battery
->external_power_changed
= jz_battery_external_power_changed
;
227 battery
->use_for_apm
= 1;
229 jz_battery
->pdata
= pdata
;
231 INIT_DELAYED_WORK(&jz_battery
->work
, jz_battery_work
);
233 if (gpio_is_valid(pdata
->gpio_charge
)) {
234 ret
= gpio_request(pdata
->gpio_charge
, dev_name(&pdev
->dev
));
236 dev_err(&pdev
->dev
, "charger state gpio request failed.\n");
239 ret
= gpio_direction_input(pdata
->gpio_charge
);
241 dev_err(&pdev
->dev
, "charger state gpio set direction failed.\n");
245 jz_battery
->charge_irq
= gpio_to_irq(pdata
->gpio_charge
);
247 if (jz_battery
->charge_irq
>= 0) {
248 ret
= request_irq(jz_battery
->charge_irq
,
249 jz_battery_charge_irq
,
250 IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING
,
251 dev_name(&pdev
->dev
), jz_battery
);
253 dev_err(&pdev
->dev
, "Failed to request charge irq: %d\n", ret
);
258 jz_battery
->charge_irq
= -1;
262 ret
= power_supply_register(&pdev
->dev
, &jz_battery
->battery
);
264 dev_err(&pdev
->dev
, "power supply battery register failed.\n");
268 platform_set_drvdata(pdev
, jz_battery
);
269 schedule_delayed_work(&jz_battery
->work
, 0);
274 if (jz_battery
->charge_irq
>= 0)
275 free_irq(jz_battery
->charge_irq
, jz_battery
);
277 if (gpio_is_valid(pdata
->gpio_charge
))
278 gpio_free(jz_battery
->pdata
->gpio_charge
);
284 static int jz_battery_remove(struct platform_device
*pdev
)
286 struct jz_battery
*jz_battery
= platform_get_drvdata(pdev
);
288 cancel_delayed_work_sync(&jz_battery
->work
);
290 if (gpio_is_valid(jz_battery
->pdata
->gpio_charge
)) {
291 if (jz_battery
->charge_irq
>= 0)
292 free_irq(jz_battery
->charge_irq
, jz_battery
);
293 gpio_free(jz_battery
->pdata
->gpio_charge
);
296 power_supply_unregister(&jz_battery
->battery
);
302 static int jz_battery_suspend(struct platform_device
*pdev
, pm_message_t state
)
304 struct jz_battery
*jz_battery
= platform_get_drvdata(pdev
);
306 cancel_delayed_work_sync(&jz_battery
->work
);
307 jz_battery
->status
= POWER_SUPPLY_STATUS_UNKNOWN
;
312 static int jz_battery_resume(struct platform_device
*pdev
)
314 struct jz_battery
*jz_battery
= platform_get_drvdata(pdev
);
316 schedule_delayed_work(&jz_battery
->work
, 0);
321 #define jz_battery_suspend NULL
322 #define jz_battery_resume NULL
325 static struct platform_driver jz_battery_driver
= {
326 .probe
= jz_battery_probe
,
327 .remove
= __devexit_p(jz_battery_remove
),
328 .suspend
= jz_battery_suspend
,
329 .resume
= jz_battery_resume
,
331 .name
= "jz4740-battery",
332 .owner
= THIS_MODULE
,
336 static int __init
jz_battery_init(void)
338 return platform_driver_register(&jz_battery_driver
);
340 module_init(jz_battery_init
);
342 static void __exit
jz_battery_exit(void)
344 platform_driver_unregister(&jz_battery_driver
);
346 module_exit(jz_battery_exit
);
348 MODULE_LICENSE("GPL");
349 MODULE_AUTHOR("Jiejing Zhang <kzjeef@gmail.com>");
350 MODULE_DESCRIPTION("JZ4720/JZ4740 SoC battery driver");