18c0984e5SSebastian Reichel /* 28c0984e5SSebastian Reichel * BQ27xxx battery driver 38c0984e5SSebastian Reichel * 48c0984e5SSebastian Reichel * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> 58c0984e5SSebastian Reichel * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> 68c0984e5SSebastian Reichel * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de> 78c0984e5SSebastian Reichel * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com> 80670c9b3SLiam Breck * Copyright (C) 2017 Liam Breck <kernel@networkimprov.net> 98c0984e5SSebastian Reichel * 108c0984e5SSebastian Reichel * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. 118c0984e5SSebastian Reichel * 128c0984e5SSebastian Reichel * This package is free software; you can redistribute it and/or modify 138c0984e5SSebastian Reichel * it under the terms of the GNU General Public License version 2 as 148c0984e5SSebastian Reichel * published by the Free Software Foundation. 158c0984e5SSebastian Reichel * 168c0984e5SSebastian Reichel * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 178c0984e5SSebastian Reichel * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 188c0984e5SSebastian Reichel * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 198c0984e5SSebastian Reichel * 208c0984e5SSebastian Reichel * Datasheets: 218c0984e5SSebastian Reichel * http://www.ti.com/product/bq27000 228c0984e5SSebastian Reichel * http://www.ti.com/product/bq27200 238c0984e5SSebastian Reichel * http://www.ti.com/product/bq27010 248c0984e5SSebastian Reichel * http://www.ti.com/product/bq27210 258c0984e5SSebastian Reichel * http://www.ti.com/product/bq27500 26bd28177fSChris Lapa * http://www.ti.com/product/bq27510-g1 27698a2bf5SChris Lapa * http://www.ti.com/product/bq27510-g2 288c0984e5SSebastian Reichel * http://www.ti.com/product/bq27510-g3 298c0984e5SSebastian Reichel * http://www.ti.com/product/bq27520-g4 3068f2a813SChris Lapa * http://www.ti.com/product/bq27520-g1 31a5deb9a9SChris Lapa * http://www.ti.com/product/bq27520-g2 32825e915bSChris Lapa * http://www.ti.com/product/bq27520-g3 338835cae5SChris Lapa * http://www.ti.com/product/bq27520-g4 348c0984e5SSebastian Reichel * http://www.ti.com/product/bq27530-g1 358c0984e5SSebastian Reichel * http://www.ti.com/product/bq27531-g1 368c0984e5SSebastian Reichel * http://www.ti.com/product/bq27541-g1 378c0984e5SSebastian Reichel * http://www.ti.com/product/bq27542-g1 388c0984e5SSebastian Reichel * http://www.ti.com/product/bq27546-g1 398c0984e5SSebastian Reichel * http://www.ti.com/product/bq27742-g1 408c0984e5SSebastian Reichel * http://www.ti.com/product/bq27545-g1 418c0984e5SSebastian Reichel * http://www.ti.com/product/bq27421-g1 428c0984e5SSebastian Reichel * http://www.ti.com/product/bq27425-g1 438c0984e5SSebastian Reichel * http://www.ti.com/product/bq27411-g1 448c0984e5SSebastian Reichel * http://www.ti.com/product/bq27621-g1 458c0984e5SSebastian Reichel */ 468c0984e5SSebastian Reichel 478c0984e5SSebastian Reichel #include <linux/device.h> 488c0984e5SSebastian Reichel #include <linux/module.h> 491d72706fSMatt Ranostay #include <linux/mutex.h> 508c0984e5SSebastian Reichel #include <linux/param.h> 518c0984e5SSebastian Reichel #include <linux/jiffies.h> 528c0984e5SSebastian Reichel #include <linux/workqueue.h> 538c0984e5SSebastian Reichel #include <linux/delay.h> 548c0984e5SSebastian Reichel #include <linux/platform_device.h> 558c0984e5SSebastian Reichel #include <linux/power_supply.h> 568c0984e5SSebastian Reichel #include <linux/slab.h> 578c0984e5SSebastian Reichel #include <linux/of.h> 588c0984e5SSebastian Reichel 598c0984e5SSebastian Reichel #include <linux/power/bq27xxx_battery.h> 608c0984e5SSebastian Reichel 618c0984e5SSebastian Reichel #define BQ27XXX_MANUFACTURER "Texas Instruments" 628c0984e5SSebastian Reichel 638c0984e5SSebastian Reichel /* BQ27XXX Flags */ 648c0984e5SSebastian Reichel #define BQ27XXX_FLAG_DSC BIT(0) 658c0984e5SSebastian Reichel #define BQ27XXX_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ 668c0984e5SSebastian Reichel #define BQ27XXX_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ 670670c9b3SLiam Breck #define BQ27XXX_FLAG_CFGUP BIT(4) 688c0984e5SSebastian Reichel #define BQ27XXX_FLAG_FC BIT(9) 698c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OTD BIT(14) 708c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OTC BIT(15) 718c0984e5SSebastian Reichel #define BQ27XXX_FLAG_UT BIT(14) 728c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OT BIT(15) 738c0984e5SSebastian Reichel 748c0984e5SSebastian Reichel /* BQ27000 has different layout for Flags register */ 758c0984e5SSebastian Reichel #define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */ 768c0984e5SSebastian Reichel #define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */ 778c0984e5SSebastian Reichel #define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */ 788c0984e5SSebastian Reichel #define BQ27000_FLAG_FC BIT(5) 798c0984e5SSebastian Reichel #define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ 808c0984e5SSebastian Reichel 810670c9b3SLiam Breck /* control register params */ 820670c9b3SLiam Breck #define BQ27XXX_SEALED 0x20 830670c9b3SLiam Breck #define BQ27XXX_SET_CFGUPDATE 0x13 840670c9b3SLiam Breck #define BQ27XXX_SOFT_RESET 0x42 850670c9b3SLiam Breck #define BQ27XXX_RESET 0x41 860670c9b3SLiam Breck 878c0984e5SSebastian Reichel #define BQ27XXX_RS (20) /* Resistor sense mOhm */ 888c0984e5SSebastian Reichel #define BQ27XXX_POWER_CONSTANT (29200) /* 29.2 µV^2 * 1000 */ 898c0984e5SSebastian Reichel #define BQ27XXX_CURRENT_CONSTANT (3570) /* 3.57 µV * 1000 */ 908c0984e5SSebastian Reichel 918c0984e5SSebastian Reichel #define INVALID_REG_ADDR 0xff 928c0984e5SSebastian Reichel 938c0984e5SSebastian Reichel /* 948c0984e5SSebastian Reichel * bq27xxx_reg_index - Register names 958c0984e5SSebastian Reichel * 968c0984e5SSebastian Reichel * These are indexes into a device's register mapping array. 978c0984e5SSebastian Reichel */ 988c0984e5SSebastian Reichel 998c0984e5SSebastian Reichel enum bq27xxx_reg_index { 1008c0984e5SSebastian Reichel BQ27XXX_REG_CTRL = 0, /* Control */ 1018c0984e5SSebastian Reichel BQ27XXX_REG_TEMP, /* Temperature */ 1028c0984e5SSebastian Reichel BQ27XXX_REG_INT_TEMP, /* Internal Temperature */ 1038c0984e5SSebastian Reichel BQ27XXX_REG_VOLT, /* Voltage */ 1048c0984e5SSebastian Reichel BQ27XXX_REG_AI, /* Average Current */ 1058c0984e5SSebastian Reichel BQ27XXX_REG_FLAGS, /* Flags */ 1068c0984e5SSebastian Reichel BQ27XXX_REG_TTE, /* Time-to-Empty */ 1078c0984e5SSebastian Reichel BQ27XXX_REG_TTF, /* Time-to-Full */ 1088c0984e5SSebastian Reichel BQ27XXX_REG_TTES, /* Time-to-Empty Standby */ 1098c0984e5SSebastian Reichel BQ27XXX_REG_TTECP, /* Time-to-Empty at Constant Power */ 1108c0984e5SSebastian Reichel BQ27XXX_REG_NAC, /* Nominal Available Capacity */ 1118c0984e5SSebastian Reichel BQ27XXX_REG_FCC, /* Full Charge Capacity */ 1128c0984e5SSebastian Reichel BQ27XXX_REG_CYCT, /* Cycle Count */ 1138c0984e5SSebastian Reichel BQ27XXX_REG_AE, /* Available Energy */ 1148c0984e5SSebastian Reichel BQ27XXX_REG_SOC, /* State-of-Charge */ 1158c0984e5SSebastian Reichel BQ27XXX_REG_DCAP, /* Design Capacity */ 1168c0984e5SSebastian Reichel BQ27XXX_REG_AP, /* Average Power */ 1170670c9b3SLiam Breck BQ27XXX_DM_CTRL, /* Block Data Control */ 1180670c9b3SLiam Breck BQ27XXX_DM_CLASS, /* Data Class */ 1190670c9b3SLiam Breck BQ27XXX_DM_BLOCK, /* Data Block */ 1200670c9b3SLiam Breck BQ27XXX_DM_DATA, /* Block Data */ 1210670c9b3SLiam Breck BQ27XXX_DM_CKSUM, /* Block Data Checksum */ 1228c0984e5SSebastian Reichel BQ27XXX_REG_MAX, /* sentinel */ 1238c0984e5SSebastian Reichel }; 1248c0984e5SSebastian Reichel 1250670c9b3SLiam Breck #define BQ27XXX_DM_REG_ROWS \ 1260670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = 0x61, \ 1270670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = 0x3e, \ 1280670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = 0x3f, \ 1290670c9b3SLiam Breck [BQ27XXX_DM_DATA] = 0x40, \ 1300670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = 0x60 1310670c9b3SLiam Breck 1328c0984e5SSebastian Reichel /* Register mappings */ 1339aade6d8SLiam Breck static u8 1349aade6d8SLiam Breck bq27000_regs[BQ27XXX_REG_MAX] = { 1358c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1368c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1378c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 1388c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1398c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1408c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1418c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1428c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = 0x18, 1438c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1c, 1448c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = 0x26, 1458c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1468c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1478c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1488c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = 0x22, 1498c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x0b, 1508c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x76, 1518c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 1520670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, 1530670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, 1540670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, 1550670c9b3SLiam Breck [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, 1560670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, 1578c0984e5SSebastian Reichel }, 1589aade6d8SLiam Breck bq27010_regs[BQ27XXX_REG_MAX] = { 1598c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1608c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1618c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 1628c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1638c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1648c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1658c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1668c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = 0x18, 1678c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1c, 1688c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = 0x26, 1698c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1708c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1718c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1728c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 1738c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x0b, 1748c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x76, 1758c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 1760670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, 1770670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, 1780670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, 1790670c9b3SLiam Breck [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, 1800670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, 1818c0984e5SSebastian Reichel }, 1829aade6d8SLiam Breck bq2750x_regs[BQ27XXX_REG_MAX] = { 1838c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1848c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1858c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 1868c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1878c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1888c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1898c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1908c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 1918c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1a, 1928c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 1938c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1948c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1958c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1968c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 1978c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 1988c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 1998c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 2000670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 2018c0984e5SSebastian Reichel }, 2021059361fSLiam Breck #define bq2751x_regs bq27510g3_regs 2031059361fSLiam Breck #define bq2752x_regs bq27510g3_regs 2049aade6d8SLiam Breck bq27500_regs[BQ27XXX_REG_MAX] = { 20532833635SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 20632833635SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 20732833635SChris Lapa [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 20832833635SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 20932833635SChris Lapa [BQ27XXX_REG_AI] = 0x14, 21032833635SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 21132833635SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 21232833635SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 21332833635SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 21432833635SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 21532833635SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 21632833635SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 21732833635SChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 21832833635SChris Lapa [BQ27XXX_REG_AE] = 0x22, 21932833635SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 22032833635SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 22132833635SChris Lapa [BQ27XXX_REG_AP] = 0x24, 2220670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 22332833635SChris Lapa }, 2241059361fSLiam Breck #define bq27510g1_regs bq27500_regs 2251059361fSLiam Breck #define bq27510g2_regs bq27500_regs 2269aade6d8SLiam Breck bq27510g3_regs[BQ27XXX_REG_MAX] = { 22771375aa7SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 22871375aa7SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 22971375aa7SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x28, 23071375aa7SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 23171375aa7SChris Lapa [BQ27XXX_REG_AI] = 0x14, 23271375aa7SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 23371375aa7SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 23471375aa7SChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 23571375aa7SChris Lapa [BQ27XXX_REG_TTES] = 0x1a, 23671375aa7SChris Lapa [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 23771375aa7SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 23871375aa7SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 23971375aa7SChris Lapa [BQ27XXX_REG_CYCT] = 0x1e, 24071375aa7SChris Lapa [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 24171375aa7SChris Lapa [BQ27XXX_REG_SOC] = 0x20, 24271375aa7SChris Lapa [BQ27XXX_REG_DCAP] = 0x2e, 24371375aa7SChris Lapa [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 2440670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 24571375aa7SChris Lapa }, 2469aade6d8SLiam Breck bq27520g1_regs[BQ27XXX_REG_MAX] = { 24768f2a813SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 24868f2a813SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 24968f2a813SChris Lapa [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 25068f2a813SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 25168f2a813SChris Lapa [BQ27XXX_REG_AI] = 0x14, 25268f2a813SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 25368f2a813SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 25468f2a813SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 25568f2a813SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 25668f2a813SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 25768f2a813SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 25868f2a813SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 25968f2a813SChris Lapa [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, 26068f2a813SChris Lapa [BQ27XXX_REG_AE] = 0x22, 26168f2a813SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 26268f2a813SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 26368f2a813SChris Lapa [BQ27XXX_REG_AP] = 0x24, 2640670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 26568f2a813SChris Lapa }, 2669aade6d8SLiam Breck bq27520g2_regs[BQ27XXX_REG_MAX] = { 267a5deb9a9SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 268a5deb9a9SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 269a5deb9a9SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x36, 270a5deb9a9SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 271a5deb9a9SChris Lapa [BQ27XXX_REG_AI] = 0x14, 272a5deb9a9SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 273a5deb9a9SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 274a5deb9a9SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 275a5deb9a9SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 276a5deb9a9SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 277a5deb9a9SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 278a5deb9a9SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 279a5deb9a9SChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 280a5deb9a9SChris Lapa [BQ27XXX_REG_AE] = 0x22, 281a5deb9a9SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 282a5deb9a9SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 283a5deb9a9SChris Lapa [BQ27XXX_REG_AP] = 0x24, 2840670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 285a5deb9a9SChris Lapa }, 2869aade6d8SLiam Breck bq27520g3_regs[BQ27XXX_REG_MAX] = { 287825e915bSChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 288825e915bSChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 289825e915bSChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x36, 290825e915bSChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 291825e915bSChris Lapa [BQ27XXX_REG_AI] = 0x14, 292825e915bSChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 293825e915bSChris Lapa [BQ27XXX_REG_TTE] = 0x16, 294825e915bSChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 295825e915bSChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 296825e915bSChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 297825e915bSChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 298825e915bSChris Lapa [BQ27XXX_REG_FCC] = 0x12, 299825e915bSChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 300825e915bSChris Lapa [BQ27XXX_REG_AE] = 0x22, 301825e915bSChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 302825e915bSChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 303825e915bSChris Lapa [BQ27XXX_REG_AP] = 0x24, 3040670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 305825e915bSChris Lapa }, 3069aade6d8SLiam Breck bq27520g4_regs[BQ27XXX_REG_MAX] = { 3078835cae5SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 3088835cae5SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 3098835cae5SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x28, 3108835cae5SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 3118835cae5SChris Lapa [BQ27XXX_REG_AI] = 0x14, 3128835cae5SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 3138835cae5SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 3148835cae5SChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 3158835cae5SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 3168835cae5SChris Lapa [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 3178835cae5SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 3188835cae5SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 3198835cae5SChris Lapa [BQ27XXX_REG_CYCT] = 0x1e, 3208835cae5SChris Lapa [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 3218835cae5SChris Lapa [BQ27XXX_REG_SOC] = 0x20, 3228835cae5SChris Lapa [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 3238835cae5SChris Lapa [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 3240670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 3258835cae5SChris Lapa }, 32670a39e10SPavel Machek bq27521_regs[BQ27XXX_REG_MAX] = { 32770a39e10SPavel Machek [BQ27XXX_REG_CTRL] = 0x02, 32870a39e10SPavel Machek [BQ27XXX_REG_TEMP] = 0x0a, 32970a39e10SPavel Machek [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 33070a39e10SPavel Machek [BQ27XXX_REG_VOLT] = 0x0c, 33170a39e10SPavel Machek [BQ27XXX_REG_AI] = 0x0e, 33270a39e10SPavel Machek [BQ27XXX_REG_FLAGS] = 0x08, 33370a39e10SPavel Machek [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, 33470a39e10SPavel Machek [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 33570a39e10SPavel Machek [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 33670a39e10SPavel Machek [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 33770a39e10SPavel Machek [BQ27XXX_REG_NAC] = INVALID_REG_ADDR, 33870a39e10SPavel Machek [BQ27XXX_REG_FCC] = INVALID_REG_ADDR, 33970a39e10SPavel Machek [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, 34070a39e10SPavel Machek [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 34170a39e10SPavel Machek [BQ27XXX_REG_SOC] = INVALID_REG_ADDR, 34270a39e10SPavel Machek [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 34370a39e10SPavel Machek [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 34470a39e10SPavel Machek [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, 34570a39e10SPavel Machek [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, 34670a39e10SPavel Machek [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, 34770a39e10SPavel Machek [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, 34870a39e10SPavel Machek [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, 34970a39e10SPavel Machek }, 3509aade6d8SLiam Breck bq27530_regs[BQ27XXX_REG_MAX] = { 3518c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 3528c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 3538c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x32, 3548c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 3558c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 3568c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 3578c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 3588c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 3598c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 3608c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 3618c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 3628c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 3638c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 3648c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 3658c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 3668c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 3678c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 3680670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 3698c0984e5SSebastian Reichel }, 3703a731c64SLiam Breck #define bq27531_regs bq27530_regs 3719aade6d8SLiam Breck bq27541_regs[BQ27XXX_REG_MAX] = { 3728c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 3738c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 3748c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 3758c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 3768c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 3778c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 3788c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 3798c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 3808c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 3818c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 3828c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 3838c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 3848c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 3858c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 3868c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 3878c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 3888c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 3890670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 3908c0984e5SSebastian Reichel }, 3913a731c64SLiam Breck #define bq27542_regs bq27541_regs 3923a731c64SLiam Breck #define bq27546_regs bq27541_regs 3933a731c64SLiam Breck #define bq27742_regs bq27541_regs 3949aade6d8SLiam Breck bq27545_regs[BQ27XXX_REG_MAX] = { 3958c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 3968c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 3978c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 3988c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 3998c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 4008c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 4018c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 4028c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 4038c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 4048c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 4058c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 4068c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 4078c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 4088c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 4098c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 4108c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 4118c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 4120670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 4138c0984e5SSebastian Reichel }, 4149aade6d8SLiam Breck bq27421_regs[BQ27XXX_REG_MAX] = { 4158c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 4168c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x02, 4178c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x1e, 4188c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x04, 4198c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x10, 4208c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x06, 4218c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, 4228c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 4238c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 4248c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 4258c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x08, 4268c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x0e, 4278c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, 4288c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 4298c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x1c, 4308c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 4318c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x18, 4320670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 4338c0984e5SSebastian Reichel }; 4343a731c64SLiam Breck #define bq27425_regs bq27421_regs 4355ef6a160SAndrew F. Davis #define bq27426_regs bq27421_regs 4363a731c64SLiam Breck #define bq27441_regs bq27421_regs 4373a731c64SLiam Breck #define bq27621_regs bq27421_regs 4388c0984e5SSebastian Reichel 4399aade6d8SLiam Breck static enum power_supply_property bq27000_props[] = { 4408c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4418c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 4428c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4438c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 4448c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4458c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 4468c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 4478c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 4488c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 4498c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 4508c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 4518c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 4528c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 4538c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 4548c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 4558c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ENERGY_NOW, 4568c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 4578c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 4588c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 4598c0984e5SSebastian Reichel }; 4608c0984e5SSebastian Reichel 4619aade6d8SLiam Breck static enum power_supply_property bq27010_props[] = { 4628c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4638c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 4648c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4658c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 4668c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4678c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 4688c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 4698c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 4708c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 4718c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 4728c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 4738c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 4748c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 4758c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 4768c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 4778c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 4788c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 4798c0984e5SSebastian Reichel }; 4808c0984e5SSebastian Reichel 4811059361fSLiam Breck #define bq2750x_props bq27510g3_props 4821059361fSLiam Breck #define bq2751x_props bq27510g3_props 4831059361fSLiam Breck #define bq2752x_props bq27510g3_props 4843bee9ea1SAndrew F. Davis 4859aade6d8SLiam Breck static enum power_supply_property bq27500_props[] = { 48632833635SChris Lapa POWER_SUPPLY_PROP_STATUS, 48732833635SChris Lapa POWER_SUPPLY_PROP_PRESENT, 48832833635SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 48932833635SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 49032833635SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 49132833635SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 49232833635SChris Lapa POWER_SUPPLY_PROP_TEMP, 49332833635SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 49432833635SChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 49532833635SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 49632833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 49732833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 49832833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 49932833635SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 50032833635SChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 50132833635SChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 50232833635SChris Lapa POWER_SUPPLY_PROP_HEALTH, 50332833635SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 50432833635SChris Lapa }; 5051059361fSLiam Breck #define bq27510g1_props bq27500_props 5061059361fSLiam Breck #define bq27510g2_props bq27500_props 507698a2bf5SChris Lapa 5089aade6d8SLiam Breck static enum power_supply_property bq27510g3_props[] = { 50971375aa7SChris Lapa POWER_SUPPLY_PROP_STATUS, 51071375aa7SChris Lapa POWER_SUPPLY_PROP_PRESENT, 51171375aa7SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 51271375aa7SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 51371375aa7SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 51471375aa7SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 51571375aa7SChris Lapa POWER_SUPPLY_PROP_TEMP, 51671375aa7SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 51771375aa7SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 51871375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 51971375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 52071375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 52171375aa7SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 52271375aa7SChris Lapa POWER_SUPPLY_PROP_HEALTH, 52371375aa7SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 52471375aa7SChris Lapa }; 52571375aa7SChris Lapa 5269aade6d8SLiam Breck static enum power_supply_property bq27520g1_props[] = { 52768f2a813SChris Lapa POWER_SUPPLY_PROP_STATUS, 52868f2a813SChris Lapa POWER_SUPPLY_PROP_PRESENT, 52968f2a813SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 53068f2a813SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 53168f2a813SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 53268f2a813SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 53368f2a813SChris Lapa POWER_SUPPLY_PROP_TEMP, 53468f2a813SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 53568f2a813SChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 53668f2a813SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 53768f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 53868f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 53968f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 54068f2a813SChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 54168f2a813SChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 54268f2a813SChris Lapa POWER_SUPPLY_PROP_HEALTH, 54368f2a813SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 54468f2a813SChris Lapa }; 54568f2a813SChris Lapa 5461059361fSLiam Breck #define bq27520g2_props bq27500_props 547a5deb9a9SChris Lapa 5489aade6d8SLiam Breck static enum power_supply_property bq27520g3_props[] = { 549825e915bSChris Lapa POWER_SUPPLY_PROP_STATUS, 550825e915bSChris Lapa POWER_SUPPLY_PROP_PRESENT, 551825e915bSChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 552825e915bSChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 553825e915bSChris Lapa POWER_SUPPLY_PROP_CAPACITY, 554825e915bSChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 555825e915bSChris Lapa POWER_SUPPLY_PROP_TEMP, 556825e915bSChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 557825e915bSChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 558825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 559825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 560825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 561825e915bSChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 562825e915bSChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 563825e915bSChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 564825e915bSChris Lapa POWER_SUPPLY_PROP_HEALTH, 565825e915bSChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 566825e915bSChris Lapa }; 567825e915bSChris Lapa 5689aade6d8SLiam Breck static enum power_supply_property bq27520g4_props[] = { 5698835cae5SChris Lapa POWER_SUPPLY_PROP_STATUS, 5708835cae5SChris Lapa POWER_SUPPLY_PROP_PRESENT, 5718835cae5SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 5728835cae5SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 5738835cae5SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 5748835cae5SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 5758835cae5SChris Lapa POWER_SUPPLY_PROP_TEMP, 5768835cae5SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 5778835cae5SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 5788835cae5SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 5798835cae5SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 5808835cae5SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 5818835cae5SChris Lapa POWER_SUPPLY_PROP_HEALTH, 5828835cae5SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 5838835cae5SChris Lapa }; 5848835cae5SChris Lapa 58570a39e10SPavel Machek static enum power_supply_property bq27521_props[] = { 58670a39e10SPavel Machek POWER_SUPPLY_PROP_STATUS, 58770a39e10SPavel Machek POWER_SUPPLY_PROP_PRESENT, 58870a39e10SPavel Machek POWER_SUPPLY_PROP_VOLTAGE_NOW, 58970a39e10SPavel Machek POWER_SUPPLY_PROP_CURRENT_NOW, 59070a39e10SPavel Machek POWER_SUPPLY_PROP_TEMP, 59170a39e10SPavel Machek POWER_SUPPLY_PROP_TECHNOLOGY, 59270a39e10SPavel Machek }; 59370a39e10SPavel Machek 5949aade6d8SLiam Breck static enum power_supply_property bq27530_props[] = { 5958c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 5968c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 5978c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 5988c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 5998c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 6008c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6018c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 6028c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 6038c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 6048c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 6058c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 6068c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 6078c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 6088c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 6098c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 6108c0984e5SSebastian Reichel }; 6113a731c64SLiam Breck #define bq27531_props bq27530_props 6128c0984e5SSebastian Reichel 6139aade6d8SLiam Breck static enum power_supply_property bq27541_props[] = { 6148c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 6158c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 6168c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 6178c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 6188c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 6198c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6208c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 6218c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 6228c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 6238c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 6248c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 6258c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 6268c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 6278c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 6288c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 6298c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 6308c0984e5SSebastian Reichel }; 6313a731c64SLiam Breck #define bq27542_props bq27541_props 6323a731c64SLiam Breck #define bq27546_props bq27541_props 6333a731c64SLiam Breck #define bq27742_props bq27541_props 6348c0984e5SSebastian Reichel 6359aade6d8SLiam Breck static enum power_supply_property bq27545_props[] = { 6368c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 6378c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 6388c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 6398c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 6408c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 6418c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6428c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 6438c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 6448c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 6458c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 6468c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 6478c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 6488c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 6498c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 6508c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 6518c0984e5SSebastian Reichel }; 6528c0984e5SSebastian Reichel 6539aade6d8SLiam Breck static enum power_supply_property bq27421_props[] = { 6548c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 6558c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 6568c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 6578c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 6588c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 6598c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6608c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 6618c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 6628c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 6638c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 6648c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 6658c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 6668c0984e5SSebastian Reichel }; 6673a731c64SLiam Breck #define bq27425_props bq27421_props 6685ef6a160SAndrew F. Davis #define bq27426_props bq27421_props 6693a731c64SLiam Breck #define bq27441_props bq27421_props 6703a731c64SLiam Breck #define bq27621_props bq27421_props 6718c0984e5SSebastian Reichel 67205045379SLiam Breck struct bq27xxx_dm_reg { 67305045379SLiam Breck u8 subclass_id; 67405045379SLiam Breck u8 offset; 67505045379SLiam Breck u8 bytes; 67605045379SLiam Breck u16 min, max; 67705045379SLiam Breck }; 67805045379SLiam Breck 67905045379SLiam Breck enum bq27xxx_dm_reg_id { 68005045379SLiam Breck BQ27XXX_DM_DESIGN_CAPACITY = 0, 68105045379SLiam Breck BQ27XXX_DM_DESIGN_ENERGY, 68205045379SLiam Breck BQ27XXX_DM_TERMINATE_VOLTAGE, 68305045379SLiam Breck }; 68405045379SLiam Breck 68505045379SLiam Breck #define bq27000_dm_regs 0 68605045379SLiam Breck #define bq27010_dm_regs 0 68705045379SLiam Breck #define bq2750x_dm_regs 0 68805045379SLiam Breck #define bq2751x_dm_regs 0 68905045379SLiam Breck #define bq2752x_dm_regs 0 69005045379SLiam Breck 69105045379SLiam Breck #if 0 /* not yet tested */ 69205045379SLiam Breck static struct bq27xxx_dm_reg bq27500_dm_regs[] = { 69305045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 10, 2, 0, 65535 }, 69405045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { }, /* missing on chip */ 69505045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 48, 2, 1000, 32767 }, 69605045379SLiam Breck }; 69705045379SLiam Breck #else 69805045379SLiam Breck #define bq27500_dm_regs 0 69905045379SLiam Breck #endif 70005045379SLiam Breck 70105045379SLiam Breck /* todo create data memory definitions from datasheets and test on chips */ 70205045379SLiam Breck #define bq27510g1_dm_regs 0 70305045379SLiam Breck #define bq27510g2_dm_regs 0 70405045379SLiam Breck #define bq27510g3_dm_regs 0 70505045379SLiam Breck #define bq27520g1_dm_regs 0 70605045379SLiam Breck #define bq27520g2_dm_regs 0 70705045379SLiam Breck #define bq27520g3_dm_regs 0 70805045379SLiam Breck #define bq27520g4_dm_regs 0 70970a39e10SPavel Machek #define bq27521_dm_regs 0 71005045379SLiam Breck #define bq27530_dm_regs 0 71105045379SLiam Breck #define bq27531_dm_regs 0 71205045379SLiam Breck #define bq27541_dm_regs 0 71305045379SLiam Breck #define bq27542_dm_regs 0 71405045379SLiam Breck #define bq27546_dm_regs 0 71505045379SLiam Breck #define bq27742_dm_regs 0 71605045379SLiam Breck 71705045379SLiam Breck #if 0 /* not yet tested */ 71805045379SLiam Breck static struct bq27xxx_dm_reg bq27545_dm_regs[] = { 71905045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 23, 2, 0, 32767 }, 72005045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { 48, 25, 2, 0, 32767 }, 72105045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 67, 2, 2800, 3700 }, 72205045379SLiam Breck }; 72305045379SLiam Breck #else 72405045379SLiam Breck #define bq27545_dm_regs 0 72505045379SLiam Breck #endif 72605045379SLiam Breck 72705045379SLiam Breck static struct bq27xxx_dm_reg bq27421_dm_regs[] = { 72805045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 10, 2, 0, 8000 }, 72905045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 12, 2, 0, 32767 }, 73005045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 16, 2, 2500, 3700 }, 73105045379SLiam Breck }; 73205045379SLiam Breck 73305045379SLiam Breck static struct bq27xxx_dm_reg bq27425_dm_regs[] = { 73405045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 12, 2, 0, 32767 }, 73505045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 14, 2, 0, 32767 }, 73605045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 18, 2, 2800, 3700 }, 73705045379SLiam Breck }; 73805045379SLiam Breck 7395ef6a160SAndrew F. Davis static struct bq27xxx_dm_reg bq27426_dm_regs[] = { 7405ef6a160SAndrew F. Davis [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 6, 2, 0, 8000 }, 7415ef6a160SAndrew F. Davis [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 8, 2, 0, 32767 }, 7425ef6a160SAndrew F. Davis [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 10, 2, 2500, 3700 }, 7435ef6a160SAndrew F. Davis }; 7445ef6a160SAndrew F. Davis 74505045379SLiam Breck #if 0 /* not yet tested */ 74605045379SLiam Breck #define bq27441_dm_regs bq27421_dm_regs 74705045379SLiam Breck #else 74805045379SLiam Breck #define bq27441_dm_regs 0 74905045379SLiam Breck #endif 75005045379SLiam Breck 75105045379SLiam Breck #if 0 /* not yet tested */ 75205045379SLiam Breck static struct bq27xxx_dm_reg bq27621_dm_regs[] = { 75305045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 3, 2, 0, 8000 }, 75405045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 5, 2, 0, 32767 }, 75505045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 9, 2, 2500, 3700 }, 75605045379SLiam Breck }; 75705045379SLiam Breck #else 75805045379SLiam Breck #define bq27621_dm_regs 0 75905045379SLiam Breck #endif 76005045379SLiam Breck 7613a731c64SLiam Breck #define BQ27XXX_O_ZERO 0x00000001 76270a39e10SPavel Machek #define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */ 76370a39e10SPavel Machek #define BQ27XXX_O_UTOT 0x00000004 /* has OT overtemperature flag */ 76405045379SLiam Breck #define BQ27XXX_O_CFGUP 0x00000008 76505045379SLiam Breck #define BQ27XXX_O_RAM 0x00000010 7663a731c64SLiam Breck 76705045379SLiam Breck #define BQ27XXX_DATA(ref, key, opt) { \ 7683a731c64SLiam Breck .opts = (opt), \ 76905045379SLiam Breck .unseal_key = key, \ 7709aade6d8SLiam Breck .regs = ref##_regs, \ 77105045379SLiam Breck .dm_regs = ref##_dm_regs, \ 7729aade6d8SLiam Breck .props = ref##_props, \ 7739aade6d8SLiam Breck .props_size = ARRAY_SIZE(ref##_props) } 7748c0984e5SSebastian Reichel 7758c0984e5SSebastian Reichel static struct { 7763a731c64SLiam Breck u32 opts; 77705045379SLiam Breck u32 unseal_key; 7789aade6d8SLiam Breck u8 *regs; 77905045379SLiam Breck struct bq27xxx_dm_reg *dm_regs; 7808c0984e5SSebastian Reichel enum power_supply_property *props; 7819aade6d8SLiam Breck size_t props_size; 7829aade6d8SLiam Breck } bq27xxx_chip_data[] = { 78305045379SLiam Breck [BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO), 78405045379SLiam Breck [BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO), 78505045379SLiam Breck [BQ2750X] = BQ27XXX_DATA(bq2750x, 0 , BQ27XXX_O_OTDC), 78605045379SLiam Breck [BQ2751X] = BQ27XXX_DATA(bq2751x, 0 , BQ27XXX_O_OTDC), 78705045379SLiam Breck [BQ2752X] = BQ27XXX_DATA(bq2752x, 0 , BQ27XXX_O_OTDC), 78805045379SLiam Breck [BQ27500] = BQ27XXX_DATA(bq27500, 0x04143672, BQ27XXX_O_OTDC), 78905045379SLiam Breck [BQ27510G1] = BQ27XXX_DATA(bq27510g1, 0 , BQ27XXX_O_OTDC), 79005045379SLiam Breck [BQ27510G2] = BQ27XXX_DATA(bq27510g2, 0 , BQ27XXX_O_OTDC), 79105045379SLiam Breck [BQ27510G3] = BQ27XXX_DATA(bq27510g3, 0 , BQ27XXX_O_OTDC), 79205045379SLiam Breck [BQ27520G1] = BQ27XXX_DATA(bq27520g1, 0 , BQ27XXX_O_OTDC), 79305045379SLiam Breck [BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC), 79405045379SLiam Breck [BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC), 79505045379SLiam Breck [BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0 , BQ27XXX_O_OTDC), 79670a39e10SPavel Machek [BQ27521] = BQ27XXX_DATA(bq27521, 0 , 0), 79705045379SLiam Breck [BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT), 79805045379SLiam Breck [BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT), 79905045379SLiam Breck [BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC), 80005045379SLiam Breck [BQ27542] = BQ27XXX_DATA(bq27542, 0 , BQ27XXX_O_OTDC), 80105045379SLiam Breck [BQ27546] = BQ27XXX_DATA(bq27546, 0 , BQ27XXX_O_OTDC), 80205045379SLiam Breck [BQ27742] = BQ27XXX_DATA(bq27742, 0 , BQ27XXX_O_OTDC), 80305045379SLiam Breck [BQ27545] = BQ27XXX_DATA(bq27545, 0x04143672, BQ27XXX_O_OTDC), 80405045379SLiam Breck [BQ27421] = BQ27XXX_DATA(bq27421, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 80505045379SLiam Breck [BQ27425] = BQ27XXX_DATA(bq27425, 0x04143672, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP), 8065ef6a160SAndrew F. Davis [BQ27426] = BQ27XXX_DATA(bq27426, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 80705045379SLiam Breck [BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 80805045379SLiam Breck [BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 8098c0984e5SSebastian Reichel }; 8108c0984e5SSebastian Reichel 8111d72706fSMatt Ranostay static DEFINE_MUTEX(bq27xxx_list_lock); 8121d72706fSMatt Ranostay static LIST_HEAD(bq27xxx_battery_devices); 8131d72706fSMatt Ranostay 8140670c9b3SLiam Breck #define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500) 8150670c9b3SLiam Breck 8160670c9b3SLiam Breck #define BQ27XXX_DM_SZ 32 8170670c9b3SLiam Breck 8180670c9b3SLiam Breck /** 8190670c9b3SLiam Breck * struct bq27xxx_dm_buf - chip data memory buffer 8200670c9b3SLiam Breck * @class: data memory subclass_id 8210670c9b3SLiam Breck * @block: data memory block number 8220670c9b3SLiam Breck * @data: data from/for the block 8230670c9b3SLiam Breck * @has_data: true if data has been filled by read 8240670c9b3SLiam Breck * @dirty: true if data has changed since last read/write 8250670c9b3SLiam Breck * 8260670c9b3SLiam Breck * Encapsulates info required to manage chip data memory blocks. 8270670c9b3SLiam Breck */ 8280670c9b3SLiam Breck struct bq27xxx_dm_buf { 8290670c9b3SLiam Breck u8 class; 8300670c9b3SLiam Breck u8 block; 8310670c9b3SLiam Breck u8 data[BQ27XXX_DM_SZ]; 8320670c9b3SLiam Breck bool has_data, dirty; 8330670c9b3SLiam Breck }; 8340670c9b3SLiam Breck 835ccce4409SLiam Breck #define BQ27XXX_DM_BUF(di, i) { \ 836ccce4409SLiam Breck .class = (di)->dm_regs[i].subclass_id, \ 837ccce4409SLiam Breck .block = (di)->dm_regs[i].offset / BQ27XXX_DM_SZ, \ 838ccce4409SLiam Breck } 839ccce4409SLiam Breck 840ccce4409SLiam Breck static inline u16 *bq27xxx_dm_reg_ptr(struct bq27xxx_dm_buf *buf, 841ccce4409SLiam Breck struct bq27xxx_dm_reg *reg) 842ccce4409SLiam Breck { 843ccce4409SLiam Breck if (buf->class == reg->subclass_id && 844ccce4409SLiam Breck buf->block == reg->offset / BQ27XXX_DM_SZ) 845ccce4409SLiam Breck return (u16 *) (buf->data + reg->offset % BQ27XXX_DM_SZ); 846ccce4409SLiam Breck 847ccce4409SLiam Breck return NULL; 848ccce4409SLiam Breck } 849ccce4409SLiam Breck 850ccce4409SLiam Breck static const char * const bq27xxx_dm_reg_name[] = { 851ccce4409SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity", 852ccce4409SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy", 853ccce4409SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage", 854ccce4409SLiam Breck }; 855ccce4409SLiam Breck 856ccce4409SLiam Breck 857ccce4409SLiam Breck static bool bq27xxx_dt_to_nvm = true; 858ccce4409SLiam Breck module_param_named(dt_monitored_battery_updates_nvm, bq27xxx_dt_to_nvm, bool, 0444); 859ccce4409SLiam Breck MODULE_PARM_DESC(dt_monitored_battery_updates_nvm, 860ccce4409SLiam Breck "Devicetree monitored-battery config updates data memory on NVM/flash chips.\n" 861ccce4409SLiam Breck "Users must set this =0 when installing a different type of battery!\n" 862ccce4409SLiam Breck "Default is =1." 863ccce4409SLiam Breck #ifndef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM 864ccce4409SLiam Breck "\nSetting this affects future kernel updates, not the current configuration." 865ccce4409SLiam Breck #endif 866ccce4409SLiam Breck ); 8670670c9b3SLiam Breck 8681d72706fSMatt Ranostay static int poll_interval_param_set(const char *val, const struct kernel_param *kp) 8691d72706fSMatt Ranostay { 8701d72706fSMatt Ranostay struct bq27xxx_device_info *di; 871950b6c2dSMatt Ranostay unsigned int prev_val = *(unsigned int *) kp->arg; 8721d72706fSMatt Ranostay int ret; 8731d72706fSMatt Ranostay 8741d72706fSMatt Ranostay ret = param_set_uint(val, kp); 875950b6c2dSMatt Ranostay if (ret < 0 || prev_val == *(unsigned int *) kp->arg) 8761d72706fSMatt Ranostay return ret; 8771d72706fSMatt Ranostay 8781d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 8791d72706fSMatt Ranostay list_for_each_entry(di, &bq27xxx_battery_devices, list) { 8801d72706fSMatt Ranostay cancel_delayed_work_sync(&di->work); 8811d72706fSMatt Ranostay schedule_delayed_work(&di->work, 0); 8821d72706fSMatt Ranostay } 8831d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 8841d72706fSMatt Ranostay 8851d72706fSMatt Ranostay return ret; 8861d72706fSMatt Ranostay } 8871d72706fSMatt Ranostay 8881d72706fSMatt Ranostay static const struct kernel_param_ops param_ops_poll_interval = { 8891d72706fSMatt Ranostay .get = param_get_uint, 8901d72706fSMatt Ranostay .set = poll_interval_param_set, 8911d72706fSMatt Ranostay }; 8921d72706fSMatt Ranostay 8938c0984e5SSebastian Reichel static unsigned int poll_interval = 360; 8941d72706fSMatt Ranostay module_param_cb(poll_interval, ¶m_ops_poll_interval, &poll_interval, 0644); 8958c0984e5SSebastian Reichel MODULE_PARM_DESC(poll_interval, 8968c0984e5SSebastian Reichel "battery poll interval in seconds - 0 disables polling"); 8978c0984e5SSebastian Reichel 8988c0984e5SSebastian Reichel /* 8998c0984e5SSebastian Reichel * Common code for BQ27xxx devices 9008c0984e5SSebastian Reichel */ 9018c0984e5SSebastian Reichel 9028c0984e5SSebastian Reichel static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, 9038c0984e5SSebastian Reichel bool single) 9048c0984e5SSebastian Reichel { 90514073f66SMatt Ranostay int ret; 90614073f66SMatt Ranostay 9078c0984e5SSebastian Reichel if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 9088c0984e5SSebastian Reichel return -EINVAL; 9098c0984e5SSebastian Reichel 91014073f66SMatt Ranostay ret = di->bus.read(di, di->regs[reg_index], single); 91114073f66SMatt Ranostay if (ret < 0) 91214073f66SMatt Ranostay dev_dbg(di->dev, "failed to read register 0x%02x (index %d)\n", 91314073f66SMatt Ranostay di->regs[reg_index], reg_index); 91414073f66SMatt Ranostay 91514073f66SMatt Ranostay return ret; 91614073f66SMatt Ranostay } 91714073f66SMatt Ranostay 91814073f66SMatt Ranostay static inline int bq27xxx_write(struct bq27xxx_device_info *di, int reg_index, 91914073f66SMatt Ranostay u16 value, bool single) 92014073f66SMatt Ranostay { 92114073f66SMatt Ranostay int ret; 92214073f66SMatt Ranostay 92314073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 92414073f66SMatt Ranostay return -EINVAL; 92514073f66SMatt Ranostay 92614073f66SMatt Ranostay if (!di->bus.write) 92714073f66SMatt Ranostay return -EPERM; 92814073f66SMatt Ranostay 92914073f66SMatt Ranostay ret = di->bus.write(di, di->regs[reg_index], value, single); 93014073f66SMatt Ranostay if (ret < 0) 93114073f66SMatt Ranostay dev_dbg(di->dev, "failed to write register 0x%02x (index %d)\n", 93214073f66SMatt Ranostay di->regs[reg_index], reg_index); 93314073f66SMatt Ranostay 93414073f66SMatt Ranostay return ret; 93514073f66SMatt Ranostay } 93614073f66SMatt Ranostay 93714073f66SMatt Ranostay static inline int bq27xxx_read_block(struct bq27xxx_device_info *di, int reg_index, 93814073f66SMatt Ranostay u8 *data, int len) 93914073f66SMatt Ranostay { 94014073f66SMatt Ranostay int ret; 94114073f66SMatt Ranostay 94214073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 94314073f66SMatt Ranostay return -EINVAL; 94414073f66SMatt Ranostay 94514073f66SMatt Ranostay if (!di->bus.read_bulk) 94614073f66SMatt Ranostay return -EPERM; 94714073f66SMatt Ranostay 94814073f66SMatt Ranostay ret = di->bus.read_bulk(di, di->regs[reg_index], data, len); 94914073f66SMatt Ranostay if (ret < 0) 95014073f66SMatt Ranostay dev_dbg(di->dev, "failed to read_bulk register 0x%02x (index %d)\n", 95114073f66SMatt Ranostay di->regs[reg_index], reg_index); 95214073f66SMatt Ranostay 95314073f66SMatt Ranostay return ret; 95414073f66SMatt Ranostay } 95514073f66SMatt Ranostay 95614073f66SMatt Ranostay static inline int bq27xxx_write_block(struct bq27xxx_device_info *di, int reg_index, 95714073f66SMatt Ranostay u8 *data, int len) 95814073f66SMatt Ranostay { 95914073f66SMatt Ranostay int ret; 96014073f66SMatt Ranostay 96114073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 96214073f66SMatt Ranostay return -EINVAL; 96314073f66SMatt Ranostay 96414073f66SMatt Ranostay if (!di->bus.write_bulk) 96514073f66SMatt Ranostay return -EPERM; 96614073f66SMatt Ranostay 96714073f66SMatt Ranostay ret = di->bus.write_bulk(di, di->regs[reg_index], data, len); 96814073f66SMatt Ranostay if (ret < 0) 96914073f66SMatt Ranostay dev_dbg(di->dev, "failed to write_bulk register 0x%02x (index %d)\n", 97014073f66SMatt Ranostay di->regs[reg_index], reg_index); 97114073f66SMatt Ranostay 97214073f66SMatt Ranostay return ret; 9738c0984e5SSebastian Reichel } 9748c0984e5SSebastian Reichel 9750670c9b3SLiam Breck static int bq27xxx_battery_seal(struct bq27xxx_device_info *di) 9760670c9b3SLiam Breck { 9770670c9b3SLiam Breck int ret; 9780670c9b3SLiam Breck 9790670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_SEALED, false); 9800670c9b3SLiam Breck if (ret < 0) { 9810670c9b3SLiam Breck dev_err(di->dev, "bus error on seal: %d\n", ret); 9820670c9b3SLiam Breck return ret; 9830670c9b3SLiam Breck } 9840670c9b3SLiam Breck 9850670c9b3SLiam Breck return 0; 9860670c9b3SLiam Breck } 9870670c9b3SLiam Breck 9880670c9b3SLiam Breck static int bq27xxx_battery_unseal(struct bq27xxx_device_info *di) 9890670c9b3SLiam Breck { 9900670c9b3SLiam Breck int ret; 9910670c9b3SLiam Breck 9920670c9b3SLiam Breck if (di->unseal_key == 0) { 9930670c9b3SLiam Breck dev_err(di->dev, "unseal failed due to missing key\n"); 9940670c9b3SLiam Breck return -EINVAL; 9950670c9b3SLiam Breck } 9960670c9b3SLiam Breck 9970670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, (u16)(di->unseal_key >> 16), false); 9980670c9b3SLiam Breck if (ret < 0) 9990670c9b3SLiam Breck goto out; 10000670c9b3SLiam Breck 10010670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, (u16)di->unseal_key, false); 10020670c9b3SLiam Breck if (ret < 0) 10030670c9b3SLiam Breck goto out; 10040670c9b3SLiam Breck 10050670c9b3SLiam Breck return 0; 10060670c9b3SLiam Breck 10070670c9b3SLiam Breck out: 10080670c9b3SLiam Breck dev_err(di->dev, "bus error on unseal: %d\n", ret); 10090670c9b3SLiam Breck return ret; 10100670c9b3SLiam Breck } 10110670c9b3SLiam Breck 10120670c9b3SLiam Breck static u8 bq27xxx_battery_checksum_dm_block(struct bq27xxx_dm_buf *buf) 10130670c9b3SLiam Breck { 10140670c9b3SLiam Breck u16 sum = 0; 10150670c9b3SLiam Breck int i; 10160670c9b3SLiam Breck 10170670c9b3SLiam Breck for (i = 0; i < BQ27XXX_DM_SZ; i++) 10180670c9b3SLiam Breck sum += buf->data[i]; 10190670c9b3SLiam Breck sum &= 0xff; 10200670c9b3SLiam Breck 10210670c9b3SLiam Breck return 0xff - sum; 10220670c9b3SLiam Breck } 10230670c9b3SLiam Breck 10240670c9b3SLiam Breck static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di, 10250670c9b3SLiam Breck struct bq27xxx_dm_buf *buf) 10260670c9b3SLiam Breck { 10270670c9b3SLiam Breck int ret; 10280670c9b3SLiam Breck 10290670c9b3SLiam Breck buf->has_data = false; 10300670c9b3SLiam Breck 10310670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CLASS, buf->class, true); 10320670c9b3SLiam Breck if (ret < 0) 10330670c9b3SLiam Breck goto out; 10340670c9b3SLiam Breck 10350670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_BLOCK, buf->block, true); 10360670c9b3SLiam Breck if (ret < 0) 10370670c9b3SLiam Breck goto out; 10380670c9b3SLiam Breck 10390670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 10400670c9b3SLiam Breck 10410670c9b3SLiam Breck ret = bq27xxx_read_block(di, BQ27XXX_DM_DATA, buf->data, BQ27XXX_DM_SZ); 10420670c9b3SLiam Breck if (ret < 0) 10430670c9b3SLiam Breck goto out; 10440670c9b3SLiam Breck 10450670c9b3SLiam Breck ret = bq27xxx_read(di, BQ27XXX_DM_CKSUM, true); 10460670c9b3SLiam Breck if (ret < 0) 10470670c9b3SLiam Breck goto out; 10480670c9b3SLiam Breck 10490670c9b3SLiam Breck if ((u8)ret != bq27xxx_battery_checksum_dm_block(buf)) { 10500670c9b3SLiam Breck ret = -EINVAL; 10510670c9b3SLiam Breck goto out; 10520670c9b3SLiam Breck } 10530670c9b3SLiam Breck 10540670c9b3SLiam Breck buf->has_data = true; 10550670c9b3SLiam Breck buf->dirty = false; 10560670c9b3SLiam Breck 10570670c9b3SLiam Breck return 0; 10580670c9b3SLiam Breck 10590670c9b3SLiam Breck out: 10600670c9b3SLiam Breck dev_err(di->dev, "bus error reading chip memory: %d\n", ret); 10610670c9b3SLiam Breck return ret; 10620670c9b3SLiam Breck } 10630670c9b3SLiam Breck 1064ccce4409SLiam Breck static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di, 1065ccce4409SLiam Breck struct bq27xxx_dm_buf *buf, 1066ccce4409SLiam Breck enum bq27xxx_dm_reg_id reg_id, 1067ccce4409SLiam Breck unsigned int val) 1068ccce4409SLiam Breck { 1069ccce4409SLiam Breck struct bq27xxx_dm_reg *reg = &di->dm_regs[reg_id]; 1070ccce4409SLiam Breck const char *str = bq27xxx_dm_reg_name[reg_id]; 1071ccce4409SLiam Breck u16 *prev = bq27xxx_dm_reg_ptr(buf, reg); 1072ccce4409SLiam Breck 1073ccce4409SLiam Breck if (prev == NULL) { 1074ccce4409SLiam Breck dev_warn(di->dev, "buffer does not match %s dm spec\n", str); 1075ccce4409SLiam Breck return; 1076ccce4409SLiam Breck } 1077ccce4409SLiam Breck 1078ccce4409SLiam Breck if (reg->bytes != 2) { 1079ccce4409SLiam Breck dev_warn(di->dev, "%s dm spec has unsupported byte size\n", str); 1080ccce4409SLiam Breck return; 1081ccce4409SLiam Breck } 1082ccce4409SLiam Breck 1083ccce4409SLiam Breck if (!buf->has_data) 1084ccce4409SLiam Breck return; 1085ccce4409SLiam Breck 1086ccce4409SLiam Breck if (be16_to_cpup(prev) == val) { 1087ccce4409SLiam Breck dev_info(di->dev, "%s has %u\n", str, val); 1088ccce4409SLiam Breck return; 1089ccce4409SLiam Breck } 1090ccce4409SLiam Breck 1091ccce4409SLiam Breck #ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM 109205045379SLiam Breck if (!(di->opts & BQ27XXX_O_RAM) && !bq27xxx_dt_to_nvm) { 1093ccce4409SLiam Breck #else 109405045379SLiam Breck if (!(di->opts & BQ27XXX_O_RAM)) { 1095ccce4409SLiam Breck #endif 1096ccce4409SLiam Breck /* devicetree and NVM differ; defer to NVM */ 1097ccce4409SLiam Breck dev_warn(di->dev, "%s has %u; update to %u disallowed " 1098ccce4409SLiam Breck #ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM 1099ccce4409SLiam Breck "by dt_monitored_battery_updates_nvm=0" 1100ccce4409SLiam Breck #else 1101ccce4409SLiam Breck "for flash/NVM data memory" 1102ccce4409SLiam Breck #endif 1103ccce4409SLiam Breck "\n", str, be16_to_cpup(prev), val); 1104ccce4409SLiam Breck return; 1105ccce4409SLiam Breck } 1106ccce4409SLiam Breck 1107ccce4409SLiam Breck dev_info(di->dev, "update %s to %u\n", str, val); 1108ccce4409SLiam Breck 1109ccce4409SLiam Breck *prev = cpu_to_be16(val); 1110ccce4409SLiam Breck buf->dirty = true; 1111ccce4409SLiam Breck } 1112ccce4409SLiam Breck 11130670c9b3SLiam Breck static int bq27xxx_battery_cfgupdate_priv(struct bq27xxx_device_info *di, bool active) 11140670c9b3SLiam Breck { 11150670c9b3SLiam Breck const int limit = 100; 11160670c9b3SLiam Breck u16 cmd = active ? BQ27XXX_SET_CFGUPDATE : BQ27XXX_SOFT_RESET; 11170670c9b3SLiam Breck int ret, try = limit; 11180670c9b3SLiam Breck 11190670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, cmd, false); 11200670c9b3SLiam Breck if (ret < 0) 11210670c9b3SLiam Breck return ret; 11220670c9b3SLiam Breck 11230670c9b3SLiam Breck do { 11240670c9b3SLiam Breck BQ27XXX_MSLEEP(25); 11250670c9b3SLiam Breck ret = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false); 11260670c9b3SLiam Breck if (ret < 0) 11270670c9b3SLiam Breck return ret; 11280670c9b3SLiam Breck } while (!!(ret & BQ27XXX_FLAG_CFGUP) != active && --try); 11290670c9b3SLiam Breck 113005045379SLiam Breck if (!try && di->chip != BQ27425) { // 425 has a bug 11310670c9b3SLiam Breck dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", active); 11320670c9b3SLiam Breck return -EINVAL; 11330670c9b3SLiam Breck } 11340670c9b3SLiam Breck 11350670c9b3SLiam Breck if (limit - try > 3) 11360670c9b3SLiam Breck dev_warn(di->dev, "cfgupdate %d, retries %d\n", active, limit - try); 11370670c9b3SLiam Breck 11380670c9b3SLiam Breck return 0; 11390670c9b3SLiam Breck } 11400670c9b3SLiam Breck 11410670c9b3SLiam Breck static inline int bq27xxx_battery_set_cfgupdate(struct bq27xxx_device_info *di) 11420670c9b3SLiam Breck { 11430670c9b3SLiam Breck int ret = bq27xxx_battery_cfgupdate_priv(di, true); 11440670c9b3SLiam Breck if (ret < 0 && ret != -EINVAL) 11450670c9b3SLiam Breck dev_err(di->dev, "bus error on set_cfgupdate: %d\n", ret); 11460670c9b3SLiam Breck 11470670c9b3SLiam Breck return ret; 11480670c9b3SLiam Breck } 11490670c9b3SLiam Breck 11500670c9b3SLiam Breck static inline int bq27xxx_battery_soft_reset(struct bq27xxx_device_info *di) 11510670c9b3SLiam Breck { 11520670c9b3SLiam Breck int ret = bq27xxx_battery_cfgupdate_priv(di, false); 11530670c9b3SLiam Breck if (ret < 0 && ret != -EINVAL) 11540670c9b3SLiam Breck dev_err(di->dev, "bus error on soft_reset: %d\n", ret); 11550670c9b3SLiam Breck 11560670c9b3SLiam Breck return ret; 11570670c9b3SLiam Breck } 11580670c9b3SLiam Breck 11590670c9b3SLiam Breck static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di, 11600670c9b3SLiam Breck struct bq27xxx_dm_buf *buf) 11610670c9b3SLiam Breck { 116205045379SLiam Breck bool cfgup = di->opts & BQ27XXX_O_CFGUP; 11630670c9b3SLiam Breck int ret; 11640670c9b3SLiam Breck 11650670c9b3SLiam Breck if (!buf->dirty) 11660670c9b3SLiam Breck return 0; 11670670c9b3SLiam Breck 11680670c9b3SLiam Breck if (cfgup) { 11690670c9b3SLiam Breck ret = bq27xxx_battery_set_cfgupdate(di); 11700670c9b3SLiam Breck if (ret < 0) 11710670c9b3SLiam Breck return ret; 11720670c9b3SLiam Breck } 11730670c9b3SLiam Breck 11740670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CTRL, 0, true); 11750670c9b3SLiam Breck if (ret < 0) 11760670c9b3SLiam Breck goto out; 11770670c9b3SLiam Breck 11780670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CLASS, buf->class, true); 11790670c9b3SLiam Breck if (ret < 0) 11800670c9b3SLiam Breck goto out; 11810670c9b3SLiam Breck 11820670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_BLOCK, buf->block, true); 11830670c9b3SLiam Breck if (ret < 0) 11840670c9b3SLiam Breck goto out; 11850670c9b3SLiam Breck 11860670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 11870670c9b3SLiam Breck 11880670c9b3SLiam Breck ret = bq27xxx_write_block(di, BQ27XXX_DM_DATA, buf->data, BQ27XXX_DM_SZ); 11890670c9b3SLiam Breck if (ret < 0) 11900670c9b3SLiam Breck goto out; 11910670c9b3SLiam Breck 11920670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CKSUM, 11930670c9b3SLiam Breck bq27xxx_battery_checksum_dm_block(buf), true); 11940670c9b3SLiam Breck if (ret < 0) 11950670c9b3SLiam Breck goto out; 11960670c9b3SLiam Breck 11970670c9b3SLiam Breck /* DO NOT read BQ27XXX_DM_CKSUM here to verify it! That may cause NVM 11980670c9b3SLiam Breck * corruption on the '425 chip (and perhaps others), which can damage 11990670c9b3SLiam Breck * the chip. 12000670c9b3SLiam Breck */ 12010670c9b3SLiam Breck 12020670c9b3SLiam Breck if (cfgup) { 12030670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 12040670c9b3SLiam Breck ret = bq27xxx_battery_soft_reset(di); 12050670c9b3SLiam Breck if (ret < 0) 12060670c9b3SLiam Breck return ret; 12070670c9b3SLiam Breck } else { 12080670c9b3SLiam Breck BQ27XXX_MSLEEP(100); /* flash DM updates in <100ms */ 12090670c9b3SLiam Breck } 12100670c9b3SLiam Breck 12110670c9b3SLiam Breck buf->dirty = false; 12120670c9b3SLiam Breck 12130670c9b3SLiam Breck return 0; 12140670c9b3SLiam Breck 12150670c9b3SLiam Breck out: 12160670c9b3SLiam Breck if (cfgup) 12170670c9b3SLiam Breck bq27xxx_battery_soft_reset(di); 12180670c9b3SLiam Breck 12190670c9b3SLiam Breck dev_err(di->dev, "bus error writing chip memory: %d\n", ret); 12200670c9b3SLiam Breck return ret; 12210670c9b3SLiam Breck } 12220670c9b3SLiam Breck 1223ccce4409SLiam Breck static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di, 1224ccce4409SLiam Breck struct power_supply_battery_info *info) 1225ccce4409SLiam Breck { 1226ccce4409SLiam Breck struct bq27xxx_dm_buf bd = BQ27XXX_DM_BUF(di, BQ27XXX_DM_DESIGN_CAPACITY); 1227ccce4409SLiam Breck struct bq27xxx_dm_buf bt = BQ27XXX_DM_BUF(di, BQ27XXX_DM_TERMINATE_VOLTAGE); 1228ccce4409SLiam Breck bool updated; 1229ccce4409SLiam Breck 1230ccce4409SLiam Breck if (bq27xxx_battery_unseal(di) < 0) 1231ccce4409SLiam Breck return; 1232ccce4409SLiam Breck 1233ccce4409SLiam Breck if (info->charge_full_design_uah != -EINVAL && 1234ccce4409SLiam Breck info->energy_full_design_uwh != -EINVAL) { 1235ccce4409SLiam Breck bq27xxx_battery_read_dm_block(di, &bd); 1236ccce4409SLiam Breck /* assume design energy & capacity are in same block */ 1237ccce4409SLiam Breck bq27xxx_battery_update_dm_block(di, &bd, 1238ccce4409SLiam Breck BQ27XXX_DM_DESIGN_CAPACITY, 1239ccce4409SLiam Breck info->charge_full_design_uah / 1000); 1240ccce4409SLiam Breck bq27xxx_battery_update_dm_block(di, &bd, 1241ccce4409SLiam Breck BQ27XXX_DM_DESIGN_ENERGY, 1242ccce4409SLiam Breck info->energy_full_design_uwh / 1000); 1243ccce4409SLiam Breck } 1244ccce4409SLiam Breck 1245ccce4409SLiam Breck if (info->voltage_min_design_uv != -EINVAL) { 1246ccce4409SLiam Breck bool same = bd.class == bt.class && bd.block == bt.block; 1247ccce4409SLiam Breck if (!same) 1248ccce4409SLiam Breck bq27xxx_battery_read_dm_block(di, &bt); 1249ccce4409SLiam Breck bq27xxx_battery_update_dm_block(di, same ? &bd : &bt, 1250ccce4409SLiam Breck BQ27XXX_DM_TERMINATE_VOLTAGE, 1251ccce4409SLiam Breck info->voltage_min_design_uv / 1000); 1252ccce4409SLiam Breck } 1253ccce4409SLiam Breck 1254ccce4409SLiam Breck updated = bd.dirty || bt.dirty; 1255ccce4409SLiam Breck 1256ccce4409SLiam Breck bq27xxx_battery_write_dm_block(di, &bd); 1257ccce4409SLiam Breck bq27xxx_battery_write_dm_block(di, &bt); 1258ccce4409SLiam Breck 1259ccce4409SLiam Breck bq27xxx_battery_seal(di); 1260ccce4409SLiam Breck 126105045379SLiam Breck if (updated && !(di->opts & BQ27XXX_O_CFGUP)) { 1262ccce4409SLiam Breck bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_RESET, false); 1263ccce4409SLiam Breck BQ27XXX_MSLEEP(300); /* reset time is not documented */ 1264ccce4409SLiam Breck } 1265ccce4409SLiam Breck /* assume bq27xxx_battery_update() is called hereafter */ 1266ccce4409SLiam Breck } 1267ccce4409SLiam Breck 1268ccce4409SLiam Breck static void bq27xxx_battery_settings(struct bq27xxx_device_info *di) 1269ccce4409SLiam Breck { 1270ccce4409SLiam Breck struct power_supply_battery_info info = {}; 1271ccce4409SLiam Breck unsigned int min, max; 1272ccce4409SLiam Breck 1273ccce4409SLiam Breck if (power_supply_get_battery_info(di->bat, &info) < 0) 1274ccce4409SLiam Breck return; 1275ccce4409SLiam Breck 1276ccce4409SLiam Breck if (!di->dm_regs) { 1277ccce4409SLiam Breck dev_warn(di->dev, "data memory update not supported for chip\n"); 1278ccce4409SLiam Breck return; 1279ccce4409SLiam Breck } 1280ccce4409SLiam Breck 1281ccce4409SLiam Breck if (info.energy_full_design_uwh != info.charge_full_design_uah) { 1282ccce4409SLiam Breck if (info.energy_full_design_uwh == -EINVAL) 1283ccce4409SLiam Breck dev_warn(di->dev, "missing battery:energy-full-design-microwatt-hours\n"); 1284ccce4409SLiam Breck else if (info.charge_full_design_uah == -EINVAL) 1285ccce4409SLiam Breck dev_warn(di->dev, "missing battery:charge-full-design-microamp-hours\n"); 1286ccce4409SLiam Breck } 1287ccce4409SLiam Breck 1288ccce4409SLiam Breck /* assume min == 0 */ 1289ccce4409SLiam Breck max = di->dm_regs[BQ27XXX_DM_DESIGN_ENERGY].max; 1290ccce4409SLiam Breck if (info.energy_full_design_uwh > max * 1000) { 1291ccce4409SLiam Breck dev_err(di->dev, "invalid battery:energy-full-design-microwatt-hours %d\n", 1292ccce4409SLiam Breck info.energy_full_design_uwh); 1293ccce4409SLiam Breck info.energy_full_design_uwh = -EINVAL; 1294ccce4409SLiam Breck } 1295ccce4409SLiam Breck 1296ccce4409SLiam Breck /* assume min == 0 */ 1297ccce4409SLiam Breck max = di->dm_regs[BQ27XXX_DM_DESIGN_CAPACITY].max; 1298ccce4409SLiam Breck if (info.charge_full_design_uah > max * 1000) { 1299ccce4409SLiam Breck dev_err(di->dev, "invalid battery:charge-full-design-microamp-hours %d\n", 1300ccce4409SLiam Breck info.charge_full_design_uah); 1301ccce4409SLiam Breck info.charge_full_design_uah = -EINVAL; 1302ccce4409SLiam Breck } 1303ccce4409SLiam Breck 1304ccce4409SLiam Breck min = di->dm_regs[BQ27XXX_DM_TERMINATE_VOLTAGE].min; 1305ccce4409SLiam Breck max = di->dm_regs[BQ27XXX_DM_TERMINATE_VOLTAGE].max; 1306ccce4409SLiam Breck if ((info.voltage_min_design_uv < min * 1000 || 1307ccce4409SLiam Breck info.voltage_min_design_uv > max * 1000) && 1308ccce4409SLiam Breck info.voltage_min_design_uv != -EINVAL) { 1309ccce4409SLiam Breck dev_err(di->dev, "invalid battery:voltage-min-design-microvolt %d\n", 1310ccce4409SLiam Breck info.voltage_min_design_uv); 1311ccce4409SLiam Breck info.voltage_min_design_uv = -EINVAL; 1312ccce4409SLiam Breck } 1313ccce4409SLiam Breck 1314ccce4409SLiam Breck if ((info.energy_full_design_uwh != -EINVAL && 1315ccce4409SLiam Breck info.charge_full_design_uah != -EINVAL) || 1316ccce4409SLiam Breck info.voltage_min_design_uv != -EINVAL) 1317ccce4409SLiam Breck bq27xxx_battery_set_config(di, &info); 1318ccce4409SLiam Breck } 1319ccce4409SLiam Breck 13208c0984e5SSebastian Reichel /* 13218c0984e5SSebastian Reichel * Return the battery State-of-Charge 13228c0984e5SSebastian Reichel * Or < 0 if something fails. 13238c0984e5SSebastian Reichel */ 13248c0984e5SSebastian Reichel static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di) 13258c0984e5SSebastian Reichel { 13268c0984e5SSebastian Reichel int soc; 13278c0984e5SSebastian Reichel 13283a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 13298c0984e5SSebastian Reichel soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true); 13308c0984e5SSebastian Reichel else 13318c0984e5SSebastian Reichel soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false); 13328c0984e5SSebastian Reichel 13338c0984e5SSebastian Reichel if (soc < 0) 13348c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading State-of-Charge\n"); 13358c0984e5SSebastian Reichel 13368c0984e5SSebastian Reichel return soc; 13378c0984e5SSebastian Reichel } 13388c0984e5SSebastian Reichel 13398c0984e5SSebastian Reichel /* 13408c0984e5SSebastian Reichel * Return a battery charge value in µAh 13418c0984e5SSebastian Reichel * Or < 0 if something fails. 13428c0984e5SSebastian Reichel */ 13438c0984e5SSebastian Reichel static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg) 13448c0984e5SSebastian Reichel { 13458c0984e5SSebastian Reichel int charge; 13468c0984e5SSebastian Reichel 13478c0984e5SSebastian Reichel charge = bq27xxx_read(di, reg, false); 13488c0984e5SSebastian Reichel if (charge < 0) { 13498c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading charge register %02x: %d\n", 13508c0984e5SSebastian Reichel reg, charge); 13518c0984e5SSebastian Reichel return charge; 13528c0984e5SSebastian Reichel } 13538c0984e5SSebastian Reichel 13543a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 13558c0984e5SSebastian Reichel charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 13568c0984e5SSebastian Reichel else 13578c0984e5SSebastian Reichel charge *= 1000; 13588c0984e5SSebastian Reichel 13598c0984e5SSebastian Reichel return charge; 13608c0984e5SSebastian Reichel } 13618c0984e5SSebastian Reichel 13628c0984e5SSebastian Reichel /* 13638c0984e5SSebastian Reichel * Return the battery Nominal available capacity in µAh 13648c0984e5SSebastian Reichel * Or < 0 if something fails. 13658c0984e5SSebastian Reichel */ 13668c0984e5SSebastian Reichel static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di) 13678c0984e5SSebastian Reichel { 13688c0984e5SSebastian Reichel int flags; 13698c0984e5SSebastian Reichel 13703a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) { 13718c0984e5SSebastian Reichel flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); 13728c0984e5SSebastian Reichel if (flags >= 0 && (flags & BQ27000_FLAG_CI)) 13738c0984e5SSebastian Reichel return -ENODATA; 13748c0984e5SSebastian Reichel } 13758c0984e5SSebastian Reichel 13768c0984e5SSebastian Reichel return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC); 13778c0984e5SSebastian Reichel } 13788c0984e5SSebastian Reichel 13798c0984e5SSebastian Reichel /* 13808c0984e5SSebastian Reichel * Return the battery Full Charge Capacity in µAh 13818c0984e5SSebastian Reichel * Or < 0 if something fails. 13828c0984e5SSebastian Reichel */ 13838c0984e5SSebastian Reichel static inline int bq27xxx_battery_read_fcc(struct bq27xxx_device_info *di) 13848c0984e5SSebastian Reichel { 13858c0984e5SSebastian Reichel return bq27xxx_battery_read_charge(di, BQ27XXX_REG_FCC); 13868c0984e5SSebastian Reichel } 13878c0984e5SSebastian Reichel 13888c0984e5SSebastian Reichel /* 13898c0984e5SSebastian Reichel * Return the Design Capacity in µAh 13908c0984e5SSebastian Reichel * Or < 0 if something fails. 13918c0984e5SSebastian Reichel */ 13928c0984e5SSebastian Reichel static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di) 13938c0984e5SSebastian Reichel { 13948c0984e5SSebastian Reichel int dcap; 13958c0984e5SSebastian Reichel 13963a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 13978c0984e5SSebastian Reichel dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true); 13988c0984e5SSebastian Reichel else 13998c0984e5SSebastian Reichel dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false); 14008c0984e5SSebastian Reichel 14018c0984e5SSebastian Reichel if (dcap < 0) { 14028c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading initial last measured discharge\n"); 14038c0984e5SSebastian Reichel return dcap; 14048c0984e5SSebastian Reichel } 14058c0984e5SSebastian Reichel 14063a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 14078c0984e5SSebastian Reichel dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 14088c0984e5SSebastian Reichel else 14098c0984e5SSebastian Reichel dcap *= 1000; 14108c0984e5SSebastian Reichel 14118c0984e5SSebastian Reichel return dcap; 14128c0984e5SSebastian Reichel } 14138c0984e5SSebastian Reichel 14148c0984e5SSebastian Reichel /* 14158c0984e5SSebastian Reichel * Return the battery Available energy in µWh 14168c0984e5SSebastian Reichel * Or < 0 if something fails. 14178c0984e5SSebastian Reichel */ 14188c0984e5SSebastian Reichel static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di) 14198c0984e5SSebastian Reichel { 14208c0984e5SSebastian Reichel int ae; 14218c0984e5SSebastian Reichel 14228c0984e5SSebastian Reichel ae = bq27xxx_read(di, BQ27XXX_REG_AE, false); 14238c0984e5SSebastian Reichel if (ae < 0) { 14248c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading available energy\n"); 14258c0984e5SSebastian Reichel return ae; 14268c0984e5SSebastian Reichel } 14278c0984e5SSebastian Reichel 14283a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 14298c0984e5SSebastian Reichel ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS; 14308c0984e5SSebastian Reichel else 14318c0984e5SSebastian Reichel ae *= 1000; 14328c0984e5SSebastian Reichel 14338c0984e5SSebastian Reichel return ae; 14348c0984e5SSebastian Reichel } 14358c0984e5SSebastian Reichel 14368c0984e5SSebastian Reichel /* 14378c0984e5SSebastian Reichel * Return the battery temperature in tenths of degree Kelvin 14388c0984e5SSebastian Reichel * Or < 0 if something fails. 14398c0984e5SSebastian Reichel */ 14408c0984e5SSebastian Reichel static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di) 14418c0984e5SSebastian Reichel { 14428c0984e5SSebastian Reichel int temp; 14438c0984e5SSebastian Reichel 14448c0984e5SSebastian Reichel temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false); 14458c0984e5SSebastian Reichel if (temp < 0) { 14468c0984e5SSebastian Reichel dev_err(di->dev, "error reading temperature\n"); 14478c0984e5SSebastian Reichel return temp; 14488c0984e5SSebastian Reichel } 14498c0984e5SSebastian Reichel 14503a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 14518c0984e5SSebastian Reichel temp = 5 * temp / 2; 14528c0984e5SSebastian Reichel 14538c0984e5SSebastian Reichel return temp; 14548c0984e5SSebastian Reichel } 14558c0984e5SSebastian Reichel 14568c0984e5SSebastian Reichel /* 14578c0984e5SSebastian Reichel * Return the battery Cycle count total 14588c0984e5SSebastian Reichel * Or < 0 if something fails. 14598c0984e5SSebastian Reichel */ 14608c0984e5SSebastian Reichel static int bq27xxx_battery_read_cyct(struct bq27xxx_device_info *di) 14618c0984e5SSebastian Reichel { 14628c0984e5SSebastian Reichel int cyct; 14638c0984e5SSebastian Reichel 14648c0984e5SSebastian Reichel cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false); 14658c0984e5SSebastian Reichel if (cyct < 0) 14668c0984e5SSebastian Reichel dev_err(di->dev, "error reading cycle count total\n"); 14678c0984e5SSebastian Reichel 14688c0984e5SSebastian Reichel return cyct; 14698c0984e5SSebastian Reichel } 14708c0984e5SSebastian Reichel 14718c0984e5SSebastian Reichel /* 14728c0984e5SSebastian Reichel * Read a time register. 14738c0984e5SSebastian Reichel * Return < 0 if something fails. 14748c0984e5SSebastian Reichel */ 14758c0984e5SSebastian Reichel static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg) 14768c0984e5SSebastian Reichel { 14778c0984e5SSebastian Reichel int tval; 14788c0984e5SSebastian Reichel 14798c0984e5SSebastian Reichel tval = bq27xxx_read(di, reg, false); 14808c0984e5SSebastian Reichel if (tval < 0) { 14818c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading time register %02x: %d\n", 14828c0984e5SSebastian Reichel reg, tval); 14838c0984e5SSebastian Reichel return tval; 14848c0984e5SSebastian Reichel } 14858c0984e5SSebastian Reichel 14868c0984e5SSebastian Reichel if (tval == 65535) 14878c0984e5SSebastian Reichel return -ENODATA; 14888c0984e5SSebastian Reichel 14898c0984e5SSebastian Reichel return tval * 60; 14908c0984e5SSebastian Reichel } 14918c0984e5SSebastian Reichel 14928c0984e5SSebastian Reichel /* 14938c0984e5SSebastian Reichel * Read an average power register. 14948c0984e5SSebastian Reichel * Return < 0 if something fails. 14958c0984e5SSebastian Reichel */ 14968c0984e5SSebastian Reichel static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di) 14978c0984e5SSebastian Reichel { 14988c0984e5SSebastian Reichel int tval; 14998c0984e5SSebastian Reichel 15008c0984e5SSebastian Reichel tval = bq27xxx_read(di, BQ27XXX_REG_AP, false); 15018c0984e5SSebastian Reichel if (tval < 0) { 15028c0984e5SSebastian Reichel dev_err(di->dev, "error reading average power register %02x: %d\n", 15038c0984e5SSebastian Reichel BQ27XXX_REG_AP, tval); 15048c0984e5SSebastian Reichel return tval; 15058c0984e5SSebastian Reichel } 15068c0984e5SSebastian Reichel 15073a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 15088c0984e5SSebastian Reichel return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS; 15098c0984e5SSebastian Reichel else 15108c0984e5SSebastian Reichel return tval; 15118c0984e5SSebastian Reichel } 15128c0984e5SSebastian Reichel 15138c0984e5SSebastian Reichel /* 15148c0984e5SSebastian Reichel * Returns true if a battery over temperature condition is detected 15158c0984e5SSebastian Reichel */ 15168c0984e5SSebastian Reichel static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags) 15178c0984e5SSebastian Reichel { 15183a731c64SLiam Breck if (di->opts & BQ27XXX_O_OTDC) 15198c0984e5SSebastian Reichel return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD); 15203a731c64SLiam Breck if (di->opts & BQ27XXX_O_UTOT) 15218c0984e5SSebastian Reichel return flags & BQ27XXX_FLAG_OT; 15223a731c64SLiam Breck 15238c0984e5SSebastian Reichel return false; 15248c0984e5SSebastian Reichel } 15258c0984e5SSebastian Reichel 15268c0984e5SSebastian Reichel /* 15278c0984e5SSebastian Reichel * Returns true if a battery under temperature condition is detected 15288c0984e5SSebastian Reichel */ 15298c0984e5SSebastian Reichel static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags) 15308c0984e5SSebastian Reichel { 15313a731c64SLiam Breck if (di->opts & BQ27XXX_O_UTOT) 15328c0984e5SSebastian Reichel return flags & BQ27XXX_FLAG_UT; 15338c0984e5SSebastian Reichel 15348c0984e5SSebastian Reichel return false; 15358c0984e5SSebastian Reichel } 15368c0984e5SSebastian Reichel 15378c0984e5SSebastian Reichel /* 15388c0984e5SSebastian Reichel * Returns true if a low state of charge condition is detected 15398c0984e5SSebastian Reichel */ 15408c0984e5SSebastian Reichel static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags) 15418c0984e5SSebastian Reichel { 15423a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 15438c0984e5SSebastian Reichel return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF); 15448c0984e5SSebastian Reichel else 15458c0984e5SSebastian Reichel return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF); 15468c0984e5SSebastian Reichel } 15478c0984e5SSebastian Reichel 15488c0984e5SSebastian Reichel /* 15498c0984e5SSebastian Reichel * Read flag register. 15508c0984e5SSebastian Reichel * Return < 0 if something fails. 15518c0984e5SSebastian Reichel */ 15528c0984e5SSebastian Reichel static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) 15538c0984e5SSebastian Reichel { 15548c0984e5SSebastian Reichel int flags; 15553a731c64SLiam Breck bool has_singe_flag = di->opts & BQ27XXX_O_ZERO; 15568c0984e5SSebastian Reichel 1557e4a404a0SH. Nikolaus Schaller flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); 15588c0984e5SSebastian Reichel if (flags < 0) { 15598c0984e5SSebastian Reichel dev_err(di->dev, "error reading flag register:%d\n", flags); 15608c0984e5SSebastian Reichel return flags; 15618c0984e5SSebastian Reichel } 15628c0984e5SSebastian Reichel 15638c0984e5SSebastian Reichel /* Unlikely but important to return first */ 15648c0984e5SSebastian Reichel if (unlikely(bq27xxx_battery_overtemp(di, flags))) 15658c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_OVERHEAT; 15668c0984e5SSebastian Reichel if (unlikely(bq27xxx_battery_undertemp(di, flags))) 15678c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_COLD; 15688c0984e5SSebastian Reichel if (unlikely(bq27xxx_battery_dead(di, flags))) 15698c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_DEAD; 15708c0984e5SSebastian Reichel 15718c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_GOOD; 15728c0984e5SSebastian Reichel } 15738c0984e5SSebastian Reichel 15748c0984e5SSebastian Reichel void bq27xxx_battery_update(struct bq27xxx_device_info *di) 15758c0984e5SSebastian Reichel { 15768c0984e5SSebastian Reichel struct bq27xxx_reg_cache cache = {0, }; 15773a731c64SLiam Breck bool has_ci_flag = di->opts & BQ27XXX_O_ZERO; 15783a731c64SLiam Breck bool has_singe_flag = di->opts & BQ27XXX_O_ZERO; 15798c0984e5SSebastian Reichel 15808c0984e5SSebastian Reichel cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); 15818c0984e5SSebastian Reichel if ((cache.flags & 0xff) == 0xff) 15828c0984e5SSebastian Reichel cache.flags = -1; /* read error */ 15838c0984e5SSebastian Reichel if (cache.flags >= 0) { 15848c0984e5SSebastian Reichel cache.temperature = bq27xxx_battery_read_temperature(di); 15858c0984e5SSebastian Reichel if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) { 15868c0984e5SSebastian Reichel dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n"); 15878c0984e5SSebastian Reichel cache.capacity = -ENODATA; 15888c0984e5SSebastian Reichel cache.energy = -ENODATA; 15898c0984e5SSebastian Reichel cache.time_to_empty = -ENODATA; 15908c0984e5SSebastian Reichel cache.time_to_empty_avg = -ENODATA; 15918c0984e5SSebastian Reichel cache.time_to_full = -ENODATA; 15928c0984e5SSebastian Reichel cache.charge_full = -ENODATA; 15938c0984e5SSebastian Reichel cache.health = -ENODATA; 15948c0984e5SSebastian Reichel } else { 15958c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR) 15968c0984e5SSebastian Reichel cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE); 15978c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR) 15988c0984e5SSebastian Reichel cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP); 15998c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR) 16008c0984e5SSebastian Reichel cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF); 16018c0984e5SSebastian Reichel cache.charge_full = bq27xxx_battery_read_fcc(di); 16028c0984e5SSebastian Reichel cache.capacity = bq27xxx_battery_read_soc(di); 16038c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR) 16048c0984e5SSebastian Reichel cache.energy = bq27xxx_battery_read_energy(di); 16058c0984e5SSebastian Reichel cache.health = bq27xxx_battery_read_health(di); 16068c0984e5SSebastian Reichel } 16078c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR) 16088c0984e5SSebastian Reichel cache.cycle_count = bq27xxx_battery_read_cyct(di); 16098c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_AP] != INVALID_REG_ADDR) 16108c0984e5SSebastian Reichel cache.power_avg = bq27xxx_battery_read_pwr_avg(di); 16118c0984e5SSebastian Reichel 16128c0984e5SSebastian Reichel /* We only have to read charge design full once */ 16138c0984e5SSebastian Reichel if (di->charge_design_full <= 0) 16148c0984e5SSebastian Reichel di->charge_design_full = bq27xxx_battery_read_dcap(di); 16158c0984e5SSebastian Reichel } 16168c0984e5SSebastian Reichel 16178c0984e5SSebastian Reichel if (di->cache.capacity != cache.capacity) 16188c0984e5SSebastian Reichel power_supply_changed(di->bat); 16198c0984e5SSebastian Reichel 16208c0984e5SSebastian Reichel if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) 16218c0984e5SSebastian Reichel di->cache = cache; 16228c0984e5SSebastian Reichel 16238c0984e5SSebastian Reichel di->last_update = jiffies; 16248c0984e5SSebastian Reichel } 16258c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_update); 16268c0984e5SSebastian Reichel 16278c0984e5SSebastian Reichel static void bq27xxx_battery_poll(struct work_struct *work) 16288c0984e5SSebastian Reichel { 16298c0984e5SSebastian Reichel struct bq27xxx_device_info *di = 16308c0984e5SSebastian Reichel container_of(work, struct bq27xxx_device_info, 16318c0984e5SSebastian Reichel work.work); 16328c0984e5SSebastian Reichel 16338c0984e5SSebastian Reichel bq27xxx_battery_update(di); 16348c0984e5SSebastian Reichel 16358c0984e5SSebastian Reichel if (poll_interval > 0) 16368c0984e5SSebastian Reichel schedule_delayed_work(&di->work, poll_interval * HZ); 16378c0984e5SSebastian Reichel } 16388c0984e5SSebastian Reichel 16398c0984e5SSebastian Reichel /* 16408c0984e5SSebastian Reichel * Return the battery average current in µA 16418c0984e5SSebastian Reichel * Note that current can be negative signed as well 16428c0984e5SSebastian Reichel * Or 0 if something fails. 16438c0984e5SSebastian Reichel */ 16448c0984e5SSebastian Reichel static int bq27xxx_battery_current(struct bq27xxx_device_info *di, 16458c0984e5SSebastian Reichel union power_supply_propval *val) 16468c0984e5SSebastian Reichel { 16478c0984e5SSebastian Reichel int curr; 16488c0984e5SSebastian Reichel int flags; 16498c0984e5SSebastian Reichel 16508c0984e5SSebastian Reichel curr = bq27xxx_read(di, BQ27XXX_REG_AI, false); 16518c0984e5SSebastian Reichel if (curr < 0) { 16528c0984e5SSebastian Reichel dev_err(di->dev, "error reading current\n"); 16538c0984e5SSebastian Reichel return curr; 16548c0984e5SSebastian Reichel } 16558c0984e5SSebastian Reichel 16563a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) { 1657e4a404a0SH. Nikolaus Schaller flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); 16588c0984e5SSebastian Reichel if (flags & BQ27000_FLAG_CHGS) { 16598c0984e5SSebastian Reichel dev_dbg(di->dev, "negative current!\n"); 16608c0984e5SSebastian Reichel curr = -curr; 16618c0984e5SSebastian Reichel } 16628c0984e5SSebastian Reichel 16638c0984e5SSebastian Reichel val->intval = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 16648c0984e5SSebastian Reichel } else { 16658c0984e5SSebastian Reichel /* Other gauges return signed value */ 16668c0984e5SSebastian Reichel val->intval = (int)((s16)curr) * 1000; 16678c0984e5SSebastian Reichel } 16688c0984e5SSebastian Reichel 16698c0984e5SSebastian Reichel return 0; 16708c0984e5SSebastian Reichel } 16718c0984e5SSebastian Reichel 16728c0984e5SSebastian Reichel static int bq27xxx_battery_status(struct bq27xxx_device_info *di, 16738c0984e5SSebastian Reichel union power_supply_propval *val) 16748c0984e5SSebastian Reichel { 16758c0984e5SSebastian Reichel int status; 16768c0984e5SSebastian Reichel 16773a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) { 16788c0984e5SSebastian Reichel if (di->cache.flags & BQ27000_FLAG_FC) 16798c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_FULL; 16808c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_CHGS) 16818c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_CHARGING; 1682f72c14adSSebastian Reichel else if (power_supply_am_i_supplied(di->bat) > 0) 16838c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_NOT_CHARGING; 16848c0984e5SSebastian Reichel else 16858c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_DISCHARGING; 16868c0984e5SSebastian Reichel } else { 16878c0984e5SSebastian Reichel if (di->cache.flags & BQ27XXX_FLAG_FC) 16888c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_FULL; 16898c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_DSC) 16908c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_DISCHARGING; 16918c0984e5SSebastian Reichel else 16928c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_CHARGING; 16938c0984e5SSebastian Reichel } 16948c0984e5SSebastian Reichel 16958c0984e5SSebastian Reichel val->intval = status; 16968c0984e5SSebastian Reichel 16978c0984e5SSebastian Reichel return 0; 16988c0984e5SSebastian Reichel } 16998c0984e5SSebastian Reichel 17008c0984e5SSebastian Reichel static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di, 17018c0984e5SSebastian Reichel union power_supply_propval *val) 17028c0984e5SSebastian Reichel { 17038c0984e5SSebastian Reichel int level; 17048c0984e5SSebastian Reichel 17053a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) { 17068c0984e5SSebastian Reichel if (di->cache.flags & BQ27000_FLAG_FC) 17078c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 17088c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_EDV1) 17098c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 17108c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_EDVF) 17118c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 17128c0984e5SSebastian Reichel else 17138c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 17148c0984e5SSebastian Reichel } else { 17158c0984e5SSebastian Reichel if (di->cache.flags & BQ27XXX_FLAG_FC) 17168c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 17178c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_SOC1) 17188c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 17198c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_SOCF) 17208c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 17218c0984e5SSebastian Reichel else 17228c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 17238c0984e5SSebastian Reichel } 17248c0984e5SSebastian Reichel 17258c0984e5SSebastian Reichel val->intval = level; 17268c0984e5SSebastian Reichel 17278c0984e5SSebastian Reichel return 0; 17288c0984e5SSebastian Reichel } 17298c0984e5SSebastian Reichel 17308c0984e5SSebastian Reichel /* 17318c0984e5SSebastian Reichel * Return the battery Voltage in millivolts 17328c0984e5SSebastian Reichel * Or < 0 if something fails. 17338c0984e5SSebastian Reichel */ 17348c0984e5SSebastian Reichel static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di, 17358c0984e5SSebastian Reichel union power_supply_propval *val) 17368c0984e5SSebastian Reichel { 17378c0984e5SSebastian Reichel int volt; 17388c0984e5SSebastian Reichel 17398c0984e5SSebastian Reichel volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false); 17408c0984e5SSebastian Reichel if (volt < 0) { 17418c0984e5SSebastian Reichel dev_err(di->dev, "error reading voltage\n"); 17428c0984e5SSebastian Reichel return volt; 17438c0984e5SSebastian Reichel } 17448c0984e5SSebastian Reichel 17458c0984e5SSebastian Reichel val->intval = volt * 1000; 17468c0984e5SSebastian Reichel 17478c0984e5SSebastian Reichel return 0; 17488c0984e5SSebastian Reichel } 17498c0984e5SSebastian Reichel 17508c0984e5SSebastian Reichel static int bq27xxx_simple_value(int value, 17518c0984e5SSebastian Reichel union power_supply_propval *val) 17528c0984e5SSebastian Reichel { 17538c0984e5SSebastian Reichel if (value < 0) 17548c0984e5SSebastian Reichel return value; 17558c0984e5SSebastian Reichel 17568c0984e5SSebastian Reichel val->intval = value; 17578c0984e5SSebastian Reichel 17588c0984e5SSebastian Reichel return 0; 17598c0984e5SSebastian Reichel } 17608c0984e5SSebastian Reichel 17618c0984e5SSebastian Reichel static int bq27xxx_battery_get_property(struct power_supply *psy, 17628c0984e5SSebastian Reichel enum power_supply_property psp, 17638c0984e5SSebastian Reichel union power_supply_propval *val) 17648c0984e5SSebastian Reichel { 17658c0984e5SSebastian Reichel int ret = 0; 17668c0984e5SSebastian Reichel struct bq27xxx_device_info *di = power_supply_get_drvdata(psy); 17678c0984e5SSebastian Reichel 17688c0984e5SSebastian Reichel mutex_lock(&di->lock); 17698c0984e5SSebastian Reichel if (time_is_before_jiffies(di->last_update + 5 * HZ)) { 17708c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 17718c0984e5SSebastian Reichel bq27xxx_battery_poll(&di->work.work); 17728c0984e5SSebastian Reichel } 17738c0984e5SSebastian Reichel mutex_unlock(&di->lock); 17748c0984e5SSebastian Reichel 17758c0984e5SSebastian Reichel if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) 17768c0984e5SSebastian Reichel return -ENODEV; 17778c0984e5SSebastian Reichel 17788c0984e5SSebastian Reichel switch (psp) { 17798c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 17808c0984e5SSebastian Reichel ret = bq27xxx_battery_status(di, val); 17818c0984e5SSebastian Reichel break; 17828c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW: 17838c0984e5SSebastian Reichel ret = bq27xxx_battery_voltage(di, val); 17848c0984e5SSebastian Reichel break; 17858c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_PRESENT: 17868c0984e5SSebastian Reichel val->intval = di->cache.flags < 0 ? 0 : 1; 17878c0984e5SSebastian Reichel break; 17888c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CURRENT_NOW: 17898c0984e5SSebastian Reichel ret = bq27xxx_battery_current(di, val); 17908c0984e5SSebastian Reichel break; 17918c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY: 17928c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.capacity, val); 17938c0984e5SSebastian Reichel break; 17948c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 17958c0984e5SSebastian Reichel ret = bq27xxx_battery_capacity_level(di, val); 17968c0984e5SSebastian Reichel break; 17978c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TEMP: 17988c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.temperature, val); 17998c0984e5SSebastian Reichel if (ret == 0) 18008c0984e5SSebastian Reichel val->intval -= 2731; /* convert decidegree k to c */ 18018c0984e5SSebastian Reichel break; 18028c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: 18038c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_empty, val); 18048c0984e5SSebastian Reichel break; 18058c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: 18068c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_empty_avg, val); 18078c0984e5SSebastian Reichel break; 18088c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: 18098c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_full, val); 18108c0984e5SSebastian Reichel break; 18118c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TECHNOLOGY: 18128c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 18138c0984e5SSebastian Reichel break; 18148c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_NOW: 18158c0984e5SSebastian Reichel ret = bq27xxx_simple_value(bq27xxx_battery_read_nac(di), val); 18168c0984e5SSebastian Reichel break; 18178c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL: 18188c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.charge_full, val); 18198c0984e5SSebastian Reichel break; 18208c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 18218c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->charge_design_full, val); 18228c0984e5SSebastian Reichel break; 1823ccce4409SLiam Breck /* 1824ccce4409SLiam Breck * TODO: Implement these to make registers set from 1825ccce4409SLiam Breck * power_supply_battery_info visible in sysfs. 1826ccce4409SLiam Breck */ 1827ccce4409SLiam Breck case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 1828ccce4409SLiam Breck case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 1829ccce4409SLiam Breck return -EINVAL; 18308c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CYCLE_COUNT: 18318c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.cycle_count, val); 18328c0984e5SSebastian Reichel break; 18338c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_ENERGY_NOW: 18348c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.energy, val); 18358c0984e5SSebastian Reichel break; 18368c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_POWER_AVG: 18378c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.power_avg, val); 18388c0984e5SSebastian Reichel break; 18398c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH: 18408c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.health, val); 18418c0984e5SSebastian Reichel break; 18428c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_MANUFACTURER: 18438c0984e5SSebastian Reichel val->strval = BQ27XXX_MANUFACTURER; 18448c0984e5SSebastian Reichel break; 18458c0984e5SSebastian Reichel default: 18468c0984e5SSebastian Reichel return -EINVAL; 18478c0984e5SSebastian Reichel } 18488c0984e5SSebastian Reichel 18498c0984e5SSebastian Reichel return ret; 18508c0984e5SSebastian Reichel } 18518c0984e5SSebastian Reichel 18528c0984e5SSebastian Reichel static void bq27xxx_external_power_changed(struct power_supply *psy) 18538c0984e5SSebastian Reichel { 18548c0984e5SSebastian Reichel struct bq27xxx_device_info *di = power_supply_get_drvdata(psy); 18558c0984e5SSebastian Reichel 18568c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 18578c0984e5SSebastian Reichel schedule_delayed_work(&di->work, 0); 18588c0984e5SSebastian Reichel } 18598c0984e5SSebastian Reichel 18608c0984e5SSebastian Reichel int bq27xxx_battery_setup(struct bq27xxx_device_info *di) 18618c0984e5SSebastian Reichel { 18628c0984e5SSebastian Reichel struct power_supply_desc *psy_desc; 1863ccce4409SLiam Breck struct power_supply_config psy_cfg = { 1864ccce4409SLiam Breck .of_node = di->dev->of_node, 1865ccce4409SLiam Breck .drv_data = di, 1866ccce4409SLiam Breck }; 18678c0984e5SSebastian Reichel 18688c0984e5SSebastian Reichel INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll); 18698c0984e5SSebastian Reichel mutex_init(&di->lock); 18703a731c64SLiam Breck 18719aade6d8SLiam Breck di->regs = bq27xxx_chip_data[di->chip].regs; 187205045379SLiam Breck di->unseal_key = bq27xxx_chip_data[di->chip].unseal_key; 187305045379SLiam Breck di->dm_regs = bq27xxx_chip_data[di->chip].dm_regs; 18743a731c64SLiam Breck di->opts = bq27xxx_chip_data[di->chip].opts; 18758c0984e5SSebastian Reichel 18768c0984e5SSebastian Reichel psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL); 18778c0984e5SSebastian Reichel if (!psy_desc) 18788c0984e5SSebastian Reichel return -ENOMEM; 18798c0984e5SSebastian Reichel 18808c0984e5SSebastian Reichel psy_desc->name = di->name; 18818c0984e5SSebastian Reichel psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; 18829aade6d8SLiam Breck psy_desc->properties = bq27xxx_chip_data[di->chip].props; 18839aade6d8SLiam Breck psy_desc->num_properties = bq27xxx_chip_data[di->chip].props_size; 18848c0984e5SSebastian Reichel psy_desc->get_property = bq27xxx_battery_get_property; 18858c0984e5SSebastian Reichel psy_desc->external_power_changed = bq27xxx_external_power_changed; 18868c0984e5SSebastian Reichel 18878c0984e5SSebastian Reichel di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg); 18888c0984e5SSebastian Reichel if (IS_ERR(di->bat)) { 18898c0984e5SSebastian Reichel dev_err(di->dev, "failed to register battery\n"); 18908c0984e5SSebastian Reichel return PTR_ERR(di->bat); 18918c0984e5SSebastian Reichel } 18928c0984e5SSebastian Reichel 1893ccce4409SLiam Breck bq27xxx_battery_settings(di); 18948c0984e5SSebastian Reichel bq27xxx_battery_update(di); 18958c0984e5SSebastian Reichel 18961d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 18971d72706fSMatt Ranostay list_add(&di->list, &bq27xxx_battery_devices); 18981d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 18991d72706fSMatt Ranostay 19008c0984e5SSebastian Reichel return 0; 19018c0984e5SSebastian Reichel } 19028c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_setup); 19038c0984e5SSebastian Reichel 19048c0984e5SSebastian Reichel void bq27xxx_battery_teardown(struct bq27xxx_device_info *di) 19058c0984e5SSebastian Reichel { 19068c0984e5SSebastian Reichel /* 19078c0984e5SSebastian Reichel * power_supply_unregister call bq27xxx_battery_get_property which 19088c0984e5SSebastian Reichel * call bq27xxx_battery_poll. 19098c0984e5SSebastian Reichel * Make sure that bq27xxx_battery_poll will not call 19108c0984e5SSebastian Reichel * schedule_delayed_work again after unregister (which cause OOPS). 19118c0984e5SSebastian Reichel */ 19128c0984e5SSebastian Reichel poll_interval = 0; 19138c0984e5SSebastian Reichel 19148c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 19158c0984e5SSebastian Reichel 19168c0984e5SSebastian Reichel power_supply_unregister(di->bat); 19178c0984e5SSebastian Reichel 19181d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 19191d72706fSMatt Ranostay list_del(&di->list); 19201d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 19211d72706fSMatt Ranostay 19228c0984e5SSebastian Reichel mutex_destroy(&di->lock); 19238c0984e5SSebastian Reichel } 19248c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown); 19258c0984e5SSebastian Reichel 19268c0984e5SSebastian Reichel MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 19278c0984e5SSebastian Reichel MODULE_DESCRIPTION("BQ27xxx battery monitor driver"); 19288c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 1929