Blame view

drivers/power/wm97xx_battery.c 7.23 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)
  {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
35
  	struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
36
  	struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
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)
  {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
44
  	struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
45
  	struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
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)
  {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
55
  	struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
56
  	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;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
105
  	struct wm97xx_pdata *wmdata = bat_ps->dev.parent->platform_data;
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
106
  	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
  
  	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) {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
117
118
  		pr_debug("%s: %i -> %i
  ", bat_ps->desc->name, old_status,
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
119
120
121
122
123
124
  					bat_status);
  		power_supply_changed(bat_ps);
  	}
  
  	mutex_unlock(&work_lock);
  }
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
125
126
  static struct power_supply *bat_psy;
  static struct power_supply_desc bat_psy_desc = {
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
127
128
129
130
131
132
133
134
  	.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)
  {
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
135
  	wm97xx_bat_update(bat_psy);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
136
  }
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
  {
43829731d   Tejun Heo   workqueue: deprec...
145
  	flush_work(&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
  #endif
c8afa6406   Bill Pemberton   power: remove use...
159
  static int wm97xx_bat_probe(struct platform_device *dev)
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
160
161
162
163
  {
  	int ret = 0;
  	int props = 1;	/* POWER_SUPPLY_PROP_PRESENT */
  	int i = 0;
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
164
165
  	struct wm97xx_pdata *wmdata = dev->dev.platform_data;
  	struct wm97xx_batt_pdata *pdata;
12b336a8b   Mark Brown   wm97xx_battery: H...
166
167
168
169
170
171
172
  	if (!wmdata) {
  		dev_err(&dev->dev, "No platform data supplied
  ");
  		return -EINVAL;
  	}
  
  	pdata = wmdata->batt_pdata;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
173
174
175
  
  	if (dev->id != -1)
  		return -EINVAL;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
176
  	if (!pdata) {
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
177
178
  		dev_err(&dev->dev, "No platform_data supplied
  ");
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
179
180
  		return -EINVAL;
  	}
7c87942ae   Marek Vasut   wm97xx_battery: U...
181
  	if (gpio_is_valid(pdata->charge_gpio)) {
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
182
183
184
185
186
187
  		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...
188
  		ret = request_irq(gpio_to_irq(pdata->charge_gpio),
3b176b25a   Yong Zhang   power_supply: Rem...
189
  				wm97xx_chrg_irq, 0,
f8d94eb77   Mark Brown   wm97xx_battery: C...
190
  				"AC Detect", dev);
7c87942ae   Marek Vasut   wm97xx_battery: U...
191
192
  		if (ret)
  			goto err2;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  		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);
588d4f0b0   Julia Lawall   wm97xx_battery: F...
208
209
  	if (!prop) {
  		ret = -ENOMEM;
7c87942ae   Marek Vasut   wm97xx_battery: U...
210
  		goto err3;
588d4f0b0   Julia Lawall   wm97xx_battery: F...
211
  	}
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
  
  	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\"
  ");
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
234
  		bat_psy_desc.name = "wm97xx-batt";
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
235
  	} else
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
236
  		bat_psy_desc.name = pdata->batt_name;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
237

297d716f6   Krzysztof Kozlowski   power_supply: Cha...
238
239
  	bat_psy_desc.properties = prop;
  	bat_psy_desc.num_properties = props;
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
240

297d716f6   Krzysztof Kozlowski   power_supply: Cha...
241
242
  	bat_psy = power_supply_register(&dev->dev, &bat_psy_desc, NULL);
  	if (!IS_ERR(bat_psy)) {
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
243
  		schedule_work(&bat_work);
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
244
245
  	} else {
  		ret = PTR_ERR(bat_psy);
7c87942ae   Marek Vasut   wm97xx_battery: U...
246
  		goto err4;
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
247
  	}
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
248
249
  
  	return 0;
7c87942ae   Marek Vasut   wm97xx_battery: U...
250
  err4:
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
251
  	kfree(prop);
7c87942ae   Marek Vasut   wm97xx_battery: U...
252
253
254
  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...
255
  err2:
7c87942ae   Marek Vasut   wm97xx_battery: U...
256
257
  	if (gpio_is_valid(pdata->charge_gpio))
  		gpio_free(pdata->charge_gpio);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
258
259
260
  err:
  	return ret;
  }
415ec69fb   Bill Pemberton   power: remove use...
261
  static int wm97xx_bat_remove(struct platform_device *dev)
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
262
  {
b8bdc1d0c   Marek Vasut   wm97xx_battery: U...
263
264
  	struct wm97xx_pdata *wmdata = dev->dev.platform_data;
  	struct wm97xx_batt_pdata *pdata = wmdata->batt_pdata;
7c87942ae   Marek Vasut   wm97xx_battery: U...
265
266
  	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...
267
  		gpio_free(pdata->charge_gpio);
7c87942ae   Marek Vasut   wm97xx_battery: U...
268
  	}
bc51e7ff5   Tejun Heo   power_supply: Don...
269
  	cancel_work_sync(&bat_work);
297d716f6   Krzysztof Kozlowski   power_supply: Cha...
270
  	power_supply_unregister(bat_psy);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
271
272
273
274
275
276
277
  	kfree(prop);
  	return 0;
  }
  
  static struct platform_driver wm97xx_bat_driver = {
  	.driver	= {
  		.name	= "wm97xx-battery",
83a8af0d3   Marek Vasut   wm97xx_battery: C...
278
279
280
  #ifdef CONFIG_PM
  		.pm	= &wm97xx_bat_pm_ops,
  #endif
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
281
282
  	},
  	.probe		= wm97xx_bat_probe,
28ea73f4c   Bill Pemberton   power: remove use...
283
  	.remove		= wm97xx_bat_remove,
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
284
  };
300bac7fb   Axel Lin   power_supply: Con...
285
  module_platform_driver(wm97xx_bat_driver);
4e9687d9c   Marek Vašut   [ARM] 5248/1: wm9...
286
287
288
289
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
  MODULE_DESCRIPTION("WM97xx battery driver");