Blame view

drivers/power/ds2782_battery.c 9.51 KB
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
1
2
3
4
5
6
7
  /*
   * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC
   *
   * Copyright (C) 2009 Bluewater Systems Ltd
   *
   * Author: Ryan Mallon <ryan@bluewatersys.com>
   *
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
8
9
   * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
   *
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   * 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/kernel.h>
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/swab.h>
  #include <linux/i2c.h>
  #include <linux/idr.h>
  #include <linux/power_supply.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
24
  #include <linux/slab.h>
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
25
  #include <linux/ds2782_battery.h>
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
26
27
  
  #define DS2782_REG_RARC		0x06	/* Remaining active relative capacity */
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
28
29
30
  #define DS278x_REG_VOLT_MSB	0x0c
  #define DS278x_REG_TEMP_MSB	0x0a
  #define DS278x_REG_CURRENT_MSB	0x0e
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
31
32
33
34
35
36
  
  /* EEPROM Block */
  #define DS2782_REG_RSNSP	0x69	/* Sense resistor value */
  
  /* Current unit measurement in uA for a 1 milli-ohm sense resistor */
  #define DS2782_CURRENT_UNITS	1563
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
37
38
39
40
41
42
43
  #define DS2786_REG_RARC		0x02	/* Remaining active relative capacity */
  
  #define DS2786_CURRENT_UNITS	25
  
  struct ds278x_info;
  
  struct ds278x_battery_ops {
eb9650d6d   Peter Huewe   ds2782_battery: R...
44
  	int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
353f867b5   Ryan Mallon   ds2782_battery: F...
45
46
  	int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV);
  	int (*get_battery_capacity)(struct ds278x_info *info, int *capacity);
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
47
48
49
  };
  
  #define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
50

9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
51
  struct ds278x_info {
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
52
53
  	struct i2c_client	*client;
  	struct power_supply	battery;
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
54
  	struct ds278x_battery_ops  *ops;
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
55
  	int			id;
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
56
  	int                     rsns;
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
57
58
59
60
  };
  
  static DEFINE_IDR(battery_id);
  static DEFINE_MUTEX(battery_lock);
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
61
  static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
