ds2782_battery.c revision 5ff92e7ab3591299089cfba440acb4d2ba8ab92f
1bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon/*
2bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC
3bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon *
4bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * Copyright (C) 2009 Bluewater Systems Ltd
5bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon *
61c5454eed85af71df9c01ab923e0c1b841b2e99bRyan Mallon * Author: Ryan Mallon
7bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon *
89b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
99b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky *
10bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * This program is free software; you can redistribute it and/or modify
11bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * it under the terms of the GNU General Public License version 2 as
12bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * published by the Free Software Foundation.
13bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon *
14bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon */
15bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
16bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/kernel.h>
17bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/module.h>
18bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/types.h>
19bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/errno.h>
20bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/swab.h>
21bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/i2c.h>
22bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/idr.h>
23bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/power_supply.h>
245a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
259b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#include <linux/ds2782_battery.h>
26bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
27bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#define DS2782_REG_RARC		0x06	/* Remaining active relative capacity */
28bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
299b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS278x_REG_VOLT_MSB	0x0c
309b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS278x_REG_TEMP_MSB	0x0a
319b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS278x_REG_CURRENT_MSB	0x0e
32bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
33bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon/* EEPROM Block */
34bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#define DS2782_REG_RSNSP	0x69	/* Sense resistor value */
35bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
36bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
37bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#define DS2782_CURRENT_UNITS	1563
38bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
399b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS2786_REG_RARC		0x02	/* Remaining active relative capacity */
409b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
419b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS2786_CURRENT_UNITS	25
429b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
439b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystruct ds278x_info;
449b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
459b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystruct ds278x_battery_ops {
46eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe	int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
47353f867b5536e55e46801562987d605778cf262bRyan Mallon	int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV);
48353f867b5536e55e46801562987d605778cf262bRyan Mallon	int (*get_battery_capacity)(struct ds278x_info *info, int *capacity);
499b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky};
509b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
519b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)
52bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
539b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystruct ds278x_info {
54bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	struct i2c_client	*client;
55bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	struct power_supply	battery;
569b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_battery_ops  *ops;
57bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int			id;
589b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int                     rsns;
59bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
60bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
61bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonstatic DEFINE_IDR(battery_id);
62bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonstatic DEFINE_MUTEX(battery_lock);
63bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
649b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
65bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
66bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
67bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
68bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = i2c_smbus_read_byte_data(info->client, reg);
69bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0) {
70bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "register read failed\n");
71bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return ret;
72bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
73bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
74bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*val = ret;
75bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
76bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
77bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
789b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
79bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				    s16 *val)
80bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
81bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
82bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
83bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb));
84bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0) {
85bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "register read failed\n");
86bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return ret;
87bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
88bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
89bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*val = ret;
90bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
91bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
92bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
939b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_get_temp(struct ds278x_info *info, int *temp)
94bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
95bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
96bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
97bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
98bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
99bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * Temperature is measured in units of 0.125 degrees celcius, the
100bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * power_supply class measures temperature in tenths of degrees
101bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * celsius. The temperature value is stored as a 10 bit number, plus
102bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * sign in the upper bits of a 16 bit register.
103bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1049b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
105bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
106bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
107bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*temp = ((raw / 32) * 125) / 100;
108bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
109bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
110bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1119b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2782_get_current(struct ds278x_info *info, int *current_uA)
112bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
113bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int sense_res;
114bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
115bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	u8 sense_res_raw;
116bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
117bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
118bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
119bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * The units of measurement for current are dependent on the value of
120bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * the sense resistor.
121bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1229b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
123bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
124bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
125bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (sense_res_raw == 0) {
126bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "sense resistor value is 0\n");
127bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return -ENXIO;
128bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
129bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	sense_res = 1000 / sense_res_raw;
130bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
131bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n",
132bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		sense_res);
1339b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
134bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
135bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
136bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
137bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
138bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
139bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
140353f867b5536e55e46801562987d605778cf262bRyan Mallonstatic int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV)
141bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
142bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
143bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
144bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
145bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
146bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * Voltage is measured in units of 4.88mV. The voltage is stored as
147bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
148bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1499b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
150bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
151bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
152353f867b5536e55e46801562987d605778cf262bRyan Mallon	*voltage_uV = (raw / 32) * 4800;
153bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
154bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
155bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1569b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
157bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
158bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
159bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	u8 raw;
160bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1619b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
162bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
163bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
164bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*capacity = raw;
1652d31757c87a741823f77daaa07eeb8d56be63943Ryan Mallon	return 0;
166bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
167bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1689b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2786_get_current(struct ds278x_info *info, int *current_uA)
1699b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
1709b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
1719b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	s16 raw;
1729b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1739b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
1749b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
1759b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
1769b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	*current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
1779b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
1789b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
1799b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
180353f867b5536e55e46801562987d605778cf262bRyan Mallonstatic int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV)
1819b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
1829b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	s16 raw;
1839b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
1849b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1859b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/*
1869b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * Voltage is measured in units of 1.22mV. The voltage is stored as
1879b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
1889b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 */
1899b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
1909b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
1919b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
192353f867b5536e55e46801562987d605778cf262bRyan Mallon	*voltage_uV = (raw / 8) * 1220;
1939b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
1949b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
1959b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1969b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
1979b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
1989b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
1999b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	u8 raw;
2009b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
2019b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
2029b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
2039b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
2049b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/* Relative capacity is displayed with resolution 0.5 % */
2059b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	*capacity = raw/2 ;
2069b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
2079b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
2089b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
2099b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_get_status(struct ds278x_info *info, int *status)
210bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
211bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
212bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int current_uA;
213bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int capacity;
214bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
215eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe	err = info->ops->get_battery_current(info, &current_uA);
216bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
217bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
218bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
219eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe	err = info->ops->get_battery_capacity(info, &capacity);
220bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
221bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
222bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
223bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (capacity == 100)
224bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_FULL;
225bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else if (current_uA == 0)
226bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
227bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else if (current_uA < 0)
228bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_DISCHARGING;
229bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else
230bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_CHARGING;
231bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
232bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
233bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
234bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2359b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_get_property(struct power_supply *psy,
236bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				       enum power_supply_property prop,
237bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				       union power_supply_propval *val)
238bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
2399b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info = to_ds278x_info(psy);
240bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
241bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
242bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	switch (prop) {
243bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_STATUS:
2449b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = ds278x_get_status(info, &val->intval);
245bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
246bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
247bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_CAPACITY:
248eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		ret = info->ops->get_battery_capacity(info, &val->intval);
249bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
250bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
251bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
252eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		ret = info->ops->get_battery_voltage(info, &val->intval);
253bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
254bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
255bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_CURRENT_NOW:
256eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		ret = info->ops->get_battery_current(info, &val->intval);
257bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
258bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
259bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_TEMP:
2609b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = ds278x_get_temp(info, &val->intval);
261bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
262bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
263bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	default:
264bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -EINVAL;
265bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
266bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
267bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return ret;
268bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
269bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2709b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic enum power_supply_property ds278x_battery_props[] = {
271bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_STATUS,
272bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_CAPACITY,
273bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_VOLTAGE_NOW,
274bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_CURRENT_NOW,
275bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_TEMP,
276bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
277bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2789b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic void ds278x_power_supply_init(struct power_supply *battery)
279bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
280bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	battery->type			= POWER_SUPPLY_TYPE_BATTERY;
2819b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->properties		= ds278x_battery_props;
2829b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->num_properties		= ARRAY_SIZE(ds278x_battery_props);
2839b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->get_property		= ds278x_battery_get_property;
284bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	battery->external_power_changed	= NULL;
285bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
286bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2879b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_remove(struct i2c_client *client)
288bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
2899b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info = i2c_get_clientdata(client);
290bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
291bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	power_supply_unregister(&info->battery);
292bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info->battery.name);
293bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
294bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
295bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	idr_remove(&battery_id, info->id);
296bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
297bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
298bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info);
299bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
300bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
301bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
302ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsovenum ds278x_num_id {
303ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	DS2782 = 0,
304ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	DS2786,
305ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov};
306ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov
3079b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic struct ds278x_battery_ops ds278x_ops[] = {
308ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	[DS2782] = {
309eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_current  = ds2782_get_current,
310eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_voltage  = ds2782_get_voltage,
311eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_capacity = ds2782_get_capacity,
3129b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	},
313ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	[DS2786] = {
314eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_current  = ds2786_get_current,
315eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_voltage  = ds2786_get_voltage,
316eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_capacity = ds2786_get_capacity,
3179b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	}
3189b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky};
3199b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
3209b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_probe(struct i2c_client *client,
321bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				const struct i2c_device_id *id)
322bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
3239b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_platform_data *pdata = client->dev.platform_data;
3249b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info;
325bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
326bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int num;
327bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3289b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/*
3299b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * ds2786 should have the sense resistor value set
3309b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * in the platform data
3319b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 */
332ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	if (id->driver_data == DS2786 && !pdata) {
3339b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		dev_err(&client->dev, "missing platform data for ds2786\n");
3349b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return -EINVAL;
3359b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	}
3369b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
337bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/* Get an ID for this battery */
338bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = idr_pre_get(&battery_id, GFP_KERNEL);
339bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret == 0) {
340bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -ENOMEM;
341bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_id;
342bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
343bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
344bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
345bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = idr_get_new(&battery_id, client, &num);
346bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
347bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0)
348bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_id;
349bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
350bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	info = kzalloc(sizeof(*info), GFP_KERNEL);
351bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (!info) {
352bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -ENOMEM;
353bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_info;
354bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
355bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3569b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
357bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (!info->battery.name) {
358bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -ENOMEM;
359bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_name;
360bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
361bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
362ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	if (id->driver_data == DS2786)
3639b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		info->rsns = pdata->rsns;
3649b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
365bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	i2c_set_clientdata(client, info);
366bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	info->client = client;
3679b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->id = num;
3689b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->ops  = &ds278x_ops[id->driver_data];
3699b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	ds278x_power_supply_init(&info->battery);
370bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
371bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = power_supply_register(&client->dev, &info->battery);
372bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret) {
373bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&client->dev, "failed to register battery\n");
374bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_register;
375bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
376bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
377bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
378bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
379bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_register:
380bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info->battery.name);
381bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_name:
382bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info);
383bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_info:
384bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
385bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	idr_remove(&battery_id, num);
386bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
387bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_id:
388bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return ret;
389bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
390bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3919b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic const struct i2c_device_id ds278x_id[] = {
392ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	{"ds2782", DS2782},
393ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	{"ds2786", DS2786},
394bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	{},
395bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
39684ab16f54bbde67c8c2aabc36b034c91044935cbAxel LinMODULE_DEVICE_TABLE(i2c, ds278x_id);
397bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3989b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic struct i2c_driver ds278x_battery_driver = {
399bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	.driver 	= {
400bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		.name	= "ds2782-battery",
401bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	},
4029b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.probe		= ds278x_battery_probe,
4039b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.remove		= ds278x_battery_remove,
4049b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.id_table	= ds278x_id,
405bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
4065ff92e7ab3591299089cfba440acb4d2ba8ab92fAxel Linmodule_i2c_driver(ds278x_battery_driver);
407bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
4081c5454eed85af71df9c01ab923e0c1b841b2e99bRyan MallonMODULE_AUTHOR("Ryan Mallon");
409bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
410bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_LICENSE("GPL");
411