Blame view

drivers/power/wm97xx_battery.c 7.13 KB
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   * linux/drivers/power/wm97xx_battery.c
   *
   * Battery measurement code for WM97xx
   *
   * based on tosa_battery.c
   *
   * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
  
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/power_supply.h>
  #include <linux/wm97xx.h>
  #include <linux/spinlock.h>
  #include <linux/interrupt.h>
  #include <linux/gpio.h>
7c87942ae   Marek Vasut   wm97xx_battery: U...
25
  #include <linux/irq.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
26
  #include <linux/slab.h>
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
27

4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
28
  static struct work_struct bat_work;
daf22c3c4   Axel Lin   wm97xx_battery: U...
29
  static DEFINE_MUTEX(work_lock);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
30
  static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
31
32
33
34
  static enum power_supply_property *prop;
  
  static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
  {
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
35
36
  	struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data;
  	struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
38c7dc373   Alexander Beregalov   wm97xx_batery: re...
37
  	return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent),
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
38
39
40
41
42
43
  					pdata->batt_aux) * pdata->batt_mult /
  					pdata->batt_div;
  }
  
  static unsigned long wm97xx_read_temp(struct power_supply *bat_ps)
  {
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
44
45
  	struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data;
  	struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
38c7dc373   Alexander Beregalov   wm97xx_batery: re...
46
  	return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev->parent),
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
47
48
49
50
51
52
53
54
  					pdata->temp_aux) * pdata->temp_mult /
  					pdata->temp_div;
  }
  
  static int wm97xx_bat_get_property(struct power_supply *bat_ps,
  			    enum power_supply_property psp,
  			    union power_supply_propval *val)
  {
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
55
56
  	struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data;
  	struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  	switch (psp) {
  	case POWER_SUPPLY_PROP_STATUS:
  		val->intval = bat_status;
  		break;
  	case POWER_SUPPLY_PROP_TECHNOLOGY:
  		val->intval = pdata->batt_tech;
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  		if (pdata->batt_aux >= 0)
  			val->intval = wm97xx_read_bat(bat_ps);
  		else
  			return -EINVAL;
  		break;
  	case POWER_SUPPLY_PROP_TEMP:
  		if (pdata->temp_aux >= 0)
  			val->intval = wm97xx_read_temp(bat_ps);
  		else
  			return -EINVAL;
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
  		if (pdata->max_voltage >= 0)
  			val->intval = pdata->max_voltage;
  		else
  			return -EINVAL;
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
  		if (pdata->min_voltage >= 0)
  			val->intval = pdata->min_voltage;
  		else
  			return -EINVAL;
  		break;
  	case POWER_SUPPLY_PROP_PRESENT:
  		val->intval = 1;
  		break;
  	default:
  		return -EINVAL;
  	}
  	return 0;
  }
  
  static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps)
  {
  	schedule_work(&bat_work);
  }
  
  static void wm97xx_bat_update(struct power_supply *bat_ps)
  {
  	int old_status = bat_status;
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
105
106
  	struct wm97xx_pdata *wmdata = bat_ps->dev->parent->platform_data;
  	struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  
  	mutex_lock(&work_lock);
  
  	bat_status = (pdata->charge_gpio >= 0) ?
  			(gpio_get_value(pdata->charge_gpio) ?
  			POWER_SUPPLY_STATUS_DISCHARGING :
  			POWER_SUPPLY_STATUS_CHARGING) :
  			POWER_SUPPLY_STATUS_UNKNOWN;
  
  	if (old_status != bat_status) {
  		pr_debug("%s: %i -> %i
  ", bat_ps->name, old_status,
  					bat_status);
  		power_supply_changed(bat_ps);
  	}
  
  	mutex_unlock(&work_lock);
  }
  
  static struct power_supply bat_ps = {
  	.type			= POWER_SUPPLY_TYPE_BATTERY,
  	.get_property		= wm97xx_bat_get_property,
  	.external_power_changed = wm97xx_bat_external_power_changed,
  	.use_for_apm		= 1,
  };
  
  static void wm97xx_bat_work(struct work_struct *work)
  {
  	wm97xx_bat_update(&bat_ps);
  }
7c87942ae   Marek Vasut   wm97xx_battery: U...
137
138
139
140
141
  static irqreturn_t wm97xx_chrg_irq(int irq, void *data)
  {
  	schedule_work(&bat_work);
  	return IRQ_HANDLED;
  }
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
142
  #ifdef CONFIG_PM
83a8af0d3   Marek Vasut   wm97xx_battery: C...
143
  static int wm97xx_bat_suspend(struct device *dev)
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
144
  {
bc51e7ff5   Tejun Heo   power_supply: Don...
145
  	flush_work_sync(&bat_work);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
146
147
  	return 0;
  }
83a8af0d3   Marek Vasut   wm97xx_battery: C...
148
  static int wm97xx_bat_resume(struct device *dev)
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
149
150
151
152
  {
  	schedule_work(&bat_work);
  	return 0;
  }
83a8af0d3   Marek Vasut   wm97xx_battery: C...
153

