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 *
107cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov * UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru>
117cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov *
12bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * This program is free software; you can redistribute it and/or modify
13bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * it under the terms of the GNU General Public License version 2 as
14bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * published by the Free Software Foundation.
15bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon *
16bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon */
17bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
18bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/kernel.h>
19bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/module.h>
20bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/types.h>
21bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/errno.h>
22bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/swab.h>
23bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/i2c.h>
247cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov#include <linux/delay.h>
25bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/idr.h>
26bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#include <linux/power_supply.h>
275a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
289b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#include <linux/ds2782_battery.h>
29bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
30bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#define DS2782_REG_RARC		0x06	/* Remaining active relative capacity */
31bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
329b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS278x_REG_VOLT_MSB	0x0c
339b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS278x_REG_TEMP_MSB	0x0a
349b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS278x_REG_CURRENT_MSB	0x0e
35bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
36bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon/* EEPROM Block */
37bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#define DS2782_REG_RSNSP	0x69	/* Sense resistor value */
38bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
39bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
40bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon#define DS2782_CURRENT_UNITS	1563
41bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
429b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS2786_REG_RARC		0x02	/* Remaining active relative capacity */
439b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
449b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define DS2786_CURRENT_UNITS	25
459b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
467cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov#define DS278x_DELAY		1000
477cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
489b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystruct ds278x_info;
499b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
509b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystruct ds278x_battery_ops {
51eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe	int (*get_battery_current)(struct ds278x_info *info, int *current_uA);
52353f867b5536e55e46801562987d605778cf262bRyan Mallon	int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV);
53353f867b5536e55e46801562987d605778cf262bRyan Mallon	int (*get_battery_capacity)(struct ds278x_info *info, int *capacity);
549b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky};
559b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
569b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)
57bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
589b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystruct ds278x_info {
59bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	struct i2c_client	*client;
60bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	struct power_supply	battery;
619b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_battery_ops  *ops;
627cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	struct delayed_work	bat_work;
63bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int			id;
649b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int                     rsns;
657cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	int			capacity;
667cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	int			status;		/* State Of Charge */
67bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
68bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
69bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonstatic DEFINE_IDR(battery_id);
70bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonstatic DEFINE_MUTEX(battery_lock);
71bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
729b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
73bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
74bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
75bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
76bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = i2c_smbus_read_byte_data(info->client, reg);
77bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0) {
78bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "register read failed\n");
79bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return ret;
80bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
81bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
82bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*val = ret;
83bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
84bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
85bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
869b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
87bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				    s16 *val)
88bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
89bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
90bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
918511748ba1ab83a7d89d802a50335115358a8cfcDan Carpenter	ret = i2c_smbus_read_word_data(info->client, reg_msb);
92bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0) {
93bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "register read failed\n");
94bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return ret;
95bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
96bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
978511748ba1ab83a7d89d802a50335115358a8cfcDan Carpenter	*val = swab16(ret);
98bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
99bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
100bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1019b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_get_temp(struct ds278x_info *info, int *temp)
102bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
103bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
104bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
105bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
106bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
107bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * Temperature is measured in units of 0.125 degrees celcius, the
108bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * power_supply class measures temperature in tenths of degrees
109bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * celsius. The temperature value is stored as a 10 bit number, plus
110bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * sign in the upper bits of a 16 bit register.
111bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1129b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
113bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
114bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
115bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*temp = ((raw / 32) * 125) / 100;
116bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
117bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
118bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1199b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2782_get_current(struct ds278x_info *info, int *current_uA)
120bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
121bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int sense_res;
122bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
123bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	u8 sense_res_raw;
124bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
125bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
126bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
127bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * The units of measurement for current are dependent on the value of
128bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * the sense resistor.
129bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1309b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
131bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
132bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
133bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (sense_res_raw == 0) {
134bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "sense resistor value is 0\n");
135bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return -ENXIO;
136bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
137bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	sense_res = 1000 / sense_res_raw;
138bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
139bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n",
140bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		sense_res);
1419b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
142bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
143bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
144bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
145bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
146bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
147bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
148353f867b5536e55e46801562987d605778cf262bRyan Mallonstatic int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV)
149bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
150bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
151bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
152bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
153bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
154bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * Voltage is measured in units of 4.88mV. The voltage is stored as
155bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
156bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1579b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
158bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
159bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
160353f867b5536e55e46801562987d605778cf262bRyan Mallon	*voltage_uV = (raw / 32) * 4800;
161bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
162bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
163bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1649b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
165bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
166bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
167bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	u8 raw;
168bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1699b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
170bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
171bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
172bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*capacity = raw;
1732d31757c87a741823f77daaa07eeb8d56be63943Ryan Mallon	return 0;
174bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
175bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1769b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2786_get_current(struct ds278x_info *info, int *current_uA)
1779b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
1789b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
1799b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	s16 raw;
1809b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1819b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
1829b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
1839b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
1849b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	*current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
1859b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
1869b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
1879b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
188353f867b5536e55e46801562987d605778cf262bRyan Mallonstatic int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV)
1899b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
1909b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	s16 raw;
1919b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
1929b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1939b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/*
1949b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * Voltage is measured in units of 1.22mV. The voltage is stored as
1951b5e1c6e6e728774ba7a1e8286d4833e4a16e918Matthias Brugger	 * a 12-bit number plus sign, in the upper bits of a 16-bit register
1969b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 */
1979b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
1989b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
1999b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
200353f867b5536e55e46801562987d605778cf262bRyan Mallon	*voltage_uV = (raw / 8) * 1220;
2019b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
2029b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
2039b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
2049b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
2059b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
2069b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
2079b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	u8 raw;
2089b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
2099b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
2109b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
2119b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
2129b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/* Relative capacity is displayed with resolution 0.5 % */
2139b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	*capacity = raw/2 ;
2149b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
2159b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
2169b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
2179b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_get_status(struct ds278x_info *info, int *status)
218bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
219bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
220bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int current_uA;
221bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int capacity;
222bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
223eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe	err = info->ops->get_battery_current(info, &current_uA);
224bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
225bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
226bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
227eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe	err = info->ops->get_battery_capacity(info, &capacity);
228bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
229bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
230bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2317cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	info->capacity = capacity;
2327cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
233bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (capacity == 100)
234bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_FULL;
235bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else if (current_uA == 0)
236bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
237bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else if (current_uA < 0)
238bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_DISCHARGING;
239bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else
240bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_CHARGING;
241bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
242bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
243bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
244bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2459b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_get_property(struct power_supply *psy,
246bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				       enum power_supply_property prop,
247bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				       union power_supply_propval *val)
248bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
2499b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info = to_ds278x_info(psy);
250bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
251bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
252bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	switch (prop) {
253bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_STATUS:
2549b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = ds278x_get_status(info, &val->intval);
255bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
256bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
257bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_CAPACITY:
258eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		ret = info->ops->get_battery_capacity(info, &val->intval);
259bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
260bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
261bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
262eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		ret = info->ops->get_battery_voltage(info, &val->intval);
263bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
264bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
265bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_CURRENT_NOW:
266eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		ret = info->ops->get_battery_current(info, &val->intval);
267bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
268bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
269bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_TEMP:
2709b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = ds278x_get_temp(info, &val->intval);
271bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
272bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
273bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	default:
274bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -EINVAL;
275bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
276bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
277bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return ret;
278bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
279bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2807cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanovstatic void ds278x_bat_update(struct ds278x_info *info)
2817cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov{
2827cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	int old_status = info->status;
2837cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	int old_capacity = info->capacity;
2847cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
2857cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	ds278x_get_status(info, &info->status);
2867cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
2877cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	if ((old_status != info->status) || (old_capacity != info->capacity))
2887cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov		power_supply_changed(&info->battery);
2897cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov}
2907cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
2917cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanovstatic void ds278x_bat_work(struct work_struct *work)
2927cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov{
2937cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	struct ds278x_info *info;
2947cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
2957cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	info = container_of(work, struct ds278x_info, bat_work.work);
2967cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	ds278x_bat_update(info);
2977cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
2987cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	schedule_delayed_work(&info->bat_work, DS278x_DELAY);
2997cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov}
3007cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
3019b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic enum power_supply_property ds278x_battery_props[] = {
302bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_STATUS,
303bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_CAPACITY,
304bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_VOLTAGE_NOW,
305bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_CURRENT_NOW,
306bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_TEMP,
307bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
308bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3099b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic void ds278x_power_supply_init(struct power_supply *battery)
310bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
311bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	battery->type			= POWER_SUPPLY_TYPE_BATTERY;
3129b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->properties		= ds278x_battery_props;
3139b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->num_properties		= ARRAY_SIZE(ds278x_battery_props);
3149b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->get_property		= ds278x_battery_get_property;
315bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	battery->external_power_changed	= NULL;
316bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
317bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3189b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_remove(struct i2c_client *client)
319bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
3209b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info = i2c_get_clientdata(client);
321bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
322bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	power_supply_unregister(&info->battery);
323bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info->battery.name);
324bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
325bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
326bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	idr_remove(&battery_id, info->id);
327bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
328bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3297cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	cancel_delayed_work(&info->bat_work);
3307cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
331bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info);
332bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
333bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
334bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
335cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausen#ifdef CONFIG_PM_SLEEP
3367cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
337cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausenstatic int ds278x_suspend(struct device *dev)
3387cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov{
339cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausen	struct i2c_client *client = to_i2c_client(dev);
3407cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	struct ds278x_info *info = i2c_get_clientdata(client);
3417cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
3427cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	cancel_delayed_work(&info->bat_work);
3437cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	return 0;
3447cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov}
3457cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
346cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausenstatic int ds278x_resume(struct device *dev)
3477cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov{
348cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausen	struct i2c_client *client = to_i2c_client(dev);
3497cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	struct ds278x_info *info = i2c_get_clientdata(client);
3507cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
3517cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	schedule_delayed_work(&info->bat_work, DS278x_DELAY);
3527cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	return 0;
3537cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov}
3547cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
355cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausenstatic SIMPLE_DEV_PM_OPS(ds278x_battery_pm_ops, ds278x_suspend, ds278x_resume);
356cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausen#define DS278X_BATTERY_PM_OPS (&ds278x_battery_pm_ops)
3577cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
358cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausen#else
359cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausen#define DS278X_BATTERY_PM_OPS NULL
360cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausen#endif /* CONFIG_PM_SLEEP */
3617cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
362ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsovenum ds278x_num_id {
363ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	DS2782 = 0,
364ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	DS2786,
365ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov};
366ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov
3679b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic struct ds278x_battery_ops ds278x_ops[] = {
368ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	[DS2782] = {
369eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_current  = ds2782_get_current,
370eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_voltage  = ds2782_get_voltage,
371eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_capacity = ds2782_get_capacity,
3729b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	},
373ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	[DS2786] = {
374eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_current  = ds2786_get_current,
375eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_voltage  = ds2786_get_voltage,
376eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe		.get_battery_capacity = ds2786_get_capacity,
3779b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	}
3789b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky};
3799b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
3809b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_probe(struct i2c_client *client,
381bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				const struct i2c_device_id *id)
382bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
3839b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_platform_data *pdata = client->dev.platform_data;
3849b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info;
385bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
386bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int num;
387bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3889b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/*
3899b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * ds2786 should have the sense resistor value set
3909b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * in the platform data
3919b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 */
392ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	if (id->driver_data == DS2786 && !pdata) {
3939b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		dev_err(&client->dev, "missing platform data for ds2786\n");
3949b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return -EINVAL;
3959b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	}
3969b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
397bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/* Get an ID for this battery */
398bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
39905e2cefab4acb5ae9b54266935eeec32cc5269eaTejun Heo	ret = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
400bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
401bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0)
402bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_id;
40305e2cefab4acb5ae9b54266935eeec32cc5269eaTejun Heo	num = ret;
404bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
405bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	info = kzalloc(sizeof(*info), GFP_KERNEL);
406bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (!info) {
407bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -ENOMEM;
408bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_info;
409bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
410bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
4119b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
412bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (!info->battery.name) {
413bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -ENOMEM;
414bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_name;
415bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
416bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
417ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	if (id->driver_data == DS2786)
4189b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		info->rsns = pdata->rsns;
4199b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
420bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	i2c_set_clientdata(client, info);
421bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	info->client = client;
4229b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->id = num;
4239b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->ops  = &ds278x_ops[id->driver_data];
4249b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	ds278x_power_supply_init(&info->battery);
425bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
4267cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	info->capacity = 100;
4277cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	info->status = POWER_SUPPLY_STATUS_FULL;
4287cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
4297cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work);
4307cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov
431bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = power_supply_register(&client->dev, &info->battery);
432bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret) {
433bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&client->dev, "failed to register battery\n");
434bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_register;
4357cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov	} else {
4367cee9aefda78019b6e7c42fcf3600b9fd8aeb8d5Evgeny Romanov		schedule_delayed_work(&info->bat_work, DS278x_DELAY);
437bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
438bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
439bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
440bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
441bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_register:
442bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info->battery.name);
443bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_name:
444bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info);
445bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_info:
446bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
447bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	idr_remove(&battery_id, num);
448bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
449bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_id:
450bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return ret;
451bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
452bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
4539b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic const struct i2c_device_id ds278x_id[] = {
454ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	{"ds2782", DS2782},
455ab6cc8f9b716a3d0a41b42cd81d392183211a7f2Anton Vorontsov	{"ds2786", DS2786},
456bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	{},
457bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
45884ab16f54bbde67c8c2aabc36b034c91044935cbAxel LinMODULE_DEVICE_TABLE(i2c, ds278x_id);
459bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
4609b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic struct i2c_driver ds278x_battery_driver = {
461bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	.driver 	= {
462bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		.name	= "ds2782-battery",
463cc6616f6902fe96287585664cbad082d0c431206Lars-Peter Clausen		.pm	= DS278X_BATTERY_PM_OPS,
464bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	},
4659b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.probe		= ds278x_battery_probe,
4669b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.remove		= ds278x_battery_remove,
4679b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.id_table	= ds278x_id,
468bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
4695ff92e7ab3591299089cfba440acb4d2ba8ab92fAxel Linmodule_i2c_driver(ds278x_battery_driver);
470bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
4711c5454eed85af71df9c01ab923e0c1b841b2e99bRyan MallonMODULE_AUTHOR("Ryan Mallon");
472bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
473bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_LICENSE("GPL");
474