1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC |
4 | * |
5 | * Copyright (C) 2009 Bluewater Systems Ltd |
6 | * |
7 | * Author: Ryan Mallon |
8 | * |
9 | * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il> |
10 | * |
11 | * UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru> |
12 | */ |
13 | |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/types.h> |
17 | #include <linux/errno.h> |
18 | #include <linux/swab.h> |
19 | #include <linux/i2c.h> |
20 | #include <linux/delay.h> |
21 | #include <linux/idr.h> |
22 | #include <linux/power_supply.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/ds2782_battery.h> |
25 | |
26 | #define DS2782_REG_RARC 0x06 /* Remaining active relative capacity */ |
27 | |
28 | #define DS278x_REG_VOLT_MSB 0x0c |
29 | #define DS278x_REG_TEMP_MSB 0x0a |
30 | #define DS278x_REG_CURRENT_MSB 0x0e |
31 | |
32 | /* EEPROM Block */ |
33 | #define DS2782_REG_RSNSP 0x69 /* Sense resistor value */ |
34 | |
35 | /* Current unit measurement in uA for a 1 milli-ohm sense resistor */ |
36 | #define DS2782_CURRENT_UNITS 1563 |
37 | |
38 | #define DS2786_REG_RARC 0x02 /* Remaining active relative capacity */ |
39 | |
40 | #define DS2786_CURRENT_UNITS 25 |
41 | |
42 | #define DS278x_DELAY 1000 |
43 | |
44 | struct ds278x_info; |
45 | |
46 | struct ds278x_battery_ops { |
47 | int (*get_battery_current)(struct ds278x_info *info, int *current_uA); |
48 | int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV); |
49 | int (*get_battery_capacity)(struct ds278x_info *info, int *capacity); |
50 | }; |
51 | |
52 | #define to_ds278x_info(x) power_supply_get_drvdata(x) |
53 | |
54 | struct ds278x_info { |
55 | struct i2c_client *client; |
56 | struct power_supply *battery; |
57 | struct power_supply_desc battery_desc; |
58 | const struct ds278x_battery_ops *ops; |
59 | struct delayed_work bat_work; |
60 | int id; |
61 | int rsns; |
62 | int capacity; |
63 | int status; /* State Of Charge */ |
64 | }; |
65 | |
66 | static DEFINE_IDR(battery_id); |
67 | static DEFINE_MUTEX(battery_lock); |
68 | |
69 | static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val) |
70 | { |
71 | int ret; |
72 | |
73 | ret = i2c_smbus_read_byte_data(client: info->client, command: reg); |
74 | if (ret < 0) { |
75 | dev_err(&info->client->dev, "register read failed\n" ); |
76 | return ret; |
77 | } |
78 | |
79 | *val = ret; |
80 | return 0; |
81 | } |
82 | |
83 | static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb, |
84 | s16 *val) |
85 | { |
86 | int ret; |
87 | |
88 | ret = i2c_smbus_read_word_data(client: info->client, command: reg_msb); |
89 | if (ret < 0) { |
90 | dev_err(&info->client->dev, "register read failed\n" ); |
91 | return ret; |
92 | } |
93 | |
94 | *val = swab16(ret); |
95 | return 0; |
96 | } |
97 | |
98 | static int ds278x_get_temp(struct ds278x_info *info, int *temp) |
99 | { |
100 | s16 raw; |
101 | int err; |
102 | |
103 | /* |
104 | * Temperature is measured in units of 0.125 degrees celcius, the |
105 | * power_supply class measures temperature in tenths of degrees |
106 | * celsius. The temperature value is stored as a 10 bit number, plus |
107 | * sign in the upper bits of a 16 bit register. |
108 | */ |
109 | err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, val: &raw); |
110 | if (err) |
111 | return err; |
112 | *temp = ((raw / 32) * 125) / 100; |
113 | return 0; |
114 | } |
115 | |
116 | static int ds2782_get_current(struct ds278x_info *info, int *current_uA) |
117 | { |
118 | int sense_res; |
119 | int err; |
120 | u8 sense_res_raw; |
121 | s16 raw; |
122 | |
123 | /* |
124 | * The units of measurement for current are dependent on the value of |
125 | * the sense resistor. |
126 | */ |
127 | err = ds278x_read_reg(info, DS2782_REG_RSNSP, val: &sense_res_raw); |
128 | if (err) |
129 | return err; |
130 | if (sense_res_raw == 0) { |
131 | dev_err(&info->client->dev, "sense resistor value is 0\n" ); |
132 | return -ENXIO; |
133 | } |
134 | sense_res = 1000 / sense_res_raw; |
135 | |
136 | dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n" , |
137 | sense_res); |
138 | err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, val: &raw); |
139 | if (err) |
140 | return err; |
141 | *current_uA = raw * (DS2782_CURRENT_UNITS / sense_res); |
142 | return 0; |
143 | } |
144 | |
145 | static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV) |
146 | { |
147 | s16 raw; |
148 | int err; |
149 | |
150 | /* |
151 | * Voltage is measured in units of 4.88mV. The voltage is stored as |
152 | * a 10-bit number plus sign, in the upper bits of a 16-bit register |
153 | */ |
154 | err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, val: &raw); |
155 | if (err) |
156 | return err; |
157 | *voltage_uV = (raw / 32) * 4800; |
158 | return 0; |
159 | } |
160 | |
161 | static int ds2782_get_capacity(struct ds278x_info *info, int *capacity) |
162 | { |
163 | int err; |
164 | u8 raw; |
165 | |
166 | err = ds278x_read_reg(info, DS2782_REG_RARC, val: &raw); |
167 | if (err) |
168 | return err; |
169 | *capacity = raw; |
170 | return 0; |
171 | } |
172 | |
173 | static int ds2786_get_current(struct ds278x_info *info, int *current_uA) |
174 | { |
175 | int err; |
176 | s16 raw; |
177 | |
178 | err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, val: &raw); |
179 | if (err) |
180 | return err; |
181 | *current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns); |
182 | return 0; |
183 | } |
184 | |
185 | static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV) |
186 | { |
187 | s16 raw; |
188 | int err; |
189 | |
190 | /* |
191 | * Voltage is measured in units of 1.22mV. The voltage is stored as |
192 | * a 12-bit number plus sign, in the upper bits of a 16-bit register |
193 | */ |
194 | err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, val: &raw); |
195 | if (err) |
196 | return err; |
197 | *voltage_uV = (raw / 8) * 1220; |
198 | return 0; |
199 | } |
200 | |
201 | static int ds2786_get_capacity(struct ds278x_info *info, int *capacity) |
202 | { |
203 | int err; |
204 | u8 raw; |
205 | |
206 | err = ds278x_read_reg(info, DS2786_REG_RARC, val: &raw); |
207 | if (err) |
208 | return err; |
209 | /* Relative capacity is displayed with resolution 0.5 % */ |
210 | *capacity = raw/2 ; |
211 | return 0; |
212 | } |
213 | |
214 | static int ds278x_get_status(struct ds278x_info *info, int *status) |
215 | { |
216 | int err; |
217 | int current_uA; |
218 | int capacity; |
219 | |
220 | err = info->ops->get_battery_current(info, ¤t_uA); |
221 | if (err) |
222 | return err; |
223 | |
224 | err = info->ops->get_battery_capacity(info, &capacity); |
225 | if (err) |
226 | return err; |
227 | |
228 | info->capacity = capacity; |
229 | |
230 | if (capacity == 100) |
231 | *status = POWER_SUPPLY_STATUS_FULL; |
232 | else if (current_uA == 0) |
233 | *status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
234 | else if (current_uA < 0) |
235 | *status = POWER_SUPPLY_STATUS_DISCHARGING; |
236 | else |
237 | *status = POWER_SUPPLY_STATUS_CHARGING; |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | static int ds278x_battery_get_property(struct power_supply *psy, |
243 | enum power_supply_property prop, |
244 | union power_supply_propval *val) |
245 | { |
246 | struct ds278x_info *info = to_ds278x_info(psy); |
247 | int ret; |
248 | |
249 | switch (prop) { |
250 | case POWER_SUPPLY_PROP_STATUS: |
251 | ret = ds278x_get_status(info, status: &val->intval); |
252 | break; |
253 | |
254 | case POWER_SUPPLY_PROP_CAPACITY: |
255 | ret = info->ops->get_battery_capacity(info, &val->intval); |
256 | break; |
257 | |
258 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
259 | ret = info->ops->get_battery_voltage(info, &val->intval); |
260 | break; |
261 | |
262 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
263 | ret = info->ops->get_battery_current(info, &val->intval); |
264 | break; |
265 | |
266 | case POWER_SUPPLY_PROP_TEMP: |
267 | ret = ds278x_get_temp(info, temp: &val->intval); |
268 | break; |
269 | |
270 | default: |
271 | ret = -EINVAL; |
272 | } |
273 | |
274 | return ret; |
275 | } |
276 | |
277 | static void ds278x_bat_update(struct ds278x_info *info) |
278 | { |
279 | int old_status = info->status; |
280 | int old_capacity = info->capacity; |
281 | |
282 | ds278x_get_status(info, status: &info->status); |
283 | |
284 | if ((old_status != info->status) || (old_capacity != info->capacity)) |
285 | power_supply_changed(psy: info->battery); |
286 | } |
287 | |
288 | static void ds278x_bat_work(struct work_struct *work) |
289 | { |
290 | struct ds278x_info *info; |
291 | |
292 | info = container_of(work, struct ds278x_info, bat_work.work); |
293 | ds278x_bat_update(info); |
294 | |
295 | schedule_delayed_work(dwork: &info->bat_work, DS278x_DELAY); |
296 | } |
297 | |
298 | static enum power_supply_property ds278x_battery_props[] = { |
299 | POWER_SUPPLY_PROP_STATUS, |
300 | POWER_SUPPLY_PROP_CAPACITY, |
301 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
302 | POWER_SUPPLY_PROP_CURRENT_NOW, |
303 | POWER_SUPPLY_PROP_TEMP, |
304 | }; |
305 | |
306 | static void ds278x_power_supply_init(struct power_supply_desc *battery) |
307 | { |
308 | battery->type = POWER_SUPPLY_TYPE_BATTERY; |
309 | battery->properties = ds278x_battery_props; |
310 | battery->num_properties = ARRAY_SIZE(ds278x_battery_props); |
311 | battery->get_property = ds278x_battery_get_property; |
312 | battery->external_power_changed = NULL; |
313 | } |
314 | |
315 | static void ds278x_battery_remove(struct i2c_client *client) |
316 | { |
317 | struct ds278x_info *info = i2c_get_clientdata(client); |
318 | int id = info->id; |
319 | |
320 | power_supply_unregister(psy: info->battery); |
321 | cancel_delayed_work_sync(dwork: &info->bat_work); |
322 | kfree(objp: info->battery_desc.name); |
323 | kfree(objp: info); |
324 | |
325 | mutex_lock(&battery_lock); |
326 | idr_remove(&battery_id, id); |
327 | mutex_unlock(lock: &battery_lock); |
328 | } |
329 | |
330 | #ifdef CONFIG_PM_SLEEP |
331 | |
332 | static int ds278x_suspend(struct device *dev) |
333 | { |
334 | struct i2c_client *client = to_i2c_client(dev); |
335 | struct ds278x_info *info = i2c_get_clientdata(client); |
336 | |
337 | cancel_delayed_work(dwork: &info->bat_work); |
338 | return 0; |
339 | } |
340 | |
341 | static int ds278x_resume(struct device *dev) |
342 | { |
343 | struct i2c_client *client = to_i2c_client(dev); |
344 | struct ds278x_info *info = i2c_get_clientdata(client); |
345 | |
346 | schedule_delayed_work(dwork: &info->bat_work, DS278x_DELAY); |
347 | return 0; |
348 | } |
349 | #endif /* CONFIG_PM_SLEEP */ |
350 | |
351 | static SIMPLE_DEV_PM_OPS(ds278x_battery_pm_ops, ds278x_suspend, ds278x_resume); |
352 | |
353 | enum ds278x_num_id { |
354 | DS2782 = 0, |
355 | DS2786, |
356 | }; |
357 | |
358 | static const struct ds278x_battery_ops ds278x_ops[] = { |
359 | [DS2782] = { |
360 | .get_battery_current = ds2782_get_current, |
361 | .get_battery_voltage = ds2782_get_voltage, |
362 | .get_battery_capacity = ds2782_get_capacity, |
363 | }, |
364 | [DS2786] = { |
365 | .get_battery_current = ds2786_get_current, |
366 | .get_battery_voltage = ds2786_get_voltage, |
367 | .get_battery_capacity = ds2786_get_capacity, |
368 | } |
369 | }; |
370 | |
371 | static int ds278x_battery_probe(struct i2c_client *client) |
372 | { |
373 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
374 | struct ds278x_platform_data *pdata = client->dev.platform_data; |
375 | struct power_supply_config psy_cfg = {}; |
376 | struct ds278x_info *info; |
377 | int ret; |
378 | int num; |
379 | |
380 | /* |
381 | * ds2786 should have the sense resistor value set |
382 | * in the platform data |
383 | */ |
384 | if (id->driver_data == DS2786 && !pdata) { |
385 | dev_err(&client->dev, "missing platform data for ds2786\n" ); |
386 | return -EINVAL; |
387 | } |
388 | |
389 | /* Get an ID for this battery */ |
390 | mutex_lock(&battery_lock); |
391 | ret = idr_alloc(&battery_id, ptr: client, start: 0, end: 0, GFP_KERNEL); |
392 | mutex_unlock(lock: &battery_lock); |
393 | if (ret < 0) |
394 | goto fail_id; |
395 | num = ret; |
396 | |
397 | info = kzalloc(size: sizeof(*info), GFP_KERNEL); |
398 | if (!info) { |
399 | ret = -ENOMEM; |
400 | goto fail_info; |
401 | } |
402 | |
403 | info->battery_desc.name = kasprintf(GFP_KERNEL, fmt: "%s-%d" , |
404 | client->name, num); |
405 | if (!info->battery_desc.name) { |
406 | ret = -ENOMEM; |
407 | goto fail_name; |
408 | } |
409 | |
410 | if (id->driver_data == DS2786) |
411 | info->rsns = pdata->rsns; |
412 | |
413 | i2c_set_clientdata(client, data: info); |
414 | info->client = client; |
415 | info->id = num; |
416 | info->ops = &ds278x_ops[id->driver_data]; |
417 | ds278x_power_supply_init(battery: &info->battery_desc); |
418 | psy_cfg.drv_data = info; |
419 | |
420 | info->capacity = 100; |
421 | info->status = POWER_SUPPLY_STATUS_FULL; |
422 | |
423 | INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work); |
424 | |
425 | info->battery = power_supply_register(parent: &client->dev, |
426 | desc: &info->battery_desc, cfg: &psy_cfg); |
427 | if (IS_ERR(ptr: info->battery)) { |
428 | dev_err(&client->dev, "failed to register battery\n" ); |
429 | ret = PTR_ERR(ptr: info->battery); |
430 | goto fail_register; |
431 | } else { |
432 | schedule_delayed_work(dwork: &info->bat_work, DS278x_DELAY); |
433 | } |
434 | |
435 | return 0; |
436 | |
437 | fail_register: |
438 | kfree(objp: info->battery_desc.name); |
439 | fail_name: |
440 | kfree(objp: info); |
441 | fail_info: |
442 | mutex_lock(&battery_lock); |
443 | idr_remove(&battery_id, id: num); |
444 | mutex_unlock(lock: &battery_lock); |
445 | fail_id: |
446 | return ret; |
447 | } |
448 | |
449 | static const struct i2c_device_id ds278x_id[] = { |
450 | {"ds2782" , DS2782}, |
451 | {"ds2786" , DS2786}, |
452 | {}, |
453 | }; |
454 | MODULE_DEVICE_TABLE(i2c, ds278x_id); |
455 | |
456 | static struct i2c_driver ds278x_battery_driver = { |
457 | .driver = { |
458 | .name = "ds2782-battery" , |
459 | .pm = &ds278x_battery_pm_ops, |
460 | }, |
461 | .probe = ds278x_battery_probe, |
462 | .remove = ds278x_battery_remove, |
463 | .id_table = ds278x_id, |
464 | }; |
465 | module_i2c_driver(ds278x_battery_driver); |
466 | |
467 | MODULE_AUTHOR("Ryan Mallon" ); |
468 | MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC driver" ); |
469 | MODULE_LICENSE("GPL" ); |
470 | |