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, ¤t_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