18c0984e5SSebastian Reichel /* 28c0984e5SSebastian Reichel * BQ27xxx battery driver 38c0984e5SSebastian Reichel * 48c0984e5SSebastian Reichel * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> 58c0984e5SSebastian Reichel * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> 68c0984e5SSebastian Reichel * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de> 78c0984e5SSebastian Reichel * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com> 80670c9b3SLiam Breck * Copyright (C) 2017 Liam Breck <kernel@networkimprov.net> 98c0984e5SSebastian Reichel * 108c0984e5SSebastian Reichel * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. 118c0984e5SSebastian Reichel * 128c0984e5SSebastian Reichel * This package is free software; you can redistribute it and/or modify 138c0984e5SSebastian Reichel * it under the terms of the GNU General Public License version 2 as 148c0984e5SSebastian Reichel * published by the Free Software Foundation. 158c0984e5SSebastian Reichel * 168c0984e5SSebastian Reichel * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 178c0984e5SSebastian Reichel * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 188c0984e5SSebastian Reichel * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 198c0984e5SSebastian Reichel * 208c0984e5SSebastian Reichel * Datasheets: 218c0984e5SSebastian Reichel * http://www.ti.com/product/bq27000 228c0984e5SSebastian Reichel * http://www.ti.com/product/bq27200 238c0984e5SSebastian Reichel * http://www.ti.com/product/bq27010 248c0984e5SSebastian Reichel * http://www.ti.com/product/bq27210 258c0984e5SSebastian Reichel * http://www.ti.com/product/bq27500 26bd28177fSChris Lapa * http://www.ti.com/product/bq27510-g1 27698a2bf5SChris Lapa * http://www.ti.com/product/bq27510-g2 288c0984e5SSebastian Reichel * http://www.ti.com/product/bq27510-g3 298c0984e5SSebastian Reichel * http://www.ti.com/product/bq27520-g4 3068f2a813SChris Lapa * http://www.ti.com/product/bq27520-g1 31a5deb9a9SChris Lapa * http://www.ti.com/product/bq27520-g2 32825e915bSChris Lapa * http://www.ti.com/product/bq27520-g3 338835cae5SChris Lapa * http://www.ti.com/product/bq27520-g4 348c0984e5SSebastian Reichel * http://www.ti.com/product/bq27530-g1 358c0984e5SSebastian Reichel * http://www.ti.com/product/bq27531-g1 368c0984e5SSebastian Reichel * http://www.ti.com/product/bq27541-g1 378c0984e5SSebastian Reichel * http://www.ti.com/product/bq27542-g1 388c0984e5SSebastian Reichel * http://www.ti.com/product/bq27546-g1 398c0984e5SSebastian Reichel * http://www.ti.com/product/bq27742-g1 408c0984e5SSebastian Reichel * http://www.ti.com/product/bq27545-g1 418c0984e5SSebastian Reichel * http://www.ti.com/product/bq27421-g1 428c0984e5SSebastian Reichel * http://www.ti.com/product/bq27425-g1 438c0984e5SSebastian Reichel * http://www.ti.com/product/bq27411-g1 448c0984e5SSebastian Reichel * http://www.ti.com/product/bq27621-g1 458c0984e5SSebastian Reichel */ 468c0984e5SSebastian Reichel 478c0984e5SSebastian Reichel #include <linux/device.h> 488c0984e5SSebastian Reichel #include <linux/module.h> 491d72706fSMatt Ranostay #include <linux/mutex.h> 508c0984e5SSebastian Reichel #include <linux/param.h> 518c0984e5SSebastian Reichel #include <linux/jiffies.h> 528c0984e5SSebastian Reichel #include <linux/workqueue.h> 538c0984e5SSebastian Reichel #include <linux/delay.h> 548c0984e5SSebastian Reichel #include <linux/platform_device.h> 558c0984e5SSebastian Reichel #include <linux/power_supply.h> 568c0984e5SSebastian Reichel #include <linux/slab.h> 578c0984e5SSebastian Reichel #include <linux/of.h> 588c0984e5SSebastian Reichel 598c0984e5SSebastian Reichel #include <linux/power/bq27xxx_battery.h> 608c0984e5SSebastian Reichel 618c0984e5SSebastian Reichel #define DRIVER_VERSION "1.2.0" 628c0984e5SSebastian Reichel 638c0984e5SSebastian Reichel #define BQ27XXX_MANUFACTURER "Texas Instruments" 648c0984e5SSebastian Reichel 658c0984e5SSebastian Reichel /* BQ27XXX Flags */ 668c0984e5SSebastian Reichel #define BQ27XXX_FLAG_DSC BIT(0) 678c0984e5SSebastian Reichel #define BQ27XXX_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ 688c0984e5SSebastian Reichel #define BQ27XXX_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ 690670c9b3SLiam Breck #define BQ27XXX_FLAG_CFGUP BIT(4) 708c0984e5SSebastian Reichel #define BQ27XXX_FLAG_FC BIT(9) 718c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OTD BIT(14) 728c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OTC BIT(15) 738c0984e5SSebastian Reichel #define BQ27XXX_FLAG_UT BIT(14) 748c0984e5SSebastian Reichel #define BQ27XXX_FLAG_OT BIT(15) 758c0984e5SSebastian Reichel 768c0984e5SSebastian Reichel /* BQ27000 has different layout for Flags register */ 778c0984e5SSebastian Reichel #define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */ 788c0984e5SSebastian Reichel #define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */ 798c0984e5SSebastian Reichel #define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */ 808c0984e5SSebastian Reichel #define BQ27000_FLAG_FC BIT(5) 818c0984e5SSebastian Reichel #define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ 828c0984e5SSebastian Reichel 830670c9b3SLiam Breck /* control register params */ 840670c9b3SLiam Breck #define BQ27XXX_SEALED 0x20 850670c9b3SLiam Breck #define BQ27XXX_SET_CFGUPDATE 0x13 860670c9b3SLiam Breck #define BQ27XXX_SOFT_RESET 0x42 870670c9b3SLiam Breck #define BQ27XXX_RESET 0x41 880670c9b3SLiam Breck 898c0984e5SSebastian Reichel #define BQ27XXX_RS (20) /* Resistor sense mOhm */ 908c0984e5SSebastian Reichel #define BQ27XXX_POWER_CONSTANT (29200) /* 29.2 µV^2 * 1000 */ 918c0984e5SSebastian Reichel #define BQ27XXX_CURRENT_CONSTANT (3570) /* 3.57 µV * 1000 */ 928c0984e5SSebastian Reichel 938c0984e5SSebastian Reichel #define INVALID_REG_ADDR 0xff 948c0984e5SSebastian Reichel 958c0984e5SSebastian Reichel /* 968c0984e5SSebastian Reichel * bq27xxx_reg_index - Register names 978c0984e5SSebastian Reichel * 988c0984e5SSebastian Reichel * These are indexes into a device's register mapping array. 998c0984e5SSebastian Reichel */ 1008c0984e5SSebastian Reichel 1018c0984e5SSebastian Reichel enum bq27xxx_reg_index { 1028c0984e5SSebastian Reichel BQ27XXX_REG_CTRL = 0, /* Control */ 1038c0984e5SSebastian Reichel BQ27XXX_REG_TEMP, /* Temperature */ 1048c0984e5SSebastian Reichel BQ27XXX_REG_INT_TEMP, /* Internal Temperature */ 1058c0984e5SSebastian Reichel BQ27XXX_REG_VOLT, /* Voltage */ 1068c0984e5SSebastian Reichel BQ27XXX_REG_AI, /* Average Current */ 1078c0984e5SSebastian Reichel BQ27XXX_REG_FLAGS, /* Flags */ 1088c0984e5SSebastian Reichel BQ27XXX_REG_TTE, /* Time-to-Empty */ 1098c0984e5SSebastian Reichel BQ27XXX_REG_TTF, /* Time-to-Full */ 1108c0984e5SSebastian Reichel BQ27XXX_REG_TTES, /* Time-to-Empty Standby */ 1118c0984e5SSebastian Reichel BQ27XXX_REG_TTECP, /* Time-to-Empty at Constant Power */ 1128c0984e5SSebastian Reichel BQ27XXX_REG_NAC, /* Nominal Available Capacity */ 1138c0984e5SSebastian Reichel BQ27XXX_REG_FCC, /* Full Charge Capacity */ 1148c0984e5SSebastian Reichel BQ27XXX_REG_CYCT, /* Cycle Count */ 1158c0984e5SSebastian Reichel BQ27XXX_REG_AE, /* Available Energy */ 1168c0984e5SSebastian Reichel BQ27XXX_REG_SOC, /* State-of-Charge */ 1178c0984e5SSebastian Reichel BQ27XXX_REG_DCAP, /* Design Capacity */ 1188c0984e5SSebastian Reichel BQ27XXX_REG_AP, /* Average Power */ 1190670c9b3SLiam Breck BQ27XXX_DM_CTRL, /* Block Data Control */ 1200670c9b3SLiam Breck BQ27XXX_DM_CLASS, /* Data Class */ 1210670c9b3SLiam Breck BQ27XXX_DM_BLOCK, /* Data Block */ 1220670c9b3SLiam Breck BQ27XXX_DM_DATA, /* Block Data */ 1230670c9b3SLiam Breck BQ27XXX_DM_CKSUM, /* Block Data Checksum */ 1248c0984e5SSebastian Reichel BQ27XXX_REG_MAX, /* sentinel */ 1258c0984e5SSebastian Reichel }; 1268c0984e5SSebastian Reichel 1270670c9b3SLiam Breck #define BQ27XXX_DM_REG_ROWS \ 1280670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = 0x61, \ 1290670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = 0x3e, \ 1300670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = 0x3f, \ 1310670c9b3SLiam Breck [BQ27XXX_DM_DATA] = 0x40, \ 1320670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = 0x60 1330670c9b3SLiam Breck 1348c0984e5SSebastian Reichel /* Register mappings */ 1358c0984e5SSebastian Reichel static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { 1368c0984e5SSebastian Reichel [BQ27000] = { 1378c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1388c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1398c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 1408c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1418c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1428c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1438c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1448c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = 0x18, 1458c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1c, 1468c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = 0x26, 1478c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1488c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1498c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1508c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = 0x22, 1518c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x0b, 1528c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x76, 1538c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 1540670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, 1550670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, 1560670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, 1570670c9b3SLiam Breck [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, 1580670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, 1598c0984e5SSebastian Reichel }, 1608c0984e5SSebastian Reichel [BQ27010] = { 1618c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1628c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1638c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 1648c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1658c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1668c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1678c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1688c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = 0x18, 1698c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1c, 1708c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = 0x26, 1718c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1728c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1738c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1748c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 1758c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x0b, 1768c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x76, 1778c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 1780670c9b3SLiam Breck [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, 1790670c9b3SLiam Breck [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, 1800670c9b3SLiam Breck [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, 1810670c9b3SLiam Breck [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, 1820670c9b3SLiam Breck [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, 1838c0984e5SSebastian Reichel }, 184818e3012SChris Lapa [BQ2750X] = { 1858c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 1868c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 1878c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 1888c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 1898c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 1908c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 1918c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 1928c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 1938c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = 0x1a, 1948c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 1958c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 1968c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 1978c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 1988c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 1998c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 2008c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 2018c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 2020670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 2038c0984e5SSebastian Reichel }, 2046da6e4bdSChris Lapa [BQ2751X] = { 2053bee9ea1SAndrew F. Davis [BQ27XXX_REG_CTRL] = 0x00, 2063bee9ea1SAndrew F. Davis [BQ27XXX_REG_TEMP] = 0x06, 2073bee9ea1SAndrew F. Davis [BQ27XXX_REG_INT_TEMP] = 0x28, 2083bee9ea1SAndrew F. Davis [BQ27XXX_REG_VOLT] = 0x08, 2093bee9ea1SAndrew F. Davis [BQ27XXX_REG_AI] = 0x14, 2103bee9ea1SAndrew F. Davis [BQ27XXX_REG_FLAGS] = 0x0a, 2113bee9ea1SAndrew F. Davis [BQ27XXX_REG_TTE] = 0x16, 2123bee9ea1SAndrew F. Davis [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 2133bee9ea1SAndrew F. Davis [BQ27XXX_REG_TTES] = 0x1a, 2143bee9ea1SAndrew F. Davis [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 2153bee9ea1SAndrew F. Davis [BQ27XXX_REG_NAC] = 0x0c, 2163bee9ea1SAndrew F. Davis [BQ27XXX_REG_FCC] = 0x12, 2173bee9ea1SAndrew F. Davis [BQ27XXX_REG_CYCT] = 0x1e, 2183bee9ea1SAndrew F. Davis [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 2193bee9ea1SAndrew F. Davis [BQ27XXX_REG_SOC] = 0x20, 2203bee9ea1SAndrew F. Davis [BQ27XXX_REG_DCAP] = 0x2e, 2213bee9ea1SAndrew F. Davis [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 2220670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 2233bee9ea1SAndrew F. Davis }, 22432833635SChris Lapa [BQ27500] = { 22532833635SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 22632833635SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 22732833635SChris Lapa [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 22832833635SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 22932833635SChris Lapa [BQ27XXX_REG_AI] = 0x14, 23032833635SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 23132833635SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 23232833635SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 23332833635SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 23432833635SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 23532833635SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 23632833635SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 23732833635SChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 23832833635SChris Lapa [BQ27XXX_REG_AE] = 0x22, 23932833635SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 24032833635SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 24132833635SChris Lapa [BQ27XXX_REG_AP] = 0x24, 2420670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 24332833635SChris Lapa }, 244bd28177fSChris Lapa [BQ27510G1] = { 245bd28177fSChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 246bd28177fSChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 247bd28177fSChris Lapa [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 248bd28177fSChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 249bd28177fSChris Lapa [BQ27XXX_REG_AI] = 0x14, 250bd28177fSChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 251bd28177fSChris Lapa [BQ27XXX_REG_TTE] = 0x16, 252bd28177fSChris Lapa [BQ27XXX_REG_TTF] = 0x18, 253bd28177fSChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 254bd28177fSChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 255bd28177fSChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 256bd28177fSChris Lapa [BQ27XXX_REG_FCC] = 0x12, 257bd28177fSChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 258bd28177fSChris Lapa [BQ27XXX_REG_AE] = 0x22, 259bd28177fSChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 260bd28177fSChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 261bd28177fSChris Lapa [BQ27XXX_REG_AP] = 0x24, 2620670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 263bd28177fSChris Lapa }, 264698a2bf5SChris Lapa [BQ27510G2] = { 265698a2bf5SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 266698a2bf5SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 267698a2bf5SChris Lapa [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 268698a2bf5SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 269698a2bf5SChris Lapa [BQ27XXX_REG_AI] = 0x14, 270698a2bf5SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 271698a2bf5SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 272698a2bf5SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 273698a2bf5SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 274698a2bf5SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 275698a2bf5SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 276698a2bf5SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 277698a2bf5SChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 278698a2bf5SChris Lapa [BQ27XXX_REG_AE] = 0x22, 279698a2bf5SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 280698a2bf5SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 281698a2bf5SChris Lapa [BQ27XXX_REG_AP] = 0x24, 2820670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 283698a2bf5SChris Lapa }, 28471375aa7SChris Lapa [BQ27510G3] = { 28571375aa7SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 28671375aa7SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 28771375aa7SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x28, 28871375aa7SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 28971375aa7SChris Lapa [BQ27XXX_REG_AI] = 0x14, 29071375aa7SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 29171375aa7SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 29271375aa7SChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 29371375aa7SChris Lapa [BQ27XXX_REG_TTES] = 0x1a, 29471375aa7SChris Lapa [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 29571375aa7SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 29671375aa7SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 29771375aa7SChris Lapa [BQ27XXX_REG_CYCT] = 0x1e, 29871375aa7SChris Lapa [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 29971375aa7SChris Lapa [BQ27XXX_REG_SOC] = 0x20, 30071375aa7SChris Lapa [BQ27XXX_REG_DCAP] = 0x2e, 30171375aa7SChris Lapa [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 3020670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 30371375aa7SChris Lapa }, 30468f2a813SChris Lapa [BQ27520G1] = { 30568f2a813SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 30668f2a813SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 30768f2a813SChris Lapa [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, 30868f2a813SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 30968f2a813SChris Lapa [BQ27XXX_REG_AI] = 0x14, 31068f2a813SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 31168f2a813SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 31268f2a813SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 31368f2a813SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 31468f2a813SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 31568f2a813SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 31668f2a813SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 31768f2a813SChris Lapa [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, 31868f2a813SChris Lapa [BQ27XXX_REG_AE] = 0x22, 31968f2a813SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 32068f2a813SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 32168f2a813SChris Lapa [BQ27XXX_REG_AP] = 0x24, 3220670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 32368f2a813SChris Lapa }, 324a5deb9a9SChris Lapa [BQ27520G2] = { 325a5deb9a9SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 326a5deb9a9SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 327a5deb9a9SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x36, 328a5deb9a9SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 329a5deb9a9SChris Lapa [BQ27XXX_REG_AI] = 0x14, 330a5deb9a9SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 331a5deb9a9SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 332a5deb9a9SChris Lapa [BQ27XXX_REG_TTF] = 0x18, 333a5deb9a9SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 334a5deb9a9SChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 335a5deb9a9SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 336a5deb9a9SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 337a5deb9a9SChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 338a5deb9a9SChris Lapa [BQ27XXX_REG_AE] = 0x22, 339a5deb9a9SChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 340a5deb9a9SChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 341a5deb9a9SChris Lapa [BQ27XXX_REG_AP] = 0x24, 3420670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 343a5deb9a9SChris Lapa }, 344825e915bSChris Lapa [BQ27520G3] = { 345825e915bSChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 346825e915bSChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 347825e915bSChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x36, 348825e915bSChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 349825e915bSChris Lapa [BQ27XXX_REG_AI] = 0x14, 350825e915bSChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 351825e915bSChris Lapa [BQ27XXX_REG_TTE] = 0x16, 352825e915bSChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 353825e915bSChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 354825e915bSChris Lapa [BQ27XXX_REG_TTECP] = 0x26, 355825e915bSChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 356825e915bSChris Lapa [BQ27XXX_REG_FCC] = 0x12, 357825e915bSChris Lapa [BQ27XXX_REG_CYCT] = 0x2a, 358825e915bSChris Lapa [BQ27XXX_REG_AE] = 0x22, 359825e915bSChris Lapa [BQ27XXX_REG_SOC] = 0x2c, 360825e915bSChris Lapa [BQ27XXX_REG_DCAP] = 0x3c, 361825e915bSChris Lapa [BQ27XXX_REG_AP] = 0x24, 3620670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 363825e915bSChris Lapa }, 3648835cae5SChris Lapa [BQ27520G4] = { 3658835cae5SChris Lapa [BQ27XXX_REG_CTRL] = 0x00, 3668835cae5SChris Lapa [BQ27XXX_REG_TEMP] = 0x06, 3678835cae5SChris Lapa [BQ27XXX_REG_INT_TEMP] = 0x28, 3688835cae5SChris Lapa [BQ27XXX_REG_VOLT] = 0x08, 3698835cae5SChris Lapa [BQ27XXX_REG_AI] = 0x14, 3708835cae5SChris Lapa [BQ27XXX_REG_FLAGS] = 0x0a, 3718835cae5SChris Lapa [BQ27XXX_REG_TTE] = 0x16, 3728835cae5SChris Lapa [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 3738835cae5SChris Lapa [BQ27XXX_REG_TTES] = 0x1c, 3748835cae5SChris Lapa [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 3758835cae5SChris Lapa [BQ27XXX_REG_NAC] = 0x0c, 3768835cae5SChris Lapa [BQ27XXX_REG_FCC] = 0x12, 3778835cae5SChris Lapa [BQ27XXX_REG_CYCT] = 0x1e, 3788835cae5SChris Lapa [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 3798835cae5SChris Lapa [BQ27XXX_REG_SOC] = 0x20, 3808835cae5SChris Lapa [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 3818835cae5SChris Lapa [BQ27XXX_REG_AP] = INVALID_REG_ADDR, 3820670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 3838835cae5SChris Lapa }, 3848c0984e5SSebastian Reichel [BQ27530] = { 3858c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 3868c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 3878c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x32, 3888c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 3898c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 3908c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 3918c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 3928c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 3938c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 3948c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 3958c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 3968c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 3978c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 3988c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 3998c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 4008c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 4018c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 4020670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 4038c0984e5SSebastian Reichel }, 4048c0984e5SSebastian Reichel [BQ27541] = { 4058c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 4068c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 4078c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 4088c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 4098c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 4108c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 4118c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 4128c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 4138c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 4148c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 4158c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 4168c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 4178c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 4188c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 4198c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 4208c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 4218c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 4220670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 4238c0984e5SSebastian Reichel }, 4248c0984e5SSebastian Reichel [BQ27545] = { 4258c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 4268c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x06, 4278c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x28, 4288c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x08, 4298c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x14, 4308c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x0a, 4318c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = 0x16, 4328c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 4338c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 4348c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 4358c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x0c, 4368c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x12, 4378c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = 0x2a, 4388c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 4398c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x2c, 4408c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, 4418c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x24, 4420670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 4438c0984e5SSebastian Reichel }, 4448c0984e5SSebastian Reichel [BQ27421] = { 4458c0984e5SSebastian Reichel [BQ27XXX_REG_CTRL] = 0x00, 4468c0984e5SSebastian Reichel [BQ27XXX_REG_TEMP] = 0x02, 4478c0984e5SSebastian Reichel [BQ27XXX_REG_INT_TEMP] = 0x1e, 4488c0984e5SSebastian Reichel [BQ27XXX_REG_VOLT] = 0x04, 4498c0984e5SSebastian Reichel [BQ27XXX_REG_AI] = 0x10, 4508c0984e5SSebastian Reichel [BQ27XXX_REG_FLAGS] = 0x06, 4518c0984e5SSebastian Reichel [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, 4528c0984e5SSebastian Reichel [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, 4538c0984e5SSebastian Reichel [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, 4548c0984e5SSebastian Reichel [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, 4558c0984e5SSebastian Reichel [BQ27XXX_REG_NAC] = 0x08, 4568c0984e5SSebastian Reichel [BQ27XXX_REG_FCC] = 0x0e, 4578c0984e5SSebastian Reichel [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, 4588c0984e5SSebastian Reichel [BQ27XXX_REG_AE] = INVALID_REG_ADDR, 4598c0984e5SSebastian Reichel [BQ27XXX_REG_SOC] = 0x1c, 4608c0984e5SSebastian Reichel [BQ27XXX_REG_DCAP] = 0x3c, 4618c0984e5SSebastian Reichel [BQ27XXX_REG_AP] = 0x18, 4620670c9b3SLiam Breck BQ27XXX_DM_REG_ROWS, 4638c0984e5SSebastian Reichel }, 4648c0984e5SSebastian Reichel }; 4658c0984e5SSebastian Reichel 4668c0984e5SSebastian Reichel static enum power_supply_property bq27000_battery_props[] = { 4678c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4688c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 4698c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4708c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 4718c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4728c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 4738c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 4748c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 4758c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 4768c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 4778c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 4788c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 4798c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 4808c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 4818c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 4828c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ENERGY_NOW, 4838c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 4848c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 4858c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 4868c0984e5SSebastian Reichel }; 4878c0984e5SSebastian Reichel 4888c0984e5SSebastian Reichel static enum power_supply_property bq27010_battery_props[] = { 4898c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 4908c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 4918c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 4928c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 4938c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 4948c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 4958c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 4968c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 4978c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 4988c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 4998c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 5008c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 5018c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 5028c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 5038c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 5048c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 5058c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 5068c0984e5SSebastian Reichel }; 5078c0984e5SSebastian Reichel 508818e3012SChris Lapa static enum power_supply_property bq2750x_battery_props[] = { 5098c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 5108c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 5118c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 5128c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 5138c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 5148c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 5158c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 5168c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 5178c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 5188c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 5198c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 5208c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 5218c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 5228c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 5238c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 5248c0984e5SSebastian Reichel }; 5258c0984e5SSebastian Reichel 5266da6e4bdSChris Lapa static enum power_supply_property bq2751x_battery_props[] = { 5273bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_STATUS, 5283bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_PRESENT, 5293bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_VOLTAGE_NOW, 5303bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_CURRENT_NOW, 5313bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_CAPACITY, 5323bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_CAPACITY_LEVEL, 5333bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_TEMP, 5343bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 5353bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_TECHNOLOGY, 5363bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_CHARGE_FULL, 5373bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_CHARGE_NOW, 5383bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 5393bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_CYCLE_COUNT, 5403bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_HEALTH, 5413bee9ea1SAndrew F. Davis POWER_SUPPLY_PROP_MANUFACTURER, 5423bee9ea1SAndrew F. Davis }; 5433bee9ea1SAndrew F. Davis 54432833635SChris Lapa static enum power_supply_property bq27500_battery_props[] = { 54532833635SChris Lapa POWER_SUPPLY_PROP_STATUS, 54632833635SChris Lapa POWER_SUPPLY_PROP_PRESENT, 54732833635SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 54832833635SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 54932833635SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 55032833635SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 55132833635SChris Lapa POWER_SUPPLY_PROP_TEMP, 55232833635SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 55332833635SChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 55432833635SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 55532833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 55632833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 55732833635SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 55832833635SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 55932833635SChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 56032833635SChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 56132833635SChris Lapa POWER_SUPPLY_PROP_HEALTH, 56232833635SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 56332833635SChris Lapa }; 56432833635SChris Lapa 565bd28177fSChris Lapa static enum power_supply_property bq27510g1_battery_props[] = { 566bd28177fSChris Lapa POWER_SUPPLY_PROP_STATUS, 567bd28177fSChris Lapa POWER_SUPPLY_PROP_PRESENT, 568bd28177fSChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 569bd28177fSChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 570bd28177fSChris Lapa POWER_SUPPLY_PROP_CAPACITY, 571bd28177fSChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 572bd28177fSChris Lapa POWER_SUPPLY_PROP_TEMP, 573bd28177fSChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 574bd28177fSChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 575bd28177fSChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 576bd28177fSChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 577bd28177fSChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 578bd28177fSChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 579bd28177fSChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 580bd28177fSChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 581bd28177fSChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 582bd28177fSChris Lapa POWER_SUPPLY_PROP_HEALTH, 583bd28177fSChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 584bd28177fSChris Lapa }; 585bd28177fSChris Lapa 586698a2bf5SChris Lapa static enum power_supply_property bq27510g2_battery_props[] = { 587698a2bf5SChris Lapa POWER_SUPPLY_PROP_STATUS, 588698a2bf5SChris Lapa POWER_SUPPLY_PROP_PRESENT, 589698a2bf5SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 590698a2bf5SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 591698a2bf5SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 592698a2bf5SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 593698a2bf5SChris Lapa POWER_SUPPLY_PROP_TEMP, 594698a2bf5SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 595698a2bf5SChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 596698a2bf5SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 597698a2bf5SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 598698a2bf5SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 599698a2bf5SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 600698a2bf5SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 601698a2bf5SChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 602698a2bf5SChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 603698a2bf5SChris Lapa POWER_SUPPLY_PROP_HEALTH, 604698a2bf5SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 605698a2bf5SChris Lapa }; 606698a2bf5SChris Lapa 60771375aa7SChris Lapa static enum power_supply_property bq27510g3_battery_props[] = { 60871375aa7SChris Lapa POWER_SUPPLY_PROP_STATUS, 60971375aa7SChris Lapa POWER_SUPPLY_PROP_PRESENT, 61071375aa7SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 61171375aa7SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 61271375aa7SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 61371375aa7SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 61471375aa7SChris Lapa POWER_SUPPLY_PROP_TEMP, 61571375aa7SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 61671375aa7SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 61771375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 61871375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 61971375aa7SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 62071375aa7SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 62171375aa7SChris Lapa POWER_SUPPLY_PROP_HEALTH, 62271375aa7SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 62371375aa7SChris Lapa }; 62471375aa7SChris Lapa 62568f2a813SChris Lapa static enum power_supply_property bq27520g1_battery_props[] = { 62668f2a813SChris Lapa POWER_SUPPLY_PROP_STATUS, 62768f2a813SChris Lapa POWER_SUPPLY_PROP_PRESENT, 62868f2a813SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 62968f2a813SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 63068f2a813SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 63168f2a813SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 63268f2a813SChris Lapa POWER_SUPPLY_PROP_TEMP, 63368f2a813SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 63468f2a813SChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 63568f2a813SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 63668f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 63768f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 63868f2a813SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 63968f2a813SChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 64068f2a813SChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 64168f2a813SChris Lapa POWER_SUPPLY_PROP_HEALTH, 64268f2a813SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 64368f2a813SChris Lapa }; 64468f2a813SChris Lapa 645a5deb9a9SChris Lapa static enum power_supply_property bq27520g2_battery_props[] = { 646a5deb9a9SChris Lapa POWER_SUPPLY_PROP_STATUS, 647a5deb9a9SChris Lapa POWER_SUPPLY_PROP_PRESENT, 648a5deb9a9SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 649a5deb9a9SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 650a5deb9a9SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 651a5deb9a9SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 652a5deb9a9SChris Lapa POWER_SUPPLY_PROP_TEMP, 653a5deb9a9SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 654a5deb9a9SChris Lapa POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 655a5deb9a9SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 656a5deb9a9SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 657a5deb9a9SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 658a5deb9a9SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 659a5deb9a9SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 660a5deb9a9SChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 661a5deb9a9SChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 662a5deb9a9SChris Lapa POWER_SUPPLY_PROP_HEALTH, 663a5deb9a9SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 664a5deb9a9SChris Lapa }; 665a5deb9a9SChris Lapa 666825e915bSChris Lapa static enum power_supply_property bq27520g3_battery_props[] = { 667825e915bSChris Lapa POWER_SUPPLY_PROP_STATUS, 668825e915bSChris Lapa POWER_SUPPLY_PROP_PRESENT, 669825e915bSChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 670825e915bSChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 671825e915bSChris Lapa POWER_SUPPLY_PROP_CAPACITY, 672825e915bSChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 673825e915bSChris Lapa POWER_SUPPLY_PROP_TEMP, 674825e915bSChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 675825e915bSChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 676825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 677825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 678825e915bSChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 679825e915bSChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 680825e915bSChris Lapa POWER_SUPPLY_PROP_ENERGY_NOW, 681825e915bSChris Lapa POWER_SUPPLY_PROP_POWER_AVG, 682825e915bSChris Lapa POWER_SUPPLY_PROP_HEALTH, 683825e915bSChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 684825e915bSChris Lapa }; 685825e915bSChris Lapa 6868835cae5SChris Lapa static enum power_supply_property bq27520g4_battery_props[] = { 6878835cae5SChris Lapa POWER_SUPPLY_PROP_STATUS, 6888835cae5SChris Lapa POWER_SUPPLY_PROP_PRESENT, 6898835cae5SChris Lapa POWER_SUPPLY_PROP_VOLTAGE_NOW, 6908835cae5SChris Lapa POWER_SUPPLY_PROP_CURRENT_NOW, 6918835cae5SChris Lapa POWER_SUPPLY_PROP_CAPACITY, 6928835cae5SChris Lapa POWER_SUPPLY_PROP_CAPACITY_LEVEL, 6938835cae5SChris Lapa POWER_SUPPLY_PROP_TEMP, 6948835cae5SChris Lapa POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 6958835cae5SChris Lapa POWER_SUPPLY_PROP_TECHNOLOGY, 6968835cae5SChris Lapa POWER_SUPPLY_PROP_CHARGE_FULL, 6978835cae5SChris Lapa POWER_SUPPLY_PROP_CHARGE_NOW, 6988835cae5SChris Lapa POWER_SUPPLY_PROP_CYCLE_COUNT, 6998835cae5SChris Lapa POWER_SUPPLY_PROP_HEALTH, 7008835cae5SChris Lapa POWER_SUPPLY_PROP_MANUFACTURER, 7018835cae5SChris Lapa }; 7028835cae5SChris Lapa 7038c0984e5SSebastian Reichel static enum power_supply_property bq27530_battery_props[] = { 7048c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 7058c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 7068c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 7078c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 7088c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 7098c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 7108c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 7118c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 7128c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 7138c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 7148c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 7158c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 7168c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 7178c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 7188c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 7198c0984e5SSebastian Reichel }; 7208c0984e5SSebastian Reichel 7218c0984e5SSebastian Reichel static enum power_supply_property bq27541_battery_props[] = { 7228c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 7238c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 7248c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 7258c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 7268c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 7278c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 7288c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 7298c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 7308c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 7318c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 7328c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 7338c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 7348c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 7358c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 7368c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 7378c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 7388c0984e5SSebastian Reichel }; 7398c0984e5SSebastian Reichel 7408c0984e5SSebastian Reichel static enum power_supply_property bq27545_battery_props[] = { 7418c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 7428c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 7438c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 7448c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 7458c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 7468c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 7478c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 7488c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 7498c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 7508c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 7518c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 7528c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 7538c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CYCLE_COUNT, 7548c0984e5SSebastian Reichel POWER_SUPPLY_PROP_POWER_AVG, 7558c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 7568c0984e5SSebastian Reichel }; 7578c0984e5SSebastian Reichel 7588c0984e5SSebastian Reichel static enum power_supply_property bq27421_battery_props[] = { 7598c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 7608c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 7618c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 7628c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 7638c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 7648c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY_LEVEL, 7658c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 7668c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TECHNOLOGY, 7678c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 7688c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 7698c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 7708c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 7718c0984e5SSebastian Reichel }; 7728c0984e5SSebastian Reichel 7738c0984e5SSebastian Reichel #define BQ27XXX_PROP(_id, _prop) \ 7748c0984e5SSebastian Reichel [_id] = { \ 7758c0984e5SSebastian Reichel .props = _prop, \ 7768c0984e5SSebastian Reichel .size = ARRAY_SIZE(_prop), \ 7778c0984e5SSebastian Reichel } 7788c0984e5SSebastian Reichel 7798c0984e5SSebastian Reichel static struct { 7808c0984e5SSebastian Reichel enum power_supply_property *props; 7818c0984e5SSebastian Reichel size_t size; 7828c0984e5SSebastian Reichel } bq27xxx_battery_props[] = { 7838c0984e5SSebastian Reichel BQ27XXX_PROP(BQ27000, bq27000_battery_props), 7848c0984e5SSebastian Reichel BQ27XXX_PROP(BQ27010, bq27010_battery_props), 785818e3012SChris Lapa BQ27XXX_PROP(BQ2750X, bq2750x_battery_props), 7866da6e4bdSChris Lapa BQ27XXX_PROP(BQ2751X, bq2751x_battery_props), 78732833635SChris Lapa BQ27XXX_PROP(BQ27500, bq27500_battery_props), 788bd28177fSChris Lapa BQ27XXX_PROP(BQ27510G1, bq27510g1_battery_props), 789698a2bf5SChris Lapa BQ27XXX_PROP(BQ27510G2, bq27510g2_battery_props), 79071375aa7SChris Lapa BQ27XXX_PROP(BQ27510G3, bq27510g3_battery_props), 79168f2a813SChris Lapa BQ27XXX_PROP(BQ27520G1, bq27520g1_battery_props), 792a5deb9a9SChris Lapa BQ27XXX_PROP(BQ27520G2, bq27520g2_battery_props), 793825e915bSChris Lapa BQ27XXX_PROP(BQ27520G3, bq27520g3_battery_props), 7948835cae5SChris Lapa BQ27XXX_PROP(BQ27520G4, bq27520g4_battery_props), 7958c0984e5SSebastian Reichel BQ27XXX_PROP(BQ27530, bq27530_battery_props), 7968c0984e5SSebastian Reichel BQ27XXX_PROP(BQ27541, bq27541_battery_props), 7978c0984e5SSebastian Reichel BQ27XXX_PROP(BQ27545, bq27545_battery_props), 7988c0984e5SSebastian Reichel BQ27XXX_PROP(BQ27421, bq27421_battery_props), 7998c0984e5SSebastian Reichel }; 8008c0984e5SSebastian Reichel 8011d72706fSMatt Ranostay static DEFINE_MUTEX(bq27xxx_list_lock); 8021d72706fSMatt Ranostay static LIST_HEAD(bq27xxx_battery_devices); 8031d72706fSMatt Ranostay 8040670c9b3SLiam Breck #define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500) 8050670c9b3SLiam Breck 8060670c9b3SLiam Breck #define BQ27XXX_DM_SZ 32 8070670c9b3SLiam Breck 8080670c9b3SLiam Breck /** 8090670c9b3SLiam Breck * struct bq27xxx_dm_buf - chip data memory buffer 8100670c9b3SLiam Breck * @class: data memory subclass_id 8110670c9b3SLiam Breck * @block: data memory block number 8120670c9b3SLiam Breck * @data: data from/for the block 8130670c9b3SLiam Breck * @has_data: true if data has been filled by read 8140670c9b3SLiam Breck * @dirty: true if data has changed since last read/write 8150670c9b3SLiam Breck * 8160670c9b3SLiam Breck * Encapsulates info required to manage chip data memory blocks. 8170670c9b3SLiam Breck */ 8180670c9b3SLiam Breck struct bq27xxx_dm_buf { 8190670c9b3SLiam Breck u8 class; 8200670c9b3SLiam Breck u8 block; 8210670c9b3SLiam Breck u8 data[BQ27XXX_DM_SZ]; 8220670c9b3SLiam Breck bool has_data, dirty; 8230670c9b3SLiam Breck }; 8240670c9b3SLiam Breck 8250670c9b3SLiam Breck 8261d72706fSMatt Ranostay static int poll_interval_param_set(const char *val, const struct kernel_param *kp) 8271d72706fSMatt Ranostay { 8281d72706fSMatt Ranostay struct bq27xxx_device_info *di; 829950b6c2dSMatt Ranostay unsigned int prev_val = *(unsigned int *) kp->arg; 8301d72706fSMatt Ranostay int ret; 8311d72706fSMatt Ranostay 8321d72706fSMatt Ranostay ret = param_set_uint(val, kp); 833950b6c2dSMatt Ranostay if (ret < 0 || prev_val == *(unsigned int *) kp->arg) 8341d72706fSMatt Ranostay return ret; 8351d72706fSMatt Ranostay 8361d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 8371d72706fSMatt Ranostay list_for_each_entry(di, &bq27xxx_battery_devices, list) { 8381d72706fSMatt Ranostay cancel_delayed_work_sync(&di->work); 8391d72706fSMatt Ranostay schedule_delayed_work(&di->work, 0); 8401d72706fSMatt Ranostay } 8411d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 8421d72706fSMatt Ranostay 8431d72706fSMatt Ranostay return ret; 8441d72706fSMatt Ranostay } 8451d72706fSMatt Ranostay 8461d72706fSMatt Ranostay static const struct kernel_param_ops param_ops_poll_interval = { 8471d72706fSMatt Ranostay .get = param_get_uint, 8481d72706fSMatt Ranostay .set = poll_interval_param_set, 8491d72706fSMatt Ranostay }; 8501d72706fSMatt Ranostay 8518c0984e5SSebastian Reichel static unsigned int poll_interval = 360; 8521d72706fSMatt Ranostay module_param_cb(poll_interval, ¶m_ops_poll_interval, &poll_interval, 0644); 8538c0984e5SSebastian Reichel MODULE_PARM_DESC(poll_interval, 8548c0984e5SSebastian Reichel "battery poll interval in seconds - 0 disables polling"); 8558c0984e5SSebastian Reichel 8568c0984e5SSebastian Reichel /* 8578c0984e5SSebastian Reichel * Common code for BQ27xxx devices 8588c0984e5SSebastian Reichel */ 8598c0984e5SSebastian Reichel 8608c0984e5SSebastian Reichel static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, 8618c0984e5SSebastian Reichel bool single) 8628c0984e5SSebastian Reichel { 86314073f66SMatt Ranostay int ret; 86414073f66SMatt Ranostay 8658c0984e5SSebastian Reichel if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 8668c0984e5SSebastian Reichel return -EINVAL; 8678c0984e5SSebastian Reichel 86814073f66SMatt Ranostay ret = di->bus.read(di, di->regs[reg_index], single); 86914073f66SMatt Ranostay if (ret < 0) 87014073f66SMatt Ranostay dev_dbg(di->dev, "failed to read register 0x%02x (index %d)\n", 87114073f66SMatt Ranostay di->regs[reg_index], reg_index); 87214073f66SMatt Ranostay 87314073f66SMatt Ranostay return ret; 87414073f66SMatt Ranostay } 87514073f66SMatt Ranostay 87614073f66SMatt Ranostay static inline int bq27xxx_write(struct bq27xxx_device_info *di, int reg_index, 87714073f66SMatt Ranostay u16 value, bool single) 87814073f66SMatt Ranostay { 87914073f66SMatt Ranostay int ret; 88014073f66SMatt Ranostay 88114073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 88214073f66SMatt Ranostay return -EINVAL; 88314073f66SMatt Ranostay 88414073f66SMatt Ranostay if (!di->bus.write) 88514073f66SMatt Ranostay return -EPERM; 88614073f66SMatt Ranostay 88714073f66SMatt Ranostay ret = di->bus.write(di, di->regs[reg_index], value, single); 88814073f66SMatt Ranostay if (ret < 0) 88914073f66SMatt Ranostay dev_dbg(di->dev, "failed to write register 0x%02x (index %d)\n", 89014073f66SMatt Ranostay di->regs[reg_index], reg_index); 89114073f66SMatt Ranostay 89214073f66SMatt Ranostay return ret; 89314073f66SMatt Ranostay } 89414073f66SMatt Ranostay 89514073f66SMatt Ranostay static inline int bq27xxx_read_block(struct bq27xxx_device_info *di, int reg_index, 89614073f66SMatt Ranostay u8 *data, int len) 89714073f66SMatt Ranostay { 89814073f66SMatt Ranostay int ret; 89914073f66SMatt Ranostay 90014073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 90114073f66SMatt Ranostay return -EINVAL; 90214073f66SMatt Ranostay 90314073f66SMatt Ranostay if (!di->bus.read_bulk) 90414073f66SMatt Ranostay return -EPERM; 90514073f66SMatt Ranostay 90614073f66SMatt Ranostay ret = di->bus.read_bulk(di, di->regs[reg_index], data, len); 90714073f66SMatt Ranostay if (ret < 0) 90814073f66SMatt Ranostay dev_dbg(di->dev, "failed to read_bulk register 0x%02x (index %d)\n", 90914073f66SMatt Ranostay di->regs[reg_index], reg_index); 91014073f66SMatt Ranostay 91114073f66SMatt Ranostay return ret; 91214073f66SMatt Ranostay } 91314073f66SMatt Ranostay 91414073f66SMatt Ranostay static inline int bq27xxx_write_block(struct bq27xxx_device_info *di, int reg_index, 91514073f66SMatt Ranostay u8 *data, int len) 91614073f66SMatt Ranostay { 91714073f66SMatt Ranostay int ret; 91814073f66SMatt Ranostay 91914073f66SMatt Ranostay if (!di || di->regs[reg_index] == INVALID_REG_ADDR) 92014073f66SMatt Ranostay return -EINVAL; 92114073f66SMatt Ranostay 92214073f66SMatt Ranostay if (!di->bus.write_bulk) 92314073f66SMatt Ranostay return -EPERM; 92414073f66SMatt Ranostay 92514073f66SMatt Ranostay ret = di->bus.write_bulk(di, di->regs[reg_index], data, len); 92614073f66SMatt Ranostay if (ret < 0) 92714073f66SMatt Ranostay dev_dbg(di->dev, "failed to write_bulk register 0x%02x (index %d)\n", 92814073f66SMatt Ranostay di->regs[reg_index], reg_index); 92914073f66SMatt Ranostay 93014073f66SMatt Ranostay return ret; 9318c0984e5SSebastian Reichel } 9328c0984e5SSebastian Reichel 9330670c9b3SLiam Breck static int bq27xxx_battery_seal(struct bq27xxx_device_info *di) 9340670c9b3SLiam Breck { 9350670c9b3SLiam Breck int ret; 9360670c9b3SLiam Breck 9370670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_SEALED, false); 9380670c9b3SLiam Breck if (ret < 0) { 9390670c9b3SLiam Breck dev_err(di->dev, "bus error on seal: %d\n", ret); 9400670c9b3SLiam Breck return ret; 9410670c9b3SLiam Breck } 9420670c9b3SLiam Breck 9430670c9b3SLiam Breck return 0; 9440670c9b3SLiam Breck } 9450670c9b3SLiam Breck 9460670c9b3SLiam Breck static int bq27xxx_battery_unseal(struct bq27xxx_device_info *di) 9470670c9b3SLiam Breck { 9480670c9b3SLiam Breck int ret; 9490670c9b3SLiam Breck 9500670c9b3SLiam Breck if (di->unseal_key == 0) { 9510670c9b3SLiam Breck dev_err(di->dev, "unseal failed due to missing key\n"); 9520670c9b3SLiam Breck return -EINVAL; 9530670c9b3SLiam Breck } 9540670c9b3SLiam Breck 9550670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, (u16)(di->unseal_key >> 16), false); 9560670c9b3SLiam Breck if (ret < 0) 9570670c9b3SLiam Breck goto out; 9580670c9b3SLiam Breck 9590670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, (u16)di->unseal_key, false); 9600670c9b3SLiam Breck if (ret < 0) 9610670c9b3SLiam Breck goto out; 9620670c9b3SLiam Breck 9630670c9b3SLiam Breck return 0; 9640670c9b3SLiam Breck 9650670c9b3SLiam Breck out: 9660670c9b3SLiam Breck dev_err(di->dev, "bus error on unseal: %d\n", ret); 9670670c9b3SLiam Breck return ret; 9680670c9b3SLiam Breck } 9690670c9b3SLiam Breck 9700670c9b3SLiam Breck static u8 bq27xxx_battery_checksum_dm_block(struct bq27xxx_dm_buf *buf) 9710670c9b3SLiam Breck { 9720670c9b3SLiam Breck u16 sum = 0; 9730670c9b3SLiam Breck int i; 9740670c9b3SLiam Breck 9750670c9b3SLiam Breck for (i = 0; i < BQ27XXX_DM_SZ; i++) 9760670c9b3SLiam Breck sum += buf->data[i]; 9770670c9b3SLiam Breck sum &= 0xff; 9780670c9b3SLiam Breck 9790670c9b3SLiam Breck return 0xff - sum; 9800670c9b3SLiam Breck } 9810670c9b3SLiam Breck 9820670c9b3SLiam Breck static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di, 9830670c9b3SLiam Breck struct bq27xxx_dm_buf *buf) 9840670c9b3SLiam Breck { 9850670c9b3SLiam Breck int ret; 9860670c9b3SLiam Breck 9870670c9b3SLiam Breck buf->has_data = false; 9880670c9b3SLiam Breck 9890670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CLASS, buf->class, true); 9900670c9b3SLiam Breck if (ret < 0) 9910670c9b3SLiam Breck goto out; 9920670c9b3SLiam Breck 9930670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_BLOCK, buf->block, true); 9940670c9b3SLiam Breck if (ret < 0) 9950670c9b3SLiam Breck goto out; 9960670c9b3SLiam Breck 9970670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 9980670c9b3SLiam Breck 9990670c9b3SLiam Breck ret = bq27xxx_read_block(di, BQ27XXX_DM_DATA, buf->data, BQ27XXX_DM_SZ); 10000670c9b3SLiam Breck if (ret < 0) 10010670c9b3SLiam Breck goto out; 10020670c9b3SLiam Breck 10030670c9b3SLiam Breck ret = bq27xxx_read(di, BQ27XXX_DM_CKSUM, true); 10040670c9b3SLiam Breck if (ret < 0) 10050670c9b3SLiam Breck goto out; 10060670c9b3SLiam Breck 10070670c9b3SLiam Breck if ((u8)ret != bq27xxx_battery_checksum_dm_block(buf)) { 10080670c9b3SLiam Breck ret = -EINVAL; 10090670c9b3SLiam Breck goto out; 10100670c9b3SLiam Breck } 10110670c9b3SLiam Breck 10120670c9b3SLiam Breck buf->has_data = true; 10130670c9b3SLiam Breck buf->dirty = false; 10140670c9b3SLiam Breck 10150670c9b3SLiam Breck return 0; 10160670c9b3SLiam Breck 10170670c9b3SLiam Breck out: 10180670c9b3SLiam Breck dev_err(di->dev, "bus error reading chip memory: %d\n", ret); 10190670c9b3SLiam Breck return ret; 10200670c9b3SLiam Breck } 10210670c9b3SLiam Breck 10220670c9b3SLiam Breck static int bq27xxx_battery_cfgupdate_priv(struct bq27xxx_device_info *di, bool active) 10230670c9b3SLiam Breck { 10240670c9b3SLiam Breck const int limit = 100; 10250670c9b3SLiam Breck u16 cmd = active ? BQ27XXX_SET_CFGUPDATE : BQ27XXX_SOFT_RESET; 10260670c9b3SLiam Breck int ret, try = limit; 10270670c9b3SLiam Breck 10280670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, cmd, false); 10290670c9b3SLiam Breck if (ret < 0) 10300670c9b3SLiam Breck return ret; 10310670c9b3SLiam Breck 10320670c9b3SLiam Breck do { 10330670c9b3SLiam Breck BQ27XXX_MSLEEP(25); 10340670c9b3SLiam Breck ret = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false); 10350670c9b3SLiam Breck if (ret < 0) 10360670c9b3SLiam Breck return ret; 10370670c9b3SLiam Breck } while (!!(ret & BQ27XXX_FLAG_CFGUP) != active && --try); 10380670c9b3SLiam Breck 10390670c9b3SLiam Breck if (!try) { 10400670c9b3SLiam Breck dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", active); 10410670c9b3SLiam Breck return -EINVAL; 10420670c9b3SLiam Breck } 10430670c9b3SLiam Breck 10440670c9b3SLiam Breck if (limit - try > 3) 10450670c9b3SLiam Breck dev_warn(di->dev, "cfgupdate %d, retries %d\n", active, limit - try); 10460670c9b3SLiam Breck 10470670c9b3SLiam Breck return 0; 10480670c9b3SLiam Breck } 10490670c9b3SLiam Breck 10500670c9b3SLiam Breck static inline int bq27xxx_battery_set_cfgupdate(struct bq27xxx_device_info *di) 10510670c9b3SLiam Breck { 10520670c9b3SLiam Breck int ret = bq27xxx_battery_cfgupdate_priv(di, true); 10530670c9b3SLiam Breck if (ret < 0 && ret != -EINVAL) 10540670c9b3SLiam Breck dev_err(di->dev, "bus error on set_cfgupdate: %d\n", ret); 10550670c9b3SLiam Breck 10560670c9b3SLiam Breck return ret; 10570670c9b3SLiam Breck } 10580670c9b3SLiam Breck 10590670c9b3SLiam Breck static inline int bq27xxx_battery_soft_reset(struct bq27xxx_device_info *di) 10600670c9b3SLiam Breck { 10610670c9b3SLiam Breck int ret = bq27xxx_battery_cfgupdate_priv(di, false); 10620670c9b3SLiam Breck if (ret < 0 && ret != -EINVAL) 10630670c9b3SLiam Breck dev_err(di->dev, "bus error on soft_reset: %d\n", ret); 10640670c9b3SLiam Breck 10650670c9b3SLiam Breck return ret; 10660670c9b3SLiam Breck } 10670670c9b3SLiam Breck 10680670c9b3SLiam Breck static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di, 10690670c9b3SLiam Breck struct bq27xxx_dm_buf *buf) 10700670c9b3SLiam Breck { 10710670c9b3SLiam Breck bool cfgup = di->chip == BQ27421; /* assume related chips need cfgupdate */ 10720670c9b3SLiam Breck int ret; 10730670c9b3SLiam Breck 10740670c9b3SLiam Breck if (!buf->dirty) 10750670c9b3SLiam Breck return 0; 10760670c9b3SLiam Breck 10770670c9b3SLiam Breck if (cfgup) { 10780670c9b3SLiam Breck ret = bq27xxx_battery_set_cfgupdate(di); 10790670c9b3SLiam Breck if (ret < 0) 10800670c9b3SLiam Breck return ret; 10810670c9b3SLiam Breck } 10820670c9b3SLiam Breck 10830670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CTRL, 0, true); 10840670c9b3SLiam Breck if (ret < 0) 10850670c9b3SLiam Breck goto out; 10860670c9b3SLiam Breck 10870670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CLASS, buf->class, true); 10880670c9b3SLiam Breck if (ret < 0) 10890670c9b3SLiam Breck goto out; 10900670c9b3SLiam Breck 10910670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_BLOCK, buf->block, true); 10920670c9b3SLiam Breck if (ret < 0) 10930670c9b3SLiam Breck goto out; 10940670c9b3SLiam Breck 10950670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 10960670c9b3SLiam Breck 10970670c9b3SLiam Breck ret = bq27xxx_write_block(di, BQ27XXX_DM_DATA, buf->data, BQ27XXX_DM_SZ); 10980670c9b3SLiam Breck if (ret < 0) 10990670c9b3SLiam Breck goto out; 11000670c9b3SLiam Breck 11010670c9b3SLiam Breck ret = bq27xxx_write(di, BQ27XXX_DM_CKSUM, 11020670c9b3SLiam Breck bq27xxx_battery_checksum_dm_block(buf), true); 11030670c9b3SLiam Breck if (ret < 0) 11040670c9b3SLiam Breck goto out; 11050670c9b3SLiam Breck 11060670c9b3SLiam Breck /* DO NOT read BQ27XXX_DM_CKSUM here to verify it! That may cause NVM 11070670c9b3SLiam Breck * corruption on the '425 chip (and perhaps others), which can damage 11080670c9b3SLiam Breck * the chip. 11090670c9b3SLiam Breck */ 11100670c9b3SLiam Breck 11110670c9b3SLiam Breck if (cfgup) { 11120670c9b3SLiam Breck BQ27XXX_MSLEEP(1); 11130670c9b3SLiam Breck ret = bq27xxx_battery_soft_reset(di); 11140670c9b3SLiam Breck if (ret < 0) 11150670c9b3SLiam Breck return ret; 11160670c9b3SLiam Breck } else { 11170670c9b3SLiam Breck BQ27XXX_MSLEEP(100); /* flash DM updates in <100ms */ 11180670c9b3SLiam Breck } 11190670c9b3SLiam Breck 11200670c9b3SLiam Breck buf->dirty = false; 11210670c9b3SLiam Breck 11220670c9b3SLiam Breck return 0; 11230670c9b3SLiam Breck 11240670c9b3SLiam Breck out: 11250670c9b3SLiam Breck if (cfgup) 11260670c9b3SLiam Breck bq27xxx_battery_soft_reset(di); 11270670c9b3SLiam Breck 11280670c9b3SLiam Breck dev_err(di->dev, "bus error writing chip memory: %d\n", ret); 11290670c9b3SLiam Breck return ret; 11300670c9b3SLiam Breck } 11310670c9b3SLiam Breck 11328c0984e5SSebastian Reichel /* 11338c0984e5SSebastian Reichel * Return the battery State-of-Charge 11348c0984e5SSebastian Reichel * Or < 0 if something fails. 11358c0984e5SSebastian Reichel */ 11368c0984e5SSebastian Reichel static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di) 11378c0984e5SSebastian Reichel { 11388c0984e5SSebastian Reichel int soc; 11398c0984e5SSebastian Reichel 11408c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) 11418c0984e5SSebastian Reichel soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true); 11428c0984e5SSebastian Reichel else 11438c0984e5SSebastian Reichel soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false); 11448c0984e5SSebastian Reichel 11458c0984e5SSebastian Reichel if (soc < 0) 11468c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading State-of-Charge\n"); 11478c0984e5SSebastian Reichel 11488c0984e5SSebastian Reichel return soc; 11498c0984e5SSebastian Reichel } 11508c0984e5SSebastian Reichel 11518c0984e5SSebastian Reichel /* 11528c0984e5SSebastian Reichel * Return a battery charge value in µAh 11538c0984e5SSebastian Reichel * Or < 0 if something fails. 11548c0984e5SSebastian Reichel */ 11558c0984e5SSebastian Reichel static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg) 11568c0984e5SSebastian Reichel { 11578c0984e5SSebastian Reichel int charge; 11588c0984e5SSebastian Reichel 11598c0984e5SSebastian Reichel charge = bq27xxx_read(di, reg, false); 11608c0984e5SSebastian Reichel if (charge < 0) { 11618c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading charge register %02x: %d\n", 11628c0984e5SSebastian Reichel reg, charge); 11638c0984e5SSebastian Reichel return charge; 11648c0984e5SSebastian Reichel } 11658c0984e5SSebastian Reichel 11668c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) 11678c0984e5SSebastian Reichel charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 11688c0984e5SSebastian Reichel else 11698c0984e5SSebastian Reichel charge *= 1000; 11708c0984e5SSebastian Reichel 11718c0984e5SSebastian Reichel return charge; 11728c0984e5SSebastian Reichel } 11738c0984e5SSebastian Reichel 11748c0984e5SSebastian Reichel /* 11758c0984e5SSebastian Reichel * Return the battery Nominal available capacity in µAh 11768c0984e5SSebastian Reichel * Or < 0 if something fails. 11778c0984e5SSebastian Reichel */ 11788c0984e5SSebastian Reichel static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di) 11798c0984e5SSebastian Reichel { 11808c0984e5SSebastian Reichel int flags; 11818c0984e5SSebastian Reichel 11828c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) { 11838c0984e5SSebastian Reichel flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); 11848c0984e5SSebastian Reichel if (flags >= 0 && (flags & BQ27000_FLAG_CI)) 11858c0984e5SSebastian Reichel return -ENODATA; 11868c0984e5SSebastian Reichel } 11878c0984e5SSebastian Reichel 11888c0984e5SSebastian Reichel return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC); 11898c0984e5SSebastian Reichel } 11908c0984e5SSebastian Reichel 11918c0984e5SSebastian Reichel /* 11928c0984e5SSebastian Reichel * Return the battery Full Charge Capacity in µAh 11938c0984e5SSebastian Reichel * Or < 0 if something fails. 11948c0984e5SSebastian Reichel */ 11958c0984e5SSebastian Reichel static inline int bq27xxx_battery_read_fcc(struct bq27xxx_device_info *di) 11968c0984e5SSebastian Reichel { 11978c0984e5SSebastian Reichel return bq27xxx_battery_read_charge(di, BQ27XXX_REG_FCC); 11988c0984e5SSebastian Reichel } 11998c0984e5SSebastian Reichel 12008c0984e5SSebastian Reichel /* 12018c0984e5SSebastian Reichel * Return the Design Capacity in µAh 12028c0984e5SSebastian Reichel * Or < 0 if something fails. 12038c0984e5SSebastian Reichel */ 12048c0984e5SSebastian Reichel static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di) 12058c0984e5SSebastian Reichel { 12068c0984e5SSebastian Reichel int dcap; 12078c0984e5SSebastian Reichel 12088c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) 12098c0984e5SSebastian Reichel dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true); 12108c0984e5SSebastian Reichel else 12118c0984e5SSebastian Reichel dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false); 12128c0984e5SSebastian Reichel 12138c0984e5SSebastian Reichel if (dcap < 0) { 12148c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading initial last measured discharge\n"); 12158c0984e5SSebastian Reichel return dcap; 12168c0984e5SSebastian Reichel } 12178c0984e5SSebastian Reichel 12188c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) 12198c0984e5SSebastian Reichel dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 12208c0984e5SSebastian Reichel else 12218c0984e5SSebastian Reichel dcap *= 1000; 12228c0984e5SSebastian Reichel 12238c0984e5SSebastian Reichel return dcap; 12248c0984e5SSebastian Reichel } 12258c0984e5SSebastian Reichel 12268c0984e5SSebastian Reichel /* 12278c0984e5SSebastian Reichel * Return the battery Available energy in µWh 12288c0984e5SSebastian Reichel * Or < 0 if something fails. 12298c0984e5SSebastian Reichel */ 12308c0984e5SSebastian Reichel static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di) 12318c0984e5SSebastian Reichel { 12328c0984e5SSebastian Reichel int ae; 12338c0984e5SSebastian Reichel 12348c0984e5SSebastian Reichel ae = bq27xxx_read(di, BQ27XXX_REG_AE, false); 12358c0984e5SSebastian Reichel if (ae < 0) { 12368c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading available energy\n"); 12378c0984e5SSebastian Reichel return ae; 12388c0984e5SSebastian Reichel } 12398c0984e5SSebastian Reichel 12408c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) 12418c0984e5SSebastian Reichel ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS; 12428c0984e5SSebastian Reichel else 12438c0984e5SSebastian Reichel ae *= 1000; 12448c0984e5SSebastian Reichel 12458c0984e5SSebastian Reichel return ae; 12468c0984e5SSebastian Reichel } 12478c0984e5SSebastian Reichel 12488c0984e5SSebastian Reichel /* 12498c0984e5SSebastian Reichel * Return the battery temperature in tenths of degree Kelvin 12508c0984e5SSebastian Reichel * Or < 0 if something fails. 12518c0984e5SSebastian Reichel */ 12528c0984e5SSebastian Reichel static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di) 12538c0984e5SSebastian Reichel { 12548c0984e5SSebastian Reichel int temp; 12558c0984e5SSebastian Reichel 12568c0984e5SSebastian Reichel temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false); 12578c0984e5SSebastian Reichel if (temp < 0) { 12588c0984e5SSebastian Reichel dev_err(di->dev, "error reading temperature\n"); 12598c0984e5SSebastian Reichel return temp; 12608c0984e5SSebastian Reichel } 12618c0984e5SSebastian Reichel 12628c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) 12638c0984e5SSebastian Reichel temp = 5 * temp / 2; 12648c0984e5SSebastian Reichel 12658c0984e5SSebastian Reichel return temp; 12668c0984e5SSebastian Reichel } 12678c0984e5SSebastian Reichel 12688c0984e5SSebastian Reichel /* 12698c0984e5SSebastian Reichel * Return the battery Cycle count total 12708c0984e5SSebastian Reichel * Or < 0 if something fails. 12718c0984e5SSebastian Reichel */ 12728c0984e5SSebastian Reichel static int bq27xxx_battery_read_cyct(struct bq27xxx_device_info *di) 12738c0984e5SSebastian Reichel { 12748c0984e5SSebastian Reichel int cyct; 12758c0984e5SSebastian Reichel 12768c0984e5SSebastian Reichel cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false); 12778c0984e5SSebastian Reichel if (cyct < 0) 12788c0984e5SSebastian Reichel dev_err(di->dev, "error reading cycle count total\n"); 12798c0984e5SSebastian Reichel 12808c0984e5SSebastian Reichel return cyct; 12818c0984e5SSebastian Reichel } 12828c0984e5SSebastian Reichel 12838c0984e5SSebastian Reichel /* 12848c0984e5SSebastian Reichel * Read a time register. 12858c0984e5SSebastian Reichel * Return < 0 if something fails. 12868c0984e5SSebastian Reichel */ 12878c0984e5SSebastian Reichel static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg) 12888c0984e5SSebastian Reichel { 12898c0984e5SSebastian Reichel int tval; 12908c0984e5SSebastian Reichel 12918c0984e5SSebastian Reichel tval = bq27xxx_read(di, reg, false); 12928c0984e5SSebastian Reichel if (tval < 0) { 12938c0984e5SSebastian Reichel dev_dbg(di->dev, "error reading time register %02x: %d\n", 12948c0984e5SSebastian Reichel reg, tval); 12958c0984e5SSebastian Reichel return tval; 12968c0984e5SSebastian Reichel } 12978c0984e5SSebastian Reichel 12988c0984e5SSebastian Reichel if (tval == 65535) 12998c0984e5SSebastian Reichel return -ENODATA; 13008c0984e5SSebastian Reichel 13018c0984e5SSebastian Reichel return tval * 60; 13028c0984e5SSebastian Reichel } 13038c0984e5SSebastian Reichel 13048c0984e5SSebastian Reichel /* 13058c0984e5SSebastian Reichel * Read an average power register. 13068c0984e5SSebastian Reichel * Return < 0 if something fails. 13078c0984e5SSebastian Reichel */ 13088c0984e5SSebastian Reichel static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di) 13098c0984e5SSebastian Reichel { 13108c0984e5SSebastian Reichel int tval; 13118c0984e5SSebastian Reichel 13128c0984e5SSebastian Reichel tval = bq27xxx_read(di, BQ27XXX_REG_AP, false); 13138c0984e5SSebastian Reichel if (tval < 0) { 13148c0984e5SSebastian Reichel dev_err(di->dev, "error reading average power register %02x: %d\n", 13158c0984e5SSebastian Reichel BQ27XXX_REG_AP, tval); 13168c0984e5SSebastian Reichel return tval; 13178c0984e5SSebastian Reichel } 13188c0984e5SSebastian Reichel 13198c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) 13208c0984e5SSebastian Reichel return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS; 13218c0984e5SSebastian Reichel else 13228c0984e5SSebastian Reichel return tval; 13238c0984e5SSebastian Reichel } 13248c0984e5SSebastian Reichel 13258c0984e5SSebastian Reichel /* 13268c0984e5SSebastian Reichel * Returns true if a battery over temperature condition is detected 13278c0984e5SSebastian Reichel */ 13288c0984e5SSebastian Reichel static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags) 13298c0984e5SSebastian Reichel { 1330e839a448SChris Lapa switch (di->chip) { 1331818e3012SChris Lapa case BQ2750X: 13326da6e4bdSChris Lapa case BQ2751X: 133332833635SChris Lapa case BQ27500: 1334bd28177fSChris Lapa case BQ27510G1: 1335698a2bf5SChris Lapa case BQ27510G2: 133671375aa7SChris Lapa case BQ27510G3: 133768f2a813SChris Lapa case BQ27520G1: 1338a5deb9a9SChris Lapa case BQ27520G2: 1339825e915bSChris Lapa case BQ27520G3: 13408835cae5SChris Lapa case BQ27520G4: 1341e839a448SChris Lapa case BQ27541: 1342e839a448SChris Lapa case BQ27545: 13438c0984e5SSebastian Reichel return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD); 1344e839a448SChris Lapa case BQ27530: 1345e839a448SChris Lapa case BQ27421: 13468c0984e5SSebastian Reichel return flags & BQ27XXX_FLAG_OT; 1347e839a448SChris Lapa default: 13488c0984e5SSebastian Reichel return false; 13498c0984e5SSebastian Reichel } 1350e839a448SChris Lapa } 13518c0984e5SSebastian Reichel 13528c0984e5SSebastian Reichel /* 13538c0984e5SSebastian Reichel * Returns true if a battery under temperature condition is detected 13548c0984e5SSebastian Reichel */ 13558c0984e5SSebastian Reichel static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags) 13568c0984e5SSebastian Reichel { 13578c0984e5SSebastian Reichel if (di->chip == BQ27530 || di->chip == BQ27421) 13588c0984e5SSebastian Reichel return flags & BQ27XXX_FLAG_UT; 13598c0984e5SSebastian Reichel 13608c0984e5SSebastian Reichel return false; 13618c0984e5SSebastian Reichel } 13628c0984e5SSebastian Reichel 13638c0984e5SSebastian Reichel /* 13648c0984e5SSebastian Reichel * Returns true if a low state of charge condition is detected 13658c0984e5SSebastian Reichel */ 13668c0984e5SSebastian Reichel static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags) 13678c0984e5SSebastian Reichel { 13688c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) 13698c0984e5SSebastian Reichel return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF); 13708c0984e5SSebastian Reichel else 13718c0984e5SSebastian Reichel return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF); 13728c0984e5SSebastian Reichel } 13738c0984e5SSebastian Reichel 13748c0984e5SSebastian Reichel /* 13758c0984e5SSebastian Reichel * Read flag register. 13768c0984e5SSebastian Reichel * Return < 0 if something fails. 13778c0984e5SSebastian Reichel */ 13788c0984e5SSebastian Reichel static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) 13798c0984e5SSebastian Reichel { 13808c0984e5SSebastian Reichel int flags; 1381e4a404a0SH. Nikolaus Schaller bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010; 13828c0984e5SSebastian Reichel 1383e4a404a0SH. Nikolaus Schaller flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); 13848c0984e5SSebastian Reichel if (flags < 0) { 13858c0984e5SSebastian Reichel dev_err(di->dev, "error reading flag register:%d\n", flags); 13868c0984e5SSebastian Reichel return flags; 13878c0984e5SSebastian Reichel } 13888c0984e5SSebastian Reichel 13898c0984e5SSebastian Reichel /* Unlikely but important to return first */ 13908c0984e5SSebastian Reichel if (unlikely(bq27xxx_battery_overtemp(di, flags))) 13918c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_OVERHEAT; 13928c0984e5SSebastian Reichel if (unlikely(bq27xxx_battery_undertemp(di, flags))) 13938c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_COLD; 13948c0984e5SSebastian Reichel if (unlikely(bq27xxx_battery_dead(di, flags))) 13958c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_DEAD; 13968c0984e5SSebastian Reichel 13978c0984e5SSebastian Reichel return POWER_SUPPLY_HEALTH_GOOD; 13988c0984e5SSebastian Reichel } 13998c0984e5SSebastian Reichel 14008c0984e5SSebastian Reichel void bq27xxx_battery_update(struct bq27xxx_device_info *di) 14018c0984e5SSebastian Reichel { 14028c0984e5SSebastian Reichel struct bq27xxx_reg_cache cache = {0, }; 14038c0984e5SSebastian Reichel bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010; 14048c0984e5SSebastian Reichel bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010; 14058c0984e5SSebastian Reichel 14068c0984e5SSebastian Reichel cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); 14078c0984e5SSebastian Reichel if ((cache.flags & 0xff) == 0xff) 14088c0984e5SSebastian Reichel cache.flags = -1; /* read error */ 14098c0984e5SSebastian Reichel if (cache.flags >= 0) { 14108c0984e5SSebastian Reichel cache.temperature = bq27xxx_battery_read_temperature(di); 14118c0984e5SSebastian Reichel if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) { 14128c0984e5SSebastian Reichel dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n"); 14138c0984e5SSebastian Reichel cache.capacity = -ENODATA; 14148c0984e5SSebastian Reichel cache.energy = -ENODATA; 14158c0984e5SSebastian Reichel cache.time_to_empty = -ENODATA; 14168c0984e5SSebastian Reichel cache.time_to_empty_avg = -ENODATA; 14178c0984e5SSebastian Reichel cache.time_to_full = -ENODATA; 14188c0984e5SSebastian Reichel cache.charge_full = -ENODATA; 14198c0984e5SSebastian Reichel cache.health = -ENODATA; 14208c0984e5SSebastian Reichel } else { 14218c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR) 14228c0984e5SSebastian Reichel cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE); 14238c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR) 14248c0984e5SSebastian Reichel cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP); 14258c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR) 14268c0984e5SSebastian Reichel cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF); 14278c0984e5SSebastian Reichel cache.charge_full = bq27xxx_battery_read_fcc(di); 14288c0984e5SSebastian Reichel cache.capacity = bq27xxx_battery_read_soc(di); 14298c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR) 14308c0984e5SSebastian Reichel cache.energy = bq27xxx_battery_read_energy(di); 14318c0984e5SSebastian Reichel cache.health = bq27xxx_battery_read_health(di); 14328c0984e5SSebastian Reichel } 14338c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR) 14348c0984e5SSebastian Reichel cache.cycle_count = bq27xxx_battery_read_cyct(di); 14358c0984e5SSebastian Reichel if (di->regs[BQ27XXX_REG_AP] != INVALID_REG_ADDR) 14368c0984e5SSebastian Reichel cache.power_avg = bq27xxx_battery_read_pwr_avg(di); 14378c0984e5SSebastian Reichel 14388c0984e5SSebastian Reichel /* We only have to read charge design full once */ 14398c0984e5SSebastian Reichel if (di->charge_design_full <= 0) 14408c0984e5SSebastian Reichel di->charge_design_full = bq27xxx_battery_read_dcap(di); 14418c0984e5SSebastian Reichel } 14428c0984e5SSebastian Reichel 14438c0984e5SSebastian Reichel if (di->cache.capacity != cache.capacity) 14448c0984e5SSebastian Reichel power_supply_changed(di->bat); 14458c0984e5SSebastian Reichel 14468c0984e5SSebastian Reichel if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) 14478c0984e5SSebastian Reichel di->cache = cache; 14488c0984e5SSebastian Reichel 14498c0984e5SSebastian Reichel di->last_update = jiffies; 14508c0984e5SSebastian Reichel } 14518c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_update); 14528c0984e5SSebastian Reichel 14538c0984e5SSebastian Reichel static void bq27xxx_battery_poll(struct work_struct *work) 14548c0984e5SSebastian Reichel { 14558c0984e5SSebastian Reichel struct bq27xxx_device_info *di = 14568c0984e5SSebastian Reichel container_of(work, struct bq27xxx_device_info, 14578c0984e5SSebastian Reichel work.work); 14588c0984e5SSebastian Reichel 14598c0984e5SSebastian Reichel bq27xxx_battery_update(di); 14608c0984e5SSebastian Reichel 14618c0984e5SSebastian Reichel if (poll_interval > 0) 14628c0984e5SSebastian Reichel schedule_delayed_work(&di->work, poll_interval * HZ); 14638c0984e5SSebastian Reichel } 14648c0984e5SSebastian Reichel 14658c0984e5SSebastian Reichel /* 14668c0984e5SSebastian Reichel * Return the battery average current in µA 14678c0984e5SSebastian Reichel * Note that current can be negative signed as well 14688c0984e5SSebastian Reichel * Or 0 if something fails. 14698c0984e5SSebastian Reichel */ 14708c0984e5SSebastian Reichel static int bq27xxx_battery_current(struct bq27xxx_device_info *di, 14718c0984e5SSebastian Reichel union power_supply_propval *val) 14728c0984e5SSebastian Reichel { 14738c0984e5SSebastian Reichel int curr; 14748c0984e5SSebastian Reichel int flags; 14758c0984e5SSebastian Reichel 14768c0984e5SSebastian Reichel curr = bq27xxx_read(di, BQ27XXX_REG_AI, false); 14778c0984e5SSebastian Reichel if (curr < 0) { 14788c0984e5SSebastian Reichel dev_err(di->dev, "error reading current\n"); 14798c0984e5SSebastian Reichel return curr; 14808c0984e5SSebastian Reichel } 14818c0984e5SSebastian Reichel 14828c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) { 1483e4a404a0SH. Nikolaus Schaller flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); 14848c0984e5SSebastian Reichel if (flags & BQ27000_FLAG_CHGS) { 14858c0984e5SSebastian Reichel dev_dbg(di->dev, "negative current!\n"); 14868c0984e5SSebastian Reichel curr = -curr; 14878c0984e5SSebastian Reichel } 14888c0984e5SSebastian Reichel 14898c0984e5SSebastian Reichel val->intval = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS; 14908c0984e5SSebastian Reichel } else { 14918c0984e5SSebastian Reichel /* Other gauges return signed value */ 14928c0984e5SSebastian Reichel val->intval = (int)((s16)curr) * 1000; 14938c0984e5SSebastian Reichel } 14948c0984e5SSebastian Reichel 14958c0984e5SSebastian Reichel return 0; 14968c0984e5SSebastian Reichel } 14978c0984e5SSebastian Reichel 14988c0984e5SSebastian Reichel static int bq27xxx_battery_status(struct bq27xxx_device_info *di, 14998c0984e5SSebastian Reichel union power_supply_propval *val) 15008c0984e5SSebastian Reichel { 15018c0984e5SSebastian Reichel int status; 15028c0984e5SSebastian Reichel 15038c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) { 15048c0984e5SSebastian Reichel if (di->cache.flags & BQ27000_FLAG_FC) 15058c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_FULL; 15068c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_CHGS) 15078c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_CHARGING; 15088c0984e5SSebastian Reichel else if (power_supply_am_i_supplied(di->bat)) 15098c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_NOT_CHARGING; 15108c0984e5SSebastian Reichel else 15118c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_DISCHARGING; 15128c0984e5SSebastian Reichel } else { 15138c0984e5SSebastian Reichel if (di->cache.flags & BQ27XXX_FLAG_FC) 15148c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_FULL; 15158c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_DSC) 15168c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_DISCHARGING; 15178c0984e5SSebastian Reichel else 15188c0984e5SSebastian Reichel status = POWER_SUPPLY_STATUS_CHARGING; 15198c0984e5SSebastian Reichel } 15208c0984e5SSebastian Reichel 15218c0984e5SSebastian Reichel val->intval = status; 15228c0984e5SSebastian Reichel 15238c0984e5SSebastian Reichel return 0; 15248c0984e5SSebastian Reichel } 15258c0984e5SSebastian Reichel 15268c0984e5SSebastian Reichel static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di, 15278c0984e5SSebastian Reichel union power_supply_propval *val) 15288c0984e5SSebastian Reichel { 15298c0984e5SSebastian Reichel int level; 15308c0984e5SSebastian Reichel 15318c0984e5SSebastian Reichel if (di->chip == BQ27000 || di->chip == BQ27010) { 15328c0984e5SSebastian Reichel if (di->cache.flags & BQ27000_FLAG_FC) 15338c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 15348c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_EDV1) 15358c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 15368c0984e5SSebastian Reichel else if (di->cache.flags & BQ27000_FLAG_EDVF) 15378c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 15388c0984e5SSebastian Reichel else 15398c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 15408c0984e5SSebastian Reichel } else { 15418c0984e5SSebastian Reichel if (di->cache.flags & BQ27XXX_FLAG_FC) 15428c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 15438c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_SOC1) 15448c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 15458c0984e5SSebastian Reichel else if (di->cache.flags & BQ27XXX_FLAG_SOCF) 15468c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 15478c0984e5SSebastian Reichel else 15488c0984e5SSebastian Reichel level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 15498c0984e5SSebastian Reichel } 15508c0984e5SSebastian Reichel 15518c0984e5SSebastian Reichel val->intval = level; 15528c0984e5SSebastian Reichel 15538c0984e5SSebastian Reichel return 0; 15548c0984e5SSebastian Reichel } 15558c0984e5SSebastian Reichel 15568c0984e5SSebastian Reichel /* 15578c0984e5SSebastian Reichel * Return the battery Voltage in millivolts 15588c0984e5SSebastian Reichel * Or < 0 if something fails. 15598c0984e5SSebastian Reichel */ 15608c0984e5SSebastian Reichel static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di, 15618c0984e5SSebastian Reichel union power_supply_propval *val) 15628c0984e5SSebastian Reichel { 15638c0984e5SSebastian Reichel int volt; 15648c0984e5SSebastian Reichel 15658c0984e5SSebastian Reichel volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false); 15668c0984e5SSebastian Reichel if (volt < 0) { 15678c0984e5SSebastian Reichel dev_err(di->dev, "error reading voltage\n"); 15688c0984e5SSebastian Reichel return volt; 15698c0984e5SSebastian Reichel } 15708c0984e5SSebastian Reichel 15718c0984e5SSebastian Reichel val->intval = volt * 1000; 15728c0984e5SSebastian Reichel 15738c0984e5SSebastian Reichel return 0; 15748c0984e5SSebastian Reichel } 15758c0984e5SSebastian Reichel 15768c0984e5SSebastian Reichel static int bq27xxx_simple_value(int value, 15778c0984e5SSebastian Reichel union power_supply_propval *val) 15788c0984e5SSebastian Reichel { 15798c0984e5SSebastian Reichel if (value < 0) 15808c0984e5SSebastian Reichel return value; 15818c0984e5SSebastian Reichel 15828c0984e5SSebastian Reichel val->intval = value; 15838c0984e5SSebastian Reichel 15848c0984e5SSebastian Reichel return 0; 15858c0984e5SSebastian Reichel } 15868c0984e5SSebastian Reichel 15878c0984e5SSebastian Reichel static int bq27xxx_battery_get_property(struct power_supply *psy, 15888c0984e5SSebastian Reichel enum power_supply_property psp, 15898c0984e5SSebastian Reichel union power_supply_propval *val) 15908c0984e5SSebastian Reichel { 15918c0984e5SSebastian Reichel int ret = 0; 15928c0984e5SSebastian Reichel struct bq27xxx_device_info *di = power_supply_get_drvdata(psy); 15938c0984e5SSebastian Reichel 15948c0984e5SSebastian Reichel mutex_lock(&di->lock); 15958c0984e5SSebastian Reichel if (time_is_before_jiffies(di->last_update + 5 * HZ)) { 15968c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 15978c0984e5SSebastian Reichel bq27xxx_battery_poll(&di->work.work); 15988c0984e5SSebastian Reichel } 15998c0984e5SSebastian Reichel mutex_unlock(&di->lock); 16008c0984e5SSebastian Reichel 16018c0984e5SSebastian Reichel if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) 16028c0984e5SSebastian Reichel return -ENODEV; 16038c0984e5SSebastian Reichel 16048c0984e5SSebastian Reichel switch (psp) { 16058c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 16068c0984e5SSebastian Reichel ret = bq27xxx_battery_status(di, val); 16078c0984e5SSebastian Reichel break; 16088c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW: 16098c0984e5SSebastian Reichel ret = bq27xxx_battery_voltage(di, val); 16108c0984e5SSebastian Reichel break; 16118c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_PRESENT: 16128c0984e5SSebastian Reichel val->intval = di->cache.flags < 0 ? 0 : 1; 16138c0984e5SSebastian Reichel break; 16148c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CURRENT_NOW: 16158c0984e5SSebastian Reichel ret = bq27xxx_battery_current(di, val); 16168c0984e5SSebastian Reichel break; 16178c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY: 16188c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.capacity, val); 16198c0984e5SSebastian Reichel break; 16208c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 16218c0984e5SSebastian Reichel ret = bq27xxx_battery_capacity_level(di, val); 16228c0984e5SSebastian Reichel break; 16238c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TEMP: 16248c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.temperature, val); 16258c0984e5SSebastian Reichel if (ret == 0) 16268c0984e5SSebastian Reichel val->intval -= 2731; /* convert decidegree k to c */ 16278c0984e5SSebastian Reichel break; 16288c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: 16298c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_empty, val); 16308c0984e5SSebastian Reichel break; 16318c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: 16328c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_empty_avg, val); 16338c0984e5SSebastian Reichel break; 16348c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: 16358c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.time_to_full, val); 16368c0984e5SSebastian Reichel break; 16378c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TECHNOLOGY: 16388c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 16398c0984e5SSebastian Reichel break; 16408c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_NOW: 16418c0984e5SSebastian Reichel ret = bq27xxx_simple_value(bq27xxx_battery_read_nac(di), val); 16428c0984e5SSebastian Reichel break; 16438c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL: 16448c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.charge_full, val); 16458c0984e5SSebastian Reichel break; 16468c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 16478c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->charge_design_full, val); 16488c0984e5SSebastian Reichel break; 16498c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CYCLE_COUNT: 16508c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.cycle_count, val); 16518c0984e5SSebastian Reichel break; 16528c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_ENERGY_NOW: 16538c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.energy, val); 16548c0984e5SSebastian Reichel break; 16558c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_POWER_AVG: 16568c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.power_avg, val); 16578c0984e5SSebastian Reichel break; 16588c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH: 16598c0984e5SSebastian Reichel ret = bq27xxx_simple_value(di->cache.health, val); 16608c0984e5SSebastian Reichel break; 16618c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_MANUFACTURER: 16628c0984e5SSebastian Reichel val->strval = BQ27XXX_MANUFACTURER; 16638c0984e5SSebastian Reichel break; 16648c0984e5SSebastian Reichel default: 16658c0984e5SSebastian Reichel return -EINVAL; 16668c0984e5SSebastian Reichel } 16678c0984e5SSebastian Reichel 16688c0984e5SSebastian Reichel return ret; 16698c0984e5SSebastian Reichel } 16708c0984e5SSebastian Reichel 16718c0984e5SSebastian Reichel static void bq27xxx_external_power_changed(struct power_supply *psy) 16728c0984e5SSebastian Reichel { 16738c0984e5SSebastian Reichel struct bq27xxx_device_info *di = power_supply_get_drvdata(psy); 16748c0984e5SSebastian Reichel 16758c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 16768c0984e5SSebastian Reichel schedule_delayed_work(&di->work, 0); 16778c0984e5SSebastian Reichel } 16788c0984e5SSebastian Reichel 16798c0984e5SSebastian Reichel int bq27xxx_battery_setup(struct bq27xxx_device_info *di) 16808c0984e5SSebastian Reichel { 16818c0984e5SSebastian Reichel struct power_supply_desc *psy_desc; 16828c0984e5SSebastian Reichel struct power_supply_config psy_cfg = { .drv_data = di, }; 16838c0984e5SSebastian Reichel 16848c0984e5SSebastian Reichel INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll); 16858c0984e5SSebastian Reichel mutex_init(&di->lock); 16868c0984e5SSebastian Reichel di->regs = bq27xxx_regs[di->chip]; 16878c0984e5SSebastian Reichel 16888c0984e5SSebastian Reichel psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL); 16898c0984e5SSebastian Reichel if (!psy_desc) 16908c0984e5SSebastian Reichel return -ENOMEM; 16918c0984e5SSebastian Reichel 16928c0984e5SSebastian Reichel psy_desc->name = di->name; 16938c0984e5SSebastian Reichel psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; 16948c0984e5SSebastian Reichel psy_desc->properties = bq27xxx_battery_props[di->chip].props; 16958c0984e5SSebastian Reichel psy_desc->num_properties = bq27xxx_battery_props[di->chip].size; 16968c0984e5SSebastian Reichel psy_desc->get_property = bq27xxx_battery_get_property; 16978c0984e5SSebastian Reichel psy_desc->external_power_changed = bq27xxx_external_power_changed; 16988c0984e5SSebastian Reichel 16998c0984e5SSebastian Reichel di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg); 17008c0984e5SSebastian Reichel if (IS_ERR(di->bat)) { 17018c0984e5SSebastian Reichel dev_err(di->dev, "failed to register battery\n"); 17028c0984e5SSebastian Reichel return PTR_ERR(di->bat); 17038c0984e5SSebastian Reichel } 17048c0984e5SSebastian Reichel 17058c0984e5SSebastian Reichel dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION); 17068c0984e5SSebastian Reichel 17078c0984e5SSebastian Reichel bq27xxx_battery_update(di); 17088c0984e5SSebastian Reichel 17091d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 17101d72706fSMatt Ranostay list_add(&di->list, &bq27xxx_battery_devices); 17111d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 17121d72706fSMatt Ranostay 17138c0984e5SSebastian Reichel return 0; 17148c0984e5SSebastian Reichel } 17158c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_setup); 17168c0984e5SSebastian Reichel 17178c0984e5SSebastian Reichel void bq27xxx_battery_teardown(struct bq27xxx_device_info *di) 17188c0984e5SSebastian Reichel { 17198c0984e5SSebastian Reichel /* 17208c0984e5SSebastian Reichel * power_supply_unregister call bq27xxx_battery_get_property which 17218c0984e5SSebastian Reichel * call bq27xxx_battery_poll. 17228c0984e5SSebastian Reichel * Make sure that bq27xxx_battery_poll will not call 17238c0984e5SSebastian Reichel * schedule_delayed_work again after unregister (which cause OOPS). 17248c0984e5SSebastian Reichel */ 17258c0984e5SSebastian Reichel poll_interval = 0; 17268c0984e5SSebastian Reichel 17278c0984e5SSebastian Reichel cancel_delayed_work_sync(&di->work); 17288c0984e5SSebastian Reichel 17298c0984e5SSebastian Reichel power_supply_unregister(di->bat); 17308c0984e5SSebastian Reichel 17311d72706fSMatt Ranostay mutex_lock(&bq27xxx_list_lock); 17321d72706fSMatt Ranostay list_del(&di->list); 17331d72706fSMatt Ranostay mutex_unlock(&bq27xxx_list_lock); 17341d72706fSMatt Ranostay 17358c0984e5SSebastian Reichel mutex_destroy(&di->lock); 17368c0984e5SSebastian Reichel } 17378c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown); 17388c0984e5SSebastian Reichel 17398c0984e5SSebastian Reichel static int bq27xxx_battery_platform_read(struct bq27xxx_device_info *di, u8 reg, 17408c0984e5SSebastian Reichel bool single) 17418c0984e5SSebastian Reichel { 17428c0984e5SSebastian Reichel struct device *dev = di->dev; 17438c0984e5SSebastian Reichel struct bq27xxx_platform_data *pdata = dev->platform_data; 17448c0984e5SSebastian Reichel unsigned int timeout = 3; 17458c0984e5SSebastian Reichel int upper, lower; 17468c0984e5SSebastian Reichel int temp; 17478c0984e5SSebastian Reichel 17488c0984e5SSebastian Reichel if (!single) { 17498c0984e5SSebastian Reichel /* Make sure the value has not changed in between reading the 17508c0984e5SSebastian Reichel * lower and the upper part */ 17518c0984e5SSebastian Reichel upper = pdata->read(dev, reg + 1); 17528c0984e5SSebastian Reichel do { 17538c0984e5SSebastian Reichel temp = upper; 17548c0984e5SSebastian Reichel if (upper < 0) 17558c0984e5SSebastian Reichel return upper; 17568c0984e5SSebastian Reichel 17578c0984e5SSebastian Reichel lower = pdata->read(dev, reg); 17588c0984e5SSebastian Reichel if (lower < 0) 17598c0984e5SSebastian Reichel return lower; 17608c0984e5SSebastian Reichel 17618c0984e5SSebastian Reichel upper = pdata->read(dev, reg + 1); 17628c0984e5SSebastian Reichel } while (temp != upper && --timeout); 17638c0984e5SSebastian Reichel 17648c0984e5SSebastian Reichel if (timeout == 0) 17658c0984e5SSebastian Reichel return -EIO; 17668c0984e5SSebastian Reichel 17678c0984e5SSebastian Reichel return (upper << 8) | lower; 17688c0984e5SSebastian Reichel } 17698c0984e5SSebastian Reichel 17708c0984e5SSebastian Reichel return pdata->read(dev, reg); 17718c0984e5SSebastian Reichel } 17728c0984e5SSebastian Reichel 17738c0984e5SSebastian Reichel static int bq27xxx_battery_platform_probe(struct platform_device *pdev) 17748c0984e5SSebastian Reichel { 17758c0984e5SSebastian Reichel struct bq27xxx_device_info *di; 17768c0984e5SSebastian Reichel struct bq27xxx_platform_data *pdata = pdev->dev.platform_data; 17778c0984e5SSebastian Reichel 17788c0984e5SSebastian Reichel if (!pdata) { 17798c0984e5SSebastian Reichel dev_err(&pdev->dev, "no platform_data supplied\n"); 17808c0984e5SSebastian Reichel return -EINVAL; 17818c0984e5SSebastian Reichel } 17828c0984e5SSebastian Reichel 17838c0984e5SSebastian Reichel if (!pdata->read) { 17848c0984e5SSebastian Reichel dev_err(&pdev->dev, "no hdq read callback supplied\n"); 17858c0984e5SSebastian Reichel return -EINVAL; 17868c0984e5SSebastian Reichel } 17878c0984e5SSebastian Reichel 17888c0984e5SSebastian Reichel if (!pdata->chip) { 17898c0984e5SSebastian Reichel dev_err(&pdev->dev, "no device supplied\n"); 17908c0984e5SSebastian Reichel return -EINVAL; 17918c0984e5SSebastian Reichel } 17928c0984e5SSebastian Reichel 17938c0984e5SSebastian Reichel di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); 17948c0984e5SSebastian Reichel if (!di) 17958c0984e5SSebastian Reichel return -ENOMEM; 17968c0984e5SSebastian Reichel 17978c0984e5SSebastian Reichel platform_set_drvdata(pdev, di); 17988c0984e5SSebastian Reichel 17998c0984e5SSebastian Reichel di->dev = &pdev->dev; 18008c0984e5SSebastian Reichel di->chip = pdata->chip; 18018c0984e5SSebastian Reichel di->name = pdata->name ?: dev_name(&pdev->dev); 18028c0984e5SSebastian Reichel di->bus.read = bq27xxx_battery_platform_read; 18038c0984e5SSebastian Reichel 18048c0984e5SSebastian Reichel return bq27xxx_battery_setup(di); 18058c0984e5SSebastian Reichel } 18068c0984e5SSebastian Reichel 18078c0984e5SSebastian Reichel static int bq27xxx_battery_platform_remove(struct platform_device *pdev) 18088c0984e5SSebastian Reichel { 18098c0984e5SSebastian Reichel struct bq27xxx_device_info *di = platform_get_drvdata(pdev); 18108c0984e5SSebastian Reichel 18118c0984e5SSebastian Reichel bq27xxx_battery_teardown(di); 18128c0984e5SSebastian Reichel 18138c0984e5SSebastian Reichel return 0; 18148c0984e5SSebastian Reichel } 18158c0984e5SSebastian Reichel 18168c0984e5SSebastian Reichel static const struct platform_device_id bq27xxx_battery_platform_id_table[] = { 18178c0984e5SSebastian Reichel { "bq27000-battery", }, 18188c0984e5SSebastian Reichel { /* sentinel */ } 18198c0984e5SSebastian Reichel }; 18208c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(platform, bq27xxx_battery_platform_id_table); 18218c0984e5SSebastian Reichel 18228c0984e5SSebastian Reichel #ifdef CONFIG_OF 18238c0984e5SSebastian Reichel static const struct of_device_id bq27xxx_battery_platform_of_match_table[] = { 18248c0984e5SSebastian Reichel { .compatible = "ti,bq27000" }, 18258c0984e5SSebastian Reichel {}, 18268c0984e5SSebastian Reichel }; 18278c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, bq27xxx_battery_platform_of_match_table); 18288c0984e5SSebastian Reichel #endif 18298c0984e5SSebastian Reichel 18308c0984e5SSebastian Reichel static struct platform_driver bq27xxx_battery_platform_driver = { 18318c0984e5SSebastian Reichel .probe = bq27xxx_battery_platform_probe, 18328c0984e5SSebastian Reichel .remove = bq27xxx_battery_platform_remove, 18338c0984e5SSebastian Reichel .driver = { 18348c0984e5SSebastian Reichel .name = "bq27000-battery", 18358c0984e5SSebastian Reichel .of_match_table = of_match_ptr(bq27xxx_battery_platform_of_match_table), 18368c0984e5SSebastian Reichel }, 18378c0984e5SSebastian Reichel .id_table = bq27xxx_battery_platform_id_table, 18388c0984e5SSebastian Reichel }; 18398c0984e5SSebastian Reichel module_platform_driver(bq27xxx_battery_platform_driver); 18408c0984e5SSebastian Reichel 18418c0984e5SSebastian Reichel MODULE_ALIAS("platform:bq27000-battery"); 18428c0984e5SSebastian Reichel 18438c0984e5SSebastian Reichel MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 18448c0984e5SSebastian Reichel MODULE_DESCRIPTION("BQ27xxx battery monitor driver"); 18458c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 1846