1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 28c0984e5SSebastian Reichel /* 38c0984e5SSebastian Reichel * TI BQ25890 charger driver 48c0984e5SSebastian Reichel * 58c0984e5SSebastian Reichel * Copyright (C) 2015 Intel Corporation 68c0984e5SSebastian Reichel */ 78c0984e5SSebastian Reichel 88c0984e5SSebastian Reichel #include <linux/module.h> 98c0984e5SSebastian Reichel #include <linux/i2c.h> 108c0984e5SSebastian Reichel #include <linux/power_supply.h> 11*79d35365SHans de Goede #include <linux/power/bq25890_charger.h> 128c0984e5SSebastian Reichel #include <linux/regmap.h> 13*79d35365SHans de Goede #include <linux/regulator/driver.h> 148c0984e5SSebastian Reichel #include <linux/types.h> 158c0984e5SSebastian Reichel #include <linux/gpio/consumer.h> 168c0984e5SSebastian Reichel #include <linux/interrupt.h> 178c0984e5SSebastian Reichel #include <linux/delay.h> 188c0984e5SSebastian Reichel #include <linux/usb/phy.h> 198c0984e5SSebastian Reichel 208c0984e5SSebastian Reichel #include <linux/acpi.h> 218c0984e5SSebastian Reichel #include <linux/of.h> 228c0984e5SSebastian Reichel 238c0984e5SSebastian Reichel #define BQ25890_MANUFACTURER "Texas Instruments" 248c0984e5SSebastian Reichel #define BQ25890_IRQ_PIN "bq25890_irq" 258c0984e5SSebastian Reichel 268c0984e5SSebastian Reichel #define BQ25890_ID 3 275c35ba9bSAngus Ainslie (Purism) #define BQ25895_ID 7 282e1a2ddeSAngus Ainslie (Purism) #define BQ25896_ID 0 298c0984e5SSebastian Reichel 30d20267c9SYauhen Kharuzhy enum bq25890_chip_version { 31d20267c9SYauhen Kharuzhy BQ25890, 32d20267c9SYauhen Kharuzhy BQ25892, 33d20267c9SYauhen Kharuzhy BQ25895, 34d20267c9SYauhen Kharuzhy BQ25896, 35d20267c9SYauhen Kharuzhy }; 36d20267c9SYauhen Kharuzhy 375956fca7SMichał Mirosław static const char *const bq25890_chip_name[] = { 385956fca7SMichał Mirosław "BQ25890", 395956fca7SMichał Mirosław "BQ25892", 405956fca7SMichał Mirosław "BQ25895", 415956fca7SMichał Mirosław "BQ25896", 425956fca7SMichał Mirosław }; 435956fca7SMichał Mirosław 448c0984e5SSebastian Reichel enum bq25890_fields { 45766873c1SYauhen Kharuzhy F_EN_HIZ, F_EN_ILIM, F_IINLIM, /* Reg00 */ 468c0984e5SSebastian Reichel F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */ 478c0984e5SSebastian Reichel F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN, 488c0984e5SSebastian Reichel F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */ 49d20267c9SYauhen Kharuzhy F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN, 50d20267c9SYauhen Kharuzhy F_MIN_VBAT_SEL, /* Reg03 */ 518c0984e5SSebastian Reichel F_PUMPX_EN, F_ICHG, /* Reg04 */ 528c0984e5SSebastian Reichel F_IPRECHG, F_ITERM, /* Reg05 */ 538c0984e5SSebastian Reichel F_VREG, F_BATLOWV, F_VRECHG, /* Reg06 */ 548c0984e5SSebastian Reichel F_TERM_EN, F_STAT_DIS, F_WD, F_TMR_EN, F_CHG_TMR, 558c0984e5SSebastian Reichel F_JEITA_ISET, /* Reg07 */ 568c0984e5SSebastian Reichel F_BATCMP, F_VCLAMP, F_TREG, /* Reg08 */ 578c0984e5SSebastian Reichel F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET, 588c0984e5SSebastian Reichel F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN, /* Reg09 */ 59d20267c9SYauhen Kharuzhy F_BOOSTV, F_PFM_OTG_DIS, F_BOOSTI, /* Reg0A */ 60d20267c9SYauhen Kharuzhy F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_0B_RSVD, 61d20267c9SYauhen Kharuzhy F_VSYS_STAT, /* Reg0B */ 628c0984e5SSebastian Reichel F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT, 638c0984e5SSebastian Reichel F_NTC_FAULT, /* Reg0C */ 648c0984e5SSebastian Reichel F_FORCE_VINDPM, F_VINDPM, /* Reg0D */ 658c0984e5SSebastian Reichel F_THERM_STAT, F_BATV, /* Reg0E */ 668c0984e5SSebastian Reichel F_SYSV, /* Reg0F */ 678c0984e5SSebastian Reichel F_TSPCT, /* Reg10 */ 688c0984e5SSebastian Reichel F_VBUS_GD, F_VBUSV, /* Reg11 */ 698c0984e5SSebastian Reichel F_ICHGR, /* Reg12 */ 708c0984e5SSebastian Reichel F_VDPM_STAT, F_IDPM_STAT, F_IDPM_LIM, /* Reg13 */ 718c0984e5SSebastian Reichel F_REG_RST, F_ICO_OPTIMIZED, F_PN, F_TS_PROFILE, F_DEV_REV, /* Reg14 */ 728c0984e5SSebastian Reichel 738c0984e5SSebastian Reichel F_MAX_FIELDS 748c0984e5SSebastian Reichel }; 758c0984e5SSebastian Reichel 768c0984e5SSebastian Reichel /* initial field values, converted to register values */ 778c0984e5SSebastian Reichel struct bq25890_init_data { 788c0984e5SSebastian Reichel u8 ichg; /* charge current */ 798c0984e5SSebastian Reichel u8 vreg; /* regulation voltage */ 808c0984e5SSebastian Reichel u8 iterm; /* termination current */ 818c0984e5SSebastian Reichel u8 iprechg; /* precharge current */ 828c0984e5SSebastian Reichel u8 sysvmin; /* minimum system voltage limit */ 838c0984e5SSebastian Reichel u8 boostv; /* boost regulation voltage */ 848c0984e5SSebastian Reichel u8 boosti; /* boost current limit */ 858c0984e5SSebastian Reichel u8 boostf; /* boost frequency */ 868c0984e5SSebastian Reichel u8 ilim_en; /* enable ILIM pin */ 878c0984e5SSebastian Reichel u8 treg; /* thermal regulation threshold */ 8872408329SMichał Mirosław u8 rbatcomp; /* IBAT sense resistor value */ 8972408329SMichał Mirosław u8 vclamp; /* IBAT compensation voltage limit */ 908c0984e5SSebastian Reichel }; 918c0984e5SSebastian Reichel 928c0984e5SSebastian Reichel struct bq25890_state { 938c0984e5SSebastian Reichel u8 online; 948c0984e5SSebastian Reichel u8 chrg_status; 958c0984e5SSebastian Reichel u8 chrg_fault; 968c0984e5SSebastian Reichel u8 vsys_status; 978c0984e5SSebastian Reichel u8 boost_fault; 988c0984e5SSebastian Reichel u8 bat_fault; 99c562a43aSYauhen Kharuzhy u8 ntc_fault; 1008c0984e5SSebastian Reichel }; 1018c0984e5SSebastian Reichel 1028c0984e5SSebastian Reichel struct bq25890_device { 1038c0984e5SSebastian Reichel struct i2c_client *client; 1048c0984e5SSebastian Reichel struct device *dev; 1058c0984e5SSebastian Reichel struct power_supply *charger; 1068c0984e5SSebastian Reichel 1078c0984e5SSebastian Reichel struct usb_phy *usb_phy; 1088c0984e5SSebastian Reichel struct notifier_block usb_nb; 1098c0984e5SSebastian Reichel struct work_struct usb_work; 1108c0984e5SSebastian Reichel unsigned long usb_event; 1118c0984e5SSebastian Reichel 1128c0984e5SSebastian Reichel struct regmap *rmap; 1138c0984e5SSebastian Reichel struct regmap_field *rmap_fields[F_MAX_FIELDS]; 1148c0984e5SSebastian Reichel 1157e3b8e35SHans de Goede bool skip_reset; 11640428bd4SHans de Goede bool read_back_init_data; 117d20267c9SYauhen Kharuzhy enum bq25890_chip_version chip_version; 1188c0984e5SSebastian Reichel struct bq25890_init_data init_data; 1198c0984e5SSebastian Reichel struct bq25890_state state; 1208c0984e5SSebastian Reichel 1218c0984e5SSebastian Reichel struct mutex lock; /* protect state data */ 1228c0984e5SSebastian Reichel }; 1238c0984e5SSebastian Reichel 1248c0984e5SSebastian Reichel static const struct regmap_range bq25890_readonly_reg_ranges[] = { 1258c0984e5SSebastian Reichel regmap_reg_range(0x0b, 0x0c), 1268c0984e5SSebastian Reichel regmap_reg_range(0x0e, 0x13), 1278c0984e5SSebastian Reichel }; 1288c0984e5SSebastian Reichel 1298c0984e5SSebastian Reichel static const struct regmap_access_table bq25890_writeable_regs = { 1308c0984e5SSebastian Reichel .no_ranges = bq25890_readonly_reg_ranges, 1318c0984e5SSebastian Reichel .n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges), 1328c0984e5SSebastian Reichel }; 1338c0984e5SSebastian Reichel 1348c0984e5SSebastian Reichel static const struct regmap_range bq25890_volatile_reg_ranges[] = { 1358c0984e5SSebastian Reichel regmap_reg_range(0x00, 0x00), 13621d90edaSMichał Mirosław regmap_reg_range(0x02, 0x02), 1378c0984e5SSebastian Reichel regmap_reg_range(0x09, 0x09), 138d20267c9SYauhen Kharuzhy regmap_reg_range(0x0b, 0x14), 1398c0984e5SSebastian Reichel }; 1408c0984e5SSebastian Reichel 1418c0984e5SSebastian Reichel static const struct regmap_access_table bq25890_volatile_regs = { 1428c0984e5SSebastian Reichel .yes_ranges = bq25890_volatile_reg_ranges, 1438c0984e5SSebastian Reichel .n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges), 1448c0984e5SSebastian Reichel }; 1458c0984e5SSebastian Reichel 1468c0984e5SSebastian Reichel static const struct regmap_config bq25890_regmap_config = { 1478c0984e5SSebastian Reichel .reg_bits = 8, 1488c0984e5SSebastian Reichel .val_bits = 8, 1498c0984e5SSebastian Reichel 1508c0984e5SSebastian Reichel .max_register = 0x14, 1518c0984e5SSebastian Reichel .cache_type = REGCACHE_RBTREE, 1528c0984e5SSebastian Reichel 1538c0984e5SSebastian Reichel .wr_table = &bq25890_writeable_regs, 1548c0984e5SSebastian Reichel .volatile_table = &bq25890_volatile_regs, 1558c0984e5SSebastian Reichel }; 1568c0984e5SSebastian Reichel 1578c0984e5SSebastian Reichel static const struct reg_field bq25890_reg_fields[] = { 1588c0984e5SSebastian Reichel /* REG00 */ 1598c0984e5SSebastian Reichel [F_EN_HIZ] = REG_FIELD(0x00, 7, 7), 1608c0984e5SSebastian Reichel [F_EN_ILIM] = REG_FIELD(0x00, 6, 6), 161766873c1SYauhen Kharuzhy [F_IINLIM] = REG_FIELD(0x00, 0, 5), 1628c0984e5SSebastian Reichel /* REG01 */ 1638c0984e5SSebastian Reichel [F_BHOT] = REG_FIELD(0x01, 6, 7), 1648c0984e5SSebastian Reichel [F_BCOLD] = REG_FIELD(0x01, 5, 5), 1658c0984e5SSebastian Reichel [F_VINDPM_OFS] = REG_FIELD(0x01, 0, 4), 1668c0984e5SSebastian Reichel /* REG02 */ 1678c0984e5SSebastian Reichel [F_CONV_START] = REG_FIELD(0x02, 7, 7), 1688c0984e5SSebastian Reichel [F_CONV_RATE] = REG_FIELD(0x02, 6, 6), 1698c0984e5SSebastian Reichel [F_BOOSTF] = REG_FIELD(0x02, 5, 5), 1708c0984e5SSebastian Reichel [F_ICO_EN] = REG_FIELD(0x02, 4, 4), 1712e1a2ddeSAngus Ainslie (Purism) [F_HVDCP_EN] = REG_FIELD(0x02, 3, 3), // reserved on BQ25896 1722e1a2ddeSAngus Ainslie (Purism) [F_MAXC_EN] = REG_FIELD(0x02, 2, 2), // reserved on BQ25896 1738c0984e5SSebastian Reichel [F_FORCE_DPM] = REG_FIELD(0x02, 1, 1), 1748c0984e5SSebastian Reichel [F_AUTO_DPDM_EN] = REG_FIELD(0x02, 0, 0), 1758c0984e5SSebastian Reichel /* REG03 */ 1768c0984e5SSebastian Reichel [F_BAT_LOAD_EN] = REG_FIELD(0x03, 7, 7), 1778c0984e5SSebastian Reichel [F_WD_RST] = REG_FIELD(0x03, 6, 6), 1788c0984e5SSebastian Reichel [F_OTG_CFG] = REG_FIELD(0x03, 5, 5), 1798c0984e5SSebastian Reichel [F_CHG_CFG] = REG_FIELD(0x03, 4, 4), 1808c0984e5SSebastian Reichel [F_SYSVMIN] = REG_FIELD(0x03, 1, 3), 181d20267c9SYauhen Kharuzhy [F_MIN_VBAT_SEL] = REG_FIELD(0x03, 0, 0), // BQ25896 only 1828c0984e5SSebastian Reichel /* REG04 */ 1838c0984e5SSebastian Reichel [F_PUMPX_EN] = REG_FIELD(0x04, 7, 7), 1848c0984e5SSebastian Reichel [F_ICHG] = REG_FIELD(0x04, 0, 6), 1858c0984e5SSebastian Reichel /* REG05 */ 1868c0984e5SSebastian Reichel [F_IPRECHG] = REG_FIELD(0x05, 4, 7), 1878c0984e5SSebastian Reichel [F_ITERM] = REG_FIELD(0x05, 0, 3), 1888c0984e5SSebastian Reichel /* REG06 */ 1898c0984e5SSebastian Reichel [F_VREG] = REG_FIELD(0x06, 2, 7), 1908c0984e5SSebastian Reichel [F_BATLOWV] = REG_FIELD(0x06, 1, 1), 1918c0984e5SSebastian Reichel [F_VRECHG] = REG_FIELD(0x06, 0, 0), 1928c0984e5SSebastian Reichel /* REG07 */ 1938c0984e5SSebastian Reichel [F_TERM_EN] = REG_FIELD(0x07, 7, 7), 1948c0984e5SSebastian Reichel [F_STAT_DIS] = REG_FIELD(0x07, 6, 6), 1958c0984e5SSebastian Reichel [F_WD] = REG_FIELD(0x07, 4, 5), 1968c0984e5SSebastian Reichel [F_TMR_EN] = REG_FIELD(0x07, 3, 3), 1978c0984e5SSebastian Reichel [F_CHG_TMR] = REG_FIELD(0x07, 1, 2), 1985c35ba9bSAngus Ainslie (Purism) [F_JEITA_ISET] = REG_FIELD(0x07, 0, 0), // reserved on BQ25895 1998c0984e5SSebastian Reichel /* REG08 */ 20095809139SMichał Mirosław [F_BATCMP] = REG_FIELD(0x08, 5, 7), 2018c0984e5SSebastian Reichel [F_VCLAMP] = REG_FIELD(0x08, 2, 4), 2028c0984e5SSebastian Reichel [F_TREG] = REG_FIELD(0x08, 0, 1), 2038c0984e5SSebastian Reichel /* REG09 */ 2048c0984e5SSebastian Reichel [F_FORCE_ICO] = REG_FIELD(0x09, 7, 7), 2058c0984e5SSebastian Reichel [F_TMR2X_EN] = REG_FIELD(0x09, 6, 6), 2068c0984e5SSebastian Reichel [F_BATFET_DIS] = REG_FIELD(0x09, 5, 5), 2075c35ba9bSAngus Ainslie (Purism) [F_JEITA_VSET] = REG_FIELD(0x09, 4, 4), // reserved on BQ25895 2088c0984e5SSebastian Reichel [F_BATFET_DLY] = REG_FIELD(0x09, 3, 3), 2098c0984e5SSebastian Reichel [F_BATFET_RST_EN] = REG_FIELD(0x09, 2, 2), 2108c0984e5SSebastian Reichel [F_PUMPX_UP] = REG_FIELD(0x09, 1, 1), 2118c0984e5SSebastian Reichel [F_PUMPX_DN] = REG_FIELD(0x09, 0, 0), 2128c0984e5SSebastian Reichel /* REG0A */ 2138c0984e5SSebastian Reichel [F_BOOSTV] = REG_FIELD(0x0A, 4, 7), 2145c35ba9bSAngus Ainslie (Purism) [F_BOOSTI] = REG_FIELD(0x0A, 0, 2), // reserved on BQ25895 215d20267c9SYauhen Kharuzhy [F_PFM_OTG_DIS] = REG_FIELD(0x0A, 3, 3), // BQ25896 only 2168c0984e5SSebastian Reichel /* REG0B */ 2178c0984e5SSebastian Reichel [F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7), 2188c0984e5SSebastian Reichel [F_CHG_STAT] = REG_FIELD(0x0B, 3, 4), 2198c0984e5SSebastian Reichel [F_PG_STAT] = REG_FIELD(0x0B, 2, 2), 2202e1a2ddeSAngus Ainslie (Purism) [F_SDP_STAT] = REG_FIELD(0x0B, 1, 1), // reserved on BQ25896 2218c0984e5SSebastian Reichel [F_VSYS_STAT] = REG_FIELD(0x0B, 0, 0), 2228c0984e5SSebastian Reichel /* REG0C */ 2238c0984e5SSebastian Reichel [F_WD_FAULT] = REG_FIELD(0x0C, 7, 7), 2248c0984e5SSebastian Reichel [F_BOOST_FAULT] = REG_FIELD(0x0C, 6, 6), 2258c0984e5SSebastian Reichel [F_CHG_FAULT] = REG_FIELD(0x0C, 4, 5), 2268c0984e5SSebastian Reichel [F_BAT_FAULT] = REG_FIELD(0x0C, 3, 3), 2278c0984e5SSebastian Reichel [F_NTC_FAULT] = REG_FIELD(0x0C, 0, 2), 2288c0984e5SSebastian Reichel /* REG0D */ 2298c0984e5SSebastian Reichel [F_FORCE_VINDPM] = REG_FIELD(0x0D, 7, 7), 2308c0984e5SSebastian Reichel [F_VINDPM] = REG_FIELD(0x0D, 0, 6), 2318c0984e5SSebastian Reichel /* REG0E */ 2328c0984e5SSebastian Reichel [F_THERM_STAT] = REG_FIELD(0x0E, 7, 7), 2338c0984e5SSebastian Reichel [F_BATV] = REG_FIELD(0x0E, 0, 6), 2348c0984e5SSebastian Reichel /* REG0F */ 2358c0984e5SSebastian Reichel [F_SYSV] = REG_FIELD(0x0F, 0, 6), 2368c0984e5SSebastian Reichel /* REG10 */ 2378c0984e5SSebastian Reichel [F_TSPCT] = REG_FIELD(0x10, 0, 6), 2388c0984e5SSebastian Reichel /* REG11 */ 2398c0984e5SSebastian Reichel [F_VBUS_GD] = REG_FIELD(0x11, 7, 7), 2408c0984e5SSebastian Reichel [F_VBUSV] = REG_FIELD(0x11, 0, 6), 2418c0984e5SSebastian Reichel /* REG12 */ 2428c0984e5SSebastian Reichel [F_ICHGR] = REG_FIELD(0x12, 0, 6), 2438c0984e5SSebastian Reichel /* REG13 */ 2448c0984e5SSebastian Reichel [F_VDPM_STAT] = REG_FIELD(0x13, 7, 7), 2458c0984e5SSebastian Reichel [F_IDPM_STAT] = REG_FIELD(0x13, 6, 6), 2468c0984e5SSebastian Reichel [F_IDPM_LIM] = REG_FIELD(0x13, 0, 5), 2478c0984e5SSebastian Reichel /* REG14 */ 2488c0984e5SSebastian Reichel [F_REG_RST] = REG_FIELD(0x14, 7, 7), 2498c0984e5SSebastian Reichel [F_ICO_OPTIMIZED] = REG_FIELD(0x14, 6, 6), 2508c0984e5SSebastian Reichel [F_PN] = REG_FIELD(0x14, 3, 5), 2518c0984e5SSebastian Reichel [F_TS_PROFILE] = REG_FIELD(0x14, 2, 2), 2528c0984e5SSebastian Reichel [F_DEV_REV] = REG_FIELD(0x14, 0, 1) 2538c0984e5SSebastian Reichel }; 2548c0984e5SSebastian Reichel 2558c0984e5SSebastian Reichel /* 2568c0984e5SSebastian Reichel * Most of the val -> idx conversions can be computed, given the minimum, 2578c0984e5SSebastian Reichel * maximum and the step between values. For the rest of conversions, we use 2588c0984e5SSebastian Reichel * lookup tables. 2598c0984e5SSebastian Reichel */ 2608c0984e5SSebastian Reichel enum bq25890_table_ids { 2618c0984e5SSebastian Reichel /* range tables */ 2628c0984e5SSebastian Reichel TBL_ICHG, 2638c0984e5SSebastian Reichel TBL_ITERM, 264766873c1SYauhen Kharuzhy TBL_IINLIM, 2658c0984e5SSebastian Reichel TBL_VREG, 2668c0984e5SSebastian Reichel TBL_BOOSTV, 2678c0984e5SSebastian Reichel TBL_SYSVMIN, 26872408329SMichał Mirosław TBL_VBATCOMP, 26972408329SMichał Mirosław TBL_RBATCOMP, 2708c0984e5SSebastian Reichel 2718c0984e5SSebastian Reichel /* lookup tables */ 2728c0984e5SSebastian Reichel TBL_TREG, 2738c0984e5SSebastian Reichel TBL_BOOSTI, 2749652c024SAngus Ainslie TBL_TSPCT, 2758c0984e5SSebastian Reichel }; 2768c0984e5SSebastian Reichel 2778c0984e5SSebastian Reichel /* Thermal Regulation Threshold lookup table, in degrees Celsius */ 2788c0984e5SSebastian Reichel static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 }; 2798c0984e5SSebastian Reichel 2808c0984e5SSebastian Reichel #define BQ25890_TREG_TBL_SIZE ARRAY_SIZE(bq25890_treg_tbl) 2818c0984e5SSebastian Reichel 2828c0984e5SSebastian Reichel /* Boost mode current limit lookup table, in uA */ 2838c0984e5SSebastian Reichel static const u32 bq25890_boosti_tbl[] = { 2848c0984e5SSebastian Reichel 500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000 2858c0984e5SSebastian Reichel }; 2868c0984e5SSebastian Reichel 2878c0984e5SSebastian Reichel #define BQ25890_BOOSTI_TBL_SIZE ARRAY_SIZE(bq25890_boosti_tbl) 2888c0984e5SSebastian Reichel 2899652c024SAngus Ainslie /* NTC 10K temperature lookup table in tenths of a degree */ 2909652c024SAngus Ainslie static const u32 bq25890_tspct_tbl[] = { 2919652c024SAngus Ainslie 850, 840, 830, 820, 810, 800, 790, 780, 2929652c024SAngus Ainslie 770, 760, 750, 740, 730, 720, 710, 700, 2939652c024SAngus Ainslie 690, 685, 680, 675, 670, 660, 650, 645, 2949652c024SAngus Ainslie 640, 630, 620, 615, 610, 600, 590, 585, 2959652c024SAngus Ainslie 580, 570, 565, 560, 550, 540, 535, 530, 2969652c024SAngus Ainslie 520, 515, 510, 500, 495, 490, 480, 475, 2979652c024SAngus Ainslie 470, 460, 455, 450, 440, 435, 430, 425, 2989652c024SAngus Ainslie 420, 410, 405, 400, 390, 385, 380, 370, 2999652c024SAngus Ainslie 365, 360, 355, 350, 340, 335, 330, 320, 3009652c024SAngus Ainslie 310, 305, 300, 290, 285, 280, 275, 270, 3019652c024SAngus Ainslie 260, 250, 245, 240, 230, 225, 220, 210, 3029652c024SAngus Ainslie 205, 200, 190, 180, 175, 170, 160, 150, 3039652c024SAngus Ainslie 145, 140, 130, 120, 115, 110, 100, 90, 3049652c024SAngus Ainslie 80, 70, 60, 50, 40, 30, 20, 10, 3059652c024SAngus Ainslie 0, -10, -20, -30, -40, -60, -70, -80, 3069652c024SAngus Ainslie -90, -10, -120, -140, -150, -170, -190, -210, 3079652c024SAngus Ainslie }; 3089652c024SAngus Ainslie 3099652c024SAngus Ainslie #define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl) 3109652c024SAngus Ainslie 3118c0984e5SSebastian Reichel struct bq25890_range { 3128c0984e5SSebastian Reichel u32 min; 3138c0984e5SSebastian Reichel u32 max; 3148c0984e5SSebastian Reichel u32 step; 3158c0984e5SSebastian Reichel }; 3168c0984e5SSebastian Reichel 3178c0984e5SSebastian Reichel struct bq25890_lookup { 3188c0984e5SSebastian Reichel const u32 *tbl; 3198c0984e5SSebastian Reichel u32 size; 3208c0984e5SSebastian Reichel }; 3218c0984e5SSebastian Reichel 3228c0984e5SSebastian Reichel static const union { 3238c0984e5SSebastian Reichel struct bq25890_range rt; 3248c0984e5SSebastian Reichel struct bq25890_lookup lt; 3258c0984e5SSebastian Reichel } bq25890_tables[] = { 3268c0984e5SSebastian Reichel /* range tables */ 327d20267c9SYauhen Kharuzhy /* TODO: BQ25896 has max ICHG 3008 mA */ 3288c0984e5SSebastian Reichel [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ 3298c0984e5SSebastian Reichel [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ 330766873c1SYauhen Kharuzhy [TBL_IINLIM] = { .rt = {100000, 3250000, 50000} }, /* uA */ 3318c0984e5SSebastian Reichel [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ 3328c0984e5SSebastian Reichel [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ 3338c0984e5SSebastian Reichel [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ 33472408329SMichał Mirosław [TBL_VBATCOMP] ={ .rt = {0, 224000, 32000} }, /* uV */ 33572408329SMichał Mirosław [TBL_RBATCOMP] ={ .rt = {0, 140000, 20000} }, /* uOhm */ 3368c0984e5SSebastian Reichel 3378c0984e5SSebastian Reichel /* lookup tables */ 3388c0984e5SSebastian Reichel [TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} }, 3399652c024SAngus Ainslie [TBL_BOOSTI] = { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }, 3409652c024SAngus Ainslie [TBL_TSPCT] = { .lt = {bq25890_tspct_tbl, BQ25890_TSPCT_TBL_SIZE} } 3418c0984e5SSebastian Reichel }; 3428c0984e5SSebastian Reichel 3438c0984e5SSebastian Reichel static int bq25890_field_read(struct bq25890_device *bq, 3448c0984e5SSebastian Reichel enum bq25890_fields field_id) 3458c0984e5SSebastian Reichel { 3468c0984e5SSebastian Reichel int ret; 3478c0984e5SSebastian Reichel int val; 3488c0984e5SSebastian Reichel 3498c0984e5SSebastian Reichel ret = regmap_field_read(bq->rmap_fields[field_id], &val); 3508c0984e5SSebastian Reichel if (ret < 0) 3518c0984e5SSebastian Reichel return ret; 3528c0984e5SSebastian Reichel 3538c0984e5SSebastian Reichel return val; 3548c0984e5SSebastian Reichel } 3558c0984e5SSebastian Reichel 3568c0984e5SSebastian Reichel static int bq25890_field_write(struct bq25890_device *bq, 3578c0984e5SSebastian Reichel enum bq25890_fields field_id, u8 val) 3588c0984e5SSebastian Reichel { 3598c0984e5SSebastian Reichel return regmap_field_write(bq->rmap_fields[field_id], val); 3608c0984e5SSebastian Reichel } 3618c0984e5SSebastian Reichel 3628c0984e5SSebastian Reichel static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id) 3638c0984e5SSebastian Reichel { 3648c0984e5SSebastian Reichel u8 idx; 3658c0984e5SSebastian Reichel 3668c0984e5SSebastian Reichel if (id >= TBL_TREG) { 3678c0984e5SSebastian Reichel const u32 *tbl = bq25890_tables[id].lt.tbl; 3688c0984e5SSebastian Reichel u32 tbl_size = bq25890_tables[id].lt.size; 3698c0984e5SSebastian Reichel 3708c0984e5SSebastian Reichel for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++) 3718c0984e5SSebastian Reichel ; 3728c0984e5SSebastian Reichel } else { 3738c0984e5SSebastian Reichel const struct bq25890_range *rtbl = &bq25890_tables[id].rt; 3748c0984e5SSebastian Reichel u8 rtbl_size; 3758c0984e5SSebastian Reichel 3768c0984e5SSebastian Reichel rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1; 3778c0984e5SSebastian Reichel 3788c0984e5SSebastian Reichel for (idx = 1; 3798c0984e5SSebastian Reichel idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value); 3808c0984e5SSebastian Reichel idx++) 3818c0984e5SSebastian Reichel ; 3828c0984e5SSebastian Reichel } 3838c0984e5SSebastian Reichel 3848c0984e5SSebastian Reichel return idx - 1; 3858c0984e5SSebastian Reichel } 3868c0984e5SSebastian Reichel 3878c0984e5SSebastian Reichel static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id) 3888c0984e5SSebastian Reichel { 3898c0984e5SSebastian Reichel const struct bq25890_range *rtbl; 3908c0984e5SSebastian Reichel 3918c0984e5SSebastian Reichel /* lookup table? */ 3928c0984e5SSebastian Reichel if (id >= TBL_TREG) 3938c0984e5SSebastian Reichel return bq25890_tables[id].lt.tbl[idx]; 3948c0984e5SSebastian Reichel 3958c0984e5SSebastian Reichel /* range table */ 3968c0984e5SSebastian Reichel rtbl = &bq25890_tables[id].rt; 3978c0984e5SSebastian Reichel 3988c0984e5SSebastian Reichel return (rtbl->min + idx * rtbl->step); 3998c0984e5SSebastian Reichel } 4008c0984e5SSebastian Reichel 4018c0984e5SSebastian Reichel enum bq25890_status { 4028c0984e5SSebastian Reichel STATUS_NOT_CHARGING, 4038c0984e5SSebastian Reichel STATUS_PRE_CHARGING, 4048c0984e5SSebastian Reichel STATUS_FAST_CHARGING, 4058c0984e5SSebastian Reichel STATUS_TERMINATION_DONE, 4068c0984e5SSebastian Reichel }; 4078c0984e5SSebastian Reichel 4088c0984e5SSebastian Reichel enum bq25890_chrg_fault { 4098c0984e5SSebastian Reichel CHRG_FAULT_NORMAL, 4108c0984e5SSebastian Reichel CHRG_FAULT_INPUT, 4118c0984e5SSebastian Reichel CHRG_FAULT_THERMAL_SHUTDOWN, 4128c0984e5SSebastian Reichel CHRG_FAULT_TIMER_EXPIRED, 4138c0984e5SSebastian Reichel }; 4148c0984e5SSebastian Reichel 415c562a43aSYauhen Kharuzhy enum bq25890_ntc_fault { 416c562a43aSYauhen Kharuzhy NTC_FAULT_NORMAL = 0, 417c562a43aSYauhen Kharuzhy NTC_FAULT_WARM = 2, 418c562a43aSYauhen Kharuzhy NTC_FAULT_COOL = 3, 419c562a43aSYauhen Kharuzhy NTC_FAULT_COLD = 5, 420c562a43aSYauhen Kharuzhy NTC_FAULT_HOT = 6, 421c562a43aSYauhen Kharuzhy }; 422c562a43aSYauhen Kharuzhy 42321d90edaSMichał Mirosław static bool bq25890_is_adc_property(enum power_supply_property psp) 42421d90edaSMichał Mirosław { 42521d90edaSMichał Mirosław switch (psp) { 42621d90edaSMichał Mirosław case POWER_SUPPLY_PROP_VOLTAGE_NOW: 42721d90edaSMichał Mirosław case POWER_SUPPLY_PROP_CURRENT_NOW: 4289652c024SAngus Ainslie case POWER_SUPPLY_PROP_TEMP: 42921d90edaSMichał Mirosław return true; 43021d90edaSMichał Mirosław 43121d90edaSMichał Mirosław default: 43221d90edaSMichał Mirosław return false; 43321d90edaSMichał Mirosław } 43421d90edaSMichał Mirosław } 43521d90edaSMichał Mirosław 4363b4df57bSMichał Mirosław static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq); 4373b4df57bSMichał Mirosław 4388c0984e5SSebastian Reichel static int bq25890_power_supply_get_property(struct power_supply *psy, 4398c0984e5SSebastian Reichel enum power_supply_property psp, 4408c0984e5SSebastian Reichel union power_supply_propval *val) 4418c0984e5SSebastian Reichel { 4428c0984e5SSebastian Reichel struct bq25890_device *bq = power_supply_get_drvdata(psy); 4438c0984e5SSebastian Reichel struct bq25890_state state; 44421d90edaSMichał Mirosław bool do_adc_conv; 44521d90edaSMichał Mirosław int ret; 4468c0984e5SSebastian Reichel 4478c0984e5SSebastian Reichel mutex_lock(&bq->lock); 4483b4df57bSMichał Mirosław /* update state in case we lost an interrupt */ 4493b4df57bSMichał Mirosław __bq25890_handle_irq(bq); 4508c0984e5SSebastian Reichel state = bq->state; 45121d90edaSMichał Mirosław do_adc_conv = !state.online && bq25890_is_adc_property(psp); 45221d90edaSMichał Mirosław if (do_adc_conv) 45321d90edaSMichał Mirosław bq25890_field_write(bq, F_CONV_START, 1); 4548c0984e5SSebastian Reichel mutex_unlock(&bq->lock); 4558c0984e5SSebastian Reichel 45621d90edaSMichał Mirosław if (do_adc_conv) 45721d90edaSMichał Mirosław regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START], 45821d90edaSMichał Mirosław ret, !ret, 25000, 1000000); 45921d90edaSMichał Mirosław 4608c0984e5SSebastian Reichel switch (psp) { 4618c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 4628c0984e5SSebastian Reichel if (!state.online) 4638c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 4648c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_NOT_CHARGING) 4658c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 4668c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_PRE_CHARGING || 4678c0984e5SSebastian Reichel state.chrg_status == STATUS_FAST_CHARGING) 4688c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_CHARGING; 4698c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_TERMINATION_DONE) 4708c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_FULL; 4718c0984e5SSebastian Reichel else 4728c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 4738c0984e5SSebastian Reichel 4748c0984e5SSebastian Reichel break; 4758c0984e5SSebastian Reichel 476b302a0aeSMichał Mirosław case POWER_SUPPLY_PROP_CHARGE_TYPE: 477b302a0aeSMichał Mirosław if (!state.online || state.chrg_status == STATUS_NOT_CHARGING || 478b302a0aeSMichał Mirosław state.chrg_status == STATUS_TERMINATION_DONE) 479b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 480b302a0aeSMichał Mirosław else if (state.chrg_status == STATUS_PRE_CHARGING) 481b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; 482b302a0aeSMichał Mirosław else if (state.chrg_status == STATUS_FAST_CHARGING) 483b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 484b302a0aeSMichał Mirosław else /* unreachable */ 485b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 486b302a0aeSMichał Mirosław break; 487b302a0aeSMichał Mirosław 4888c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_MANUFACTURER: 4898c0984e5SSebastian Reichel val->strval = BQ25890_MANUFACTURER; 4908c0984e5SSebastian Reichel break; 4918c0984e5SSebastian Reichel 4922e1a2ddeSAngus Ainslie (Purism) case POWER_SUPPLY_PROP_MODEL_NAME: 4935956fca7SMichał Mirosław val->strval = bq25890_chip_name[bq->chip_version]; 4942e1a2ddeSAngus Ainslie (Purism) break; 4952e1a2ddeSAngus Ainslie (Purism) 4968c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_ONLINE: 4978c0984e5SSebastian Reichel val->intval = state.online; 4988c0984e5SSebastian Reichel break; 4998c0984e5SSebastian Reichel 5008c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH: 5018c0984e5SSebastian Reichel if (!state.chrg_fault && !state.bat_fault && !state.boost_fault) 5028c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_GOOD; 5038c0984e5SSebastian Reichel else if (state.bat_fault) 5048c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 5058c0984e5SSebastian Reichel else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED) 5068c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; 5078c0984e5SSebastian Reichel else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN) 5088c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 5098c0984e5SSebastian Reichel else 5108c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 5118c0984e5SSebastian Reichel break; 5128c0984e5SSebastian Reichel 5138c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 514f83a6eceSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG); 515c562a43aSYauhen Kharuzhy 516c562a43aSYauhen Kharuzhy /* When temperature is too low, charge current is decreased */ 517c562a43aSYauhen Kharuzhy if (bq->state.ntc_fault == NTC_FAULT_COOL) { 518c562a43aSYauhen Kharuzhy ret = bq25890_field_read(bq, F_JEITA_ISET); 519c562a43aSYauhen Kharuzhy if (ret < 0) 520c562a43aSYauhen Kharuzhy return ret; 521c562a43aSYauhen Kharuzhy 522c562a43aSYauhen Kharuzhy if (ret) 523c562a43aSYauhen Kharuzhy val->intval /= 5; 524c562a43aSYauhen Kharuzhy else 525c562a43aSYauhen Kharuzhy val->intval /= 2; 526c562a43aSYauhen Kharuzhy } 5278c0984e5SSebastian Reichel break; 5288c0984e5SSebastian Reichel 5298c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 5308c0984e5SSebastian Reichel if (!state.online) { 5318c0984e5SSebastian Reichel val->intval = 0; 5328c0984e5SSebastian Reichel break; 5338c0984e5SSebastian Reichel } 5348c0984e5SSebastian Reichel 5358c0984e5SSebastian Reichel ret = bq25890_field_read(bq, F_BATV); /* read measured value */ 5368c0984e5SSebastian Reichel if (ret < 0) 5378c0984e5SSebastian Reichel return ret; 5388c0984e5SSebastian Reichel 5398c0984e5SSebastian Reichel /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */ 5408c0984e5SSebastian Reichel val->intval = 2304000 + ret * 20000; 5418c0984e5SSebastian Reichel break; 5428c0984e5SSebastian Reichel 5438c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 544f83a6eceSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG); 5458c0984e5SSebastian Reichel break; 5468c0984e5SSebastian Reichel 547c942656dSMichał Mirosław case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: 548c942656dSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.iprechg, TBL_ITERM); 549c942656dSMichał Mirosław break; 550c942656dSMichał Mirosław 5518c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 5528c0984e5SSebastian Reichel val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM); 5538c0984e5SSebastian Reichel break; 5548c0984e5SSebastian Reichel 555478efc79SMichał Mirosław case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 556766873c1SYauhen Kharuzhy ret = bq25890_field_read(bq, F_IINLIM); 557478efc79SMichał Mirosław if (ret < 0) 558478efc79SMichał Mirosław return ret; 559478efc79SMichał Mirosław 560766873c1SYauhen Kharuzhy val->intval = bq25890_find_val(ret, TBL_IINLIM); 561478efc79SMichał Mirosław break; 562478efc79SMichał Mirosław 563ae6fe7a3SAngus Ainslie (Purism) case POWER_SUPPLY_PROP_VOLTAGE_NOW: 564ae6fe7a3SAngus Ainslie (Purism) ret = bq25890_field_read(bq, F_SYSV); /* read measured value */ 565ae6fe7a3SAngus Ainslie (Purism) if (ret < 0) 566ae6fe7a3SAngus Ainslie (Purism) return ret; 567ae6fe7a3SAngus Ainslie (Purism) 568ae6fe7a3SAngus Ainslie (Purism) /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */ 569ae6fe7a3SAngus Ainslie (Purism) val->intval = 2304000 + ret * 20000; 570ae6fe7a3SAngus Ainslie (Purism) break; 571ae6fe7a3SAngus Ainslie (Purism) 5721e4724d0SMichał Mirosław case POWER_SUPPLY_PROP_CURRENT_NOW: 5731e4724d0SMichał Mirosław ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */ 5741e4724d0SMichał Mirosław if (ret < 0) 5751e4724d0SMichał Mirosław return ret; 5761e4724d0SMichał Mirosław 5771e4724d0SMichał Mirosław /* converted_val = ADC_val * 50mA (table 10.3.19) */ 5781e4724d0SMichał Mirosław val->intval = ret * -50000; 5791e4724d0SMichał Mirosław break; 5801e4724d0SMichał Mirosław 5819652c024SAngus Ainslie case POWER_SUPPLY_PROP_TEMP: 5829652c024SAngus Ainslie ret = bq25890_field_read(bq, F_TSPCT); 5839652c024SAngus Ainslie if (ret < 0) 5849652c024SAngus Ainslie return ret; 5859652c024SAngus Ainslie 5869652c024SAngus Ainslie /* convert TS percentage into rough temperature */ 5879652c024SAngus Ainslie val->intval = bq25890_find_val(ret, TBL_TSPCT); 5889652c024SAngus Ainslie break; 5899652c024SAngus Ainslie 5908c0984e5SSebastian Reichel default: 5918c0984e5SSebastian Reichel return -EINVAL; 5928c0984e5SSebastian Reichel } 5938c0984e5SSebastian Reichel 5948c0984e5SSebastian Reichel return 0; 5958c0984e5SSebastian Reichel } 5968c0984e5SSebastian Reichel 5978c0984e5SSebastian Reichel static int bq25890_get_chip_state(struct bq25890_device *bq, 5988c0984e5SSebastian Reichel struct bq25890_state *state) 5998c0984e5SSebastian Reichel { 6008c0984e5SSebastian Reichel int i, ret; 6018c0984e5SSebastian Reichel 6028c0984e5SSebastian Reichel struct { 6038c0984e5SSebastian Reichel enum bq25890_fields id; 6048c0984e5SSebastian Reichel u8 *data; 6058c0984e5SSebastian Reichel } state_fields[] = { 6068c0984e5SSebastian Reichel {F_CHG_STAT, &state->chrg_status}, 6078c0984e5SSebastian Reichel {F_PG_STAT, &state->online}, 6088c0984e5SSebastian Reichel {F_VSYS_STAT, &state->vsys_status}, 6098c0984e5SSebastian Reichel {F_BOOST_FAULT, &state->boost_fault}, 6108c0984e5SSebastian Reichel {F_BAT_FAULT, &state->bat_fault}, 611c562a43aSYauhen Kharuzhy {F_CHG_FAULT, &state->chrg_fault}, 612c562a43aSYauhen Kharuzhy {F_NTC_FAULT, &state->ntc_fault} 6138c0984e5SSebastian Reichel }; 6148c0984e5SSebastian Reichel 6158c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(state_fields); i++) { 6168c0984e5SSebastian Reichel ret = bq25890_field_read(bq, state_fields[i].id); 6178c0984e5SSebastian Reichel if (ret < 0) 6188c0984e5SSebastian Reichel return ret; 6198c0984e5SSebastian Reichel 6208c0984e5SSebastian Reichel *state_fields[i].data = ret; 6218c0984e5SSebastian Reichel } 6228c0984e5SSebastian Reichel 623c562a43aSYauhen Kharuzhy dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n", 6248c0984e5SSebastian Reichel state->chrg_status, state->online, state->vsys_status, 625c562a43aSYauhen Kharuzhy state->chrg_fault, state->boost_fault, state->bat_fault, 626c562a43aSYauhen Kharuzhy state->ntc_fault); 6278c0984e5SSebastian Reichel 6288c0984e5SSebastian Reichel return 0; 6298c0984e5SSebastian Reichel } 6308c0984e5SSebastian Reichel 63172d9cd9cSMichał Mirosław static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq) 6328c0984e5SSebastian Reichel { 63372d9cd9cSMichał Mirosław struct bq25890_state new_state; 6348c0984e5SSebastian Reichel int ret; 6358c0984e5SSebastian Reichel 63672d9cd9cSMichał Mirosław ret = bq25890_get_chip_state(bq, &new_state); 63772d9cd9cSMichał Mirosław if (ret < 0) 63872d9cd9cSMichał Mirosław return IRQ_NONE; 6398c0984e5SSebastian Reichel 64072d9cd9cSMichał Mirosław if (!memcmp(&bq->state, &new_state, sizeof(new_state))) 64172d9cd9cSMichał Mirosław return IRQ_NONE; 64272d9cd9cSMichał Mirosław 64372d9cd9cSMichał Mirosław if (!new_state.online && bq->state.online) { /* power removed */ 6448c0984e5SSebastian Reichel /* disable ADC */ 64580211be1SYauhen Kharuzhy ret = bq25890_field_write(bq, F_CONV_RATE, 0); 6468c0984e5SSebastian Reichel if (ret < 0) 6478c0984e5SSebastian Reichel goto error; 64872d9cd9cSMichał Mirosław } else if (new_state.online && !bq->state.online) { /* power inserted */ 6498c0984e5SSebastian Reichel /* enable ADC, to have control of charge current/voltage */ 65080211be1SYauhen Kharuzhy ret = bq25890_field_write(bq, F_CONV_RATE, 1); 6518c0984e5SSebastian Reichel if (ret < 0) 6528c0984e5SSebastian Reichel goto error; 6538c0984e5SSebastian Reichel } 6548c0984e5SSebastian Reichel 65572d9cd9cSMichał Mirosław bq->state = new_state; 65672d9cd9cSMichał Mirosław power_supply_changed(bq->charger); 6578c0984e5SSebastian Reichel 65872d9cd9cSMichał Mirosław return IRQ_HANDLED; 6598c0984e5SSebastian Reichel error: 66072d9cd9cSMichał Mirosław dev_err(bq->dev, "Error communicating with the chip: %pe\n", 66172d9cd9cSMichał Mirosław ERR_PTR(ret)); 66272d9cd9cSMichał Mirosław return IRQ_HANDLED; 6638c0984e5SSebastian Reichel } 6648c0984e5SSebastian Reichel 6658c0984e5SSebastian Reichel static irqreturn_t bq25890_irq_handler_thread(int irq, void *private) 6668c0984e5SSebastian Reichel { 6678c0984e5SSebastian Reichel struct bq25890_device *bq = private; 66872d9cd9cSMichał Mirosław irqreturn_t ret; 6698c0984e5SSebastian Reichel 6708c0984e5SSebastian Reichel mutex_lock(&bq->lock); 67172d9cd9cSMichał Mirosław ret = __bq25890_handle_irq(bq); 6728c0984e5SSebastian Reichel mutex_unlock(&bq->lock); 6738c0984e5SSebastian Reichel 67472d9cd9cSMichał Mirosław return ret; 6758c0984e5SSebastian Reichel } 6768c0984e5SSebastian Reichel 6778c0984e5SSebastian Reichel static int bq25890_chip_reset(struct bq25890_device *bq) 6788c0984e5SSebastian Reichel { 6798c0984e5SSebastian Reichel int ret; 6808c0984e5SSebastian Reichel int rst_check_counter = 10; 6818c0984e5SSebastian Reichel 6828c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_REG_RST, 1); 6838c0984e5SSebastian Reichel if (ret < 0) 6848c0984e5SSebastian Reichel return ret; 6858c0984e5SSebastian Reichel 6868c0984e5SSebastian Reichel do { 6878c0984e5SSebastian Reichel ret = bq25890_field_read(bq, F_REG_RST); 6888c0984e5SSebastian Reichel if (ret < 0) 6898c0984e5SSebastian Reichel return ret; 6908c0984e5SSebastian Reichel 6918c0984e5SSebastian Reichel usleep_range(5, 10); 6928c0984e5SSebastian Reichel } while (ret == 1 && --rst_check_counter); 6938c0984e5SSebastian Reichel 6948c0984e5SSebastian Reichel if (!rst_check_counter) 6958c0984e5SSebastian Reichel return -ETIMEDOUT; 6968c0984e5SSebastian Reichel 6978c0984e5SSebastian Reichel return 0; 6988c0984e5SSebastian Reichel } 6998c0984e5SSebastian Reichel 7007b22a974SHans de Goede static int bq25890_rw_init_data(struct bq25890_device *bq) 7018c0984e5SSebastian Reichel { 70240428bd4SHans de Goede bool write = !bq->read_back_init_data; 7038c0984e5SSebastian Reichel int ret; 7048c0984e5SSebastian Reichel int i; 7058c0984e5SSebastian Reichel 7068c0984e5SSebastian Reichel const struct { 7078c0984e5SSebastian Reichel enum bq25890_fields id; 7087b22a974SHans de Goede u8 *value; 7098c0984e5SSebastian Reichel } init_data[] = { 7107b22a974SHans de Goede {F_ICHG, &bq->init_data.ichg}, 7117b22a974SHans de Goede {F_VREG, &bq->init_data.vreg}, 7127b22a974SHans de Goede {F_ITERM, &bq->init_data.iterm}, 7137b22a974SHans de Goede {F_IPRECHG, &bq->init_data.iprechg}, 7147b22a974SHans de Goede {F_SYSVMIN, &bq->init_data.sysvmin}, 7157b22a974SHans de Goede {F_BOOSTV, &bq->init_data.boostv}, 7167b22a974SHans de Goede {F_BOOSTI, &bq->init_data.boosti}, 7177b22a974SHans de Goede {F_BOOSTF, &bq->init_data.boostf}, 7187b22a974SHans de Goede {F_EN_ILIM, &bq->init_data.ilim_en}, 7197b22a974SHans de Goede {F_TREG, &bq->init_data.treg}, 7207b22a974SHans de Goede {F_BATCMP, &bq->init_data.rbatcomp}, 7217b22a974SHans de Goede {F_VCLAMP, &bq->init_data.vclamp}, 7228c0984e5SSebastian Reichel }; 7238c0984e5SSebastian Reichel 7247b22a974SHans de Goede for (i = 0; i < ARRAY_SIZE(init_data); i++) { 7257b22a974SHans de Goede if (write) { 7267b22a974SHans de Goede ret = bq25890_field_write(bq, init_data[i].id, 7277b22a974SHans de Goede *init_data[i].value); 7287b22a974SHans de Goede } else { 7297b22a974SHans de Goede ret = bq25890_field_read(bq, init_data[i].id); 7307b22a974SHans de Goede if (ret >= 0) 7317b22a974SHans de Goede *init_data[i].value = ret; 7327b22a974SHans de Goede } 7337b22a974SHans de Goede if (ret < 0) { 7347b22a974SHans de Goede dev_dbg(bq->dev, "Accessing init data failed %d\n", ret); 7357b22a974SHans de Goede return ret; 7367b22a974SHans de Goede } 7377b22a974SHans de Goede } 7387b22a974SHans de Goede 7397b22a974SHans de Goede return 0; 7407b22a974SHans de Goede } 7417b22a974SHans de Goede 7427b22a974SHans de Goede static int bq25890_hw_init(struct bq25890_device *bq) 7437b22a974SHans de Goede { 7447b22a974SHans de Goede int ret; 7457b22a974SHans de Goede 7467e3b8e35SHans de Goede if (!bq->skip_reset) { 7478c0984e5SSebastian Reichel ret = bq25890_chip_reset(bq); 7489d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7499d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Reset failed %d\n", ret); 7508c0984e5SSebastian Reichel return ret; 751ad1570d9Skbuild test robot } 75206c75095SHans de Goede } else { 75306c75095SHans de Goede /* 75406c75095SHans de Goede * Ensure charging is enabled, on some boards where the fw 75506c75095SHans de Goede * takes care of initalizition F_CHG_CFG is set to 0 before 75606c75095SHans de Goede * handing control over to the OS. 75706c75095SHans de Goede */ 75806c75095SHans de Goede ret = bq25890_field_write(bq, F_CHG_CFG, 1); 75906c75095SHans de Goede if (ret < 0) { 76006c75095SHans de Goede dev_dbg(bq->dev, "Enabling charging failed %d\n", ret); 76106c75095SHans de Goede return ret; 76206c75095SHans de Goede } 7637e3b8e35SHans de Goede } 7648c0984e5SSebastian Reichel 7658c0984e5SSebastian Reichel /* disable watchdog */ 7668c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_WD, 0); 7679d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7689d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Disabling watchdog failed %d\n", ret); 7698c0984e5SSebastian Reichel return ret; 770ad1570d9Skbuild test robot } 7718c0984e5SSebastian Reichel 7728c0984e5SSebastian Reichel /* initialize currents/voltages and other parameters */ 7737b22a974SHans de Goede ret = bq25890_rw_init_data(bq); 7747b22a974SHans de Goede if (ret) 7758c0984e5SSebastian Reichel return ret; 7768c0984e5SSebastian Reichel 77722ad4f99SHans de Goede ret = bq25890_get_chip_state(bq, &bq->state); 77822ad4f99SHans de Goede if (ret < 0) { 77922ad4f99SHans de Goede dev_dbg(bq->dev, "Get state failed %d\n", ret); 78022ad4f99SHans de Goede return ret; 78122ad4f99SHans de Goede } 78222ad4f99SHans de Goede 78321d90edaSMichał Mirosław /* Configure ADC for continuous conversions when charging */ 78421d90edaSMichał Mirosław ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online); 7859d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7869d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Config ADC failed %d\n", ret); 7878c0984e5SSebastian Reichel return ret; 788ad1570d9Skbuild test robot } 7898c0984e5SSebastian Reichel 7908c0984e5SSebastian Reichel return 0; 7918c0984e5SSebastian Reichel } 7928c0984e5SSebastian Reichel 793a6a48facSMichał Mirosław static const enum power_supply_property bq25890_power_supply_props[] = { 7948c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 7952e1a2ddeSAngus Ainslie (Purism) POWER_SUPPLY_PROP_MODEL_NAME, 7968c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 797b302a0aeSMichał Mirosław POWER_SUPPLY_PROP_CHARGE_TYPE, 7988c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 7998c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 8008c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 8018c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 8028c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 803c942656dSMichał Mirosław POWER_SUPPLY_PROP_PRECHARGE_CURRENT, 8048c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, 805478efc79SMichał Mirosław POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 806ae6fe7a3SAngus Ainslie (Purism) POWER_SUPPLY_PROP_VOLTAGE_NOW, 8071e4724d0SMichał Mirosław POWER_SUPPLY_PROP_CURRENT_NOW, 8089652c024SAngus Ainslie POWER_SUPPLY_PROP_TEMP, 8098c0984e5SSebastian Reichel }; 8108c0984e5SSebastian Reichel 8118c0984e5SSebastian Reichel static char *bq25890_charger_supplied_to[] = { 8128c0984e5SSebastian Reichel "main-battery", 8138c0984e5SSebastian Reichel }; 8148c0984e5SSebastian Reichel 8158c0984e5SSebastian Reichel static const struct power_supply_desc bq25890_power_supply_desc = { 8168c0984e5SSebastian Reichel .name = "bq25890-charger", 8178c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_USB, 8188c0984e5SSebastian Reichel .properties = bq25890_power_supply_props, 8198c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(bq25890_power_supply_props), 8208c0984e5SSebastian Reichel .get_property = bq25890_power_supply_get_property, 8218c0984e5SSebastian Reichel }; 8228c0984e5SSebastian Reichel 8238c0984e5SSebastian Reichel static int bq25890_power_supply_init(struct bq25890_device *bq) 8248c0984e5SSebastian Reichel { 8258c0984e5SSebastian Reichel struct power_supply_config psy_cfg = { .drv_data = bq, }; 8268c0984e5SSebastian Reichel 8278c0984e5SSebastian Reichel psy_cfg.supplied_to = bq25890_charger_supplied_to; 8288c0984e5SSebastian Reichel psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to); 8298c0984e5SSebastian Reichel 830d01363daSHans de Goede bq->charger = devm_power_supply_register(bq->dev, 831d01363daSHans de Goede &bq25890_power_supply_desc, 8328c0984e5SSebastian Reichel &psy_cfg); 8338c0984e5SSebastian Reichel 8348c0984e5SSebastian Reichel return PTR_ERR_OR_ZERO(bq->charger); 8358c0984e5SSebastian Reichel } 8368c0984e5SSebastian Reichel 8375575802dSHans de Goede static int bq25890_set_otg_cfg(struct bq25890_device *bq, u8 val) 8385575802dSHans de Goede { 8395575802dSHans de Goede int ret; 8405575802dSHans de Goede 8415575802dSHans de Goede ret = bq25890_field_write(bq, F_OTG_CFG, val); 8425575802dSHans de Goede if (ret < 0) 8435575802dSHans de Goede dev_err(bq->dev, "Error switching to boost/charger mode: %d\n", ret); 8445575802dSHans de Goede 8455575802dSHans de Goede return ret; 8465575802dSHans de Goede } 8475575802dSHans de Goede 8488c0984e5SSebastian Reichel static void bq25890_usb_work(struct work_struct *data) 8498c0984e5SSebastian Reichel { 8508c0984e5SSebastian Reichel int ret; 8518c0984e5SSebastian Reichel struct bq25890_device *bq = 8528c0984e5SSebastian Reichel container_of(data, struct bq25890_device, usb_work); 8538c0984e5SSebastian Reichel 8548c0984e5SSebastian Reichel switch (bq->usb_event) { 8558c0984e5SSebastian Reichel case USB_EVENT_ID: 8568c0984e5SSebastian Reichel /* Enable boost mode */ 8575575802dSHans de Goede bq25890_set_otg_cfg(bq, 1); 8588c0984e5SSebastian Reichel break; 8598c0984e5SSebastian Reichel 8608c0984e5SSebastian Reichel case USB_EVENT_NONE: 8618c0984e5SSebastian Reichel /* Disable boost mode */ 8625575802dSHans de Goede ret = bq25890_set_otg_cfg(bq, 0); 8635575802dSHans de Goede if (ret == 0) 8648c0984e5SSebastian Reichel power_supply_changed(bq->charger); 8658c0984e5SSebastian Reichel break; 8668c0984e5SSebastian Reichel } 8678c0984e5SSebastian Reichel } 8688c0984e5SSebastian Reichel 8698c0984e5SSebastian Reichel static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, 8708c0984e5SSebastian Reichel void *priv) 8718c0984e5SSebastian Reichel { 8728c0984e5SSebastian Reichel struct bq25890_device *bq = 8738c0984e5SSebastian Reichel container_of(nb, struct bq25890_device, usb_nb); 8748c0984e5SSebastian Reichel 8758c0984e5SSebastian Reichel bq->usb_event = val; 8768c0984e5SSebastian Reichel queue_work(system_power_efficient_wq, &bq->usb_work); 8778c0984e5SSebastian Reichel 8788c0984e5SSebastian Reichel return NOTIFY_OK; 8798c0984e5SSebastian Reichel } 8808c0984e5SSebastian Reichel 881*79d35365SHans de Goede #ifdef CONFIG_REGULATOR 882*79d35365SHans de Goede static int bq25890_vbus_enable(struct regulator_dev *rdev) 883*79d35365SHans de Goede { 884*79d35365SHans de Goede struct bq25890_device *bq = rdev_get_drvdata(rdev); 885*79d35365SHans de Goede 886*79d35365SHans de Goede return bq25890_set_otg_cfg(bq, 1); 887*79d35365SHans de Goede } 888*79d35365SHans de Goede 889*79d35365SHans de Goede static int bq25890_vbus_disable(struct regulator_dev *rdev) 890*79d35365SHans de Goede { 891*79d35365SHans de Goede struct bq25890_device *bq = rdev_get_drvdata(rdev); 892*79d35365SHans de Goede 893*79d35365SHans de Goede return bq25890_set_otg_cfg(bq, 0); 894*79d35365SHans de Goede } 895*79d35365SHans de Goede 896*79d35365SHans de Goede static int bq25890_vbus_is_enabled(struct regulator_dev *rdev) 897*79d35365SHans de Goede { 898*79d35365SHans de Goede struct bq25890_device *bq = rdev_get_drvdata(rdev); 899*79d35365SHans de Goede 900*79d35365SHans de Goede return bq25890_field_read(bq, F_OTG_CFG); 901*79d35365SHans de Goede } 902*79d35365SHans de Goede 903*79d35365SHans de Goede static const struct regulator_ops bq25890_vbus_ops = { 904*79d35365SHans de Goede .enable = bq25890_vbus_enable, 905*79d35365SHans de Goede .disable = bq25890_vbus_disable, 906*79d35365SHans de Goede .is_enabled = bq25890_vbus_is_enabled, 907*79d35365SHans de Goede }; 908*79d35365SHans de Goede 909*79d35365SHans de Goede static const struct regulator_desc bq25890_vbus_desc = { 910*79d35365SHans de Goede .name = "usb_otg_vbus", 911*79d35365SHans de Goede .of_match = "usb-otg-vbus", 912*79d35365SHans de Goede .type = REGULATOR_VOLTAGE, 913*79d35365SHans de Goede .owner = THIS_MODULE, 914*79d35365SHans de Goede .ops = &bq25890_vbus_ops, 915*79d35365SHans de Goede .fixed_uV = 5000000, 916*79d35365SHans de Goede .n_voltages = 1, 917*79d35365SHans de Goede }; 918*79d35365SHans de Goede #endif 919*79d35365SHans de Goede 920d20267c9SYauhen Kharuzhy static int bq25890_get_chip_version(struct bq25890_device *bq) 921d20267c9SYauhen Kharuzhy { 922d20267c9SYauhen Kharuzhy int id, rev; 923d20267c9SYauhen Kharuzhy 924d20267c9SYauhen Kharuzhy id = bq25890_field_read(bq, F_PN); 925d20267c9SYauhen Kharuzhy if (id < 0) { 926172d0cceSMartin Kepplinger dev_err(bq->dev, "Cannot read chip ID: %d\n", id); 927d20267c9SYauhen Kharuzhy return id; 928d20267c9SYauhen Kharuzhy } 929d20267c9SYauhen Kharuzhy 930d20267c9SYauhen Kharuzhy rev = bq25890_field_read(bq, F_DEV_REV); 931d20267c9SYauhen Kharuzhy if (rev < 0) { 932172d0cceSMartin Kepplinger dev_err(bq->dev, "Cannot read chip revision: %d\n", rev); 933cb619e80SColin Ian King return rev; 934d20267c9SYauhen Kharuzhy } 935d20267c9SYauhen Kharuzhy 936d20267c9SYauhen Kharuzhy switch (id) { 937d20267c9SYauhen Kharuzhy case BQ25890_ID: 938d20267c9SYauhen Kharuzhy bq->chip_version = BQ25890; 939d20267c9SYauhen Kharuzhy break; 940d20267c9SYauhen Kharuzhy 941d20267c9SYauhen Kharuzhy /* BQ25892 and BQ25896 share same ID 0 */ 942d20267c9SYauhen Kharuzhy case BQ25896_ID: 943d20267c9SYauhen Kharuzhy switch (rev) { 944d20267c9SYauhen Kharuzhy case 2: 945d20267c9SYauhen Kharuzhy bq->chip_version = BQ25896; 946d20267c9SYauhen Kharuzhy break; 947d20267c9SYauhen Kharuzhy case 1: 948d20267c9SYauhen Kharuzhy bq->chip_version = BQ25892; 949d20267c9SYauhen Kharuzhy break; 950d20267c9SYauhen Kharuzhy default: 951d20267c9SYauhen Kharuzhy dev_err(bq->dev, 952d20267c9SYauhen Kharuzhy "Unknown device revision %d, assume BQ25892\n", 953d20267c9SYauhen Kharuzhy rev); 954d20267c9SYauhen Kharuzhy bq->chip_version = BQ25892; 955d20267c9SYauhen Kharuzhy } 956d20267c9SYauhen Kharuzhy break; 957d20267c9SYauhen Kharuzhy 958d20267c9SYauhen Kharuzhy case BQ25895_ID: 959d20267c9SYauhen Kharuzhy bq->chip_version = BQ25895; 960d20267c9SYauhen Kharuzhy break; 961d20267c9SYauhen Kharuzhy 962d20267c9SYauhen Kharuzhy default: 963d20267c9SYauhen Kharuzhy dev_err(bq->dev, "Unknown chip ID %d\n", id); 964d20267c9SYauhen Kharuzhy return -ENODEV; 965d20267c9SYauhen Kharuzhy } 966d20267c9SYauhen Kharuzhy 967d20267c9SYauhen Kharuzhy return 0; 968d20267c9SYauhen Kharuzhy } 969d20267c9SYauhen Kharuzhy 9708c0984e5SSebastian Reichel static int bq25890_irq_probe(struct bq25890_device *bq) 9718c0984e5SSebastian Reichel { 9728c0984e5SSebastian Reichel struct gpio_desc *irq; 9738c0984e5SSebastian Reichel 97486775879SAndy Shevchenko irq = devm_gpiod_get(bq->dev, BQ25890_IRQ_PIN, GPIOD_IN); 975172d0cceSMartin Kepplinger if (IS_ERR(irq)) 976172d0cceSMartin Kepplinger return dev_err_probe(bq->dev, PTR_ERR(irq), 977172d0cceSMartin Kepplinger "Could not probe irq pin.\n"); 9788c0984e5SSebastian Reichel 9798c0984e5SSebastian Reichel return gpiod_to_irq(irq); 9808c0984e5SSebastian Reichel } 9818c0984e5SSebastian Reichel 9828c0984e5SSebastian Reichel static int bq25890_fw_read_u32_props(struct bq25890_device *bq) 9838c0984e5SSebastian Reichel { 9848c0984e5SSebastian Reichel int ret; 9858c0984e5SSebastian Reichel u32 property; 9868c0984e5SSebastian Reichel int i; 9878c0984e5SSebastian Reichel struct bq25890_init_data *init = &bq->init_data; 9888c0984e5SSebastian Reichel struct { 9898c0984e5SSebastian Reichel char *name; 9908c0984e5SSebastian Reichel bool optional; 9918c0984e5SSebastian Reichel enum bq25890_table_ids tbl_id; 9928c0984e5SSebastian Reichel u8 *conv_data; /* holds converted value from given property */ 9938c0984e5SSebastian Reichel } props[] = { 9948c0984e5SSebastian Reichel /* required properties */ 9958c0984e5SSebastian Reichel {"ti,charge-current", false, TBL_ICHG, &init->ichg}, 9968c0984e5SSebastian Reichel {"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg}, 9978c0984e5SSebastian Reichel {"ti,termination-current", false, TBL_ITERM, &init->iterm}, 9988c0984e5SSebastian Reichel {"ti,precharge-current", false, TBL_ITERM, &init->iprechg}, 9998c0984e5SSebastian Reichel {"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin}, 10008c0984e5SSebastian Reichel {"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv}, 10018c0984e5SSebastian Reichel {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti}, 10028c0984e5SSebastian Reichel 10038c0984e5SSebastian Reichel /* optional properties */ 100472408329SMichał Mirosław {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg}, 100572408329SMichał Mirosław {"ti,ibatcomp-micro-ohms", true, TBL_RBATCOMP, &init->rbatcomp}, 100672408329SMichał Mirosław {"ti,ibatcomp-clamp-microvolt", true, TBL_VBATCOMP, &init->vclamp}, 10078c0984e5SSebastian Reichel }; 10088c0984e5SSebastian Reichel 10098c0984e5SSebastian Reichel /* initialize data for optional properties */ 10108c0984e5SSebastian Reichel init->treg = 3; /* 120 degrees Celsius */ 101172408329SMichał Mirosław init->rbatcomp = init->vclamp = 0; /* IBAT compensation disabled */ 10128c0984e5SSebastian Reichel 10138c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(props); i++) { 10148c0984e5SSebastian Reichel ret = device_property_read_u32(bq->dev, props[i].name, 10158c0984e5SSebastian Reichel &property); 10168c0984e5SSebastian Reichel if (ret < 0) { 10178c0984e5SSebastian Reichel if (props[i].optional) 10188c0984e5SSebastian Reichel continue; 10198c0984e5SSebastian Reichel 10209d9ae341SAngus Ainslie (Purism) dev_err(bq->dev, "Unable to read property %d %s\n", ret, 10219d9ae341SAngus Ainslie (Purism) props[i].name); 10229d9ae341SAngus Ainslie (Purism) 10238c0984e5SSebastian Reichel return ret; 10248c0984e5SSebastian Reichel } 10258c0984e5SSebastian Reichel 10268c0984e5SSebastian Reichel *props[i].conv_data = bq25890_find_idx(property, 10278c0984e5SSebastian Reichel props[i].tbl_id); 10288c0984e5SSebastian Reichel } 10298c0984e5SSebastian Reichel 10308c0984e5SSebastian Reichel return 0; 10318c0984e5SSebastian Reichel } 10328c0984e5SSebastian Reichel 10338c0984e5SSebastian Reichel static int bq25890_fw_probe(struct bq25890_device *bq) 10348c0984e5SSebastian Reichel { 10358c0984e5SSebastian Reichel int ret; 10368c0984e5SSebastian Reichel struct bq25890_init_data *init = &bq->init_data; 10378c0984e5SSebastian Reichel 10387e3b8e35SHans de Goede bq->skip_reset = device_property_read_bool(bq->dev, "linux,skip-reset"); 103940428bd4SHans de Goede bq->read_back_init_data = device_property_read_bool(bq->dev, 104040428bd4SHans de Goede "linux,read-back-settings"); 104140428bd4SHans de Goede if (bq->read_back_init_data) 104240428bd4SHans de Goede return 0; 10437e3b8e35SHans de Goede 10448c0984e5SSebastian Reichel ret = bq25890_fw_read_u32_props(bq); 10458c0984e5SSebastian Reichel if (ret < 0) 10468c0984e5SSebastian Reichel return ret; 10478c0984e5SSebastian Reichel 10488c0984e5SSebastian Reichel init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin"); 10498c0984e5SSebastian Reichel init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq"); 10508c0984e5SSebastian Reichel 10518c0984e5SSebastian Reichel return 0; 10528c0984e5SSebastian Reichel } 10538c0984e5SSebastian Reichel 10548c0984e5SSebastian Reichel static int bq25890_probe(struct i2c_client *client, 10558c0984e5SSebastian Reichel const struct i2c_device_id *id) 10568c0984e5SSebastian Reichel { 10578c0984e5SSebastian Reichel struct device *dev = &client->dev; 10588c0984e5SSebastian Reichel struct bq25890_device *bq; 10598c0984e5SSebastian Reichel int ret; 10608c0984e5SSebastian Reichel int i; 10618c0984e5SSebastian Reichel 10628c0984e5SSebastian Reichel bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); 10638c0984e5SSebastian Reichel if (!bq) 10648c0984e5SSebastian Reichel return -ENOMEM; 10658c0984e5SSebastian Reichel 10668c0984e5SSebastian Reichel bq->client = client; 10678c0984e5SSebastian Reichel bq->dev = dev; 10688c0984e5SSebastian Reichel 10698c0984e5SSebastian Reichel mutex_init(&bq->lock); 10708c0984e5SSebastian Reichel 10718c0984e5SSebastian Reichel bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config); 1072172d0cceSMartin Kepplinger if (IS_ERR(bq->rmap)) 1073172d0cceSMartin Kepplinger return dev_err_probe(dev, PTR_ERR(bq->rmap), 1074172d0cceSMartin Kepplinger "failed to allocate register map\n"); 10758c0984e5SSebastian Reichel 10768c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) { 10778c0984e5SSebastian Reichel const struct reg_field *reg_fields = bq25890_reg_fields; 10788c0984e5SSebastian Reichel 10798c0984e5SSebastian Reichel bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap, 10808c0984e5SSebastian Reichel reg_fields[i]); 1081172d0cceSMartin Kepplinger if (IS_ERR(bq->rmap_fields[i])) 1082172d0cceSMartin Kepplinger return dev_err_probe(dev, PTR_ERR(bq->rmap_fields[i]), 1083172d0cceSMartin Kepplinger "cannot allocate regmap field\n"); 10848c0984e5SSebastian Reichel } 10858c0984e5SSebastian Reichel 10868c0984e5SSebastian Reichel i2c_set_clientdata(client, bq); 10878c0984e5SSebastian Reichel 1088d20267c9SYauhen Kharuzhy ret = bq25890_get_chip_version(bq); 1089d20267c9SYauhen Kharuzhy if (ret) { 1090172d0cceSMartin Kepplinger dev_err(dev, "Cannot read chip ID or unknown chip: %d\n", ret); 1091d20267c9SYauhen Kharuzhy return ret; 10928c0984e5SSebastian Reichel } 10938c0984e5SSebastian Reichel 10948c0984e5SSebastian Reichel ret = bq25890_fw_probe(bq); 1095f481d5b8SHans de Goede if (ret < 0) 1096f481d5b8SHans de Goede return dev_err_probe(dev, ret, "reading device properties\n"); 10978c0984e5SSebastian Reichel 10988c0984e5SSebastian Reichel ret = bq25890_hw_init(bq); 10998c0984e5SSebastian Reichel if (ret < 0) { 1100172d0cceSMartin Kepplinger dev_err(dev, "Cannot initialize the chip: %d\n", ret); 11018c0984e5SSebastian Reichel return ret; 11028c0984e5SSebastian Reichel } 11038c0984e5SSebastian Reichel 11048c0984e5SSebastian Reichel if (client->irq <= 0) 11058c0984e5SSebastian Reichel client->irq = bq25890_irq_probe(bq); 11068c0984e5SSebastian Reichel 11078c0984e5SSebastian Reichel if (client->irq < 0) { 11088c0984e5SSebastian Reichel dev_err(dev, "No irq resource found.\n"); 11098c0984e5SSebastian Reichel return client->irq; 11108c0984e5SSebastian Reichel } 11118c0984e5SSebastian Reichel 11128c0984e5SSebastian Reichel /* OTG reporting */ 11138c0984e5SSebastian Reichel bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); 11148c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) { 11158c0984e5SSebastian Reichel INIT_WORK(&bq->usb_work, bq25890_usb_work); 11168c0984e5SSebastian Reichel bq->usb_nb.notifier_call = bq25890_usb_notifier; 11178c0984e5SSebastian Reichel usb_register_notifier(bq->usb_phy, &bq->usb_nb); 11188c0984e5SSebastian Reichel } 1119*79d35365SHans de Goede #ifdef CONFIG_REGULATOR 1120*79d35365SHans de Goede else { 1121*79d35365SHans de Goede struct bq25890_platform_data *pdata = dev_get_platdata(dev); 1122*79d35365SHans de Goede struct regulator_config cfg = { }; 1123*79d35365SHans de Goede struct regulator_dev *reg; 1124*79d35365SHans de Goede 1125*79d35365SHans de Goede cfg.dev = dev; 1126*79d35365SHans de Goede cfg.driver_data = bq; 1127*79d35365SHans de Goede if (pdata) 1128*79d35365SHans de Goede cfg.init_data = pdata->regulator_init_data; 1129*79d35365SHans de Goede 1130*79d35365SHans de Goede reg = devm_regulator_register(dev, &bq25890_vbus_desc, &cfg); 1131*79d35365SHans de Goede if (IS_ERR(reg)) 1132*79d35365SHans de Goede return dev_err_probe(dev, PTR_ERR(reg), "registering regulator"); 1133*79d35365SHans de Goede } 1134*79d35365SHans de Goede #endif 11358c0984e5SSebastian Reichel 1136d01363daSHans de Goede ret = bq25890_power_supply_init(bq); 1137d01363daSHans de Goede if (ret < 0) { 1138d01363daSHans de Goede dev_err(dev, "Failed to register power supply\n"); 1139d01363daSHans de Goede goto err_unregister_usb_notifier; 1140d01363daSHans de Goede } 1141d01363daSHans de Goede 11428c0984e5SSebastian Reichel ret = devm_request_threaded_irq(dev, client->irq, NULL, 11438c0984e5SSebastian Reichel bq25890_irq_handler_thread, 11448c0984e5SSebastian Reichel IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 11458c0984e5SSebastian Reichel BQ25890_IRQ_PIN, bq); 11468c0984e5SSebastian Reichel if (ret) 1147d01363daSHans de Goede goto err_unregister_usb_notifier; 11488c0984e5SSebastian Reichel 11498c0984e5SSebastian Reichel return 0; 11508c0984e5SSebastian Reichel 1151d01363daSHans de Goede err_unregister_usb_notifier: 11528c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) 11538c0984e5SSebastian Reichel usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); 11548c0984e5SSebastian Reichel 11558c0984e5SSebastian Reichel return ret; 11568c0984e5SSebastian Reichel } 11578c0984e5SSebastian Reichel 11588c0984e5SSebastian Reichel static int bq25890_remove(struct i2c_client *client) 11598c0984e5SSebastian Reichel { 11608c0984e5SSebastian Reichel struct bq25890_device *bq = i2c_get_clientdata(client); 11618c0984e5SSebastian Reichel 11628c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) 11638c0984e5SSebastian Reichel usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); 11648c0984e5SSebastian Reichel 11657e3b8e35SHans de Goede if (!bq->skip_reset) { 11668c0984e5SSebastian Reichel /* reset all registers to default values */ 11678c0984e5SSebastian Reichel bq25890_chip_reset(bq); 11687e3b8e35SHans de Goede } 11698c0984e5SSebastian Reichel 11708c0984e5SSebastian Reichel return 0; 11718c0984e5SSebastian Reichel } 11728c0984e5SSebastian Reichel 1173*79d35365SHans de Goede static void bq25890_shutdown(struct i2c_client *client) 1174*79d35365SHans de Goede { 1175*79d35365SHans de Goede struct bq25890_device *bq = i2c_get_clientdata(client); 1176*79d35365SHans de Goede 1177*79d35365SHans de Goede /* 1178*79d35365SHans de Goede * TODO this if + return should probably be removed, but that would 1179*79d35365SHans de Goede * introduce a function change for boards using the usb-phy framework. 1180*79d35365SHans de Goede * This needs to be tested on such a board before making this change. 1181*79d35365SHans de Goede */ 1182*79d35365SHans de Goede if (!IS_ERR_OR_NULL(bq->usb_phy)) 1183*79d35365SHans de Goede return; 1184*79d35365SHans de Goede 1185*79d35365SHans de Goede /* 1186*79d35365SHans de Goede * Turn off the 5v Boost regulator which outputs Vbus to the device's 1187*79d35365SHans de Goede * Micro-USB or Type-C USB port. Leaving this on drains power and 1188*79d35365SHans de Goede * this avoids the PMIC on some device-models seeing this as Vbus 1189*79d35365SHans de Goede * getting inserted after shutdown, causing the device to immediately 1190*79d35365SHans de Goede * power-up again. 1191*79d35365SHans de Goede */ 1192*79d35365SHans de Goede bq25890_set_otg_cfg(bq, 0); 1193*79d35365SHans de Goede } 1194*79d35365SHans de Goede 11958c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP 11968c0984e5SSebastian Reichel static int bq25890_suspend(struct device *dev) 11978c0984e5SSebastian Reichel { 11988c0984e5SSebastian Reichel struct bq25890_device *bq = dev_get_drvdata(dev); 11998c0984e5SSebastian Reichel 12008c0984e5SSebastian Reichel /* 12018c0984e5SSebastian Reichel * If charger is removed, while in suspend, make sure ADC is diabled 12028c0984e5SSebastian Reichel * since it consumes slightly more power. 12038c0984e5SSebastian Reichel */ 120421d90edaSMichał Mirosław return bq25890_field_write(bq, F_CONV_RATE, 0); 12058c0984e5SSebastian Reichel } 12068c0984e5SSebastian Reichel 12078c0984e5SSebastian Reichel static int bq25890_resume(struct device *dev) 12088c0984e5SSebastian Reichel { 12098c0984e5SSebastian Reichel int ret; 12108c0984e5SSebastian Reichel struct bq25890_device *bq = dev_get_drvdata(dev); 12118c0984e5SSebastian Reichel 121272d9cd9cSMichał Mirosław mutex_lock(&bq->lock); 121372d9cd9cSMichał Mirosław 121472d9cd9cSMichał Mirosław ret = bq25890_get_chip_state(bq, &bq->state); 12158c0984e5SSebastian Reichel if (ret < 0) 1216cf5701bfSDan Carpenter goto unlock; 12178c0984e5SSebastian Reichel 12188c0984e5SSebastian Reichel /* Re-enable ADC only if charger is plugged in. */ 121972d9cd9cSMichał Mirosław if (bq->state.online) { 122021d90edaSMichał Mirosław ret = bq25890_field_write(bq, F_CONV_RATE, 1); 12218c0984e5SSebastian Reichel if (ret < 0) 1222cf5701bfSDan Carpenter goto unlock; 12238c0984e5SSebastian Reichel } 12248c0984e5SSebastian Reichel 12258c0984e5SSebastian Reichel /* signal userspace, maybe state changed while suspended */ 12268c0984e5SSebastian Reichel power_supply_changed(bq->charger); 12278c0984e5SSebastian Reichel 1228cf5701bfSDan Carpenter unlock: 122972d9cd9cSMichał Mirosław mutex_unlock(&bq->lock); 123072d9cd9cSMichał Mirosław 1231cf5701bfSDan Carpenter return ret; 12328c0984e5SSebastian Reichel } 12338c0984e5SSebastian Reichel #endif 12348c0984e5SSebastian Reichel 12358c0984e5SSebastian Reichel static const struct dev_pm_ops bq25890_pm = { 12368c0984e5SSebastian Reichel SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume) 12378c0984e5SSebastian Reichel }; 12388c0984e5SSebastian Reichel 12398c0984e5SSebastian Reichel static const struct i2c_device_id bq25890_i2c_ids[] = { 12408c0984e5SSebastian Reichel { "bq25890", 0 }, 124146aa27e7SYauhen Kharuzhy { "bq25892", 0 }, 124246aa27e7SYauhen Kharuzhy { "bq25895", 0 }, 124346aa27e7SYauhen Kharuzhy { "bq25896", 0 }, 12448c0984e5SSebastian Reichel {}, 12458c0984e5SSebastian Reichel }; 12468c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids); 12478c0984e5SSebastian Reichel 12488c0984e5SSebastian Reichel static const struct of_device_id bq25890_of_match[] = { 12498c0984e5SSebastian Reichel { .compatible = "ti,bq25890", }, 125046aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25892", }, 125146aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25895", }, 125246aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25896", }, 12538c0984e5SSebastian Reichel { }, 12548c0984e5SSebastian Reichel }; 12558c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, bq25890_of_match); 12568c0984e5SSebastian Reichel 125702067dc9SKrzysztof Kozlowski #ifdef CONFIG_ACPI 12588c0984e5SSebastian Reichel static const struct acpi_device_id bq25890_acpi_match[] = { 12598c0984e5SSebastian Reichel {"BQ258900", 0}, 12608c0984e5SSebastian Reichel {}, 12618c0984e5SSebastian Reichel }; 12628c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match); 126302067dc9SKrzysztof Kozlowski #endif 12648c0984e5SSebastian Reichel 12658c0984e5SSebastian Reichel static struct i2c_driver bq25890_driver = { 12668c0984e5SSebastian Reichel .driver = { 12678c0984e5SSebastian Reichel .name = "bq25890-charger", 12688c0984e5SSebastian Reichel .of_match_table = of_match_ptr(bq25890_of_match), 12698c0984e5SSebastian Reichel .acpi_match_table = ACPI_PTR(bq25890_acpi_match), 12708c0984e5SSebastian Reichel .pm = &bq25890_pm, 12718c0984e5SSebastian Reichel }, 12728c0984e5SSebastian Reichel .probe = bq25890_probe, 12738c0984e5SSebastian Reichel .remove = bq25890_remove, 1274*79d35365SHans de Goede .shutdown = bq25890_shutdown, 12758c0984e5SSebastian Reichel .id_table = bq25890_i2c_ids, 12768c0984e5SSebastian Reichel }; 12778c0984e5SSebastian Reichel module_i2c_driver(bq25890_driver); 12788c0984e5SSebastian Reichel 12798c0984e5SSebastian Reichel MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); 12808c0984e5SSebastian Reichel MODULE_DESCRIPTION("bq25890 charger driver"); 12818c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 1282