Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.1-rc8 274 lines 7.6 kB view raw
1/* 2 * Fuel gauge driver for Maxim 17042 / 8966 / 8997 3 * Note that Maxim 8966 and 8997 are mfd and this is its subdevice. 4 * 5 * Copyright (C) 2011 Samsung Electronics 6 * MyungJoo Ham <myungjoo.ham@samsung.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 * This driver is based on max17040_battery.c 23 */ 24 25#include <linux/init.h> 26#include <linux/slab.h> 27#include <linux/i2c.h> 28#include <linux/mod_devicetable.h> 29#include <linux/power_supply.h> 30#include <linux/power/max17042_battery.h> 31 32struct max17042_chip { 33 struct i2c_client *client; 34 struct power_supply battery; 35 struct max17042_platform_data *pdata; 36}; 37 38static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value) 39{ 40 int ret = i2c_smbus_write_word_data(client, reg, value); 41 42 if (ret < 0) 43 dev_err(&client->dev, "%s: err %d\n", __func__, ret); 44 45 return ret; 46} 47 48static int max17042_read_reg(struct i2c_client *client, u8 reg) 49{ 50 int ret = i2c_smbus_read_word_data(client, reg); 51 52 if (ret < 0) 53 dev_err(&client->dev, "%s: err %d\n", __func__, ret); 54 55 return ret; 56} 57 58static void max17042_set_reg(struct i2c_client *client, 59 struct max17042_reg_data *data, int size) 60{ 61 int i; 62 63 for (i = 0; i < size; i++) 64 max17042_write_reg(client, data[i].addr, data[i].data); 65} 66 67static enum power_supply_property max17042_battery_props[] = { 68 POWER_SUPPLY_PROP_PRESENT, 69 POWER_SUPPLY_PROP_CYCLE_COUNT, 70 POWER_SUPPLY_PROP_VOLTAGE_MAX, 71 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 72 POWER_SUPPLY_PROP_VOLTAGE_NOW, 73 POWER_SUPPLY_PROP_VOLTAGE_AVG, 74 POWER_SUPPLY_PROP_CAPACITY, 75 POWER_SUPPLY_PROP_CHARGE_FULL, 76 POWER_SUPPLY_PROP_TEMP, 77 POWER_SUPPLY_PROP_CURRENT_NOW, 78 POWER_SUPPLY_PROP_CURRENT_AVG, 79}; 80 81static int max17042_get_property(struct power_supply *psy, 82 enum power_supply_property psp, 83 union power_supply_propval *val) 84{ 85 struct max17042_chip *chip = container_of(psy, 86 struct max17042_chip, battery); 87 88 switch (psp) { 89 case POWER_SUPPLY_PROP_PRESENT: 90 val->intval = max17042_read_reg(chip->client, 91 MAX17042_STATUS); 92 if (val->intval & MAX17042_STATUS_BattAbsent) 93 val->intval = 0; 94 else 95 val->intval = 1; 96 break; 97 case POWER_SUPPLY_PROP_CYCLE_COUNT: 98 val->intval = max17042_read_reg(chip->client, 99 MAX17042_Cycles); 100 break; 101 case POWER_SUPPLY_PROP_VOLTAGE_MAX: 102 val->intval = max17042_read_reg(chip->client, 103 MAX17042_MinMaxVolt); 104 val->intval >>= 8; 105 val->intval *= 20000; /* Units of LSB = 20mV */ 106 break; 107 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 108 val->intval = max17042_read_reg(chip->client, 109 MAX17042_V_empty); 110 val->intval >>= 7; 111 val->intval *= 10000; /* Units of LSB = 10mV */ 112 break; 113 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 114 val->intval = max17042_read_reg(chip->client, 115 MAX17042_VCELL) * 83; /* 1000 / 12 = 83 */ 116 break; 117 case POWER_SUPPLY_PROP_VOLTAGE_AVG: 118 val->intval = max17042_read_reg(chip->client, 119 MAX17042_AvgVCELL) * 83; 120 break; 121 case POWER_SUPPLY_PROP_CAPACITY: 122 val->intval = max17042_read_reg(chip->client, 123 MAX17042_SOC) / 256; 124 break; 125 case POWER_SUPPLY_PROP_CHARGE_FULL: 126 val->intval = max17042_read_reg(chip->client, 127 MAX17042_RepSOC); 128 if ((val->intval / 256) >= MAX17042_BATTERY_FULL) 129 val->intval = 1; 130 else if (val->intval >= 0) 131 val->intval = 0; 132 break; 133 case POWER_SUPPLY_PROP_TEMP: 134 val->intval = max17042_read_reg(chip->client, 135 MAX17042_TEMP); 136 /* The value is signed. */ 137 if (val->intval & 0x8000) { 138 val->intval = (0x7fff & ~val->intval) + 1; 139 val->intval *= -1; 140 } 141 /* The value is converted into deci-centigrade scale */ 142 /* Units of LSB = 1 / 256 degree Celsius */ 143 val->intval = val->intval * 10 / 256; 144 break; 145 case POWER_SUPPLY_PROP_CURRENT_NOW: 146 if (chip->pdata->enable_current_sense) { 147 val->intval = max17042_read_reg(chip->client, 148 MAX17042_Current); 149 if (val->intval & 0x8000) { 150 /* Negative */ 151 val->intval = ~val->intval & 0x7fff; 152 val->intval++; 153 val->intval *= -1; 154 } 155 val->intval >>= 4; 156 val->intval *= 1000000 * 25 / chip->pdata->r_sns; 157 } else { 158 return -EINVAL; 159 } 160 break; 161 case POWER_SUPPLY_PROP_CURRENT_AVG: 162 if (chip->pdata->enable_current_sense) { 163 val->intval = max17042_read_reg(chip->client, 164 MAX17042_AvgCurrent); 165 if (val->intval & 0x8000) { 166 /* Negative */ 167 val->intval = ~val->intval & 0x7fff; 168 val->intval++; 169 val->intval *= -1; 170 } 171 val->intval *= 1562500 / chip->pdata->r_sns; 172 } else { 173 return -EINVAL; 174 } 175 break; 176 default: 177 return -EINVAL; 178 } 179 return 0; 180} 181 182static int __devinit max17042_probe(struct i2c_client *client, 183 const struct i2c_device_id *id) 184{ 185 struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); 186 struct max17042_chip *chip; 187 int ret; 188 189 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) 190 return -EIO; 191 192 chip = kzalloc(sizeof(*chip), GFP_KERNEL); 193 if (!chip) 194 return -ENOMEM; 195 196 chip->client = client; 197 chip->pdata = client->dev.platform_data; 198 199 i2c_set_clientdata(client, chip); 200 201 chip->battery.name = "max17042_battery"; 202 chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; 203 chip->battery.get_property = max17042_get_property; 204 chip->battery.properties = max17042_battery_props; 205 chip->battery.num_properties = ARRAY_SIZE(max17042_battery_props); 206 207 /* When current is not measured, 208 * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ 209 if (!chip->pdata->enable_current_sense) 210 chip->battery.num_properties -= 2; 211 212 ret = power_supply_register(&client->dev, &chip->battery); 213 if (ret) { 214 dev_err(&client->dev, "failed: power supply register\n"); 215 kfree(chip); 216 return ret; 217 } 218 219 /* Initialize registers according to values from the platform data */ 220 if (chip->pdata->init_data) 221 max17042_set_reg(client, chip->pdata->init_data, 222 chip->pdata->num_init_data); 223 224 if (!chip->pdata->enable_current_sense) { 225 max17042_write_reg(client, MAX17042_CGAIN, 0x0000); 226 max17042_write_reg(client, MAX17042_MiscCFG, 0x0003); 227 max17042_write_reg(client, MAX17042_LearnCFG, 0x0007); 228 } else { 229 if (chip->pdata->r_sns == 0) 230 chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; 231 } 232 233 return 0; 234} 235 236static int __devexit max17042_remove(struct i2c_client *client) 237{ 238 struct max17042_chip *chip = i2c_get_clientdata(client); 239 240 power_supply_unregister(&chip->battery); 241 kfree(chip); 242 return 0; 243} 244 245static const struct i2c_device_id max17042_id[] = { 246 { "max17042", 0 }, 247 { } 248}; 249MODULE_DEVICE_TABLE(i2c, max17042_id); 250 251static struct i2c_driver max17042_i2c_driver = { 252 .driver = { 253 .name = "max17042", 254 }, 255 .probe = max17042_probe, 256 .remove = __devexit_p(max17042_remove), 257 .id_table = max17042_id, 258}; 259 260static int __init max17042_init(void) 261{ 262 return i2c_add_driver(&max17042_i2c_driver); 263} 264module_init(max17042_init); 265 266static void __exit max17042_exit(void) 267{ 268 i2c_del_driver(&max17042_i2c_driver); 269} 270module_exit(max17042_exit); 271 272MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 273MODULE_DESCRIPTION("MAX17042 Fuel Gauge"); 274MODULE_LICENSE("GPL");