17b38ebdfSKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0 27b38ebdfSKrzysztof Kozlowski // 37b38ebdfSKrzysztof Kozlowski // max17040_battery.c 47b38ebdfSKrzysztof Kozlowski // fuel-gauge systems for lithium-ion (Li+) batteries 57b38ebdfSKrzysztof Kozlowski // 67b38ebdfSKrzysztof Kozlowski // Copyright (C) 2009 Samsung Electronics 77b38ebdfSKrzysztof Kozlowski // Minkyu Kang <mk7.kang@samsung.com> 88c0984e5SSebastian Reichel 98c0984e5SSebastian Reichel #include <linux/module.h> 108c0984e5SSebastian Reichel #include <linux/init.h> 118c0984e5SSebastian Reichel #include <linux/platform_device.h> 128c0984e5SSebastian Reichel #include <linux/mutex.h> 138c0984e5SSebastian Reichel #include <linux/err.h> 148c0984e5SSebastian Reichel #include <linux/i2c.h> 158c0984e5SSebastian Reichel #include <linux/delay.h> 162e17ed94SMatheus Castello #include <linux/interrupt.h> 178c0984e5SSebastian Reichel #include <linux/power_supply.h> 184f7f8e87SIskren Chernev #include <linux/of_device.h> 198c0984e5SSebastian Reichel #include <linux/max17040_battery.h> 206455a8a8SIskren Chernev #include <linux/regmap.h> 218c0984e5SSebastian Reichel #include <linux/slab.h> 228c0984e5SSebastian Reichel 2314d60bddSLiu Xiang #define MAX17040_VCELL 0x02 2414d60bddSLiu Xiang #define MAX17040_SOC 0x04 2514d60bddSLiu Xiang #define MAX17040_MODE 0x06 2614d60bddSLiu Xiang #define MAX17040_VER 0x08 276455a8a8SIskren Chernev #define MAX17040_CONFIG 0x0C 28ee2d433cSIskren Chernev #define MAX17040_STATUS 0x1A 2914d60bddSLiu Xiang #define MAX17040_CMD 0xFE 3014d60bddSLiu Xiang 318c0984e5SSebastian Reichel 328c0984e5SSebastian Reichel #define MAX17040_DELAY 1000 338c0984e5SSebastian Reichel #define MAX17040_BATTERY_FULL 95 341779c6e3SIskren Chernev #define MAX17040_RCOMP_DEFAULT 0x9700 358c0984e5SSebastian Reichel 366455a8a8SIskren Chernev #define MAX17040_ATHD_MASK 0x3f 37ee2d433cSIskren Chernev #define MAX17040_ALSC_MASK 0x40 38cccdd0caSMatheus Castello #define MAX17040_ATHD_DEFAULT_POWER_UP 4 39ee2d433cSIskren Chernev #define MAX17040_STATUS_HD_MASK 0x1000 40ee2d433cSIskren Chernev #define MAX17040_STATUS_SC_MASK 0x2000 411779c6e3SIskren Chernev #define MAX17040_CFG_RCOMP_MASK 0xff00 42cccdd0caSMatheus Castello 434f7f8e87SIskren Chernev enum chip_id { 444f7f8e87SIskren Chernev ID_MAX17040, 454f7f8e87SIskren Chernev ID_MAX17041, 464f7f8e87SIskren Chernev ID_MAX17043, 474f7f8e87SIskren Chernev ID_MAX17044, 484f7f8e87SIskren Chernev ID_MAX17048, 494f7f8e87SIskren Chernev ID_MAX17049, 504f7f8e87SIskren Chernev ID_MAX17058, 514f7f8e87SIskren Chernev ID_MAX17059, 524f7f8e87SIskren Chernev }; 534f7f8e87SIskren Chernev 544f7f8e87SIskren Chernev /* values that differ by chip_id */ 554f7f8e87SIskren Chernev struct chip_data { 564f7f8e87SIskren Chernev u16 reset_val; 574f7f8e87SIskren Chernev u16 vcell_shift; 584f7f8e87SIskren Chernev u16 vcell_mul; 594f7f8e87SIskren Chernev u16 vcell_div; 604f7f8e87SIskren Chernev u8 has_low_soc_alert; 611779c6e3SIskren Chernev u8 rcomp_bytes; 62ee2d433cSIskren Chernev u8 has_soc_alert; 634f7f8e87SIskren Chernev }; 644f7f8e87SIskren Chernev 654f7f8e87SIskren Chernev static struct chip_data max17040_family[] = { 664f7f8e87SIskren Chernev [ID_MAX17040] = { 674f7f8e87SIskren Chernev .reset_val = 0x0054, 684f7f8e87SIskren Chernev .vcell_shift = 4, 694f7f8e87SIskren Chernev .vcell_mul = 1250, 704f7f8e87SIskren Chernev .vcell_div = 1, 714f7f8e87SIskren Chernev .has_low_soc_alert = 0, 721779c6e3SIskren Chernev .rcomp_bytes = 2, 73ee2d433cSIskren Chernev .has_soc_alert = 0, 744f7f8e87SIskren Chernev }, 754f7f8e87SIskren Chernev [ID_MAX17041] = { 764f7f8e87SIskren Chernev .reset_val = 0x0054, 774f7f8e87SIskren Chernev .vcell_shift = 4, 784f7f8e87SIskren Chernev .vcell_mul = 2500, 794f7f8e87SIskren Chernev .vcell_div = 1, 804f7f8e87SIskren Chernev .has_low_soc_alert = 0, 811779c6e3SIskren Chernev .rcomp_bytes = 2, 82ee2d433cSIskren Chernev .has_soc_alert = 0, 834f7f8e87SIskren Chernev }, 844f7f8e87SIskren Chernev [ID_MAX17043] = { 854f7f8e87SIskren Chernev .reset_val = 0x0054, 864f7f8e87SIskren Chernev .vcell_shift = 4, 874f7f8e87SIskren Chernev .vcell_mul = 1250, 884f7f8e87SIskren Chernev .vcell_div = 1, 894f7f8e87SIskren Chernev .has_low_soc_alert = 1, 901779c6e3SIskren Chernev .rcomp_bytes = 1, 91ee2d433cSIskren Chernev .has_soc_alert = 0, 924f7f8e87SIskren Chernev }, 934f7f8e87SIskren Chernev [ID_MAX17044] = { 944f7f8e87SIskren Chernev .reset_val = 0x0054, 954f7f8e87SIskren Chernev .vcell_shift = 4, 964f7f8e87SIskren Chernev .vcell_mul = 2500, 974f7f8e87SIskren Chernev .vcell_div = 1, 984f7f8e87SIskren Chernev .has_low_soc_alert = 1, 991779c6e3SIskren Chernev .rcomp_bytes = 1, 100ee2d433cSIskren Chernev .has_soc_alert = 0, 1014f7f8e87SIskren Chernev }, 1024f7f8e87SIskren Chernev [ID_MAX17048] = { 1034f7f8e87SIskren Chernev .reset_val = 0x5400, 1044f7f8e87SIskren Chernev .vcell_shift = 0, 1054f7f8e87SIskren Chernev .vcell_mul = 625, 1064f7f8e87SIskren Chernev .vcell_div = 8, 1074f7f8e87SIskren Chernev .has_low_soc_alert = 1, 1081779c6e3SIskren Chernev .rcomp_bytes = 1, 109ee2d433cSIskren Chernev .has_soc_alert = 1, 1104f7f8e87SIskren Chernev }, 1114f7f8e87SIskren Chernev [ID_MAX17049] = { 1124f7f8e87SIskren Chernev .reset_val = 0x5400, 1134f7f8e87SIskren Chernev .vcell_shift = 0, 1144f7f8e87SIskren Chernev .vcell_mul = 625, 1154f7f8e87SIskren Chernev .vcell_div = 4, 1164f7f8e87SIskren Chernev .has_low_soc_alert = 1, 1171779c6e3SIskren Chernev .rcomp_bytes = 1, 118ee2d433cSIskren Chernev .has_soc_alert = 1, 1194f7f8e87SIskren Chernev }, 1204f7f8e87SIskren Chernev [ID_MAX17058] = { 1214f7f8e87SIskren Chernev .reset_val = 0x5400, 1224f7f8e87SIskren Chernev .vcell_shift = 0, 1234f7f8e87SIskren Chernev .vcell_mul = 625, 1244f7f8e87SIskren Chernev .vcell_div = 8, 1254f7f8e87SIskren Chernev .has_low_soc_alert = 1, 1261779c6e3SIskren Chernev .rcomp_bytes = 1, 127ee2d433cSIskren Chernev .has_soc_alert = 0, 1284f7f8e87SIskren Chernev }, 1294f7f8e87SIskren Chernev [ID_MAX17059] = { 1304f7f8e87SIskren Chernev .reset_val = 0x5400, 1314f7f8e87SIskren Chernev .vcell_shift = 0, 1324f7f8e87SIskren Chernev .vcell_mul = 625, 1334f7f8e87SIskren Chernev .vcell_div = 4, 1344f7f8e87SIskren Chernev .has_low_soc_alert = 1, 1351779c6e3SIskren Chernev .rcomp_bytes = 1, 136ee2d433cSIskren Chernev .has_soc_alert = 0, 1374f7f8e87SIskren Chernev }, 1384f7f8e87SIskren Chernev }; 1394f7f8e87SIskren Chernev 1408c0984e5SSebastian Reichel struct max17040_chip { 1418c0984e5SSebastian Reichel struct i2c_client *client; 1426455a8a8SIskren Chernev struct regmap *regmap; 1438c0984e5SSebastian Reichel struct delayed_work work; 1448c0984e5SSebastian Reichel struct power_supply *battery; 1458c0984e5SSebastian Reichel struct max17040_platform_data *pdata; 1464f7f8e87SIskren Chernev struct chip_data data; 1478c0984e5SSebastian Reichel 1488c0984e5SSebastian Reichel /* battery capacity */ 1498c0984e5SSebastian Reichel int soc; 1508c0984e5SSebastian Reichel /* State Of Charge */ 1518c0984e5SSebastian Reichel int status; 152cccdd0caSMatheus Castello /* Low alert threshold from 32% to 1% of the State of Charge */ 153cccdd0caSMatheus Castello u32 low_soc_alert; 1544f7f8e87SIskren Chernev /* some devices return twice the capacity */ 1554f7f8e87SIskren Chernev bool quirk_double_soc; 1561779c6e3SIskren Chernev /* higher 8 bits for 17043+, 16 bits for 17040,41 */ 1571779c6e3SIskren Chernev u16 rcomp; 1588c0984e5SSebastian Reichel }; 1598c0984e5SSebastian Reichel 1606455a8a8SIskren Chernev static int max17040_reset(struct max17040_chip *chip) 1618c0984e5SSebastian Reichel { 1624f7f8e87SIskren Chernev return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val); 1638c0984e5SSebastian Reichel } 1648c0984e5SSebastian Reichel 1656455a8a8SIskren Chernev static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) 1668c0984e5SSebastian Reichel { 1674f7f8e87SIskren Chernev level = 32 - level * (chip->quirk_double_soc ? 2 : 1); 1686455a8a8SIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, 1696455a8a8SIskren Chernev MAX17040_ATHD_MASK, level); 170cccdd0caSMatheus Castello } 171cccdd0caSMatheus Castello 172ee2d433cSIskren Chernev static int max17040_set_soc_alert(struct max17040_chip *chip, bool enable) 173ee2d433cSIskren Chernev { 174ee2d433cSIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, 175ee2d433cSIskren Chernev MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0); 176ee2d433cSIskren Chernev } 177ee2d433cSIskren Chernev 1781779c6e3SIskren Chernev static int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp) 1791779c6e3SIskren Chernev { 1801779c6e3SIskren Chernev u16 mask = chip->data.rcomp_bytes == 2 ? 1811779c6e3SIskren Chernev 0xffff : MAX17040_CFG_RCOMP_MASK; 1821779c6e3SIskren Chernev 1831779c6e3SIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, mask, rcomp); 1841779c6e3SIskren Chernev } 1851779c6e3SIskren Chernev 1864f7f8e87SIskren Chernev static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) 1874f7f8e87SIskren Chernev { 1884f7f8e87SIskren Chernev struct chip_data *d = &chip->data; 1894f7f8e87SIskren Chernev 1904f7f8e87SIskren Chernev return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div; 1914f7f8e87SIskren Chernev } 1924f7f8e87SIskren Chernev 1934f7f8e87SIskren Chernev 1946455a8a8SIskren Chernev static int max17040_get_vcell(struct max17040_chip *chip) 1958c0984e5SSebastian Reichel { 1966455a8a8SIskren Chernev u32 vcell; 1978c0984e5SSebastian Reichel 1986455a8a8SIskren Chernev regmap_read(chip->regmap, MAX17040_VCELL, &vcell); 1998c0984e5SSebastian Reichel 2004f7f8e87SIskren Chernev return max17040_raw_vcell_to_uvolts(chip, vcell); 2018c0984e5SSebastian Reichel } 2028c0984e5SSebastian Reichel 2036455a8a8SIskren Chernev static int max17040_get_soc(struct max17040_chip *chip) 2048c0984e5SSebastian Reichel { 2056455a8a8SIskren Chernev u32 soc; 2068c0984e5SSebastian Reichel 2076455a8a8SIskren Chernev regmap_read(chip->regmap, MAX17040_SOC, &soc); 2088c0984e5SSebastian Reichel 2094f7f8e87SIskren Chernev return soc >> (chip->quirk_double_soc ? 9 : 8); 2108c0984e5SSebastian Reichel } 2118c0984e5SSebastian Reichel 2126455a8a8SIskren Chernev static int max17040_get_version(struct max17040_chip *chip) 2138c0984e5SSebastian Reichel { 2146455a8a8SIskren Chernev int ret; 2156455a8a8SIskren Chernev u32 version; 2168c0984e5SSebastian Reichel 2176455a8a8SIskren Chernev ret = regmap_read(chip->regmap, MAX17040_VER, &version); 2188c0984e5SSebastian Reichel 2196455a8a8SIskren Chernev return ret ? ret : version; 2208c0984e5SSebastian Reichel } 2218c0984e5SSebastian Reichel 2226455a8a8SIskren Chernev static int max17040_get_online(struct max17040_chip *chip) 2238c0984e5SSebastian Reichel { 2246455a8a8SIskren Chernev return chip->pdata && chip->pdata->battery_online ? 2256455a8a8SIskren Chernev chip->pdata->battery_online() : 1; 2268c0984e5SSebastian Reichel } 2278c0984e5SSebastian Reichel 2286455a8a8SIskren Chernev static int max17040_get_status(struct max17040_chip *chip) 2298c0984e5SSebastian Reichel { 2308c0984e5SSebastian Reichel if (!chip->pdata || !chip->pdata->charger_online 2316455a8a8SIskren Chernev || !chip->pdata->charger_enable) 2326455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_UNKNOWN; 2338c0984e5SSebastian Reichel 2346455a8a8SIskren Chernev if (max17040_get_soc(chip) > MAX17040_BATTERY_FULL) 2356455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_FULL; 2366455a8a8SIskren Chernev 2376455a8a8SIskren Chernev if (chip->pdata->charger_online()) 2388c0984e5SSebastian Reichel if (chip->pdata->charger_enable()) 2396455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_CHARGING; 2408c0984e5SSebastian Reichel else 2416455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_NOT_CHARGING; 2426455a8a8SIskren Chernev else 2436455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_DISCHARGING; 2448c0984e5SSebastian Reichel } 2458c0984e5SSebastian Reichel 246cccdd0caSMatheus Castello static int max17040_get_of_data(struct max17040_chip *chip) 247cccdd0caSMatheus Castello { 248cccdd0caSMatheus Castello struct device *dev = &chip->client->dev; 2491779c6e3SIskren Chernev struct chip_data *data = &max17040_family[ 2507f977e64SIskren Chernev (uintptr_t) of_device_get_match_data(dev)]; 2511779c6e3SIskren Chernev int rcomp_len; 2521779c6e3SIskren Chernev u8 rcomp[2]; 253cccdd0caSMatheus Castello 2544f7f8e87SIskren Chernev chip->quirk_double_soc = device_property_read_bool(dev, 2554f7f8e87SIskren Chernev "maxim,double-soc"); 2564f7f8e87SIskren Chernev 257cccdd0caSMatheus Castello chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP; 258cccdd0caSMatheus Castello device_property_read_u32(dev, 259cccdd0caSMatheus Castello "maxim,alert-low-soc-level", 260cccdd0caSMatheus Castello &chip->low_soc_alert); 261cccdd0caSMatheus Castello 2624f7f8e87SIskren Chernev if (chip->low_soc_alert <= 0 || 2634f7f8e87SIskren Chernev chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) { 2646455a8a8SIskren Chernev dev_err(dev, "maxim,alert-low-soc-level out of bounds\n"); 265cccdd0caSMatheus Castello return -EINVAL; 2666455a8a8SIskren Chernev } 267cccdd0caSMatheus Castello 2681779c6e3SIskren Chernev rcomp_len = device_property_count_u8(dev, "maxim,rcomp"); 2691779c6e3SIskren Chernev chip->rcomp = MAX17040_RCOMP_DEFAULT; 2701779c6e3SIskren Chernev if (rcomp_len == data->rcomp_bytes) { 271a837f35cSKrzysztof Kozlowski if (!device_property_read_u8_array(dev, "maxim,rcomp", 272a837f35cSKrzysztof Kozlowski rcomp, rcomp_len)) 273a837f35cSKrzysztof Kozlowski chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] : 2741779c6e3SIskren Chernev rcomp[0] << 8; 2751779c6e3SIskren Chernev } else if (rcomp_len > 0) { 2761779c6e3SIskren Chernev dev_err(dev, "maxim,rcomp has incorrect length\n"); 2771779c6e3SIskren Chernev return -EINVAL; 2781779c6e3SIskren Chernev } 2791779c6e3SIskren Chernev 280cccdd0caSMatheus Castello return 0; 281cccdd0caSMatheus Castello } 282cccdd0caSMatheus Castello 2836455a8a8SIskren Chernev static void max17040_check_changes(struct max17040_chip *chip) 2842e17ed94SMatheus Castello { 2856455a8a8SIskren Chernev chip->soc = max17040_get_soc(chip); 2866455a8a8SIskren Chernev chip->status = max17040_get_status(chip); 2872e17ed94SMatheus Castello } 2882e17ed94SMatheus Castello 289e55a5061SIskren Chernev static void max17040_queue_work(struct max17040_chip *chip) 290e55a5061SIskren Chernev { 291e55a5061SIskren Chernev queue_delayed_work(system_power_efficient_wq, &chip->work, 292e55a5061SIskren Chernev MAX17040_DELAY); 293e55a5061SIskren Chernev } 294e55a5061SIskren Chernev 295e55a5061SIskren Chernev static void max17040_stop_work(void *data) 296e55a5061SIskren Chernev { 297e55a5061SIskren Chernev struct max17040_chip *chip = data; 298e55a5061SIskren Chernev 299e55a5061SIskren Chernev cancel_delayed_work_sync(&chip->work); 300e55a5061SIskren Chernev } 301e55a5061SIskren Chernev 3028c0984e5SSebastian Reichel static void max17040_work(struct work_struct *work) 3038c0984e5SSebastian Reichel { 3048c0984e5SSebastian Reichel struct max17040_chip *chip; 305a08990eaSMatheus Castello int last_soc, last_status; 3068c0984e5SSebastian Reichel 3078c0984e5SSebastian Reichel chip = container_of(work, struct max17040_chip, work.work); 308a08990eaSMatheus Castello 309a08990eaSMatheus Castello /* store SOC and status to check changes */ 310a08990eaSMatheus Castello last_soc = chip->soc; 311a08990eaSMatheus Castello last_status = chip->status; 3126455a8a8SIskren Chernev max17040_check_changes(chip); 3138c0984e5SSebastian Reichel 314a08990eaSMatheus Castello /* check changes and send uevent */ 315a08990eaSMatheus Castello if (last_soc != chip->soc || last_status != chip->status) 316a08990eaSMatheus Castello power_supply_changed(chip->battery); 317a08990eaSMatheus Castello 318e55a5061SIskren Chernev max17040_queue_work(chip); 3198c0984e5SSebastian Reichel } 3208c0984e5SSebastian Reichel 321ee2d433cSIskren Chernev /* Returns true if alert cause was SOC change, not low SOC */ 322ee2d433cSIskren Chernev static bool max17040_handle_soc_alert(struct max17040_chip *chip) 323ee2d433cSIskren Chernev { 324ee2d433cSIskren Chernev bool ret = true; 325ee2d433cSIskren Chernev u32 data; 326ee2d433cSIskren Chernev 327ee2d433cSIskren Chernev regmap_read(chip->regmap, MAX17040_STATUS, &data); 328ee2d433cSIskren Chernev 329ee2d433cSIskren Chernev if (data & MAX17040_STATUS_HD_MASK) { 330ee2d433cSIskren Chernev // this alert was caused by low soc 331ee2d433cSIskren Chernev ret = false; 332ee2d433cSIskren Chernev } 333ee2d433cSIskren Chernev if (data & MAX17040_STATUS_SC_MASK) { 334ee2d433cSIskren Chernev // soc change bit -- deassert to mark as handled 335ee2d433cSIskren Chernev regmap_write(chip->regmap, MAX17040_STATUS, 336ee2d433cSIskren Chernev data & ~MAX17040_STATUS_SC_MASK); 337ee2d433cSIskren Chernev } 338ee2d433cSIskren Chernev 339ee2d433cSIskren Chernev return ret; 340ee2d433cSIskren Chernev } 341ee2d433cSIskren Chernev 3422e17ed94SMatheus Castello static irqreturn_t max17040_thread_handler(int id, void *dev) 3432e17ed94SMatheus Castello { 3442e17ed94SMatheus Castello struct max17040_chip *chip = dev; 3452e17ed94SMatheus Castello 346ee2d433cSIskren Chernev if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip))) 347ee2d433cSIskren Chernev dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n"); 3486455a8a8SIskren Chernev 3492e17ed94SMatheus Castello /* read registers */ 3506455a8a8SIskren Chernev max17040_check_changes(chip); 3512e17ed94SMatheus Castello 3522e17ed94SMatheus Castello /* send uevent */ 3532e17ed94SMatheus Castello power_supply_changed(chip->battery); 3542e17ed94SMatheus Castello 355cccdd0caSMatheus Castello /* reset alert bit */ 3566455a8a8SIskren Chernev max17040_set_low_soc_alert(chip, chip->low_soc_alert); 357cccdd0caSMatheus Castello 3582e17ed94SMatheus Castello return IRQ_HANDLED; 3592e17ed94SMatheus Castello } 3602e17ed94SMatheus Castello 3612e17ed94SMatheus Castello static int max17040_enable_alert_irq(struct max17040_chip *chip) 3622e17ed94SMatheus Castello { 3632e17ed94SMatheus Castello struct i2c_client *client = chip->client; 3642e17ed94SMatheus Castello int ret; 3652e17ed94SMatheus Castello 3662e17ed94SMatheus Castello ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, 367*8bb2314fSKrzysztof Kozlowski max17040_thread_handler, IRQF_ONESHOT, 3682e17ed94SMatheus Castello chip->battery->desc->name, chip); 3692e17ed94SMatheus Castello 3702e17ed94SMatheus Castello return ret; 3712e17ed94SMatheus Castello } 3722e17ed94SMatheus Castello 3732f38dc4dSMatheus Castello static int max17040_prop_writeable(struct power_supply *psy, 3742f38dc4dSMatheus Castello enum power_supply_property psp) 3752f38dc4dSMatheus Castello { 3762f38dc4dSMatheus Castello switch (psp) { 3772f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3782f38dc4dSMatheus Castello return 1; 3792f38dc4dSMatheus Castello default: 3802f38dc4dSMatheus Castello return 0; 3812f38dc4dSMatheus Castello } 3822f38dc4dSMatheus Castello } 3832f38dc4dSMatheus Castello 3842f38dc4dSMatheus Castello static int max17040_set_property(struct power_supply *psy, 3852f38dc4dSMatheus Castello enum power_supply_property psp, 3862f38dc4dSMatheus Castello const union power_supply_propval *val) 3872f38dc4dSMatheus Castello { 3882f38dc4dSMatheus Castello struct max17040_chip *chip = power_supply_get_drvdata(psy); 3892f38dc4dSMatheus Castello int ret; 3902f38dc4dSMatheus Castello 3912f38dc4dSMatheus Castello switch (psp) { 3922f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3934f7f8e87SIskren Chernev /* alert threshold can be programmed from 1% up to 16/32% */ 3944f7f8e87SIskren Chernev if ((val->intval < 1) || 3954f7f8e87SIskren Chernev (val->intval > (chip->quirk_double_soc ? 16 : 32))) { 3962f38dc4dSMatheus Castello ret = -EINVAL; 3972f38dc4dSMatheus Castello break; 3982f38dc4dSMatheus Castello } 3996455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, val->intval); 4002f38dc4dSMatheus Castello chip->low_soc_alert = val->intval; 4012f38dc4dSMatheus Castello break; 4022f38dc4dSMatheus Castello default: 4032f38dc4dSMatheus Castello ret = -EINVAL; 4042f38dc4dSMatheus Castello } 4052f38dc4dSMatheus Castello 4062f38dc4dSMatheus Castello return ret; 4072f38dc4dSMatheus Castello } 4082f38dc4dSMatheus Castello 4096455a8a8SIskren Chernev static int max17040_get_property(struct power_supply *psy, 4106455a8a8SIskren Chernev enum power_supply_property psp, 4116455a8a8SIskren Chernev union power_supply_propval *val) 4126455a8a8SIskren Chernev { 4136455a8a8SIskren Chernev struct max17040_chip *chip = power_supply_get_drvdata(psy); 4146455a8a8SIskren Chernev 4156455a8a8SIskren Chernev switch (psp) { 4166455a8a8SIskren Chernev case POWER_SUPPLY_PROP_STATUS: 4176455a8a8SIskren Chernev val->intval = max17040_get_status(chip); 4186455a8a8SIskren Chernev break; 4196455a8a8SIskren Chernev case POWER_SUPPLY_PROP_ONLINE: 4206455a8a8SIskren Chernev val->intval = max17040_get_online(chip); 4216455a8a8SIskren Chernev break; 4226455a8a8SIskren Chernev case POWER_SUPPLY_PROP_VOLTAGE_NOW: 4236455a8a8SIskren Chernev val->intval = max17040_get_vcell(chip); 4246455a8a8SIskren Chernev break; 4256455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY: 4266455a8a8SIskren Chernev val->intval = max17040_get_soc(chip); 4276455a8a8SIskren Chernev break; 4286455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 4296455a8a8SIskren Chernev val->intval = chip->low_soc_alert; 4306455a8a8SIskren Chernev break; 4316455a8a8SIskren Chernev default: 4326455a8a8SIskren Chernev return -EINVAL; 4336455a8a8SIskren Chernev } 4346455a8a8SIskren Chernev return 0; 4356455a8a8SIskren Chernev } 4366455a8a8SIskren Chernev 4376455a8a8SIskren Chernev static const struct regmap_config max17040_regmap = { 4386455a8a8SIskren Chernev .reg_bits = 8, 4396455a8a8SIskren Chernev .reg_stride = 2, 4406455a8a8SIskren Chernev .val_bits = 16, 4416455a8a8SIskren Chernev .val_format_endian = REGMAP_ENDIAN_BIG, 4426455a8a8SIskren Chernev }; 4436455a8a8SIskren Chernev 4448c0984e5SSebastian Reichel static enum power_supply_property max17040_battery_props[] = { 4458c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4468c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 4478c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4488c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4492f38dc4dSMatheus Castello POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, 4508c0984e5SSebastian Reichel }; 4518c0984e5SSebastian Reichel 4528c0984e5SSebastian Reichel static const struct power_supply_desc max17040_battery_desc = { 4538c0984e5SSebastian Reichel .name = "battery", 4548c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 4558c0984e5SSebastian Reichel .get_property = max17040_get_property, 4562f38dc4dSMatheus Castello .set_property = max17040_set_property, 4572f38dc4dSMatheus Castello .property_is_writeable = max17040_prop_writeable, 4588c0984e5SSebastian Reichel .properties = max17040_battery_props, 4598c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(max17040_battery_props), 4608c0984e5SSebastian Reichel }; 4618c0984e5SSebastian Reichel 4628c0984e5SSebastian Reichel static int max17040_probe(struct i2c_client *client, 4638c0984e5SSebastian Reichel const struct i2c_device_id *id) 4648c0984e5SSebastian Reichel { 4654e9c406dSWolfram Sang struct i2c_adapter *adapter = client->adapter; 4668c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 4678c0984e5SSebastian Reichel struct max17040_chip *chip; 4684f7f8e87SIskren Chernev enum chip_id chip_id; 469ee2d433cSIskren Chernev bool enable_irq = false; 470cccdd0caSMatheus Castello int ret; 4718c0984e5SSebastian Reichel 4728c0984e5SSebastian Reichel if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 4738c0984e5SSebastian Reichel return -EIO; 4748c0984e5SSebastian Reichel 4758c0984e5SSebastian Reichel chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 4768c0984e5SSebastian Reichel if (!chip) 4778c0984e5SSebastian Reichel return -ENOMEM; 4788c0984e5SSebastian Reichel 4798c0984e5SSebastian Reichel chip->client = client; 4806455a8a8SIskren Chernev chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); 4818c0984e5SSebastian Reichel chip->pdata = client->dev.platform_data; 4824f7f8e87SIskren Chernev chip_id = (enum chip_id) id->driver_data; 4834f7f8e87SIskren Chernev if (client->dev.of_node) { 484cccdd0caSMatheus Castello ret = max17040_get_of_data(chip); 4856455a8a8SIskren Chernev if (ret) 486cccdd0caSMatheus Castello return ret; 4876dfbb2ceSKrzysztof Kozlowski chip_id = (uintptr_t)of_device_get_match_data(&client->dev); 4884f7f8e87SIskren Chernev } 4894f7f8e87SIskren Chernev chip->data = max17040_family[chip_id]; 4908c0984e5SSebastian Reichel 4918c0984e5SSebastian Reichel i2c_set_clientdata(client, chip); 4928c0984e5SSebastian Reichel psy_cfg.drv_data = chip; 4938c0984e5SSebastian Reichel 494e55a5061SIskren Chernev chip->battery = devm_power_supply_register(&client->dev, 4958c0984e5SSebastian Reichel &max17040_battery_desc, &psy_cfg); 4968c0984e5SSebastian Reichel if (IS_ERR(chip->battery)) { 4978c0984e5SSebastian Reichel dev_err(&client->dev, "failed: power supply register\n"); 4988c0984e5SSebastian Reichel return PTR_ERR(chip->battery); 4998c0984e5SSebastian Reichel } 5008c0984e5SSebastian Reichel 5016455a8a8SIskren Chernev ret = max17040_get_version(chip); 5026455a8a8SIskren Chernev if (ret < 0) 5036455a8a8SIskren Chernev return ret; 5046455a8a8SIskren Chernev dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret); 5056455a8a8SIskren Chernev 5064f7f8e87SIskren Chernev if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) 5076455a8a8SIskren Chernev max17040_reset(chip); 5088c0984e5SSebastian Reichel 5091779c6e3SIskren Chernev max17040_set_rcomp(chip, chip->rcomp); 5101779c6e3SIskren Chernev 5112e17ed94SMatheus Castello /* check interrupt */ 5124f7f8e87SIskren Chernev if (client->irq && chip->data.has_low_soc_alert) { 5136455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); 514cccdd0caSMatheus Castello if (ret) { 515cccdd0caSMatheus Castello dev_err(&client->dev, 516cccdd0caSMatheus Castello "Failed to set low SOC alert: err %d\n", ret); 517cccdd0caSMatheus Castello return ret; 518cccdd0caSMatheus Castello } 5192e17ed94SMatheus Castello 520ee2d433cSIskren Chernev enable_irq = true; 521ee2d433cSIskren Chernev } 522ee2d433cSIskren Chernev 523ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) { 524ee2d433cSIskren Chernev ret = max17040_set_soc_alert(chip, 1); 525ee2d433cSIskren Chernev if (ret) { 526ee2d433cSIskren Chernev dev_err(&client->dev, 527ee2d433cSIskren Chernev "Failed to set SOC alert: err %d\n", ret); 528ee2d433cSIskren Chernev return ret; 529ee2d433cSIskren Chernev } 530ee2d433cSIskren Chernev enable_irq = true; 531ee2d433cSIskren Chernev } else { 532ee2d433cSIskren Chernev /* soc alerts negate the need for polling */ 533ee2d433cSIskren Chernev INIT_DEFERRABLE_WORK(&chip->work, max17040_work); 534ee2d433cSIskren Chernev ret = devm_add_action(&client->dev, max17040_stop_work, chip); 535ee2d433cSIskren Chernev if (ret) 536ee2d433cSIskren Chernev return ret; 537ee2d433cSIskren Chernev max17040_queue_work(chip); 538ee2d433cSIskren Chernev } 539ee2d433cSIskren Chernev 540ee2d433cSIskren Chernev if (enable_irq) { 5412e17ed94SMatheus Castello ret = max17040_enable_alert_irq(chip); 5422e17ed94SMatheus Castello if (ret) { 5432e17ed94SMatheus Castello client->irq = 0; 5442e17ed94SMatheus Castello dev_warn(&client->dev, 5452e17ed94SMatheus Castello "Failed to get IRQ err %d\n", ret); 5462e17ed94SMatheus Castello } 5472e17ed94SMatheus Castello } 5482e17ed94SMatheus Castello 5498c0984e5SSebastian Reichel return 0; 5508c0984e5SSebastian Reichel } 5518c0984e5SSebastian Reichel 5528c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP 5538c0984e5SSebastian Reichel 5548c0984e5SSebastian Reichel static int max17040_suspend(struct device *dev) 5558c0984e5SSebastian Reichel { 5568c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 5578c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 5588c0984e5SSebastian Reichel 559ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) 560ee2d433cSIskren Chernev // disable soc alert to prevent wakeup 561ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 0); 562ee2d433cSIskren Chernev else 5638c0984e5SSebastian Reichel cancel_delayed_work(&chip->work); 5642e17ed94SMatheus Castello 565e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 5662e17ed94SMatheus Castello enable_irq_wake(client->irq); 5672e17ed94SMatheus Castello 5688c0984e5SSebastian Reichel return 0; 5698c0984e5SSebastian Reichel } 5708c0984e5SSebastian Reichel 5718c0984e5SSebastian Reichel static int max17040_resume(struct device *dev) 5728c0984e5SSebastian Reichel { 5738c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 5748c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 5758c0984e5SSebastian Reichel 576e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 5772e17ed94SMatheus Castello disable_irq_wake(client->irq); 5782e17ed94SMatheus Castello 579ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) 580ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 1); 581ee2d433cSIskren Chernev else 582e55a5061SIskren Chernev max17040_queue_work(chip); 583e55a5061SIskren Chernev 5848c0984e5SSebastian Reichel return 0; 5858c0984e5SSebastian Reichel } 5868c0984e5SSebastian Reichel 5878c0984e5SSebastian Reichel static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); 5888c0984e5SSebastian Reichel #define MAX17040_PM_OPS (&max17040_pm_ops) 5898c0984e5SSebastian Reichel 5908c0984e5SSebastian Reichel #else 5918c0984e5SSebastian Reichel 5928c0984e5SSebastian Reichel #define MAX17040_PM_OPS NULL 5938c0984e5SSebastian Reichel 5948c0984e5SSebastian Reichel #endif /* CONFIG_PM_SLEEP */ 5958c0984e5SSebastian Reichel 5968c0984e5SSebastian Reichel static const struct i2c_device_id max17040_id[] = { 5974f7f8e87SIskren Chernev { "max17040", ID_MAX17040 }, 5984f7f8e87SIskren Chernev { "max17041", ID_MAX17041 }, 5994f7f8e87SIskren Chernev { "max17043", ID_MAX17043 }, 6004f7f8e87SIskren Chernev { "max77836-battery", ID_MAX17043 }, 6014f7f8e87SIskren Chernev { "max17044", ID_MAX17044 }, 6024f7f8e87SIskren Chernev { "max17048", ID_MAX17048 }, 6034f7f8e87SIskren Chernev { "max17049", ID_MAX17049 }, 6044f7f8e87SIskren Chernev { "max17058", ID_MAX17058 }, 6054f7f8e87SIskren Chernev { "max17059", ID_MAX17059 }, 6064f7f8e87SIskren Chernev { /* sentinel */ } 6078c0984e5SSebastian Reichel }; 6088c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, max17040_id); 6098c0984e5SSebastian Reichel 610da28122cSJavier Martinez Canillas static const struct of_device_id max17040_of_match[] = { 6114f7f8e87SIskren Chernev { .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 }, 6124f7f8e87SIskren Chernev { .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 }, 6134f7f8e87SIskren Chernev { .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 }, 6144f7f8e87SIskren Chernev { .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 }, 6154f7f8e87SIskren Chernev { .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 }, 6164f7f8e87SIskren Chernev { .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 }, 6174f7f8e87SIskren Chernev { .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 }, 6184f7f8e87SIskren Chernev { .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 }, 6194f7f8e87SIskren Chernev { .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 }, 6204f7f8e87SIskren Chernev { /* sentinel */ }, 621da28122cSJavier Martinez Canillas }; 622da28122cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, max17040_of_match); 623da28122cSJavier Martinez Canillas 6248c0984e5SSebastian Reichel static struct i2c_driver max17040_i2c_driver = { 6258c0984e5SSebastian Reichel .driver = { 6268c0984e5SSebastian Reichel .name = "max17040", 627da28122cSJavier Martinez Canillas .of_match_table = max17040_of_match, 6288c0984e5SSebastian Reichel .pm = MAX17040_PM_OPS, 6298c0984e5SSebastian Reichel }, 6308c0984e5SSebastian Reichel .probe = max17040_probe, 6318c0984e5SSebastian Reichel .id_table = max17040_id, 6328c0984e5SSebastian Reichel }; 6338c0984e5SSebastian Reichel module_i2c_driver(max17040_i2c_driver); 6348c0984e5SSebastian Reichel 6358c0984e5SSebastian Reichel MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); 6368c0984e5SSebastian Reichel MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); 6378c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 638