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> 18*4f7f8e87SIskren 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 2814d60bddSLiu Xiang #define MAX17040_CMD 0xFE 2914d60bddSLiu Xiang 308c0984e5SSebastian Reichel 318c0984e5SSebastian Reichel #define MAX17040_DELAY 1000 328c0984e5SSebastian Reichel #define MAX17040_BATTERY_FULL 95 338c0984e5SSebastian Reichel 346455a8a8SIskren Chernev #define MAX17040_ATHD_MASK 0x3f 35cccdd0caSMatheus Castello #define MAX17040_ATHD_DEFAULT_POWER_UP 4 36cccdd0caSMatheus Castello 37*4f7f8e87SIskren Chernev enum chip_id { 38*4f7f8e87SIskren Chernev ID_MAX17040, 39*4f7f8e87SIskren Chernev ID_MAX17041, 40*4f7f8e87SIskren Chernev ID_MAX17043, 41*4f7f8e87SIskren Chernev ID_MAX17044, 42*4f7f8e87SIskren Chernev ID_MAX17048, 43*4f7f8e87SIskren Chernev ID_MAX17049, 44*4f7f8e87SIskren Chernev ID_MAX17058, 45*4f7f8e87SIskren Chernev ID_MAX17059, 46*4f7f8e87SIskren Chernev }; 47*4f7f8e87SIskren Chernev 48*4f7f8e87SIskren Chernev /* values that differ by chip_id */ 49*4f7f8e87SIskren Chernev struct chip_data { 50*4f7f8e87SIskren Chernev u16 reset_val; 51*4f7f8e87SIskren Chernev u16 vcell_shift; 52*4f7f8e87SIskren Chernev u16 vcell_mul; 53*4f7f8e87SIskren Chernev u16 vcell_div; 54*4f7f8e87SIskren Chernev u8 has_low_soc_alert; 55*4f7f8e87SIskren Chernev }; 56*4f7f8e87SIskren Chernev 57*4f7f8e87SIskren Chernev static struct chip_data max17040_family[] = { 58*4f7f8e87SIskren Chernev [ID_MAX17040] = { 59*4f7f8e87SIskren Chernev .reset_val = 0x0054, 60*4f7f8e87SIskren Chernev .vcell_shift = 4, 61*4f7f8e87SIskren Chernev .vcell_mul = 1250, 62*4f7f8e87SIskren Chernev .vcell_div = 1, 63*4f7f8e87SIskren Chernev .has_low_soc_alert = 0, 64*4f7f8e87SIskren Chernev }, 65*4f7f8e87SIskren Chernev [ID_MAX17041] = { 66*4f7f8e87SIskren Chernev .reset_val = 0x0054, 67*4f7f8e87SIskren Chernev .vcell_shift = 4, 68*4f7f8e87SIskren Chernev .vcell_mul = 2500, 69*4f7f8e87SIskren Chernev .vcell_div = 1, 70*4f7f8e87SIskren Chernev .has_low_soc_alert = 0, 71*4f7f8e87SIskren Chernev }, 72*4f7f8e87SIskren Chernev [ID_MAX17043] = { 73*4f7f8e87SIskren Chernev .reset_val = 0x0054, 74*4f7f8e87SIskren Chernev .vcell_shift = 4, 75*4f7f8e87SIskren Chernev .vcell_mul = 1250, 76*4f7f8e87SIskren Chernev .vcell_div = 1, 77*4f7f8e87SIskren Chernev .has_low_soc_alert = 1, 78*4f7f8e87SIskren Chernev }, 79*4f7f8e87SIskren Chernev [ID_MAX17044] = { 80*4f7f8e87SIskren Chernev .reset_val = 0x0054, 81*4f7f8e87SIskren Chernev .vcell_shift = 4, 82*4f7f8e87SIskren Chernev .vcell_mul = 2500, 83*4f7f8e87SIskren Chernev .vcell_div = 1, 84*4f7f8e87SIskren Chernev .has_low_soc_alert = 1, 85*4f7f8e87SIskren Chernev }, 86*4f7f8e87SIskren Chernev [ID_MAX17048] = { 87*4f7f8e87SIskren Chernev .reset_val = 0x5400, 88*4f7f8e87SIskren Chernev .vcell_shift = 0, 89*4f7f8e87SIskren Chernev .vcell_mul = 625, 90*4f7f8e87SIskren Chernev .vcell_div = 8, 91*4f7f8e87SIskren Chernev .has_low_soc_alert = 1, 92*4f7f8e87SIskren Chernev }, 93*4f7f8e87SIskren Chernev [ID_MAX17049] = { 94*4f7f8e87SIskren Chernev .reset_val = 0x5400, 95*4f7f8e87SIskren Chernev .vcell_shift = 0, 96*4f7f8e87SIskren Chernev .vcell_mul = 625, 97*4f7f8e87SIskren Chernev .vcell_div = 4, 98*4f7f8e87SIskren Chernev .has_low_soc_alert = 1, 99*4f7f8e87SIskren Chernev }, 100*4f7f8e87SIskren Chernev [ID_MAX17058] = { 101*4f7f8e87SIskren Chernev .reset_val = 0x5400, 102*4f7f8e87SIskren Chernev .vcell_shift = 0, 103*4f7f8e87SIskren Chernev .vcell_mul = 625, 104*4f7f8e87SIskren Chernev .vcell_div = 8, 105*4f7f8e87SIskren Chernev .has_low_soc_alert = 1, 106*4f7f8e87SIskren Chernev }, 107*4f7f8e87SIskren Chernev [ID_MAX17059] = { 108*4f7f8e87SIskren Chernev .reset_val = 0x5400, 109*4f7f8e87SIskren Chernev .vcell_shift = 0, 110*4f7f8e87SIskren Chernev .vcell_mul = 625, 111*4f7f8e87SIskren Chernev .vcell_div = 4, 112*4f7f8e87SIskren Chernev .has_low_soc_alert = 1, 113*4f7f8e87SIskren Chernev }, 114*4f7f8e87SIskren Chernev }; 115*4f7f8e87SIskren Chernev 1168c0984e5SSebastian Reichel struct max17040_chip { 1178c0984e5SSebastian Reichel struct i2c_client *client; 1186455a8a8SIskren Chernev struct regmap *regmap; 1198c0984e5SSebastian Reichel struct delayed_work work; 1208c0984e5SSebastian Reichel struct power_supply *battery; 1218c0984e5SSebastian Reichel struct max17040_platform_data *pdata; 122*4f7f8e87SIskren Chernev struct chip_data data; 1238c0984e5SSebastian Reichel 1248c0984e5SSebastian Reichel /* battery capacity */ 1258c0984e5SSebastian Reichel int soc; 1268c0984e5SSebastian Reichel /* State Of Charge */ 1278c0984e5SSebastian Reichel int status; 128cccdd0caSMatheus Castello /* Low alert threshold from 32% to 1% of the State of Charge */ 129cccdd0caSMatheus Castello u32 low_soc_alert; 130*4f7f8e87SIskren Chernev /* some devices return twice the capacity */ 131*4f7f8e87SIskren Chernev bool quirk_double_soc; 1328c0984e5SSebastian Reichel }; 1338c0984e5SSebastian Reichel 1346455a8a8SIskren Chernev static int max17040_reset(struct max17040_chip *chip) 1358c0984e5SSebastian Reichel { 136*4f7f8e87SIskren Chernev return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val); 1378c0984e5SSebastian Reichel } 1388c0984e5SSebastian Reichel 1396455a8a8SIskren Chernev static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) 1408c0984e5SSebastian Reichel { 141*4f7f8e87SIskren Chernev level = 32 - level * (chip->quirk_double_soc ? 2 : 1); 1426455a8a8SIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, 1436455a8a8SIskren Chernev MAX17040_ATHD_MASK, level); 144cccdd0caSMatheus Castello } 145cccdd0caSMatheus Castello 146*4f7f8e87SIskren Chernev static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) 147*4f7f8e87SIskren Chernev { 148*4f7f8e87SIskren Chernev struct chip_data *d = &chip->data; 149*4f7f8e87SIskren Chernev 150*4f7f8e87SIskren Chernev return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div; 151*4f7f8e87SIskren Chernev } 152*4f7f8e87SIskren Chernev 153*4f7f8e87SIskren Chernev 1546455a8a8SIskren Chernev static int max17040_get_vcell(struct max17040_chip *chip) 1558c0984e5SSebastian Reichel { 1566455a8a8SIskren Chernev u32 vcell; 1578c0984e5SSebastian Reichel 1586455a8a8SIskren Chernev regmap_read(chip->regmap, MAX17040_VCELL, &vcell); 1598c0984e5SSebastian Reichel 160*4f7f8e87SIskren Chernev return max17040_raw_vcell_to_uvolts(chip, vcell); 1618c0984e5SSebastian Reichel } 1628c0984e5SSebastian Reichel 1636455a8a8SIskren Chernev static int max17040_get_soc(struct max17040_chip *chip) 1648c0984e5SSebastian Reichel { 1656455a8a8SIskren Chernev u32 soc; 1668c0984e5SSebastian Reichel 1676455a8a8SIskren Chernev regmap_read(chip->regmap, MAX17040_SOC, &soc); 1688c0984e5SSebastian Reichel 169*4f7f8e87SIskren Chernev return soc >> (chip->quirk_double_soc ? 9 : 8); 1708c0984e5SSebastian Reichel } 1718c0984e5SSebastian Reichel 1726455a8a8SIskren Chernev static int max17040_get_version(struct max17040_chip *chip) 1738c0984e5SSebastian Reichel { 1746455a8a8SIskren Chernev int ret; 1756455a8a8SIskren Chernev u32 version; 1768c0984e5SSebastian Reichel 1776455a8a8SIskren Chernev ret = regmap_read(chip->regmap, MAX17040_VER, &version); 1788c0984e5SSebastian Reichel 1796455a8a8SIskren Chernev return ret ? ret : version; 1808c0984e5SSebastian Reichel } 1818c0984e5SSebastian Reichel 1826455a8a8SIskren Chernev static int max17040_get_online(struct max17040_chip *chip) 1838c0984e5SSebastian Reichel { 1846455a8a8SIskren Chernev return chip->pdata && chip->pdata->battery_online ? 1856455a8a8SIskren Chernev chip->pdata->battery_online() : 1; 1868c0984e5SSebastian Reichel } 1878c0984e5SSebastian Reichel 1886455a8a8SIskren Chernev static int max17040_get_status(struct max17040_chip *chip) 1898c0984e5SSebastian Reichel { 1908c0984e5SSebastian Reichel if (!chip->pdata || !chip->pdata->charger_online 1916455a8a8SIskren Chernev || !chip->pdata->charger_enable) 1926455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_UNKNOWN; 1938c0984e5SSebastian Reichel 1946455a8a8SIskren Chernev if (max17040_get_soc(chip) > MAX17040_BATTERY_FULL) 1956455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_FULL; 1966455a8a8SIskren Chernev 1976455a8a8SIskren Chernev if (chip->pdata->charger_online()) 1988c0984e5SSebastian Reichel if (chip->pdata->charger_enable()) 1996455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_CHARGING; 2008c0984e5SSebastian Reichel else 2016455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_NOT_CHARGING; 2026455a8a8SIskren Chernev else 2036455a8a8SIskren Chernev return POWER_SUPPLY_STATUS_DISCHARGING; 2048c0984e5SSebastian Reichel } 2058c0984e5SSebastian Reichel 206cccdd0caSMatheus Castello static int max17040_get_of_data(struct max17040_chip *chip) 207cccdd0caSMatheus Castello { 208cccdd0caSMatheus Castello struct device *dev = &chip->client->dev; 209cccdd0caSMatheus Castello 210*4f7f8e87SIskren Chernev chip->quirk_double_soc = device_property_read_bool(dev, 211*4f7f8e87SIskren Chernev "maxim,double-soc"); 212*4f7f8e87SIskren Chernev 213cccdd0caSMatheus Castello chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP; 214cccdd0caSMatheus Castello device_property_read_u32(dev, 215cccdd0caSMatheus Castello "maxim,alert-low-soc-level", 216cccdd0caSMatheus Castello &chip->low_soc_alert); 217cccdd0caSMatheus Castello 218*4f7f8e87SIskren Chernev if (chip->low_soc_alert <= 0 || 219*4f7f8e87SIskren Chernev chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) { 2206455a8a8SIskren Chernev dev_err(dev, "maxim,alert-low-soc-level out of bounds\n"); 221cccdd0caSMatheus Castello return -EINVAL; 2226455a8a8SIskren Chernev } 223cccdd0caSMatheus Castello 224cccdd0caSMatheus Castello return 0; 225cccdd0caSMatheus Castello } 226cccdd0caSMatheus Castello 2276455a8a8SIskren Chernev static void max17040_check_changes(struct max17040_chip *chip) 2282e17ed94SMatheus Castello { 2296455a8a8SIskren Chernev chip->soc = max17040_get_soc(chip); 2306455a8a8SIskren Chernev chip->status = max17040_get_status(chip); 2312e17ed94SMatheus Castello } 2322e17ed94SMatheus Castello 233e55a5061SIskren Chernev static void max17040_queue_work(struct max17040_chip *chip) 234e55a5061SIskren Chernev { 235e55a5061SIskren Chernev queue_delayed_work(system_power_efficient_wq, &chip->work, 236e55a5061SIskren Chernev MAX17040_DELAY); 237e55a5061SIskren Chernev } 238e55a5061SIskren Chernev 239e55a5061SIskren Chernev static void max17040_stop_work(void *data) 240e55a5061SIskren Chernev { 241e55a5061SIskren Chernev struct max17040_chip *chip = data; 242e55a5061SIskren Chernev 243e55a5061SIskren Chernev cancel_delayed_work_sync(&chip->work); 244e55a5061SIskren Chernev } 245e55a5061SIskren Chernev 2468c0984e5SSebastian Reichel static void max17040_work(struct work_struct *work) 2478c0984e5SSebastian Reichel { 2488c0984e5SSebastian Reichel struct max17040_chip *chip; 249a08990eaSMatheus Castello int last_soc, last_status; 2508c0984e5SSebastian Reichel 2518c0984e5SSebastian Reichel chip = container_of(work, struct max17040_chip, work.work); 252a08990eaSMatheus Castello 253a08990eaSMatheus Castello /* store SOC and status to check changes */ 254a08990eaSMatheus Castello last_soc = chip->soc; 255a08990eaSMatheus Castello last_status = chip->status; 2566455a8a8SIskren Chernev max17040_check_changes(chip); 2578c0984e5SSebastian Reichel 258a08990eaSMatheus Castello /* check changes and send uevent */ 259a08990eaSMatheus Castello if (last_soc != chip->soc || last_status != chip->status) 260a08990eaSMatheus Castello power_supply_changed(chip->battery); 261a08990eaSMatheus Castello 262e55a5061SIskren Chernev max17040_queue_work(chip); 2638c0984e5SSebastian Reichel } 2648c0984e5SSebastian Reichel 2652e17ed94SMatheus Castello static irqreturn_t max17040_thread_handler(int id, void *dev) 2662e17ed94SMatheus Castello { 2672e17ed94SMatheus Castello struct max17040_chip *chip = dev; 2682e17ed94SMatheus Castello 2696455a8a8SIskren Chernev dev_warn(&chip->client->dev, "IRQ: Alert battery low level"); 2706455a8a8SIskren Chernev 2712e17ed94SMatheus Castello /* read registers */ 2726455a8a8SIskren Chernev max17040_check_changes(chip); 2732e17ed94SMatheus Castello 2742e17ed94SMatheus Castello /* send uevent */ 2752e17ed94SMatheus Castello power_supply_changed(chip->battery); 2762e17ed94SMatheus Castello 277cccdd0caSMatheus Castello /* reset alert bit */ 2786455a8a8SIskren Chernev max17040_set_low_soc_alert(chip, chip->low_soc_alert); 279cccdd0caSMatheus Castello 2802e17ed94SMatheus Castello return IRQ_HANDLED; 2812e17ed94SMatheus Castello } 2822e17ed94SMatheus Castello 2832e17ed94SMatheus Castello static int max17040_enable_alert_irq(struct max17040_chip *chip) 2842e17ed94SMatheus Castello { 2852e17ed94SMatheus Castello struct i2c_client *client = chip->client; 2862e17ed94SMatheus Castello unsigned int flags; 2872e17ed94SMatheus Castello int ret; 2882e17ed94SMatheus Castello 2892e17ed94SMatheus Castello flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; 2902e17ed94SMatheus Castello ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, 2912e17ed94SMatheus Castello max17040_thread_handler, flags, 2922e17ed94SMatheus Castello chip->battery->desc->name, chip); 2932e17ed94SMatheus Castello 2942e17ed94SMatheus Castello return ret; 2952e17ed94SMatheus Castello } 2962e17ed94SMatheus Castello 2972f38dc4dSMatheus Castello static int max17040_prop_writeable(struct power_supply *psy, 2982f38dc4dSMatheus Castello enum power_supply_property psp) 2992f38dc4dSMatheus Castello { 3002f38dc4dSMatheus Castello switch (psp) { 3012f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3022f38dc4dSMatheus Castello return 1; 3032f38dc4dSMatheus Castello default: 3042f38dc4dSMatheus Castello return 0; 3052f38dc4dSMatheus Castello } 3062f38dc4dSMatheus Castello } 3072f38dc4dSMatheus Castello 3082f38dc4dSMatheus Castello static int max17040_set_property(struct power_supply *psy, 3092f38dc4dSMatheus Castello enum power_supply_property psp, 3102f38dc4dSMatheus Castello const union power_supply_propval *val) 3112f38dc4dSMatheus Castello { 3122f38dc4dSMatheus Castello struct max17040_chip *chip = power_supply_get_drvdata(psy); 3132f38dc4dSMatheus Castello int ret; 3142f38dc4dSMatheus Castello 3152f38dc4dSMatheus Castello switch (psp) { 3162f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 317*4f7f8e87SIskren Chernev /* alert threshold can be programmed from 1% up to 16/32% */ 318*4f7f8e87SIskren Chernev if ((val->intval < 1) || 319*4f7f8e87SIskren Chernev (val->intval > (chip->quirk_double_soc ? 16 : 32))) { 3202f38dc4dSMatheus Castello ret = -EINVAL; 3212f38dc4dSMatheus Castello break; 3222f38dc4dSMatheus Castello } 3236455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, val->intval); 3242f38dc4dSMatheus Castello chip->low_soc_alert = val->intval; 3252f38dc4dSMatheus Castello break; 3262f38dc4dSMatheus Castello default: 3272f38dc4dSMatheus Castello ret = -EINVAL; 3282f38dc4dSMatheus Castello } 3292f38dc4dSMatheus Castello 3302f38dc4dSMatheus Castello return ret; 3312f38dc4dSMatheus Castello } 3322f38dc4dSMatheus Castello 3336455a8a8SIskren Chernev static int max17040_get_property(struct power_supply *psy, 3346455a8a8SIskren Chernev enum power_supply_property psp, 3356455a8a8SIskren Chernev union power_supply_propval *val) 3366455a8a8SIskren Chernev { 3376455a8a8SIskren Chernev struct max17040_chip *chip = power_supply_get_drvdata(psy); 3386455a8a8SIskren Chernev 3396455a8a8SIskren Chernev switch (psp) { 3406455a8a8SIskren Chernev case POWER_SUPPLY_PROP_STATUS: 3416455a8a8SIskren Chernev val->intval = max17040_get_status(chip); 3426455a8a8SIskren Chernev break; 3436455a8a8SIskren Chernev case POWER_SUPPLY_PROP_ONLINE: 3446455a8a8SIskren Chernev val->intval = max17040_get_online(chip); 3456455a8a8SIskren Chernev break; 3466455a8a8SIskren Chernev case POWER_SUPPLY_PROP_VOLTAGE_NOW: 3476455a8a8SIskren Chernev val->intval = max17040_get_vcell(chip); 3486455a8a8SIskren Chernev break; 3496455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY: 3506455a8a8SIskren Chernev val->intval = max17040_get_soc(chip); 3516455a8a8SIskren Chernev break; 3526455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: 3536455a8a8SIskren Chernev val->intval = chip->low_soc_alert; 3546455a8a8SIskren Chernev break; 3556455a8a8SIskren Chernev default: 3566455a8a8SIskren Chernev return -EINVAL; 3576455a8a8SIskren Chernev } 3586455a8a8SIskren Chernev return 0; 3596455a8a8SIskren Chernev } 3606455a8a8SIskren Chernev 3616455a8a8SIskren Chernev static const struct regmap_config max17040_regmap = { 3626455a8a8SIskren Chernev .reg_bits = 8, 3636455a8a8SIskren Chernev .reg_stride = 2, 3646455a8a8SIskren Chernev .val_bits = 16, 3656455a8a8SIskren Chernev .val_format_endian = REGMAP_ENDIAN_BIG, 3666455a8a8SIskren Chernev }; 3676455a8a8SIskren Chernev 3688c0984e5SSebastian Reichel static enum power_supply_property max17040_battery_props[] = { 3698c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 3708c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 3718c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 3728c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 3732f38dc4dSMatheus Castello POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, 3748c0984e5SSebastian Reichel }; 3758c0984e5SSebastian Reichel 3768c0984e5SSebastian Reichel static const struct power_supply_desc max17040_battery_desc = { 3778c0984e5SSebastian Reichel .name = "battery", 3788c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 3798c0984e5SSebastian Reichel .get_property = max17040_get_property, 3802f38dc4dSMatheus Castello .set_property = max17040_set_property, 3812f38dc4dSMatheus Castello .property_is_writeable = max17040_prop_writeable, 3828c0984e5SSebastian Reichel .properties = max17040_battery_props, 3838c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(max17040_battery_props), 3848c0984e5SSebastian Reichel }; 3858c0984e5SSebastian Reichel 3868c0984e5SSebastian Reichel static int max17040_probe(struct i2c_client *client, 3878c0984e5SSebastian Reichel const struct i2c_device_id *id) 3888c0984e5SSebastian Reichel { 3894e9c406dSWolfram Sang struct i2c_adapter *adapter = client->adapter; 3908c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 3918c0984e5SSebastian Reichel struct max17040_chip *chip; 392*4f7f8e87SIskren Chernev enum chip_id chip_id; 393cccdd0caSMatheus Castello int ret; 3948c0984e5SSebastian Reichel 3958c0984e5SSebastian Reichel if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 3968c0984e5SSebastian Reichel return -EIO; 3978c0984e5SSebastian Reichel 3988c0984e5SSebastian Reichel chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); 3998c0984e5SSebastian Reichel if (!chip) 4008c0984e5SSebastian Reichel return -ENOMEM; 4018c0984e5SSebastian Reichel 4028c0984e5SSebastian Reichel chip->client = client; 4036455a8a8SIskren Chernev chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); 4048c0984e5SSebastian Reichel chip->pdata = client->dev.platform_data; 405*4f7f8e87SIskren Chernev chip_id = (enum chip_id) id->driver_data; 406*4f7f8e87SIskren Chernev if (client->dev.of_node) { 407cccdd0caSMatheus Castello ret = max17040_get_of_data(chip); 4086455a8a8SIskren Chernev if (ret) 409cccdd0caSMatheus Castello return ret; 410*4f7f8e87SIskren Chernev chip_id = (enum chip_id) (uintptr_t) 411*4f7f8e87SIskren Chernev of_device_get_match_data(&client->dev); 412*4f7f8e87SIskren Chernev } 413*4f7f8e87SIskren Chernev chip->data = max17040_family[chip_id]; 4148c0984e5SSebastian Reichel 4158c0984e5SSebastian Reichel i2c_set_clientdata(client, chip); 4168c0984e5SSebastian Reichel psy_cfg.drv_data = chip; 4178c0984e5SSebastian Reichel 418e55a5061SIskren Chernev chip->battery = devm_power_supply_register(&client->dev, 4198c0984e5SSebastian Reichel &max17040_battery_desc, &psy_cfg); 4208c0984e5SSebastian Reichel if (IS_ERR(chip->battery)) { 4218c0984e5SSebastian Reichel dev_err(&client->dev, "failed: power supply register\n"); 4228c0984e5SSebastian Reichel return PTR_ERR(chip->battery); 4238c0984e5SSebastian Reichel } 4248c0984e5SSebastian Reichel 4256455a8a8SIskren Chernev ret = max17040_get_version(chip); 4266455a8a8SIskren Chernev if (ret < 0) 4276455a8a8SIskren Chernev return ret; 4286455a8a8SIskren Chernev dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret); 4296455a8a8SIskren Chernev 430*4f7f8e87SIskren Chernev if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) 4316455a8a8SIskren Chernev max17040_reset(chip); 4328c0984e5SSebastian Reichel 4332e17ed94SMatheus Castello /* check interrupt */ 434*4f7f8e87SIskren Chernev if (client->irq && chip->data.has_low_soc_alert) { 4356455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); 436cccdd0caSMatheus Castello if (ret) { 437cccdd0caSMatheus Castello dev_err(&client->dev, 438cccdd0caSMatheus Castello "Failed to set low SOC alert: err %d\n", ret); 439cccdd0caSMatheus Castello return ret; 440cccdd0caSMatheus Castello } 4412e17ed94SMatheus Castello 4422e17ed94SMatheus Castello ret = max17040_enable_alert_irq(chip); 4432e17ed94SMatheus Castello if (ret) { 4442e17ed94SMatheus Castello client->irq = 0; 4452e17ed94SMatheus Castello dev_warn(&client->dev, 4462e17ed94SMatheus Castello "Failed to get IRQ err %d\n", ret); 4472e17ed94SMatheus Castello } 4482e17ed94SMatheus Castello } 4492e17ed94SMatheus Castello 4508c0984e5SSebastian Reichel INIT_DEFERRABLE_WORK(&chip->work, max17040_work); 451e55a5061SIskren Chernev ret = devm_add_action(&client->dev, max17040_stop_work, chip); 452e55a5061SIskren Chernev if (ret) 453e55a5061SIskren Chernev return ret; 454e55a5061SIskren Chernev max17040_queue_work(chip); 4558c0984e5SSebastian Reichel 4568c0984e5SSebastian Reichel return 0; 4578c0984e5SSebastian Reichel } 4588c0984e5SSebastian Reichel 4598c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP 4608c0984e5SSebastian Reichel 4618c0984e5SSebastian Reichel static int max17040_suspend(struct device *dev) 4628c0984e5SSebastian Reichel { 4638c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 4648c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 4658c0984e5SSebastian Reichel 4668c0984e5SSebastian Reichel cancel_delayed_work(&chip->work); 4672e17ed94SMatheus Castello 468e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 4692e17ed94SMatheus Castello enable_irq_wake(client->irq); 4702e17ed94SMatheus Castello 4718c0984e5SSebastian Reichel return 0; 4728c0984e5SSebastian Reichel } 4738c0984e5SSebastian Reichel 4748c0984e5SSebastian Reichel static int max17040_resume(struct device *dev) 4758c0984e5SSebastian Reichel { 4768c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev); 4778c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client); 4788c0984e5SSebastian Reichel 479e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev)) 4802e17ed94SMatheus Castello disable_irq_wake(client->irq); 4812e17ed94SMatheus Castello 482e55a5061SIskren Chernev max17040_queue_work(chip); 483e55a5061SIskren Chernev 4848c0984e5SSebastian Reichel return 0; 4858c0984e5SSebastian Reichel } 4868c0984e5SSebastian Reichel 4878c0984e5SSebastian Reichel static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); 4888c0984e5SSebastian Reichel #define MAX17040_PM_OPS (&max17040_pm_ops) 4898c0984e5SSebastian Reichel 4908c0984e5SSebastian Reichel #else 4918c0984e5SSebastian Reichel 4928c0984e5SSebastian Reichel #define MAX17040_PM_OPS NULL 4938c0984e5SSebastian Reichel 4948c0984e5SSebastian Reichel #endif /* CONFIG_PM_SLEEP */ 4958c0984e5SSebastian Reichel 4968c0984e5SSebastian Reichel static const struct i2c_device_id max17040_id[] = { 497*4f7f8e87SIskren Chernev { "max17040", ID_MAX17040 }, 498*4f7f8e87SIskren Chernev { "max17041", ID_MAX17041 }, 499*4f7f8e87SIskren Chernev { "max17043", ID_MAX17043 }, 500*4f7f8e87SIskren Chernev { "max77836-battery", ID_MAX17043 }, 501*4f7f8e87SIskren Chernev { "max17044", ID_MAX17044 }, 502*4f7f8e87SIskren Chernev { "max17048", ID_MAX17048 }, 503*4f7f8e87SIskren Chernev { "max17049", ID_MAX17049 }, 504*4f7f8e87SIskren Chernev { "max17058", ID_MAX17058 }, 505*4f7f8e87SIskren Chernev { "max17059", ID_MAX17059 }, 506*4f7f8e87SIskren Chernev { /* sentinel */ } 5078c0984e5SSebastian Reichel }; 5088c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, max17040_id); 5098c0984e5SSebastian Reichel 510da28122cSJavier Martinez Canillas static const struct of_device_id max17040_of_match[] = { 511*4f7f8e87SIskren Chernev { .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 }, 512*4f7f8e87SIskren Chernev { .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 }, 513*4f7f8e87SIskren Chernev { .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 }, 514*4f7f8e87SIskren Chernev { .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 }, 515*4f7f8e87SIskren Chernev { .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 }, 516*4f7f8e87SIskren Chernev { .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 }, 517*4f7f8e87SIskren Chernev { .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 }, 518*4f7f8e87SIskren Chernev { .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 }, 519*4f7f8e87SIskren Chernev { .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 }, 520*4f7f8e87SIskren Chernev { /* sentinel */ }, 521da28122cSJavier Martinez Canillas }; 522da28122cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, max17040_of_match); 523da28122cSJavier Martinez Canillas 5248c0984e5SSebastian Reichel static struct i2c_driver max17040_i2c_driver = { 5258c0984e5SSebastian Reichel .driver = { 5268c0984e5SSebastian Reichel .name = "max17040", 527da28122cSJavier Martinez Canillas .of_match_table = max17040_of_match, 5288c0984e5SSebastian Reichel .pm = MAX17040_PM_OPS, 5298c0984e5SSebastian Reichel }, 5308c0984e5SSebastian Reichel .probe = max17040_probe, 5318c0984e5SSebastian Reichel .id_table = max17040_id, 5328c0984e5SSebastian Reichel }; 5338c0984e5SSebastian Reichel module_i2c_driver(max17040_i2c_driver); 5348c0984e5SSebastian Reichel 5358c0984e5SSebastian Reichel MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); 5368c0984e5SSebastian Reichel MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); 5378c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 538