18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Bluewater Systems Ltd 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Ryan Mallon 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/types.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/swab.h> 198c2ecf20Sopenharmony_ci#include <linux/i2c.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/idr.h> 228c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/ds2782_battery.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define DS2782_REG_RARC 0x06 /* Remaining active relative capacity */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define DS278x_REG_VOLT_MSB 0x0c 298c2ecf20Sopenharmony_ci#define DS278x_REG_TEMP_MSB 0x0a 308c2ecf20Sopenharmony_ci#define DS278x_REG_CURRENT_MSB 0x0e 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* EEPROM Block */ 338c2ecf20Sopenharmony_ci#define DS2782_REG_RSNSP 0x69 /* Sense resistor value */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Current unit measurement in uA for a 1 milli-ohm sense resistor */ 368c2ecf20Sopenharmony_ci#define DS2782_CURRENT_UNITS 1563 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define DS2786_REG_RARC 0x02 /* Remaining active relative capacity */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define DS2786_CURRENT_UNITS 25 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define DS278x_DELAY 1000 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct ds278x_info; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct ds278x_battery_ops { 478c2ecf20Sopenharmony_ci int (*get_battery_current)(struct ds278x_info *info, int *current_uA); 488c2ecf20Sopenharmony_ci int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV); 498c2ecf20Sopenharmony_ci int (*get_battery_capacity)(struct ds278x_info *info, int *capacity); 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define to_ds278x_info(x) power_supply_get_drvdata(x) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct ds278x_info { 558c2ecf20Sopenharmony_ci struct i2c_client *client; 568c2ecf20Sopenharmony_ci struct power_supply *battery; 578c2ecf20Sopenharmony_ci struct power_supply_desc battery_desc; 588c2ecf20Sopenharmony_ci const struct ds278x_battery_ops *ops; 598c2ecf20Sopenharmony_ci struct delayed_work bat_work; 608c2ecf20Sopenharmony_ci int id; 618c2ecf20Sopenharmony_ci int rsns; 628c2ecf20Sopenharmony_ci int capacity; 638c2ecf20Sopenharmony_ci int status; /* State Of Charge */ 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic DEFINE_IDR(battery_id); 678c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(battery_lock); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci int ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(info->client, reg); 748c2ecf20Sopenharmony_ci if (ret < 0) { 758c2ecf20Sopenharmony_ci dev_err(&info->client->dev, "register read failed\n"); 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci *val = ret; 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb, 848c2ecf20Sopenharmony_ci s16 *val) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int ret; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(info->client, reg_msb); 898c2ecf20Sopenharmony_ci if (ret < 0) { 908c2ecf20Sopenharmony_ci dev_err(&info->client->dev, "register read failed\n"); 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci *val = swab16(ret); 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int ds278x_get_temp(struct ds278x_info *info, int *temp) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci s16 raw; 1018c2ecf20Sopenharmony_ci int err; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * Temperature is measured in units of 0.125 degrees celcius, the 1058c2ecf20Sopenharmony_ci * power_supply class measures temperature in tenths of degrees 1068c2ecf20Sopenharmony_ci * celsius. The temperature value is stored as a 10 bit number, plus 1078c2ecf20Sopenharmony_ci * sign in the upper bits of a 16 bit register. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw); 1108c2ecf20Sopenharmony_ci if (err) 1118c2ecf20Sopenharmony_ci return err; 1128c2ecf20Sopenharmony_ci *temp = ((raw / 32) * 125) / 100; 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int ds2782_get_current(struct ds278x_info *info, int *current_uA) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int sense_res; 1198c2ecf20Sopenharmony_ci int err; 1208c2ecf20Sopenharmony_ci u8 sense_res_raw; 1218c2ecf20Sopenharmony_ci s16 raw; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* 1248c2ecf20Sopenharmony_ci * The units of measurement for current are dependent on the value of 1258c2ecf20Sopenharmony_ci * the sense resistor. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw); 1288c2ecf20Sopenharmony_ci if (err) 1298c2ecf20Sopenharmony_ci return err; 1308c2ecf20Sopenharmony_ci if (sense_res_raw == 0) { 1318c2ecf20Sopenharmony_ci dev_err(&info->client->dev, "sense resistor value is 0\n"); 1328c2ecf20Sopenharmony_ci return -ENXIO; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci sense_res = 1000 / sense_res_raw; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n", 1378c2ecf20Sopenharmony_ci sense_res); 1388c2ecf20Sopenharmony_ci err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw); 1398c2ecf20Sopenharmony_ci if (err) 1408c2ecf20Sopenharmony_ci return err; 1418c2ecf20Sopenharmony_ci *current_uA = raw * (DS2782_CURRENT_UNITS / sense_res); 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci s16 raw; 1488c2ecf20Sopenharmony_ci int err; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * Voltage is measured in units of 4.88mV. The voltage is stored as 1528c2ecf20Sopenharmony_ci * a 10-bit number plus sign, in the upper bits of a 16-bit register 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); 1558c2ecf20Sopenharmony_ci if (err) 1568c2ecf20Sopenharmony_ci return err; 1578c2ecf20Sopenharmony_ci *voltage_uV = (raw / 32) * 4800; 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int ds2782_get_capacity(struct ds278x_info *info, int *capacity) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int err; 1648c2ecf20Sopenharmony_ci u8 raw; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci err = ds278x_read_reg(info, DS2782_REG_RARC, &raw); 1678c2ecf20Sopenharmony_ci if (err) 1688c2ecf20Sopenharmony_ci return err; 1698c2ecf20Sopenharmony_ci *capacity = raw; 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int ds2786_get_current(struct ds278x_info *info, int *current_uA) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int err; 1768c2ecf20Sopenharmony_ci s16 raw; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw); 1798c2ecf20Sopenharmony_ci if (err) 1808c2ecf20Sopenharmony_ci return err; 1818c2ecf20Sopenharmony_ci *current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns); 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci s16 raw; 1888c2ecf20Sopenharmony_ci int err; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * Voltage is measured in units of 1.22mV. The voltage is stored as 1928c2ecf20Sopenharmony_ci * a 12-bit number plus sign, in the upper bits of a 16-bit register 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); 1958c2ecf20Sopenharmony_ci if (err) 1968c2ecf20Sopenharmony_ci return err; 1978c2ecf20Sopenharmony_ci *voltage_uV = (raw / 8) * 1220; 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int ds2786_get_capacity(struct ds278x_info *info, int *capacity) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci int err; 2048c2ecf20Sopenharmony_ci u8 raw; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci err = ds278x_read_reg(info, DS2786_REG_RARC, &raw); 2078c2ecf20Sopenharmony_ci if (err) 2088c2ecf20Sopenharmony_ci return err; 2098c2ecf20Sopenharmony_ci /* Relative capacity is displayed with resolution 0.5 % */ 2108c2ecf20Sopenharmony_ci *capacity = raw/2 ; 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int ds278x_get_status(struct ds278x_info *info, int *status) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci int err; 2178c2ecf20Sopenharmony_ci int current_uA; 2188c2ecf20Sopenharmony_ci int capacity; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci err = info->ops->get_battery_current(info, ¤t_uA); 2218c2ecf20Sopenharmony_ci if (err) 2228c2ecf20Sopenharmony_ci return err; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci err = info->ops->get_battery_capacity(info, &capacity); 2258c2ecf20Sopenharmony_ci if (err) 2268c2ecf20Sopenharmony_ci return err; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci info->capacity = capacity; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (capacity == 100) 2318c2ecf20Sopenharmony_ci *status = POWER_SUPPLY_STATUS_FULL; 2328c2ecf20Sopenharmony_ci else if (current_uA == 0) 2338c2ecf20Sopenharmony_ci *status = POWER_SUPPLY_STATUS_NOT_CHARGING; 2348c2ecf20Sopenharmony_ci else if (current_uA < 0) 2358c2ecf20Sopenharmony_ci *status = POWER_SUPPLY_STATUS_DISCHARGING; 2368c2ecf20Sopenharmony_ci else 2378c2ecf20Sopenharmony_ci *status = POWER_SUPPLY_STATUS_CHARGING; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int ds278x_battery_get_property(struct power_supply *psy, 2438c2ecf20Sopenharmony_ci enum power_supply_property prop, 2448c2ecf20Sopenharmony_ci union power_supply_propval *val) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct ds278x_info *info = to_ds278x_info(psy); 2478c2ecf20Sopenharmony_ci int ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci switch (prop) { 2508c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 2518c2ecf20Sopenharmony_ci ret = ds278x_get_status(info, &val->intval); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 2558c2ecf20Sopenharmony_ci ret = info->ops->get_battery_capacity(info, &val->intval); 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 2598c2ecf20Sopenharmony_ci ret = info->ops->get_battery_voltage(info, &val->intval); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 2638c2ecf20Sopenharmony_ci ret = info->ops->get_battery_current(info, &val->intval); 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 2678c2ecf20Sopenharmony_ci ret = ds278x_get_temp(info, &val->intval); 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci default: 2718c2ecf20Sopenharmony_ci ret = -EINVAL; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void ds278x_bat_update(struct ds278x_info *info) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci int old_status = info->status; 2808c2ecf20Sopenharmony_ci int old_capacity = info->capacity; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ds278x_get_status(info, &info->status); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if ((old_status != info->status) || (old_capacity != info->capacity)) 2858c2ecf20Sopenharmony_ci power_supply_changed(info->battery); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void ds278x_bat_work(struct work_struct *work) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct ds278x_info *info; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci info = container_of(work, struct ds278x_info, bat_work.work); 2938c2ecf20Sopenharmony_ci ds278x_bat_update(info); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci schedule_delayed_work(&info->bat_work, DS278x_DELAY); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic enum power_supply_property ds278x_battery_props[] = { 2998c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 3008c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 3018c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 3028c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 3038c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 3048c2ecf20Sopenharmony_ci}; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void ds278x_power_supply_init(struct power_supply_desc *battery) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci battery->type = POWER_SUPPLY_TYPE_BATTERY; 3098c2ecf20Sopenharmony_ci battery->properties = ds278x_battery_props; 3108c2ecf20Sopenharmony_ci battery->num_properties = ARRAY_SIZE(ds278x_battery_props); 3118c2ecf20Sopenharmony_ci battery->get_property = ds278x_battery_get_property; 3128c2ecf20Sopenharmony_ci battery->external_power_changed = NULL; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int ds278x_battery_remove(struct i2c_client *client) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct ds278x_info *info = i2c_get_clientdata(client); 3188c2ecf20Sopenharmony_ci int id = info->id; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci power_supply_unregister(info->battery); 3218c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&info->bat_work); 3228c2ecf20Sopenharmony_ci kfree(info->battery_desc.name); 3238c2ecf20Sopenharmony_ci kfree(info); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci mutex_lock(&battery_lock); 3268c2ecf20Sopenharmony_ci idr_remove(&battery_id, id); 3278c2ecf20Sopenharmony_ci mutex_unlock(&battery_lock); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int ds278x_suspend(struct device *dev) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3378c2ecf20Sopenharmony_ci struct ds278x_info *info = i2c_get_clientdata(client); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci cancel_delayed_work(&info->bat_work); 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int ds278x_resume(struct device *dev) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3468c2ecf20Sopenharmony_ci struct ds278x_info *info = i2c_get_clientdata(client); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci schedule_delayed_work(&info->bat_work, DS278x_DELAY); 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ds278x_battery_pm_ops, ds278x_suspend, ds278x_resume); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cienum ds278x_num_id { 3568c2ecf20Sopenharmony_ci DS2782 = 0, 3578c2ecf20Sopenharmony_ci DS2786, 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic const struct ds278x_battery_ops ds278x_ops[] = { 3618c2ecf20Sopenharmony_ci [DS2782] = { 3628c2ecf20Sopenharmony_ci .get_battery_current = ds2782_get_current, 3638c2ecf20Sopenharmony_ci .get_battery_voltage = ds2782_get_voltage, 3648c2ecf20Sopenharmony_ci .get_battery_capacity = ds2782_get_capacity, 3658c2ecf20Sopenharmony_ci }, 3668c2ecf20Sopenharmony_ci [DS2786] = { 3678c2ecf20Sopenharmony_ci .get_battery_current = ds2786_get_current, 3688c2ecf20Sopenharmony_ci .get_battery_voltage = ds2786_get_voltage, 3698c2ecf20Sopenharmony_ci .get_battery_capacity = ds2786_get_capacity, 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int ds278x_battery_probe(struct i2c_client *client, 3748c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct ds278x_platform_data *pdata = client->dev.platform_data; 3778c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 3788c2ecf20Sopenharmony_ci struct ds278x_info *info; 3798c2ecf20Sopenharmony_ci int ret; 3808c2ecf20Sopenharmony_ci int num; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * ds2786 should have the sense resistor value set 3848c2ecf20Sopenharmony_ci * in the platform data 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci if (id->driver_data == DS2786 && !pdata) { 3878c2ecf20Sopenharmony_ci dev_err(&client->dev, "missing platform data for ds2786\n"); 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Get an ID for this battery */ 3928c2ecf20Sopenharmony_ci mutex_lock(&battery_lock); 3938c2ecf20Sopenharmony_ci ret = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL); 3948c2ecf20Sopenharmony_ci mutex_unlock(&battery_lock); 3958c2ecf20Sopenharmony_ci if (ret < 0) 3968c2ecf20Sopenharmony_ci goto fail_id; 3978c2ecf20Sopenharmony_ci num = ret; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 4008c2ecf20Sopenharmony_ci if (!info) { 4018c2ecf20Sopenharmony_ci ret = -ENOMEM; 4028c2ecf20Sopenharmony_ci goto fail_info; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci info->battery_desc.name = kasprintf(GFP_KERNEL, "%s-%d", 4068c2ecf20Sopenharmony_ci client->name, num); 4078c2ecf20Sopenharmony_ci if (!info->battery_desc.name) { 4088c2ecf20Sopenharmony_ci ret = -ENOMEM; 4098c2ecf20Sopenharmony_ci goto fail_name; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (id->driver_data == DS2786) 4138c2ecf20Sopenharmony_ci info->rsns = pdata->rsns; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci i2c_set_clientdata(client, info); 4168c2ecf20Sopenharmony_ci info->client = client; 4178c2ecf20Sopenharmony_ci info->id = num; 4188c2ecf20Sopenharmony_ci info->ops = &ds278x_ops[id->driver_data]; 4198c2ecf20Sopenharmony_ci ds278x_power_supply_init(&info->battery_desc); 4208c2ecf20Sopenharmony_ci psy_cfg.drv_data = info; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci info->capacity = 100; 4238c2ecf20Sopenharmony_ci info->status = POWER_SUPPLY_STATUS_FULL; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci info->battery = power_supply_register(&client->dev, 4288c2ecf20Sopenharmony_ci &info->battery_desc, &psy_cfg); 4298c2ecf20Sopenharmony_ci if (IS_ERR(info->battery)) { 4308c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to register battery\n"); 4318c2ecf20Sopenharmony_ci ret = PTR_ERR(info->battery); 4328c2ecf20Sopenharmony_ci goto fail_register; 4338c2ecf20Sopenharmony_ci } else { 4348c2ecf20Sopenharmony_ci schedule_delayed_work(&info->bat_work, DS278x_DELAY); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cifail_register: 4408c2ecf20Sopenharmony_ci kfree(info->battery_desc.name); 4418c2ecf20Sopenharmony_cifail_name: 4428c2ecf20Sopenharmony_ci kfree(info); 4438c2ecf20Sopenharmony_cifail_info: 4448c2ecf20Sopenharmony_ci mutex_lock(&battery_lock); 4458c2ecf20Sopenharmony_ci idr_remove(&battery_id, num); 4468c2ecf20Sopenharmony_ci mutex_unlock(&battery_lock); 4478c2ecf20Sopenharmony_cifail_id: 4488c2ecf20Sopenharmony_ci return ret; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic const struct i2c_device_id ds278x_id[] = { 4528c2ecf20Sopenharmony_ci {"ds2782", DS2782}, 4538c2ecf20Sopenharmony_ci {"ds2786", DS2786}, 4548c2ecf20Sopenharmony_ci {}, 4558c2ecf20Sopenharmony_ci}; 4568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ds278x_id); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic struct i2c_driver ds278x_battery_driver = { 4598c2ecf20Sopenharmony_ci .driver = { 4608c2ecf20Sopenharmony_ci .name = "ds2782-battery", 4618c2ecf20Sopenharmony_ci .pm = &ds278x_battery_pm_ops, 4628c2ecf20Sopenharmony_ci }, 4638c2ecf20Sopenharmony_ci .probe = ds278x_battery_probe, 4648c2ecf20Sopenharmony_ci .remove = ds278x_battery_remove, 4658c2ecf20Sopenharmony_ci .id_table = ds278x_id, 4668c2ecf20Sopenharmony_ci}; 4678c2ecf20Sopenharmony_cimodule_i2c_driver(ds278x_battery_driver); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ryan Mallon"); 4708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC driver"); 4718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 472