471452104   Alexey Dobriyan   const: constify r...
154
  static const struct dev_pm_ops wm97xx_bat_pm_ops = {
83a8af0d3   Marek Vasut   wm97xx_battery: C...
155
156
157
  	.suspend	= wm97xx_bat_suspend,
  	.resume		= wm97xx_bat_resume,
  };
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
158
159
160
161
162
163
164
  #endif
  
  static int __devinit wm97xx_bat_probe(struct platform_device *dev)
  {
  	int ret = 0;
  	int props = 1;	/* POWER_SUPPLY_PROP_PRESENT */
  	int i = 0;
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
165
166
  	struct wm97xx_pdata *wmdata = dev->dev.platform_data;
  	struct wm97xx_batt_pdata *pdata;
12b336a8b   Mark Brown   wm97xx_battery: H...
167
168
169
170
171
172
173
  	if (!wmdata) {
  		dev_err(&dev->dev, "No platform data supplied
  ");
  		return -EINVAL;
  	}
  
  	pdata = wmdata->batt_pdata;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
174
175
176
  
  	if (dev->id != -1)
  		return -EINVAL;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
177
  	if (!pdata) {
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
178
179
  		dev_err(&dev->dev, "No platform_data supplied
  ");
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
180
181
  		return -EINVAL;
  	}
7c87942ae   Marek Vasut   wm97xx_battery: U...
182
  	if (gpio_is_valid(pdata->charge_gpio)) {
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
183
184
185
186
187
188
  		ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
  		if (ret)
  			goto err;
  		ret = gpio_direction_input(pdata->charge_gpio);
  		if (ret)
  			goto err2;
7c87942ae   Marek Vasut   wm97xx_battery: U...
189
  		ret = request_irq(gpio_to_irq(pdata->charge_gpio),
3b176b25a   Yong Zhang   power_supply: Rem...
190
  				wm97xx_chrg_irq, 0,
f8d94eb77   Mark Brown   wm97xx_battery: C...
191
  				"AC Detect", dev);
7c87942ae   Marek Vasut   wm97xx_battery: U...
192
193
  		if (ret)
  			goto err2;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  		props++;	/* POWER_SUPPLY_PROP_STATUS */
  	}
  
  	if (pdata->batt_tech >= 0)
  		props++;	/* POWER_SUPPLY_PROP_TECHNOLOGY */
  	if (pdata->temp_aux >= 0)
  		props++;	/* POWER_SUPPLY_PROP_TEMP */
  	if (pdata->batt_aux >= 0)
  		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_NOW */
  	if (pdata->max_voltage >= 0)
  		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_MAX */
  	if (pdata->min_voltage >= 0)
  		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_MIN */
  
  	prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
  	if (!prop)
7c87942ae   Marek Vasut   wm97xx_battery: U...
210
  		goto err3;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  
  	prop[i++] = POWER_SUPPLY_PROP_PRESENT;
  	if (pdata->charge_gpio >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_STATUS;
  	if (pdata->batt_tech >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
  	if (pdata->temp_aux >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_TEMP;
  	if (pdata->batt_aux >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
  	if (pdata->max_voltage >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
  	if (pdata->min_voltage >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
  
  	INIT_WORK(&bat_work, wm97xx_bat_work);
  
  	if (!pdata->batt_name) {
  		dev_info(&dev->dev, "Please consider setting proper battery "
  				"name in platform definition file, falling "
  				"back to name \"wm97xx-batt\"
  ");
  		bat_ps.name = "wm97xx-batt";
  	} else
  		bat_ps.name = pdata->batt_name;
  
  	bat_ps.properties = prop;
  	bat_ps.num_properties = props;
  
  	ret = power_supply_register(&dev->dev, &bat_ps);
  	if (!ret)
  		schedule_work(&bat_work);
  	else
7c87942ae   Marek Vasut   wm97xx_battery: U...
244
  		goto err4;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
245
246
  
  	return 0;
7c87942ae   Marek Vasut   wm97xx_battery: U...
247
  err4:
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
248
  	kfree(prop);
7c87942ae   Marek Vasut   wm97xx_battery: U...
249
250
251
  err3:
  	if (gpio_is_valid(pdata->charge_gpio))
  		free_irq(gpio_to_irq(pdata->charge_gpio), dev);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
252
  err2:
7c87942ae   Marek Vasut   wm97xx_battery: U...
253
254
  	if (gpio_is_valid(pdata->charge_gpio))
  		gpio_free(pdata->charge_gpio);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
255
256
257
258
259
260
  err:
  	return ret;
  }
  
  static int __devexit wm97xx_bat_remove(struct platform_device *dev)
  {
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
261
262
  	struct wm97xx_pdata *wmdata = dev->dev.platform_data;
  	struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
7c87942ae   Marek Vasut   wm97xx_battery: U...
263
264
  	if (pdata && gpio_is_valid(pdata->charge_gpio)) {
  		free_irq(gpio_to_irq(pdata->charge_gpio), dev);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
265
  		gpio_free(pdata->charge_gpio);
7c87942ae   Marek Vasut   wm97xx_battery: U...
266
  	}
bc51e7ff5   Tejun Heo   power_supply: Don...
267
  	cancel_work_sync(&bat_work);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
268
269
270
271
272
273
274
275
276
  	power_supply_unregister(&bat_ps);
  	kfree(prop);
  	return 0;
  }
  
  static struct platform_driver wm97xx_bat_driver = {
  	.driver	= {
  		.name	= "wm97xx-battery",
  		.owner	= THIS_MODULE,
83a8af0d3   Marek Vasut   wm97xx_battery: C...
277
278
279
  #ifdef CONFIG_PM
  		.pm	= &wm97xx_bat_pm_ops,
  #endif
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
280
281
282
  	},
  	.probe		= wm97xx_bat_probe,
  	.remove		= __devexit_p(wm97xx_bat_remove),
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
283
  };
300bac7fb   Axel Lin   power_supply: Con...
284
  module_platform_driver(wm97xx_bat_driver);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
285
286
287
288
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
  MODULE_DESCRIPTION("WM97xx battery driver");