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; 150cccdd0caSMatheus Castello /* Low alert threshold from 32% to 1% of the State of Charge */ 151cccdd0caSMatheus Castello u32 low_soc_alert; 1524f7f8e87SIskren Chernev /* some devices return twice the capacity */ 1534f7f8e87SIskren Chernev bool quirk_double_soc; 1541779c6e3SIskren Chernev /* higher 8 bits for 17043+, 16 bits for 17040,41 */ 1551779c6e3SIskren Chernev u16 rcomp; 1568c0984e5SSebastian Reichel }; 1578c0984e5SSebastian Reichel 1586455a8a8SIskren Chernev static int max17040_reset(struct max17040_chip *chip) 1598c0984e5SSebastian Reichel { 1604f7f8e87SIskren Chernev return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val); 1618c0984e5SSebastian Reichel } 1628c0984e5SSebastian Reichel 1636455a8a8SIskren Chernev static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) 1648c0984e5SSebastian Reichel { 1654f7f8e87SIskren Chernev level = 32 - level * (chip->quirk_double_soc ? 2 : 1); 1666455a8a8SIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, 1676455a8a8SIskren Chernev MAX17040_ATHD_MASK, level); 168cccdd0caSMatheus Castello } 169cccdd0caSMatheus Castello 170ee2d433cSIskren Chernev static int max17040_set_soc_alert(struct max17040_chip *chip, bool enable) 171ee2d433cSIskren Chernev { 172ee2d433cSIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, 173ee2d433cSIskren Chernev MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0); 174ee2d433cSIskren Chernev } 175ee2d433cSIskren Chernev 1761779c6e3SIskren Chernev static int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp) 1771779c6e3SIskren Chernev { 1781779c6e3SIskren Chernev u16 mask = chip->data.rcomp_bytes == 2 ? 1791779c6e3SIskren Chernev 0xffff : MAX17040_CFG_RCOMP_MASK; 1801779c6e3SIskren Chernev 1811779c6e3SIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, mask, rcomp); 1821779c6e3SIskren Chernev } 1831779c6e3SIskren Chernev 1844f7f8e87SIskren Chernev static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) 1854f7f8e87SIskren Chernev { 1864f7f8e87SIskren Chernev struct chip_data *d = &chip->data; 1874f7f8e87SIskren Chernev 1884f7f8e87SIskren Chernev return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div; 1894f7f8e87SIskren Chernev } 1904f7f8e87SIskren Chernev 1914f7f8e87SIskren Chernev 1926455a8a8SIskren Chernev static int max17040_get_vcell(struct max17040_chip *chip) 1938c0984e5SSebastian Reichel { 1946455a8a8SIskren Chernev u32 vcell; 1958c0984e5SSebastian Reichel 1966455a8a8SIskren Chernev regmap_read(chip->regmap, MAX17040_VCELL, &vcell); 1978c0984e5SSebastian Reichel 1984f7f8e87SIskren Chernev return max17040_raw_vcell_to_uvolts(chip, vcell); 1998c0984e5SSebastian Reichel } 2008c0984e5SSebastian Reichel 2016455a8a8SIskren Chernev static int max17040_get_soc(struct max17040_chip *chip) 2028c0984e5SSebastian Reichel { 2036455a8a8SIskren Chernev u32 soc; 2048c0984e5SSebastian Reichel 2056455a8a8SIskren Chernev regmap_read(chip->regmap, MAX17040_SOC, &soc); 2068c0984e5SSebastian Reichel 2074f7f8e87SIskren Chernev return soc >> (chip->quirk_double_soc ? 9 : 8); 2088c0984e5SSebastian Reichel } 2098c0984e5SSebastian Reichel 2106455a8a8SIskren Chernev static int max17040_get_version(struct max17040_chip *chip) 2118c0984e5SSebastian Reichel { 2126455a8a8SIskren Chernev int ret; 2136455a8a8SIskren Chernev u32 version; 2148c0984e5SSebastian Reichel 2156455a8a8SIskren Chernev ret = regmap_read(chip->regmap, MAX17040_VER, &version); 2168c0984e5SSebastian Reichel 2176455a8a8SIskren Chernev return ret ? ret : version; 2188c0984e5SSebastian Reichel } 2198c0984e5SSebastian Reichel 2206455a8a8SIskren Chernev static int max17040_get_online(struct max17040_chip *chip) 2218c0984e5SSebastian Reichel { 2226455a8a8SIskren Chernev return chip->pdata && chip->pdata->battery_online ? 2236455a8a8SIskren Chernev chip->pdata->battery_online() : 1; 2248c0984e5SSebastian Reichel } 2258c0984e5SSebastian Reichel 226cccdd0caSMatheus Castello static int max17040_get_of_data(struct max17040_chip *chip) 227cccdd0caSMatheus Castello { 228cccdd0caSMatheus Castello struct device *dev = &chip->client->dev; 2291779c6e3SIskren Chernev struct chip_data *data = &max17040_family[ 2307f977e64SIskren Chernev (uintptr_t) of_device_get_match_data(dev)]; 2311779c6e3SIskren Chernev int rcomp_len; 2321779c6e3SIskren Chernev u8 rcomp[2]; 233cccdd0caSMatheus Castello 2344f7f8e87SIskren Chernev chip->quirk_double_soc = device_property_read_bool(dev, 2354f7f8e87SIskren Chernev "maxim,double-soc"); 2364f7f8e87SIskren Chernev 237cccdd0caSMatheus Castello chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP; 238cccdd0caSMatheus Castello device_property_read_u32(dev, 239cccdd0caSMatheus Castello "maxim,alert-low-soc-level", 240cccdd0caSMatheus Castello &chip->low_soc_alert); 241cccdd0caSMatheus Castello 2424f7f8e87SIskren Chernev if (chip->low_soc_alert <= 0 || 2434f7f8e87SIskren Chernev chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) { 2446455a8a8SIskren Chernev dev_err(dev, "maxim,alert-low-soc-level out of bounds\n"); 245cccdd0caSMatheus Castello return -EINVAL; 2466455a8a8SIskren Chernev } 247cccdd0caSMatheus Castello 2481779c6e3SIskren Chernev rcomp_len = device_property_count_u8(dev, "maxim,rcomp"); 2491779c6e3SIskren Chernev chip->rcomp = MAX17040_RCOMP_DEFAULT; 2501779c6e3SIskren Chernev if (rcomp_len == data->rcomp_bytes) { 251a837f35cSKrzysztof Kozlowski if (!device_property_read_u8_array(dev, "maxim,rcomp", 252a837f35cSKrzysztof Kozlowski rcomp, rcomp_len)) 253a837f35cSKrzysztof Kozlowski chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] : 2541779c6e3SIskren Chernev rcomp[0] << 8; 2551779c6e3SIskren Chernev } else if (rcomp_len > 0) { 2561779c6e3SIskren Chernev dev_err(dev, "maxim,rcomp has incorrect length\n"); 2571779c6e3SIskren Chernev return -EINVAL; 2581779c6e3SIskren Chernev } 2591779c6e3SIskren Chernev 260cccdd0caSMatheus Castello return 0; 261cccdd0caSMatheus Castello } 262cccdd0caSMatheus Castello 2636455a8a8SIskren Chernev static void max17040_check_changes(struct max17040_chip *chip) 2642e17ed94SMatheus Castello { 2656455a8a8SIskren Chernev chip->soc = max17040_get_soc(chip); 2662e17ed94SMatheus Castello } 2672e17ed94SMatheus Castello 268e55a5061SIskren Chernev static void max17040_queue_work(struct max17040_chip *chip) 269e55a5061SIskren Chernev { 270e55a5061SIskren Chernev queue_delayed_work(system_power_efficient_wq, &chip->work, 271e55a5061SIskren Chernev MAX17040_DELAY); 272e55a5061SIskren Chernev } 273e55a5061SIskren Chernev 274e55a5061SIskren Chernev static void max17040_stop_work(void *data) 275e55a5061SIskren Chernev { 276e55a5061SIskren Chernev struct max17040_chip *chip = data; 277e55a5061SIskren Chernev 278e55a5061SIskren Chernev cancel_delayed_work_sync(&chip->work); 279e55a5061SIskren Chernev } 280e55a5061SIskren Chernev 2818c0984e5SSebastian Reichel static void max17040_work(struct work_struct *work) 2828c0984e5SSebastian Reichel { 2838c0984e5SSebastian Reichel struct max17040_chip *chip; 284*c603bec3SKrzysztof Kozlowski int last_soc; 2858c0984e5SSebastian Reichel 2868c0984e5SSebastian Reichel chip = container_of(work, struct max17040_chip, work.work); 287a08990eaSMatheus Castello 288*c603bec3SKrzysztof Kozlowski /* store SOC to check changes */ 289a08990eaSMatheus Castello last_soc = chip->soc; 2906455a8a8SIskren Chernev max17040_check_changes(chip); 2918c0984e5SSebastian Reichel 292a08990eaSMatheus Castello /* check changes and send uevent */ 293*c603bec3SKrzysztof Kozlowski if (last_soc != chip->soc) 294a08990eaSMatheus Castello power_supply_changed(chip->battery); 295a08990eaSMatheus Castello 296e55a5061SIskren Chernev max17040_queue_work(chip); 2978c0984e5SSebastian Reichel } 2988c0984e5SSebastian Reichel 299ee2d433cSIskren Chernev /* Returns true if alert cause was SOC change, not low SOC */ 300ee2d433cSIskren Chernev static bool max17040_handle_soc_alert(struct max17040_chip *chip) 301ee2d433cSIskren Chernev { 302ee2d433cSIskren Chernev bool ret = true; 303ee2d433cSIskren Chernev u32 data; 304ee2d433cSIskren Chernev 305ee2d433cSIskren Chernev regmap_read(chip->regmap, MAX17040_STATUS, &data); 306ee2d433cSIskren Chernev 307ee2d433cSIskren Chernev if (data & MAX17040_STATUS_HD_MASK) { 308ee2d433cSIskren Chernev // this alert was caused by low soc 309ee2d433cSIskren Chernev ret = false; 310ee2d433cSIskren Chernev } 311ee2d433cSIskren Chernev if (data & MAX17040_STATUS_SC_MASK) { 312ee2d433cSIskren Chernev // soc change bit -- deassert to mark as handled 313ee2d433cSIskren Chernev regmap_write(chip->regmap, MAX17040_STATUS, 314ee2d433cSIskren Chernev data & ~MAX17040_STATUS_SC_MASK); 315ee2d433cSIskren Chernev } 316ee2d433cSIskren Chernev 317ee2d433cSIskren Chernev return ret; 318ee2d433cSIskren Chernev } 319ee2d433cSIskren Chernev 3202e17ed94SMatheus Castello static irqreturn_t max17040_thread_handler(int id, void *dev) 3212e17ed94SMatheus Castello { 3222e17ed94SMatheus Castello struct max17040_chip *chip = dev; 3232e17ed94SMatheus Castello 324ee2d433cSIskren Chernev if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip))) 325ee2d433cSIskren Chernev dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n"); 3266455a8a8SIskren Chernev 3272e17ed94SMatheus Castello /* read registers */ 3286455a8a8SIskren Chernev max17040_check_changes(chip); 3292e17ed94SMatheus Castello 3302e17ed94SMatheus Castello /* send uevent */ 3312e17ed94SMatheus Castello power_supply_changed(chip->battery); 3322e17ed94SMatheus Castello 333cccdd0caSMatheus Castello /* reset alert bit */ 3346455a8a8SIskren Chernev max17040_set_low_soc_alert(chip, chip->low_soc_alert); 335cccdd0caSMatheus Castello 3362e17ed94SMatheus Castello return IRQ_HANDLED; 3372e17ed94SMatheus Castello } 3382e17ed94SMatheus Castello 3392e17ed94SMatheus Castello static int max17040_enable_alert_irq(struct max17040_chip *chip) 3402e17ed94SMatheus Castello { 3412e17ed94SMatheus Castello struct i2c_client *client = chip->client; 3422e17ed94SMatheus Castello int ret; 3432e17ed94SMatheus Castello 3442e17ed94SMatheus Castello ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, 3458bb2314fSKrzysztof Kozlowski max17040_thread_handler, IRQF_ONESHOT, 3462e17ed94SMatheus Castello chip->battery->desc->name, chip); 3472e17ed94SMatheus Castello 3482e17ed94SMatheus Castello return ret; 3492e17ed94SMatheus Castello } 3502e17ed94SMatheus Castello 3512f38dc4dSMatheus Castello static int max17040_prop_writeable(struct power_supply *psy, 3522f38dc4dSMatheus Castello enum power_supply_property psp) 3532f38dc4dSMatheus Castello { 3542f38dc4dSMatheus Castello switch (psp) { 3552f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3562f38dc4dSMatheus Castello return 1; 3572f38dc4dSMatheus Castello default: 3582f38dc4dSMatheus Castello return 0; 3592f38dc4dSMatheus Castello } 3602f38dc4dSMatheus Castello } 3612f38dc4dSMatheus Castello 3622f38dc4dSMatheus Castello static int max17040_set_property(struct power_supply *psy, 3632f38dc4dSMatheus Castello enum power_supply_property psp, 3642f38dc4dSMatheus Castello const union power_supply_propval *val) 3652f38dc4dSMatheus Castello { 3662f38dc4dSMatheus Castello struct max17040_chip *chip = power_supply_get_drvdata(psy); 3672f38dc4dSMatheus Castello int ret; 3682f38dc4dSMatheus Castello 3692f38dc4dSMatheus Castello switch (psp) { 3702f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3714f7f8e87SIskren Chernev /* alert threshold can be programmed from 1% up to 16/32% */ 3724f7f8e87SIskren Chernev if ((val->intval < 1) || 3734f7f8e87SIskren Chernev (val->intval > (chip->quirk_double_soc ? 16 : 32))) { 3742f38dc4dSMatheus Castello ret = -EINVAL; 3752f38dc4dSMatheus Castello break; 3762f38dc4dSMatheus Castello } 3776455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, val->intval); 3782f38dc4dSMatheus Castello chip->low_soc_alert = val->intval; 3792f38dc4dSMatheus Castello break; 3802f38dc4dSMatheus Castello default: 3812f38dc4dSMatheus Castello ret = -EINVAL; 3822f38dc4dSMatheus Castello } 3832f38dc4dSMatheus Castello 3842f38dc4dSMatheus Castello return ret; 3852f38dc4dSMatheus Castello } 3862f38dc4dSMatheus Castello 3876455a8a8SIskren Chernev static int max17040_get_property(struct power_supply *psy, 3886455a8a8SIskren Chernev enum power_supply_property psp, 3896455a8a8SIskren Chernev union power_supply_propval *val) 3906455a8a8SIskren Chernev { 3916455a8a8SIskren Chernev struct max17040_chip *chip = power_supply_get_drvdata(psy); 3926455a8a8SIskren Chernev 3936455a8a8SIskren Chernev switch (psp) { 3946455a8a8SIskren Chernev case POWER_SUPPLY_PROP_ONLINE: 3956455a8a8SIskren Chernev val->intval = max17040_get_online(chip); 3966455a8a8SIskren Chernev break; 3976455a8a8SIskren Chernev case POWER_SUPPLY_PROP_VOLTAGE_NOW: 3986455a8a8SIskren Chernev val->intval = max17040_get_vcell(chip); 3996455a8a8SIskren Chernev break; 4006455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY: 4016455a8a8SIskren Chernev val->intval = max17040_get_soc(chip); 4026455a8a8SIskren Chernev break; 4036455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 4046455a8a8SIskren Chernev val->intval = chip->low_soc_alert; 4056455a8a8SIskren Chernev break; 4066455a8a8SIskren Chernev default: 4076455a8a8SIskren Chernev return -EINVAL; 4086455a8a8SIskren Chernev } 4096455a8a8SIskren Chernev return 0; 4106455a8a8SIskren Chernev } 4116455a8a8SIskren Chernev 4126455a8a8SIskren Chernev static const struct regmap_config max17040_regmap = { 4136455a8a8SIskren Chernev .reg_bits = 8, 4146455a8a8SIskren Chernev .reg_stride = 2, 4156455a8a8SIskren Chernev .val_bits = 16, 4166455a8a8SIskren Chernev .val_format_endian = REGMAP_ENDIAN_BIG, 4176455a8a8SIskren Chernev }; 4186455a8a8SIskren Chernev 4198c0984e5SSebastian Reichel static enum power_supply_property max17040_battery_props[] = { 4208c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 4218c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4228c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4232f38dc4dSMatheus Castello POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, 4248c0984e5SSebastian Reichel }; 4258c0984e5SSebastian Reichel 4268c0984e5SSebastian Reichel static const struct power_supply_desc max17040_battery_desc = { 4278c0984e5SSebastian Reichel .name = "battery", 4288c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 4298c0984e5SSebastian Reichel .get_property = max17040_get_property, 4302f38dc4dSMatheus Castello .set_property = max17040_set_property, 4312f38dc4dSMatheus Castello .property_is_writeable = max17040_prop_writeable, 4328c0984e5SSebastian Reichel .properties = max17040_battery_props, 4338c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(max17040_battery_props), 4348c0984e5SSebastian Reichel }; 4358c0984e5SSebastian Reichel 4368c0984e5SSebastian Reichel static int max17040_probe(struct i2c_client *client, 4378c0984e5SSebastian Reichel const struct i2c_device_id *id) 4388c0984e5SSebastian Reichel { 4394e9c406dSWolfram Sang struct i2c_adapter *adapter = client->adapter; 4408c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 4418c0984e5SSebastian Reichel struct max17040_chip *chip; 4424f7f8e87SIskren Chernev enum chip_id chip_id; 443ee2d433cSIskren Chernev bool enable_irq = false; 444cccdd0caSMatheus Castello int ret; 4458c0984e5SSebastian Reichel 4468c0984e5SSebastian Reichel if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 4478c0984e5SSebastian Reichel return -EIO; 4488c0984e5SSebastian Reichel 4498c0984e5SSebastian Reichel chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 4508c0984e5SSebastian Reichel if (!chip) 4518c0984e5SSebastian Reichel return -ENOMEM; 4528c0984e5SSebastian Reichel 4538c0984e5SSebastian Reichel chip->client = client; 4546455a8a8SIskren Chernev chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); 4558c0984e5SSebastian Reichel chip->pdata = client->dev.platform_data; 4564f7f8e87SIskren Chernev chip_id = (enum chip_id) id->driver_data; 4574f7f8e87SIskren Chernev if (client->dev.of_node) { 458cccdd0caSMatheus Castello ret = max17040_get_of_data(chip); 4596455a8a8SIskren Chernev if (ret) 460cccdd0caSMatheus Castello return ret; 4616dfbb2ceSKrzysztof Kozlowski chip_id = (uintptr_t)of_device_get_match_data(&client->dev); 4624f7f8e87SIskren Chernev } 4634f7f8e87SIskren Chernev chip->data = max17040_family[chip_id]; 4648c0984e5SSebastian Reichel 4658c0984e5SSebastian Reichel i2c_set_clientdata(client, chip); 4668c0984e5SSebastian Reichel psy_cfg.drv_data = chip; 4678c0984e5SSebastian Reichel 468e55a5061SIskren Chernev chip->battery = devm_power_supply_register(&client->dev, 4698c0984e5SSebastian Reichel &max17040_battery_desc, &psy_cfg); 4708c0984e5SSebastian Reichel if (IS_ERR(chip->battery)) { 4718c0984e5SSebastian Reichel dev_err(&client->dev, "failed: power supply register\n"); 4728c0984e5SSebastian Reichel return PTR_ERR(chip->battery); 4738c0984e5SSebastian Reichel } 4748c0984e5SSebastian Reichel 4756455a8a8SIskren Chernev ret = max17040_get_version(chip); 4766455a8a8SIskren Chernev if (ret < 0) 4776455a8a8SIskren Chernev return ret; 4786455a8a8SIskren Chernev dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret); 4796455a8a8SIskren Chernev 4804f7f8e87SIskren Chernev if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) 4816455a8a8SIskren Chernev max17040_reset(chip); 4828c0984e5SSebastian Reichel 4831779c6e3SIskren Chernev max17040_set_rcomp(chip, chip->rcomp); 4841779c6e3SIskren Chernev 4852e17ed94SMatheus Castello /* check interrupt */ 4864f7f8e87SIskren Chernev if (client->irq && chip->data.has_low_soc_alert) { 4876455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); 488cccdd0caSMatheus Castello if (ret) { 489cccdd0caSMatheus Castello dev_err(&client->dev, 490cccdd0caSMatheus Castello "Failed to set low SOC alert: err %d\n", ret); 491cccdd0caSMatheus Castello return ret; 492cccdd0caSMatheus Castello } 4932e17ed94SMatheus Castello 494ee2d433cSIskren Chernev enable_irq = true; 495ee2d433cSIskren Chernev } 496ee2d433cSIskren Chernev 497ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) { 498ee2d433cSIskren Chernev ret = max17040_set_soc_alert(chip, 1); 499ee2d433cSIskren Chernev if (ret) { 500ee2d433cSIskren Chernev dev_err(&client->dev, 501ee2d433cSIskren Chernev "Failed to set SOC alert: err %d\n", ret); 502ee2d433cSIskren Chernev return ret; 503ee2d433cSIskren Chernev } 504ee2d433cSIskren Chernev enable_irq = true; 505ee2d433cSIskren Chernev } else { 506ee2d433cSIskren Chernev /* soc alerts negate the need for polling */ 507ee2d433cSIskren Chernev INIT_DEFERRABLE_WORK(&chip->work, max17040_work); 508ee2d433cSIskren Chernev ret = devm_add_action(&client->dev, max17040_stop_work, chip); 509ee2d433cSIskren Chernev if (ret) 510ee2d433cSIskren Chernev return ret; 511ee2d433cSIskren Chernev max17040_queue_work(chip); 512ee2d433cSIskren Chernev } 513ee2d433cSIskren Chernev 514ee2d433cSIskren Chernev if (enable_irq) { 5152e17ed94SMatheus Castello ret = max17040_enable_alert_irq(chip); 5162e17ed94SMatheus Castello if (ret) { 5172e17ed94SMatheus Castello client->irq = 0; 5182e17ed94SMatheus Castello dev_warn(&client->dev, 5192e17ed94SMatheus Castello "Failed to get IRQ err %d\n", ret); 5202e17ed94SMatheus Castello } 5212e17ed94SMatheus Castello } 5222e17ed94SMatheus Castello 5238c0984e5SSebastian Reichel return 0; 5248c0984e5SSebastian Reichel } 5258c0984e5SSebastian Reichel 5268c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP 5278c0984e5SSebastian Reichel 5288c0984e5SSebastian Reichel static int max17040_suspend(struct device *dev) 5298c0984e5SSebastian Reichel { 5308c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 5318c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 5328c0984e5SSebastian Reichel 533ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) 534ee2d433cSIskren Chernev // disable soc alert to prevent wakeup 535ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 0); 536ee2d433cSIskren Chernev else 5378c0984e5SSebastian Reichel cancel_delayed_work(&chip->work); 5382e17ed94SMatheus Castello 539e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 5402e17ed94SMatheus Castello enable_irq_wake(client->irq); 5412e17ed94SMatheus Castello 5428c0984e5SSebastian Reichel return 0; 5438c0984e5SSebastian Reichel } 5448c0984e5SSebastian Reichel 5458c0984e5SSebastian Reichel static int max17040_resume(struct device *dev) 5468c0984e5SSebastian Reichel { 5478c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 5488c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 5498c0984e5SSebastian Reichel 550e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 5512e17ed94SMatheus Castello disable_irq_wake(client->irq); 5522e17ed94SMatheus Castello 553ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) 554ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 1); 555ee2d433cSIskren Chernev else 556e55a5061SIskren Chernev max17040_queue_work(chip); 557e55a5061SIskren Chernev 5588c0984e5SSebastian Reichel return 0; 5598c0984e5SSebastian Reichel } 5608c0984e5SSebastian Reichel 5618c0984e5SSebastian Reichel static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); 5628c0984e5SSebastian Reichel #define MAX17040_PM_OPS (&max17040_pm_ops) 5638c0984e5SSebastian Reichel 5648c0984e5SSebastian Reichel #else 5658c0984e5SSebastian Reichel 5668c0984e5SSebastian Reichel #define MAX17040_PM_OPS NULL 5678c0984e5SSebastian Reichel 5688c0984e5SSebastian Reichel #endif /* CONFIG_PM_SLEEP */ 5698c0984e5SSebastian Reichel 5708c0984e5SSebastian Reichel static const struct i2c_device_id max17040_id[] = { 5714f7f8e87SIskren Chernev { "max17040", ID_MAX17040 }, 5724f7f8e87SIskren Chernev { "max17041", ID_MAX17041 }, 5734f7f8e87SIskren Chernev { "max17043", ID_MAX17043 }, 5744f7f8e87SIskren Chernev { "max77836-battery", ID_MAX17043 }, 5754f7f8e87SIskren Chernev { "max17044", ID_MAX17044 }, 5764f7f8e87SIskren Chernev { "max17048", ID_MAX17048 }, 5774f7f8e87SIskren Chernev { "max17049", ID_MAX17049 }, 5784f7f8e87SIskren Chernev { "max17058", ID_MAX17058 }, 5794f7f8e87SIskren Chernev { "max17059", ID_MAX17059 }, 5804f7f8e87SIskren Chernev { /* sentinel */ } 5818c0984e5SSebastian Reichel }; 5828c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, max17040_id); 5838c0984e5SSebastian Reichel 584da28122cSJavier Martinez Canillas static const struct of_device_id max17040_of_match[] = { 5854f7f8e87SIskren Chernev { .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 }, 5864f7f8e87SIskren Chernev { .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 }, 5874f7f8e87SIskren Chernev { .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 }, 5884f7f8e87SIskren Chernev { .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 }, 5894f7f8e87SIskren Chernev { .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 }, 5904f7f8e87SIskren Chernev { .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 }, 5914f7f8e87SIskren Chernev { .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 }, 5924f7f8e87SIskren Chernev { .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 }, 5934f7f8e87SIskren Chernev { .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 }, 5944f7f8e87SIskren Chernev { /* sentinel */ }, 595da28122cSJavier Martinez Canillas }; 596da28122cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, max17040_of_match); 597da28122cSJavier Martinez Canillas 5988c0984e5SSebastian Reichel static struct i2c_driver max17040_i2c_driver = { 5998c0984e5SSebastian Reichel .driver = { 6008c0984e5SSebastian Reichel .name = "max17040", 601da28122cSJavier Martinez Canillas .of_match_table = max17040_of_match, 6028c0984e5SSebastian Reichel .pm = MAX17040_PM_OPS, 6038c0984e5SSebastian Reichel }, 6048c0984e5SSebastian Reichel .probe = max17040_probe, 6058c0984e5SSebastian Reichel .id_table = max17040_id, 6068c0984e5SSebastian Reichel }; 6078c0984e5SSebastian Reichel module_i2c_driver(max17040_i2c_driver); 6088c0984e5SSebastian Reichel 6098c0984e5SSebastian Reichel MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); 6108c0984e5SSebastian Reichel MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); 6118c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 612