diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/power | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'drivers/power')
| -rw-r--r-- | drivers/power/bq20z75.c | 810 | ||||
| -rw-r--r-- | drivers/power/max17048_battery.c | 579 | ||||
| -rw-r--r-- | drivers/power/max8907c-charger.c | 228 | ||||
| -rw-r--r-- | drivers/power/smb349-charger.c | 588 | ||||
| -rw-r--r-- | drivers/power/tegra_bpc_mgmt.c | 139 | ||||
| -rw-r--r-- | drivers/power/tps80031-charger.c | 476 | ||||
| -rw-r--r-- | drivers/power/tps80031_battery_gauge.c | 606 |
7 files changed, 3426 insertions, 0 deletions
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c new file mode 100644 index 00000000000..32e5a8c5c3d --- /dev/null +++ b/drivers/power/bq20z75.c | |||
| @@ -0,0 +1,810 @@ | |||
| 1 | /* | ||
| 2 | * Gas Gauge driver for TI's BQ20Z75 | ||
| 3 | * | ||
| 4 | * Copyright (c) 2010, NVIDIA Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along | ||
| 17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/err.h> | ||
| 25 | #include <linux/power_supply.h> | ||
| 26 | #include <linux/i2c.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/interrupt.h> | ||
| 29 | #include <linux/gpio.h> | ||
| 30 | |||
| 31 | #include <linux/power/bq20z75.h> | ||
| 32 | |||
| 33 | enum { | ||
| 34 | REG_MANUFACTURER_DATA, | ||
| 35 | REG_TEMPERATURE, | ||
| 36 | REG_VOLTAGE, | ||
| 37 | REG_CURRENT, | ||
| 38 | REG_CAPACITY, | ||
| 39 | REG_TIME_TO_EMPTY, | ||
| 40 | REG_TIME_TO_FULL, | ||
| 41 | REG_STATUS, | ||
| 42 | REG_CYCLE_COUNT, | ||
| 43 | REG_SERIAL_NUMBER, | ||
| 44 | REG_REMAINING_CAPACITY, | ||
| 45 | REG_REMAINING_CAPACITY_CHARGE, | ||
| 46 | REG_FULL_CHARGE_CAPACITY, | ||
| 47 | REG_FULL_CHARGE_CAPACITY_CHARGE, | ||
| 48 | REG_DESIGN_CAPACITY, | ||
| 49 | REG_DESIGN_CAPACITY_CHARGE, | ||
| 50 | REG_DESIGN_VOLTAGE, | ||
| 51 | }; | ||
| 52 | |||
| 53 | /* Battery Mode defines */ | ||
| 54 | #define BATTERY_MODE_OFFSET 0x03 | ||
| 55 | #define BATTERY_MODE_MASK 0x8000 | ||
| 56 | enum bq20z75_battery_mode { | ||
| 57 | BATTERY_MODE_AMPS, | ||
| 58 | BATTERY_MODE_WATTS | ||
| 59 | }; | ||
| 60 | |||
| 61 | /* manufacturer access defines */ | ||
| 62 | #define MANUFACTURER_ACCESS_STATUS 0x0006 | ||
| 63 | #define MANUFACTURER_ACCESS_SLEEP 0x0011 | ||
| 64 | |||
| 65 | /* battery status value bits */ | ||
| 66 | #define BATTERY_DISCHARGING 0x40 | ||
| 67 | #define BATTERY_FULL_CHARGED 0x20 | ||
| 68 | #define BATTERY_FULL_DISCHARGED 0x10 | ||
| 69 | |||
| 70 | #define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \ | ||
| 71 | .psp = _psp, \ | ||
| 72 | .addr = _addr, \ | ||
| 73 | .min_value = _min_value, \ | ||
| 74 | .max_value = _max_value, \ | ||
| 75 | } | ||
| 76 | |||
| 77 | static const struct bq20z75_device_data { | ||
| 78 | enum power_supply_property psp; | ||
| 79 | u8 addr; | ||
| 80 | int min_value; | ||
| 81 | int max_value; | ||
| 82 | } bq20z75_data[] = { | ||
| 83 | [REG_MANUFACTURER_DATA] = | ||
| 84 | BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), | ||
| 85 | [REG_TEMPERATURE] = | ||
| 86 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), | ||
| 87 | [REG_VOLTAGE] = | ||
| 88 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), | ||
| 89 | [REG_CURRENT] = | ||
| 90 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, | ||
| 91 | 32767), | ||
| 92 | [REG_CAPACITY] = | ||
| 93 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), | ||
| 94 | [REG_REMAINING_CAPACITY] = | ||
| 95 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), | ||
| 96 | [REG_REMAINING_CAPACITY_CHARGE] = | ||
| 97 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535), | ||
| 98 | [REG_FULL_CHARGE_CAPACITY] = | ||
| 99 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), | ||
| 100 | [REG_FULL_CHARGE_CAPACITY_CHARGE] = | ||
| 101 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535), | ||
| 102 | [REG_TIME_TO_EMPTY] = | ||
| 103 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, | ||
| 104 | 65535), | ||
| 105 | [REG_TIME_TO_FULL] = | ||
| 106 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, | ||
| 107 | 65535), | ||
| 108 | [REG_STATUS] = | ||
| 109 | BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), | ||
| 110 | [REG_CYCLE_COUNT] = | ||
| 111 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), | ||
| 112 | [REG_DESIGN_CAPACITY] = | ||
| 113 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, | ||
| 114 | 65535), | ||
| 115 | [REG_DESIGN_CAPACITY_CHARGE] = | ||
| 116 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, | ||
| 117 | 65535), | ||
| 118 | [REG_DESIGN_VOLTAGE] = | ||
| 119 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, | ||
| 120 | 65535), | ||
| 121 | [REG_SERIAL_NUMBER] = | ||
| 122 | BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), | ||
| 123 | }; | ||
| 124 | |||
| 125 | static enum power_supply_property bq20z75_properties[] = { | ||
| 126 | POWER_SUPPLY_PROP_STATUS, | ||
| 127 | POWER_SUPPLY_PROP_HEALTH, | ||
| 128 | POWER_SUPPLY_PROP_PRESENT, | ||
| 129 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
| 130 | POWER_SUPPLY_PROP_CYCLE_COUNT, | ||
| 131 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
| 132 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
| 133 | POWER_SUPPLY_PROP_CAPACITY, | ||
| 134 | POWER_SUPPLY_PROP_TEMP, | ||
| 135 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, | ||
| 136 | POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, | ||
| 137 | POWER_SUPPLY_PROP_SERIAL_NUMBER, | ||
| 138 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | ||
| 139 | POWER_SUPPLY_PROP_ENERGY_NOW, | ||
| 140 | POWER_SUPPLY_PROP_ENERGY_FULL, | ||
| 141 | POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, | ||
| 142 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
| 143 | POWER_SUPPLY_PROP_CHARGE_FULL, | ||
| 144 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||
| 145 | }; | ||
| 146 | |||
| 147 | struct bq20z75_info { | ||
| 148 | struct i2c_client *client; | ||
| 149 | struct power_supply power_supply; | ||
| 150 | struct bq20z75_platform_data plat_data; | ||
| 151 | bool is_present; | ||
| 152 | bool gpio_detect; | ||
| 153 | bool enable_detection; | ||
| 154 | int irq; | ||
| 155 | int last_state; | ||
| 156 | int poll_time; | ||
| 157 | struct delayed_work work; | ||
| 158 | int ignore_changes; | ||
| 159 | }; | ||
| 160 | |||
| 161 | static int bq20z75_read_word_data(struct i2c_client *client, u8 address) | ||
| 162 | { | ||
| 163 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
| 164 | s32 ret = 0; | ||
| 165 | int retries = 1; | ||
| 166 | |||
| 167 | retries = max(bq20z75_device->plat_data.i2c_retry_count + 1, 1); | ||
| 168 | |||
| 169 | while (retries > 0) { | ||
| 170 | ret = i2c_smbus_read_word_data(client, address); | ||
| 171 | if (ret >= 0) | ||
| 172 | break; | ||
| 173 | retries--; | ||
| 174 | } | ||
| 175 | |||
| 176 | if (ret < 0) { | ||
| 177 | dev_dbg(&client->dev, | ||
| 178 | "%s: i2c read at address 0x%x failed\n", | ||
| 179 | __func__, address); | ||
| 180 | return ret; | ||
| 181 | } | ||
| 182 | |||
| 183 | return le16_to_cpu(ret); | ||
| 184 | } | ||
| 185 | |||
| 186 | static int bq20z75_write_word_data(struct i2c_client *client, u8 address, | ||
| 187 | u16 value) | ||
| 188 | { | ||
| 189 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
| 190 | s32 ret = 0; | ||
| 191 | int retries = 1; | ||
| 192 | |||
| 193 | retries = max(bq20z75_device->plat_data.i2c_retry_count + 1, 1); | ||
| 194 | |||
| 195 | while (retries > 0) { | ||
| 196 | ret = i2c_smbus_write_word_data(client, address, | ||
| 197 | le16_to_cpu(value)); | ||
| 198 | if (ret >= 0) | ||
| 199 | break; | ||
| 200 | retries--; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (ret < 0) { | ||
| 204 | dev_dbg(&client->dev, | ||
| 205 | "%s: i2c write to address 0x%x failed\n", | ||
| 206 | __func__, address); | ||
| 207 | return ret; | ||
| 208 | } | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | static int bq20z75_get_battery_presence_and_health( | ||
| 214 | struct i2c_client *client, enum power_supply_property psp, | ||
| 215 | union power_supply_propval *val) | ||
| 216 | { | ||
| 217 | s32 ret; | ||
| 218 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
| 219 | |||
| 220 | if (psp == POWER_SUPPLY_PROP_PRESENT && | ||
| 221 | bq20z75_device->gpio_detect) { | ||
| 222 | ret = gpio_get_value( | ||
| 223 | bq20z75_device->plat_data.battery_detect); | ||
| 224 | if (ret == bq20z75_device->plat_data.battery_detect_present) | ||
| 225 | val->intval = 1; | ||
| 226 | else | ||
| 227 | val->intval = 0; | ||
| 228 | bq20z75_device->is_present = val->intval; | ||
| 229 | return ret; | ||
| 230 | } | ||
| 231 | |||
| 232 | /* Write to ManufacturerAccess with | ||
| 233 | * ManufacturerAccess command and then | ||
| 234 | * read the status */ | ||
| 235 | ret = bq20z75_write_word_data(client, | ||
| 236 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | ||
| 237 | MANUFACTURER_ACCESS_STATUS); | ||
| 238 | if (ret < 0) { | ||
| 239 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
| 240 | val->intval = 0; /* battery removed */ | ||
| 241 | return ret; | ||
| 242 | } | ||
| 243 | |||
| 244 | ret = bq20z75_read_word_data(client, | ||
| 245 | bq20z75_data[REG_MANUFACTURER_DATA].addr); | ||
| 246 | if (ret < 0) | ||
| 247 | return ret; | ||
| 248 | |||
| 249 | if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || | ||
| 250 | ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { | ||
| 251 | val->intval = 0; | ||
| 252 | return 0; | ||
| 253 | } | ||
| 254 | |||
| 255 | /* Mask the upper nibble of 2nd byte and | ||
| 256 | * lower byte of response then | ||
| 257 | * shift the result by 8 to get status*/ | ||
| 258 | ret &= 0x0F00; | ||
| 259 | ret >>= 8; | ||
| 260 | if (psp == POWER_SUPPLY_PROP_PRESENT) { | ||
| 261 | if (ret == 0x0F) | ||
| 262 | /* battery removed */ | ||
| 263 | val->intval = 0; | ||
| 264 | else | ||
| 265 | val->intval = 1; | ||
| 266 | } else if (psp == POWER_SUPPLY_PROP_HEALTH) { | ||
| 267 | if (ret == 0x09) | ||
| 268 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
| 269 | else if (ret == 0x0B) | ||
| 270 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
| 271 | else if (ret == 0x0C) | ||
| 272 | val->intval = POWER_SUPPLY_HEALTH_DEAD; | ||
| 273 | else | ||
| 274 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
| 275 | } | ||
| 276 | |||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | |||
| 280 | static int bq20z75_get_battery_property(struct i2c_client *client, | ||
| 281 | int reg_offset, enum power_supply_property psp, | ||
| 282 | union power_supply_propval *val) | ||
| 283 | { | ||
| 284 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
| 285 | s32 ret; | ||
| 286 | |||
| 287 | ret = bq20z75_read_word_data(client, | ||
| 288 | bq20z75_data[reg_offset].addr); | ||
| 289 | if (ret < 0) | ||
| 290 | return ret; | ||
| 291 | |||
| 292 | /* returned values are 16 bit */ | ||
| 293 | if (bq20z75_data[reg_offset].min_value < 0) | ||
| 294 | ret = (s16)ret; | ||
| 295 | |||
| 296 | if (ret >= bq20z75_data[reg_offset].min_value && | ||
| 297 | ret <= bq20z75_data[reg_offset].max_value) { | ||
| 298 | val->intval = ret; | ||
| 299 | if (psp != POWER_SUPPLY_PROP_STATUS) | ||
| 300 | return 0; | ||
| 301 | |||
| 302 | if (ret & BATTERY_FULL_CHARGED) | ||
| 303 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
| 304 | else if (ret & BATTERY_FULL_DISCHARGED) | ||
| 305 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
| 306 | else if (ret & BATTERY_DISCHARGING) | ||
| 307 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
| 308 | else | ||
| 309 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
| 310 | |||
| 311 | if (bq20z75_device->poll_time == 0) | ||
| 312 | bq20z75_device->last_state = val->intval; | ||
| 313 | else if (bq20z75_device->last_state != val->intval) { | ||
| 314 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
| 315 | power_supply_changed(&bq20z75_device->power_supply); | ||
| 316 | bq20z75_device->poll_time = 0; | ||
| 317 | } | ||
| 318 | } else { | ||
| 319 | if (psp == POWER_SUPPLY_PROP_STATUS) | ||
| 320 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; | ||
| 321 | else | ||
| 322 | val->intval = 0; | ||
| 323 | } | ||
| 324 | |||
| 325 | return 0; | ||
| 326 | } | ||
| 327 | |||
| 328 | static void bq20z75_unit_adjustment(struct i2c_client *client, | ||
| 329 | enum power_supply_property psp, union power_supply_propval *val) | ||
| 330 | { | ||
| 331 | #define BASE_UNIT_CONVERSION 1000 | ||
| 332 | #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) | ||
| 333 | #define TIME_UNIT_CONVERSION 60 | ||
| 334 | #define TEMP_KELVIN_TO_CELSIUS 2731 | ||
| 335 | switch (psp) { | ||
| 336 | case POWER_SUPPLY_PROP_ENERGY_NOW: | ||
| 337 | case POWER_SUPPLY_PROP_ENERGY_FULL: | ||
| 338 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | ||
| 339 | /* bq20z75 provides energy in units of 10mWh. | ||
| 340 | * Convert to µWh | ||
| 341 | */ | ||
| 342 | val->intval *= BATTERY_MODE_CAP_MULT_WATT; | ||
| 343 | break; | ||
| 344 | |||
| 345 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
| 346 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
| 347 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
| 348 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
| 349 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
| 350 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
| 351 | val->intval *= BASE_UNIT_CONVERSION; | ||
| 352 | break; | ||
| 353 | |||
| 354 | case POWER_SUPPLY_PROP_TEMP: | ||
| 355 | /* bq20z75 provides battery temperature in 0.1K | ||
| 356 | * so convert it to 0.1°C | ||
| 357 | */ | ||
| 358 | val->intval -= TEMP_KELVIN_TO_CELSIUS; | ||
| 359 | break; | ||
| 360 | |||
| 361 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | ||
| 362 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | ||
| 363 | /* bq20z75 provides time to empty and time to full in minutes. | ||
| 364 | * Convert to seconds | ||
| 365 | */ | ||
| 366 | val->intval *= TIME_UNIT_CONVERSION; | ||
| 367 | break; | ||
| 368 | |||
| 369 | default: | ||
| 370 | dev_dbg(&client->dev, | ||
| 371 | "%s: no need for unit conversion %d\n", __func__, psp); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | static enum bq20z75_battery_mode | ||
| 376 | bq20z75_set_battery_mode(struct i2c_client *client, | ||
| 377 | enum bq20z75_battery_mode mode) | ||
| 378 | { | ||
| 379 | int ret, original_val; | ||
| 380 | |||
| 381 | original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET); | ||
| 382 | if (original_val < 0) | ||
| 383 | return original_val; | ||
| 384 | |||
| 385 | if ((original_val & BATTERY_MODE_MASK) == mode) | ||
| 386 | return mode; | ||
| 387 | |||
| 388 | if (mode == BATTERY_MODE_AMPS) | ||
| 389 | ret = original_val & ~BATTERY_MODE_MASK; | ||
| 390 | else | ||
| 391 | ret = original_val | BATTERY_MODE_MASK; | ||
| 392 | |||
| 393 | ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret); | ||
| 394 | if (ret < 0) | ||
| 395 | return ret; | ||
| 396 | |||
| 397 | return original_val & BATTERY_MODE_MASK; | ||
| 398 | } | ||
| 399 | |||
| 400 | static int bq20z75_get_battery_capacity(struct i2c_client *client, | ||
| 401 | int reg_offset, enum power_supply_property psp, | ||
| 402 | union power_supply_propval *val) | ||
| 403 | { | ||
| 404 | s32 ret; | ||
| 405 | enum bq20z75_battery_mode mode = BATTERY_MODE_WATTS; | ||
| 406 | |||
| 407 | if (power_supply_is_amp_property(psp)) | ||
| 408 | mode = BATTERY_MODE_AMPS; | ||
| 409 | |||
| 410 | mode = bq20z75_set_battery_mode(client, mode); | ||
| 411 | if (mode < 0) | ||
| 412 | return mode; | ||
| 413 | |||
| 414 | ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); | ||
| 415 | if (ret < 0) | ||
| 416 | return ret; | ||
| 417 | |||
| 418 | if (psp == POWER_SUPPLY_PROP_CAPACITY) { | ||
| 419 | /* bq20z75 spec says that this can be >100 % | ||
| 420 | * even if max value is 100 % */ | ||
| 421 | val->intval = min(ret, 100); | ||
| 422 | } else | ||
| 423 | val->intval = ret; | ||
| 424 | |||
| 425 | ret = bq20z75_set_battery_mode(client, mode); | ||
| 426 | if (ret < 0) | ||
| 427 | return ret; | ||
| 428 | |||
| 429 | return 0; | ||
| 430 | } | ||
| 431 | |||
| 432 | static char bq20z75_serial[5]; | ||
| 433 | static int bq20z75_get_battery_serial_number(struct i2c_client *client, | ||
| 434 | union power_supply_propval *val) | ||
| 435 | { | ||
| 436 | int ret; | ||
| 437 | |||
| 438 | ret = bq20z75_read_word_data(client, | ||
| 439 | bq20z75_data[REG_SERIAL_NUMBER].addr); | ||
| 440 | if (ret < 0) | ||
| 441 | return ret; | ||
| 442 | |||
| 443 | ret = sprintf(bq20z75_serial, "%04x", ret); | ||
| 444 | val->strval = bq20z75_serial; | ||
| 445 | |||
| 446 | return 0; | ||
| 447 | } | ||
| 448 | |||
| 449 | static int bq20z75_get_property_index(struct i2c_client *client, | ||
| 450 | enum power_supply_property psp) | ||
| 451 | { | ||
| 452 | int count; | ||
| 453 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) | ||
| 454 | if (psp == bq20z75_data[count].psp) | ||
| 455 | return count; | ||
| 456 | |||
| 457 | dev_warn(&client->dev, | ||
| 458 | "%s: Invalid Property - %d\n", __func__, psp); | ||
| 459 | |||
| 460 | return -EINVAL; | ||
| 461 | } | ||
| 462 | |||
| 463 | static int bq20z75_get_property(struct power_supply *psy, | ||
| 464 | enum power_supply_property psp, | ||
| 465 | union power_supply_propval *val) | ||
| 466 | { | ||
| 467 | int ret = 0; | ||
| 468 | struct bq20z75_info *bq20z75_device = container_of(psy, | ||
| 469 | struct bq20z75_info, power_supply); | ||
| 470 | struct i2c_client *client = bq20z75_device->client; | ||
| 471 | |||
| 472 | switch (psp) { | ||
| 473 | case POWER_SUPPLY_PROP_PRESENT: | ||
| 474 | case POWER_SUPPLY_PROP_HEALTH: | ||
| 475 | ret = bq20z75_get_battery_presence_and_health(client, psp, val); | ||
| 476 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
| 477 | return 0; | ||
| 478 | break; | ||
| 479 | |||
| 480 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
| 481 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | ||
| 482 | break; | ||
| 483 | |||
| 484 | case POWER_SUPPLY_PROP_ENERGY_NOW: | ||
| 485 | case POWER_SUPPLY_PROP_ENERGY_FULL: | ||
| 486 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | ||
| 487 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
| 488 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
| 489 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
| 490 | case POWER_SUPPLY_PROP_CAPACITY: | ||
| 491 | ret = bq20z75_get_property_index(client, psp); | ||
| 492 | if (ret < 0) | ||
| 493 | break; | ||
| 494 | |||
| 495 | ret = bq20z75_get_battery_capacity(client, ret, psp, val); | ||
| 496 | break; | ||
| 497 | |||
| 498 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | ||
| 499 | ret = bq20z75_get_battery_serial_number(client, val); | ||
| 500 | break; | ||
| 501 | |||
| 502 | case POWER_SUPPLY_PROP_STATUS: | ||
| 503 | case POWER_SUPPLY_PROP_CYCLE_COUNT: | ||
| 504 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
| 505 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
| 506 | case POWER_SUPPLY_PROP_TEMP: | ||
| 507 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | ||
| 508 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | ||
| 509 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
| 510 | ret = bq20z75_get_property_index(client, psp); | ||
| 511 | if (ret < 0) | ||
| 512 | break; | ||
| 513 | |||
| 514 | ret = bq20z75_get_battery_property(client, ret, psp, val); | ||
| 515 | break; | ||
| 516 | |||
| 517 | default: | ||
| 518 | dev_err(&client->dev, | ||
| 519 | "%s: INVALID property\n", __func__); | ||
| 520 | return -EINVAL; | ||
| 521 | } | ||
| 522 | |||
| 523 | if (!bq20z75_device->enable_detection) | ||
| 524 | goto done; | ||
| 525 | |||
| 526 | if (!bq20z75_device->gpio_detect && | ||
| 527 | bq20z75_device->is_present != (ret >= 0)) { | ||
| 528 | bq20z75_device->is_present = (ret >= 0); | ||
| 529 | power_supply_changed(&bq20z75_device->power_supply); | ||
| 530 | } | ||
| 531 | |||
| 532 | done: | ||
| 533 | if (!ret) { | ||
| 534 | /* Convert units to match requirements for power supply class */ | ||
| 535 | bq20z75_unit_adjustment(client, psp, val); | ||
| 536 | } | ||
| 537 | |||
| 538 | dev_dbg(&client->dev, | ||
| 539 | "%s: property = %d, value = %x\n", __func__, psp, val->intval); | ||
| 540 | |||
| 541 | if (ret && bq20z75_device->is_present) | ||
| 542 | return ret; | ||
| 543 | |||
| 544 | /* battery not present, so return NODATA for properties */ | ||
| 545 | if (ret) | ||
| 546 | return -ENODATA; | ||
| 547 | |||
| 548 | return 0; | ||
| 549 | } | ||
| 550 | |||
| 551 | static irqreturn_t bq20z75_irq(int irq, void *devid) | ||
| 552 | { | ||
| 553 | struct power_supply *battery = devid; | ||
| 554 | |||
| 555 | power_supply_changed(battery); | ||
| 556 | |||
| 557 | return IRQ_HANDLED; | ||
| 558 | } | ||
| 559 | |||
| 560 | static void bq20z75_external_power_changed(struct power_supply *psy) | ||
| 561 | { | ||
| 562 | struct bq20z75_info *bq20z75_device; | ||
| 563 | |||
| 564 | bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); | ||
| 565 | |||
| 566 | if (bq20z75_device->ignore_changes > 0) { | ||
| 567 | bq20z75_device->ignore_changes--; | ||
| 568 | return; | ||
| 569 | } | ||
| 570 | |||
| 571 | /* cancel outstanding work */ | ||
| 572 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
| 573 | |||
| 574 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
| 575 | bq20z75_device->poll_time = bq20z75_device->plat_data.poll_retry_count; | ||
| 576 | } | ||
| 577 | |||
| 578 | static void bq20z75_delayed_work(struct work_struct *work) | ||
| 579 | { | ||
| 580 | struct bq20z75_info *bq20z75_device; | ||
| 581 | s32 ret; | ||
| 582 | |||
| 583 | bq20z75_device = container_of(work, struct bq20z75_info, work.work); | ||
| 584 | |||
| 585 | ret = bq20z75_read_word_data(bq20z75_device->client, | ||
| 586 | bq20z75_data[REG_STATUS].addr); | ||
| 587 | /* if the read failed, give up on this work */ | ||
| 588 | if (ret < 0) { | ||
| 589 | bq20z75_device->poll_time = 0; | ||
| 590 | return; | ||
| 591 | } | ||
| 592 | |||
| 593 | if (ret & BATTERY_FULL_CHARGED) | ||
| 594 | ret = POWER_SUPPLY_STATUS_FULL; | ||
| 595 | else if (ret & BATTERY_FULL_DISCHARGED) | ||
| 596 | ret = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
| 597 | else if (ret & BATTERY_DISCHARGING) | ||
| 598 | ret = POWER_SUPPLY_STATUS_DISCHARGING; | ||
| 599 | else | ||
| 600 | ret = POWER_SUPPLY_STATUS_CHARGING; | ||
| 601 | |||
| 602 | if (bq20z75_device->last_state != ret) { | ||
| 603 | bq20z75_device->poll_time = 0; | ||
| 604 | power_supply_changed(&bq20z75_device->power_supply); | ||
| 605 | return; | ||
| 606 | } | ||
| 607 | if (bq20z75_device->poll_time > 0) { | ||
| 608 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
| 609 | bq20z75_device->poll_time--; | ||
| 610 | return; | ||
| 611 | } | ||
| 612 | } | ||
| 613 | |||
| 614 | static int __devinit bq20z75_probe(struct i2c_client *client, | ||
| 615 | const struct i2c_device_id *id) | ||
| 616 | { | ||
| 617 | struct bq20z75_info *bq20z75_device; | ||
| 618 | struct bq20z75_platform_data *pdata = client->dev.platform_data; | ||
| 619 | int rc; | ||
| 620 | int irq; | ||
| 621 | |||
| 622 | bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); | ||
| 623 | if (!bq20z75_device) | ||
| 624 | return -ENOMEM; | ||
| 625 | |||
| 626 | bq20z75_device->client = client; | ||
| 627 | bq20z75_device->enable_detection = false; | ||
| 628 | bq20z75_device->gpio_detect = false; | ||
| 629 | bq20z75_device->power_supply.name = "battery"; | ||
| 630 | bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; | ||
| 631 | bq20z75_device->power_supply.properties = bq20z75_properties; | ||
| 632 | bq20z75_device->power_supply.num_properties = | ||
| 633 | ARRAY_SIZE(bq20z75_properties); | ||
| 634 | bq20z75_device->power_supply.get_property = bq20z75_get_property; | ||
| 635 | /* ignore first notification of external change, it is generated | ||
| 636 | * from the power_supply_register call back | ||
| 637 | */ | ||
| 638 | bq20z75_device->ignore_changes = 1; | ||
| 639 | bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; | ||
| 640 | bq20z75_device->power_supply.external_power_changed = | ||
| 641 | bq20z75_external_power_changed; | ||
| 642 | |||
| 643 | if (pdata) { | ||
| 644 | bq20z75_device->gpio_detect = | ||
| 645 | gpio_is_valid(pdata->battery_detect); | ||
| 646 | memcpy(&bq20z75_device->plat_data, pdata, sizeof(struct bq20z75_platform_data)); | ||
| 647 | } | ||
| 648 | |||
| 649 | i2c_set_clientdata(client, bq20z75_device); | ||
| 650 | |||
| 651 | /* Probing for the presence of the bq20z75 */ | ||
| 652 | rc = bq20z75_read_word_data(client, | ||
| 653 | bq20z75_data[REG_SERIAL_NUMBER].addr); | ||
| 654 | |||
| 655 | if (rc < 0) { | ||
| 656 | dev_err(&client->dev, | ||
| 657 | "%s: bq20z75 is not responding\n", __func__); | ||
| 658 | rc = -ENODEV; | ||
| 659 | goto exit_mem_free; | ||
| 660 | } | ||
| 661 | |||
| 662 | if (!bq20z75_device->gpio_detect) | ||
| 663 | goto skip_gpio; | ||
| 664 | |||
| 665 | rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); | ||
| 666 | if (rc) { | ||
| 667 | dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); | ||
| 668 | bq20z75_device->gpio_detect = false; | ||
| 669 | goto skip_gpio; | ||
| 670 | } | ||
| 671 | |||
| 672 | rc = gpio_direction_input(pdata->battery_detect); | ||
| 673 | if (rc) { | ||
| 674 | dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); | ||
| 675 | gpio_free(pdata->battery_detect); | ||
| 676 | bq20z75_device->gpio_detect = false; | ||
| 677 | goto skip_gpio; | ||
| 678 | } | ||
| 679 | |||
| 680 | irq = gpio_to_irq(pdata->battery_detect); | ||
| 681 | if (irq <= 0) { | ||
| 682 | dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); | ||
| 683 | gpio_free(pdata->battery_detect); | ||
| 684 | bq20z75_device->gpio_detect = false; | ||
| 685 | goto skip_gpio; | ||
| 686 | } | ||
| 687 | |||
| 688 | rc = request_irq(irq, bq20z75_irq, | ||
| 689 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
| 690 | dev_name(&client->dev), &bq20z75_device->power_supply); | ||
| 691 | if (rc) { | ||
| 692 | dev_warn(&client->dev, "Failed to request irq: %d\n", rc); | ||
| 693 | gpio_free(pdata->battery_detect); | ||
| 694 | bq20z75_device->gpio_detect = false; | ||
| 695 | goto skip_gpio; | ||
| 696 | } | ||
| 697 | |||
| 698 | bq20z75_device->irq = irq; | ||
| 699 | |||
| 700 | skip_gpio: | ||
| 701 | rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); | ||
| 702 | if (rc) { | ||
| 703 | dev_err(&client->dev, | ||
| 704 | "%s: Failed to register power supply\n", __func__); | ||
| 705 | goto exit_psupply; | ||
| 706 | } | ||
| 707 | |||
| 708 | dev_info(&client->dev, | ||
| 709 | "%s: battery gas gauge device registered\n", client->name); | ||
| 710 | |||
| 711 | INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); | ||
| 712 | |||
| 713 | bq20z75_device->enable_detection = true; | ||
| 714 | |||
| 715 | return 0; | ||
| 716 | |||
| 717 | exit_psupply: | ||
| 718 | if (bq20z75_device->irq) | ||
| 719 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
| 720 | if (bq20z75_device->gpio_detect) | ||
| 721 | gpio_free(pdata->battery_detect); | ||
| 722 | |||
| 723 | exit_mem_free: | ||
| 724 | kfree(bq20z75_device); | ||
| 725 | |||
| 726 | return rc; | ||
| 727 | } | ||
| 728 | |||
| 729 | static int __devexit bq20z75_remove(struct i2c_client *client) | ||
| 730 | { | ||
| 731 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
| 732 | |||
| 733 | if (bq20z75_device->irq) | ||
| 734 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
| 735 | if (bq20z75_device->gpio_detect) | ||
| 736 | gpio_free(bq20z75_device->plat_data.battery_detect); | ||
| 737 | |||
| 738 | power_supply_unregister(&bq20z75_device->power_supply); | ||
| 739 | |||
| 740 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
| 741 | |||
| 742 | kfree(bq20z75_device); | ||
| 743 | bq20z75_device = NULL; | ||
| 744 | |||
| 745 | return 0; | ||
| 746 | } | ||
| 747 | |||
| 748 | #if defined CONFIG_PM | ||
| 749 | static int bq20z75_suspend(struct i2c_client *client, | ||
| 750 | pm_message_t state) | ||
| 751 | { | ||
| 752 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
| 753 | s32 ret; | ||
| 754 | |||
| 755 | if (bq20z75_device->poll_time > 0) | ||
| 756 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
| 757 | |||
| 758 | /* write to manufacturer access with sleep command */ | ||
| 759 | ret = bq20z75_write_word_data(client, | ||
| 760 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | ||
| 761 | MANUFACTURER_ACCESS_SLEEP); | ||
| 762 | if (bq20z75_device->is_present && ret < 0) | ||
| 763 | return ret; | ||
| 764 | |||
| 765 | return 0; | ||
| 766 | } | ||
| 767 | |||
| 768 | static int bq20z75_resume(struct i2c_client *client) | ||
| 769 | { | ||
| 770 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
| 771 | |||
| 772 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
| 773 | return 0; | ||
| 774 | } | ||
| 775 | #else | ||
| 776 | #define bq20z75_suspend NULL | ||
| 777 | #define bq20z75_resume NULL | ||
| 778 | #endif | ||
| 779 | |||
| 780 | static const struct i2c_device_id bq20z75_id[] = { | ||
| 781 | { "bq20z75", 0 }, | ||
| 782 | {} | ||
| 783 | }; | ||
| 784 | MODULE_DEVICE_TABLE(i2c, bq20z75_id); | ||
| 785 | |||
| 786 | static struct i2c_driver bq20z75_battery_driver = { | ||
| 787 | .probe = bq20z75_probe, | ||
| 788 | .remove = __devexit_p(bq20z75_remove), | ||
| 789 | .suspend = bq20z75_suspend, | ||
| 790 | .resume = bq20z75_resume, | ||
| 791 | .id_table = bq20z75_id, | ||
| 792 | .driver = { | ||
| 793 | .name = "bq20z75-battery", | ||
| 794 | }, | ||
| 795 | }; | ||
| 796 | |||
| 797 | static int __init bq20z75_battery_init(void) | ||
| 798 | { | ||
| 799 | return i2c_add_driver(&bq20z75_battery_driver); | ||
| 800 | } | ||
| 801 | module_init(bq20z75_battery_init); | ||
| 802 | |||
| 803 | static void __exit bq20z75_battery_exit(void) | ||
| 804 | { | ||
| 805 | i2c_del_driver(&bq20z75_battery_driver); | ||
| 806 | } | ||
| 807 | module_exit(bq20z75_battery_exit); | ||
| 808 | |||
| 809 | MODULE_DESCRIPTION("BQ20z75 battery monitor driver"); | ||
| 810 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/max17048_battery.c b/drivers/power/max17048_battery.c new file mode 100644 index 00000000000..44275e329c5 --- /dev/null +++ b/drivers/power/max17048_battery.c | |||
| @@ -0,0 +1,579 @@ | |||
| 1 | /* | ||
| 2 | * max17048_battery.c | ||
| 3 | * fuel-gauge systems for lithium-ion (Li+) batteries | ||
| 4 | * | ||
| 5 | * Copyright (C) 2012 Nvidia Cooperation | ||
| 6 | * Chandler Zhang <chazhang@nvidia.com> | ||
| 7 | * Syed Rafiuddin <srafiuddin@nvidia.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <asm/unaligned.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/mutex.h> | ||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/i2c.h> | ||
| 21 | #include <linux/delay.h> | ||
| 22 | #include <linux/power_supply.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/max17048_battery.h> | ||
| 25 | |||
| 26 | #define MAX17048_VCELL 0x02 | ||
| 27 | #define MAX17048_SOC 0x04 | ||
| 28 | #define MAX17048_VER 0x08 | ||
| 29 | #define MAX17048_HIBRT 0x0A | ||
| 30 | #define MAX17048_CONFIG 0x0C | ||
| 31 | #define MAX17048_OCV 0x0E | ||
| 32 | #define MAX17048_VLRT 0x14 | ||
| 33 | #define MAX17048_VRESET 0x18 | ||
| 34 | #define MAX17048_STATUS 0x1A | ||
| 35 | #define MAX17048_UNLOCK 0x3E | ||
| 36 | #define MAX17048_TABLE 0x40 | ||
| 37 | #define MAX17048_RCOMPSEG1 0x80 | ||
| 38 | #define MAX17048_RCOMPSEG2 0x90 | ||
| 39 | #define MAX17048_CMD 0xFF | ||
| 40 | #define MAX17048_UNLOCK_VALUE 0x4a57 | ||
| 41 | #define MAX17048_RESET_VALUE 0x5400 | ||
| 42 | #define MAX17048_DELAY 1000 | ||
| 43 | #define MAX17048_BATTERY_FULL 95 | ||
| 44 | #define MAX17048_VERSION_NO 0x11 | ||
| 45 | |||
| 46 | struct max17048_chip { | ||
| 47 | struct i2c_client *client; | ||
| 48 | struct delayed_work work; | ||
| 49 | struct power_supply battery; | ||
| 50 | struct power_supply ac; | ||
| 51 | struct power_supply usb; | ||
| 52 | struct max17048_battery_model *model_data; | ||
| 53 | |||
| 54 | /* State Of Connect */ | ||
| 55 | int ac_online; | ||
| 56 | int usb_online; | ||
| 57 | /* battery voltage */ | ||
| 58 | int vcell; | ||
| 59 | /* battery capacity */ | ||
| 60 | int soc; | ||
| 61 | /* State Of Charge */ | ||
| 62 | int status; | ||
| 63 | |||
| 64 | int lasttime_vcell; | ||
| 65 | int lasttime_soc; | ||
| 66 | int lasttime_status; | ||
| 67 | }; | ||
| 68 | |||
| 69 | uint8_t max17048_custom_data[] = { | ||
| 70 | 0xAA, 0x00, 0xB1, 0xF0, 0xB7, 0xE0, 0xB9, 0x60, 0xBB, 0x80, | ||
| 71 | 0xBC, 0x40, 0xBD, 0x30, 0xBD, 0x50, 0xBD, 0xF0, 0xBE, 0x40, | ||
| 72 | 0xBF, 0xD0, 0xC0, 0x90, 0xC4, 0x30, 0xC7, 0xC0, 0xCA, 0x60, | ||
| 73 | 0xCF, 0x30, 0x01, 0x20, 0x09, 0xC0, 0x1F, 0xC0, 0x2B, 0xE0, | ||
| 74 | 0x4F, 0xC0, 0x30, 0x00, 0x47, 0x80, 0x4F, 0xE0, 0x77, 0x00, | ||
| 75 | 0x15, 0x60, 0x46, 0x20, 0x13, 0x80, 0x1A, 0x60, 0x12, 0x20, | ||
| 76 | 0x14, 0xA0, 0x14, 0xA0}; | ||
| 77 | |||
| 78 | static int max17048_write_word(struct i2c_client *client, int reg, u16 value) | ||
| 79 | { | ||
| 80 | int ret; | ||
| 81 | |||
| 82 | ret = i2c_smbus_write_word_data(client, reg, swab16(value)); | ||
| 83 | |||
| 84 | if (ret < 0) | ||
| 85 | dev_err(&client->dev, "%s(): Failed in writing register" | ||
| 86 | "0x%02x err %d\n", __func__, reg, ret); | ||
| 87 | |||
| 88 | return ret; | ||
| 89 | } | ||
| 90 | |||
| 91 | static uint16_t max17048_read_word(struct i2c_client *client, int reg) | ||
| 92 | { | ||
| 93 | uint16_t ret; | ||
| 94 | |||
| 95 | ret = i2c_smbus_read_word_data(client, reg); | ||
| 96 | |||
| 97 | if (ret < 0) | ||
| 98 | dev_err(&client->dev, "%s(): Failed in reading register" | ||
| 99 | "0x%02x err %d\n", __func__, reg, ret); | ||
| 100 | |||
| 101 | return swab16(ret); | ||
| 102 | } | ||
| 103 | |||
| 104 | static int max17048_get_property(struct power_supply *psy, | ||
| 105 | enum power_supply_property psp, | ||
| 106 | union power_supply_propval *val) | ||
| 107 | { | ||
| 108 | struct max17048_chip *chip = container_of(psy, | ||
| 109 | struct max17048_chip, battery); | ||
| 110 | |||
| 111 | switch (psp) { | ||
| 112 | case POWER_SUPPLY_PROP_STATUS: | ||
| 113 | val->intval = chip->status; | ||
| 114 | break; | ||
| 115 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
| 116 | val->intval = chip->vcell; | ||
| 117 | break; | ||
| 118 | case POWER_SUPPLY_PROP_CAPACITY: | ||
| 119 | val->intval = chip->soc; | ||
| 120 | break; | ||
| 121 | default: | ||
| 122 | return -EINVAL; | ||
| 123 | } | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | static int max17048_ac_get_property(struct power_supply *psy, | ||
| 128 | enum power_supply_property psp, | ||
| 129 | union power_supply_propval *val) | ||
| 130 | { | ||
| 131 | struct max17048_chip *chip = container_of(psy, | ||
| 132 | struct max17048_chip, ac); | ||
| 133 | |||
| 134 | if (psp == POWER_SUPPLY_PROP_ONLINE) | ||
| 135 | val->intval = chip->ac_online; | ||
| 136 | else | ||
| 137 | return -EINVAL; | ||
| 138 | |||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | static int max17048_usb_get_property(struct power_supply *psy, | ||
| 143 | enum power_supply_property psp, | ||
| 144 | union power_supply_propval *val) | ||
| 145 | { | ||
| 146 | struct max17048_chip *chip = container_of(psy, | ||
| 147 | struct max17048_chip, usb); | ||
| 148 | |||
| 149 | if (psp == POWER_SUPPLY_PROP_ONLINE) | ||
| 150 | val->intval = chip->usb_online; | ||
| 151 | else | ||
| 152 | return -EINVAL; | ||
| 153 | |||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | |||
| 157 | static void max17048_get_vcell(struct i2c_client *client) | ||
| 158 | { | ||
| 159 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
| 160 | uint16_t vcell; | ||
| 161 | |||
| 162 | vcell = max17048_read_word(client, MAX17048_VCELL); | ||
| 163 | if (vcell < 0) | ||
| 164 | dev_err(&client->dev, "%s: err %d\n", __func__, vcell); | ||
| 165 | |||
| 166 | chip->vcell = vcell; | ||
| 167 | } | ||
| 168 | |||
| 169 | static void max17048_get_soc(struct i2c_client *client) | ||
| 170 | { | ||
| 171 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
| 172 | uint16_t soc; | ||
| 173 | |||
| 174 | soc = max17048_read_word(client, MAX17048_SOC); | ||
| 175 | if (soc < 0) | ||
| 176 | dev_err(&client->dev, "%s: err %d\n", __func__, soc); | ||
| 177 | |||
| 178 | chip->soc = soc >> 9; | ||
| 179 | } | ||
| 180 | |||
| 181 | static uint16_t max17048_get_version(struct i2c_client *client) | ||
| 182 | { | ||
| 183 | return swab16(i2c_smbus_read_word_data(client, MAX17048_VER)); | ||
| 184 | } | ||
| 185 | |||
| 186 | static void max17048_work(struct work_struct *work) | ||
| 187 | { | ||
| 188 | struct max17048_chip *chip; | ||
| 189 | |||
| 190 | chip = container_of(work, struct max17048_chip, work.work); | ||
| 191 | |||
| 192 | max17048_get_vcell(chip->client); | ||
| 193 | max17048_get_soc(chip->client); | ||
| 194 | |||
| 195 | if (chip->vcell != chip->lasttime_vcell || | ||
| 196 | chip->soc != chip->lasttime_soc || | ||
| 197 | chip->status != chip->lasttime_status) { | ||
| 198 | |||
| 199 | chip->lasttime_vcell = chip->vcell; | ||
| 200 | chip->lasttime_soc = chip->soc; | ||
| 201 | |||
| 202 | power_supply_changed(&chip->battery); | ||
| 203 | } | ||
| 204 | schedule_delayed_work(&chip->work, MAX17048_DELAY); | ||
| 205 | } | ||
| 206 | |||
| 207 | static void max17048_battery_status(enum charging_states status, | ||
| 208 | enum charger_type chrg_type, void *data) | ||
| 209 | { | ||
| 210 | struct max17048_chip *chip = data; | ||
| 211 | |||
| 212 | chip->ac_online = 0; | ||
| 213 | chip->usb_online = 0; | ||
| 214 | |||
| 215 | if (chrg_type == AC) | ||
| 216 | chip->ac_online = 1; | ||
| 217 | else if (chrg_type == USB) | ||
| 218 | chip->usb_online = 1; | ||
| 219 | |||
| 220 | if (status == progress) | ||
| 221 | chip->status = POWER_SUPPLY_STATUS_CHARGING; | ||
| 222 | else | ||
| 223 | chip->status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
| 224 | |||
| 225 | if (chip->soc > MAX17048_BATTERY_FULL) | ||
| 226 | chip->status = POWER_SUPPLY_STATUS_FULL; | ||
| 227 | |||
| 228 | power_supply_changed(&chip->battery); | ||
| 229 | power_supply_changed(&chip->usb); | ||
| 230 | power_supply_changed(&chip->ac); | ||
| 231 | } | ||
| 232 | |||
| 233 | static enum power_supply_property max17048_battery_props[] = { | ||
| 234 | POWER_SUPPLY_PROP_STATUS, | ||
| 235 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
| 236 | POWER_SUPPLY_PROP_CAPACITY, | ||
| 237 | }; | ||
| 238 | |||
| 239 | static enum power_supply_property max17048_ac_props[] = { | ||
| 240 | POWER_SUPPLY_PROP_ONLINE, | ||
| 241 | }; | ||
| 242 | |||
| 243 | static enum power_supply_property max17048_usb_props[] = { | ||
| 244 | POWER_SUPPLY_PROP_ONLINE, | ||
| 245 | }; | ||
| 246 | |||
| 247 | static int max17048_write_rcomp_seg(struct i2c_client *client, | ||
| 248 | uint16_t rcomp_seg) | ||
| 249 | { | ||
| 250 | uint8_t rs1, rs2; | ||
| 251 | int ret; | ||
| 252 | |||
| 253 | rs2 = rcomp_seg | 0x00FF; | ||
| 254 | rs1 = rcomp_seg >> 8; | ||
| 255 | uint8_t rcomp_seg_table[16] = { rs1, rs2, rs1, rs2, | ||
| 256 | rs1, rs2, rs1, rs2, | ||
| 257 | rs1, rs2, rs1, rs2, | ||
| 258 | rs1, rs2, rs1, rs2}; | ||
| 259 | |||
| 260 | ret = i2c_smbus_write_i2c_block_data(client, MAX17048_RCOMPSEG1, | ||
| 261 | 16, (uint8_t *)rcomp_seg_table); | ||
| 262 | if (ret < 0) { | ||
| 263 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 264 | return ret; | ||
| 265 | } | ||
| 266 | |||
| 267 | ret = i2c_smbus_write_i2c_block_data(client, MAX17048_RCOMPSEG2, | ||
| 268 | 16, (uint8_t *)rcomp_seg_table); | ||
| 269 | if (ret < 0) { | ||
| 270 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 271 | return ret; | ||
| 272 | } | ||
| 273 | |||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | |||
| 277 | static int max17048_load_model_data(struct max17048_chip *chip) | ||
| 278 | { | ||
| 279 | struct i2c_client *client = chip->client; | ||
| 280 | struct max17048_battery_model *mdata = chip->model_data; | ||
| 281 | uint16_t soc_tst, ocv; | ||
| 282 | int i, ret = 0; | ||
| 283 | |||
| 284 | /* read OCV */ | ||
| 285 | ocv = max17048_read_word(client, MAX17048_OCV); | ||
| 286 | if (ocv == 0xffff) { | ||
| 287 | dev_err(&client->dev, "%s: Failed in unlocking" | ||
| 288 | "max17048 err: %d\n", __func__, ocv); | ||
| 289 | return -1; | ||
| 290 | } | ||
| 291 | |||
| 292 | /* write custom model data */ | ||
| 293 | for (i = 0; i < 4; i += 1) { | ||
| 294 | if (i2c_smbus_write_i2c_block_data(client, | ||
| 295 | (MAX17048_TABLE+i*16), 16, | ||
| 296 | &max17048_custom_data[i*0x10]) < 0) { | ||
| 297 | dev_err(&client->dev, "%s: error writing model data:\n", | ||
| 298 | __func__); | ||
| 299 | return -1; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | /* Write OCV Test value */ | ||
| 304 | ret = max17048_write_word(client, MAX17048_OCV, mdata->ocvtest); | ||
| 305 | if (ret < 0) | ||
| 306 | return ret; | ||
| 307 | |||
| 308 | ret = max17048_write_rcomp_seg(client, mdata->rcomp_seg); | ||
| 309 | if (ret < 0) | ||
| 310 | return ret; | ||
| 311 | |||
| 312 | /* Disable hibernate */ | ||
| 313 | ret = max17048_write_word(client, MAX17048_HIBRT, 0x0000); | ||
| 314 | if (ret < 0) | ||
| 315 | return ret; | ||
| 316 | |||
| 317 | /* Lock model access */ | ||
| 318 | ret = max17048_write_word(client, MAX17048_UNLOCK, 0x0000); | ||
| 319 | if (ret < 0) | ||
| 320 | return ret; | ||
| 321 | |||
| 322 | /* Delay between 150ms to 600ms */ | ||
| 323 | mdelay(200); | ||
| 324 | |||
| 325 | /* Read SOC Register and compare to expected result */ | ||
| 326 | soc_tst = max17048_read_word(client, MAX17048_SOC); | ||
| 327 | if (!((soc_tst >> 8) >= mdata->soccheck_A && | ||
| 328 | (soc_tst >> 8) <= mdata->soccheck_B)) { | ||
| 329 | dev_err(&client->dev, "%s: soc comparison failed %d\n", | ||
| 330 | __func__, ret); | ||
| 331 | return ret; | ||
| 332 | } else { | ||
| 333 | dev_info(&client->dev, "MAX17048 Custom data" | ||
| 334 | " loading successfull\n"); | ||
| 335 | } | ||
| 336 | |||
| 337 | /* unlock model access */ | ||
| 338 | ret = max17048_write_word(client, MAX17048_UNLOCK, | ||
| 339 | MAX17048_UNLOCK_VALUE); | ||
| 340 | if (ret < 0) | ||
| 341 | return ret; | ||
| 342 | |||
| 343 | /* Restore OCV */ | ||
| 344 | ret = max17048_write_word(client, MAX17048_OCV, ocv); | ||
| 345 | if (ret < 0) | ||
| 346 | return ret; | ||
| 347 | |||
| 348 | return ret; | ||
| 349 | } | ||
| 350 | |||
| 351 | static int max17048_initialize(struct max17048_chip *chip) | ||
| 352 | { | ||
| 353 | uint8_t ret, config, status; | ||
| 354 | uint16_t wrt_status; | ||
| 355 | struct i2c_client *client = chip->client; | ||
| 356 | struct max17048_battery_model *mdata = chip->model_data; | ||
| 357 | |||
| 358 | /* unlock model access */ | ||
| 359 | ret = max17048_write_word(client, MAX17048_UNLOCK, | ||
| 360 | MAX17048_UNLOCK_VALUE); | ||
| 361 | if (ret < 0) | ||
| 362 | return ret; | ||
| 363 | |||
| 364 | /* load model data */ | ||
| 365 | ret = max17048_load_model_data(chip); | ||
| 366 | if (ret < 0) { | ||
| 367 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 368 | return ret; | ||
| 369 | } | ||
| 370 | |||
| 371 | if (mdata->bits == 19) | ||
| 372 | config = 32 - (mdata->alert_threshold * 2); | ||
| 373 | else if (mdata->bits == 18) | ||
| 374 | config = 32 - mdata->alert_threshold; | ||
| 375 | |||
| 376 | config = mdata->one_percent_alerts | config; | ||
| 377 | |||
| 378 | ret = max17048_write_word(client, MAX17048_CONFIG, | ||
| 379 | ((mdata->rcomp << 8) | config)); | ||
| 380 | if (ret < 0) | ||
| 381 | return ret; | ||
| 382 | |||
| 383 | /* Voltage Alert configuration */ | ||
| 384 | ret = max17048_write_word(client, MAX17048_VLRT, mdata->valert); | ||
| 385 | if (ret < 0) | ||
| 386 | return ret; | ||
| 387 | |||
| 388 | /* Hibernate configuration */ | ||
| 389 | ret = max17048_write_word(client, MAX17048_HIBRT, mdata->hibernate); | ||
| 390 | if (ret < 0) | ||
| 391 | return ret; | ||
| 392 | |||
| 393 | ret = max17048_write_word(client, MAX17048_VRESET, mdata->vreset); | ||
| 394 | if (ret < 0) | ||
| 395 | return ret; | ||
| 396 | |||
| 397 | /* clears the reset indicator */ | ||
| 398 | ret = max17048_read_word(client, MAX17048_STATUS); | ||
| 399 | if (ret < 0) | ||
| 400 | return ret; | ||
| 401 | |||
| 402 | /* Sets the EnVR bit if selected */ | ||
| 403 | status = (ret & 0xFE) | mdata->alert_on_reset; | ||
| 404 | wrt_status = status << 8; | ||
| 405 | |||
| 406 | ret = max17048_write_word(client, MAX17048_STATUS, wrt_status); | ||
| 407 | if (ret < 0) | ||
| 408 | return ret; | ||
| 409 | |||
| 410 | /* Lock model access */ | ||
| 411 | ret = max17048_write_word(client, MAX17048_UNLOCK, 0x0000); | ||
| 412 | if (ret < 0) | ||
| 413 | return ret; | ||
| 414 | |||
| 415 | /* Add delay */ | ||
| 416 | mdelay(200); | ||
| 417 | return 0; | ||
| 418 | } | ||
| 419 | |||
| 420 | static int __devinit max17048_probe(struct i2c_client *client, | ||
| 421 | const struct i2c_device_id *id) | ||
| 422 | { | ||
| 423 | struct max17048_chip *chip; | ||
| 424 | int ret; | ||
| 425 | uint16_t version; | ||
| 426 | |||
| 427 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | ||
| 428 | if (!chip) | ||
| 429 | return -ENOMEM; | ||
| 430 | |||
| 431 | chip->client = client; | ||
| 432 | chip->model_data = client->dev.platform_data; | ||
| 433 | chip->ac_online = 0; | ||
| 434 | chip->usb_online = 0; | ||
| 435 | |||
| 436 | i2c_set_clientdata(client, chip); | ||
| 437 | |||
| 438 | version = max17048_get_version(client); | ||
| 439 | if (version != MAX17048_VERSION_NO) { | ||
| 440 | ret = -ENODEV; | ||
| 441 | goto error2; | ||
| 442 | } | ||
| 443 | dev_info(&client->dev, "MAX17048 Fuel-Gauge Ver 0x%x\n", version); | ||
| 444 | |||
| 445 | ret = max17048_initialize(chip); | ||
| 446 | if (ret < 0) { | ||
| 447 | dev_err(&client->dev, "Error: Initializing fuel-gauge\n"); | ||
| 448 | goto error2; | ||
| 449 | } | ||
| 450 | |||
| 451 | ret = register_callback(max17048_battery_status, chip); | ||
| 452 | if (ret < 0) | ||
| 453 | goto error2; | ||
| 454 | |||
| 455 | chip->battery.name = "battery"; | ||
| 456 | chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; | ||
| 457 | chip->battery.get_property = max17048_get_property; | ||
| 458 | chip->battery.properties = max17048_battery_props; | ||
| 459 | chip->battery.num_properties = ARRAY_SIZE(max17048_battery_props); | ||
| 460 | |||
| 461 | ret = power_supply_register(&client->dev, &chip->battery); | ||
| 462 | if (ret) { | ||
| 463 | dev_err(&client->dev, "failed: power supply register\n"); | ||
| 464 | goto error2; | ||
| 465 | } | ||
| 466 | |||
| 467 | chip->ac.name = "maxim-ac"; | ||
| 468 | chip->ac.type = POWER_SUPPLY_TYPE_MAINS; | ||
| 469 | chip->ac.get_property = max17048_ac_get_property; | ||
| 470 | chip->ac.properties = max17048_ac_props; | ||
| 471 | chip->ac.num_properties = ARRAY_SIZE(max17048_ac_props); | ||
| 472 | |||
| 473 | ret = power_supply_register(&client->dev, &chip->ac); | ||
| 474 | if (ret) { | ||
| 475 | dev_err(&client->dev, "failed: power supply register\n"); | ||
| 476 | goto error1; | ||
| 477 | } | ||
| 478 | |||
| 479 | chip->usb.name = "maxim-usb"; | ||
| 480 | chip->usb.type = POWER_SUPPLY_TYPE_USB; | ||
| 481 | chip->usb.get_property = max17048_usb_get_property; | ||
| 482 | chip->usb.properties = max17048_usb_props; | ||
| 483 | chip->usb.num_properties = ARRAY_SIZE(max17048_usb_props); | ||
| 484 | |||
| 485 | ret = power_supply_register(&client->dev, &chip->usb); | ||
| 486 | if (ret) { | ||
| 487 | dev_err(&client->dev, "failed: power supply register\n"); | ||
| 488 | goto error; | ||
| 489 | } | ||
| 490 | |||
| 491 | INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17048_work); | ||
| 492 | schedule_delayed_work(&chip->work, MAX17048_DELAY); | ||
| 493 | |||
| 494 | ret = update_charger_status(); | ||
| 495 | if (ret) { | ||
| 496 | dev_err(&client->dev, "failed: update_charger_status\n"); | ||
| 497 | goto error; | ||
| 498 | } | ||
| 499 | |||
| 500 | return 0; | ||
| 501 | error: | ||
| 502 | power_supply_unregister(&chip->ac); | ||
| 503 | error1: | ||
| 504 | power_supply_unregister(&chip->battery); | ||
| 505 | error2: | ||
| 506 | kfree(chip); | ||
| 507 | return ret; | ||
| 508 | } | ||
| 509 | |||
| 510 | static int __devexit max17048_remove(struct i2c_client *client) | ||
| 511 | { | ||
| 512 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
| 513 | |||
| 514 | power_supply_unregister(&chip->battery); | ||
| 515 | power_supply_unregister(&chip->usb); | ||
| 516 | power_supply_unregister(&chip->ac); | ||
| 517 | cancel_delayed_work(&chip->work); | ||
| 518 | kfree(chip); | ||
| 519 | return 0; | ||
| 520 | } | ||
| 521 | |||
| 522 | #ifdef CONFIG_PM | ||
| 523 | |||
| 524 | static int max17048_suspend(struct i2c_client *client, | ||
| 525 | pm_message_t state) | ||
| 526 | { | ||
| 527 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
| 528 | |||
| 529 | cancel_delayed_work(&chip->work); | ||
| 530 | return 0; | ||
| 531 | } | ||
| 532 | |||
| 533 | static int max17048_resume(struct i2c_client *client) | ||
| 534 | { | ||
| 535 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
| 536 | |||
| 537 | schedule_delayed_work(&chip->work, MAX17048_DELAY); | ||
| 538 | return 0; | ||
| 539 | } | ||
| 540 | |||
| 541 | #else | ||
| 542 | |||
| 543 | #define max17048_suspend NULL | ||
| 544 | #define max17048_resume NULL | ||
| 545 | |||
| 546 | #endif /* CONFIG_PM */ | ||
| 547 | |||
| 548 | static const struct i2c_device_id max17048_id[] = { | ||
| 549 | { "max17048", 0 }, | ||
| 550 | { } | ||
| 551 | }; | ||
| 552 | MODULE_DEVICE_TABLE(i2c, max17048_id); | ||
| 553 | |||
| 554 | static struct i2c_driver max17048_i2c_driver = { | ||
| 555 | .driver = { | ||
| 556 | .name = "max17048", | ||
| 557 | }, | ||
| 558 | .probe = max17048_probe, | ||
| 559 | .remove = __devexit_p(max17048_remove), | ||
| 560 | .suspend = max17048_suspend, | ||
| 561 | .resume = max17048_resume, | ||
| 562 | .id_table = max17048_id, | ||
| 563 | }; | ||
| 564 | |||
| 565 | static int __init max17048_init(void) | ||
| 566 | { | ||
| 567 | return i2c_add_driver(&max17048_i2c_driver); | ||
| 568 | } | ||
| 569 | module_init(max17048_init); | ||
| 570 | |||
| 571 | static void __exit max17048_exit(void) | ||
| 572 | { | ||
| 573 | i2c_del_driver(&max17048_i2c_driver); | ||
| 574 | } | ||
| 575 | module_exit(max17048_exit); | ||
| 576 | |||
| 577 | MODULE_AUTHOR("Chandler Zhang <chazhang@nvidia.com>"); | ||
| 578 | MODULE_DESCRIPTION("MAX17048 Fuel Gauge"); | ||
| 579 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/max8907c-charger.c b/drivers/power/max8907c-charger.c new file mode 100644 index 00000000000..64855c589b1 --- /dev/null +++ b/drivers/power/max8907c-charger.c | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | /* | ||
| 2 | * Battery driver for Maxim MAX8907C | ||
| 3 | * | ||
| 4 | * Copyright (c) 2011, NVIDIA Corporation. | ||
| 5 | * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/err.h> | ||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/platform_device.h> | ||
| 16 | #include <linux/power_supply.h> | ||
| 17 | #include <linux/mfd/max8907c.h> | ||
| 18 | #include <linux/power/max8907c-charger.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | |||
| 21 | struct max8907c_charger { | ||
| 22 | struct max8907c_charger_pdata *pdata; | ||
| 23 | struct max8907c *chip; | ||
| 24 | struct i2c_client *i2c; | ||
| 25 | int online; | ||
| 26 | }; | ||
| 27 | |||
| 28 | static void max8907c_set_charger(struct max8907c_charger *charger) | ||
| 29 | { | ||
| 30 | struct max8907c_charger_pdata *pdata = charger->pdata; | ||
| 31 | int ret; | ||
| 32 | if (charger->online) { | ||
| 33 | ret = max8907c_reg_write(charger->i2c, MAX8907C_REG_CHG_CNTL1, | ||
| 34 | (pdata->topoff_threshold << 5) | | ||
| 35 | (pdata->restart_hysteresis << 3) | | ||
| 36 | (pdata->fast_charging_current)); | ||
| 37 | if (unlikely(ret != 0)) | ||
| 38 | pr_err("Failed to set CHG_CNTL1: %d\n", ret); | ||
| 39 | |||
| 40 | ret = max8907c_set_bits(charger->i2c, MAX8907C_REG_CHG_CNTL2, | ||
| 41 | 0x30, pdata->fast_charger_time << 4); | ||
| 42 | if (unlikely(ret != 0)) | ||
| 43 | pr_err("Failed to set CHG_CNTL2: %d\n", ret); | ||
| 44 | } else { | ||
| 45 | ret = max8907c_set_bits(charger->i2c, MAX8907C_REG_CHG_CNTL1, 0x80, 0x1); | ||
| 46 | if (unlikely(ret != 0)) | ||
| 47 | pr_err("Failed to set CHG_CNTL1: %d\n", ret); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | static irqreturn_t max8907c_charger_isr(int irq, void *dev_id) | ||
| 52 | { | ||
| 53 | struct max8907c_charger *charger = dev_id; | ||
| 54 | struct max8907c *chip = charger->chip; | ||
| 55 | |||
| 56 | switch (irq - chip->irq_base) { | ||
| 57 | case MAX8907C_IRQ_VCHG_DC_R: | ||
| 58 | charger->online = 1; | ||
| 59 | max8907c_set_charger(charger); | ||
| 60 | break; | ||
| 61 | case MAX8907C_IRQ_VCHG_DC_F: | ||
| 62 | charger->online = 0; | ||
| 63 | max8907c_set_charger(charger); | ||
| 64 | break; | ||
| 65 | } | ||
| 66 | |||
| 67 | return IRQ_HANDLED; | ||
| 68 | } | ||
| 69 | |||
| 70 | static int max8907c_charger_get_property(struct power_supply *psy, | ||
| 71 | enum power_supply_property psp, | ||
| 72 | union power_supply_propval *val) | ||
| 73 | { | ||
| 74 | const static int types[] = { | ||
| 75 | POWER_SUPPLY_CHARGE_TYPE_TRICKLE, | ||
| 76 | POWER_SUPPLY_CHARGE_TYPE_FAST, | ||
| 77 | POWER_SUPPLY_CHARGE_TYPE_FAST, | ||
| 78 | POWER_SUPPLY_CHARGE_TYPE_NONE, | ||
| 79 | }; | ||
| 80 | int ret = -ENODEV; | ||
| 81 | int status; | ||
| 82 | |||
| 83 | struct max8907c_charger *charger = dev_get_drvdata(psy->dev->parent); | ||
| 84 | |||
| 85 | switch (psp) { | ||
| 86 | case POWER_SUPPLY_PROP_ONLINE: | ||
| 87 | val->intval = charger->online; | ||
| 88 | ret = 0; | ||
| 89 | break; | ||
| 90 | |||
| 91 | case POWER_SUPPLY_PROP_STATUS: | ||
| 92 | /* Get charger status from CHG_EN_STAT */ | ||
| 93 | status = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT); | ||
| 94 | val->intval = ((status & 0x10) == 0x10) ? | ||
| 95 | POWER_SUPPLY_STATUS_CHARGING : | ||
| 96 | POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
| 97 | ret = 0; | ||
| 98 | break; | ||
| 99 | |||
| 100 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | ||
| 101 | /* Get charging type from CHG_MODE */ | ||
| 102 | status = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT); | ||
| 103 | val->intval = types[(status & 0x0C) >> 2]; | ||
| 104 | ret = 0; | ||
| 105 | break; | ||
| 106 | |||
| 107 | default: | ||
| 108 | val->intval = 0; | ||
| 109 | ret = -EINVAL; | ||
| 110 | break; | ||
| 111 | } | ||
| 112 | return ret; | ||
| 113 | } | ||
| 114 | |||
| 115 | static enum power_supply_property max8907c_charger_props[] = { | ||
| 116 | POWER_SUPPLY_PROP_ONLINE, | ||
| 117 | POWER_SUPPLY_PROP_STATUS, | ||
| 118 | POWER_SUPPLY_PROP_CHARGE_TYPE, | ||
| 119 | }; | ||
| 120 | |||
| 121 | static struct power_supply max8907c_charger_ps = { | ||
| 122 | .name = "charger", | ||
| 123 | .type = POWER_SUPPLY_TYPE_MAINS, | ||
| 124 | .properties = max8907c_charger_props, | ||
| 125 | .num_properties = ARRAY_SIZE(max8907c_charger_props), | ||
| 126 | .get_property = max8907c_charger_get_property, | ||
| 127 | }; | ||
| 128 | |||
| 129 | static __devinit int max8907c_charger_probe(struct platform_device *pdev) | ||
| 130 | { | ||
| 131 | struct max8907c_charger_pdata *pdata = pdev->dev.platform_data; | ||
| 132 | struct max8907c_charger *charger = 0; | ||
| 133 | struct max8907c *chip = dev_get_drvdata(pdev->dev.parent); | ||
| 134 | int ret; | ||
| 135 | |||
| 136 | charger = kzalloc(sizeof(*charger), GFP_KERNEL); | ||
| 137 | if (!charger) | ||
| 138 | return -ENOMEM; | ||
| 139 | |||
| 140 | charger->pdata = pdata; | ||
| 141 | charger->online = 0; | ||
| 142 | charger->chip = chip; | ||
| 143 | charger->i2c = chip->i2c_power; | ||
| 144 | |||
| 145 | platform_set_drvdata(pdev, charger); | ||
| 146 | |||
| 147 | ret = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT); | ||
| 148 | if (ret & (1 << 7)) { | ||
| 149 | charger->online = 1; | ||
| 150 | max8907c_set_charger(charger); | ||
| 151 | } | ||
| 152 | |||
| 153 | ret = request_threaded_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, NULL, | ||
| 154 | max8907c_charger_isr, IRQF_ONESHOT, | ||
| 155 | "power-remove", charger); | ||
| 156 | if (unlikely(ret < 0)) { | ||
| 157 | pr_debug("max8907c: failed to request IRQ %X\n", ret); | ||
| 158 | goto out; | ||
| 159 | } | ||
| 160 | |||
| 161 | ret = request_threaded_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, NULL, | ||
| 162 | max8907c_charger_isr, IRQF_ONESHOT, | ||
| 163 | "power-insert", charger); | ||
| 164 | if (unlikely(ret < 0)) { | ||
| 165 | pr_debug("max8907c: failed to request IRQ %X\n", ret); | ||
| 166 | goto out1; | ||
| 167 | } | ||
| 168 | |||
| 169 | |||
| 170 | ret = power_supply_register(&pdev->dev, &max8907c_charger_ps); | ||
| 171 | if (unlikely(ret != 0)) { | ||
| 172 | pr_err("Failed to register max8907c_charger driver: %d\n", ret); | ||
| 173 | goto out2; | ||
| 174 | } | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | out2: | ||
| 178 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, charger); | ||
| 179 | out1: | ||
| 180 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, charger); | ||
| 181 | out: | ||
| 182 | kfree(charger); | ||
| 183 | return ret; | ||
| 184 | } | ||
| 185 | |||
| 186 | static __devexit int max8907c_charger_remove(struct platform_device *pdev) | ||
| 187 | { | ||
| 188 | struct max8907c_charger *charger = platform_get_drvdata(pdev); | ||
| 189 | struct max8907c *chip = charger->chip; | ||
| 190 | int ret; | ||
| 191 | |||
| 192 | ret = max8907c_reg_write(charger->i2c, MAX8907C_REG_CHG_IRQ1_MASK, 0xFF); | ||
| 193 | if (unlikely(ret != 0)) { | ||
| 194 | pr_err("Failed to set IRQ1_MASK: %d\n", ret); | ||
| 195 | goto out; | ||
| 196 | } | ||
| 197 | |||
| 198 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, charger); | ||
| 199 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, charger); | ||
| 200 | power_supply_unregister(&max8907c_charger_ps); | ||
| 201 | out: | ||
| 202 | kfree(charger); | ||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | static struct platform_driver max8907c_charger_driver = { | ||
| 207 | .probe = max8907c_charger_probe, | ||
| 208 | .remove = __devexit_p(max8907c_charger_remove), | ||
| 209 | .driver = { | ||
| 210 | .name = "max8907c-charger", | ||
| 211 | }, | ||
| 212 | }; | ||
| 213 | |||
| 214 | static int __init max8907c_charger_init(void) | ||
| 215 | { | ||
| 216 | return platform_driver_register(&max8907c_charger_driver); | ||
| 217 | } | ||
| 218 | module_init(max8907c_charger_init); | ||
| 219 | |||
| 220 | static void __exit max8907c_charger_exit(void) | ||
| 221 | { | ||
| 222 | platform_driver_unregister(&max8907c_charger_driver); | ||
| 223 | } | ||
| 224 | module_exit(max8907c_charger_exit); | ||
| 225 | |||
| 226 | MODULE_DESCRIPTION("Charger driver for MAX8907C"); | ||
| 227 | MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>"); | ||
| 228 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/smb349-charger.c b/drivers/power/smb349-charger.c new file mode 100644 index 00000000000..1f230baadcb --- /dev/null +++ b/drivers/power/smb349-charger.c | |||
| @@ -0,0 +1,588 @@ | |||
| 1 | /* | ||
| 2 | * drivers/power/smb349-charger.c | ||
| 3 | * | ||
| 4 | * Battery charger driver for smb349 from summit microelectronics | ||
| 5 | * | ||
| 6 | * Copyright (c) 2012, NVIDIA Corporation. | ||
| 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; | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 15 | * more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License along | ||
| 18 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <linux/mutex.h> | ||
| 26 | #include <linux/err.h> | ||
| 27 | #include <linux/i2c.h> | ||
| 28 | #include <linux/delay.h> | ||
| 29 | #include <linux/power_supply.h> | ||
| 30 | #include <linux/platform_device.h> | ||
| 31 | #include <linux/regulator/driver.h> | ||
| 32 | #include <linux/regulator/machine.h> | ||
| 33 | #include <linux/smb349-charger.h> | ||
| 34 | #include <linux/slab.h> | ||
| 35 | #include <linux/gpio.h> | ||
| 36 | #include <linux/interrupt.h> | ||
| 37 | #include <linux/irq.h> | ||
| 38 | #include <linux/usb/otg.h> | ||
| 39 | |||
| 40 | #define SMB349_CHARGE 0x00 | ||
| 41 | #define SMB349_CHRG_CRNTS 0x01 | ||
| 42 | #define SMB349_VRS_FUNC 0x02 | ||
| 43 | #define SMB349_FLOAT_VLTG 0x03 | ||
| 44 | #define SMB349_CHRG_CTRL 0x04 | ||
| 45 | #define SMB349_STAT_TIME_CTRL 0x05 | ||
| 46 | #define SMB349_PIN_CTRL 0x06 | ||
| 47 | #define SMB349_THERM_CTRL 0x07 | ||
| 48 | #define SMB349_CTRL_REG 0x09 | ||
| 49 | |||
| 50 | #define SMB349_OTG_TLIM_REG 0x0A | ||
| 51 | #define SMB349_HRD_SFT_TEMP 0x0B | ||
| 52 | #define SMB349_FAULT_INTR 0x0C | ||
| 53 | #define SMB349_STS_INTR_1 0x0D | ||
| 54 | #define SMB349_SYSOK_USB3 0x0E | ||
| 55 | #define SMB349_IN_CLTG_DET 0x10 | ||
| 56 | #define SMB349_STS_INTR_2 0x11 | ||
| 57 | |||
| 58 | #define SMB349_CMD_REG 0x30 | ||
| 59 | #define SMB349_CMD_REG_B 0x31 | ||
| 60 | #define SMB349_CMD_REG_c 0x33 | ||
| 61 | |||
| 62 | #define SMB349_INTR_STS_A 0x35 | ||
| 63 | #define SMB349_INTR_STS_B 0x36 | ||
| 64 | #define SMB349_INTR_STS_C 0x37 | ||
| 65 | #define SMB349_INTR_STS_D 0x38 | ||
| 66 | #define SMB349_INTR_STS_E 0x39 | ||
| 67 | #define SMB349_INTR_STS_F 0x3A | ||
| 68 | |||
| 69 | #define SMB349_STS_REG_A 0x3B | ||
| 70 | #define SMB349_STS_REG_B 0x3C | ||
| 71 | #define SMB349_STS_REG_C 0x3D | ||
| 72 | #define SMB349_STS_REG_D 0x3E | ||
| 73 | #define SMB349_STS_REG_E 0x3F | ||
| 74 | |||
| 75 | #define SMB349_ENABLE_WRITE 1 | ||
| 76 | #define SMB349_DISABLE_WRITE 0 | ||
| 77 | #define ENABLE_WRT_ACCESS 0x80 | ||
| 78 | #define THERM_CTRL 0x10 | ||
| 79 | #define BATTERY_MISSING 0x10 | ||
| 80 | #define CHARGING 0x06 | ||
| 81 | #define DEDICATED_CHARGER 0x04 | ||
| 82 | #define CHRG_DOWNSTRM_PORT 0x08 | ||
| 83 | #define ENABLE_CHARGE 0x02 | ||
| 84 | |||
| 85 | static struct smb349_charger *charger; | ||
| 86 | static int smb349_configure_charger(struct i2c_client *client, int value); | ||
| 87 | static int smb349_configure_interrupts(struct i2c_client *client); | ||
| 88 | |||
| 89 | static int smb349_read(struct i2c_client *client, int reg) | ||
| 90 | { | ||
| 91 | int ret; | ||
| 92 | |||
| 93 | ret = i2c_smbus_read_byte_data(client, reg); | ||
| 94 | |||
| 95 | if (ret < 0) | ||
| 96 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 97 | |||
| 98 | return ret; | ||
| 99 | } | ||
| 100 | |||
| 101 | static int smb349_write(struct i2c_client *client, int reg, u8 value) | ||
| 102 | { | ||
| 103 | int ret; | ||
| 104 | |||
| 105 | ret = i2c_smbus_write_byte_data(client, reg, value); | ||
| 106 | |||
| 107 | if (ret < 0) | ||
| 108 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 109 | |||
| 110 | return ret; | ||
| 111 | } | ||
| 112 | |||
| 113 | static int smb349_update_reg(struct i2c_client *client, int reg, u8 value) | ||
| 114 | { | ||
| 115 | int ret, retval; | ||
| 116 | |||
| 117 | retval = smb349_read(client, reg); | ||
| 118 | if (retval < 0) { | ||
| 119 | dev_err(&client->dev, "%s: err %d\n", __func__, retval); | ||
| 120 | return retval; | ||
| 121 | } | ||
| 122 | |||
| 123 | ret = smb349_write(client, reg, retval | value); | ||
| 124 | if (ret < 0) { | ||
| 125 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 126 | return ret; | ||
| 127 | } | ||
| 128 | |||
| 129 | return ret; | ||
| 130 | } | ||
| 131 | |||
| 132 | int smb349_volatile_writes(struct i2c_client *client, uint8_t value) | ||
| 133 | { | ||
| 134 | int ret = 0; | ||
| 135 | |||
| 136 | if (value == SMB349_ENABLE_WRITE) { | ||
| 137 | /* Enable volatile write to config registers */ | ||
| 138 | ret = smb349_update_reg(client, SMB349_CMD_REG, | ||
| 139 | ENABLE_WRT_ACCESS); | ||
| 140 | if (ret < 0) { | ||
| 141 | dev_err(&client->dev, "%s(): Failed in writing" | ||
| 142 | "register 0x%02x\n", __func__, SMB349_CMD_REG); | ||
| 143 | return ret; | ||
| 144 | } | ||
| 145 | } else { | ||
| 146 | ret = smb349_read(client, SMB349_CMD_REG); | ||
| 147 | if (ret < 0) { | ||
| 148 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 149 | return ret; | ||
| 150 | } | ||
| 151 | |||
| 152 | ret = smb349_write(client, SMB349_CMD_REG, ret & (~(1<<7))); | ||
| 153 | if (ret < 0) { | ||
| 154 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 155 | return ret; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | return ret; | ||
| 159 | } | ||
| 160 | |||
| 161 | static void smb349_clear_interrupts(struct i2c_client *client) | ||
| 162 | { | ||
| 163 | uint8_t val, buf[6]; | ||
| 164 | |||
| 165 | val = i2c_smbus_read_i2c_block_data(client, SMB349_INTR_STS_A, 6, buf); | ||
| 166 | if (val < 0) | ||
| 167 | dev_err(&client->dev, "%s(): Failed in clearing interrupts\n", | ||
| 168 | __func__); | ||
| 169 | } | ||
| 170 | |||
| 171 | static int smb349_configure_otg(struct i2c_client *client, int enable) | ||
| 172 | { | ||
| 173 | int ret = 0; | ||
| 174 | |||
| 175 | /*Enable volatile writes to registers*/ | ||
| 176 | ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); | ||
| 177 | if (ret < 0) { | ||
| 178 | dev_err(&client->dev, "%s error in configuring otg..\n", | ||
| 179 | __func__); | ||
| 180 | goto error; | ||
| 181 | } | ||
| 182 | |||
| 183 | if (enable) { | ||
| 184 | /* Configure PGOOD to be active low */ | ||
| 185 | ret = smb349_read(client, SMB349_SYSOK_USB3); | ||
| 186 | if (ret < 0) { | ||
| 187 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 188 | goto error; | ||
| 189 | } | ||
| 190 | |||
| 191 | ret = smb349_write(client, SMB349_SYSOK_USB3, (ret & (~(1)))); | ||
| 192 | if (ret < 0) { | ||
| 193 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 194 | goto error; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* Enable OTG */ | ||
| 198 | ret = smb349_update_reg(client, SMB349_CMD_REG, 0x10); | ||
| 199 | if (ret < 0) { | ||
| 200 | dev_err(&client->dev, "%s: Failed in writing register" | ||
| 201 | "0x%02x\n", __func__, SMB349_CMD_REG); | ||
| 202 | goto error; | ||
| 203 | } | ||
| 204 | } else { | ||
| 205 | /* Disable OTG */ | ||
| 206 | ret = smb349_read(client, SMB349_CMD_REG); | ||
| 207 | if (ret < 0) { | ||
| 208 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 209 | goto error; | ||
| 210 | } | ||
| 211 | |||
| 212 | ret = smb349_write(client, SMB349_CMD_REG, (ret & (~(1<<4)))); | ||
| 213 | if (ret < 0) { | ||
| 214 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 215 | goto error; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* Configure PGOOD to be active high */ | ||
| 219 | ret = smb349_update_reg(client, SMB349_SYSOK_USB3, 0x01); | ||
| 220 | if (ret < 0) { | ||
| 221 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 222 | goto error; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | /* Disable volatile writes to registers */ | ||
| 227 | ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); | ||
| 228 | if (ret < 0) | ||
| 229 | dev_err(&client->dev, "%s error in configuring OTG..\n", | ||
| 230 | __func__); | ||
| 231 | error: | ||
| 232 | return ret; | ||
| 233 | } | ||
| 234 | |||
| 235 | static int smb349_configure_charger(struct i2c_client *client, int value) | ||
| 236 | { | ||
| 237 | int ret = 0; | ||
| 238 | |||
| 239 | /* Enable volatile writes to registers */ | ||
| 240 | ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); | ||
| 241 | if (ret < 0) { | ||
| 242 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
| 243 | __func__); | ||
| 244 | goto error; | ||
| 245 | } | ||
| 246 | |||
| 247 | if (value) { | ||
| 248 | /* Enable charging */ | ||
| 249 | ret = smb349_update_reg(client, SMB349_CMD_REG, ENABLE_CHARGE); | ||
| 250 | if (ret < 0) { | ||
| 251 | dev_err(&client->dev, "%s(): Failed in writing register" | ||
| 252 | "0x%02x\n", __func__, SMB349_CMD_REG); | ||
| 253 | goto error; | ||
| 254 | } | ||
| 255 | |||
| 256 | /* Configure THERM ctrl */ | ||
| 257 | ret = smb349_update_reg(client, SMB349_THERM_CTRL, THERM_CTRL); | ||
| 258 | if (ret < 0) { | ||
| 259 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 260 | goto error; | ||
| 261 | } | ||
| 262 | } else { | ||
| 263 | ret = smb349_read(client, SMB349_CMD_REG); | ||
| 264 | if (ret < 0) { | ||
| 265 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 266 | goto error; | ||
| 267 | } | ||
| 268 | |||
| 269 | ret = smb349_write(client, SMB349_CMD_REG, (ret & (~(1<<1)))); | ||
| 270 | if (ret < 0) { | ||
| 271 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 272 | goto error; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | /* Disable volatile writes to registers */ | ||
| 276 | ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); | ||
| 277 | if (ret < 0) { | ||
| 278 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
| 279 | __func__); | ||
| 280 | goto error; | ||
| 281 | } | ||
| 282 | error: | ||
| 283 | return ret; | ||
| 284 | } | ||
| 285 | |||
| 286 | static irqreturn_t smb349_status_isr(int irq, void *dev_id) | ||
| 287 | { | ||
| 288 | struct i2c_client *client = charger->client; | ||
| 289 | int ret, val; | ||
| 290 | |||
| 291 | val = smb349_read(client, SMB349_STS_REG_D); | ||
| 292 | if (val < 0) { | ||
| 293 | dev_err(&client->dev, "%s(): Failed in reading register" | ||
| 294 | "0x%02x\n", __func__, SMB349_STS_REG_D); | ||
| 295 | goto irq_error; | ||
| 296 | } else if (val != 0) { | ||
| 297 | if (val & DEDICATED_CHARGER) | ||
| 298 | charger->chrg_type = AC; | ||
| 299 | else if (val & CHRG_DOWNSTRM_PORT) | ||
| 300 | charger->chrg_type = USB; | ||
| 301 | |||
| 302 | /* configure charger */ | ||
| 303 | ret = smb349_configure_charger(client, 1); | ||
| 304 | if (ret < 0) { | ||
| 305 | dev_err(&client->dev, "%s() error in configuring" | ||
| 306 | "charger..\n", __func__); | ||
| 307 | goto irq_error; | ||
| 308 | } | ||
| 309 | |||
| 310 | charger->state = progress; | ||
| 311 | } else { | ||
| 312 | charger->state = stopped; | ||
| 313 | |||
| 314 | /* Disable charger */ | ||
| 315 | ret = smb349_configure_charger(client, 0); | ||
| 316 | if (ret < 0) { | ||
| 317 | dev_err(&client->dev, "%s() error in configuring" | ||
| 318 | "charger..\n", __func__); | ||
| 319 | goto irq_error; | ||
| 320 | } | ||
| 321 | |||
| 322 | ret = smb349_configure_interrupts(client); | ||
| 323 | if (ret < 0) { | ||
| 324 | dev_err(&client->dev, "%s() error in configuring" | ||
| 325 | "charger..\n", __func__); | ||
| 326 | goto irq_error; | ||
| 327 | } | ||
| 328 | |||
| 329 | } | ||
| 330 | |||
| 331 | if (charger->charger_cb) | ||
| 332 | charger->charger_cb(charger->state, charger->chrg_type, | ||
| 333 | charger->charger_cb_data); | ||
| 334 | irq_error: | ||
| 335 | smb349_clear_interrupts(client); | ||
| 336 | return IRQ_HANDLED; | ||
| 337 | } | ||
| 338 | |||
| 339 | int update_charger_status(void) | ||
| 340 | { | ||
| 341 | struct i2c_client *client = charger->client; | ||
| 342 | int ret, val; | ||
| 343 | |||
| 344 | val = smb349_read(client, SMB349_STS_REG_D); | ||
| 345 | if (val < 0) { | ||
| 346 | dev_err(&client->dev, "%s(): Failed in reading register" | ||
| 347 | "0x%02x\n", __func__, SMB349_STS_REG_D); | ||
| 348 | goto val_error; | ||
| 349 | } else if (val != 0) { | ||
| 350 | if (val & DEDICATED_CHARGER) | ||
| 351 | charger->chrg_type = AC; | ||
| 352 | else if (val & CHRG_DOWNSTRM_PORT) | ||
| 353 | charger->chrg_type = USB; | ||
| 354 | |||
| 355 | /* configure charger */ | ||
| 356 | ret = smb349_configure_charger(client, 1); | ||
| 357 | if (ret < 0) { | ||
| 358 | dev_err(&client->dev, "%s() error in configuring" | ||
| 359 | "charger..\n", __func__); | ||
| 360 | goto ret_error; | ||
| 361 | } | ||
| 362 | |||
| 363 | charger->state = progress; | ||
| 364 | } else { | ||
| 365 | charger->state = stopped; | ||
| 366 | |||
| 367 | /* Disable charger */ | ||
| 368 | ret = smb349_configure_charger(client, 0); | ||
| 369 | if (ret < 0) { | ||
| 370 | dev_err(&client->dev, "%s() error in configuring" | ||
| 371 | "charger..\n", __func__); | ||
| 372 | goto ret_error; | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | if (charger->charger_cb) | ||
| 377 | charger->charger_cb(charger->state, charger->chrg_type, | ||
| 378 | charger->charger_cb_data); | ||
| 379 | return 0; | ||
| 380 | val_error: | ||
| 381 | return val; | ||
| 382 | ret_error: | ||
| 383 | return ret; | ||
| 384 | } | ||
| 385 | EXPORT_SYMBOL_GPL(update_charger_status); | ||
| 386 | |||
| 387 | int register_callback(charging_callback_t cb, void *args) | ||
| 388 | { | ||
| 389 | struct smb349_charger *charger_data = charger; | ||
| 390 | if (!charger_data) | ||
| 391 | return -ENODEV; | ||
| 392 | |||
| 393 | charger_data->charger_cb = cb; | ||
| 394 | charger_data->charger_cb_data = args; | ||
| 395 | return 0; | ||
| 396 | } | ||
| 397 | EXPORT_SYMBOL_GPL(register_callback); | ||
| 398 | |||
| 399 | int smb349_battery_online(void) | ||
| 400 | { | ||
| 401 | int val; | ||
| 402 | struct i2c_client *client = charger->client; | ||
| 403 | |||
| 404 | val = smb349_read(charger->client, SMB349_INTR_STS_B); | ||
| 405 | if (val < 0) { | ||
| 406 | dev_err(&client->dev, "%s(): Failed in reading register" | ||
| 407 | "0x%02x\n", __func__, SMB349_INTR_STS_B); | ||
| 408 | return val; | ||
| 409 | } | ||
| 410 | if (val & BATTERY_MISSING) | ||
| 411 | return 0; | ||
| 412 | else | ||
| 413 | return 1; | ||
| 414 | } | ||
| 415 | |||
| 416 | static int smb349_configure_interrupts(struct i2c_client *client) | ||
| 417 | { | ||
| 418 | int ret = 0; | ||
| 419 | |||
| 420 | /* Enable volatile writes to registers */ | ||
| 421 | ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); | ||
| 422 | if (ret < 0) { | ||
| 423 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
| 424 | __func__); | ||
| 425 | goto error; | ||
| 426 | } | ||
| 427 | |||
| 428 | ret = smb349_update_reg(client, SMB349_FAULT_INTR, 0xff); | ||
| 429 | if (ret < 0) { | ||
| 430 | dev_err(&client->dev, "%s(): Failed in writing register" | ||
| 431 | "0x%02x\n", __func__, SMB349_CMD_REG); | ||
| 432 | goto error; | ||
| 433 | } | ||
| 434 | |||
| 435 | ret = smb349_update_reg(client, SMB349_STS_INTR_1, 0xff); | ||
| 436 | if (ret < 0) { | ||
| 437 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
| 438 | goto error; | ||
| 439 | } | ||
| 440 | |||
| 441 | /* Disable volatile writes to registers */ | ||
| 442 | ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); | ||
| 443 | if (ret < 0) { | ||
| 444 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
| 445 | __func__); | ||
| 446 | goto error; | ||
| 447 | } | ||
| 448 | |||
| 449 | error: | ||
| 450 | return ret; | ||
| 451 | } | ||
| 452 | |||
| 453 | static void smb349_otg_status(enum usb_otg_state to, enum usb_otg_state from, void *data) | ||
| 454 | { | ||
| 455 | struct i2c_client *client = charger->client; | ||
| 456 | int ret; | ||
| 457 | |||
| 458 | if ((from == OTG_STATE_A_SUSPEND) && (to == OTG_STATE_A_HOST)) { | ||
| 459 | |||
| 460 | /* configure charger */ | ||
| 461 | ret = smb349_configure_charger(client, 0); | ||
| 462 | if (ret < 0) | ||
| 463 | dev_err(&client->dev, "%s() error in configuring" | ||
| 464 | "otg..\n", __func__); | ||
| 465 | |||
| 466 | /* ENABLE OTG */ | ||
| 467 | ret = smb349_configure_otg(client, 1); | ||
| 468 | if (ret < 0) | ||
| 469 | dev_err(&client->dev, "%s() error in configuring" | ||
| 470 | "otg..\n", __func__); | ||
| 471 | |||
| 472 | } else if ((from == OTG_STATE_A_HOST) && (to == OTG_STATE_A_SUSPEND)) { | ||
| 473 | |||
| 474 | /* Disable OTG */ | ||
| 475 | ret = smb349_configure_otg(client, 0); | ||
| 476 | if (ret < 0) | ||
| 477 | dev_err(&client->dev, "%s() error in configuring" | ||
| 478 | "otg..\n", __func__); | ||
| 479 | |||
| 480 | /* configure charger */ | ||
| 481 | ret = smb349_configure_charger(client, 1); | ||
| 482 | if (ret < 0) | ||
| 483 | dev_err(&client->dev, "%s() error in configuring" | ||
| 484 | "otg..\n", __func__); | ||
| 485 | |||
| 486 | ret = smb349_configure_interrupts(client); | ||
| 487 | if (ret < 0) | ||
| 488 | dev_err(&client->dev, "%s() error in configuring" | ||
| 489 | "otg..\n", __func__); | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | static int __devinit smb349_probe(struct i2c_client *client, | ||
| 494 | const struct i2c_device_id *id) | ||
| 495 | { | ||
| 496 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||
| 497 | int ret, irq_num; | ||
| 498 | |||
| 499 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) | ||
| 500 | return -EIO; | ||
| 501 | |||
| 502 | charger = kzalloc(sizeof(*charger), GFP_KERNEL); | ||
| 503 | if (!charger) | ||
| 504 | return -ENOMEM; | ||
| 505 | |||
| 506 | charger->client = client; | ||
| 507 | charger->dev = &client->dev; | ||
| 508 | i2c_set_clientdata(client, charger); | ||
| 509 | |||
| 510 | /* Check battery presence */ | ||
| 511 | if (!smb349_battery_online()) { | ||
| 512 | dev_err(&client->dev, "%s() No Battery present, exiting..\n", | ||
| 513 | __func__); | ||
| 514 | ret = -ENODEV; | ||
| 515 | goto error; | ||
| 516 | } | ||
| 517 | |||
| 518 | ret = register_otg_callback(smb349_otg_status, charger); | ||
| 519 | if (ret < 0) | ||
| 520 | goto error; | ||
| 521 | |||
| 522 | ret = smb349_configure_charger(client, 1); | ||
| 523 | if (ret < 0) | ||
| 524 | return ret; | ||
| 525 | |||
| 526 | ret = smb349_configure_interrupts(client); | ||
| 527 | if (ret < 0) { | ||
| 528 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
| 529 | __func__); | ||
| 530 | goto error; | ||
| 531 | } | ||
| 532 | |||
| 533 | irq_num = gpio_to_irq(client->irq); | ||
| 534 | ret = request_threaded_irq(irq_num, | ||
| 535 | NULL, smb349_status_isr, IRQ_TYPE_EDGE_FALLING, | ||
| 536 | "smb349", charger); | ||
| 537 | if (ret) { | ||
| 538 | dev_err(&client->dev, "%s(): Failed in requesting isr\n", | ||
| 539 | __func__); | ||
| 540 | goto error; | ||
| 541 | } | ||
| 542 | |||
| 543 | return 0; | ||
| 544 | error: | ||
| 545 | kfree(charger); | ||
| 546 | return ret; | ||
| 547 | } | ||
| 548 | |||
| 549 | static int __devexit smb349_remove(struct i2c_client *client) | ||
| 550 | { | ||
| 551 | struct smb349_charger *charger = i2c_get_clientdata(client); | ||
| 552 | |||
| 553 | free_irq(gpio_to_irq(client->irq), charger); | ||
| 554 | kfree(charger); | ||
| 555 | |||
| 556 | return 0; | ||
| 557 | } | ||
| 558 | |||
| 559 | static const struct i2c_device_id smb349_id[] = { | ||
| 560 | { "smb349", 0 }, | ||
| 561 | { } | ||
| 562 | }; | ||
| 563 | MODULE_DEVICE_TABLE(i2c, smb349_id); | ||
| 564 | |||
| 565 | static struct i2c_driver smb349_i2c_driver = { | ||
| 566 | .driver = { | ||
| 567 | .name = "smb349", | ||
| 568 | }, | ||
| 569 | .probe = smb349_probe, | ||
| 570 | .remove = __devexit_p(smb349_remove), | ||
| 571 | .id_table = smb349_id, | ||
| 572 | }; | ||
| 573 | |||
| 574 | static int __init smb349_init(void) | ||
| 575 | { | ||
| 576 | return i2c_add_driver(&smb349_i2c_driver); | ||
| 577 | } | ||
| 578 | module_init(smb349_init); | ||
| 579 | |||
| 580 | static void __exit smb349_exit(void) | ||
| 581 | { | ||
| 582 | i2c_del_driver(&smb349_i2c_driver); | ||
| 583 | } | ||
| 584 | module_exit(smb349_exit); | ||
| 585 | |||
| 586 | MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>"); | ||
| 587 | MODULE_DESCRIPTION("SMB349 Battery-Charger"); | ||
| 588 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/tegra_bpc_mgmt.c b/drivers/power/tegra_bpc_mgmt.c new file mode 100644 index 00000000000..0d9ddeee282 --- /dev/null +++ b/drivers/power/tegra_bpc_mgmt.c | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
| 3 | * | ||
| 4 | * This software is licensed under the terms of the GNU General Public | ||
| 5 | * License version 2, as published by the Free Software Foundation, and | ||
| 6 | * may be copied, distributed, and modified under those terms. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/gpio.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/interrupt.h> | ||
| 19 | #include <linux/irqflags.h> | ||
| 20 | #include <linux/slab.h> | ||
| 21 | #include <linux/irq.h> | ||
| 22 | #include <linux/sched.h> | ||
| 23 | #include <linux/jiffies.h> | ||
| 24 | #include <linux/platform_data/tegra_bpc_mgmt.h> | ||
| 25 | |||
| 26 | #include <mach/edp.h> | ||
| 27 | |||
| 28 | static irqreturn_t tegra_bpc_mgmt_bh(int irq, void *data) | ||
| 29 | { | ||
| 30 | int gpio_val = 0; | ||
| 31 | struct tegra_bpc_mgmt_platform_data *bpc_platform_data; | ||
| 32 | bpc_platform_data = (struct tegra_bpc_mgmt_platform_data *)data; | ||
| 33 | |||
| 34 | tegra_system_edp_alarm(true); | ||
| 35 | /** | ||
| 36 | * Keep on checking whether event has passed or not. | ||
| 37 | */ | ||
| 38 | while (!gpio_val) { | ||
| 39 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 40 | schedule_timeout(msecs_to_jiffies( | ||
| 41 | bpc_platform_data->bpc_mgmt_timeout)); | ||
| 42 | |||
| 43 | gpio_val = gpio_get_value(bpc_platform_data->gpio_trigger); | ||
| 44 | } | ||
| 45 | |||
| 46 | tegra_system_edp_alarm(false); | ||
| 47 | |||
| 48 | return IRQ_HANDLED; | ||
| 49 | } | ||
| 50 | |||
| 51 | static irqreturn_t tegra_bpc_mgmt_isr(int irq, void *data) | ||
| 52 | { | ||
| 53 | tegra_edp_throttle_cpu_now(2); | ||
| 54 | return IRQ_WAKE_THREAD; | ||
| 55 | } | ||
| 56 | |||
| 57 | static __devinit int tegra_bpc_mgmt_probe(struct platform_device *pdev) | ||
| 58 | { | ||
| 59 | u32 ret; | ||
| 60 | struct task_struct *bh_thread; | ||
| 61 | struct irq_desc *bat_desc; | ||
| 62 | struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; | ||
| 63 | struct tegra_bpc_mgmt_platform_data *bpc_platform_data; | ||
| 64 | |||
| 65 | bpc_platform_data = pdev->dev.platform_data; | ||
| 66 | if (!bpc_platform_data) | ||
| 67 | return -ENODEV; | ||
| 68 | |||
| 69 | if (gpio_is_valid(bpc_platform_data->gpio_trigger)) { | ||
| 70 | ret = gpio_request(bpc_platform_data->gpio_trigger, | ||
| 71 | "tegra-bpc-mgmt"); | ||
| 72 | |||
| 73 | if (ret < 0) { | ||
| 74 | pr_err("BPC: GPIO request failed"); | ||
| 75 | return -ENODEV; | ||
| 76 | } | ||
| 77 | } else { | ||
| 78 | pr_err("BPC: GPIO check failed, gpio %d", | ||
| 79 | bpc_platform_data->gpio_trigger); | ||
| 80 | return -ENODEV; | ||
| 81 | } | ||
| 82 | |||
| 83 | gpio_direction_input(bpc_platform_data->gpio_trigger); | ||
| 84 | |||
| 85 | ret = request_threaded_irq( | ||
| 86 | gpio_to_irq(bpc_platform_data->gpio_trigger), | ||
| 87 | tegra_bpc_mgmt_isr, | ||
| 88 | tegra_bpc_mgmt_bh, IRQF_TRIGGER_LOW | IRQF_ONESHOT, | ||
| 89 | "tegra-bpc-mgmt", bpc_platform_data); | ||
| 90 | if (ret < 0) { | ||
| 91 | pr_err("BPC:IRQ Installation failed\n"); | ||
| 92 | return -ENODEV; | ||
| 93 | } | ||
| 94 | bat_desc = irq_to_desc( | ||
| 95 | gpio_to_irq(bpc_platform_data->gpio_trigger)); | ||
| 96 | |||
| 97 | if (bat_desc) { | ||
| 98 | bh_thread = bat_desc->action->thread; | ||
| 99 | if (bh_thread) | ||
| 100 | sched_setscheduler_nocheck(bh_thread, | ||
| 101 | SCHED_FIFO, ¶m); | ||
| 102 | } | ||
| 103 | |||
| 104 | return 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | static __devexit int tegra_bpc_mgmt_remove(struct platform_device *pdev) | ||
| 108 | { | ||
| 109 | struct tegra_bpc_mgmt_platform_data *bpc_platform_data; | ||
| 110 | bpc_platform_data = pdev->dev.platform_data; | ||
| 111 | free_irq(gpio_to_irq(bpc_platform_data->gpio_trigger), NULL); | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static struct platform_driver tegra_bpc_mgmt_driver = { | ||
| 116 | .probe = tegra_bpc_mgmt_probe, | ||
| 117 | .remove = tegra_bpc_mgmt_remove, | ||
| 118 | .driver = { | ||
| 119 | .name = "tegra-bpc-mgmt", | ||
| 120 | .owner = THIS_MODULE, | ||
| 121 | }, | ||
| 122 | }; | ||
| 123 | |||
| 124 | static int __init tegra_bpc_mgmt_init(void) | ||
| 125 | { | ||
| 126 | return platform_driver_register(&tegra_bpc_mgmt_driver); | ||
| 127 | } | ||
| 128 | |||
| 129 | static void __exit tegra_bpc_mgmt_exit(void) | ||
| 130 | { | ||
| 131 | platform_driver_unregister(&tegra_bpc_mgmt_driver); | ||
| 132 | } | ||
| 133 | |||
| 134 | module_init(tegra_bpc_mgmt_init); | ||
| 135 | module_exit(tegra_bpc_mgmt_exit); | ||
| 136 | |||
| 137 | MODULE_DESCRIPTION("TEGRA Battery Peak current Management"); | ||
| 138 | MODULE_AUTHOR("NVIDIA"); | ||
| 139 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/tps80031-charger.c b/drivers/power/tps80031-charger.c new file mode 100644 index 00000000000..93b283e0b04 --- /dev/null +++ b/drivers/power/tps80031-charger.c | |||
| @@ -0,0 +1,476 @@ | |||
| 1 | /* | ||
| 2 | * drivers/power/tps80031_charger.c | ||
| 3 | * | ||
| 4 | * Battery charger driver for TI's tps80031 | ||
| 5 | * | ||
| 6 | * Copyright (c) 2011, NVIDIA Corporation. | ||
| 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, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along | ||
| 19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 21 | */ | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/delay.h> | ||
| 24 | #include <linux/init.h> | ||
| 25 | #include <linux/err.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/platform_device.h> | ||
| 28 | #include <linux/regulator/driver.h> | ||
| 29 | #include <linux/regulator/machine.h> | ||
| 30 | #include <linux/mfd/tps80031.h> | ||
| 31 | #include <linux/tps80031-charger.h> | ||
| 32 | |||
| 33 | #define CONTROLLER_CTRL1 0xe1 | ||
| 34 | #define CONTROLLER_STAT1 0xe3 | ||
| 35 | #define CHARGERUSB_CTRL2 0xe9 | ||
| 36 | #define CHARGERUSB_CTRL3 0xea | ||
| 37 | #define CHARGERUSB_VOREG 0xec | ||
| 38 | #define CHARGERUSB_VICHRG 0xed | ||
| 39 | #define CHARGERUSB_CINLIMIT 0xee | ||
| 40 | #define CHARGERUSB_CTRLLIMIT2 0xf0 | ||
| 41 | #define CHARGERUSB_CTRLLIMIT1 0xef | ||
| 42 | #define CHARGERUSB_VICHRG_PC 0xdd | ||
| 43 | #define CONTROLLER_WDG 0xe2 | ||
| 44 | #define LINEAR_CHRG_STS 0xde | ||
| 45 | |||
| 46 | #define TPS80031_VBUS_DET BIT(2) | ||
| 47 | #define TPS80031_VAC_DET BIT(3) | ||
| 48 | |||
| 49 | struct tps80031_charger { | ||
| 50 | int max_charge_current_mA; | ||
| 51 | int max_charge_volt_mV; | ||
| 52 | struct device *dev; | ||
| 53 | struct regulator_dev *rdev; | ||
| 54 | struct regulator_desc reg_desc; | ||
| 55 | struct regulator_init_data reg_init_data; | ||
| 56 | struct tps80031_charger_platform_data *pdata; | ||
| 57 | int (*board_init)(void *board_data); | ||
| 58 | void *board_data; | ||
| 59 | int irq_base; | ||
| 60 | int watch_time_sec; | ||
| 61 | enum charging_states state; | ||
| 62 | int charging_term_current_mA; | ||
| 63 | charging_callback_t charger_cb; | ||
| 64 | void *charger_cb_data; | ||
| 65 | }; | ||
| 66 | |||
| 67 | static struct tps80031_charger *charger_data; | ||
| 68 | static uint8_t charging_current_val_code[] = { | ||
| 69 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0x27, | ||
| 70 | 0x37, 0x28, 0x38, 0x29, 0x39, 0x2A, 0x3A, 0x2B, 0x3B, 0x2C, | ||
| 71 | 0x3C, 0x2D, 0x3D, 0x2E, | ||
| 72 | }; | ||
| 73 | |||
| 74 | static int set_charge_current_limit(struct regulator_dev *rdev, | ||
| 75 | int min_uA, int max_uA) | ||
| 76 | { | ||
| 77 | struct tps80031_charger *charger = rdev_get_drvdata(rdev); | ||
| 78 | int max_vbus_current = 1500; | ||
| 79 | int max_charge_current = 1500; | ||
| 80 | int ret; | ||
| 81 | |||
| 82 | dev_info(charger->dev, "%s(): Min curr %dmA and max current %dmA\n", | ||
| 83 | __func__, min_uA/1000, max_uA/1000); | ||
| 84 | |||
| 85 | if (!max_uA) { | ||
| 86 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 87 | CONTROLLER_CTRL1, 0x0); | ||
| 88 | if (ret < 0) | ||
| 89 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 90 | __func__, CONTROLLER_CTRL1); | ||
| 91 | |||
| 92 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 93 | CONTROLLER_WDG, 0x0); | ||
| 94 | if (ret < 0) | ||
| 95 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 96 | __func__, CONTROLLER_WDG); | ||
| 97 | charger->state = charging_state_charging_stopped; | ||
| 98 | if (charger->charger_cb) | ||
| 99 | charger->charger_cb(charger->state, | ||
| 100 | charger->charger_cb_data); | ||
| 101 | return ret; | ||
| 102 | } | ||
| 103 | |||
| 104 | max_vbus_current = min(max_uA/1000, max_vbus_current); | ||
| 105 | max_vbus_current = max_vbus_current/50; | ||
| 106 | if (max_vbus_current) | ||
| 107 | max_vbus_current--; | ||
| 108 | ret = tps80031_update(charger->dev->parent, SLAVE_ID2, | ||
| 109 | CHARGERUSB_CINLIMIT, | ||
| 110 | charging_current_val_code[max_vbus_current], 0x3F); | ||
| 111 | if (ret < 0) { | ||
| 112 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 113 | __func__, CHARGERUSB_CINLIMIT); | ||
| 114 | return ret; | ||
| 115 | } | ||
| 116 | |||
| 117 | max_charge_current = min(max_uA/1000, max_charge_current); | ||
| 118 | if (max_charge_current <= 300) | ||
| 119 | max_charge_current = 0; | ||
| 120 | else if ((max_charge_current > 300) && (max_charge_current <= 500)) | ||
| 121 | max_charge_current = (max_charge_current - 300)/50; | ||
| 122 | else | ||
| 123 | max_charge_current = (max_charge_current - 500) / 100 + 4; | ||
| 124 | ret = tps80031_update(charger->dev->parent, SLAVE_ID2, | ||
| 125 | CHARGERUSB_VICHRG, (uint8_t)max_charge_current, 0xF); | ||
| 126 | if (ret < 0) { | ||
| 127 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 128 | __func__, CHARGERUSB_VICHRG); | ||
| 129 | return ret; | ||
| 130 | } | ||
| 131 | |||
| 132 | /* Enable watchdog timer */ | ||
| 133 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 134 | CONTROLLER_WDG, charger->watch_time_sec); | ||
| 135 | if (ret < 0) { | ||
| 136 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 137 | __func__, CONTROLLER_WDG); | ||
| 138 | return ret; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Enable the charging */ | ||
| 142 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 143 | CONTROLLER_CTRL1, 0x30); | ||
| 144 | if (ret < 0) { | ||
| 145 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 146 | __func__, CONTROLLER_CTRL1); | ||
| 147 | return ret; | ||
| 148 | } | ||
| 149 | charger->state = charging_state_charging_in_progress; | ||
| 150 | if (charger->charger_cb) | ||
| 151 | charger->charger_cb(charger->state, | ||
| 152 | charger->charger_cb_data); | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static struct regulator_ops tegra_regulator_ops = { | ||
| 157 | .set_current_limit = set_charge_current_limit, | ||
| 158 | }; | ||
| 159 | |||
| 160 | int register_charging_state_callback(charging_callback_t cb, void *args) | ||
| 161 | { | ||
| 162 | struct tps80031_charger *charger = charger_data; | ||
| 163 | if (!charger_data) | ||
| 164 | return -ENODEV; | ||
| 165 | |||
| 166 | charger->charger_cb = cb; | ||
| 167 | charger->charger_cb_data = args; | ||
| 168 | return 0; | ||
| 169 | } | ||
| 170 | EXPORT_SYMBOL_GPL(register_charging_state_callback); | ||
| 171 | |||
| 172 | static int configure_charging_parameter(struct tps80031_charger *charger) | ||
| 173 | { | ||
| 174 | int ret; | ||
| 175 | int max_charge_current; | ||
| 176 | int max_charge_volt; | ||
| 177 | int term_current; | ||
| 178 | |||
| 179 | /* Disable watchdog timer */ | ||
| 180 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 181 | CONTROLLER_WDG, 0x0); | ||
| 182 | if (ret < 0) { | ||
| 183 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 184 | __func__, CONTROLLER_WDG); | ||
| 185 | return ret; | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Disable the charging if any */ | ||
| 189 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 190 | CONTROLLER_CTRL1, 0x0); | ||
| 191 | if (ret < 0) { | ||
| 192 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 193 | __func__, CONTROLLER_CTRL1); | ||
| 194 | return ret; | ||
| 195 | } | ||
| 196 | |||
| 197 | if (charger->board_init) { | ||
| 198 | ret = charger->board_init(charger->board_data); | ||
| 199 | if (ret < 0) { | ||
| 200 | dev_err(charger->dev, "%s(): Failed in board init\n", | ||
| 201 | __func__); | ||
| 202 | return ret; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /* Unlock value */ | ||
| 207 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 208 | CHARGERUSB_CTRLLIMIT2, 0); | ||
| 209 | if (ret < 0) { | ||
| 210 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 211 | __func__, CHARGERUSB_CTRLLIMIT2); | ||
| 212 | return ret; | ||
| 213 | } | ||
| 214 | |||
| 215 | /* Set max current limit */ | ||
| 216 | max_charge_current = min(1500, charger->max_charge_current_mA); | ||
| 217 | if (max_charge_current < 100) | ||
| 218 | max_charge_current = 0; | ||
| 219 | else | ||
| 220 | max_charge_current = (max_charge_current - 100)/100; | ||
| 221 | max_charge_current &= 0xF; | ||
| 222 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 223 | CHARGERUSB_CTRLLIMIT2, (uint8_t)max_charge_current); | ||
| 224 | if (ret < 0) { | ||
| 225 | dev_err(charger->dev, "%s(): Failed in writing register " | ||
| 226 | "0x%02x\n", __func__, CHARGERUSB_CTRLLIMIT2); | ||
| 227 | return ret; | ||
| 228 | } | ||
| 229 | |||
| 230 | /* Set max voltage limit */ | ||
| 231 | max_charge_volt = min(4760, charger->max_charge_volt_mV); | ||
| 232 | max_charge_volt = max(3500, max_charge_volt); | ||
| 233 | max_charge_volt -= 3500; | ||
| 234 | max_charge_volt = max_charge_volt/20; | ||
| 235 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 236 | CHARGERUSB_CTRLLIMIT1, (uint8_t)max_charge_volt); | ||
| 237 | if (ret < 0) { | ||
| 238 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 239 | __func__, CHARGERUSB_CTRLLIMIT1); | ||
| 240 | return ret; | ||
| 241 | } | ||
| 242 | |||
| 243 | /* Lock value */ | ||
| 244 | ret = tps80031_set_bits(charger->dev->parent, SLAVE_ID2, | ||
| 245 | CHARGERUSB_CTRLLIMIT2, (1 << 4)); | ||
| 246 | if (ret < 0) { | ||
| 247 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 248 | __func__, CHARGERUSB_CTRLLIMIT2); | ||
| 249 | return ret; | ||
| 250 | } | ||
| 251 | |||
| 252 | /* set Pre Charge current to 400mA */ | ||
| 253 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, 0xDE, 0x3); | ||
| 254 | if (ret < 0) { | ||
| 255 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 256 | __func__, 0xDD); | ||
| 257 | return ret; | ||
| 258 | } | ||
| 259 | |||
| 260 | /* set charging termination current*/ | ||
| 261 | if (charger->charging_term_current_mA > 400) | ||
| 262 | term_current = 7; | ||
| 263 | else | ||
| 264 | term_current = (charger->charging_term_current_mA - 50)/50; | ||
| 265 | term_current = term_current << 5; | ||
| 266 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 267 | CHARGERUSB_CTRL2, term_current); | ||
| 268 | if (ret < 0) { | ||
| 269 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 270 | __func__, CHARGERUSB_CTRL2); | ||
| 271 | return ret; | ||
| 272 | } | ||
| 273 | |||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | |||
| 277 | static irqreturn_t linch_status_isr(int irq, void *dev_id) | ||
| 278 | { | ||
| 279 | struct tps80031_charger *charger = dev_id; | ||
| 280 | uint8_t linch_status; | ||
| 281 | int ret; | ||
| 282 | dev_info(charger->dev, "%s() got called\n", __func__); | ||
| 283 | |||
| 284 | ret = tps80031_read(charger->dev->parent, SLAVE_ID2, | ||
| 285 | LINEAR_CHRG_STS, &linch_status); | ||
| 286 | if (ret < 0) { | ||
| 287 | dev_err(charger->dev, "%s(): Failed in reading register 0x%02x\n", | ||
| 288 | __func__, LINEAR_CHRG_STS); | ||
| 289 | } else { | ||
| 290 | dev_info(charger->dev, "%s():The status of LINEAR_CHRG_STS is 0x%02x\n", | ||
| 291 | __func__, linch_status); | ||
| 292 | if (linch_status & 0x20) { | ||
| 293 | charger->state = charging_state_charging_completed; | ||
| 294 | if (charger->charger_cb) | ||
| 295 | charger->charger_cb(charger->state, | ||
| 296 | charger->charger_cb_data); | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | return IRQ_HANDLED; | ||
| 301 | } | ||
| 302 | |||
| 303 | static irqreturn_t watchdog_expire_isr(int irq, void *dev_id) | ||
| 304 | { | ||
| 305 | struct tps80031_charger *charger = dev_id; | ||
| 306 | int ret; | ||
| 307 | |||
| 308 | dev_info(charger->dev, "%s()\n", __func__); | ||
| 309 | if (charger->state != charging_state_charging_in_progress) | ||
| 310 | return IRQ_HANDLED; | ||
| 311 | |||
| 312 | /* Enable watchdog timer again*/ | ||
| 313 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, CONTROLLER_WDG, | ||
| 314 | charger->watch_time_sec); | ||
| 315 | if (ret < 0) | ||
| 316 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
| 317 | __func__, CONTROLLER_WDG); | ||
| 318 | |||
| 319 | /* Rewrite to enable the charging */ | ||
| 320 | if (!ret) { | ||
| 321 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
| 322 | CONTROLLER_CTRL1, 0x30); | ||
| 323 | if (ret < 0) | ||
| 324 | dev_err(charger->dev, "%s(): Failed in writing " | ||
| 325 | "register 0x%02x\n", | ||
| 326 | __func__, CONTROLLER_CTRL1); | ||
| 327 | } | ||
| 328 | return IRQ_HANDLED; | ||
| 329 | } | ||
| 330 | |||
| 331 | static int tps80031_charger_probe(struct platform_device *pdev) | ||
| 332 | { | ||
| 333 | int ret = 0; | ||
| 334 | struct device *dev = &pdev->dev; | ||
| 335 | struct tps80031_charger *charger; | ||
| 336 | struct tps80031_charger_platform_data *pdata = pdev->dev.platform_data; | ||
| 337 | |||
| 338 | dev_info(dev, "%s()\n", __func__); | ||
| 339 | |||
| 340 | if (!pdata) { | ||
| 341 | dev_err(dev, "%s() No platform data, exiting..\n", __func__); | ||
| 342 | return -ENODEV; | ||
| 343 | } | ||
| 344 | |||
| 345 | if (!pdata->num_consumer_supplies) { | ||
| 346 | dev_err(dev, "%s() No consumer supply list, exiting..\n", | ||
| 347 | __func__); | ||
| 348 | return -ENODEV; | ||
| 349 | } | ||
| 350 | |||
| 351 | charger = kzalloc(sizeof(*charger), GFP_KERNEL); | ||
| 352 | if (!charger) { | ||
| 353 | dev_err(dev, "failed to allocate memory status\n"); | ||
| 354 | return -ENOMEM; | ||
| 355 | } | ||
| 356 | |||
| 357 | charger->dev = &pdev->dev; | ||
| 358 | |||
| 359 | charger->max_charge_current_mA = (pdata->max_charge_current_mA) ? | ||
| 360 | pdata->max_charge_current_mA : 1000; | ||
| 361 | charger->max_charge_volt_mV = (pdata->max_charge_volt_mV) ? | ||
| 362 | pdata->max_charge_volt_mV : 4200; | ||
| 363 | charger->irq_base = pdata->irq_base; | ||
| 364 | charger->watch_time_sec = min(pdata->watch_time_sec, 127); | ||
| 365 | if (!charger->watch_time_sec) | ||
| 366 | charger->watch_time_sec = 127; | ||
| 367 | charger->charging_term_current_mA = | ||
| 368 | min(50, pdata->charging_term_current_mA); | ||
| 369 | if (charger->charging_term_current_mA < 50) | ||
| 370 | charger->charging_term_current_mA = 50; | ||
| 371 | |||
| 372 | charger->reg_desc.name = "vbus_charger"; | ||
| 373 | charger->reg_desc.id = pdata->regulator_id; | ||
| 374 | charger->reg_desc.ops = &tegra_regulator_ops; | ||
| 375 | charger->reg_desc.type = REGULATOR_CURRENT; | ||
| 376 | charger->reg_desc.owner = THIS_MODULE; | ||
| 377 | |||
| 378 | charger->reg_init_data.supply_regulator = NULL; | ||
| 379 | charger->reg_init_data.num_consumer_supplies = | ||
| 380 | pdata->num_consumer_supplies; | ||
| 381 | charger->reg_init_data.consumer_supplies = pdata->consumer_supplies; | ||
| 382 | charger->reg_init_data.regulator_init = NULL; | ||
| 383 | charger->reg_init_data.driver_data = charger; | ||
| 384 | charger->reg_init_data.constraints.name = "vbus_charger"; | ||
| 385 | charger->reg_init_data.constraints.min_uA = 0; | ||
| 386 | charger->reg_init_data.constraints.max_uA = | ||
| 387 | pdata->max_charge_current_mA * 1000; | ||
| 388 | charger->reg_init_data.constraints.valid_modes_mask = | ||
| 389 | REGULATOR_MODE_NORMAL | | ||
| 390 | REGULATOR_MODE_STANDBY; | ||
| 391 | charger->reg_init_data.constraints.valid_ops_mask = | ||
| 392 | REGULATOR_CHANGE_MODE | | ||
| 393 | REGULATOR_CHANGE_STATUS | | ||
| 394 | REGULATOR_CHANGE_CURRENT; | ||
| 395 | |||
| 396 | charger->board_init = pdata->board_init; | ||
| 397 | charger->board_data = pdata->board_data; | ||
| 398 | charger->state = charging_state_idle; | ||
| 399 | |||
| 400 | charger->rdev = regulator_register(&charger->reg_desc, &pdev->dev, | ||
| 401 | &charger->reg_init_data, charger); | ||
| 402 | if (IS_ERR(charger->rdev)) { | ||
| 403 | dev_err(&pdev->dev, "failed to register %s\n", | ||
| 404 | charger->reg_desc.name); | ||
| 405 | ret = PTR_ERR(charger->rdev); | ||
| 406 | goto regulator_fail; | ||
| 407 | } | ||
| 408 | |||
| 409 | ret = request_threaded_irq(charger->irq_base + TPS80031_INT_LINCH_GATED, | ||
| 410 | NULL, linch_status_isr, 0, "tps80031-linch", charger); | ||
| 411 | if (ret) { | ||
| 412 | dev_err(&pdev->dev, "Unable to register irq %d; error %d\n", | ||
| 413 | charger->irq_base + TPS80031_INT_LINCH_GATED, ret); | ||
| 414 | goto irq_linch_fail; | ||
| 415 | } | ||
| 416 | |||
| 417 | ret = request_threaded_irq(charger->irq_base + TPS80031_INT_FAULT_WDG, | ||
| 418 | NULL, watchdog_expire_isr, 0, "tps80031-wdg", charger); | ||
| 419 | if (ret) { | ||
| 420 | dev_err(&pdev->dev, "Unable to register irq %d; error %d\n", | ||
| 421 | charger->irq_base + TPS80031_INT_FAULT_WDG, ret); | ||
| 422 | goto irq_wdg_fail; | ||
| 423 | } | ||
| 424 | |||
| 425 | ret = configure_charging_parameter(charger); | ||
| 426 | if (ret) | ||
| 427 | goto config_fail; | ||
| 428 | |||
| 429 | dev_set_drvdata(&pdev->dev, charger); | ||
| 430 | charger_data = charger; | ||
| 431 | return ret; | ||
| 432 | |||
| 433 | config_fail: | ||
| 434 | free_irq(charger->irq_base + TPS80031_INT_FAULT_WDG, charger); | ||
| 435 | irq_wdg_fail: | ||
| 436 | free_irq(charger->irq_base + TPS80031_INT_LINCH_GATED, charger); | ||
| 437 | irq_linch_fail: | ||
| 438 | regulator_unregister(charger->rdev); | ||
| 439 | regulator_fail: | ||
| 440 | kfree(charger); | ||
| 441 | return ret; | ||
| 442 | } | ||
| 443 | |||
| 444 | static int tps80031_charger_remove(struct platform_device *pdev) | ||
| 445 | { | ||
| 446 | struct tps80031_charger *charger = dev_get_drvdata(&pdev->dev); | ||
| 447 | |||
| 448 | regulator_unregister(charger->rdev); | ||
| 449 | kfree(charger); | ||
| 450 | return 0; | ||
| 451 | } | ||
| 452 | |||
| 453 | static struct platform_driver tps80031_charger_driver = { | ||
| 454 | .driver = { | ||
| 455 | .name = "tps80031-charger", | ||
| 456 | .owner = THIS_MODULE, | ||
| 457 | }, | ||
| 458 | .probe = tps80031_charger_probe, | ||
| 459 | .remove = tps80031_charger_remove, | ||
| 460 | }; | ||
| 461 | |||
| 462 | static int __init tps80031_charger_init(void) | ||
| 463 | { | ||
| 464 | return platform_driver_register(&tps80031_charger_driver); | ||
| 465 | } | ||
| 466 | |||
| 467 | static void __exit tps80031_charger_exit(void) | ||
| 468 | { | ||
| 469 | platform_driver_unregister(&tps80031_charger_driver); | ||
| 470 | } | ||
| 471 | |||
| 472 | subsys_initcall(tps80031_charger_init); | ||
| 473 | module_exit(tps80031_charger_exit); | ||
| 474 | |||
| 475 | MODULE_LICENSE("GPL"); | ||
| 476 | MODULE_DESCRIPTION("tps80031 battery charger driver"); | ||
diff --git a/drivers/power/tps80031_battery_gauge.c b/drivers/power/tps80031_battery_gauge.c new file mode 100644 index 00000000000..9ccfaad4144 --- /dev/null +++ b/drivers/power/tps80031_battery_gauge.c | |||
| @@ -0,0 +1,606 @@ | |||
| 1 | /* | ||
| 2 | * drivers/power/tps80031_battery_gauge.c | ||
| 3 | * | ||
| 4 | * Gas Gauge driver for TI's tps80031 | ||
| 5 | * | ||
| 6 | * Copyright (c) 2011, NVIDIA Corporation. | ||
| 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, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along | ||
| 19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 21 | */ | ||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/platform_device.h> | ||
| 24 | #include <linux/regulator/driver.h> | ||
| 25 | #include <linux/err.h> | ||
| 26 | #include <linux/regulator/machine.h> | ||
| 27 | #include <linux/mutex.h> | ||
| 28 | |||
| 29 | #include <linux/module.h> | ||
| 30 | #include <linux/param.h> | ||
| 31 | #include <linux/jiffies.h> | ||
| 32 | #include <linux/workqueue.h> | ||
| 33 | #include <linux/pm.h> | ||
| 34 | #include <linux/i2c.h> | ||
| 35 | #include <linux/slab.h> | ||
| 36 | #include <linux/platform_device.h> | ||
| 37 | #include <linux/power_supply.h> | ||
| 38 | |||
| 39 | #include <linux/mfd/core.h> | ||
| 40 | #include <linux/mfd/tps80031.h> | ||
| 41 | #include <linux/tps80031-charger.h> | ||
| 42 | |||
| 43 | #define CHARGERUSB_CINLIMIT 0xee | ||
| 44 | #define CONTROLLER_STAT1 0xe3 | ||
| 45 | #define LINEAR_CHARGE_STS 0xde | ||
| 46 | #define STS_HW_CONDITIONS 0x21 | ||
| 47 | #define TOGGLE1 0x90 | ||
| 48 | #define TOGGLE1_FGS BIT(5) | ||
| 49 | #define TOGGLE1_GPADCR BIT(1) | ||
| 50 | #define GPCH0_LSB 0x3b | ||
| 51 | #define GPCH0_MSB 0x3c | ||
| 52 | #define GPCH0_MSB_COLLISION_GP BIT(4) | ||
| 53 | #define GPSELECT_ISB 0x35 | ||
| 54 | #define GPADC_CTRL 0x2e | ||
| 55 | #define MISC1 0xe4 | ||
| 56 | #define CTRL_P1 0x36 | ||
| 57 | #define CTRL_P1_SP1 BIT(3) | ||
| 58 | #define CTRL_P1_EOCRT BIT(2) | ||
| 59 | #define CTRL_P1_EOCP1 BIT(1) | ||
| 60 | #define CTRL_P1_BUSY BIT(0) | ||
| 61 | #define FG_REG_00 0xc0 | ||
| 62 | #define FG_REG_00_CC_CAL_EN BIT(1) | ||
| 63 | #define FG_REG_00_CC_AUTOCLEAR BIT(2) | ||
| 64 | #define FG_REG_01 0xc1 /* CONV_NR (unsigned) 0 - 7 */ | ||
| 65 | #define FG_REG_02 0xc2 /* CONV_NR (unsigned) 8 - 15 */ | ||
| 66 | #define FG_REG_03 0xc3 /* CONV_NR (unsigned) 16 - 23 */ | ||
| 67 | #define FG_REG_04 0xc4 /* ACCM (signed) 0 - 7 */ | ||
| 68 | #define FG_REG_05 0xc5 /* ACCM (signed) 8 - 15 */ | ||
| 69 | #define FG_REG_06 0xc6 /* ACCM (signed) 16 - 23 */ | ||
| 70 | #define FG_REG_07 0xc7 /* ACCM (signed) 24 - 31 */ | ||
| 71 | #define FG_REG_08 0xc8 /* OFFSET (signed) 0 - 7 */ | ||
| 72 | #define FG_REG_09 0xc9 /* OFFSET (signed) 8 - 9 */ | ||
| 73 | #define FG_REG_10 0xca /* LAST_READ (signed) 0 - 7 */ | ||
| 74 | #define FG_REG_11 0xcb /* LAST_READ (signed) 8 - 13 */ | ||
| 75 | |||
| 76 | #define TPS80031_VBUS_DET BIT(2) | ||
| 77 | #define TPS80031_VAC_DET BIT(3) | ||
| 78 | #define TPS80031_STS_VYSMIN_HI BIT(4) | ||
| 79 | #define END_OF_CHARGE BIT(5) | ||
| 80 | |||
| 81 | #define DRIVER_VERSION "1.1.0" | ||
| 82 | #define BATTERY_POLL_PERIOD 30000 | ||
| 83 | |||
| 84 | static int tps80031_temp_table[] = { | ||
| 85 | /* adc code for temperature in degree C */ | ||
| 86 | 929, 925, /* -2, -1 */ | ||
| 87 | 920, 917, 912, 908, 904, 899, 895, 890, 885, 880, /* 00 - 09 */ | ||
| 88 | 875, 869, 864, 858, 853, 847, 841, 835, 829, 823, /* 10 - 19 */ | ||
| 89 | 816, 810, 804, 797, 790, 783, 776, 769, 762, 755, /* 20 - 29 */ | ||
| 90 | 748, 740, 732, 725, 718, 710, 703, 695, 687, 679, /* 30 - 39 */ | ||
| 91 | 671, 663, 655, 647, 639, 631, 623, 615, 607, 599, /* 40 - 49 */ | ||
| 92 | 591, 583, 575, 567, 559, 551, 543, 535, 527, 519, /* 50 - 59 */ | ||
| 93 | 511, 504, 496 /* 60 - 62 */ | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct tps80031_device_info { | ||
| 97 | struct device *dev; | ||
| 98 | struct i2c_client *client; | ||
| 99 | struct power_supply bat; | ||
| 100 | struct power_supply ac; | ||
| 101 | struct power_supply usb; | ||
| 102 | struct timer_list battery_poll_timer; | ||
| 103 | uint32_t vsys; | ||
| 104 | uint8_t usb_online; | ||
| 105 | uint8_t ac_online; | ||
| 106 | uint8_t usb_status; | ||
| 107 | uint8_t capacity_sts; | ||
| 108 | uint8_t health; | ||
| 109 | uint8_t sys_vlow_intr; | ||
| 110 | uint8_t fg_calib_intr; | ||
| 111 | int16_t fg_offset; | ||
| 112 | struct mutex adc_lock; | ||
| 113 | }; | ||
| 114 | |||
| 115 | static enum power_supply_property tps80031_bat_props[] = { | ||
| 116 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
| 117 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
| 118 | POWER_SUPPLY_PROP_TEMP, | ||
| 119 | POWER_SUPPLY_PROP_CAPACITY, | ||
| 120 | POWER_SUPPLY_PROP_HEALTH, | ||
| 121 | POWER_SUPPLY_PROP_STATUS, | ||
| 122 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
| 123 | POWER_SUPPLY_PROP_CHARGE_COUNTER, | ||
| 124 | }; | ||
| 125 | |||
| 126 | static enum power_supply_property tps80031_usb_props[] = { | ||
| 127 | POWER_SUPPLY_PROP_ONLINE, | ||
| 128 | }; | ||
| 129 | |||
| 130 | static enum power_supply_property tps80031_ac_props[] = { | ||
| 131 | POWER_SUPPLY_PROP_ONLINE, | ||
| 132 | }; | ||
| 133 | |||
| 134 | static int tps80031_reg_read(struct tps80031_device_info *di, int sid, int reg, | ||
| 135 | uint8_t *val) | ||
| 136 | { | ||
| 137 | int ret; | ||
| 138 | |||
| 139 | ret = tps80031_read(di->dev->parent, sid, reg, val); | ||
| 140 | if (ret < 0) | ||
| 141 | dev_err(di->dev, "Failed read register 0x%02x\n", | ||
| 142 | reg); | ||
| 143 | return ret; | ||
| 144 | } | ||
| 145 | |||
| 146 | static int tps80031_reg_write(struct tps80031_device_info *di, int sid, int reg, | ||
| 147 | uint8_t val) | ||
| 148 | { | ||
| 149 | int ret; | ||
| 150 | |||
| 151 | ret = tps80031_write(di->dev->parent, sid, reg, val); | ||
| 152 | if (ret < 0) | ||
| 153 | dev_err(di->dev, "Failed write register 0x%02x\n", | ||
| 154 | reg); | ||
| 155 | return ret; | ||
| 156 | } | ||
| 157 | |||
| 158 | static int tps80031_battery_capacity(struct tps80031_device_info *di, | ||
| 159 | union power_supply_propval *val) | ||
| 160 | { | ||
| 161 | uint8_t hwsts; | ||
| 162 | int ret; | ||
| 163 | |||
| 164 | ret = tps80031_reg_read(di, SLAVE_ID2, LINEAR_CHARGE_STS, &hwsts); | ||
| 165 | if (ret < 0) | ||
| 166 | return ret; | ||
| 167 | |||
| 168 | di->capacity_sts = di->vsys; | ||
| 169 | if (hwsts & END_OF_CHARGE) | ||
| 170 | di->capacity_sts = 100; | ||
| 171 | |||
| 172 | if (di->sys_vlow_intr) { | ||
| 173 | di->capacity_sts = 10; | ||
| 174 | di->sys_vlow_intr = 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | if (di->capacity_sts <= 10) | ||
| 178 | di->health = POWER_SUPPLY_HEALTH_DEAD; | ||
| 179 | else | ||
| 180 | di->health = POWER_SUPPLY_HEALTH_GOOD; | ||
| 181 | |||
| 182 | return di->capacity_sts; | ||
| 183 | } | ||
| 184 | |||
| 185 | static int tps80031_battery_voltage(struct tps80031_device_info *di, | ||
| 186 | union power_supply_propval *val) | ||
| 187 | { | ||
| 188 | int voltage; | ||
| 189 | |||
| 190 | voltage = tps80031_gpadc_conversion(SYSTEM_SUPPLY); | ||
| 191 | if (voltage < 0) | ||
| 192 | return voltage; | ||
| 193 | voltage = ((voltage * 1000) / 4) * 5; | ||
| 194 | |||
| 195 | if (voltage < 3700000) | ||
| 196 | di->vsys = 10; | ||
| 197 | else if (voltage > 3700000 && voltage <= 3800000) | ||
| 198 | di->vsys = 20; | ||
| 199 | else if (voltage > 3800000 && voltage <= 3900000) | ||
| 200 | di->vsys = 50; | ||
| 201 | else if (voltage > 3900000 && voltage <= 4000000) | ||
| 202 | di->vsys = 75; | ||
| 203 | else if (voltage >= 4000000) | ||
| 204 | di->vsys = 90; | ||
| 205 | |||
| 206 | return voltage; | ||
| 207 | } | ||
| 208 | |||
| 209 | static int tps80031_battery_charge_now(struct tps80031_device_info *di, | ||
| 210 | union power_supply_propval *val) | ||
| 211 | { | ||
| 212 | int charge; | ||
| 213 | |||
| 214 | charge = tps80031_gpadc_conversion(BATTERY_CHARGING_CURRENT); | ||
| 215 | if (charge < 0) | ||
| 216 | return charge; | ||
| 217 | charge = charge * 78125 / 40; | ||
| 218 | |||
| 219 | return charge; | ||
| 220 | } | ||
| 221 | |||
| 222 | static int tps80031_battery_charge_counter(struct tps80031_device_info *di, | ||
| 223 | union power_supply_propval *val) | ||
| 224 | { | ||
| 225 | int retval, ret; | ||
| 226 | uint32_t cnt_byte; | ||
| 227 | uint32_t acc_byte; | ||
| 228 | |||
| 229 | /* check if calibrated */ | ||
| 230 | if (di->fg_calib_intr == 0) | ||
| 231 | return 0; | ||
| 232 | |||
| 233 | /* get current accumlator */ | ||
| 234 | ret = tps80031_reads(di->dev->parent, SLAVE_ID2, FG_REG_04, 4, | ||
| 235 | (uint8_t *) &acc_byte); | ||
| 236 | if (ret < 0) | ||
| 237 | return ret; | ||
| 238 | /* counter value is mAs, need report uAh */ | ||
| 239 | retval = (int32_t) acc_byte / 18 * 5; | ||
| 240 | |||
| 241 | /* get counter */ | ||
| 242 | ret = tps80031_reads(di->dev->parent, SLAVE_ID2, FG_REG_01, 3, | ||
| 243 | (uint8_t *) &cnt_byte); | ||
| 244 | if (ret < 0) | ||
| 245 | return ret; | ||
| 246 | /* we need calibrate the offset current in uAh*/ | ||
| 247 | retval = retval - (di->fg_offset / 4 * cnt_byte); | ||
| 248 | |||
| 249 | /* @todo, counter value will overflow if battery get continuously | ||
| 250 | * charged discharged for more than 108Ah using 250mS integration | ||
| 251 | * period althrough it is hightly impossible. | ||
| 252 | */ | ||
| 253 | |||
| 254 | return retval; | ||
| 255 | } | ||
| 256 | |||
| 257 | static int tps80031_battery_temp(struct tps80031_device_info *di, | ||
| 258 | union power_supply_propval *val) | ||
| 259 | { | ||
| 260 | int adc_code, temp; | ||
| 261 | |||
| 262 | adc_code = tps80031_gpadc_conversion(BATTERY_TEMPERATURE); | ||
| 263 | if (adc_code < 0) | ||
| 264 | return adc_code; | ||
| 265 | |||
| 266 | for (temp = 0; temp < ARRAY_SIZE(tps80031_temp_table); temp++) { | ||
| 267 | if (adc_code >= tps80031_temp_table[temp]) | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | /* first 2 values are for negative temperature */ | ||
| 271 | val->intval = (temp - 2) * 10; /* in tenths of degree Celsius */ | ||
| 272 | |||
| 273 | return val->intval; | ||
| 274 | } | ||
| 275 | |||
| 276 | #define to_tps80031_device_info_bat(x) container_of((x), \ | ||
| 277 | struct tps80031_device_info, bat); | ||
| 278 | |||
| 279 | static int tps80031_bat_get_property(struct power_supply *psy, | ||
| 280 | enum power_supply_property psp, union power_supply_propval *val) | ||
| 281 | { | ||
| 282 | struct tps80031_device_info *di = to_tps80031_device_info_bat(psy); | ||
| 283 | |||
| 284 | switch (psp) { | ||
| 285 | |||
| 286 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
| 287 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | ||
| 288 | break; | ||
| 289 | |||
| 290 | case POWER_SUPPLY_PROP_HEALTH: | ||
| 291 | val->intval = di->health; | ||
| 292 | break; | ||
| 293 | |||
| 294 | case POWER_SUPPLY_PROP_CAPACITY: | ||
| 295 | val->intval = tps80031_battery_capacity(di, val); | ||
| 296 | break; | ||
| 297 | |||
| 298 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
| 299 | val->intval = tps80031_battery_charge_now(di, val); | ||
| 300 | break; | ||
| 301 | |||
| 302 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
| 303 | val->intval = tps80031_battery_voltage(di, val); | ||
| 304 | break; | ||
| 305 | |||
| 306 | case POWER_SUPPLY_PROP_CHARGE_COUNTER: | ||
| 307 | val->intval = tps80031_battery_charge_counter(di, val); | ||
| 308 | break; | ||
| 309 | |||
| 310 | case POWER_SUPPLY_PROP_STATUS: | ||
| 311 | val->intval = di->usb_status; | ||
| 312 | break; | ||
| 313 | |||
| 314 | case POWER_SUPPLY_PROP_TEMP: | ||
| 315 | val->intval = tps80031_battery_temp(di, val); | ||
| 316 | break; | ||
| 317 | |||
| 318 | default: | ||
| 319 | return -EINVAL; | ||
| 320 | } | ||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | #define to_tps80031_device_info_usb(x) container_of((x), \ | ||
| 325 | struct tps80031_device_info, usb); | ||
| 326 | |||
| 327 | static int tps80031_usb_get_property(struct power_supply *psy, | ||
| 328 | enum power_supply_property psp, union power_supply_propval *val) | ||
| 329 | { | ||
| 330 | struct tps80031_device_info *di = to_tps80031_device_info_usb(psy); | ||
| 331 | |||
| 332 | switch (psp) { | ||
| 333 | case POWER_SUPPLY_PROP_ONLINE: | ||
| 334 | val->intval = di->usb_online; | ||
| 335 | break; | ||
| 336 | default: | ||
| 337 | return -EINVAL; | ||
| 338 | } | ||
| 339 | return 0; | ||
| 340 | } | ||
| 341 | |||
| 342 | #define to_tps80031_device_info_ac(x) container_of((x), \ | ||
| 343 | struct tps80031_device_info, ac); | ||
| 344 | |||
| 345 | static int tps80031_ac_get_property(struct power_supply *psy, | ||
| 346 | enum power_supply_property psp, union power_supply_propval *val) | ||
| 347 | { | ||
| 348 | struct tps80031_device_info *di = to_tps80031_device_info_ac(psy); | ||
| 349 | |||
| 350 | switch (psp) { | ||
| 351 | case POWER_SUPPLY_PROP_ONLINE: | ||
| 352 | val->intval = di->ac_online; | ||
| 353 | break; | ||
| 354 | default: | ||
| 355 | return -EINVAL; | ||
| 356 | } | ||
| 357 | return 0; | ||
| 358 | } | ||
| 359 | |||
| 360 | static irqreturn_t tps80031_sys_vlow(int irq, void *data) | ||
| 361 | { | ||
| 362 | struct tps80031_device_info *di = data; | ||
| 363 | |||
| 364 | di->sys_vlow_intr = 1; | ||
| 365 | power_supply_changed(&di->bat); | ||
| 366 | return IRQ_HANDLED; | ||
| 367 | } | ||
| 368 | |||
| 369 | static irqreturn_t tps80031_fg_calibrated(int irq, void *data) | ||
| 370 | { | ||
| 371 | struct tps80031_device_info *di = data; | ||
| 372 | uint8_t acc_byte0; | ||
| 373 | uint8_t acc_byte1; | ||
| 374 | int ret; | ||
| 375 | |||
| 376 | ret = tps80031_reg_read(di, SLAVE_ID2, FG_REG_08, &acc_byte0); | ||
| 377 | if (ret < 0) | ||
| 378 | return IRQ_HANDLED; | ||
| 379 | ret = tps80031_reg_read(di, SLAVE_ID2, FG_REG_09, &acc_byte1); | ||
| 380 | if (ret < 0) | ||
| 381 | return IRQ_HANDLED; | ||
| 382 | /* sign extension */ | ||
| 383 | if (acc_byte1 & 0x02) | ||
| 384 | acc_byte1 = acc_byte1 | 0xFC; | ||
| 385 | else | ||
| 386 | acc_byte1 = acc_byte1 & 0x03; | ||
| 387 | |||
| 388 | di->fg_offset = (int16_t) ((acc_byte1 << 8) | acc_byte0); | ||
| 389 | /* fuel gauge auto calibration finished */ | ||
| 390 | di->fg_calib_intr = 1; | ||
| 391 | return IRQ_HANDLED; | ||
| 392 | } | ||
| 393 | |||
| 394 | static int tps80031_fg_start_gas_gauge(struct tps80031_device_info *di) | ||
| 395 | { | ||
| 396 | int ret = 0; | ||
| 397 | di->fg_calib_intr = 0; | ||
| 398 | |||
| 399 | /* start gas gauge */ | ||
| 400 | ret = tps80031_reg_write(di, SLAVE_ID2, TOGGLE1, 0x20); | ||
| 401 | if (ret < 0) | ||
| 402 | return ret; | ||
| 403 | /* set ADC update time to 3.9ms and start calibration */ | ||
| 404 | ret = tps80031_reg_write(di, SLAVE_ID2, FG_REG_00, FG_REG_00_CC_CAL_EN); | ||
| 405 | if (ret < 0) | ||
| 406 | return ret; | ||
| 407 | return ret; | ||
| 408 | } | ||
| 409 | |||
| 410 | void tps80031_battery_status(enum charging_states status, void *data) | ||
| 411 | { | ||
| 412 | struct tps80031_device_info *di = data; | ||
| 413 | int ret; | ||
| 414 | uint8_t retval; | ||
| 415 | |||
| 416 | if ((status == charging_state_charging_in_progress)) { | ||
| 417 | di->usb_status = POWER_SUPPLY_STATUS_CHARGING; | ||
| 418 | di->health = POWER_SUPPLY_HEALTH_GOOD; | ||
| 419 | ret = tps80031_reg_read(di, SLAVE_ID2, | ||
| 420 | CHARGERUSB_CINLIMIT, &retval); | ||
| 421 | if (ret < 0) { | ||
| 422 | di->ac_online = 0; | ||
| 423 | di->usb_online = 0; | ||
| 424 | } | ||
| 425 | if (retval == 0x9) { | ||
| 426 | di->ac_online = 0; | ||
| 427 | di->usb_online = 1; | ||
| 428 | } else { | ||
| 429 | di->usb_online = 0; | ||
| 430 | di->ac_online = 1; | ||
| 431 | } | ||
| 432 | } else if (status == charging_state_charging_stopped) { | ||
| 433 | di->usb_status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
| 434 | di->ac_online = 0; | ||
| 435 | di->usb_online = 0; | ||
| 436 | } | ||
| 437 | power_supply_changed(&di->usb); | ||
| 438 | power_supply_changed(&di->bat); | ||
| 439 | power_supply_changed(&di->ac); | ||
| 440 | } | ||
| 441 | |||
| 442 | static void battery_poll_timer_func(unsigned long pdi) | ||
| 443 | { | ||
| 444 | struct tps80031_device_info *di = (void *)pdi; | ||
| 445 | power_supply_changed(&di->bat); | ||
| 446 | mod_timer(&di->battery_poll_timer, | ||
| 447 | jiffies + msecs_to_jiffies(BATTERY_POLL_PERIOD)); | ||
| 448 | } | ||
| 449 | |||
| 450 | static int tps80031_battery_probe(struct platform_device *pdev) | ||
| 451 | { | ||
| 452 | int ret; | ||
| 453 | uint8_t retval; | ||
| 454 | struct device *dev = &pdev->dev; | ||
| 455 | struct tps80031_device_info *di; | ||
| 456 | struct tps80031_bg_platform_data *pdata = pdev->dev.platform_data; | ||
| 457 | |||
| 458 | di = devm_kzalloc(&pdev->dev, sizeof *di, GFP_KERNEL); | ||
| 459 | if (!di) { | ||
| 460 | dev_err(dev->parent, "failed to allocate device info data\n"); | ||
| 461 | return -ENOMEM; | ||
| 462 | } | ||
| 463 | |||
| 464 | if (!pdata->battery_present) { | ||
| 465 | dev_err(dev, "%s() No battery detected, exiting..\n", | ||
| 466 | __func__); | ||
| 467 | return -ENODEV; | ||
| 468 | } | ||
| 469 | |||
| 470 | di->dev = &pdev->dev; | ||
| 471 | |||
| 472 | ret = tps80031_reg_read(di, SLAVE_ID2, CONTROLLER_STAT1, &retval); | ||
| 473 | if (ret < 0) | ||
| 474 | return ret; | ||
| 475 | |||
| 476 | if ((retval & TPS80031_VAC_DET) | (retval & TPS80031_VBUS_DET)) { | ||
| 477 | di->usb_status = POWER_SUPPLY_STATUS_CHARGING; | ||
| 478 | di->usb_online = 1; | ||
| 479 | } else { | ||
| 480 | di->usb_status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
| 481 | di->usb_online = 0; | ||
| 482 | } | ||
| 483 | |||
| 484 | di->capacity_sts = 50; | ||
| 485 | di->health = POWER_SUPPLY_HEALTH_GOOD; | ||
| 486 | |||
| 487 | di->bat.name = "tps80031-bat"; | ||
| 488 | di->bat.type = POWER_SUPPLY_TYPE_BATTERY; | ||
| 489 | di->bat.properties = tps80031_bat_props; | ||
| 490 | di->bat.num_properties = ARRAY_SIZE(tps80031_bat_props); | ||
| 491 | di->bat.get_property = tps80031_bat_get_property; | ||
| 492 | |||
| 493 | ret = power_supply_register(dev->parent, &di->bat); | ||
| 494 | if (ret) { | ||
| 495 | dev_err(dev->parent, "failed to register bat power supply\n"); | ||
| 496 | return ret; | ||
| 497 | } | ||
| 498 | |||
| 499 | di->usb.name = "tps80031-usb"; | ||
| 500 | di->usb.type = POWER_SUPPLY_TYPE_USB; | ||
| 501 | di->usb.properties = tps80031_usb_props; | ||
| 502 | di->usb.num_properties = ARRAY_SIZE(tps80031_usb_props); | ||
| 503 | di->usb.get_property = tps80031_usb_get_property; | ||
| 504 | |||
| 505 | ret = power_supply_register(dev->parent, &di->usb); | ||
| 506 | if (ret) { | ||
| 507 | dev_err(dev->parent, "failed to register ac power supply\n"); | ||
| 508 | goto power_supply_fail2; | ||
| 509 | } | ||
| 510 | |||
| 511 | di->ac.name = "tps80031-ac"; | ||
| 512 | di->ac.type = POWER_SUPPLY_TYPE_MAINS; | ||
| 513 | di->ac.properties = tps80031_ac_props; | ||
| 514 | di->ac.num_properties = ARRAY_SIZE(tps80031_ac_props); | ||
| 515 | di->ac.get_property = tps80031_ac_get_property; | ||
| 516 | |||
| 517 | ret = power_supply_register(dev->parent, &di->ac); | ||
| 518 | if (ret) { | ||
| 519 | dev_err(dev->parent, "failed to register ac power supply\n"); | ||
| 520 | goto power_supply_fail1; | ||
| 521 | } | ||
| 522 | |||
| 523 | dev_set_drvdata(&pdev->dev, di); | ||
| 524 | |||
| 525 | ret = register_charging_state_callback(tps80031_battery_status, di); | ||
| 526 | if (ret < 0) | ||
| 527 | goto power_supply_fail0; | ||
| 528 | |||
| 529 | ret = request_threaded_irq(pdata->irq_base + TPS80031_INT_SYS_VLOW, | ||
| 530 | NULL, tps80031_sys_vlow, | ||
| 531 | IRQF_ONESHOT, "tps80031_sys_vlow", di); | ||
| 532 | if (ret < 0) { | ||
| 533 | dev_err(dev->parent, "request IRQ %d fail\n", pdata->irq_base); | ||
| 534 | goto power_supply_fail0; | ||
| 535 | } | ||
| 536 | |||
| 537 | ret = request_threaded_irq(pdata->irq_base + TPS80031_INT_CC_AUTOCAL, | ||
| 538 | NULL, tps80031_fg_calibrated, IRQF_ONESHOT, | ||
| 539 | "tps80031_fuel_gauge_calibration", di); | ||
| 540 | if (ret < 0) { | ||
| 541 | dev_err(dev->parent, "request IRQ %d fail\n", pdata->irq_base); | ||
| 542 | goto irq_fail2; | ||
| 543 | } | ||
| 544 | setup_timer(&di->battery_poll_timer, | ||
| 545 | battery_poll_timer_func, (unsigned long) di); | ||
| 546 | mod_timer(&di->battery_poll_timer, | ||
| 547 | jiffies + msecs_to_jiffies(BATTERY_POLL_PERIOD)); | ||
| 548 | |||
| 549 | ret = tps80031_fg_start_gas_gauge(di); | ||
| 550 | if (ret < 0) { | ||
| 551 | dev_err(dev->parent, "failed to start fuel-gauge\n"); | ||
| 552 | goto irq_fail1; | ||
| 553 | } | ||
| 554 | dev_info(dev->parent, "support ver. %s enabled\n", DRIVER_VERSION); | ||
| 555 | |||
| 556 | return ret; | ||
| 557 | |||
| 558 | irq_fail1: | ||
| 559 | free_irq(pdata->irq_base + TPS80031_INT_CC_AUTOCAL, di); | ||
| 560 | irq_fail2: | ||
| 561 | free_irq(pdata->irq_base + TPS80031_INT_SYS_VLOW, di); | ||
| 562 | power_supply_fail0: | ||
| 563 | power_supply_unregister(&di->ac); | ||
| 564 | power_supply_fail1: | ||
| 565 | power_supply_unregister(&di->usb); | ||
| 566 | power_supply_fail2: | ||
| 567 | power_supply_unregister(&di->bat); | ||
| 568 | return ret; | ||
| 569 | } | ||
| 570 | |||
| 571 | static int tps80031_battery_remove(struct platform_device *pdev) | ||
| 572 | { | ||
| 573 | struct tps80031_device_info *di = dev_get_drvdata(&pdev->dev); | ||
| 574 | |||
| 575 | power_supply_unregister(&di->bat); | ||
| 576 | power_supply_unregister(&di->usb); | ||
| 577 | power_supply_unregister(&di->ac); | ||
| 578 | |||
| 579 | return 0; | ||
| 580 | } | ||
| 581 | |||
| 582 | static struct platform_driver tps80031_battery_driver = { | ||
| 583 | .driver = { | ||
| 584 | .name = "tps80031-battery-gauge", | ||
| 585 | .owner = THIS_MODULE, | ||
| 586 | }, | ||
| 587 | .probe = tps80031_battery_probe, | ||
| 588 | .remove = tps80031_battery_remove, | ||
| 589 | }; | ||
| 590 | |||
| 591 | static int __init tps80031_battery_init(void) | ||
| 592 | { | ||
| 593 | return platform_driver_register(&tps80031_battery_driver); | ||
| 594 | } | ||
| 595 | |||
| 596 | static void __exit tps80031_battery_exit(void) | ||
| 597 | { | ||
| 598 | platform_driver_unregister(&tps80031_battery_driver); | ||
| 599 | } | ||
| 600 | |||
| 601 | module_init(tps80031_battery_init); | ||
| 602 | module_exit(tps80031_battery_exit); | ||
| 603 | |||
| 604 | MODULE_LICENSE("GPL"); | ||
| 605 | MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com> "); | ||
| 606 | MODULE_DESCRIPTION("tps80031 battery gauge driver"); | ||
