ds2782_battery.c revision 9b9ade6b612e562c4a5bd02ef38cc32e10f3f9ba
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 *
6bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon * Author: Ryan Mallon <ryan@bluewatersys.com>
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 {
469b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int	(*get_current)(struct ds278x_info *info, int *current_uA);
479b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int	(*get_voltage)(struct ds278x_info *info, int *voltage_uA);
489b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int	(*get_capacity)(struct ds278x_info *info, int *capacity_uA);
499b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
509b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky};
519b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
529b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)
53bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
549b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystruct ds278x_info {
55bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	struct i2c_client	*client;
56bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	struct power_supply	battery;
579b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_battery_ops  *ops;
58bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int			id;
599b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int                     rsns;
60bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
61bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
62bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonstatic DEFINE_IDR(battery_id);
63bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonstatic DEFINE_MUTEX(battery_lock);
64bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
659b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
66bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
67bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
68bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
69bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = i2c_smbus_read_byte_data(info->client, reg);
70bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0) {
71bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "register read failed\n");
72bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return ret;
73bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
74bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
75bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*val = ret;
76bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
77bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
78bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
799b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
80bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				    s16 *val)
81bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
82bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
83bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
84bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb));
85bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0) {
86bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "register read failed\n");
87bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return ret;
88bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
89bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
90bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*val = ret;
91bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
92bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
93bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
949b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_get_temp(struct ds278x_info *info, int *temp)
95bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
96bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
97bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
98bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
99bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
100bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * Temperature is measured in units of 0.125 degrees celcius, the
101bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * power_supply class measures temperature in tenths of degrees
102bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * celsius. The temperature value is stored as a 10 bit number, plus
103bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * sign in the upper bits of a 16 bit register.
104bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1059b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
106bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
107bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
108bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*temp = ((raw / 32) * 125) / 100;
109bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
110bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
111bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1129b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2782_get_current(struct ds278x_info *info, int *current_uA)
113bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
114bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int sense_res;
115bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
116bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	u8 sense_res_raw;
117bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
118bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
119bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
120bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * The units of measurement for current are dependent on the value of
121bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * the sense resistor.
122bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1239b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
124bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
125bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
126bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (sense_res_raw == 0) {
127bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&info->client->dev, "sense resistor value is 0\n");
128bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return -ENXIO;
129bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
130bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	sense_res = 1000 / sense_res_raw;
131bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
132bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n",
133bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		sense_res);
1349b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
135bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
136bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
137bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
138bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
139bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
140bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1419b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA)
142bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
143bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	s16 raw;
144bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
145bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
146bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/*
147bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * Voltage is measured in units of 4.88mV. The voltage is stored as
148bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
149bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	 */
1509b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
151bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
152bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
153bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*voltage_uA = (raw / 32) * 4800;
154bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
155bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
156bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1579b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
158bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
159bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
160bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	u8 raw;
161bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1629b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
163bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
164bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
165bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	*capacity = raw;
166bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return raw;
167bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
168bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
1699b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2786_get_current(struct ds278x_info *info, int *current_uA)
1709b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
1719b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
1729b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	s16 raw;
1739b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1749b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
1759b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
1769b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
1779b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	*current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
1789b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
1799b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
1809b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1819b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA)
1829b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
1839b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	s16 raw;
1849b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
1859b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1869b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/*
1879b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * Voltage is measured in units of 1.22mV. The voltage is stored as
1889b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
1899b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 */
1909b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
1919b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
1929b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
1939b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	*voltage_uA = (raw / 8) * 1220;
1949b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
1959b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
1969b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
1979b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
1989b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky{
1999b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	int err;
2009b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	u8 raw;
2019b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
2029b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
2039b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (err)
2049b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return err;
2059b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/* Relative capacity is displayed with resolution 0.5 % */
2069b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	*capacity = raw/2 ;
2079b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return 0;
2089b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky}
2099b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
2109b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_get_status(struct ds278x_info *info, int *status)
211bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
212bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int err;
213bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int current_uA;
214bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int capacity;
215bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2169b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = info->ops->get_current(info, &current_uA);
217bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
218bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
219bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2209b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	err = info->ops->get_capacity(info, &capacity);
221bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (err)
222bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		return err;
223bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
224bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (capacity == 100)
225bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_FULL;
226bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else if (current_uA == 0)
227bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
228bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else if (current_uA < 0)
229bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_DISCHARGING;
230bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	else
231bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		*status = POWER_SUPPLY_STATUS_CHARGING;
232bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
233bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
234bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
235bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2369b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_get_property(struct power_supply *psy,
237bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				       enum power_supply_property prop,
238bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				       union power_supply_propval *val)
239bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
2409b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info = to_ds278x_info(psy);
241bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
242bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
243bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	switch (prop) {
244bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_STATUS:
2459b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = ds278x_get_status(info, &val->intval);
246bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
247bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
248bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_CAPACITY:
2499b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = info->ops->get_capacity(info, &val->intval);
250bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
251bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
252bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
2539b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = info->ops->get_voltage(info, &val->intval);
254bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
255bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
256bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_CURRENT_NOW:
2579b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = info->ops->get_current(info, &val->intval);
258bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
259bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
260bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	case POWER_SUPPLY_PROP_TEMP:
2619b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		ret = ds278x_get_temp(info, &val->intval);
262bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		break;
263bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
264bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	default:
265bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -EINVAL;
266bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
267bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
268bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return ret;
269bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
270bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2719b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic enum power_supply_property ds278x_battery_props[] = {
272bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_STATUS,
273bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_CAPACITY,
274bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_VOLTAGE_NOW,
275bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_CURRENT_NOW,
276bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	POWER_SUPPLY_PROP_TEMP,
277bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
278bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2799b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic void ds278x_power_supply_init(struct power_supply *battery)
280bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
281bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	battery->type			= POWER_SUPPLY_TYPE_BATTERY;
2829b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->properties		= ds278x_battery_props;
2839b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->num_properties		= ARRAY_SIZE(ds278x_battery_props);
2849b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	battery->get_property		= ds278x_battery_get_property;
285bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	battery->external_power_changed	= NULL;
286bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
287bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
2889b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_remove(struct i2c_client *client)
289bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
2909b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info = i2c_get_clientdata(client);
291bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
292bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	power_supply_unregister(&info->battery);
293bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info->battery.name);
294bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
295bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
296bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	idr_remove(&battery_id, info->id);
297bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
298bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
299bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info);
300bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
301bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
302bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3039b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic struct ds278x_battery_ops ds278x_ops[] = {
3049b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	[0] = {
3059b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		.get_current  = ds2782_get_current,
3069b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		.get_voltage  = ds2782_get_voltage,
3079b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		.get_capacity = ds2782_get_capacity,
3089b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	},
3099b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	[1] = {
3109b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		.get_current  = ds2786_get_current,
3119b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		.get_voltage  = ds2786_get_voltage,
3129b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		.get_capacity = ds2786_get_capacity,
3139b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	}
3149b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky};
3159b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
3169b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds278x_battery_probe(struct i2c_client *client,
317bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon				const struct i2c_device_id *id)
318bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
3199b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_platform_data *pdata = client->dev.platform_data;
3209b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	struct ds278x_info *info;
321bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int ret;
322bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	int num;
323bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3249b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	/*
3259b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * ds2786 should have the sense resistor value set
3269b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 * in the platform data
3279b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	 */
3289b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (id->driver_data == 1 && !pdata) {
3299b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		dev_err(&client->dev, "missing platform data for ds2786\n");
3309b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		return -EINVAL;
3319b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	}
3329b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
333bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	/* Get an ID for this battery */
334bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = idr_pre_get(&battery_id, GFP_KERNEL);
335bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret == 0) {
336bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -ENOMEM;
337bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_id;
338bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
339bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
340bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
341bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = idr_get_new(&battery_id, client, &num);
342bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
343bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret < 0)
344bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_id;
345bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
346bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	info = kzalloc(sizeof(*info), GFP_KERNEL);
347bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (!info) {
348bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -ENOMEM;
349bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_info;
350bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
351bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3529b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
353bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (!info->battery.name) {
354bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		ret = -ENOMEM;
355bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_name;
356bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
357bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3589b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	if (id->driver_data == 1)
3599b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky		info->rsns = pdata->rsns;
3609b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky
361bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	i2c_set_clientdata(client, info);
362bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	info->client = client;
3639b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->id = num;
3649b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	info->ops  = &ds278x_ops[id->driver_data];
3659b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	ds278x_power_supply_init(&info->battery);
366bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
367bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	ret = power_supply_register(&client->dev, &info->battery);
368bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	if (ret) {
369bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		dev_err(&client->dev, "failed to register battery\n");
370bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		goto fail_register;
371bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	}
372bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
373bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return 0;
374bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
375bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_register:
376bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info->battery.name);
377bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_name:
378bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	kfree(info);
379bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_info:
380bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_lock(&battery_lock);
381bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	idr_remove(&battery_id, num);
382bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	mutex_unlock(&battery_lock);
383bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallonfail_id:
384bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	return ret;
385bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
386bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3879b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic const struct i2c_device_id ds278x_id[] = {
388bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	{"ds2782", 0},
3899b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	{"ds2786", 1},
390bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	{},
391bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
392bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
3939b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic struct i2c_driver ds278x_battery_driver = {
394bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	.driver 	= {
395bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon		.name	= "ds2782-battery",
396bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon	},
3979b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.probe		= ds278x_battery_probe,
3989b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.remove		= ds278x_battery_remove,
3999b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	.id_table	= ds278x_id,
400bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon};
401bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
4029b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int __init ds278x_init(void)
403bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
4049b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	return i2c_add_driver(&ds278x_battery_driver);
405bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
4069b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskymodule_init(ds278x_init);
407bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
4089b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic void __exit ds278x_exit(void)
409bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{
4109b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky	i2c_del_driver(&ds278x_battery_driver);
411bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}
4129b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskymodule_exit(ds278x_exit);
413bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon
414bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
415bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
416bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_LICENSE("GPL");
417