62
63
64
65
66
67
68
69
70
71
72
73
74
  {
  	int ret;
  
  	ret = i2c_smbus_read_byte_data(info->client, reg);
  	if (ret < 0) {
  		dev_err(&info->client->dev, "register read failed
  ");
  		return ret;
  	}
  
  	*val = ret;
  	return 0;
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
75
  static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  				    s16 *val)
  {
  	int ret;
  
  	ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb));
  	if (ret < 0) {
  		dev_err(&info->client->dev, "register read failed
  ");
  		return ret;
  	}
  
  	*val = ret;
  	return 0;
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
90
  static int ds278x_get_temp(struct ds278x_info *info, int *temp)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
91
92
93
94
95
96
97
98
99
100
  {
  	s16 raw;
  	int err;
  
  	/*
  	 * Temperature is measured in units of 0.125 degrees celcius, the
  	 * power_supply class measures temperature in tenths of degrees
  	 * celsius. The temperature value is stored as a 10 bit number, plus
  	 * sign in the upper bits of a 16 bit register.
  	 */
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
101
  	err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
102
103
104
105
106
  	if (err)
  		return err;
  	*temp = ((raw / 32) * 125) / 100;
  	return 0;
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
107
  static int ds2782_get_current(struct ds278x_info *info, int *current_uA)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
108
109
110
111
112
113
114
115
116
117
  {
  	int sense_res;
  	int err;
  	u8 sense_res_raw;
  	s16 raw;
  
  	/*
  	 * The units of measurement for current are dependent on the value of
  	 * the sense resistor.
  	 */
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
118
  	err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
119
120
121
122
123
124
125
126
127
128
129
130
  	if (err)
  		return err;
  	if (sense_res_raw == 0) {
  		dev_err(&info->client->dev, "sense resistor value is 0
  ");
  		return -ENXIO;
  	}
  	sense_res = 1000 / sense_res_raw;
  
  	dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms
  ",
  		sense_res);
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
131
  	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
132
133
134
135
136
  	if (err)
  		return err;
  	*current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
  	return 0;
  }
353f867b5   Ryan Mallon   ds2782_battery: F...
137
  static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
138
139
140
141
142
143
144
145
  {
  	s16 raw;
  	int err;
  
  	/*
  	 * Voltage is measured in units of 4.88mV. The voltage is stored as
  	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
  	 */
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
146
  	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
147
148
  	if (err)
  		return err;
353f867b5   Ryan Mallon   ds2782_battery: F...
149
  	*voltage_uV = (raw / 32) * 4800;
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
150
151
  	return 0;
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
152
  static int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
153
154
155
  {
  	int err;
  	u8 raw;
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
156
  	err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
157
158
159
  	if (err)
  		return err;
  	*capacity = raw;
2d31757c8   Ryan Mallon   ds2782_battery: F...
160
  	return 0;
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
161
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
162
163
164
165
166
167
168
169
170
171
172
  static int ds2786_get_current(struct ds278x_info *info, int *current_uA)
  {
  	int err;
  	s16 raw;
  
  	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
  	if (err)
  		return err;
  	*current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
  	return 0;
  }
353f867b5   Ryan Mallon   ds2782_battery: F...
173
  static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV)
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
174
175
176
177
178
179
180
181
182
183
184
  {
  	s16 raw;
  	int err;
  
  	/*
  	 * Voltage is measured in units of 1.22mV. The voltage is stored as
  	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
  	 */
  	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
  	if (err)
  		return err;
353f867b5   Ryan Mallon   ds2782_battery: F...
185
  	*voltage_uV = (raw / 8) * 1220;
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  	return 0;
  }
  
  static int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
  {
  	int err;
  	u8 raw;
  
  	err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
  	if (err)
  		return err;
  	/* Relative capacity is displayed with resolution 0.5 % */
  	*capacity = raw/2 ;
  	return 0;
  }
  
  static int ds278x_get_status(struct ds278x_info *info, int *status)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
203
204
205
206
  {
  	int err;
  	int current_uA;
  	int capacity;
eb9650d6d   Peter Huewe   ds2782_battery: R...
207
  	err = info->ops->get_battery_current(info, &current_uA);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
208
209
  	if (err)
  		return err;
eb9650d6d   Peter Huewe   ds2782_battery: R...
210
  	err = info->ops->get_battery_capacity(info, &capacity);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  	if (err)
  		return err;
  
  	if (capacity == 100)
  		*status = POWER_SUPPLY_STATUS_FULL;
  	else if (current_uA == 0)
  		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
  	else if (current_uA < 0)
  		*status = POWER_SUPPLY_STATUS_DISCHARGING;
  	else
  		*status = POWER_SUPPLY_STATUS_CHARGING;
  
  	return 0;
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
225
  static int ds278x_battery_get_property(struct power_supply *psy,
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
226
227
228
  				       enum power_supply_property prop,
  				       union power_supply_propval *val)
  {
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
229
  	struct ds278x_info *info = to_ds278x_info(psy);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
230
231
232
233
  	int ret;
  
  	switch (prop) {
  	case POWER_SUPPLY_PROP_STATUS:
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
234
  		ret = ds278x_get_status(info, &val->intval);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
235
236
237
  		break;
  
  	case POWER_SUPPLY_PROP_CAPACITY:
eb9650d6d   Peter Huewe   ds2782_battery: R...
238
  		ret = info->ops->get_battery_capacity(info, &val->intval);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
239
240
241
  		break;
  
  	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
eb9650d6d   Peter Huewe   ds2782_battery: R...
242
  		ret = info->ops->get_battery_voltage(info, &val->intval);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
243
244
245
  		break;
  
  	case POWER_SUPPLY_PROP_CURRENT_NOW:
eb9650d6d   Peter Huewe   ds2782_battery: R...
246
  		ret = info->ops->get_battery_current(info, &val->intval);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
247
248
249
  		break;
  
  	case POWER_SUPPLY_PROP_TEMP:
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
250
  		ret = ds278x_get_temp(info, &val->intval);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
251
252
253
254
255
256
257
258
  		break;
  
  	default:
  		ret = -EINVAL;
  	}
  
  	return ret;
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
259
  static enum power_supply_property ds278x_battery_props[] = {
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
260
261
262
263
264
265
  	POWER_SUPPLY_PROP_STATUS,
  	POWER_SUPPLY_PROP_CAPACITY,
  	POWER_SUPPLY_PROP_VOLTAGE_NOW,
  	POWER_SUPPLY_PROP_CURRENT_NOW,
  	POWER_SUPPLY_PROP_TEMP,
  };
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
266
  static void ds278x_power_supply_init(struct power_supply *battery)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
267
268
  {
  	battery->type			= POWER_SUPPLY_TYPE_BATTERY;
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
269
270
271
  	battery->properties		= ds278x_battery_props;
  	battery->num_properties		= ARRAY_SIZE(ds278x_battery_props);
  	battery->get_property		= ds278x_battery_get_property;
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
272
273
  	battery->external_power_changed	= NULL;
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
274
  static int ds278x_battery_remove(struct i2c_client *client)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
275
  {
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
276
  	struct ds278x_info *info = i2c_get_clientdata(client);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
277
278
279
280
281
282
283
  
  	power_supply_unregister(&info->battery);
  	kfree(info->battery.name);
  
  	mutex_lock(&battery_lock);
  	idr_remove(&battery_id, info->id);
  	mutex_unlock(&battery_lock);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
284
285
286
  	kfree(info);
  	return 0;
  }
ab6cc8f9b   Anton Vorontsov   ds2782_battery: G...
287
288
289
290
  enum ds278x_num_id {
  	DS2782 = 0,
  	DS2786,
  };
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
291
  static struct ds278x_battery_ops ds278x_ops[] = {
ab6cc8f9b   Anton Vorontsov   ds2782_battery: G...
292
  	[DS2782] = {
eb9650d6d   Peter Huewe   ds2782_battery: R...
293
294
295
  		.get_battery_current  = ds2782_get_current,
  		.get_battery_voltage  = ds2782_get_voltage,
  		.get_battery_capacity = ds2782_get_capacity,
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
296
  	},
ab6cc8f9b   Anton Vorontsov   ds2782_battery: G...
297
  	[DS2786] = {
eb9650d6d   Peter Huewe   ds2782_battery: R...
298
299
300
  		.get_battery_current  = ds2786_get_current,
  		.get_battery_voltage  = ds2786_get_voltage,
  		.get_battery_capacity = ds2786_get_capacity,
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
301
302
303
304
  	}
  };
  
  static int ds278x_battery_probe(struct i2c_client *client,
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
305
306
  				const struct i2c_device_id *id)
  {
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
307
308
  	struct ds278x_platform_data *pdata = client->dev.platform_data;
  	struct ds278x_info *info;
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
309
310
  	int ret;
  	int num;
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
311
312
313
314
  	/*
  	 * ds2786 should have the sense resistor value set
  	 * in the platform data
  	 */
ab6cc8f9b   Anton Vorontsov   ds2782_battery: G...
315
  	if (id->driver_data == DS2786 && !pdata) {
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
316
317
318
319
  		dev_err(&client->dev, "missing platform data for ds2786
  ");
  		return -EINVAL;
  	}
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
  	/* Get an ID for this battery */
  	ret = idr_pre_get(&battery_id, GFP_KERNEL);
  	if (ret == 0) {
  		ret = -ENOMEM;
  		goto fail_id;
  	}
  
  	mutex_lock(&battery_lock);
  	ret = idr_get_new(&battery_id, client, &num);
  	mutex_unlock(&battery_lock);
  	if (ret < 0)
  		goto fail_id;
  
  	info = kzalloc(sizeof(*info), GFP_KERNEL);
  	if (!info) {
  		ret = -ENOMEM;
  		goto fail_info;
  	}
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
338
  	info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
339
340
341
342
  	if (!info->battery.name) {
  		ret = -ENOMEM;
  		goto fail_name;
  	}
ab6cc8f9b   Anton Vorontsov   ds2782_battery: G...
343
  	if (id->driver_data == DS2786)
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
344
  		info->rsns = pdata->rsns;
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
345
346
  	i2c_set_clientdata(client, info);
  	info->client = client;
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
347
348
349
  	info->id = num;
  	info->ops  = &ds278x_ops[id->driver_data];
  	ds278x_power_supply_init(&info->battery);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
350
351
352
353
354
355
356
357
358
359
360
361
362
  
  	ret = power_supply_register(&client->dev, &info->battery);
  	if (ret) {
  		dev_err(&client->dev, "failed to register battery
  ");
  		goto fail_register;
  	}
  
  	return 0;
  
  fail_register:
  	kfree(info->battery.name);
  fail_name:
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
363
364
365
366
367
368
369
370
  	kfree(info);
  fail_info:
  	mutex_lock(&battery_lock);
  	idr_remove(&battery_id, num);
  	mutex_unlock(&battery_lock);
  fail_id:
  	return ret;
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
371
  static const struct i2c_device_id ds278x_id[] = {
ab6cc8f9b   Anton Vorontsov   ds2782_battery: G...
372
373
  	{"ds2782", DS2782},
  	{"ds2786", DS2786},
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
374
375
  	{},
  };
84ab16f54   Axel Lin   ds2782_battery: A...
376
  MODULE_DEVICE_TABLE(i2c, ds278x_id);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
377

9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
378
  static struct i2c_driver ds278x_battery_driver = {
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
379
380
381
  	.driver 	= {
  		.name	= "ds2782-battery",
  	},
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
382
383
384
  	.probe		= ds278x_battery_probe,
  	.remove		= ds278x_battery_remove,
  	.id_table	= ds278x_id,
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
385
  };
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
386
  static int __init ds278x_init(void)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
387
  {
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
388
  	return i2c_add_driver(&ds278x_battery_driver);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
389
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
390
  module_init(ds278x_init);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
391

9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
392
  static void __exit ds278x_exit(void)
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
393
  {
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
394
  	i2c_del_driver(&ds278x_battery_driver);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
395
  }
9b9ade6b6   Yulia Vilensky   ds2782_battery: A...
396
  module_exit(ds278x_exit);
bfdb46ce8   Ryan Mallon   Add ds2782 batter...
397
398
399
400
  
  MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
  MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
  MODULE_LICENSE("GPL");