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 { 222*ce0ae832SKrzysztof Kozlowski return 1; 2238c0984e5SSebastian Reichel } 2248c0984e5SSebastian Reichel 225cccdd0caSMatheus Castello static int max17040_get_of_data(struct max17040_chip *chip) 226cccdd0caSMatheus Castello { 227cccdd0caSMatheus Castello struct device *dev = &chip->client->dev; 2281779c6e3SIskren Chernev struct chip_data *data = &max17040_family[ 2297f977e64SIskren Chernev (uintptr_t) of_device_get_match_data(dev)]; 2301779c6e3SIskren Chernev int rcomp_len; 2311779c6e3SIskren Chernev u8 rcomp[2]; 232cccdd0caSMatheus Castello 2334f7f8e87SIskren Chernev chip->quirk_double_soc = device_property_read_bool(dev, 2344f7f8e87SIskren Chernev "maxim,double-soc"); 2354f7f8e87SIskren Chernev 236cccdd0caSMatheus Castello chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP; 237cccdd0caSMatheus Castello device_property_read_u32(dev, 238cccdd0caSMatheus Castello "maxim,alert-low-soc-level", 239cccdd0caSMatheus Castello &chip->low_soc_alert); 240cccdd0caSMatheus Castello 2414f7f8e87SIskren Chernev if (chip->low_soc_alert <= 0 || 2424f7f8e87SIskren Chernev chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) { 2436455a8a8SIskren Chernev dev_err(dev, "maxim,alert-low-soc-level out of bounds\n"); 244cccdd0caSMatheus Castello return -EINVAL; 2456455a8a8SIskren Chernev } 246cccdd0caSMatheus Castello 2471779c6e3SIskren Chernev rcomp_len = device_property_count_u8(dev, "maxim,rcomp"); 2481779c6e3SIskren Chernev chip->rcomp = MAX17040_RCOMP_DEFAULT; 2491779c6e3SIskren Chernev if (rcomp_len == data->rcomp_bytes) { 250a837f35cSKrzysztof Kozlowski if (!device_property_read_u8_array(dev, "maxim,rcomp", 251a837f35cSKrzysztof Kozlowski rcomp, rcomp_len)) 252a837f35cSKrzysztof Kozlowski chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] : 2531779c6e3SIskren Chernev rcomp[0] << 8; 2541779c6e3SIskren Chernev } else if (rcomp_len > 0) { 2551779c6e3SIskren Chernev dev_err(dev, "maxim,rcomp has incorrect length\n"); 2561779c6e3SIskren Chernev return -EINVAL; 2571779c6e3SIskren Chernev } 2581779c6e3SIskren Chernev 259cccdd0caSMatheus Castello return 0; 260cccdd0caSMatheus Castello } 261cccdd0caSMatheus Castello 2626455a8a8SIskren Chernev static void max17040_check_changes(struct max17040_chip *chip) 2632e17ed94SMatheus Castello { 2646455a8a8SIskren Chernev chip->soc = max17040_get_soc(chip); 2652e17ed94SMatheus Castello } 2662e17ed94SMatheus Castello 267e55a5061SIskren Chernev static void max17040_queue_work(struct max17040_chip *chip) 268e55a5061SIskren Chernev { 269e55a5061SIskren Chernev queue_delayed_work(system_power_efficient_wq, &chip->work, 270e55a5061SIskren Chernev MAX17040_DELAY); 271e55a5061SIskren Chernev } 272e55a5061SIskren Chernev 273e55a5061SIskren Chernev static void max17040_stop_work(void *data) 274e55a5061SIskren Chernev { 275e55a5061SIskren Chernev struct max17040_chip *chip = data; 276e55a5061SIskren Chernev 277e55a5061SIskren Chernev cancel_delayed_work_sync(&chip->work); 278e55a5061SIskren Chernev } 279e55a5061SIskren Chernev 2808c0984e5SSebastian Reichel static void max17040_work(struct work_struct *work) 2818c0984e5SSebastian Reichel { 2828c0984e5SSebastian Reichel struct max17040_chip *chip; 283c603bec3SKrzysztof Kozlowski int last_soc; 2848c0984e5SSebastian Reichel 2858c0984e5SSebastian Reichel chip = container_of(work, struct max17040_chip, work.work); 286a08990eaSMatheus Castello 287c603bec3SKrzysztof Kozlowski /* store SOC to check changes */ 288a08990eaSMatheus Castello last_soc = chip->soc; 2896455a8a8SIskren Chernev max17040_check_changes(chip); 2908c0984e5SSebastian Reichel 291a08990eaSMatheus Castello /* check changes and send uevent */ 292c603bec3SKrzysztof Kozlowski if (last_soc != chip->soc) 293a08990eaSMatheus Castello power_supply_changed(chip->battery); 294a08990eaSMatheus Castello 295e55a5061SIskren Chernev max17040_queue_work(chip); 2968c0984e5SSebastian Reichel } 2978c0984e5SSebastian Reichel 298ee2d433cSIskren Chernev /* Returns true if alert cause was SOC change, not low SOC */ 299ee2d433cSIskren Chernev static bool max17040_handle_soc_alert(struct max17040_chip *chip) 300ee2d433cSIskren Chernev { 301ee2d433cSIskren Chernev bool ret = true; 302ee2d433cSIskren Chernev u32 data; 303ee2d433cSIskren Chernev 304ee2d433cSIskren Chernev regmap_read(chip->regmap, MAX17040_STATUS, &data); 305ee2d433cSIskren Chernev 306ee2d433cSIskren Chernev if (data & MAX17040_STATUS_HD_MASK) { 307ee2d433cSIskren Chernev // this alert was caused by low soc 308ee2d433cSIskren Chernev ret = false; 309ee2d433cSIskren Chernev } 310ee2d433cSIskren Chernev if (data & MAX17040_STATUS_SC_MASK) { 311ee2d433cSIskren Chernev // soc change bit -- deassert to mark as handled 312ee2d433cSIskren Chernev regmap_write(chip->regmap, MAX17040_STATUS, 313ee2d433cSIskren Chernev data & ~MAX17040_STATUS_SC_MASK); 314ee2d433cSIskren Chernev } 315ee2d433cSIskren Chernev 316ee2d433cSIskren Chernev return ret; 317ee2d433cSIskren Chernev } 318ee2d433cSIskren Chernev 3192e17ed94SMatheus Castello static irqreturn_t max17040_thread_handler(int id, void *dev) 3202e17ed94SMatheus Castello { 3212e17ed94SMatheus Castello struct max17040_chip *chip = dev; 3222e17ed94SMatheus Castello 323ee2d433cSIskren Chernev if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip))) 324ee2d433cSIskren Chernev dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n"); 3256455a8a8SIskren Chernev 3262e17ed94SMatheus Castello /* read registers */ 3276455a8a8SIskren Chernev max17040_check_changes(chip); 3282e17ed94SMatheus Castello 3292e17ed94SMatheus Castello /* send uevent */ 3302e17ed94SMatheus Castello power_supply_changed(chip->battery); 3312e17ed94SMatheus Castello 332cccdd0caSMatheus Castello /* reset alert bit */ 3336455a8a8SIskren Chernev max17040_set_low_soc_alert(chip, chip->low_soc_alert); 334cccdd0caSMatheus Castello 3352e17ed94SMatheus Castello return IRQ_HANDLED; 3362e17ed94SMatheus Castello } 3372e17ed94SMatheus Castello 3382e17ed94SMatheus Castello static int max17040_enable_alert_irq(struct max17040_chip *chip) 3392e17ed94SMatheus Castello { 3402e17ed94SMatheus Castello struct i2c_client *client = chip->client; 3412e17ed94SMatheus Castello int ret; 3422e17ed94SMatheus Castello 3432e17ed94SMatheus Castello ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, 3448bb2314fSKrzysztof Kozlowski max17040_thread_handler, IRQF_ONESHOT, 3452e17ed94SMatheus Castello chip->battery->desc->name, chip); 3462e17ed94SMatheus Castello 3472e17ed94SMatheus Castello return ret; 3482e17ed94SMatheus Castello } 3492e17ed94SMatheus Castello 3502f38dc4dSMatheus Castello static int max17040_prop_writeable(struct power_supply *psy, 3512f38dc4dSMatheus Castello enum power_supply_property psp) 3522f38dc4dSMatheus Castello { 3532f38dc4dSMatheus Castello switch (psp) { 3542f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3552f38dc4dSMatheus Castello return 1; 3562f38dc4dSMatheus Castello default: 3572f38dc4dSMatheus Castello return 0; 3582f38dc4dSMatheus Castello } 3592f38dc4dSMatheus Castello } 3602f38dc4dSMatheus Castello 3612f38dc4dSMatheus Castello static int max17040_set_property(struct power_supply *psy, 3622f38dc4dSMatheus Castello enum power_supply_property psp, 3632f38dc4dSMatheus Castello const union power_supply_propval *val) 3642f38dc4dSMatheus Castello { 3652f38dc4dSMatheus Castello struct max17040_chip *chip = power_supply_get_drvdata(psy); 3662f38dc4dSMatheus Castello int ret; 3672f38dc4dSMatheus Castello 3682f38dc4dSMatheus Castello switch (psp) { 3692f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3704f7f8e87SIskren Chernev /* alert threshold can be programmed from 1% up to 16/32% */ 3714f7f8e87SIskren Chernev if ((val->intval < 1) || 3724f7f8e87SIskren Chernev (val->intval > (chip->quirk_double_soc ? 16 : 32))) { 3732f38dc4dSMatheus Castello ret = -EINVAL; 3742f38dc4dSMatheus Castello break; 3752f38dc4dSMatheus Castello } 3766455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, val->intval); 3772f38dc4dSMatheus Castello chip->low_soc_alert = val->intval; 3782f38dc4dSMatheus Castello break; 3792f38dc4dSMatheus Castello default: 3802f38dc4dSMatheus Castello ret = -EINVAL; 3812f38dc4dSMatheus Castello } 3822f38dc4dSMatheus Castello 3832f38dc4dSMatheus Castello return ret; 3842f38dc4dSMatheus Castello } 3852f38dc4dSMatheus Castello 3866455a8a8SIskren Chernev static int max17040_get_property(struct power_supply *psy, 3876455a8a8SIskren Chernev enum power_supply_property psp, 3886455a8a8SIskren Chernev union power_supply_propval *val) 3896455a8a8SIskren Chernev { 3906455a8a8SIskren Chernev struct max17040_chip *chip = power_supply_get_drvdata(psy); 3916455a8a8SIskren Chernev 3926455a8a8SIskren Chernev switch (psp) { 3936455a8a8SIskren Chernev case POWER_SUPPLY_PROP_ONLINE: 3946455a8a8SIskren Chernev val->intval = max17040_get_online(chip); 3956455a8a8SIskren Chernev break; 3966455a8a8SIskren Chernev case POWER_SUPPLY_PROP_VOLTAGE_NOW: 3976455a8a8SIskren Chernev val->intval = max17040_get_vcell(chip); 3986455a8a8SIskren Chernev break; 3996455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY: 4006455a8a8SIskren Chernev val->intval = max17040_get_soc(chip); 4016455a8a8SIskren Chernev break; 4026455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 4036455a8a8SIskren Chernev val->intval = chip->low_soc_alert; 4046455a8a8SIskren Chernev break; 4056455a8a8SIskren Chernev default: 4066455a8a8SIskren Chernev return -EINVAL; 4076455a8a8SIskren Chernev } 4086455a8a8SIskren Chernev return 0; 4096455a8a8SIskren Chernev } 4106455a8a8SIskren Chernev 4116455a8a8SIskren Chernev static const struct regmap_config max17040_regmap = { 4126455a8a8SIskren Chernev .reg_bits = 8, 4136455a8a8SIskren Chernev .reg_stride = 2, 4146455a8a8SIskren Chernev .val_bits = 16, 4156455a8a8SIskren Chernev .val_format_endian = REGMAP_ENDIAN_BIG, 4166455a8a8SIskren Chernev }; 4176455a8a8SIskren Chernev 4188c0984e5SSebastian Reichel static enum power_supply_property max17040_battery_props[] = { 4198c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 4208c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4218c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4222f38dc4dSMatheus Castello POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, 4238c0984e5SSebastian Reichel }; 4248c0984e5SSebastian Reichel 4258c0984e5SSebastian Reichel static const struct power_supply_desc max17040_battery_desc = { 4268c0984e5SSebastian Reichel .name = "battery", 4278c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 4288c0984e5SSebastian Reichel .get_property = max17040_get_property, 4292f38dc4dSMatheus Castello .set_property = max17040_set_property, 4302f38dc4dSMatheus Castello .property_is_writeable = max17040_prop_writeable, 4318c0984e5SSebastian Reichel .properties = max17040_battery_props, 4328c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(max17040_battery_props), 4338c0984e5SSebastian Reichel }; 4348c0984e5SSebastian Reichel 4358c0984e5SSebastian Reichel static int max17040_probe(struct i2c_client *client, 4368c0984e5SSebastian Reichel const struct i2c_device_id *id) 4378c0984e5SSebastian Reichel { 4384e9c406dSWolfram Sang struct i2c_adapter *adapter = client->adapter; 4398c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 4408c0984e5SSebastian Reichel struct max17040_chip *chip; 4414f7f8e87SIskren Chernev enum chip_id chip_id; 442ee2d433cSIskren Chernev bool enable_irq = false; 443cccdd0caSMatheus Castello int ret; 4448c0984e5SSebastian Reichel 4458c0984e5SSebastian Reichel if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 4468c0984e5SSebastian Reichel return -EIO; 4478c0984e5SSebastian Reichel 4488c0984e5SSebastian Reichel chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 4498c0984e5SSebastian Reichel if (!chip) 4508c0984e5SSebastian Reichel return -ENOMEM; 4518c0984e5SSebastian Reichel 4528c0984e5SSebastian Reichel chip->client = client; 4536455a8a8SIskren Chernev chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); 4548c0984e5SSebastian Reichel chip->pdata = client->dev.platform_data; 4554f7f8e87SIskren Chernev chip_id = (enum chip_id) id->driver_data; 4564f7f8e87SIskren Chernev if (client->dev.of_node) { 457cccdd0caSMatheus Castello ret = max17040_get_of_data(chip); 4586455a8a8SIskren Chernev if (ret) 459cccdd0caSMatheus Castello return ret; 4606dfbb2ceSKrzysztof Kozlowski chip_id = (uintptr_t)of_device_get_match_data(&client->dev); 4614f7f8e87SIskren Chernev } 4624f7f8e87SIskren Chernev chip->data = max17040_family[chip_id]; 4638c0984e5SSebastian Reichel 4648c0984e5SSebastian Reichel i2c_set_clientdata(client, chip); 4658c0984e5SSebastian Reichel psy_cfg.drv_data = chip; 4668c0984e5SSebastian Reichel 467e55a5061SIskren Chernev chip->battery = devm_power_supply_register(&client->dev, 4688c0984e5SSebastian Reichel &max17040_battery_desc, &psy_cfg); 4698c0984e5SSebastian Reichel if (IS_ERR(chip->battery)) { 4708c0984e5SSebastian Reichel dev_err(&client->dev, "failed: power supply register\n"); 4718c0984e5SSebastian Reichel return PTR_ERR(chip->battery); 4728c0984e5SSebastian Reichel } 4738c0984e5SSebastian Reichel 4746455a8a8SIskren Chernev ret = max17040_get_version(chip); 4756455a8a8SIskren Chernev if (ret < 0) 4766455a8a8SIskren Chernev return ret; 4776455a8a8SIskren Chernev dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret); 4786455a8a8SIskren Chernev 4794f7f8e87SIskren Chernev if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) 4806455a8a8SIskren Chernev max17040_reset(chip); 4818c0984e5SSebastian Reichel 4821779c6e3SIskren Chernev max17040_set_rcomp(chip, chip->rcomp); 4831779c6e3SIskren Chernev 4842e17ed94SMatheus Castello /* check interrupt */ 4854f7f8e87SIskren Chernev if (client->irq && chip->data.has_low_soc_alert) { 4866455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); 487cccdd0caSMatheus Castello if (ret) { 488cccdd0caSMatheus Castello dev_err(&client->dev, 489cccdd0caSMatheus Castello "Failed to set low SOC alert: err %d\n", ret); 490cccdd0caSMatheus Castello return ret; 491cccdd0caSMatheus Castello } 4922e17ed94SMatheus Castello 493ee2d433cSIskren Chernev enable_irq = true; 494ee2d433cSIskren Chernev } 495ee2d433cSIskren Chernev 496ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) { 497ee2d433cSIskren Chernev ret = max17040_set_soc_alert(chip, 1); 498ee2d433cSIskren Chernev if (ret) { 499ee2d433cSIskren Chernev dev_err(&client->dev, 500ee2d433cSIskren Chernev "Failed to set SOC alert: err %d\n", ret); 501ee2d433cSIskren Chernev return ret; 502ee2d433cSIskren Chernev } 503ee2d433cSIskren Chernev enable_irq = true; 504ee2d433cSIskren Chernev } else { 505ee2d433cSIskren Chernev /* soc alerts negate the need for polling */ 506ee2d433cSIskren Chernev INIT_DEFERRABLE_WORK(&chip->work, max17040_work); 507ee2d433cSIskren Chernev ret = devm_add_action(&client->dev, max17040_stop_work, chip); 508ee2d433cSIskren Chernev if (ret) 509ee2d433cSIskren Chernev return ret; 510ee2d433cSIskren Chernev max17040_queue_work(chip); 511ee2d433cSIskren Chernev } 512ee2d433cSIskren Chernev 513ee2d433cSIskren Chernev if (enable_irq) { 5142e17ed94SMatheus Castello ret = max17040_enable_alert_irq(chip); 5152e17ed94SMatheus Castello if (ret) { 5162e17ed94SMatheus Castello client->irq = 0; 5172e17ed94SMatheus Castello dev_warn(&client->dev, 5182e17ed94SMatheus Castello "Failed to get IRQ err %d\n", ret); 5192e17ed94SMatheus Castello } 5202e17ed94SMatheus Castello } 5212e17ed94SMatheus Castello 5228c0984e5SSebastian Reichel return 0; 5238c0984e5SSebastian Reichel } 5248c0984e5SSebastian Reichel 5258c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP 5268c0984e5SSebastian Reichel 5278c0984e5SSebastian Reichel static int max17040_suspend(struct device *dev) 5288c0984e5SSebastian Reichel { 5298c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 5308c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 5318c0984e5SSebastian Reichel 532ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) 533ee2d433cSIskren Chernev // disable soc alert to prevent wakeup 534ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 0); 535ee2d433cSIskren Chernev else 5368c0984e5SSebastian Reichel cancel_delayed_work(&chip->work); 5372e17ed94SMatheus Castello 538e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 5392e17ed94SMatheus Castello enable_irq_wake(client->irq); 5402e17ed94SMatheus Castello 5418c0984e5SSebastian Reichel return 0; 5428c0984e5SSebastian Reichel } 5438c0984e5SSebastian Reichel 5448c0984e5SSebastian Reichel static int max17040_resume(struct device *dev) 5458c0984e5SSebastian Reichel { 5468c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 5478c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 5488c0984e5SSebastian Reichel 549e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 5502e17ed94SMatheus Castello disable_irq_wake(client->irq); 5512e17ed94SMatheus Castello 552ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) 553ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 1); 554ee2d433cSIskren Chernev else 555e55a5061SIskren Chernev max17040_queue_work(chip); 556e55a5061SIskren Chernev 5578c0984e5SSebastian Reichel return 0; 5588c0984e5SSebastian Reichel } 5598c0984e5SSebastian Reichel 5608c0984e5SSebastian Reichel static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); 5618c0984e5SSebastian Reichel #define MAX17040_PM_OPS (&max17040_pm_ops) 5628c0984e5SSebastian Reichel 5638c0984e5SSebastian Reichel #else 5648c0984e5SSebastian Reichel 5658c0984e5SSebastian Reichel #define MAX17040_PM_OPS NULL 5668c0984e5SSebastian Reichel 5678c0984e5SSebastian Reichel #endif /* CONFIG_PM_SLEEP */ 5688c0984e5SSebastian Reichel 5698c0984e5SSebastian Reichel static const struct i2c_device_id max17040_id[] = { 5704f7f8e87SIskren Chernev { "max17040", ID_MAX17040 }, 5714f7f8e87SIskren Chernev { "max17041", ID_MAX17041 }, 5724f7f8e87SIskren Chernev { "max17043", ID_MAX17043 }, 5734f7f8e87SIskren Chernev { "max77836-battery", ID_MAX17043 }, 5744f7f8e87SIskren Chernev { "max17044", ID_MAX17044 }, 5754f7f8e87SIskren Chernev { "max17048", ID_MAX17048 }, 5764f7f8e87SIskren Chernev { "max17049", ID_MAX17049 }, 5774f7f8e87SIskren Chernev { "max17058", ID_MAX17058 }, 5784f7f8e87SIskren Chernev { "max17059", ID_MAX17059 }, 5794f7f8e87SIskren Chernev { /* sentinel */ } 5808c0984e5SSebastian Reichel }; 5818c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, max17040_id); 5828c0984e5SSebastian Reichel 583da28122cSJavier Martinez Canillas static const struct of_device_id max17040_of_match[] = { 5844f7f8e87SIskren Chernev { .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 }, 5854f7f8e87SIskren Chernev { .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 }, 5864f7f8e87SIskren Chernev { .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 }, 5874f7f8e87SIskren Chernev { .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 }, 5884f7f8e87SIskren Chernev { .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 }, 5894f7f8e87SIskren Chernev { .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 }, 5904f7f8e87SIskren Chernev { .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 }, 5914f7f8e87SIskren Chernev { .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 }, 5924f7f8e87SIskren Chernev { .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 }, 5934f7f8e87SIskren Chernev { /* sentinel */ }, 594da28122cSJavier Martinez Canillas }; 595da28122cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, max17040_of_match); 596da28122cSJavier Martinez Canillas 5978c0984e5SSebastian Reichel static struct i2c_driver max17040_i2c_driver = { 5988c0984e5SSebastian Reichel .driver = { 5998c0984e5SSebastian Reichel .name = "max17040", 600da28122cSJavier Martinez Canillas .of_match_table = max17040_of_match, 6018c0984e5SSebastian Reichel .pm = MAX17040_PM_OPS, 6028c0984e5SSebastian Reichel }, 6038c0984e5SSebastian Reichel .probe = max17040_probe, 6048c0984e5SSebastian Reichel .id_table = max17040_id, 6058c0984e5SSebastian Reichel }; 6068c0984e5SSebastian Reichel module_i2c_driver(max17040_i2c_driver); 6078c0984e5SSebastian Reichel 6088c0984e5SSebastian Reichel MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); 6098c0984e5SSebastian Reichel MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); 6108c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 611