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 28*ee2d433cSIskren 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 37*ee2d433cSIskren Chernev #define MAX17040_ALSC_MASK 0x40 38cccdd0caSMatheus Castello #define MAX17040_ATHD_DEFAULT_POWER_UP 4 39*ee2d433cSIskren Chernev #define MAX17040_STATUS_HD_MASK 0x1000 40*ee2d433cSIskren 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; 62*ee2d433cSIskren 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, 73*ee2d433cSIskren 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, 82*ee2d433cSIskren 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, 91*ee2d433cSIskren 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, 100*ee2d433cSIskren 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, 109*ee2d433cSIskren 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, 118*ee2d433cSIskren 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, 127*ee2d433cSIskren 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, 136*ee2d433cSIskren 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 172*ee2d433cSIskren Chernev static int max17040_set_soc_alert(struct max17040_chip *chip, bool enable) 173*ee2d433cSIskren Chernev { 174*ee2d433cSIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, 175*ee2d433cSIskren Chernev MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0); 176*ee2d433cSIskren Chernev } 177*ee2d433cSIskren 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[ 2501779c6e3SIskren Chernev (enum chip_id) 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) { 2711779c6e3SIskren Chernev device_property_read_u8_array(dev, "maxim,rcomp", 2721779c6e3SIskren Chernev rcomp, rcomp_len); 2731779c6e3SIskren Chernev chip->rcomp = rcomp_len == 2 ? 2741779c6e3SIskren Chernev rcomp[0] << 8 | rcomp[1] : 2751779c6e3SIskren Chernev rcomp[0] << 8; 2761779c6e3SIskren Chernev } else if (rcomp_len > 0) { 2771779c6e3SIskren Chernev dev_err(dev, "maxim,rcomp has incorrect length\n"); 2781779c6e3SIskren Chernev return -EINVAL; 2791779c6e3SIskren Chernev } 2801779c6e3SIskren Chernev 281cccdd0caSMatheus Castello return 0; 282cccdd0caSMatheus Castello } 283cccdd0caSMatheus Castello 2846455a8a8SIskren Chernev static void max17040_check_changes(struct max17040_chip *chip) 2852e17ed94SMatheus Castello { 2866455a8a8SIskren Chernev chip->soc = max17040_get_soc(chip); 2876455a8a8SIskren Chernev chip->status = max17040_get_status(chip); 2882e17ed94SMatheus Castello } 2892e17ed94SMatheus Castello 290e55a5061SIskren Chernev static void max17040_queue_work(struct max17040_chip *chip) 291e55a5061SIskren Chernev { 292e55a5061SIskren Chernev queue_delayed_work(system_power_efficient_wq, &chip->work, 293e55a5061SIskren Chernev MAX17040_DELAY); 294e55a5061SIskren Chernev } 295e55a5061SIskren Chernev 296e55a5061SIskren Chernev static void max17040_stop_work(void *data) 297e55a5061SIskren Chernev { 298e55a5061SIskren Chernev struct max17040_chip *chip = data; 299e55a5061SIskren Chernev 300e55a5061SIskren Chernev cancel_delayed_work_sync(&chip->work); 301e55a5061SIskren Chernev } 302e55a5061SIskren Chernev 3038c0984e5SSebastian Reichel static void max17040_work(struct work_struct *work) 3048c0984e5SSebastian Reichel { 3058c0984e5SSebastian Reichel struct max17040_chip *chip; 306a08990eaSMatheus Castello int last_soc, last_status; 3078c0984e5SSebastian Reichel 3088c0984e5SSebastian Reichel chip = container_of(work, struct max17040_chip, work.work); 309a08990eaSMatheus Castello 310a08990eaSMatheus Castello /* store SOC and status to check changes */ 311a08990eaSMatheus Castello last_soc = chip->soc; 312a08990eaSMatheus Castello last_status = chip->status; 3136455a8a8SIskren Chernev max17040_check_changes(chip); 3148c0984e5SSebastian Reichel 315a08990eaSMatheus Castello /* check changes and send uevent */ 316a08990eaSMatheus Castello if (last_soc != chip->soc || last_status != chip->status) 317a08990eaSMatheus Castello power_supply_changed(chip->battery); 318a08990eaSMatheus Castello 319e55a5061SIskren Chernev max17040_queue_work(chip); 3208c0984e5SSebastian Reichel } 3218c0984e5SSebastian Reichel 322*ee2d433cSIskren Chernev /* Returns true if alert cause was SOC change, not low SOC */ 323*ee2d433cSIskren Chernev static bool max17040_handle_soc_alert(struct max17040_chip *chip) 324*ee2d433cSIskren Chernev { 325*ee2d433cSIskren Chernev bool ret = true; 326*ee2d433cSIskren Chernev u32 data; 327*ee2d433cSIskren Chernev 328*ee2d433cSIskren Chernev regmap_read(chip->regmap, MAX17040_STATUS, &data); 329*ee2d433cSIskren Chernev 330*ee2d433cSIskren Chernev if (data & MAX17040_STATUS_HD_MASK) { 331*ee2d433cSIskren Chernev // this alert was caused by low soc 332*ee2d433cSIskren Chernev ret = false; 333*ee2d433cSIskren Chernev } 334*ee2d433cSIskren Chernev if (data & MAX17040_STATUS_SC_MASK) { 335*ee2d433cSIskren Chernev // soc change bit -- deassert to mark as handled 336*ee2d433cSIskren Chernev regmap_write(chip->regmap, MAX17040_STATUS, 337*ee2d433cSIskren Chernev data & ~MAX17040_STATUS_SC_MASK); 338*ee2d433cSIskren Chernev } 339*ee2d433cSIskren Chernev 340*ee2d433cSIskren Chernev return ret; 341*ee2d433cSIskren Chernev } 342*ee2d433cSIskren Chernev 3432e17ed94SMatheus Castello static irqreturn_t max17040_thread_handler(int id, void *dev) 3442e17ed94SMatheus Castello { 3452e17ed94SMatheus Castello struct max17040_chip *chip = dev; 3462e17ed94SMatheus Castello 347*ee2d433cSIskren Chernev if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip))) 348*ee2d433cSIskren Chernev dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n"); 3496455a8a8SIskren Chernev 3502e17ed94SMatheus Castello /* read registers */ 3516455a8a8SIskren Chernev max17040_check_changes(chip); 3522e17ed94SMatheus Castello 3532e17ed94SMatheus Castello /* send uevent */ 3542e17ed94SMatheus Castello power_supply_changed(chip->battery); 3552e17ed94SMatheus Castello 356cccdd0caSMatheus Castello /* reset alert bit */ 3576455a8a8SIskren Chernev max17040_set_low_soc_alert(chip, chip->low_soc_alert); 358cccdd0caSMatheus Castello 3592e17ed94SMatheus Castello return IRQ_HANDLED; 3602e17ed94SMatheus Castello } 3612e17ed94SMatheus Castello 3622e17ed94SMatheus Castello static int max17040_enable_alert_irq(struct max17040_chip *chip) 3632e17ed94SMatheus Castello { 3642e17ed94SMatheus Castello struct i2c_client *client = chip->client; 3652e17ed94SMatheus Castello unsigned int flags; 3662e17ed94SMatheus Castello int ret; 3672e17ed94SMatheus Castello 3682e17ed94SMatheus Castello flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; 3692e17ed94SMatheus Castello ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, 3702e17ed94SMatheus Castello max17040_thread_handler, flags, 3712e17ed94SMatheus Castello chip->battery->desc->name, chip); 3722e17ed94SMatheus Castello 3732e17ed94SMatheus Castello return ret; 3742e17ed94SMatheus Castello } 3752e17ed94SMatheus Castello 3762f38dc4dSMatheus Castello static int max17040_prop_writeable(struct power_supply *psy, 3772f38dc4dSMatheus Castello enum power_supply_property psp) 3782f38dc4dSMatheus Castello { 3792f38dc4dSMatheus Castello switch (psp) { 3802f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3812f38dc4dSMatheus Castello return 1; 3822f38dc4dSMatheus Castello default: 3832f38dc4dSMatheus Castello return 0; 3842f38dc4dSMatheus Castello } 3852f38dc4dSMatheus Castello } 3862f38dc4dSMatheus Castello 3872f38dc4dSMatheus Castello static int max17040_set_property(struct power_supply *psy, 3882f38dc4dSMatheus Castello enum power_supply_property psp, 3892f38dc4dSMatheus Castello const union power_supply_propval *val) 3902f38dc4dSMatheus Castello { 3912f38dc4dSMatheus Castello struct max17040_chip *chip = power_supply_get_drvdata(psy); 3922f38dc4dSMatheus Castello int ret; 3932f38dc4dSMatheus Castello 3942f38dc4dSMatheus Castello switch (psp) { 3952f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3964f7f8e87SIskren Chernev /* alert threshold can be programmed from 1% up to 16/32% */ 3974f7f8e87SIskren Chernev if ((val->intval < 1) || 3984f7f8e87SIskren Chernev (val->intval > (chip->quirk_double_soc ? 16 : 32))) { 3992f38dc4dSMatheus Castello ret = -EINVAL; 4002f38dc4dSMatheus Castello break; 4012f38dc4dSMatheus Castello } 4026455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, val->intval); 4032f38dc4dSMatheus Castello chip->low_soc_alert = val->intval; 4042f38dc4dSMatheus Castello break; 4052f38dc4dSMatheus Castello default: 4062f38dc4dSMatheus Castello ret = -EINVAL; 4072f38dc4dSMatheus Castello } 4082f38dc4dSMatheus Castello 4092f38dc4dSMatheus Castello return ret; 4102f38dc4dSMatheus Castello } 4112f38dc4dSMatheus Castello 4126455a8a8SIskren Chernev static int max17040_get_property(struct power_supply *psy, 4136455a8a8SIskren Chernev enum power_supply_property psp, 4146455a8a8SIskren Chernev union power_supply_propval *val) 4156455a8a8SIskren Chernev { 4166455a8a8SIskren Chernev struct max17040_chip *chip = power_supply_get_drvdata(psy); 4176455a8a8SIskren Chernev 4186455a8a8SIskren Chernev switch (psp) { 4196455a8a8SIskren Chernev case POWER_SUPPLY_PROP_STATUS: 4206455a8a8SIskren Chernev val->intval = max17040_get_status(chip); 4216455a8a8SIskren Chernev break; 4226455a8a8SIskren Chernev case POWER_SUPPLY_PROP_ONLINE: 4236455a8a8SIskren Chernev val->intval = max17040_get_online(chip); 4246455a8a8SIskren Chernev break; 4256455a8a8SIskren Chernev case POWER_SUPPLY_PROP_VOLTAGE_NOW: 4266455a8a8SIskren Chernev val->intval = max17040_get_vcell(chip); 4276455a8a8SIskren Chernev break; 4286455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY: 4296455a8a8SIskren Chernev val->intval = max17040_get_soc(chip); 4306455a8a8SIskren Chernev break; 4316455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 4326455a8a8SIskren Chernev val->intval = chip->low_soc_alert; 4336455a8a8SIskren Chernev break; 4346455a8a8SIskren Chernev default: 4356455a8a8SIskren Chernev return -EINVAL; 4366455a8a8SIskren Chernev } 4376455a8a8SIskren Chernev return 0; 4386455a8a8SIskren Chernev } 4396455a8a8SIskren Chernev 4406455a8a8SIskren Chernev static const struct regmap_config max17040_regmap = { 4416455a8a8SIskren Chernev .reg_bits = 8, 4426455a8a8SIskren Chernev .reg_stride = 2, 4436455a8a8SIskren Chernev .val_bits = 16, 4446455a8a8SIskren Chernev .val_format_endian = REGMAP_ENDIAN_BIG, 4456455a8a8SIskren Chernev }; 4466455a8a8SIskren Chernev 4478c0984e5SSebastian Reichel static enum power_supply_property max17040_battery_props[] = { 4488c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4498c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 4508c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4518c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4522f38dc4dSMatheus Castello POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, 4538c0984e5SSebastian Reichel }; 4548c0984e5SSebastian Reichel 4558c0984e5SSebastian Reichel static const struct power_supply_desc max17040_battery_desc = { 4568c0984e5SSebastian Reichel .name = "battery", 4578c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 4588c0984e5SSebastian Reichel .get_property = max17040_get_property, 4592f38dc4dSMatheus Castello .set_property = max17040_set_property, 4602f38dc4dSMatheus Castello .property_is_writeable = max17040_prop_writeable, 4618c0984e5SSebastian Reichel .properties = max17040_battery_props, 4628c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(max17040_battery_props), 4638c0984e5SSebastian Reichel }; 4648c0984e5SSebastian Reichel 4658c0984e5SSebastian Reichel static int max17040_probe(struct i2c_client *client, 4668c0984e5SSebastian Reichel const struct i2c_device_id *id) 4678c0984e5SSebastian Reichel { 4684e9c406dSWolfram Sang struct i2c_adapter *adapter = client->adapter; 4698c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 4708c0984e5SSebastian Reichel struct max17040_chip *chip; 4714f7f8e87SIskren Chernev enum chip_id chip_id; 472*ee2d433cSIskren Chernev bool enable_irq = false; 473cccdd0caSMatheus Castello int ret; 4748c0984e5SSebastian Reichel 4758c0984e5SSebastian Reichel if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 4768c0984e5SSebastian Reichel return -EIO; 4778c0984e5SSebastian Reichel 4788c0984e5SSebastian Reichel chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 4798c0984e5SSebastian Reichel if (!chip) 4808c0984e5SSebastian Reichel return -ENOMEM; 4818c0984e5SSebastian Reichel 4828c0984e5SSebastian Reichel chip->client = client; 4836455a8a8SIskren Chernev chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); 4848c0984e5SSebastian Reichel chip->pdata = client->dev.platform_data; 4854f7f8e87SIskren Chernev chip_id = (enum chip_id) id->driver_data; 4864f7f8e87SIskren Chernev if (client->dev.of_node) { 487cccdd0caSMatheus Castello ret = max17040_get_of_data(chip); 4886455a8a8SIskren Chernev if (ret) 489cccdd0caSMatheus Castello return ret; 4904f7f8e87SIskren Chernev chip_id = (enum chip_id) (uintptr_t) 4914f7f8e87SIskren Chernev of_device_get_match_data(&client->dev); 4924f7f8e87SIskren Chernev } 4934f7f8e87SIskren Chernev chip->data = max17040_family[chip_id]; 4948c0984e5SSebastian Reichel 4958c0984e5SSebastian Reichel i2c_set_clientdata(client, chip); 4968c0984e5SSebastian Reichel psy_cfg.drv_data = chip; 4978c0984e5SSebastian Reichel 498e55a5061SIskren Chernev chip->battery = devm_power_supply_register(&client->dev, 4998c0984e5SSebastian Reichel &max17040_battery_desc, &psy_cfg); 5008c0984e5SSebastian Reichel if (IS_ERR(chip->battery)) { 5018c0984e5SSebastian Reichel dev_err(&client->dev, "failed: power supply register\n"); 5028c0984e5SSebastian Reichel return PTR_ERR(chip->battery); 5038c0984e5SSebastian Reichel } 5048c0984e5SSebastian Reichel 5056455a8a8SIskren Chernev ret = max17040_get_version(chip); 5066455a8a8SIskren Chernev if (ret < 0) 5076455a8a8SIskren Chernev return ret; 5086455a8a8SIskren Chernev dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret); 5096455a8a8SIskren Chernev 5104f7f8e87SIskren Chernev if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) 5116455a8a8SIskren Chernev max17040_reset(chip); 5128c0984e5SSebastian Reichel 5131779c6e3SIskren Chernev max17040_set_rcomp(chip, chip->rcomp); 5141779c6e3SIskren Chernev 5152e17ed94SMatheus Castello /* check interrupt */ 5164f7f8e87SIskren Chernev if (client->irq && chip->data.has_low_soc_alert) { 5176455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); 518cccdd0caSMatheus Castello if (ret) { 519cccdd0caSMatheus Castello dev_err(&client->dev, 520cccdd0caSMatheus Castello "Failed to set low SOC alert: err %d\n", ret); 521cccdd0caSMatheus Castello return ret; 522cccdd0caSMatheus Castello } 5232e17ed94SMatheus Castello 524*ee2d433cSIskren Chernev enable_irq = true; 525*ee2d433cSIskren Chernev } 526*ee2d433cSIskren Chernev 527*ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) { 528*ee2d433cSIskren Chernev ret = max17040_set_soc_alert(chip, 1); 529*ee2d433cSIskren Chernev if (ret) { 530*ee2d433cSIskren Chernev dev_err(&client->dev, 531*ee2d433cSIskren Chernev "Failed to set SOC alert: err %d\n", ret); 532*ee2d433cSIskren Chernev return ret; 533*ee2d433cSIskren Chernev } 534*ee2d433cSIskren Chernev enable_irq = true; 535*ee2d433cSIskren Chernev } else { 536*ee2d433cSIskren Chernev /* soc alerts negate the need for polling */ 537*ee2d433cSIskren Chernev INIT_DEFERRABLE_WORK(&chip->work, max17040_work); 538*ee2d433cSIskren Chernev ret = devm_add_action(&client->dev, max17040_stop_work, chip); 539*ee2d433cSIskren Chernev if (ret) 540*ee2d433cSIskren Chernev return ret; 541*ee2d433cSIskren Chernev max17040_queue_work(chip); 542*ee2d433cSIskren Chernev } 543*ee2d433cSIskren Chernev 544*ee2d433cSIskren Chernev if (enable_irq) { 5452e17ed94SMatheus Castello ret = max17040_enable_alert_irq(chip); 5462e17ed94SMatheus Castello if (ret) { 5472e17ed94SMatheus Castello client->irq = 0; 5482e17ed94SMatheus Castello dev_warn(&client->dev, 5492e17ed94SMatheus Castello "Failed to get IRQ err %d\n", ret); 5502e17ed94SMatheus Castello } 5512e17ed94SMatheus Castello } 5522e17ed94SMatheus Castello 5538c0984e5SSebastian Reichel return 0; 5548c0984e5SSebastian Reichel } 5558c0984e5SSebastian Reichel 5568c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP 5578c0984e5SSebastian Reichel 5588c0984e5SSebastian Reichel static int max17040_suspend(struct device *dev) 5598c0984e5SSebastian Reichel { 5608c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 5618c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 5628c0984e5SSebastian Reichel 563*ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) 564*ee2d433cSIskren Chernev // disable soc alert to prevent wakeup 565*ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 0); 566*ee2d433cSIskren Chernev else 5678c0984e5SSebastian Reichel cancel_delayed_work(&chip->work); 5682e17ed94SMatheus Castello 569e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 5702e17ed94SMatheus Castello enable_irq_wake(client->irq); 5712e17ed94SMatheus Castello 5728c0984e5SSebastian Reichel return 0; 5738c0984e5SSebastian Reichel } 5748c0984e5SSebastian Reichel 5758c0984e5SSebastian Reichel static int max17040_resume(struct device *dev) 5768c0984e5SSebastian Reichel { 5778c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 5788c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 5798c0984e5SSebastian Reichel 580e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 5812e17ed94SMatheus Castello disable_irq_wake(client->irq); 5822e17ed94SMatheus Castello 583*ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) 584*ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 1); 585*ee2d433cSIskren Chernev else 586e55a5061SIskren Chernev max17040_queue_work(chip); 587e55a5061SIskren Chernev 5888c0984e5SSebastian Reichel return 0; 5898c0984e5SSebastian Reichel } 5908c0984e5SSebastian Reichel 5918c0984e5SSebastian Reichel static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); 5928c0984e5SSebastian Reichel #define MAX17040_PM_OPS (&max17040_pm_ops) 5938c0984e5SSebastian Reichel 5948c0984e5SSebastian Reichel #else 5958c0984e5SSebastian Reichel 5968c0984e5SSebastian Reichel #define MAX17040_PM_OPS NULL 5978c0984e5SSebastian Reichel 5988c0984e5SSebastian Reichel #endif /* CONFIG_PM_SLEEP */ 5998c0984e5SSebastian Reichel 6008c0984e5SSebastian Reichel static const struct i2c_device_id max17040_id[] = { 6014f7f8e87SIskren Chernev { "max17040", ID_MAX17040 }, 6024f7f8e87SIskren Chernev { "max17041", ID_MAX17041 }, 6034f7f8e87SIskren Chernev { "max17043", ID_MAX17043 }, 6044f7f8e87SIskren Chernev { "max77836-battery", ID_MAX17043 }, 6054f7f8e87SIskren Chernev { "max17044", ID_MAX17044 }, 6064f7f8e87SIskren Chernev { "max17048", ID_MAX17048 }, 6074f7f8e87SIskren Chernev { "max17049", ID_MAX17049 }, 6084f7f8e87SIskren Chernev { "max17058", ID_MAX17058 }, 6094f7f8e87SIskren Chernev { "max17059", ID_MAX17059 }, 6104f7f8e87SIskren Chernev { /* sentinel */ } 6118c0984e5SSebastian Reichel }; 6128c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, max17040_id); 6138c0984e5SSebastian Reichel 614da28122cSJavier Martinez Canillas static const struct of_device_id max17040_of_match[] = { 6154f7f8e87SIskren Chernev { .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 }, 6164f7f8e87SIskren Chernev { .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 }, 6174f7f8e87SIskren Chernev { .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 }, 6184f7f8e87SIskren Chernev { .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 }, 6194f7f8e87SIskren Chernev { .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 }, 6204f7f8e87SIskren Chernev { .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 }, 6214f7f8e87SIskren Chernev { .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 }, 6224f7f8e87SIskren Chernev { .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 }, 6234f7f8e87SIskren Chernev { .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 }, 6244f7f8e87SIskren Chernev { /* sentinel */ }, 625da28122cSJavier Martinez Canillas }; 626da28122cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, max17040_of_match); 627da28122cSJavier Martinez Canillas 6288c0984e5SSebastian Reichel static struct i2c_driver max17040_i2c_driver = { 6298c0984e5SSebastian Reichel .driver = { 6308c0984e5SSebastian Reichel .name = "max17040", 631da28122cSJavier Martinez Canillas .of_match_table = max17040_of_match, 6328c0984e5SSebastian Reichel .pm = MAX17040_PM_OPS, 6338c0984e5SSebastian Reichel }, 6348c0984e5SSebastian Reichel .probe = max17040_probe, 6358c0984e5SSebastian Reichel .id_table = max17040_id, 6368c0984e5SSebastian Reichel }; 6378c0984e5SSebastian Reichel module_i2c_driver(max17040_i2c_driver); 6388c0984e5SSebastian Reichel 6398c0984e5SSebastian Reichel MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); 6408c0984e5SSebastian Reichel MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); 6418c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 642