Blame view

drivers/power/z2_battery.c 8 KB
e3e8d1c93   Marek Vasut   Driver for Zipit ...
1
2
3
4
5
6
7
8
9
10
  /*
   * Battery measurement code for Zipit Z2
   *
   * Copyright (C) 2009 Peter Edwards <sweetlilmre@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.
   *
   */
e3e8d1c93   Marek Vasut   Driver for Zipit ...
11
  #include <linux/module.h>
e3e8d1c93   Marek Vasut   Driver for Zipit ...
12
  #include <linux/gpio.h>
56b925fcc   Marek Vasut   [ARM] pxa/z2: fix...
13
  #include <linux/i2c.h>
e3e8d1c93   Marek Vasut   Driver for Zipit ...
14
15
  #include <linux/interrupt.h>
  #include <linux/irq.h>
56b925fcc   Marek Vasut   [ARM] pxa/z2: fix...
16
17
  #include <linux/power_supply.h>
  #include <linux/slab.h>
e3e8d1c93   Marek Vasut   Driver for Zipit ...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
105
106
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
  #include <linux/z2_battery.h>
  
  #define	Z2_DEFAULT_NAME	"Z2"
  
  struct z2_charger {
  	struct z2_battery_info	*info;
  	int			bat_status;
  	struct i2c_client	*client;
  	struct power_supply	batt_ps;
  	struct mutex		work_lock;
  	struct work_struct	bat_work;
  };
  
  static unsigned long z2_read_bat(struct z2_charger *charger)
  {
  	int data;
  	data = i2c_smbus_read_byte_data(charger->client,
  					charger->info->batt_I2C_reg);
  	if (data < 0)
  		return 0;
  
  	return data * charger->info->batt_mult / charger->info->batt_div;
  }
  
  static int z2_batt_get_property(struct power_supply *batt_ps,
  			    enum power_supply_property psp,
  			    union power_supply_propval *val)
  {
  	struct z2_charger *charger = container_of(batt_ps, struct z2_charger,
  						batt_ps);
  	struct z2_battery_info *info = charger->info;
  
  	switch (psp) {
  	case POWER_SUPPLY_PROP_STATUS:
  		val->intval = charger->bat_status;
  		break;
  	case POWER_SUPPLY_PROP_TECHNOLOGY:
  		val->intval = info->batt_tech;
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  		if (info->batt_I2C_reg >= 0)
  			val->intval = z2_read_bat(charger);
  		else
  			return -EINVAL;
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
  		if (info->max_voltage >= 0)
  			val->intval = info->max_voltage;
  		else
  			return -EINVAL;
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
  		if (info->min_voltage >= 0)
  			val->intval = info->min_voltage;
  		else
  			return -EINVAL;
  		break;
  	case POWER_SUPPLY_PROP_PRESENT:
  		val->intval = 1;
  		break;
  	default:
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  static void z2_batt_ext_power_changed(struct power_supply *batt_ps)
  {
  	struct z2_charger *charger = container_of(batt_ps, struct z2_charger,
  						batt_ps);
  	schedule_work(&charger->bat_work);
  }
  
  static void z2_batt_update(struct z2_charger *charger)
  {
  	int old_status = charger->bat_status;
  	struct z2_battery_info *info;
  
  	info = charger->info;
  
  	mutex_lock(&charger->work_lock);
  
  	charger->bat_status = (info->charge_gpio >= 0) ?
  		(gpio_get_value(info->charge_gpio) ?
  		POWER_SUPPLY_STATUS_CHARGING :
  		POWER_SUPPLY_STATUS_DISCHARGING) :
  		POWER_SUPPLY_STATUS_UNKNOWN;
  
  	if (old_status != charger->bat_status) {
  		pr_debug("%s: %i -> %i
  ", charger->batt_ps.name, old_status,
  			charger->bat_status);
  		power_supply_changed(&charger->batt_ps);
  	}
  
  	mutex_unlock(&charger->work_lock);
  }
  
  static void z2_batt_work(struct work_struct *work)
  {
  	struct z2_charger *charger;
  	charger = container_of(work, struct z2_charger, bat_work);
  	z2_batt_update(charger);
  }
  
  static irqreturn_t z2_charge_switch_irq(int irq, void *devid)
  {
  	struct z2_charger *charger = devid;
  	schedule_work(&charger->bat_work);
  	return IRQ_HANDLED;
  }
  
  static int z2_batt_ps_init(struct z2_charger *charger, int props)
  {
  	int i = 0;
  	enum power_supply_property *prop;
  	struct z2_battery_info *info = charger->info;
a223246f7   Vasily Khoruzhick   z2_battery: Fix c...
136
137
  	if (info->charge_gpio >= 0)
  		props++;	/* POWER_SUPPLY_PROP_STATUS */
e3e8d1c93   Marek Vasut   Driver for Zipit ...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  	if (info->batt_tech >= 0)
  		props++;	/* POWER_SUPPLY_PROP_TECHNOLOGY */
  	if (info->batt_I2C_reg >= 0)
  		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_NOW */
  	if (info->max_voltage >= 0)
  		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_MAX */
  	if (info->min_voltage >= 0)
  		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_MIN */
  
  	prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
  	if (!prop)
  		return -ENOMEM;
  
  	prop[i++] = POWER_SUPPLY_PROP_PRESENT;
  	if (info->charge_gpio >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_STATUS;
  	if (info->batt_tech >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
  	if (info->batt_I2C_reg >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
  	if (info->max_voltage >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
  	if (info->min_voltage >= 0)
  		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
  
  	if (!info->batt_name) {
  		dev_info(&charger->client->dev,
  				"Please consider setting proper battery "
  				"name in platform definition file, falling "
  				"back to name \" Z2_DEFAULT_NAME \"
  ");
  		charger->batt_ps.name = Z2_DEFAULT_NAME;
  	} else
  		charger->batt_ps.name = info->batt_name;
  
  	charger->batt_ps.properties		= prop;
  	charger->batt_ps.num_properties		= props;
  	charger->batt_ps.type			= POWER_SUPPLY_TYPE_BATTERY;
  	charger->batt_ps.get_property		= z2_batt_get_property;
  	charger->batt_ps.external_power_changed	= z2_batt_ext_power_changed;
  	charger->batt_ps.use_for_apm		= 1;
  
  	return 0;
  }
  
  static int __devinit z2_batt_probe(struct i2c_client *client,
  				const struct i2c_device_id *id)
  {
  	int ret = 0;
  	int props = 1;	/* POWER_SUPPLY_PROP_PRESENT */
  	struct z2_charger *charger;
  	struct z2_battery_info *info = client->dev.platform_data;
  
  	if (info == NULL) {
  		dev_err(&client->dev,
  			"Please set platform device platform_data"
  			" to a valid z2_battery_info pointer!
  ");
  		return -EINVAL;
  	}
  
  	charger = kzalloc(sizeof(*charger), GFP_KERNEL);
  	if (charger == NULL)
  		return -ENOMEM;
  
  	charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
  	charger->info = info;
  	charger->client = client;
  	i2c_set_clientdata(client, charger);
  
  	mutex_init(&charger->work_lock);
  
  	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
  		ret = gpio_request(info->charge_gpio, "BATT CHRG");
  		if (ret)
  			goto err;
  
  		ret = gpio_direction_input(info->charge_gpio);
  		if (ret)
  			goto err2;
dced35aeb   Thomas Gleixner   drivers: Final ir...
218
219
  		irq_set_irq_type(gpio_to_irq(info->charge_gpio),
  				 IRQ_TYPE_EDGE_BOTH);
e3e8d1c93   Marek Vasut   Driver for Zipit ...
220
  		ret = request_irq(gpio_to_irq(info->charge_gpio),
3b176b25a   Yong Zhang   power_supply: Rem...
221
  				z2_charge_switch_irq, 0,
e3e8d1c93   Marek Vasut   Driver for Zipit ...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  				"AC Detect", charger);
  		if (ret)
  			goto err3;
  	}
  
  	ret = z2_batt_ps_init(charger, props);
  	if (ret)
  		goto err3;
  
  	INIT_WORK(&charger->bat_work, z2_batt_work);
  
  	ret = power_supply_register(&client->dev, &charger->batt_ps);
  	if (ret)
  		goto err4;
  
  	schedule_work(&charger->bat_work);
  
  	return 0;
  
  err4:
  	kfree(charger->batt_ps.properties);
  err3:
  	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
  		free_irq(gpio_to_irq(info->charge_gpio), charger);
  err2:
  	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
  		gpio_free(info->charge_gpio);
  err:
  	kfree(charger);
  	return ret;
  }
  
  static int __devexit z2_batt_remove(struct i2c_client *client)
  {
  	struct z2_charger *charger = i2c_get_clientdata(client);
  	struct z2_battery_info *info = charger->info;
bc51e7ff5   Tejun Heo   power_supply: Don...
258
  	cancel_work_sync(&charger->bat_work);
e3e8d1c93   Marek Vasut   Driver for Zipit ...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  	power_supply_unregister(&charger->batt_ps);
  
  	kfree(charger->batt_ps.properties);
  	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
  		free_irq(gpio_to_irq(info->charge_gpio), charger);
  		gpio_free(info->charge_gpio);
  	}
  
  	kfree(charger);
  
  	return 0;
  }
  
  #ifdef CONFIG_PM
906649de0   Marek Vasut   PXA: Use dev_pm_o...
273
  static int z2_batt_suspend(struct device *dev)
e3e8d1c93   Marek Vasut   Driver for Zipit ...
274
  {
906649de0   Marek Vasut   PXA: Use dev_pm_o...
275
  	struct i2c_client *client = to_i2c_client(dev);
bc51e7ff5   Tejun Heo   power_supply: Don...
276
277
278
  	struct z2_charger *charger = i2c_get_clientdata(client);
  
  	flush_work_sync(&charger->bat_work);
e3e8d1c93   Marek Vasut   Driver for Zipit ...
279
280
  	return 0;
  }
906649de0   Marek Vasut   PXA: Use dev_pm_o...
281
  static int z2_batt_resume(struct device *dev)
e3e8d1c93   Marek Vasut   Driver for Zipit ...
282
  {
906649de0   Marek Vasut   PXA: Use dev_pm_o...
283
  	struct i2c_client *client = to_i2c_client(dev);
e3e8d1c93   Marek Vasut   Driver for Zipit ...
284
285
286
287
288
  	struct z2_charger *charger = i2c_get_clientdata(client);
  
  	schedule_work(&charger->bat_work);
  	return 0;
  }
906649de0   Marek Vasut   PXA: Use dev_pm_o...
289
290
291
292
293
294
295
  
  static const struct dev_pm_ops z2_battery_pm_ops = {
  	.suspend	= z2_batt_suspend,
  	.resume		= z2_batt_resume,
  };
  
  #define	Z2_BATTERY_PM_OPS	(&z2_battery_pm_ops)
e3e8d1c93   Marek Vasut   Driver for Zipit ...
296
  #else
906649de0   Marek Vasut   PXA: Use dev_pm_o...
297
  #define	Z2_BATTERY_PM_OPS	(NULL)
e3e8d1c93   Marek Vasut   Driver for Zipit ...
298
299
300
301
302
303
  #endif
  
  static const struct i2c_device_id z2_batt_id[] = {
  	{ "aer915", 0 },
  	{ }
  };
dcbc9169e   Axel Lin   z2_battery: Add M...
304
  MODULE_DEVICE_TABLE(i2c, z2_batt_id);
e3e8d1c93   Marek Vasut   Driver for Zipit ...
305
306
307
308
309
  
  static struct i2c_driver z2_batt_driver = {
  	.driver	= {
  		.name	= "z2-battery",
  		.owner	= THIS_MODULE,
906649de0   Marek Vasut   PXA: Use dev_pm_o...
310
  		.pm	= Z2_BATTERY_PM_OPS
e3e8d1c93   Marek Vasut   Driver for Zipit ...
311
312
  	},
  	.probe		= z2_batt_probe,
0bea4b866   Axel Lin   ds2780_battery&z2...
313
  	.remove		= __devexit_p(z2_batt_remove),
e3e8d1c93   Marek Vasut   Driver for Zipit ...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
  	.id_table	= z2_batt_id,
  };
  
  static int __init z2_batt_init(void)
  {
  	return i2c_add_driver(&z2_batt_driver);
  }
  
  static void __exit z2_batt_exit(void)
  {
  	i2c_del_driver(&z2_batt_driver);
  }
  
  module_init(z2_batt_init);
  module_exit(z2_batt_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
  MODULE_DESCRIPTION("Zipit Z2 battery driver");