2 * linux/drivers/power/wm97xx_battery.c
4 * Battery measurement code for WM97xx
6 * based on tosa_battery.c
8 * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
16 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/power_supply.h>
21 #include <linux/wm97xx.h>
22 #include <linux/spinlock.h>
23 #include <linux/interrupt.h>
24 #include <linux/gpio.h>
25 #include <linux/wm97xx_batt.h>
27 static DEFINE_MUTEX(bat_lock
);
28 static struct work_struct bat_work
;
29 struct mutex work_lock
;
30 static int bat_status
= POWER_SUPPLY_STATUS_UNKNOWN
;
31 static struct wm97xx_batt_info
*pdata
;
32 static enum power_supply_property
*prop
;
34 static unsigned long wm97xx_read_bat(struct power_supply
*bat_ps
)
36 return wm97xx_read_aux_adc(bat_ps
->dev
->parent
->driver_data
,
37 pdata
->batt_aux
) * pdata
->batt_mult
/
41 static unsigned long wm97xx_read_temp(struct power_supply
*bat_ps
)
43 return wm97xx_read_aux_adc(bat_ps
->dev
->parent
->driver_data
,
44 pdata
->temp_aux
) * pdata
->temp_mult
/
48 static int wm97xx_bat_get_property(struct power_supply
*bat_ps
,
49 enum power_supply_property psp
,
50 union power_supply_propval
*val
)
53 case POWER_SUPPLY_PROP_STATUS
:
54 val
->intval
= bat_status
;
56 case POWER_SUPPLY_PROP_TECHNOLOGY
:
57 val
->intval
= pdata
->batt_tech
;
59 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
60 if (pdata
->batt_aux
>= 0)
61 val
->intval
= wm97xx_read_bat(bat_ps
);
65 case POWER_SUPPLY_PROP_TEMP
:
66 if (pdata
->temp_aux
>= 0)
67 val
->intval
= wm97xx_read_temp(bat_ps
);
71 case POWER_SUPPLY_PROP_VOLTAGE_MAX
:
72 if (pdata
->max_voltage
>= 0)
73 val
->intval
= pdata
->max_voltage
;
77 case POWER_SUPPLY_PROP_VOLTAGE_MIN
:
78 if (pdata
->min_voltage
>= 0)
79 val
->intval
= pdata
->min_voltage
;
83 case POWER_SUPPLY_PROP_PRESENT
:
92 static void wm97xx_bat_external_power_changed(struct power_supply
*bat_ps
)
94 schedule_work(&bat_work
);
97 static void wm97xx_bat_update(struct power_supply
*bat_ps
)
99 int old_status
= bat_status
;
101 mutex_lock(&work_lock
);
103 bat_status
= (pdata
->charge_gpio
>= 0) ?
104 (gpio_get_value(pdata
->charge_gpio
) ?
105 POWER_SUPPLY_STATUS_DISCHARGING
:
106 POWER_SUPPLY_STATUS_CHARGING
) :
107 POWER_SUPPLY_STATUS_UNKNOWN
;
109 if (old_status
!= bat_status
) {
110 pr_debug("%s: %i -> %i\n", bat_ps
->name
, old_status
,
112 power_supply_changed(bat_ps
);
115 mutex_unlock(&work_lock
);
118 static struct power_supply bat_ps
= {
119 .type
= POWER_SUPPLY_TYPE_BATTERY
,
120 .get_property
= wm97xx_bat_get_property
,
121 .external_power_changed
= wm97xx_bat_external_power_changed
,
125 static void wm97xx_bat_work(struct work_struct
*work
)
127 wm97xx_bat_update(&bat_ps
);
131 static int wm97xx_bat_suspend(struct platform_device
*dev
, pm_message_t state
)
133 flush_scheduled_work();
137 static int wm97xx_bat_resume(struct platform_device
*dev
)
139 schedule_work(&bat_work
);
143 #define wm97xx_bat_suspend NULL
144 #define wm97xx_bat_resume NULL
147 static int __devinit
wm97xx_bat_probe(struct platform_device
*dev
)
150 int props
= 1; /* POWER_SUPPLY_PROP_PRESENT */
156 mutex_init(&work_lock
);
159 dev_err(&dev
->dev
, "Please use wm97xx_bat_set_pdata\n");
163 if (pdata
->charge_gpio
>= 0 && gpio_is_valid(pdata
->charge_gpio
)) {
164 ret
= gpio_request(pdata
->charge_gpio
, "BATT CHRG");
167 ret
= gpio_direction_input(pdata
->charge_gpio
);
170 props
++; /* POWER_SUPPLY_PROP_STATUS */
173 if (pdata
->batt_tech
>= 0)
174 props
++; /* POWER_SUPPLY_PROP_TECHNOLOGY */
175 if (pdata
->temp_aux
>= 0)
176 props
++; /* POWER_SUPPLY_PROP_TEMP */
177 if (pdata
->batt_aux
>= 0)
178 props
++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
179 if (pdata
->max_voltage
>= 0)
180 props
++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
181 if (pdata
->min_voltage
>= 0)
182 props
++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
184 prop
= kzalloc(props
* sizeof(*prop
), GFP_KERNEL
);
188 prop
[i
++] = POWER_SUPPLY_PROP_PRESENT
;
189 if (pdata
->charge_gpio
>= 0)
190 prop
[i
++] = POWER_SUPPLY_PROP_STATUS
;
191 if (pdata
->batt_tech
>= 0)
192 prop
[i
++] = POWER_SUPPLY_PROP_TECHNOLOGY
;
193 if (pdata
->temp_aux
>= 0)
194 prop
[i
++] = POWER_SUPPLY_PROP_TEMP
;
195 if (pdata
->batt_aux
>= 0)
196 prop
[i
++] = POWER_SUPPLY_PROP_VOLTAGE_NOW
;
197 if (pdata
->max_voltage
>= 0)
198 prop
[i
++] = POWER_SUPPLY_PROP_VOLTAGE_MAX
;
199 if (pdata
->min_voltage
>= 0)
200 prop
[i
++] = POWER_SUPPLY_PROP_VOLTAGE_MIN
;
202 INIT_WORK(&bat_work
, wm97xx_bat_work
);
204 if (!pdata
->batt_name
) {
205 dev_info(&dev
->dev
, "Please consider setting proper battery "
206 "name in platform definition file, falling "
207 "back to name \"wm97xx-batt\"\n");
208 bat_ps
.name
= "wm97xx-batt";
210 bat_ps
.name
= pdata
->batt_name
;
212 bat_ps
.properties
= prop
;
213 bat_ps
.num_properties
= props
;
215 ret
= power_supply_register(&dev
->dev
, &bat_ps
);
217 schedule_work(&bat_work
);
225 gpio_free(pdata
->charge_gpio
);
230 static int __devexit
wm97xx_bat_remove(struct platform_device
*dev
)
232 if (pdata
&& pdata
->charge_gpio
&& pdata
->charge_gpio
>= 0)
233 gpio_free(pdata
->charge_gpio
);
234 flush_scheduled_work();
235 power_supply_unregister(&bat_ps
);
240 static struct platform_driver wm97xx_bat_driver
= {
242 .name
= "wm97xx-battery",
243 .owner
= THIS_MODULE
,
245 .probe
= wm97xx_bat_probe
,
246 .remove
= __devexit_p(wm97xx_bat_remove
),
247 .suspend
= wm97xx_bat_suspend
,
248 .resume
= wm97xx_bat_resume
,
251 static int __init
wm97xx_bat_init(void)
253 return platform_driver_register(&wm97xx_bat_driver
);
256 static void __exit
wm97xx_bat_exit(void)
258 platform_driver_unregister(&wm97xx_bat_driver
);
261 void __init
wm97xx_bat_set_pdata(struct wm97xx_batt_info
*data
)
265 EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata
);
267 module_init(wm97xx_bat_init
);
268 module_exit(wm97xx_bat_exit
);
270 MODULE_LICENSE("GPL");
271 MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
272 MODULE_DESCRIPTION("WM97xx battery driver");