ds2782_battery.c revision eb9650d6d989f24f21232a055d8fd45f1a9dcf99
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 { 46eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe int (*get_battery_current)(struct ds278x_info *info, int *current_uA); 47eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uA); 48eb9650d6d989f24f21232a055d8fd45f1a9dcf99Peter Huewe int (*get_battery_capacity)(struct ds278x_info *info, int *capacity_uA); 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 1409b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA) 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; 152bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon *voltage_uA = (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; 165bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon return raw; 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 1809b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA) 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; 1929b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky *voltage_uA = (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, ¤t_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}; 396bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon 3979b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic struct i2c_driver ds278x_battery_driver = { 398bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon .driver = { 399bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon .name = "ds2782-battery", 400bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon }, 4019b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky .probe = ds278x_battery_probe, 4029b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky .remove = ds278x_battery_remove, 4039b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky .id_table = ds278x_id, 404bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon}; 405bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon 4069b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic int __init ds278x_init(void) 407bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{ 4089b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky return i2c_add_driver(&ds278x_battery_driver); 409bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon} 4109b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskymodule_init(ds278x_init); 411bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon 4129b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskystatic void __exit ds278x_exit(void) 413bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon{ 4149b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilensky i2c_del_driver(&ds278x_battery_driver); 415bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon} 4169b9ade6b612e562c4a5bd02ef38cc32e10f3f9baYulia Vilenskymodule_exit(ds278x_exit); 417bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan Mallon 418bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>"); 419bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver"); 420bfdb46ce8494eae30dbaae65c81e684e6db6228bRyan MallonMODULE_LICENSE("GPL"); 421