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 2968f2a813SChris Lapa * http://www.ti.com/product/bq27520-g1 30a5deb9a9SChris Lapa * http://www.ti.com/product/bq27520-g2 31825e915bSChris Lapa * http://www.ti.com/product/bq27520-g3 328835cae5SChris Lapa * http://www.ti.com/product/bq27520-g4 338c0984e5SSebastian Reichel * http://www.ti.com/product/bq27530-g1 348c0984e5SSebastian Reichel * http://www.ti.com/product/bq27531-g1 358c0984e5SSebastian Reichel * http://www.ti.com/product/bq27541-g1 368c0984e5SSebastian Reichel * http://www.ti.com/product/bq27542-g1 378c0984e5SSebastian Reichel * http://www.ti.com/product/bq27546-g1 388c0984e5SSebastian Reichel * http://www.ti.com/product/bq27742-g1 398c0984e5SSebastian Reichel * http://www.ti.com/product/bq27545-g1 408c0984e5SSebastian Reichel * http://www.ti.com/product/bq27421-g1 418c0984e5SSebastian Reichel * http://www.ti.com/product/bq27425-g1 423d779180SLiu Xiang * http://www.ti.com/product/bq27426 438c0984e5SSebastian Reichel * http://www.ti.com/product/bq27411-g1 443d779180SLiu Xiang * http://www.ti.com/product/bq27441-g1 458c0984e5SSebastian Reichel * http://www.ti.com/product/bq27621-g1 468c0984e5SSebastian Reichel */ 478c0984e5SSebastian Reichel 488c0984e5SSebastian Reichel #include <linux/device.h> 498c0984e5SSebastian Reichel #include <linux/module.h> 501d72706fSMatt Ranostay #include <linux/mutex.h> 518c0984e5SSebastian Reichel #include <linux/param.h> 528c0984e5SSebastian Reichel #include <linux/jiffies.h> 538c0984e5SSebastian Reichel #include <linux/workqueue.h> 548c0984e5SSebastian Reichel #include <linux/delay.h> 558c0984e5SSebastian Reichel #include <linux/platform_device.h> 568c0984e5SSebastian Reichel #include <linux/power_supply.h> 578c0984e5SSebastian Reichel #include <linux/slab.h> 588c0984e5SSebastian Reichel #include <linux/of.h> 598c0984e5SSebastian Reichel 608c0984e5SSebastian Reichel #include <linux/power/bq27xxx_battery.h> 618c0984e5SSebastian Reichel 628c0984e5SSebastian Reichel #define BQ27XXX_MANUFACTURER "Texas Instruments" 638c0984e5SSebastian Reichel 648c0984e5SSebastian Reichel /* BQ27XXX Flags */ 658c0984e5SSebastian Reichel #define BQ27XXX_FLAG_DSC BIT(0) 668c0984e5SSebastian Reichel #define BQ27XXX_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ 678c0984e5SSebastian Reichel #define BQ27XXX_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ 680670c9b3SLiam Breck #define BQ27XXX_FLAG_CFGUP BIT(4) 698c0984e5SSebastian Reichel #define BQ27XXX_FLAG_FC BIT(9) 708c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OTD BIT(14) 718c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OTC BIT(15) 728c0984e5SSebastian Reichel #define BQ27XXX_FLAG_UT BIT(14) 738c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OT BIT(15) 748c0984e5SSebastian Reichel 758c0984e5SSebastian Reichel /* BQ27000 has different layout for Flags register */ 768c0984e5SSebastian Reichel #define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */ 778c0984e5SSebastian Reichel #define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */ 788c0984e5SSebastian Reichel #define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */ 798c0984e5SSebastian Reichel #define BQ27000_FLAG_FC BIT(5) 808c0984e5SSebastian Reichel #define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ 818c0984e5SSebastian Reichel 820670c9b3SLiam Breck /* control register params */ 830670c9b3SLiam Breck #define BQ27XXX_SEALED 0x20 840670c9b3SLiam Breck #define BQ27XXX_SET_CFGUPDATE 0x13 850670c9b3SLiam Breck #define BQ27XXX_SOFT_RESET 0x42 860670c9b3SLiam Breck #define BQ27XXX_RESET 0x41 870670c9b3SLiam Breck 888c0984e5SSebastian Reichel #define BQ27XXX_RS (20) /* Resistor sense mOhm */ 898c0984e5SSebastian Reichel #define BQ27XXX_POWER_CONSTANT (29200) /* 29.2 µV^2 * 1000 */ 908c0984e5SSebastian Reichel #define BQ27XXX_CURRENT_CONSTANT (3570) /* 3.57 µV * 1000 */ 918c0984e5SSebastian Reichel 928c0984e5SSebastian Reichel #define INVALID_REG_ADDR 0xff 938c0984e5SSebastian Reichel 948c0984e5SSebastian Reichel /* 958c0984e5SSebastian Reichel * bq27xxx_reg_index - Register names 968c0984e5SSebastian Reichel * 978c0984e5SSebastian Reichel * These are indexes into a device's register mapping array. 988c0984e5SSebastian Reichel */ 998c0984e5SSebastian Reichel 1008c0984e5SSebastian Reichel enum bq27xxx_reg_index { 1018c0984e5SSebastian Reichel BQ27XXX_REG_CTRL = 0, /* Control */ 1028c0984e5SSebastian Reichel BQ27XXX_REG_TEMP, /* Temperature */ 1038c0984e5SSebastian Reichel BQ27XXX_REG_INT_TEMP, /* Internal Temperature */ 1048c0984e5SSebastian Reichel BQ27XXX_REG_VOLT, /* Voltage */ 1058c0984e5SSebastian Reichel BQ27XXX_REG_AI, /* Average Current */ 1068c0984e5SSebastian Reichel BQ27XXX_REG_FLAGS, /* Flags */ 1078c0984e5SSebastian Reichel BQ27XXX_REG_TTE, /* Time-to-Empty */ 1088c0984e5SSebastian Reichel BQ27XXX_REG_TTF, /* Time-to-Full */ 1098c0984e5SSebastian Reichel BQ27XXX_REG_TTES, /* Time-to-Empty Standby */ 1108c0984e5SSebastian Reichel BQ27XXX_REG_TTECP, /* Time-to-Empty at Constant Power */ 1118c0984e5SSebastian Reichel BQ27XXX_REG_NAC, /* Nominal Available Capacity */ 1128c0984e5SSebastian Reichel BQ27XXX_REG_FCC, /* Full Charge Capacity */ 1138c0984e5SSebastian Reichel BQ27XXX_REG_CYCT, /* Cycle Count */ 1148c0984e5SSebastian Reichel BQ27XXX_REG_AE, /* Available Energy */ 1158c0984e5SSebastian Reichel BQ27XXX_REG_SOC, /* State-of-Charge */ 1168c0984e5SSebastian Reichel BQ27XXX_REG_DCAP, /* Design Capacity */ 1178c0984e5SSebastian Reichel BQ27XXX_REG_AP, /* Average Power */ 1180670c9b3SLiam Breck BQ27XXX_DM_CTRL, /* Block Data Control */ 1190670c9b3SLiam Breck BQ27XXX_DM_CLASS, /* Data Class */ 1200670c9b3SLiam Breck BQ27XXX_DM_BLOCK, /* Data Block */ 1210670c9b3SLiam Breck BQ27XXX_DM_DATA, /* Block Data */ 1220670c9b3SLiam Breck BQ27XXX_DM_CKSUM, /* Block Data Checksum */ 1238c0984e5SSebastian Reichel BQ27XXX_REG_MAX, /* sentinel */ 1248c0984e5SSebastian Reichel }; 1258c0984e5SSebastian Reichel 1260670c9b3SLiam Breck #define BQ27XXX_DM_REG_ROWS \ 1270670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = 0x61, \ 1280670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = 0x3e, \ 1290670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = 0x3f, \ 1300670c9b3SLiam Breck [BQ27XXX_DM_DATA] = 0x40, \ 1310670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = 0x60 1320670c9b3SLiam Breck 1338c0984e5SSebastian Reichel /* Register mappings */ 1349aade6d8SLiam Breck static u8 1359aade6d8SLiam Breck bq27000_regs[BQ27XXX_REG_MAX] = { 1368c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1378c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1388c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 1398c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1408c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1418c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1428c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1438c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = 0x18, 1448c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1c, 1458c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = 0x26, 1468c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1478c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1488c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1498c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = 0x22, 1508c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x0b, 1518c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x76, 1528c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 1530670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, 1540670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, 1550670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, 1560670c9b3SLiam Breck [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, 1570670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, 1588c0984e5SSebastian Reichel }, 1599aade6d8SLiam Breck bq27010_regs[BQ27XXX_REG_MAX] = { 1608c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1618c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1628c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 1638c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1648c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1658c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1668c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1678c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = 0x18, 1688c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1c, 1698c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = 0x26, 1708c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1718c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1728c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1738c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 1748c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x0b, 1758c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x76, 1768c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 1770670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, 1780670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, 1790670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, 1800670c9b3SLiam Breck [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, 1810670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, 1828c0984e5SSebastian Reichel }, 1839aade6d8SLiam Breck bq2750x_regs[BQ27XXX_REG_MAX] = { 1848c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1858c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1868c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 1878c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1888c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1898c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1908c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1918c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 1928c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1a, 1938c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 1948c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1958c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1968c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1978c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 1988c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 1998c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 2008c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 2010670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 2028c0984e5SSebastian Reichel }, 2031059361fSLiam Breck #define bq2751x_regs bq27510g3_regs 2041059361fSLiam Breck #define bq2752x_regs bq27510g3_regs 2059aade6d8SLiam Breck bq27500_regs[BQ27XXX_REG_MAX] = { 20632833635SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 20732833635SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 20832833635SChris Lapa [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 20932833635SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 21032833635SChris Lapa [BQ27XXX_REG_AI] = 0x14, 21132833635SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 21232833635SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 21332833635SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 21432833635SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 21532833635SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 21632833635SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 21732833635SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 21832833635SChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 21932833635SChris Lapa [BQ27XXX_REG_AE] = 0x22, 22032833635SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 22132833635SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 22232833635SChris Lapa [BQ27XXX_REG_AP] = 0x24, 2230670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 22432833635SChris Lapa }, 2251059361fSLiam Breck #define bq27510g1_regs bq27500_regs 2261059361fSLiam Breck #define bq27510g2_regs bq27500_regs 2279aade6d8SLiam Breck bq27510g3_regs[BQ27XXX_REG_MAX] = { 22871375aa7SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 22971375aa7SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 23071375aa7SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x28, 23171375aa7SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 23271375aa7SChris Lapa [BQ27XXX_REG_AI] = 0x14, 23371375aa7SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 23471375aa7SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 23571375aa7SChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 23671375aa7SChris Lapa [BQ27XXX_REG_TTES] = 0x1a, 23771375aa7SChris Lapa [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 23871375aa7SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 23971375aa7SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 24071375aa7SChris Lapa [BQ27XXX_REG_CYCT] = 0x1e, 24171375aa7SChris Lapa [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 24271375aa7SChris Lapa [BQ27XXX_REG_SOC] = 0x20, 24371375aa7SChris Lapa [BQ27XXX_REG_DCAP] = 0x2e, 24471375aa7SChris Lapa [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 2450670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 24671375aa7SChris Lapa }, 2479aade6d8SLiam Breck bq27520g1_regs[BQ27XXX_REG_MAX] = { 24868f2a813SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 24968f2a813SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 25068f2a813SChris Lapa [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 25168f2a813SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 25268f2a813SChris Lapa [BQ27XXX_REG_AI] = 0x14, 25368f2a813SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 25468f2a813SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 25568f2a813SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 25668f2a813SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 25768f2a813SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 25868f2a813SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 25968f2a813SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 26068f2a813SChris Lapa [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, 26168f2a813SChris Lapa [BQ27XXX_REG_AE] = 0x22, 26268f2a813SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 26368f2a813SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 26468f2a813SChris Lapa [BQ27XXX_REG_AP] = 0x24, 2650670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 26668f2a813SChris Lapa }, 2679aade6d8SLiam Breck bq27520g2_regs[BQ27XXX_REG_MAX] = { 268a5deb9a9SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 269a5deb9a9SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 270a5deb9a9SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x36, 271a5deb9a9SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 272a5deb9a9SChris Lapa [BQ27XXX_REG_AI] = 0x14, 273a5deb9a9SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 274a5deb9a9SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 275a5deb9a9SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 276a5deb9a9SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 277a5deb9a9SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 278a5deb9a9SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 279a5deb9a9SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 280a5deb9a9SChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 281a5deb9a9SChris Lapa [BQ27XXX_REG_AE] = 0x22, 282a5deb9a9SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 283a5deb9a9SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 284a5deb9a9SChris Lapa [BQ27XXX_REG_AP] = 0x24, 2850670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 286a5deb9a9SChris Lapa }, 2879aade6d8SLiam Breck bq27520g3_regs[BQ27XXX_REG_MAX] = { 288825e915bSChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 289825e915bSChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 290825e915bSChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x36, 291825e915bSChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 292825e915bSChris Lapa [BQ27XXX_REG_AI] = 0x14, 293825e915bSChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 294825e915bSChris Lapa [BQ27XXX_REG_TTE] = 0x16, 295825e915bSChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 296825e915bSChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 297825e915bSChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 298825e915bSChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 299825e915bSChris Lapa [BQ27XXX_REG_FCC] = 0x12, 300825e915bSChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 301825e915bSChris Lapa [BQ27XXX_REG_AE] = 0x22, 302825e915bSChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 303825e915bSChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 304825e915bSChris Lapa [BQ27XXX_REG_AP] = 0x24, 3050670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 306825e915bSChris Lapa }, 3079aade6d8SLiam Breck bq27520g4_regs[BQ27XXX_REG_MAX] = { 3088835cae5SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 3098835cae5SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 3108835cae5SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x28, 3118835cae5SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 3128835cae5SChris Lapa [BQ27XXX_REG_AI] = 0x14, 3138835cae5SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 3148835cae5SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 3158835cae5SChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 3168835cae5SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 3178835cae5SChris Lapa [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 3188835cae5SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 3198835cae5SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 3208835cae5SChris Lapa [BQ27XXX_REG_CYCT] = 0x1e, 3218835cae5SChris Lapa [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 3228835cae5SChris Lapa [BQ27XXX_REG_SOC] = 0x20, 3238835cae5SChris Lapa [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 3248835cae5SChris Lapa [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 3250670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 3268835cae5SChris Lapa }, 32770a39e10SPavel Machek bq27521_regs[BQ27XXX_REG_MAX] = { 32870a39e10SPavel Machek [BQ27XXX_REG_CTRL] = 0x02, 32970a39e10SPavel Machek [BQ27XXX_REG_TEMP] = 0x0a, 33070a39e10SPavel Machek [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 33170a39e10SPavel Machek [BQ27XXX_REG_VOLT] = 0x0c, 33270a39e10SPavel Machek [BQ27XXX_REG_AI] = 0x0e, 33370a39e10SPavel Machek [BQ27XXX_REG_FLAGS] = 0x08, 33470a39e10SPavel Machek [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, 33570a39e10SPavel Machek [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 33670a39e10SPavel Machek [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 33770a39e10SPavel Machek [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 33870a39e10SPavel Machek [BQ27XXX_REG_NAC] = INVALID_REG_ADDR, 33970a39e10SPavel Machek [BQ27XXX_REG_FCC] = INVALID_REG_ADDR, 34070a39e10SPavel Machek [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, 34170a39e10SPavel Machek [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 34270a39e10SPavel Machek [BQ27XXX_REG_SOC] = INVALID_REG_ADDR, 34370a39e10SPavel Machek [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 34470a39e10SPavel Machek [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 34570a39e10SPavel Machek [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, 34670a39e10SPavel Machek [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, 34770a39e10SPavel Machek [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, 34870a39e10SPavel Machek [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, 34970a39e10SPavel Machek [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, 35070a39e10SPavel Machek }, 3519aade6d8SLiam Breck bq27530_regs[BQ27XXX_REG_MAX] = { 3528c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 3538c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 3548c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x32, 3558c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 3568c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 3578c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 3588c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 3598c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 3608c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 3618c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 3628c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 3638c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 3648c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 3658c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 3668c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 3678c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 3688c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 3690670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 3708c0984e5SSebastian Reichel }, 3713a731c64SLiam Breck #define bq27531_regs bq27530_regs 3729aade6d8SLiam Breck bq27541_regs[BQ27XXX_REG_MAX] = { 3738c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 3748c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 3758c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 3768c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 3778c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 3788c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 3798c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 3808c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 3818c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 3828c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 3838c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 3848c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 3858c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 3868c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 3878c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 3888c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 3898c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 3900670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 3918c0984e5SSebastian Reichel }, 3923a731c64SLiam Breck #define bq27542_regs bq27541_regs 3933a731c64SLiam Breck #define bq27546_regs bq27541_regs 3943a731c64SLiam Breck #define bq27742_regs bq27541_regs 3959aade6d8SLiam Breck bq27545_regs[BQ27XXX_REG_MAX] = { 3968c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 3978c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 3988c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 3998c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 4008c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 4018c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 4028c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 4038c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 4048c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 4058c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 4068c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 4078c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 4088c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 4098c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 4108c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 4118c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 4128c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 4130670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 4148c0984e5SSebastian Reichel }, 4159aade6d8SLiam Breck bq27421_regs[BQ27XXX_REG_MAX] = { 4168c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 4178c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x02, 4188c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x1e, 4198c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x04, 4208c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x10, 4218c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x06, 4228c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, 4238c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 4248c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 4258c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 4268c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x08, 4278c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x0e, 4288c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, 4298c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 4308c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x1c, 4318c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 4328c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x18, 4330670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 4348c0984e5SSebastian Reichel }; 435457b42f0SLiu Xiang #define bq27411_regs bq27421_regs 4363a731c64SLiam Breck #define bq27425_regs bq27421_regs 4375ef6a160SAndrew F. Davis #define bq27426_regs bq27421_regs 4383a731c64SLiam Breck #define bq27441_regs bq27421_regs 4393a731c64SLiam Breck #define bq27621_regs bq27421_regs 4408c0984e5SSebastian Reichel 4419aade6d8SLiam Breck static enum power_supply_property bq27000_props[] = { 4428c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4438c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 4448c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4458c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 4468c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4478c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 4488c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 4498c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 4508c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 4518c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 4528c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 4538c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 4548c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 4558c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 4568c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 4578c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ENERGY_NOW, 4588c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 4598c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 4608c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 4618c0984e5SSebastian Reichel }; 4628c0984e5SSebastian Reichel 4639aade6d8SLiam Breck static enum power_supply_property bq27010_props[] = { 4648c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4658c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 4668c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4678c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 4688c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4698c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 4708c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 4718c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 4728c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 4738c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 4748c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 4758c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 4768c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 4778c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 4788c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 4798c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 4808c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 4818c0984e5SSebastian Reichel }; 4828c0984e5SSebastian Reichel 4831059361fSLiam Breck #define bq2750x_props bq27510g3_props 4841059361fSLiam Breck #define bq2751x_props bq27510g3_props 4851059361fSLiam Breck #define bq2752x_props bq27510g3_props 4863bee9ea1SAndrew F. Davis 4879aade6d8SLiam Breck static enum power_supply_property bq27500_props[] = { 48832833635SChris Lapa POWER_SUPPLY_PROP_STATUS, 48932833635SChris Lapa POWER_SUPPLY_PROP_PRESENT, 49032833635SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 49132833635SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 49232833635SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 49332833635SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 49432833635SChris Lapa POWER_SUPPLY_PROP_TEMP, 49532833635SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 49632833635SChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 49732833635SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 49832833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 49932833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 50032833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 50132833635SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 50232833635SChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 50332833635SChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 50432833635SChris Lapa POWER_SUPPLY_PROP_HEALTH, 50532833635SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 50632833635SChris Lapa }; 5071059361fSLiam Breck #define bq27510g1_props bq27500_props 5081059361fSLiam Breck #define bq27510g2_props bq27500_props 509698a2bf5SChris Lapa 5109aade6d8SLiam Breck static enum power_supply_property bq27510g3_props[] = { 51171375aa7SChris Lapa POWER_SUPPLY_PROP_STATUS, 51271375aa7SChris Lapa POWER_SUPPLY_PROP_PRESENT, 51371375aa7SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 51471375aa7SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 51571375aa7SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 51671375aa7SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 51771375aa7SChris Lapa POWER_SUPPLY_PROP_TEMP, 51871375aa7SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 51971375aa7SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 52071375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 52171375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 52271375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 52371375aa7SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 52471375aa7SChris Lapa POWER_SUPPLY_PROP_HEALTH, 52571375aa7SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 52671375aa7SChris Lapa }; 52771375aa7SChris Lapa 5289aade6d8SLiam Breck static enum power_supply_property bq27520g1_props[] = { 52968f2a813SChris Lapa POWER_SUPPLY_PROP_STATUS, 53068f2a813SChris Lapa POWER_SUPPLY_PROP_PRESENT, 53168f2a813SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 53268f2a813SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 53368f2a813SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 53468f2a813SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 53568f2a813SChris Lapa POWER_SUPPLY_PROP_TEMP, 53668f2a813SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 53768f2a813SChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 53868f2a813SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 53968f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 54068f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 54168f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 54268f2a813SChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 54368f2a813SChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 54468f2a813SChris Lapa POWER_SUPPLY_PROP_HEALTH, 54568f2a813SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 54668f2a813SChris Lapa }; 54768f2a813SChris Lapa 5481059361fSLiam Breck #define bq27520g2_props bq27500_props 549a5deb9a9SChris Lapa 5509aade6d8SLiam Breck static enum power_supply_property bq27520g3_props[] = { 551825e915bSChris Lapa POWER_SUPPLY_PROP_STATUS, 552825e915bSChris Lapa POWER_SUPPLY_PROP_PRESENT, 553825e915bSChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 554825e915bSChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 555825e915bSChris Lapa POWER_SUPPLY_PROP_CAPACITY, 556825e915bSChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 557825e915bSChris Lapa POWER_SUPPLY_PROP_TEMP, 558825e915bSChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 559825e915bSChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 560825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 561825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 562825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 563825e915bSChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 564825e915bSChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 565825e915bSChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 566825e915bSChris Lapa POWER_SUPPLY_PROP_HEALTH, 567825e915bSChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 568825e915bSChris Lapa }; 569825e915bSChris Lapa 5709aade6d8SLiam Breck static enum power_supply_property bq27520g4_props[] = { 5718835cae5SChris Lapa POWER_SUPPLY_PROP_STATUS, 5728835cae5SChris Lapa POWER_SUPPLY_PROP_PRESENT, 5738835cae5SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 5748835cae5SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 5758835cae5SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 5768835cae5SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 5778835cae5SChris Lapa POWER_SUPPLY_PROP_TEMP, 5788835cae5SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 5798835cae5SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 5808835cae5SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 5818835cae5SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 5828835cae5SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 5838835cae5SChris Lapa POWER_SUPPLY_PROP_HEALTH, 5848835cae5SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 5858835cae5SChris Lapa }; 5868835cae5SChris Lapa 58770a39e10SPavel Machek static enum power_supply_property bq27521_props[] = { 58870a39e10SPavel Machek POWER_SUPPLY_PROP_STATUS, 58970a39e10SPavel Machek POWER_SUPPLY_PROP_PRESENT, 59070a39e10SPavel Machek POWER_SUPPLY_PROP_VOLTAGE_NOW, 59170a39e10SPavel Machek POWER_SUPPLY_PROP_CURRENT_NOW, 59270a39e10SPavel Machek POWER_SUPPLY_PROP_TEMP, 59370a39e10SPavel Machek POWER_SUPPLY_PROP_TECHNOLOGY, 59470a39e10SPavel Machek }; 59570a39e10SPavel Machek 5969aade6d8SLiam Breck static enum power_supply_property bq27530_props[] = { 5978c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 5988c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 5998c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 6008c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 6018c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 6028c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6038c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 6048c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 6058c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 6068c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 6078c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 6088c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 6098c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 6108c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 6118c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 6128c0984e5SSebastian Reichel }; 6133a731c64SLiam Breck #define bq27531_props bq27530_props 6148c0984e5SSebastian Reichel 6159aade6d8SLiam Breck static enum power_supply_property bq27541_props[] = { 6168c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 6178c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 6188c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 6198c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 6208c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 6218c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6228c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 6238c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 6248c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 6258c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 6268c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 6278c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 6288c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 6298c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 6308c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 6318c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 6328c0984e5SSebastian Reichel }; 6333a731c64SLiam Breck #define bq27542_props bq27541_props 6343a731c64SLiam Breck #define bq27546_props bq27541_props 6353a731c64SLiam Breck #define bq27742_props bq27541_props 6368c0984e5SSebastian Reichel 6379aade6d8SLiam Breck static enum power_supply_property bq27545_props[] = { 6388c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 6398c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 6408c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 6418c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 6428c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 6438c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6448c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 6458c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 6468c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 6478c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 6488c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 6498c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 6508c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 6518c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 6528c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 6538c0984e5SSebastian Reichel }; 6548c0984e5SSebastian Reichel 6559aade6d8SLiam Breck static enum power_supply_property bq27421_props[] = { 6568c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 6578c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 6588c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 6598c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 6608c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 6618c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6628c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 6638c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 6648c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 6658c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 6668c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 6678c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 6688c0984e5SSebastian Reichel }; 669457b42f0SLiu Xiang #define bq27411_props bq27421_props 6703a731c64SLiam Breck #define bq27425_props bq27421_props 6715ef6a160SAndrew F. Davis #define bq27426_props bq27421_props 6723a731c64SLiam Breck #define bq27441_props bq27421_props 6733a731c64SLiam Breck #define bq27621_props bq27421_props 6748c0984e5SSebastian Reichel 67505045379SLiam Breck struct bq27xxx_dm_reg { 67605045379SLiam Breck u8 subclass_id; 67705045379SLiam Breck u8 offset; 67805045379SLiam Breck u8 bytes; 67905045379SLiam Breck u16 min, max; 68005045379SLiam Breck }; 68105045379SLiam Breck 68205045379SLiam Breck enum bq27xxx_dm_reg_id { 68305045379SLiam Breck BQ27XXX_DM_DESIGN_CAPACITY = 0, 68405045379SLiam Breck BQ27XXX_DM_DESIGN_ENERGY, 68505045379SLiam Breck BQ27XXX_DM_TERMINATE_VOLTAGE, 68605045379SLiam Breck }; 68705045379SLiam Breck 68805045379SLiam Breck #define bq27000_dm_regs 0 68905045379SLiam Breck #define bq27010_dm_regs 0 69005045379SLiam Breck #define bq2750x_dm_regs 0 69105045379SLiam Breck #define bq2751x_dm_regs 0 69205045379SLiam Breck #define bq2752x_dm_regs 0 69305045379SLiam Breck 69405045379SLiam Breck #if 0 /* not yet tested */ 69505045379SLiam Breck static struct bq27xxx_dm_reg bq27500_dm_regs[] = { 69605045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 10, 2, 0, 65535 }, 69705045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { }, /* missing on chip */ 69805045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 48, 2, 1000, 32767 }, 69905045379SLiam Breck }; 70005045379SLiam Breck #else 70105045379SLiam Breck #define bq27500_dm_regs 0 70205045379SLiam Breck #endif 70305045379SLiam Breck 70405045379SLiam Breck /* todo create data memory definitions from datasheets and test on chips */ 70505045379SLiam Breck #define bq27510g1_dm_regs 0 70605045379SLiam Breck #define bq27510g2_dm_regs 0 70705045379SLiam Breck #define bq27510g3_dm_regs 0 70805045379SLiam Breck #define bq27520g1_dm_regs 0 70905045379SLiam Breck #define bq27520g2_dm_regs 0 71005045379SLiam Breck #define bq27520g3_dm_regs 0 71105045379SLiam Breck #define bq27520g4_dm_regs 0 71270a39e10SPavel Machek #define bq27521_dm_regs 0 71305045379SLiam Breck #define bq27530_dm_regs 0 71405045379SLiam Breck #define bq27531_dm_regs 0 71505045379SLiam Breck #define bq27541_dm_regs 0 71605045379SLiam Breck #define bq27542_dm_regs 0 71705045379SLiam Breck #define bq27546_dm_regs 0 71805045379SLiam Breck #define bq27742_dm_regs 0 71905045379SLiam Breck 72005045379SLiam Breck #if 0 /* not yet tested */ 72105045379SLiam Breck static struct bq27xxx_dm_reg bq27545_dm_regs[] = { 72205045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 23, 2, 0, 32767 }, 72305045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { 48, 25, 2, 0, 32767 }, 72405045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 67, 2, 2800, 3700 }, 72505045379SLiam Breck }; 72605045379SLiam Breck #else 72705045379SLiam Breck #define bq27545_dm_regs 0 72805045379SLiam Breck #endif 72905045379SLiam Breck 730457b42f0SLiu Xiang static struct bq27xxx_dm_reg bq27411_dm_regs[] = { 731457b42f0SLiu Xiang [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 10, 2, 0, 32767 }, 732457b42f0SLiu Xiang [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 12, 2, 0, 32767 }, 733457b42f0SLiu Xiang [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 16, 2, 2800, 3700 }, 734457b42f0SLiu Xiang }; 735457b42f0SLiu Xiang 73605045379SLiam Breck static struct bq27xxx_dm_reg bq27421_dm_regs[] = { 73705045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 10, 2, 0, 8000 }, 73805045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 12, 2, 0, 32767 }, 73905045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 16, 2, 2500, 3700 }, 74005045379SLiam Breck }; 74105045379SLiam Breck 74205045379SLiam Breck static struct bq27xxx_dm_reg bq27425_dm_regs[] = { 74305045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 12, 2, 0, 32767 }, 74405045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 14, 2, 0, 32767 }, 74505045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 18, 2, 2800, 3700 }, 74605045379SLiam Breck }; 74705045379SLiam Breck 7485ef6a160SAndrew F. Davis static struct bq27xxx_dm_reg bq27426_dm_regs[] = { 7495ef6a160SAndrew F. Davis [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 6, 2, 0, 8000 }, 7505ef6a160SAndrew F. Davis [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 8, 2, 0, 32767 }, 7515ef6a160SAndrew F. Davis [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 10, 2, 2500, 3700 }, 7525ef6a160SAndrew F. Davis }; 7535ef6a160SAndrew F. Davis 75405045379SLiam Breck #if 0 /* not yet tested */ 75505045379SLiam Breck #define bq27441_dm_regs bq27421_dm_regs 75605045379SLiam Breck #else 75705045379SLiam Breck #define bq27441_dm_regs 0 75805045379SLiam Breck #endif 75905045379SLiam Breck 76005045379SLiam Breck #if 0 /* not yet tested */ 76105045379SLiam Breck static struct bq27xxx_dm_reg bq27621_dm_regs[] = { 76205045379SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 3, 2, 0, 8000 }, 76305045379SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 5, 2, 0, 32767 }, 76405045379SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 9, 2, 2500, 3700 }, 76505045379SLiam Breck }; 76605045379SLiam Breck #else 76705045379SLiam Breck #define bq27621_dm_regs 0 76805045379SLiam Breck #endif 76905045379SLiam Breck 7703a731c64SLiam Breck #define BQ27XXX_O_ZERO 0x00000001 77170a39e10SPavel Machek #define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */ 77270a39e10SPavel Machek #define BQ27XXX_O_UTOT 0x00000004 /* has OT overtemperature flag */ 77305045379SLiam Breck #define BQ27XXX_O_CFGUP 0x00000008 77405045379SLiam Breck #define BQ27XXX_O_RAM 0x00000010 7753a731c64SLiam Breck 77605045379SLiam Breck #define BQ27XXX_DATA(ref, key, opt) { \ 7773a731c64SLiam Breck .opts = (opt), \ 77805045379SLiam Breck .unseal_key = key, \ 7799aade6d8SLiam Breck .regs = ref##_regs, \ 78005045379SLiam Breck .dm_regs = ref##_dm_regs, \ 7819aade6d8SLiam Breck .props = ref##_props, \ 7829aade6d8SLiam Breck .props_size = ARRAY_SIZE(ref##_props) } 7838c0984e5SSebastian Reichel 7848c0984e5SSebastian Reichel static struct { 7853a731c64SLiam Breck u32 opts; 78605045379SLiam Breck u32 unseal_key; 7879aade6d8SLiam Breck u8 *regs; 78805045379SLiam Breck struct bq27xxx_dm_reg *dm_regs; 7898c0984e5SSebastian Reichel enum power_supply_property *props; 7909aade6d8SLiam Breck size_t props_size; 7919aade6d8SLiam Breck } bq27xxx_chip_data[] = { 79205045379SLiam Breck [BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO), 79305045379SLiam Breck [BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO), 79405045379SLiam Breck [BQ2750X] = BQ27XXX_DATA(bq2750x, 0 , BQ27XXX_O_OTDC), 79505045379SLiam Breck [BQ2751X] = BQ27XXX_DATA(bq2751x, 0 , BQ27XXX_O_OTDC), 79605045379SLiam Breck [BQ2752X] = BQ27XXX_DATA(bq2752x, 0 , BQ27XXX_O_OTDC), 79705045379SLiam Breck [BQ27500] = BQ27XXX_DATA(bq27500, 0x04143672, BQ27XXX_O_OTDC), 79805045379SLiam Breck [BQ27510G1] = BQ27XXX_DATA(bq27510g1, 0 , BQ27XXX_O_OTDC), 79905045379SLiam Breck [BQ27510G2] = BQ27XXX_DATA(bq27510g2, 0 , BQ27XXX_O_OTDC), 80005045379SLiam Breck [BQ27510G3] = BQ27XXX_DATA(bq27510g3, 0 , BQ27XXX_O_OTDC), 80105045379SLiam Breck [BQ27520G1] = BQ27XXX_DATA(bq27520g1, 0 , BQ27XXX_O_OTDC), 80205045379SLiam Breck [BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC), 80305045379SLiam Breck [BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC), 80405045379SLiam Breck [BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0 , BQ27XXX_O_OTDC), 80570a39e10SPavel Machek [BQ27521] = BQ27XXX_DATA(bq27521, 0 , 0), 80605045379SLiam Breck [BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT), 80705045379SLiam Breck [BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT), 80805045379SLiam Breck [BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC), 80905045379SLiam Breck [BQ27542] = BQ27XXX_DATA(bq27542, 0 , BQ27XXX_O_OTDC), 81005045379SLiam Breck [BQ27546] = BQ27XXX_DATA(bq27546, 0 , BQ27XXX_O_OTDC), 81105045379SLiam Breck [BQ27742] = BQ27XXX_DATA(bq27742, 0 , BQ27XXX_O_OTDC), 81205045379SLiam Breck [BQ27545] = BQ27XXX_DATA(bq27545, 0x04143672, BQ27XXX_O_OTDC), 813457b42f0SLiu Xiang [BQ27411] = BQ27XXX_DATA(bq27411, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 81405045379SLiam Breck [BQ27421] = BQ27XXX_DATA(bq27421, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 81505045379SLiam Breck [BQ27425] = BQ27XXX_DATA(bq27425, 0x04143672, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP), 8165ef6a160SAndrew F. Davis [BQ27426] = BQ27XXX_DATA(bq27426, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 81705045379SLiam Breck [BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 81805045379SLiam Breck [BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), 8198c0984e5SSebastian Reichel }; 8208c0984e5SSebastian Reichel 8211d72706fSMatt Ranostay static DEFINE_MUTEX(bq27xxx_list_lock); 8221d72706fSMatt Ranostay static LIST_HEAD(bq27xxx_battery_devices); 8231d72706fSMatt Ranostay 8240670c9b3SLiam Breck #define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500) 8250670c9b3SLiam Breck 8260670c9b3SLiam Breck #define BQ27XXX_DM_SZ 32 8270670c9b3SLiam Breck 8280670c9b3SLiam Breck /** 8290670c9b3SLiam Breck * struct bq27xxx_dm_buf - chip data memory buffer 8300670c9b3SLiam Breck * @class: data memory subclass_id 8310670c9b3SLiam Breck * @block: data memory block number 8320670c9b3SLiam Breck * @data: data from/for the block 8330670c9b3SLiam Breck * @has_data: true if data has been filled by read 8340670c9b3SLiam Breck * @dirty: true if data has changed since last read/write 8350670c9b3SLiam Breck * 8360670c9b3SLiam Breck * Encapsulates info required to manage chip data memory blocks. 8370670c9b3SLiam Breck */ 8380670c9b3SLiam Breck struct bq27xxx_dm_buf { 8390670c9b3SLiam Breck u8 class; 8400670c9b3SLiam Breck u8 block; 8410670c9b3SLiam Breck u8 data[BQ27XXX_DM_SZ]; 8420670c9b3SLiam Breck bool has_data, dirty; 8430670c9b3SLiam Breck }; 8440670c9b3SLiam Breck 845ccce4409SLiam Breck #define BQ27XXX_DM_BUF(di, i) { \ 846ccce4409SLiam Breck .class = (di)->dm_regs[i].subclass_id, \ 847ccce4409SLiam Breck .block = (di)->dm_regs[i].offset / BQ27XXX_DM_SZ, \ 848ccce4409SLiam Breck } 849ccce4409SLiam Breck 850ccce4409SLiam Breck static inline u16 *bq27xxx_dm_reg_ptr(struct bq27xxx_dm_buf *buf, 851ccce4409SLiam Breck struct bq27xxx_dm_reg *reg) 852ccce4409SLiam Breck { 853ccce4409SLiam Breck if (buf->class == reg->subclass_id && 854ccce4409SLiam Breck buf->block == reg->offset / BQ27XXX_DM_SZ) 855ccce4409SLiam Breck return (u16 *) (buf->data + reg->offset % BQ27XXX_DM_SZ); 856ccce4409SLiam Breck 857ccce4409SLiam Breck return NULL; 858ccce4409SLiam Breck } 859ccce4409SLiam Breck 860ccce4409SLiam Breck static const char * const bq27xxx_dm_reg_name[] = { 861ccce4409SLiam Breck [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity", 862ccce4409SLiam Breck [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy", 863ccce4409SLiam Breck [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage", 864ccce4409SLiam Breck }; 865ccce4409SLiam Breck 866ccce4409SLiam Breck 867ccce4409SLiam Breck static bool bq27xxx_dt_to_nvm = true; 868ccce4409SLiam Breck module_param_named(dt_monitored_battery_updates_nvm, bq27xxx_dt_to_nvm, bool, 0444); 869ccce4409SLiam Breck MODULE_PARM_DESC(dt_monitored_battery_updates_nvm, 870ccce4409SLiam Breck "Devicetree monitored-battery config updates data memory on NVM/flash chips.\n" 871ccce4409SLiam Breck "Users must set this =0 when installing a different type of battery!\n" 872ccce4409SLiam Breck "Default is =1." 873ccce4409SLiam Breck #ifndef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM 874ccce4409SLiam Breck "\nSetting this affects future kernel updates, not the current configuration." 875ccce4409SLiam Breck #endif 876ccce4409SLiam Breck ); 8770670c9b3SLiam Breck 8781d72706fSMatt Ranostay static int poll_interval_param_set(const char *val, const struct kernel_param *kp) 8791d72706fSMatt Ranostay { 8801d72706fSMatt Ranostay struct bq27xxx_device_info *di; 881950b6c2dSMatt Ranostay unsigned int prev_val = *(unsigned int *) kp->arg; 8821d72706fSMatt Ranostay int ret; 8831d72706fSMatt Ranostay 8841d72706fSMatt Ranostay ret = param_set_uint(val, kp); 885950b6c2dSMatt Ranostay if (ret < 0 || prev_val == *(unsigned int *) kp->arg) 8861d72706fSMatt Ranostay return ret; 8871d72706fSMatt Ranostay 8881d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 8891d72706fSMatt Ranostay list_for_each_entry(di, &bq27xxx_battery_devices, list) { 8901d72706fSMatt Ranostay cancel_delayed_work_sync(&di->work); 8911d72706fSMatt Ranostay schedule_delayed_work(&di->work, 0); 8921d72706fSMatt Ranostay } 8931d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 8941d72706fSMatt Ranostay 8951d72706fSMatt Ranostay return ret; 8961d72706fSMatt Ranostay } 8971d72706fSMatt Ranostay 8981d72706fSMatt Ranostay static const struct kernel_param_ops param_ops_poll_interval = { 8991d72706fSMatt Ranostay .get = param_get_uint, 9001d72706fSMatt Ranostay .set = poll_interval_param_set, 9011d72706fSMatt Ranostay }; 9021d72706fSMatt Ranostay 9038c0984e5SSebastian Reichel static unsigned int poll_interval = 360; 9041d72706fSMatt Ranostay module_param_cb(poll_interval, ¶m_ops_poll_interval, &poll_interval, 0644); 9058c0984e5SSebastian Reichel MODULE_PARM_DESC(poll_interval, 9068c0984e5SSebastian Reichel "battery poll interval in seconds - 0 disables polling"); 9078c0984e5SSebastian Reichel 9088c0984e5SSebastian Reichel /* 9098c0984e5SSebastian Reichel * Common code for BQ27xxx devices 9108c0984e5SSebastian Reichel */ 9118c0984e5SSebastian Reichel 9128c0984e5SSebastian Reichel static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, 9138c0984e5SSebastian Reichel bool single) 9148c0984e5SSebastian Reichel { 91514073f66SMatt Ranostay int ret; 91614073f66SMatt Ranostay 9178c0984e5SSebastian Reichel if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 9188c0984e5SSebastian Reichel return -EINVAL; 9198c0984e5SSebastian Reichel 92014073f66SMatt Ranostay ret = di->bus.read(di, di->regs[reg_index], single); 92114073f66SMatt Ranostay if (ret < 0) 92214073f66SMatt Ranostay dev_dbg(di->dev, "failed to read register 0x%02x (index %d)\n", 92314073f66SMatt Ranostay di->regs[reg_index], reg_index); 92414073f66SMatt Ranostay 92514073f66SMatt Ranostay return ret; 92614073f66SMatt Ranostay } 92714073f66SMatt Ranostay 92814073f66SMatt Ranostay static inline int bq27xxx_write(struct bq27xxx_device_info *di, int reg_index, 92914073f66SMatt Ranostay u16 value, bool single) 93014073f66SMatt Ranostay { 93114073f66SMatt Ranostay int ret; 93214073f66SMatt Ranostay 93314073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 93414073f66SMatt Ranostay return -EINVAL; 93514073f66SMatt Ranostay 93614073f66SMatt Ranostay if (!di->bus.write) 93714073f66SMatt Ranostay return -EPERM; 93814073f66SMatt Ranostay 93914073f66SMatt Ranostay ret = di->bus.write(di, di->regs[reg_index], value, single); 94014073f66SMatt Ranostay if (ret < 0) 94114073f66SMatt Ranostay dev_dbg(di->dev, "failed to write register 0x%02x (index %d)\n", 94214073f66SMatt Ranostay di->regs[reg_index], reg_index); 94314073f66SMatt Ranostay 94414073f66SMatt Ranostay return ret; 94514073f66SMatt Ranostay } 94614073f66SMatt Ranostay 94714073f66SMatt Ranostay static inline int bq27xxx_read_block(struct bq27xxx_device_info *di, int reg_index, 94814073f66SMatt Ranostay u8 *data, int len) 94914073f66SMatt Ranostay { 95014073f66SMatt Ranostay int ret; 95114073f66SMatt Ranostay 95214073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 95314073f66SMatt Ranostay return -EINVAL; 95414073f66SMatt Ranostay 95514073f66SMatt Ranostay if (!di->bus.read_bulk) 95614073f66SMatt Ranostay return -EPERM; 95714073f66SMatt Ranostay 95814073f66SMatt Ranostay ret = di->bus.read_bulk(di, di->regs[reg_index], data, len); 95914073f66SMatt Ranostay if (ret < 0) 96014073f66SMatt Ranostay dev_dbg(di->dev, "failed to read_bulk register 0x%02x (index %d)\n", 96114073f66SMatt Ranostay di->regs[reg_index], reg_index); 96214073f66SMatt Ranostay 96314073f66SMatt Ranostay return ret; 96414073f66SMatt Ranostay } 96514073f66SMatt Ranostay 96614073f66SMatt Ranostay static inline int bq27xxx_write_block(struct bq27xxx_device_info *di, int reg_index, 96714073f66SMatt Ranostay u8 *data, int len) 96814073f66SMatt Ranostay { 96914073f66SMatt Ranostay int ret; 97014073f66SMatt Ranostay 97114073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 97214073f66SMatt Ranostay return -EINVAL; 97314073f66SMatt Ranostay 97414073f66SMatt Ranostay if (!di->bus.write_bulk) 97514073f66SMatt Ranostay return -EPERM; 97614073f66SMatt Ranostay 97714073f66SMatt Ranostay ret = di->bus.write_bulk(di, di->regs[reg_index], data, len); 97814073f66SMatt Ranostay if (ret < 0) 97914073f66SMatt Ranostay dev_dbg(di->dev, "failed to write_bulk register 0x%02x (index %d)\n", 98014073f66SMatt Ranostay di->regs[reg_index], reg_index); 98114073f66SMatt Ranostay 98214073f66SMatt Ranostay return ret; 9838c0984e5SSebastian Reichel } 9848c0984e5SSebastian Reichel 9850670c9b3SLiam Breck static int bq27xxx_battery_seal(struct bq27xxx_device_info *di) 9860670c9b3SLiam Breck { 9870670c9b3SLiam Breck int ret; 9880670c9b3SLiam Breck 9890670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_SEALED, false); 9900670c9b3SLiam Breck if (ret < 0) { 9910670c9b3SLiam Breck dev_err(di->dev, "bus error on seal: %d\n", ret); 9920670c9b3SLiam Breck return ret; 9930670c9b3SLiam Breck } 9940670c9b3SLiam Breck 9950670c9b3SLiam Breck return 0; 9960670c9b3SLiam Breck } 9970670c9b3SLiam Breck 9980670c9b3SLiam Breck static int bq27xxx_battery_unseal(struct bq27xxx_device_info *di) 9990670c9b3SLiam Breck { 10000670c9b3SLiam Breck int ret; 10010670c9b3SLiam Breck 10020670c9b3SLiam Breck if (di->unseal_key == 0) { 10030670c9b3SLiam Breck dev_err(di->dev, "unseal failed due to missing key\n"); 10040670c9b3SLiam Breck return -EINVAL; 10050670c9b3SLiam Breck } 10060670c9b3SLiam Breck 10070670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, (u16)(di->unseal_key >> 16), false); 10080670c9b3SLiam Breck if (ret < 0) 10090670c9b3SLiam Breck goto out; 10100670c9b3SLiam Breck 10110670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, (u16)di->unseal_key, false); 10120670c9b3SLiam Breck if (ret < 0) 10130670c9b3SLiam Breck goto out; 10140670c9b3SLiam Breck 10150670c9b3SLiam Breck return 0; 10160670c9b3SLiam Breck 10170670c9b3SLiam Breck out: 10180670c9b3SLiam Breck dev_err(di->dev, "bus error on unseal: %d\n", ret); 10190670c9b3SLiam Breck return ret; 10200670c9b3SLiam Breck } 10210670c9b3SLiam Breck 10220670c9b3SLiam Breck static u8 bq27xxx_battery_checksum_dm_block(struct bq27xxx_dm_buf *buf) 10230670c9b3SLiam Breck { 10240670c9b3SLiam Breck u16 sum = 0; 10250670c9b3SLiam Breck int i; 10260670c9b3SLiam Breck 10270670c9b3SLiam Breck for (i = 0; i < BQ27XXX_DM_SZ; i++) 10280670c9b3SLiam Breck sum += buf->data[i]; 10290670c9b3SLiam Breck sum &= 0xff; 10300670c9b3SLiam Breck 10310670c9b3SLiam Breck return 0xff - sum; 10320670c9b3SLiam Breck } 10330670c9b3SLiam Breck 10340670c9b3SLiam Breck static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di, 10350670c9b3SLiam Breck struct bq27xxx_dm_buf *buf) 10360670c9b3SLiam Breck { 10370670c9b3SLiam Breck int ret; 10380670c9b3SLiam Breck 10390670c9b3SLiam Breck buf->has_data = false; 10400670c9b3SLiam Breck 10410670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CLASS, buf->class, true); 10420670c9b3SLiam Breck if (ret < 0) 10430670c9b3SLiam Breck goto out; 10440670c9b3SLiam Breck 10450670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_BLOCK, buf->block, true); 10460670c9b3SLiam Breck if (ret < 0) 10470670c9b3SLiam Breck goto out; 10480670c9b3SLiam Breck 10490670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 10500670c9b3SLiam Breck 10510670c9b3SLiam Breck ret = bq27xxx_read_block(di, BQ27XXX_DM_DATA, buf->data, BQ27XXX_DM_SZ); 10520670c9b3SLiam Breck if (ret < 0) 10530670c9b3SLiam Breck goto out; 10540670c9b3SLiam Breck 10550670c9b3SLiam Breck ret = bq27xxx_read(di, BQ27XXX_DM_CKSUM, true); 10560670c9b3SLiam Breck if (ret < 0) 10570670c9b3SLiam Breck goto out; 10580670c9b3SLiam Breck 10590670c9b3SLiam Breck if ((u8)ret != bq27xxx_battery_checksum_dm_block(buf)) { 10600670c9b3SLiam Breck ret = -EINVAL; 10610670c9b3SLiam Breck goto out; 10620670c9b3SLiam Breck } 10630670c9b3SLiam Breck 10640670c9b3SLiam Breck buf->has_data = true; 10650670c9b3SLiam Breck buf->dirty = false; 10660670c9b3SLiam Breck 10670670c9b3SLiam Breck return 0; 10680670c9b3SLiam Breck 10690670c9b3SLiam Breck out: 10700670c9b3SLiam Breck dev_err(di->dev, "bus error reading chip memory: %d\n", ret); 10710670c9b3SLiam Breck return ret; 10720670c9b3SLiam Breck } 10730670c9b3SLiam Breck 1074ccce4409SLiam Breck static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di, 1075ccce4409SLiam Breck struct bq27xxx_dm_buf *buf, 1076ccce4409SLiam Breck enum bq27xxx_dm_reg_id reg_id, 1077ccce4409SLiam Breck unsigned int val) 1078ccce4409SLiam Breck { 1079ccce4409SLiam Breck struct bq27xxx_dm_reg *reg = &di->dm_regs[reg_id]; 1080ccce4409SLiam Breck const char *str = bq27xxx_dm_reg_name[reg_id]; 1081ccce4409SLiam Breck u16 *prev = bq27xxx_dm_reg_ptr(buf, reg); 1082ccce4409SLiam Breck 1083ccce4409SLiam Breck if (prev == NULL) { 1084ccce4409SLiam Breck dev_warn(di->dev, "buffer does not match %s dm spec\n", str); 1085ccce4409SLiam Breck return; 1086ccce4409SLiam Breck } 1087ccce4409SLiam Breck 1088ccce4409SLiam Breck if (reg->bytes != 2) { 1089ccce4409SLiam Breck dev_warn(di->dev, "%s dm spec has unsupported byte size\n", str); 1090ccce4409SLiam Breck return; 1091ccce4409SLiam Breck } 1092ccce4409SLiam Breck 1093ccce4409SLiam Breck if (!buf->has_data) 1094ccce4409SLiam Breck return; 1095ccce4409SLiam Breck 1096ccce4409SLiam Breck if (be16_to_cpup(prev) == val) { 1097ccce4409SLiam Breck dev_info(di->dev, "%s has %u\n", str, val); 1098ccce4409SLiam Breck return; 1099ccce4409SLiam Breck } 1100ccce4409SLiam Breck 1101ccce4409SLiam Breck #ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM 110205045379SLiam Breck if (!(di->opts & BQ27XXX_O_RAM) && !bq27xxx_dt_to_nvm) { 1103ccce4409SLiam Breck #else 110405045379SLiam Breck if (!(di->opts & BQ27XXX_O_RAM)) { 1105ccce4409SLiam Breck #endif 1106ccce4409SLiam Breck /* devicetree and NVM differ; defer to NVM */ 1107ccce4409SLiam Breck dev_warn(di->dev, "%s has %u; update to %u disallowed " 1108ccce4409SLiam Breck #ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM 1109ccce4409SLiam Breck "by dt_monitored_battery_updates_nvm=0" 1110ccce4409SLiam Breck #else 1111ccce4409SLiam Breck "for flash/NVM data memory" 1112ccce4409SLiam Breck #endif 1113ccce4409SLiam Breck "\n", str, be16_to_cpup(prev), val); 1114ccce4409SLiam Breck return; 1115ccce4409SLiam Breck } 1116ccce4409SLiam Breck 1117ccce4409SLiam Breck dev_info(di->dev, "update %s to %u\n", str, val); 1118ccce4409SLiam Breck 1119ccce4409SLiam Breck *prev = cpu_to_be16(val); 1120ccce4409SLiam Breck buf->dirty = true; 1121ccce4409SLiam Breck } 1122ccce4409SLiam Breck 11230670c9b3SLiam Breck static int bq27xxx_battery_cfgupdate_priv(struct bq27xxx_device_info *di, bool active) 11240670c9b3SLiam Breck { 11250670c9b3SLiam Breck const int limit = 100; 11260670c9b3SLiam Breck u16 cmd = active ? BQ27XXX_SET_CFGUPDATE : BQ27XXX_SOFT_RESET; 11270670c9b3SLiam Breck int ret, try = limit; 11280670c9b3SLiam Breck 11290670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, cmd, false); 11300670c9b3SLiam Breck if (ret < 0) 11310670c9b3SLiam Breck return ret; 11320670c9b3SLiam Breck 11330670c9b3SLiam Breck do { 11340670c9b3SLiam Breck BQ27XXX_MSLEEP(25); 11350670c9b3SLiam Breck ret = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false); 11360670c9b3SLiam Breck if (ret < 0) 11370670c9b3SLiam Breck return ret; 11380670c9b3SLiam Breck } while (!!(ret & BQ27XXX_FLAG_CFGUP) != active && --try); 11390670c9b3SLiam Breck 114005045379SLiam Breck if (!try && di->chip != BQ27425) { // 425 has a bug 11410670c9b3SLiam Breck dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", active); 11420670c9b3SLiam Breck return -EINVAL; 11430670c9b3SLiam Breck } 11440670c9b3SLiam Breck 11450670c9b3SLiam Breck if (limit - try > 3) 11460670c9b3SLiam Breck dev_warn(di->dev, "cfgupdate %d, retries %d\n", active, limit - try); 11470670c9b3SLiam Breck 11480670c9b3SLiam Breck return 0; 11490670c9b3SLiam Breck } 11500670c9b3SLiam Breck 11510670c9b3SLiam Breck static inline int bq27xxx_battery_set_cfgupdate(struct bq27xxx_device_info *di) 11520670c9b3SLiam Breck { 11530670c9b3SLiam Breck int ret = bq27xxx_battery_cfgupdate_priv(di, true); 11540670c9b3SLiam Breck if (ret < 0 && ret != -EINVAL) 11550670c9b3SLiam Breck dev_err(di->dev, "bus error on set_cfgupdate: %d\n", ret); 11560670c9b3SLiam Breck 11570670c9b3SLiam Breck return ret; 11580670c9b3SLiam Breck } 11590670c9b3SLiam Breck 11600670c9b3SLiam Breck static inline int bq27xxx_battery_soft_reset(struct bq27xxx_device_info *di) 11610670c9b3SLiam Breck { 11620670c9b3SLiam Breck int ret = bq27xxx_battery_cfgupdate_priv(di, false); 11630670c9b3SLiam Breck if (ret < 0 && ret != -EINVAL) 11640670c9b3SLiam Breck dev_err(di->dev, "bus error on soft_reset: %d\n", ret); 11650670c9b3SLiam Breck 11660670c9b3SLiam Breck return ret; 11670670c9b3SLiam Breck } 11680670c9b3SLiam Breck 11690670c9b3SLiam Breck static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di, 11700670c9b3SLiam Breck struct bq27xxx_dm_buf *buf) 11710670c9b3SLiam Breck { 117205045379SLiam Breck bool cfgup = di->opts & BQ27XXX_O_CFGUP; 11730670c9b3SLiam Breck int ret; 11740670c9b3SLiam Breck 11750670c9b3SLiam Breck if (!buf->dirty) 11760670c9b3SLiam Breck return 0; 11770670c9b3SLiam Breck 11780670c9b3SLiam Breck if (cfgup) { 11790670c9b3SLiam Breck ret = bq27xxx_battery_set_cfgupdate(di); 11800670c9b3SLiam Breck if (ret < 0) 11810670c9b3SLiam Breck return ret; 11820670c9b3SLiam Breck } 11830670c9b3SLiam Breck 11840670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CTRL, 0, true); 11850670c9b3SLiam Breck if (ret < 0) 11860670c9b3SLiam Breck goto out; 11870670c9b3SLiam Breck 11880670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CLASS, buf->class, true); 11890670c9b3SLiam Breck if (ret < 0) 11900670c9b3SLiam Breck goto out; 11910670c9b3SLiam Breck 11920670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_BLOCK, buf->block, true); 11930670c9b3SLiam Breck if (ret < 0) 11940670c9b3SLiam Breck goto out; 11950670c9b3SLiam Breck 11960670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 11970670c9b3SLiam Breck 11980670c9b3SLiam Breck ret = bq27xxx_write_block(di, BQ27XXX_DM_DATA, buf->data, BQ27XXX_DM_SZ); 11990670c9b3SLiam Breck if (ret < 0) 12000670c9b3SLiam Breck goto out; 12010670c9b3SLiam Breck 12020670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CKSUM, 12030670c9b3SLiam Breck bq27xxx_battery_checksum_dm_block(buf), true); 12040670c9b3SLiam Breck if (ret < 0) 12050670c9b3SLiam Breck goto out; 12060670c9b3SLiam Breck 12070670c9b3SLiam Breck /* DO NOT read BQ27XXX_DM_CKSUM here to verify it! That may cause NVM 12080670c9b3SLiam Breck * corruption on the '425 chip (and perhaps others), which can damage 12090670c9b3SLiam Breck * the chip. 12100670c9b3SLiam Breck */ 12110670c9b3SLiam Breck 12120670c9b3SLiam Breck if (cfgup) { 12130670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 12140670c9b3SLiam Breck ret = bq27xxx_battery_soft_reset(di); 12150670c9b3SLiam Breck if (ret < 0) 12160670c9b3SLiam Breck return ret; 12170670c9b3SLiam Breck } else { 12180670c9b3SLiam Breck BQ27XXX_MSLEEP(100); /* flash DM updates in <100ms */ 12190670c9b3SLiam Breck } 12200670c9b3SLiam Breck 12210670c9b3SLiam Breck buf->dirty = false; 12220670c9b3SLiam Breck 12230670c9b3SLiam Breck return 0; 12240670c9b3SLiam Breck 12250670c9b3SLiam Breck out: 12260670c9b3SLiam Breck if (cfgup) 12270670c9b3SLiam Breck bq27xxx_battery_soft_reset(di); 12280670c9b3SLiam Breck 12290670c9b3SLiam Breck dev_err(di->dev, "bus error writing chip memory: %d\n", ret); 12300670c9b3SLiam Breck return ret; 12310670c9b3SLiam Breck } 12320670c9b3SLiam Breck 1233ccce4409SLiam Breck static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di, 1234ccce4409SLiam Breck struct power_supply_battery_info *info) 1235ccce4409SLiam Breck { 1236ccce4409SLiam Breck struct bq27xxx_dm_buf bd = BQ27XXX_DM_BUF(di, BQ27XXX_DM_DESIGN_CAPACITY); 1237ccce4409SLiam Breck struct bq27xxx_dm_buf bt = BQ27XXX_DM_BUF(di, BQ27XXX_DM_TERMINATE_VOLTAGE); 1238ccce4409SLiam Breck bool updated; 1239ccce4409SLiam Breck 1240ccce4409SLiam Breck if (bq27xxx_battery_unseal(di) < 0) 1241ccce4409SLiam Breck return; 1242ccce4409SLiam Breck 1243ccce4409SLiam Breck if (info->charge_full_design_uah != -EINVAL && 1244ccce4409SLiam Breck info->energy_full_design_uwh != -EINVAL) { 1245ccce4409SLiam Breck bq27xxx_battery_read_dm_block(di, &bd); 1246ccce4409SLiam Breck /* assume design energy & capacity are in same block */ 1247ccce4409SLiam Breck bq27xxx_battery_update_dm_block(di, &bd, 1248ccce4409SLiam Breck BQ27XXX_DM_DESIGN_CAPACITY, 1249ccce4409SLiam Breck info->charge_full_design_uah / 1000); 1250ccce4409SLiam Breck bq27xxx_battery_update_dm_block(di, &bd, 1251ccce4409SLiam Breck BQ27XXX_DM_DESIGN_ENERGY, 1252ccce4409SLiam Breck info->energy_full_design_uwh / 1000); 1253ccce4409SLiam Breck } 1254ccce4409SLiam Breck 1255ccce4409SLiam Breck if (info->voltage_min_design_uv != -EINVAL) { 1256ccce4409SLiam Breck bool same = bd.class == bt.class && bd.block == bt.block; 1257ccce4409SLiam Breck if (!same) 1258ccce4409SLiam Breck bq27xxx_battery_read_dm_block(di, &bt); 1259ccce4409SLiam Breck bq27xxx_battery_update_dm_block(di, same ? &bd : &bt, 1260ccce4409SLiam Breck BQ27XXX_DM_TERMINATE_VOLTAGE, 1261ccce4409SLiam Breck info->voltage_min_design_uv / 1000); 1262ccce4409SLiam Breck } 1263ccce4409SLiam Breck 1264ccce4409SLiam Breck updated = bd.dirty || bt.dirty; 1265ccce4409SLiam Breck 1266ccce4409SLiam Breck bq27xxx_battery_write_dm_block(di, &bd); 1267ccce4409SLiam Breck bq27xxx_battery_write_dm_block(di, &bt); 1268ccce4409SLiam Breck 1269ccce4409SLiam Breck bq27xxx_battery_seal(di); 1270ccce4409SLiam Breck 127105045379SLiam Breck if (updated && !(di->opts & BQ27XXX_O_CFGUP)) { 1272ccce4409SLiam Breck bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_RESET, false); 1273ccce4409SLiam Breck BQ27XXX_MSLEEP(300); /* reset time is not documented */ 1274ccce4409SLiam Breck } 1275ccce4409SLiam Breck /* assume bq27xxx_battery_update() is called hereafter */ 1276ccce4409SLiam Breck } 1277ccce4409SLiam Breck 1278ccce4409SLiam Breck static void bq27xxx_battery_settings(struct bq27xxx_device_info *di) 1279ccce4409SLiam Breck { 1280ccce4409SLiam Breck struct power_supply_battery_info info = {}; 1281ccce4409SLiam Breck unsigned int min, max; 1282ccce4409SLiam Breck 1283ccce4409SLiam Breck if (power_supply_get_battery_info(di->bat, &info) < 0) 1284ccce4409SLiam Breck return; 1285ccce4409SLiam Breck 1286ccce4409SLiam Breck if (!di->dm_regs) { 1287ccce4409SLiam Breck dev_warn(di->dev, "data memory update not supported for chip\n"); 1288ccce4409SLiam Breck return; 1289ccce4409SLiam Breck } 1290ccce4409SLiam Breck 1291ccce4409SLiam Breck if (info.energy_full_design_uwh != info.charge_full_design_uah) { 1292ccce4409SLiam Breck if (info.energy_full_design_uwh == -EINVAL) 1293ccce4409SLiam Breck dev_warn(di->dev, "missing battery:energy-full-design-microwatt-hours\n"); 1294ccce4409SLiam Breck else if (info.charge_full_design_uah == -EINVAL) 1295ccce4409SLiam Breck dev_warn(di->dev, "missing battery:charge-full-design-microamp-hours\n"); 1296ccce4409SLiam Breck } 1297ccce4409SLiam Breck 1298ccce4409SLiam Breck /* assume min == 0 */ 1299ccce4409SLiam Breck max = di->dm_regs[BQ27XXX_DM_DESIGN_ENERGY].max; 1300ccce4409SLiam Breck if (info.energy_full_design_uwh > max * 1000) { 1301ccce4409SLiam Breck dev_err(di->dev, "invalid battery:energy-full-design-microwatt-hours %d\n", 1302ccce4409SLiam Breck info.energy_full_design_uwh); 1303ccce4409SLiam Breck info.energy_full_design_uwh = -EINVAL; 1304ccce4409SLiam Breck } 1305ccce4409SLiam Breck 1306ccce4409SLiam Breck /* assume min == 0 */ 1307ccce4409SLiam Breck max = di->dm_regs[BQ27XXX_DM_DESIGN_CAPACITY].max; 1308ccce4409SLiam Breck if (info.charge_full_design_uah > max * 1000) { 1309ccce4409SLiam Breck dev_err(di->dev, "invalid battery:charge-full-design-microamp-hours %d\n", 1310ccce4409SLiam Breck info.charge_full_design_uah); 1311ccce4409SLiam Breck info.charge_full_design_uah = -EINVAL; 1312ccce4409SLiam Breck } 1313ccce4409SLiam Breck 1314ccce4409SLiam Breck min = di->dm_regs[BQ27XXX_DM_TERMINATE_VOLTAGE].min; 1315ccce4409SLiam Breck max = di->dm_regs[BQ27XXX_DM_TERMINATE_VOLTAGE].max; 1316ccce4409SLiam Breck if ((info.voltage_min_design_uv < min * 1000 || 1317ccce4409SLiam Breck info.voltage_min_design_uv > max * 1000) && 1318ccce4409SLiam Breck info.voltage_min_design_uv != -EINVAL) { 1319ccce4409SLiam Breck dev_err(di->dev, "invalid battery:voltage-min-design-microvolt %d\n", 1320ccce4409SLiam Breck info.voltage_min_design_uv); 1321ccce4409SLiam Breck info.voltage_min_design_uv = -EINVAL; 1322ccce4409SLiam Breck } 1323ccce4409SLiam Breck 1324ccce4409SLiam Breck if ((info.energy_full_design_uwh != -EINVAL && 1325ccce4409SLiam Breck info.charge_full_design_uah != -EINVAL) || 1326ccce4409SLiam Breck info.voltage_min_design_uv != -EINVAL) 1327ccce4409SLiam Breck bq27xxx_battery_set_config(di, &info); 1328ccce4409SLiam Breck } 1329ccce4409SLiam Breck 13308c0984e5SSebastian Reichel /* 13318c0984e5SSebastian Reichel * Return the battery State-of-Charge 13328c0984e5SSebastian Reichel * Or < 0 if something fails. 13338c0984e5SSebastian Reichel */ 13348c0984e5SSebastian Reichel static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di) 13358c0984e5SSebastian Reichel { 13368c0984e5SSebastian Reichel int soc; 13378c0984e5SSebastian Reichel 13383a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 13398c0984e5SSebastian Reichel soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true); 13408c0984e5SSebastian Reichel else 13418c0984e5SSebastian Reichel soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false); 13428c0984e5SSebastian Reichel 13438c0984e5SSebastian Reichel if (soc < 0) 13448c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading State-of-Charge\n"); 13458c0984e5SSebastian Reichel 13468c0984e5SSebastian Reichel return soc; 13478c0984e5SSebastian Reichel } 13488c0984e5SSebastian Reichel 13498c0984e5SSebastian Reichel /* 13508c0984e5SSebastian Reichel * Return a battery charge value in µAh 13518c0984e5SSebastian Reichel * Or < 0 if something fails. 13528c0984e5SSebastian Reichel */ 13538c0984e5SSebastian Reichel static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg) 13548c0984e5SSebastian Reichel { 13558c0984e5SSebastian Reichel int charge; 13568c0984e5SSebastian Reichel 13578c0984e5SSebastian Reichel charge = bq27xxx_read(di, reg, false); 13588c0984e5SSebastian Reichel if (charge < 0) { 13598c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading charge register %02x: %d\n", 13608c0984e5SSebastian Reichel reg, charge); 13618c0984e5SSebastian Reichel return charge; 13628c0984e5SSebastian Reichel } 13638c0984e5SSebastian Reichel 13643a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 13658c0984e5SSebastian Reichel charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 13668c0984e5SSebastian Reichel else 13678c0984e5SSebastian Reichel charge *= 1000; 13688c0984e5SSebastian Reichel 13698c0984e5SSebastian Reichel return charge; 13708c0984e5SSebastian Reichel } 13718c0984e5SSebastian Reichel 13728c0984e5SSebastian Reichel /* 13738c0984e5SSebastian Reichel * Return the battery Nominal available capacity in µAh 13748c0984e5SSebastian Reichel * Or < 0 if something fails. 13758c0984e5SSebastian Reichel */ 13768c0984e5SSebastian Reichel static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di) 13778c0984e5SSebastian Reichel { 13788c0984e5SSebastian Reichel int flags; 13798c0984e5SSebastian Reichel 13803a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) { 13818c0984e5SSebastian Reichel flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); 13828c0984e5SSebastian Reichel if (flags >= 0 && (flags & BQ27000_FLAG_CI)) 13838c0984e5SSebastian Reichel return -ENODATA; 13848c0984e5SSebastian Reichel } 13858c0984e5SSebastian Reichel 13868c0984e5SSebastian Reichel return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC); 13878c0984e5SSebastian Reichel } 13888c0984e5SSebastian Reichel 13898c0984e5SSebastian Reichel /* 13908c0984e5SSebastian Reichel * Return the battery Full Charge Capacity in µAh 13918c0984e5SSebastian Reichel * Or < 0 if something fails. 13928c0984e5SSebastian Reichel */ 13938c0984e5SSebastian Reichel static inline int bq27xxx_battery_read_fcc(struct bq27xxx_device_info *di) 13948c0984e5SSebastian Reichel { 13958c0984e5SSebastian Reichel return bq27xxx_battery_read_charge(di, BQ27XXX_REG_FCC); 13968c0984e5SSebastian Reichel } 13978c0984e5SSebastian Reichel 13988c0984e5SSebastian Reichel /* 13998c0984e5SSebastian Reichel * Return the Design Capacity in µAh 14008c0984e5SSebastian Reichel * Or < 0 if something fails. 14018c0984e5SSebastian Reichel */ 14028c0984e5SSebastian Reichel static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di) 14038c0984e5SSebastian Reichel { 14048c0984e5SSebastian Reichel int dcap; 14058c0984e5SSebastian Reichel 14063a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 14078c0984e5SSebastian Reichel dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true); 14088c0984e5SSebastian Reichel else 14098c0984e5SSebastian Reichel dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false); 14108c0984e5SSebastian Reichel 14118c0984e5SSebastian Reichel if (dcap < 0) { 14128c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading initial last measured discharge\n"); 14138c0984e5SSebastian Reichel return dcap; 14148c0984e5SSebastian Reichel } 14158c0984e5SSebastian Reichel 14163a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 14178c0984e5SSebastian Reichel dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 14188c0984e5SSebastian Reichel else 14198c0984e5SSebastian Reichel dcap *= 1000; 14208c0984e5SSebastian Reichel 14218c0984e5SSebastian Reichel return dcap; 14228c0984e5SSebastian Reichel } 14238c0984e5SSebastian Reichel 14248c0984e5SSebastian Reichel /* 14258c0984e5SSebastian Reichel * Return the battery Available energy in µWh 14268c0984e5SSebastian Reichel * Or < 0 if something fails. 14278c0984e5SSebastian Reichel */ 14288c0984e5SSebastian Reichel static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di) 14298c0984e5SSebastian Reichel { 14308c0984e5SSebastian Reichel int ae; 14318c0984e5SSebastian Reichel 14328c0984e5SSebastian Reichel ae = bq27xxx_read(di, BQ27XXX_REG_AE, false); 14338c0984e5SSebastian Reichel if (ae < 0) { 14348c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading available energy\n"); 14358c0984e5SSebastian Reichel return ae; 14368c0984e5SSebastian Reichel } 14378c0984e5SSebastian Reichel 14383a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 14398c0984e5SSebastian Reichel ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS; 14408c0984e5SSebastian Reichel else 14418c0984e5SSebastian Reichel ae *= 1000; 14428c0984e5SSebastian Reichel 14438c0984e5SSebastian Reichel return ae; 14448c0984e5SSebastian Reichel } 14458c0984e5SSebastian Reichel 14468c0984e5SSebastian Reichel /* 14478c0984e5SSebastian Reichel * Return the battery temperature in tenths of degree Kelvin 14488c0984e5SSebastian Reichel * Or < 0 if something fails. 14498c0984e5SSebastian Reichel */ 14508c0984e5SSebastian Reichel static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di) 14518c0984e5SSebastian Reichel { 14528c0984e5SSebastian Reichel int temp; 14538c0984e5SSebastian Reichel 14548c0984e5SSebastian Reichel temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false); 14558c0984e5SSebastian Reichel if (temp < 0) { 14568c0984e5SSebastian Reichel dev_err(di->dev, "error reading temperature\n"); 14578c0984e5SSebastian Reichel return temp; 14588c0984e5SSebastian Reichel } 14598c0984e5SSebastian Reichel 14603a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 14618c0984e5SSebastian Reichel temp = 5 * temp / 2; 14628c0984e5SSebastian Reichel 14638c0984e5SSebastian Reichel return temp; 14648c0984e5SSebastian Reichel } 14658c0984e5SSebastian Reichel 14668c0984e5SSebastian Reichel /* 14678c0984e5SSebastian Reichel * Return the battery Cycle count total 14688c0984e5SSebastian Reichel * Or < 0 if something fails. 14698c0984e5SSebastian Reichel */ 14708c0984e5SSebastian Reichel static int bq27xxx_battery_read_cyct(struct bq27xxx_device_info *di) 14718c0984e5SSebastian Reichel { 14728c0984e5SSebastian Reichel int cyct; 14738c0984e5SSebastian Reichel 14748c0984e5SSebastian Reichel cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false); 14758c0984e5SSebastian Reichel if (cyct < 0) 14768c0984e5SSebastian Reichel dev_err(di->dev, "error reading cycle count total\n"); 14778c0984e5SSebastian Reichel 14788c0984e5SSebastian Reichel return cyct; 14798c0984e5SSebastian Reichel } 14808c0984e5SSebastian Reichel 14818c0984e5SSebastian Reichel /* 14828c0984e5SSebastian Reichel * Read a time register. 14838c0984e5SSebastian Reichel * Return < 0 if something fails. 14848c0984e5SSebastian Reichel */ 14858c0984e5SSebastian Reichel static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg) 14868c0984e5SSebastian Reichel { 14878c0984e5SSebastian Reichel int tval; 14888c0984e5SSebastian Reichel 14898c0984e5SSebastian Reichel tval = bq27xxx_read(di, reg, false); 14908c0984e5SSebastian Reichel if (tval < 0) { 14918c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading time register %02x: %d\n", 14928c0984e5SSebastian Reichel reg, tval); 14938c0984e5SSebastian Reichel return tval; 14948c0984e5SSebastian Reichel } 14958c0984e5SSebastian Reichel 14968c0984e5SSebastian Reichel if (tval == 65535) 14978c0984e5SSebastian Reichel return -ENODATA; 14988c0984e5SSebastian Reichel 14998c0984e5SSebastian Reichel return tval * 60; 15008c0984e5SSebastian Reichel } 15018c0984e5SSebastian Reichel 15028c0984e5SSebastian Reichel /* 15038c0984e5SSebastian Reichel * Read an average power register. 15048c0984e5SSebastian Reichel * Return < 0 if something fails. 15058c0984e5SSebastian Reichel */ 15068c0984e5SSebastian Reichel static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di) 15078c0984e5SSebastian Reichel { 15088c0984e5SSebastian Reichel int tval; 15098c0984e5SSebastian Reichel 15108c0984e5SSebastian Reichel tval = bq27xxx_read(di, BQ27XXX_REG_AP, false); 15118c0984e5SSebastian Reichel if (tval < 0) { 15128c0984e5SSebastian Reichel dev_err(di->dev, "error reading average power register %02x: %d\n", 15138c0984e5SSebastian Reichel BQ27XXX_REG_AP, tval); 15148c0984e5SSebastian Reichel return tval; 15158c0984e5SSebastian Reichel } 15168c0984e5SSebastian Reichel 15173a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 15188c0984e5SSebastian Reichel return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS; 15198c0984e5SSebastian Reichel else 15208c0984e5SSebastian Reichel return tval; 15218c0984e5SSebastian Reichel } 15228c0984e5SSebastian Reichel 15238c0984e5SSebastian Reichel /* 15248c0984e5SSebastian Reichel * Returns true if a battery over temperature condition is detected 15258c0984e5SSebastian Reichel */ 15268c0984e5SSebastian Reichel static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags) 15278c0984e5SSebastian Reichel { 15283a731c64SLiam Breck if (di->opts & BQ27XXX_O_OTDC) 15298c0984e5SSebastian Reichel return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD); 15303a731c64SLiam Breck if (di->opts & BQ27XXX_O_UTOT) 15318c0984e5SSebastian Reichel return flags & BQ27XXX_FLAG_OT; 15323a731c64SLiam Breck 15338c0984e5SSebastian Reichel return false; 15348c0984e5SSebastian Reichel } 15358c0984e5SSebastian Reichel 15368c0984e5SSebastian Reichel /* 15378c0984e5SSebastian Reichel * Returns true if a battery under temperature condition is detected 15388c0984e5SSebastian Reichel */ 15398c0984e5SSebastian Reichel static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags) 15408c0984e5SSebastian Reichel { 15413a731c64SLiam Breck if (di->opts & BQ27XXX_O_UTOT) 15428c0984e5SSebastian Reichel return flags & BQ27XXX_FLAG_UT; 15438c0984e5SSebastian Reichel 15448c0984e5SSebastian Reichel return false; 15458c0984e5SSebastian Reichel } 15468c0984e5SSebastian Reichel 15478c0984e5SSebastian Reichel /* 15488c0984e5SSebastian Reichel * Returns true if a low state of charge condition is detected 15498c0984e5SSebastian Reichel */ 15508c0984e5SSebastian Reichel static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags) 15518c0984e5SSebastian Reichel { 15523a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) 15538c0984e5SSebastian Reichel return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF); 15548c0984e5SSebastian Reichel else 15558c0984e5SSebastian Reichel return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF); 15568c0984e5SSebastian Reichel } 15578c0984e5SSebastian Reichel 15588c0984e5SSebastian Reichel static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) 15598c0984e5SSebastian Reichel { 15608c0984e5SSebastian Reichel /* Unlikely but important to return first */ 15619b2c945fSArthur Demchenkov if (unlikely(bq27xxx_battery_overtemp(di, di->cache.flags))) 15628c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_OVERHEAT; 15639b2c945fSArthur Demchenkov if (unlikely(bq27xxx_battery_undertemp(di, di->cache.flags))) 15648c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_COLD; 15659b2c945fSArthur Demchenkov if (unlikely(bq27xxx_battery_dead(di, di->cache.flags))) 15668c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_DEAD; 15678c0984e5SSebastian Reichel 15688c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_GOOD; 15698c0984e5SSebastian Reichel } 15708c0984e5SSebastian Reichel 15718c0984e5SSebastian Reichel void bq27xxx_battery_update(struct bq27xxx_device_info *di) 15728c0984e5SSebastian Reichel { 15738c0984e5SSebastian Reichel struct bq27xxx_reg_cache cache = {0, }; 15743a731c64SLiam Breck bool has_ci_flag = di->opts & BQ27XXX_O_ZERO; 15753a731c64SLiam Breck bool has_singe_flag = di->opts & BQ27XXX_O_ZERO; 15768c0984e5SSebastian Reichel 15778c0984e5SSebastian Reichel cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); 15788c0984e5SSebastian Reichel if ((cache.flags & 0xff) == 0xff) 15798c0984e5SSebastian Reichel cache.flags = -1; /* read error */ 15808c0984e5SSebastian Reichel if (cache.flags >= 0) { 15818c0984e5SSebastian Reichel cache.temperature = bq27xxx_battery_read_temperature(di); 15828c0984e5SSebastian Reichel if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) { 15838c0984e5SSebastian Reichel dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n"); 15848c0984e5SSebastian Reichel cache.capacity = -ENODATA; 15858c0984e5SSebastian Reichel cache.energy = -ENODATA; 15868c0984e5SSebastian Reichel cache.time_to_empty = -ENODATA; 15878c0984e5SSebastian Reichel cache.time_to_empty_avg = -ENODATA; 15888c0984e5SSebastian Reichel cache.time_to_full = -ENODATA; 15898c0984e5SSebastian Reichel cache.charge_full = -ENODATA; 15908c0984e5SSebastian Reichel cache.health = -ENODATA; 15918c0984e5SSebastian Reichel } else { 15928c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR) 15938c0984e5SSebastian Reichel cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE); 15948c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR) 15958c0984e5SSebastian Reichel cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP); 15968c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR) 15978c0984e5SSebastian Reichel cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF); 15988c0984e5SSebastian Reichel cache.charge_full = bq27xxx_battery_read_fcc(di); 15998c0984e5SSebastian Reichel cache.capacity = bq27xxx_battery_read_soc(di); 16008c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR) 16018c0984e5SSebastian Reichel cache.energy = bq27xxx_battery_read_energy(di); 16029b2c945fSArthur Demchenkov di->cache.flags = cache.flags; 16038c0984e5SSebastian Reichel cache.health = bq27xxx_battery_read_health(di); 16048c0984e5SSebastian Reichel } 16058c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR) 16068c0984e5SSebastian Reichel cache.cycle_count = bq27xxx_battery_read_cyct(di); 16078c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_AP] != INVALID_REG_ADDR) 16088c0984e5SSebastian Reichel cache.power_avg = bq27xxx_battery_read_pwr_avg(di); 16098c0984e5SSebastian Reichel 16108c0984e5SSebastian Reichel /* We only have to read charge design full once */ 16118c0984e5SSebastian Reichel if (di->charge_design_full <= 0) 16128c0984e5SSebastian Reichel di->charge_design_full = bq27xxx_battery_read_dcap(di); 16138c0984e5SSebastian Reichel } 16148c0984e5SSebastian Reichel 1615243f8ffcSKrzysztof Kozlowski if ((di->cache.capacity != cache.capacity) || 1616243f8ffcSKrzysztof Kozlowski (di->cache.flags != cache.flags)) 16178c0984e5SSebastian Reichel power_supply_changed(di->bat); 16188c0984e5SSebastian Reichel 16198c0984e5SSebastian Reichel if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) 16208c0984e5SSebastian Reichel di->cache = cache; 16218c0984e5SSebastian Reichel 16228c0984e5SSebastian Reichel di->last_update = jiffies; 16238c0984e5SSebastian Reichel } 16248c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_update); 16258c0984e5SSebastian Reichel 16268c0984e5SSebastian Reichel static void bq27xxx_battery_poll(struct work_struct *work) 16278c0984e5SSebastian Reichel { 16288c0984e5SSebastian Reichel struct bq27xxx_device_info *di = 16298c0984e5SSebastian Reichel container_of(work, struct bq27xxx_device_info, 16308c0984e5SSebastian Reichel work.work); 16318c0984e5SSebastian Reichel 16328c0984e5SSebastian Reichel bq27xxx_battery_update(di); 16338c0984e5SSebastian Reichel 16348c0984e5SSebastian Reichel if (poll_interval > 0) 16358c0984e5SSebastian Reichel schedule_delayed_work(&di->work, poll_interval * HZ); 16368c0984e5SSebastian Reichel } 16378c0984e5SSebastian Reichel 16388c0984e5SSebastian Reichel /* 16398c0984e5SSebastian Reichel * Return the battery average current in µA 16408c0984e5SSebastian Reichel * Note that current can be negative signed as well 16418c0984e5SSebastian Reichel * Or 0 if something fails. 16428c0984e5SSebastian Reichel */ 16438c0984e5SSebastian Reichel static int bq27xxx_battery_current(struct bq27xxx_device_info *di, 16448c0984e5SSebastian Reichel union power_supply_propval *val) 16458c0984e5SSebastian Reichel { 16468c0984e5SSebastian Reichel int curr; 16478c0984e5SSebastian Reichel int flags; 16488c0984e5SSebastian Reichel 16498c0984e5SSebastian Reichel curr = bq27xxx_read(di, BQ27XXX_REG_AI, false); 16508c0984e5SSebastian Reichel if (curr < 0) { 16518c0984e5SSebastian Reichel dev_err(di->dev, "error reading current\n"); 16528c0984e5SSebastian Reichel return curr; 16538c0984e5SSebastian Reichel } 16548c0984e5SSebastian Reichel 16553a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) { 1656e4a404a0SH. Nikolaus Schaller flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); 16578c0984e5SSebastian Reichel if (flags & BQ27000_FLAG_CHGS) { 16588c0984e5SSebastian Reichel dev_dbg(di->dev, "negative current!\n"); 16598c0984e5SSebastian Reichel curr = -curr; 16608c0984e5SSebastian Reichel } 16618c0984e5SSebastian Reichel 16628c0984e5SSebastian Reichel val->intval = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 16638c0984e5SSebastian Reichel } else { 16648c0984e5SSebastian Reichel /* Other gauges return signed value */ 16658c0984e5SSebastian Reichel val->intval = (int)((s16)curr) * 1000; 16668c0984e5SSebastian Reichel } 16678c0984e5SSebastian Reichel 16688c0984e5SSebastian Reichel return 0; 16698c0984e5SSebastian Reichel } 16708c0984e5SSebastian Reichel 16718c0984e5SSebastian Reichel static int bq27xxx_battery_status(struct bq27xxx_device_info *di, 16728c0984e5SSebastian Reichel union power_supply_propval *val) 16738c0984e5SSebastian Reichel { 16748c0984e5SSebastian Reichel int status; 16758c0984e5SSebastian Reichel 16763a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) { 16778c0984e5SSebastian Reichel if (di->cache.flags & BQ27000_FLAG_FC) 16788c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_FULL; 16798c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_CHGS) 16808c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_CHARGING; 1681f72c14adSSebastian Reichel else if (power_supply_am_i_supplied(di->bat) > 0) 16828c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_NOT_CHARGING; 16838c0984e5SSebastian Reichel else 16848c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_DISCHARGING; 16858c0984e5SSebastian Reichel } else { 16868c0984e5SSebastian Reichel if (di->cache.flags & BQ27XXX_FLAG_FC) 16878c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_FULL; 16888c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_DSC) 16898c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_DISCHARGING; 16908c0984e5SSebastian Reichel else 16918c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_CHARGING; 16928c0984e5SSebastian Reichel } 16938c0984e5SSebastian Reichel 16948c0984e5SSebastian Reichel val->intval = status; 16958c0984e5SSebastian Reichel 16968c0984e5SSebastian Reichel return 0; 16978c0984e5SSebastian Reichel } 16988c0984e5SSebastian Reichel 16998c0984e5SSebastian Reichel static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di, 17008c0984e5SSebastian Reichel union power_supply_propval *val) 17018c0984e5SSebastian Reichel { 17028c0984e5SSebastian Reichel int level; 17038c0984e5SSebastian Reichel 17043a731c64SLiam Breck if (di->opts & BQ27XXX_O_ZERO) { 17058c0984e5SSebastian Reichel if (di->cache.flags & BQ27000_FLAG_FC) 17068c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 17078c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_EDV1) 17088c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 17098c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_EDVF) 17108c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 17118c0984e5SSebastian Reichel else 17128c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 17138c0984e5SSebastian Reichel } else { 17148c0984e5SSebastian Reichel if (di->cache.flags & BQ27XXX_FLAG_FC) 17158c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 17168c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_SOC1) 17178c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 17188c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_SOCF) 17198c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 17208c0984e5SSebastian Reichel else 17218c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 17228c0984e5SSebastian Reichel } 17238c0984e5SSebastian Reichel 17248c0984e5SSebastian Reichel val->intval = level; 17258c0984e5SSebastian Reichel 17268c0984e5SSebastian Reichel return 0; 17278c0984e5SSebastian Reichel } 17288c0984e5SSebastian Reichel 17298c0984e5SSebastian Reichel /* 17308c0984e5SSebastian Reichel * Return the battery Voltage in millivolts 17318c0984e5SSebastian Reichel * Or < 0 if something fails. 17328c0984e5SSebastian Reichel */ 17338c0984e5SSebastian Reichel static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di, 17348c0984e5SSebastian Reichel union power_supply_propval *val) 17358c0984e5SSebastian Reichel { 17368c0984e5SSebastian Reichel int volt; 17378c0984e5SSebastian Reichel 17388c0984e5SSebastian Reichel volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false); 17398c0984e5SSebastian Reichel if (volt < 0) { 17408c0984e5SSebastian Reichel dev_err(di->dev, "error reading voltage\n"); 17418c0984e5SSebastian Reichel return volt; 17428c0984e5SSebastian Reichel } 17438c0984e5SSebastian Reichel 17448c0984e5SSebastian Reichel val->intval = volt * 1000; 17458c0984e5SSebastian Reichel 17468c0984e5SSebastian Reichel return 0; 17478c0984e5SSebastian Reichel } 17488c0984e5SSebastian Reichel 17498c0984e5SSebastian Reichel static int bq27xxx_simple_value(int value, 17508c0984e5SSebastian Reichel union power_supply_propval *val) 17518c0984e5SSebastian Reichel { 17528c0984e5SSebastian Reichel if (value < 0) 17538c0984e5SSebastian Reichel return value; 17548c0984e5SSebastian Reichel 17558c0984e5SSebastian Reichel val->intval = value; 17568c0984e5SSebastian Reichel 17578c0984e5SSebastian Reichel return 0; 17588c0984e5SSebastian Reichel } 17598c0984e5SSebastian Reichel 17608c0984e5SSebastian Reichel static int bq27xxx_battery_get_property(struct power_supply *psy, 17618c0984e5SSebastian Reichel enum power_supply_property psp, 17628c0984e5SSebastian Reichel union power_supply_propval *val) 17638c0984e5SSebastian Reichel { 17648c0984e5SSebastian Reichel int ret = 0; 17658c0984e5SSebastian Reichel struct bq27xxx_device_info *di = power_supply_get_drvdata(psy); 17668c0984e5SSebastian Reichel 17678c0984e5SSebastian Reichel mutex_lock(&di->lock); 17688c0984e5SSebastian Reichel if (time_is_before_jiffies(di->last_update + 5 * HZ)) { 17698c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 17708c0984e5SSebastian Reichel bq27xxx_battery_poll(&di->work.work); 17718c0984e5SSebastian Reichel } 17728c0984e5SSebastian Reichel mutex_unlock(&di->lock); 17738c0984e5SSebastian Reichel 17748c0984e5SSebastian Reichel if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) 17758c0984e5SSebastian Reichel return -ENODEV; 17768c0984e5SSebastian Reichel 17778c0984e5SSebastian Reichel switch (psp) { 17788c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 17798c0984e5SSebastian Reichel ret = bq27xxx_battery_status(di, val); 17808c0984e5SSebastian Reichel break; 17818c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW: 17828c0984e5SSebastian Reichel ret = bq27xxx_battery_voltage(di, val); 17838c0984e5SSebastian Reichel break; 17848c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_PRESENT: 17858c0984e5SSebastian Reichel val->intval = di->cache.flags < 0 ? 0 : 1; 17868c0984e5SSebastian Reichel break; 17878c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CURRENT_NOW: 17888c0984e5SSebastian Reichel ret = bq27xxx_battery_current(di, val); 17898c0984e5SSebastian Reichel break; 17908c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY: 17918c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.capacity, val); 17928c0984e5SSebastian Reichel break; 17938c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 17948c0984e5SSebastian Reichel ret = bq27xxx_battery_capacity_level(di, val); 17958c0984e5SSebastian Reichel break; 17968c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TEMP: 17978c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.temperature, val); 17988c0984e5SSebastian Reichel if (ret == 0) 17998c0984e5SSebastian Reichel val->intval -= 2731; /* convert decidegree k to c */ 18008c0984e5SSebastian Reichel break; 18018c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: 18028c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_empty, val); 18038c0984e5SSebastian Reichel break; 18048c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: 18058c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_empty_avg, val); 18068c0984e5SSebastian Reichel break; 18078c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: 18088c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_full, val); 18098c0984e5SSebastian Reichel break; 18108c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TECHNOLOGY: 18118c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 18128c0984e5SSebastian Reichel break; 18138c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_NOW: 18148c0984e5SSebastian Reichel ret = bq27xxx_simple_value(bq27xxx_battery_read_nac(di), val); 18158c0984e5SSebastian Reichel break; 18168c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL: 18178c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.charge_full, val); 18188c0984e5SSebastian Reichel break; 18198c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 18208c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->charge_design_full, val); 18218c0984e5SSebastian Reichel break; 1822ccce4409SLiam Breck /* 1823ccce4409SLiam Breck * TODO: Implement these to make registers set from 1824ccce4409SLiam Breck * power_supply_battery_info visible in sysfs. 1825ccce4409SLiam Breck */ 1826ccce4409SLiam Breck case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 1827ccce4409SLiam Breck case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 1828ccce4409SLiam Breck return -EINVAL; 18298c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CYCLE_COUNT: 18308c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.cycle_count, val); 18318c0984e5SSebastian Reichel break; 18328c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_ENERGY_NOW: 18338c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.energy, val); 18348c0984e5SSebastian Reichel break; 18358c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_POWER_AVG: 18368c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.power_avg, val); 18378c0984e5SSebastian Reichel break; 18388c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH: 18398c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.health, val); 18408c0984e5SSebastian Reichel break; 18418c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_MANUFACTURER: 18428c0984e5SSebastian Reichel val->strval = BQ27XXX_MANUFACTURER; 18438c0984e5SSebastian Reichel break; 18448c0984e5SSebastian Reichel default: 18458c0984e5SSebastian Reichel return -EINVAL; 18468c0984e5SSebastian Reichel } 18478c0984e5SSebastian Reichel 18488c0984e5SSebastian Reichel return ret; 18498c0984e5SSebastian Reichel } 18508c0984e5SSebastian Reichel 18518c0984e5SSebastian Reichel static void bq27xxx_external_power_changed(struct power_supply *psy) 18528c0984e5SSebastian Reichel { 18538c0984e5SSebastian Reichel struct bq27xxx_device_info *di = power_supply_get_drvdata(psy); 18548c0984e5SSebastian Reichel 18558c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 18568c0984e5SSebastian Reichel schedule_delayed_work(&di->work, 0); 18578c0984e5SSebastian Reichel } 18588c0984e5SSebastian Reichel 18598c0984e5SSebastian Reichel int bq27xxx_battery_setup(struct bq27xxx_device_info *di) 18608c0984e5SSebastian Reichel { 18618c0984e5SSebastian Reichel struct power_supply_desc *psy_desc; 1862ccce4409SLiam Breck struct power_supply_config psy_cfg = { 1863ccce4409SLiam Breck .of_node = di->dev->of_node, 1864ccce4409SLiam Breck .drv_data = di, 1865ccce4409SLiam Breck }; 18668c0984e5SSebastian Reichel 18678c0984e5SSebastian Reichel INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll); 18688c0984e5SSebastian Reichel mutex_init(&di->lock); 18693a731c64SLiam Breck 18709aade6d8SLiam Breck di->regs = bq27xxx_chip_data[di->chip].regs; 187105045379SLiam Breck di->unseal_key = bq27xxx_chip_data[di->chip].unseal_key; 187205045379SLiam Breck di->dm_regs = bq27xxx_chip_data[di->chip].dm_regs; 18733a731c64SLiam Breck di->opts = bq27xxx_chip_data[di->chip].opts; 18748c0984e5SSebastian Reichel 18758c0984e5SSebastian Reichel psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL); 18768c0984e5SSebastian Reichel if (!psy_desc) 18778c0984e5SSebastian Reichel return -ENOMEM; 18788c0984e5SSebastian Reichel 18798c0984e5SSebastian Reichel psy_desc->name = di->name; 18808c0984e5SSebastian Reichel psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; 18819aade6d8SLiam Breck psy_desc->properties = bq27xxx_chip_data[di->chip].props; 18829aade6d8SLiam Breck psy_desc->num_properties = bq27xxx_chip_data[di->chip].props_size; 18838c0984e5SSebastian Reichel psy_desc->get_property = bq27xxx_battery_get_property; 18848c0984e5SSebastian Reichel psy_desc->external_power_changed = bq27xxx_external_power_changed; 18858c0984e5SSebastian Reichel 18868c0984e5SSebastian Reichel di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg); 18878c0984e5SSebastian Reichel if (IS_ERR(di->bat)) { 1888*583b53ecSDmitry Osipenko if (PTR_ERR(di->bat) == -EPROBE_DEFER) 1889*583b53ecSDmitry Osipenko dev_dbg(di->dev, "failed to register battery, deferring probe\n"); 1890*583b53ecSDmitry Osipenko else 18918c0984e5SSebastian Reichel dev_err(di->dev, "failed to register battery\n"); 18928c0984e5SSebastian Reichel return PTR_ERR(di->bat); 18938c0984e5SSebastian Reichel } 18948c0984e5SSebastian Reichel 1895ccce4409SLiam Breck bq27xxx_battery_settings(di); 18968c0984e5SSebastian Reichel bq27xxx_battery_update(di); 18978c0984e5SSebastian Reichel 18981d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 18991d72706fSMatt Ranostay list_add(&di->list, &bq27xxx_battery_devices); 19001d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 19011d72706fSMatt Ranostay 19028c0984e5SSebastian Reichel return 0; 19038c0984e5SSebastian Reichel } 19048c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_setup); 19058c0984e5SSebastian Reichel 19068c0984e5SSebastian Reichel void bq27xxx_battery_teardown(struct bq27xxx_device_info *di) 19078c0984e5SSebastian Reichel { 19088c0984e5SSebastian Reichel /* 19098c0984e5SSebastian Reichel * power_supply_unregister call bq27xxx_battery_get_property which 19108c0984e5SSebastian Reichel * call bq27xxx_battery_poll. 19118c0984e5SSebastian Reichel * Make sure that bq27xxx_battery_poll will not call 19128c0984e5SSebastian Reichel * schedule_delayed_work again after unregister (which cause OOPS). 19138c0984e5SSebastian Reichel */ 19148c0984e5SSebastian Reichel poll_interval = 0; 19158c0984e5SSebastian Reichel 19168c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 19178c0984e5SSebastian Reichel 19188c0984e5SSebastian Reichel power_supply_unregister(di->bat); 19198c0984e5SSebastian Reichel 19201d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 19211d72706fSMatt Ranostay list_del(&di->list); 19221d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 19231d72706fSMatt Ranostay 19248c0984e5SSebastian Reichel mutex_destroy(&di->lock); 19258c0984e5SSebastian Reichel } 19268c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown); 19278c0984e5SSebastian Reichel 19288c0984e5SSebastian Reichel MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 19298c0984e5SSebastian Reichel MODULE_DESCRIPTION("BQ27xxx battery monitor driver"); 19308c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 1931