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> 118c0984e5SSebastian Reichel #include <linux/regmap.h> 128c0984e5SSebastian Reichel #include <linux/types.h> 138c0984e5SSebastian Reichel #include <linux/gpio/consumer.h> 148c0984e5SSebastian Reichel #include <linux/interrupt.h> 158c0984e5SSebastian Reichel #include <linux/delay.h> 168c0984e5SSebastian Reichel #include <linux/usb/phy.h> 178c0984e5SSebastian Reichel 188c0984e5SSebastian Reichel #include <linux/acpi.h> 198c0984e5SSebastian Reichel #include <linux/of.h> 208c0984e5SSebastian Reichel 218c0984e5SSebastian Reichel #define BQ25890_MANUFACTURER "Texas Instruments" 228c0984e5SSebastian Reichel #define BQ25890_IRQ_PIN "bq25890_irq" 238c0984e5SSebastian Reichel 248c0984e5SSebastian Reichel #define BQ25890_ID 3 255c35ba9bSAngus Ainslie (Purism) #define BQ25895_ID 7 262e1a2ddeSAngus Ainslie (Purism) #define BQ25896_ID 0 278c0984e5SSebastian Reichel 28d20267c9SYauhen Kharuzhy enum bq25890_chip_version { 29d20267c9SYauhen Kharuzhy BQ25890, 30d20267c9SYauhen Kharuzhy BQ25892, 31d20267c9SYauhen Kharuzhy BQ25895, 32d20267c9SYauhen Kharuzhy BQ25896, 33d20267c9SYauhen Kharuzhy }; 34d20267c9SYauhen Kharuzhy 355956fca7SMichał Mirosław static const char *const bq25890_chip_name[] = { 365956fca7SMichał Mirosław "BQ25890", 375956fca7SMichał Mirosław "BQ25892", 385956fca7SMichał Mirosław "BQ25895", 395956fca7SMichał Mirosław "BQ25896", 405956fca7SMichał Mirosław }; 415956fca7SMichał Mirosław 428c0984e5SSebastian Reichel enum bq25890_fields { 43766873c1SYauhen Kharuzhy F_EN_HIZ, F_EN_ILIM, F_IINLIM, /* Reg00 */ 448c0984e5SSebastian Reichel F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */ 458c0984e5SSebastian Reichel F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN, 468c0984e5SSebastian Reichel F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */ 47d20267c9SYauhen Kharuzhy F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN, 48d20267c9SYauhen Kharuzhy F_MIN_VBAT_SEL, /* Reg03 */ 498c0984e5SSebastian Reichel F_PUMPX_EN, F_ICHG, /* Reg04 */ 508c0984e5SSebastian Reichel F_IPRECHG, F_ITERM, /* Reg05 */ 518c0984e5SSebastian Reichel F_VREG, F_BATLOWV, F_VRECHG, /* Reg06 */ 528c0984e5SSebastian Reichel F_TERM_EN, F_STAT_DIS, F_WD, F_TMR_EN, F_CHG_TMR, 538c0984e5SSebastian Reichel F_JEITA_ISET, /* Reg07 */ 548c0984e5SSebastian Reichel F_BATCMP, F_VCLAMP, F_TREG, /* Reg08 */ 558c0984e5SSebastian Reichel F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET, 568c0984e5SSebastian Reichel F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN, /* Reg09 */ 57d20267c9SYauhen Kharuzhy F_BOOSTV, F_PFM_OTG_DIS, F_BOOSTI, /* Reg0A */ 58d20267c9SYauhen Kharuzhy F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_0B_RSVD, 59d20267c9SYauhen Kharuzhy F_VSYS_STAT, /* Reg0B */ 608c0984e5SSebastian Reichel F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT, 618c0984e5SSebastian Reichel F_NTC_FAULT, /* Reg0C */ 628c0984e5SSebastian Reichel F_FORCE_VINDPM, F_VINDPM, /* Reg0D */ 638c0984e5SSebastian Reichel F_THERM_STAT, F_BATV, /* Reg0E */ 648c0984e5SSebastian Reichel F_SYSV, /* Reg0F */ 658c0984e5SSebastian Reichel F_TSPCT, /* Reg10 */ 668c0984e5SSebastian Reichel F_VBUS_GD, F_VBUSV, /* Reg11 */ 678c0984e5SSebastian Reichel F_ICHGR, /* Reg12 */ 688c0984e5SSebastian Reichel F_VDPM_STAT, F_IDPM_STAT, F_IDPM_LIM, /* Reg13 */ 698c0984e5SSebastian Reichel F_REG_RST, F_ICO_OPTIMIZED, F_PN, F_TS_PROFILE, F_DEV_REV, /* Reg14 */ 708c0984e5SSebastian Reichel 718c0984e5SSebastian Reichel F_MAX_FIELDS 728c0984e5SSebastian Reichel }; 738c0984e5SSebastian Reichel 748c0984e5SSebastian Reichel /* initial field values, converted to register values */ 758c0984e5SSebastian Reichel struct bq25890_init_data { 768c0984e5SSebastian Reichel u8 ichg; /* charge current */ 778c0984e5SSebastian Reichel u8 vreg; /* regulation voltage */ 788c0984e5SSebastian Reichel u8 iterm; /* termination current */ 798c0984e5SSebastian Reichel u8 iprechg; /* precharge current */ 808c0984e5SSebastian Reichel u8 sysvmin; /* minimum system voltage limit */ 818c0984e5SSebastian Reichel u8 boostv; /* boost regulation voltage */ 828c0984e5SSebastian Reichel u8 boosti; /* boost current limit */ 838c0984e5SSebastian Reichel u8 boostf; /* boost frequency */ 848c0984e5SSebastian Reichel u8 ilim_en; /* enable ILIM pin */ 858c0984e5SSebastian Reichel u8 treg; /* thermal regulation threshold */ 8672408329SMichał Mirosław u8 rbatcomp; /* IBAT sense resistor value */ 8772408329SMichał Mirosław u8 vclamp; /* IBAT compensation voltage limit */ 888c0984e5SSebastian Reichel }; 898c0984e5SSebastian Reichel 908c0984e5SSebastian Reichel struct bq25890_state { 918c0984e5SSebastian Reichel u8 online; 928c0984e5SSebastian Reichel u8 chrg_status; 938c0984e5SSebastian Reichel u8 chrg_fault; 948c0984e5SSebastian Reichel u8 vsys_status; 958c0984e5SSebastian Reichel u8 boost_fault; 968c0984e5SSebastian Reichel u8 bat_fault; 97c562a43aSYauhen Kharuzhy u8 ntc_fault; 988c0984e5SSebastian Reichel }; 998c0984e5SSebastian Reichel 1008c0984e5SSebastian Reichel struct bq25890_device { 1018c0984e5SSebastian Reichel struct i2c_client *client; 1028c0984e5SSebastian Reichel struct device *dev; 1038c0984e5SSebastian Reichel struct power_supply *charger; 1048c0984e5SSebastian Reichel 1058c0984e5SSebastian Reichel struct usb_phy *usb_phy; 1068c0984e5SSebastian Reichel struct notifier_block usb_nb; 1078c0984e5SSebastian Reichel struct work_struct usb_work; 1088c0984e5SSebastian Reichel unsigned long usb_event; 1098c0984e5SSebastian Reichel 1108c0984e5SSebastian Reichel struct regmap *rmap; 1118c0984e5SSebastian Reichel struct regmap_field *rmap_fields[F_MAX_FIELDS]; 1128c0984e5SSebastian Reichel 1137e3b8e35SHans de Goede bool skip_reset; 11440428bd4SHans de Goede bool read_back_init_data; 115d20267c9SYauhen Kharuzhy enum bq25890_chip_version chip_version; 1168c0984e5SSebastian Reichel struct bq25890_init_data init_data; 1178c0984e5SSebastian Reichel struct bq25890_state state; 1188c0984e5SSebastian Reichel 1198c0984e5SSebastian Reichel struct mutex lock; /* protect state data */ 1208c0984e5SSebastian Reichel }; 1218c0984e5SSebastian Reichel 1228c0984e5SSebastian Reichel static const struct regmap_range bq25890_readonly_reg_ranges[] = { 1238c0984e5SSebastian Reichel regmap_reg_range(0x0b, 0x0c), 1248c0984e5SSebastian Reichel regmap_reg_range(0x0e, 0x13), 1258c0984e5SSebastian Reichel }; 1268c0984e5SSebastian Reichel 1278c0984e5SSebastian Reichel static const struct regmap_access_table bq25890_writeable_regs = { 1288c0984e5SSebastian Reichel .no_ranges = bq25890_readonly_reg_ranges, 1298c0984e5SSebastian Reichel .n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges), 1308c0984e5SSebastian Reichel }; 1318c0984e5SSebastian Reichel 1328c0984e5SSebastian Reichel static const struct regmap_range bq25890_volatile_reg_ranges[] = { 1338c0984e5SSebastian Reichel regmap_reg_range(0x00, 0x00), 13421d90edaSMichał Mirosław regmap_reg_range(0x02, 0x02), 1358c0984e5SSebastian Reichel regmap_reg_range(0x09, 0x09), 136d20267c9SYauhen Kharuzhy regmap_reg_range(0x0b, 0x14), 1378c0984e5SSebastian Reichel }; 1388c0984e5SSebastian Reichel 1398c0984e5SSebastian Reichel static const struct regmap_access_table bq25890_volatile_regs = { 1408c0984e5SSebastian Reichel .yes_ranges = bq25890_volatile_reg_ranges, 1418c0984e5SSebastian Reichel .n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges), 1428c0984e5SSebastian Reichel }; 1438c0984e5SSebastian Reichel 1448c0984e5SSebastian Reichel static const struct regmap_config bq25890_regmap_config = { 1458c0984e5SSebastian Reichel .reg_bits = 8, 1468c0984e5SSebastian Reichel .val_bits = 8, 1478c0984e5SSebastian Reichel 1488c0984e5SSebastian Reichel .max_register = 0x14, 1498c0984e5SSebastian Reichel .cache_type = REGCACHE_RBTREE, 1508c0984e5SSebastian Reichel 1518c0984e5SSebastian Reichel .wr_table = &bq25890_writeable_regs, 1528c0984e5SSebastian Reichel .volatile_table = &bq25890_volatile_regs, 1538c0984e5SSebastian Reichel }; 1548c0984e5SSebastian Reichel 1558c0984e5SSebastian Reichel static const struct reg_field bq25890_reg_fields[] = { 1568c0984e5SSebastian Reichel /* REG00 */ 1578c0984e5SSebastian Reichel [F_EN_HIZ] = REG_FIELD(0x00, 7, 7), 1588c0984e5SSebastian Reichel [F_EN_ILIM] = REG_FIELD(0x00, 6, 6), 159766873c1SYauhen Kharuzhy [F_IINLIM] = REG_FIELD(0x00, 0, 5), 1608c0984e5SSebastian Reichel /* REG01 */ 1618c0984e5SSebastian Reichel [F_BHOT] = REG_FIELD(0x01, 6, 7), 1628c0984e5SSebastian Reichel [F_BCOLD] = REG_FIELD(0x01, 5, 5), 1638c0984e5SSebastian Reichel [F_VINDPM_OFS] = REG_FIELD(0x01, 0, 4), 1648c0984e5SSebastian Reichel /* REG02 */ 1658c0984e5SSebastian Reichel [F_CONV_START] = REG_FIELD(0x02, 7, 7), 1668c0984e5SSebastian Reichel [F_CONV_RATE] = REG_FIELD(0x02, 6, 6), 1678c0984e5SSebastian Reichel [F_BOOSTF] = REG_FIELD(0x02, 5, 5), 1688c0984e5SSebastian Reichel [F_ICO_EN] = REG_FIELD(0x02, 4, 4), 1692e1a2ddeSAngus Ainslie (Purism) [F_HVDCP_EN] = REG_FIELD(0x02, 3, 3), // reserved on BQ25896 1702e1a2ddeSAngus Ainslie (Purism) [F_MAXC_EN] = REG_FIELD(0x02, 2, 2), // reserved on BQ25896 1718c0984e5SSebastian Reichel [F_FORCE_DPM] = REG_FIELD(0x02, 1, 1), 1728c0984e5SSebastian Reichel [F_AUTO_DPDM_EN] = REG_FIELD(0x02, 0, 0), 1738c0984e5SSebastian Reichel /* REG03 */ 1748c0984e5SSebastian Reichel [F_BAT_LOAD_EN] = REG_FIELD(0x03, 7, 7), 1758c0984e5SSebastian Reichel [F_WD_RST] = REG_FIELD(0x03, 6, 6), 1768c0984e5SSebastian Reichel [F_OTG_CFG] = REG_FIELD(0x03, 5, 5), 1778c0984e5SSebastian Reichel [F_CHG_CFG] = REG_FIELD(0x03, 4, 4), 1788c0984e5SSebastian Reichel [F_SYSVMIN] = REG_FIELD(0x03, 1, 3), 179d20267c9SYauhen Kharuzhy [F_MIN_VBAT_SEL] = REG_FIELD(0x03, 0, 0), // BQ25896 only 1808c0984e5SSebastian Reichel /* REG04 */ 1818c0984e5SSebastian Reichel [F_PUMPX_EN] = REG_FIELD(0x04, 7, 7), 1828c0984e5SSebastian Reichel [F_ICHG] = REG_FIELD(0x04, 0, 6), 1838c0984e5SSebastian Reichel /* REG05 */ 1848c0984e5SSebastian Reichel [F_IPRECHG] = REG_FIELD(0x05, 4, 7), 1858c0984e5SSebastian Reichel [F_ITERM] = REG_FIELD(0x05, 0, 3), 1868c0984e5SSebastian Reichel /* REG06 */ 1878c0984e5SSebastian Reichel [F_VREG] = REG_FIELD(0x06, 2, 7), 1888c0984e5SSebastian Reichel [F_BATLOWV] = REG_FIELD(0x06, 1, 1), 1898c0984e5SSebastian Reichel [F_VRECHG] = REG_FIELD(0x06, 0, 0), 1908c0984e5SSebastian Reichel /* REG07 */ 1918c0984e5SSebastian Reichel [F_TERM_EN] = REG_FIELD(0x07, 7, 7), 1928c0984e5SSebastian Reichel [F_STAT_DIS] = REG_FIELD(0x07, 6, 6), 1938c0984e5SSebastian Reichel [F_WD] = REG_FIELD(0x07, 4, 5), 1948c0984e5SSebastian Reichel [F_TMR_EN] = REG_FIELD(0x07, 3, 3), 1958c0984e5SSebastian Reichel [F_CHG_TMR] = REG_FIELD(0x07, 1, 2), 1965c35ba9bSAngus Ainslie (Purism) [F_JEITA_ISET] = REG_FIELD(0x07, 0, 0), // reserved on BQ25895 1978c0984e5SSebastian Reichel /* REG08 */ 19895809139SMichał Mirosław [F_BATCMP] = REG_FIELD(0x08, 5, 7), 1998c0984e5SSebastian Reichel [F_VCLAMP] = REG_FIELD(0x08, 2, 4), 2008c0984e5SSebastian Reichel [F_TREG] = REG_FIELD(0x08, 0, 1), 2018c0984e5SSebastian Reichel /* REG09 */ 2028c0984e5SSebastian Reichel [F_FORCE_ICO] = REG_FIELD(0x09, 7, 7), 2038c0984e5SSebastian Reichel [F_TMR2X_EN] = REG_FIELD(0x09, 6, 6), 2048c0984e5SSebastian Reichel [F_BATFET_DIS] = REG_FIELD(0x09, 5, 5), 2055c35ba9bSAngus Ainslie (Purism) [F_JEITA_VSET] = REG_FIELD(0x09, 4, 4), // reserved on BQ25895 2068c0984e5SSebastian Reichel [F_BATFET_DLY] = REG_FIELD(0x09, 3, 3), 2078c0984e5SSebastian Reichel [F_BATFET_RST_EN] = REG_FIELD(0x09, 2, 2), 2088c0984e5SSebastian Reichel [F_PUMPX_UP] = REG_FIELD(0x09, 1, 1), 2098c0984e5SSebastian Reichel [F_PUMPX_DN] = REG_FIELD(0x09, 0, 0), 2108c0984e5SSebastian Reichel /* REG0A */ 2118c0984e5SSebastian Reichel [F_BOOSTV] = REG_FIELD(0x0A, 4, 7), 2125c35ba9bSAngus Ainslie (Purism) [F_BOOSTI] = REG_FIELD(0x0A, 0, 2), // reserved on BQ25895 213d20267c9SYauhen Kharuzhy [F_PFM_OTG_DIS] = REG_FIELD(0x0A, 3, 3), // BQ25896 only 2148c0984e5SSebastian Reichel /* REG0B */ 2158c0984e5SSebastian Reichel [F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7), 2168c0984e5SSebastian Reichel [F_CHG_STAT] = REG_FIELD(0x0B, 3, 4), 2178c0984e5SSebastian Reichel [F_PG_STAT] = REG_FIELD(0x0B, 2, 2), 2182e1a2ddeSAngus Ainslie (Purism) [F_SDP_STAT] = REG_FIELD(0x0B, 1, 1), // reserved on BQ25896 2198c0984e5SSebastian Reichel [F_VSYS_STAT] = REG_FIELD(0x0B, 0, 0), 2208c0984e5SSebastian Reichel /* REG0C */ 2218c0984e5SSebastian Reichel [F_WD_FAULT] = REG_FIELD(0x0C, 7, 7), 2228c0984e5SSebastian Reichel [F_BOOST_FAULT] = REG_FIELD(0x0C, 6, 6), 2238c0984e5SSebastian Reichel [F_CHG_FAULT] = REG_FIELD(0x0C, 4, 5), 2248c0984e5SSebastian Reichel [F_BAT_FAULT] = REG_FIELD(0x0C, 3, 3), 2258c0984e5SSebastian Reichel [F_NTC_FAULT] = REG_FIELD(0x0C, 0, 2), 2268c0984e5SSebastian Reichel /* REG0D */ 2278c0984e5SSebastian Reichel [F_FORCE_VINDPM] = REG_FIELD(0x0D, 7, 7), 2288c0984e5SSebastian Reichel [F_VINDPM] = REG_FIELD(0x0D, 0, 6), 2298c0984e5SSebastian Reichel /* REG0E */ 2308c0984e5SSebastian Reichel [F_THERM_STAT] = REG_FIELD(0x0E, 7, 7), 2318c0984e5SSebastian Reichel [F_BATV] = REG_FIELD(0x0E, 0, 6), 2328c0984e5SSebastian Reichel /* REG0F */ 2338c0984e5SSebastian Reichel [F_SYSV] = REG_FIELD(0x0F, 0, 6), 2348c0984e5SSebastian Reichel /* REG10 */ 2358c0984e5SSebastian Reichel [F_TSPCT] = REG_FIELD(0x10, 0, 6), 2368c0984e5SSebastian Reichel /* REG11 */ 2378c0984e5SSebastian Reichel [F_VBUS_GD] = REG_FIELD(0x11, 7, 7), 2388c0984e5SSebastian Reichel [F_VBUSV] = REG_FIELD(0x11, 0, 6), 2398c0984e5SSebastian Reichel /* REG12 */ 2408c0984e5SSebastian Reichel [F_ICHGR] = REG_FIELD(0x12, 0, 6), 2418c0984e5SSebastian Reichel /* REG13 */ 2428c0984e5SSebastian Reichel [F_VDPM_STAT] = REG_FIELD(0x13, 7, 7), 2438c0984e5SSebastian Reichel [F_IDPM_STAT] = REG_FIELD(0x13, 6, 6), 2448c0984e5SSebastian Reichel [F_IDPM_LIM] = REG_FIELD(0x13, 0, 5), 2458c0984e5SSebastian Reichel /* REG14 */ 2468c0984e5SSebastian Reichel [F_REG_RST] = REG_FIELD(0x14, 7, 7), 2478c0984e5SSebastian Reichel [F_ICO_OPTIMIZED] = REG_FIELD(0x14, 6, 6), 2488c0984e5SSebastian Reichel [F_PN] = REG_FIELD(0x14, 3, 5), 2498c0984e5SSebastian Reichel [F_TS_PROFILE] = REG_FIELD(0x14, 2, 2), 2508c0984e5SSebastian Reichel [F_DEV_REV] = REG_FIELD(0x14, 0, 1) 2518c0984e5SSebastian Reichel }; 2528c0984e5SSebastian Reichel 2538c0984e5SSebastian Reichel /* 2548c0984e5SSebastian Reichel * Most of the val -> idx conversions can be computed, given the minimum, 2558c0984e5SSebastian Reichel * maximum and the step between values. For the rest of conversions, we use 2568c0984e5SSebastian Reichel * lookup tables. 2578c0984e5SSebastian Reichel */ 2588c0984e5SSebastian Reichel enum bq25890_table_ids { 2598c0984e5SSebastian Reichel /* range tables */ 2608c0984e5SSebastian Reichel TBL_ICHG, 2618c0984e5SSebastian Reichel TBL_ITERM, 262766873c1SYauhen Kharuzhy TBL_IINLIM, 2638c0984e5SSebastian Reichel TBL_VREG, 2648c0984e5SSebastian Reichel TBL_BOOSTV, 2658c0984e5SSebastian Reichel TBL_SYSVMIN, 26672408329SMichał Mirosław TBL_VBATCOMP, 26772408329SMichał Mirosław TBL_RBATCOMP, 2688c0984e5SSebastian Reichel 2698c0984e5SSebastian Reichel /* lookup tables */ 2708c0984e5SSebastian Reichel TBL_TREG, 2718c0984e5SSebastian Reichel TBL_BOOSTI, 2729652c024SAngus Ainslie TBL_TSPCT, 2738c0984e5SSebastian Reichel }; 2748c0984e5SSebastian Reichel 2758c0984e5SSebastian Reichel /* Thermal Regulation Threshold lookup table, in degrees Celsius */ 2768c0984e5SSebastian Reichel static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 }; 2778c0984e5SSebastian Reichel 2788c0984e5SSebastian Reichel #define BQ25890_TREG_TBL_SIZE ARRAY_SIZE(bq25890_treg_tbl) 2798c0984e5SSebastian Reichel 2808c0984e5SSebastian Reichel /* Boost mode current limit lookup table, in uA */ 2818c0984e5SSebastian Reichel static const u32 bq25890_boosti_tbl[] = { 2828c0984e5SSebastian Reichel 500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000 2838c0984e5SSebastian Reichel }; 2848c0984e5SSebastian Reichel 2858c0984e5SSebastian Reichel #define BQ25890_BOOSTI_TBL_SIZE ARRAY_SIZE(bq25890_boosti_tbl) 2868c0984e5SSebastian Reichel 2879652c024SAngus Ainslie /* NTC 10K temperature lookup table in tenths of a degree */ 2889652c024SAngus Ainslie static const u32 bq25890_tspct_tbl[] = { 2899652c024SAngus Ainslie 850, 840, 830, 820, 810, 800, 790, 780, 2909652c024SAngus Ainslie 770, 760, 750, 740, 730, 720, 710, 700, 2919652c024SAngus Ainslie 690, 685, 680, 675, 670, 660, 650, 645, 2929652c024SAngus Ainslie 640, 630, 620, 615, 610, 600, 590, 585, 2939652c024SAngus Ainslie 580, 570, 565, 560, 550, 540, 535, 530, 2949652c024SAngus Ainslie 520, 515, 510, 500, 495, 490, 480, 475, 2959652c024SAngus Ainslie 470, 460, 455, 450, 440, 435, 430, 425, 2969652c024SAngus Ainslie 420, 410, 405, 400, 390, 385, 380, 370, 2979652c024SAngus Ainslie 365, 360, 355, 350, 340, 335, 330, 320, 2989652c024SAngus Ainslie 310, 305, 300, 290, 285, 280, 275, 270, 2999652c024SAngus Ainslie 260, 250, 245, 240, 230, 225, 220, 210, 3009652c024SAngus Ainslie 205, 200, 190, 180, 175, 170, 160, 150, 3019652c024SAngus Ainslie 145, 140, 130, 120, 115, 110, 100, 90, 3029652c024SAngus Ainslie 80, 70, 60, 50, 40, 30, 20, 10, 3039652c024SAngus Ainslie 0, -10, -20, -30, -40, -60, -70, -80, 3049652c024SAngus Ainslie -90, -10, -120, -140, -150, -170, -190, -210, 3059652c024SAngus Ainslie }; 3069652c024SAngus Ainslie 3079652c024SAngus Ainslie #define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl) 3089652c024SAngus Ainslie 3098c0984e5SSebastian Reichel struct bq25890_range { 3108c0984e5SSebastian Reichel u32 min; 3118c0984e5SSebastian Reichel u32 max; 3128c0984e5SSebastian Reichel u32 step; 3138c0984e5SSebastian Reichel }; 3148c0984e5SSebastian Reichel 3158c0984e5SSebastian Reichel struct bq25890_lookup { 3168c0984e5SSebastian Reichel const u32 *tbl; 3178c0984e5SSebastian Reichel u32 size; 3188c0984e5SSebastian Reichel }; 3198c0984e5SSebastian Reichel 3208c0984e5SSebastian Reichel static const union { 3218c0984e5SSebastian Reichel struct bq25890_range rt; 3228c0984e5SSebastian Reichel struct bq25890_lookup lt; 3238c0984e5SSebastian Reichel } bq25890_tables[] = { 3248c0984e5SSebastian Reichel /* range tables */ 325d20267c9SYauhen Kharuzhy /* TODO: BQ25896 has max ICHG 3008 mA */ 3268c0984e5SSebastian Reichel [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ 3278c0984e5SSebastian Reichel [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ 328766873c1SYauhen Kharuzhy [TBL_IINLIM] = { .rt = {100000, 3250000, 50000} }, /* uA */ 3298c0984e5SSebastian Reichel [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ 3308c0984e5SSebastian Reichel [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ 3318c0984e5SSebastian Reichel [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ 33272408329SMichał Mirosław [TBL_VBATCOMP] ={ .rt = {0, 224000, 32000} }, /* uV */ 33372408329SMichał Mirosław [TBL_RBATCOMP] ={ .rt = {0, 140000, 20000} }, /* uOhm */ 3348c0984e5SSebastian Reichel 3358c0984e5SSebastian Reichel /* lookup tables */ 3368c0984e5SSebastian Reichel [TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} }, 3379652c024SAngus Ainslie [TBL_BOOSTI] = { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }, 3389652c024SAngus Ainslie [TBL_TSPCT] = { .lt = {bq25890_tspct_tbl, BQ25890_TSPCT_TBL_SIZE} } 3398c0984e5SSebastian Reichel }; 3408c0984e5SSebastian Reichel 3418c0984e5SSebastian Reichel static int bq25890_field_read(struct bq25890_device *bq, 3428c0984e5SSebastian Reichel enum bq25890_fields field_id) 3438c0984e5SSebastian Reichel { 3448c0984e5SSebastian Reichel int ret; 3458c0984e5SSebastian Reichel int val; 3468c0984e5SSebastian Reichel 3478c0984e5SSebastian Reichel ret = regmap_field_read(bq->rmap_fields[field_id], &val); 3488c0984e5SSebastian Reichel if (ret < 0) 3498c0984e5SSebastian Reichel return ret; 3508c0984e5SSebastian Reichel 3518c0984e5SSebastian Reichel return val; 3528c0984e5SSebastian Reichel } 3538c0984e5SSebastian Reichel 3548c0984e5SSebastian Reichel static int bq25890_field_write(struct bq25890_device *bq, 3558c0984e5SSebastian Reichel enum bq25890_fields field_id, u8 val) 3568c0984e5SSebastian Reichel { 3578c0984e5SSebastian Reichel return regmap_field_write(bq->rmap_fields[field_id], val); 3588c0984e5SSebastian Reichel } 3598c0984e5SSebastian Reichel 3608c0984e5SSebastian Reichel static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id) 3618c0984e5SSebastian Reichel { 3628c0984e5SSebastian Reichel u8 idx; 3638c0984e5SSebastian Reichel 3648c0984e5SSebastian Reichel if (id >= TBL_TREG) { 3658c0984e5SSebastian Reichel const u32 *tbl = bq25890_tables[id].lt.tbl; 3668c0984e5SSebastian Reichel u32 tbl_size = bq25890_tables[id].lt.size; 3678c0984e5SSebastian Reichel 3688c0984e5SSebastian Reichel for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++) 3698c0984e5SSebastian Reichel ; 3708c0984e5SSebastian Reichel } else { 3718c0984e5SSebastian Reichel const struct bq25890_range *rtbl = &bq25890_tables[id].rt; 3728c0984e5SSebastian Reichel u8 rtbl_size; 3738c0984e5SSebastian Reichel 3748c0984e5SSebastian Reichel rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1; 3758c0984e5SSebastian Reichel 3768c0984e5SSebastian Reichel for (idx = 1; 3778c0984e5SSebastian Reichel idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value); 3788c0984e5SSebastian Reichel idx++) 3798c0984e5SSebastian Reichel ; 3808c0984e5SSebastian Reichel } 3818c0984e5SSebastian Reichel 3828c0984e5SSebastian Reichel return idx - 1; 3838c0984e5SSebastian Reichel } 3848c0984e5SSebastian Reichel 3858c0984e5SSebastian Reichel static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id) 3868c0984e5SSebastian Reichel { 3878c0984e5SSebastian Reichel const struct bq25890_range *rtbl; 3888c0984e5SSebastian Reichel 3898c0984e5SSebastian Reichel /* lookup table? */ 3908c0984e5SSebastian Reichel if (id >= TBL_TREG) 3918c0984e5SSebastian Reichel return bq25890_tables[id].lt.tbl[idx]; 3928c0984e5SSebastian Reichel 3938c0984e5SSebastian Reichel /* range table */ 3948c0984e5SSebastian Reichel rtbl = &bq25890_tables[id].rt; 3958c0984e5SSebastian Reichel 3968c0984e5SSebastian Reichel return (rtbl->min + idx * rtbl->step); 3978c0984e5SSebastian Reichel } 3988c0984e5SSebastian Reichel 3998c0984e5SSebastian Reichel enum bq25890_status { 4008c0984e5SSebastian Reichel STATUS_NOT_CHARGING, 4018c0984e5SSebastian Reichel STATUS_PRE_CHARGING, 4028c0984e5SSebastian Reichel STATUS_FAST_CHARGING, 4038c0984e5SSebastian Reichel STATUS_TERMINATION_DONE, 4048c0984e5SSebastian Reichel }; 4058c0984e5SSebastian Reichel 4068c0984e5SSebastian Reichel enum bq25890_chrg_fault { 4078c0984e5SSebastian Reichel CHRG_FAULT_NORMAL, 4088c0984e5SSebastian Reichel CHRG_FAULT_INPUT, 4098c0984e5SSebastian Reichel CHRG_FAULT_THERMAL_SHUTDOWN, 4108c0984e5SSebastian Reichel CHRG_FAULT_TIMER_EXPIRED, 4118c0984e5SSebastian Reichel }; 4128c0984e5SSebastian Reichel 413c562a43aSYauhen Kharuzhy enum bq25890_ntc_fault { 414c562a43aSYauhen Kharuzhy NTC_FAULT_NORMAL = 0, 415c562a43aSYauhen Kharuzhy NTC_FAULT_WARM = 2, 416c562a43aSYauhen Kharuzhy NTC_FAULT_COOL = 3, 417c562a43aSYauhen Kharuzhy NTC_FAULT_COLD = 5, 418c562a43aSYauhen Kharuzhy NTC_FAULT_HOT = 6, 419c562a43aSYauhen Kharuzhy }; 420c562a43aSYauhen Kharuzhy 42121d90edaSMichał Mirosław static bool bq25890_is_adc_property(enum power_supply_property psp) 42221d90edaSMichał Mirosław { 42321d90edaSMichał Mirosław switch (psp) { 42421d90edaSMichał Mirosław case POWER_SUPPLY_PROP_VOLTAGE_NOW: 42521d90edaSMichał Mirosław case POWER_SUPPLY_PROP_CURRENT_NOW: 4269652c024SAngus Ainslie case POWER_SUPPLY_PROP_TEMP: 42721d90edaSMichał Mirosław return true; 42821d90edaSMichał Mirosław 42921d90edaSMichał Mirosław default: 43021d90edaSMichał Mirosław return false; 43121d90edaSMichał Mirosław } 43221d90edaSMichał Mirosław } 43321d90edaSMichał Mirosław 4343b4df57bSMichał Mirosław static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq); 4353b4df57bSMichał Mirosław 4368c0984e5SSebastian Reichel static int bq25890_power_supply_get_property(struct power_supply *psy, 4378c0984e5SSebastian Reichel enum power_supply_property psp, 4388c0984e5SSebastian Reichel union power_supply_propval *val) 4398c0984e5SSebastian Reichel { 4408c0984e5SSebastian Reichel struct bq25890_device *bq = power_supply_get_drvdata(psy); 4418c0984e5SSebastian Reichel struct bq25890_state state; 44221d90edaSMichał Mirosław bool do_adc_conv; 44321d90edaSMichał Mirosław int ret; 4448c0984e5SSebastian Reichel 4458c0984e5SSebastian Reichel mutex_lock(&bq->lock); 4463b4df57bSMichał Mirosław /* update state in case we lost an interrupt */ 4473b4df57bSMichał Mirosław __bq25890_handle_irq(bq); 4488c0984e5SSebastian Reichel state = bq->state; 44921d90edaSMichał Mirosław do_adc_conv = !state.online && bq25890_is_adc_property(psp); 45021d90edaSMichał Mirosław if (do_adc_conv) 45121d90edaSMichał Mirosław bq25890_field_write(bq, F_CONV_START, 1); 4528c0984e5SSebastian Reichel mutex_unlock(&bq->lock); 4538c0984e5SSebastian Reichel 45421d90edaSMichał Mirosław if (do_adc_conv) 45521d90edaSMichał Mirosław regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START], 45621d90edaSMichał Mirosław ret, !ret, 25000, 1000000); 45721d90edaSMichał Mirosław 4588c0984e5SSebastian Reichel switch (psp) { 4598c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 4608c0984e5SSebastian Reichel if (!state.online) 4618c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 4628c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_NOT_CHARGING) 4638c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 4648c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_PRE_CHARGING || 4658c0984e5SSebastian Reichel state.chrg_status == STATUS_FAST_CHARGING) 4668c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_CHARGING; 4678c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_TERMINATION_DONE) 4688c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_FULL; 4698c0984e5SSebastian Reichel else 4708c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 4718c0984e5SSebastian Reichel 4728c0984e5SSebastian Reichel break; 4738c0984e5SSebastian Reichel 474b302a0aeSMichał Mirosław case POWER_SUPPLY_PROP_CHARGE_TYPE: 475b302a0aeSMichał Mirosław if (!state.online || state.chrg_status == STATUS_NOT_CHARGING || 476b302a0aeSMichał Mirosław state.chrg_status == STATUS_TERMINATION_DONE) 477b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 478b302a0aeSMichał Mirosław else if (state.chrg_status == STATUS_PRE_CHARGING) 479b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; 480b302a0aeSMichał Mirosław else if (state.chrg_status == STATUS_FAST_CHARGING) 481b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 482b302a0aeSMichał Mirosław else /* unreachable */ 483b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 484b302a0aeSMichał Mirosław break; 485b302a0aeSMichał Mirosław 4868c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_MANUFACTURER: 4878c0984e5SSebastian Reichel val->strval = BQ25890_MANUFACTURER; 4888c0984e5SSebastian Reichel break; 4898c0984e5SSebastian Reichel 4902e1a2ddeSAngus Ainslie (Purism) case POWER_SUPPLY_PROP_MODEL_NAME: 4915956fca7SMichał Mirosław val->strval = bq25890_chip_name[bq->chip_version]; 4922e1a2ddeSAngus Ainslie (Purism) break; 4932e1a2ddeSAngus Ainslie (Purism) 4948c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_ONLINE: 4958c0984e5SSebastian Reichel val->intval = state.online; 4968c0984e5SSebastian Reichel break; 4978c0984e5SSebastian Reichel 4988c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH: 4998c0984e5SSebastian Reichel if (!state.chrg_fault && !state.bat_fault && !state.boost_fault) 5008c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_GOOD; 5018c0984e5SSebastian Reichel else if (state.bat_fault) 5028c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 5038c0984e5SSebastian Reichel else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED) 5048c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; 5058c0984e5SSebastian Reichel else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN) 5068c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 5078c0984e5SSebastian Reichel else 5088c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 5098c0984e5SSebastian Reichel break; 5108c0984e5SSebastian Reichel 5118c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 512f83a6eceSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG); 513c562a43aSYauhen Kharuzhy 514c562a43aSYauhen Kharuzhy /* When temperature is too low, charge current is decreased */ 515c562a43aSYauhen Kharuzhy if (bq->state.ntc_fault == NTC_FAULT_COOL) { 516c562a43aSYauhen Kharuzhy ret = bq25890_field_read(bq, F_JEITA_ISET); 517c562a43aSYauhen Kharuzhy if (ret < 0) 518c562a43aSYauhen Kharuzhy return ret; 519c562a43aSYauhen Kharuzhy 520c562a43aSYauhen Kharuzhy if (ret) 521c562a43aSYauhen Kharuzhy val->intval /= 5; 522c562a43aSYauhen Kharuzhy else 523c562a43aSYauhen Kharuzhy val->intval /= 2; 524c562a43aSYauhen Kharuzhy } 5258c0984e5SSebastian Reichel break; 5268c0984e5SSebastian Reichel 5278c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 5288c0984e5SSebastian Reichel if (!state.online) { 5298c0984e5SSebastian Reichel val->intval = 0; 5308c0984e5SSebastian Reichel break; 5318c0984e5SSebastian Reichel } 5328c0984e5SSebastian Reichel 5338c0984e5SSebastian Reichel ret = bq25890_field_read(bq, F_BATV); /* read measured value */ 5348c0984e5SSebastian Reichel if (ret < 0) 5358c0984e5SSebastian Reichel return ret; 5368c0984e5SSebastian Reichel 5378c0984e5SSebastian Reichel /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */ 5388c0984e5SSebastian Reichel val->intval = 2304000 + ret * 20000; 5398c0984e5SSebastian Reichel break; 5408c0984e5SSebastian Reichel 5418c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 542f83a6eceSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG); 5438c0984e5SSebastian Reichel break; 5448c0984e5SSebastian Reichel 545c942656dSMichał Mirosław case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: 546c942656dSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.iprechg, TBL_ITERM); 547c942656dSMichał Mirosław break; 548c942656dSMichał Mirosław 5498c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 5508c0984e5SSebastian Reichel val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM); 5518c0984e5SSebastian Reichel break; 5528c0984e5SSebastian Reichel 553478efc79SMichał Mirosław case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 554766873c1SYauhen Kharuzhy ret = bq25890_field_read(bq, F_IINLIM); 555478efc79SMichał Mirosław if (ret < 0) 556478efc79SMichał Mirosław return ret; 557478efc79SMichał Mirosław 558766873c1SYauhen Kharuzhy val->intval = bq25890_find_val(ret, TBL_IINLIM); 559478efc79SMichał Mirosław break; 560478efc79SMichał Mirosław 561ae6fe7a3SAngus Ainslie (Purism) case POWER_SUPPLY_PROP_VOLTAGE_NOW: 562ae6fe7a3SAngus Ainslie (Purism) ret = bq25890_field_read(bq, F_SYSV); /* read measured value */ 563ae6fe7a3SAngus Ainslie (Purism) if (ret < 0) 564ae6fe7a3SAngus Ainslie (Purism) return ret; 565ae6fe7a3SAngus Ainslie (Purism) 566ae6fe7a3SAngus Ainslie (Purism) /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */ 567ae6fe7a3SAngus Ainslie (Purism) val->intval = 2304000 + ret * 20000; 568ae6fe7a3SAngus Ainslie (Purism) break; 569ae6fe7a3SAngus Ainslie (Purism) 5701e4724d0SMichał Mirosław case POWER_SUPPLY_PROP_CURRENT_NOW: 5711e4724d0SMichał Mirosław ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */ 5721e4724d0SMichał Mirosław if (ret < 0) 5731e4724d0SMichał Mirosław return ret; 5741e4724d0SMichał Mirosław 5751e4724d0SMichał Mirosław /* converted_val = ADC_val * 50mA (table 10.3.19) */ 5761e4724d0SMichał Mirosław val->intval = ret * -50000; 5771e4724d0SMichał Mirosław break; 5781e4724d0SMichał Mirosław 5799652c024SAngus Ainslie case POWER_SUPPLY_PROP_TEMP: 5809652c024SAngus Ainslie ret = bq25890_field_read(bq, F_TSPCT); 5819652c024SAngus Ainslie if (ret < 0) 5829652c024SAngus Ainslie return ret; 5839652c024SAngus Ainslie 5849652c024SAngus Ainslie /* convert TS percentage into rough temperature */ 5859652c024SAngus Ainslie val->intval = bq25890_find_val(ret, TBL_TSPCT); 5869652c024SAngus Ainslie break; 5879652c024SAngus Ainslie 5888c0984e5SSebastian Reichel default: 5898c0984e5SSebastian Reichel return -EINVAL; 5908c0984e5SSebastian Reichel } 5918c0984e5SSebastian Reichel 5928c0984e5SSebastian Reichel return 0; 5938c0984e5SSebastian Reichel } 5948c0984e5SSebastian Reichel 5958c0984e5SSebastian Reichel static int bq25890_get_chip_state(struct bq25890_device *bq, 5968c0984e5SSebastian Reichel struct bq25890_state *state) 5978c0984e5SSebastian Reichel { 5988c0984e5SSebastian Reichel int i, ret; 5998c0984e5SSebastian Reichel 6008c0984e5SSebastian Reichel struct { 6018c0984e5SSebastian Reichel enum bq25890_fields id; 6028c0984e5SSebastian Reichel u8 *data; 6038c0984e5SSebastian Reichel } state_fields[] = { 6048c0984e5SSebastian Reichel {F_CHG_STAT, &state->chrg_status}, 6058c0984e5SSebastian Reichel {F_PG_STAT, &state->online}, 6068c0984e5SSebastian Reichel {F_VSYS_STAT, &state->vsys_status}, 6078c0984e5SSebastian Reichel {F_BOOST_FAULT, &state->boost_fault}, 6088c0984e5SSebastian Reichel {F_BAT_FAULT, &state->bat_fault}, 609c562a43aSYauhen Kharuzhy {F_CHG_FAULT, &state->chrg_fault}, 610c562a43aSYauhen Kharuzhy {F_NTC_FAULT, &state->ntc_fault} 6118c0984e5SSebastian Reichel }; 6128c0984e5SSebastian Reichel 6138c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(state_fields); i++) { 6148c0984e5SSebastian Reichel ret = bq25890_field_read(bq, state_fields[i].id); 6158c0984e5SSebastian Reichel if (ret < 0) 6168c0984e5SSebastian Reichel return ret; 6178c0984e5SSebastian Reichel 6188c0984e5SSebastian Reichel *state_fields[i].data = ret; 6198c0984e5SSebastian Reichel } 6208c0984e5SSebastian Reichel 621c562a43aSYauhen Kharuzhy dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n", 6228c0984e5SSebastian Reichel state->chrg_status, state->online, state->vsys_status, 623c562a43aSYauhen Kharuzhy state->chrg_fault, state->boost_fault, state->bat_fault, 624c562a43aSYauhen Kharuzhy state->ntc_fault); 6258c0984e5SSebastian Reichel 6268c0984e5SSebastian Reichel return 0; 6278c0984e5SSebastian Reichel } 6288c0984e5SSebastian Reichel 62972d9cd9cSMichał Mirosław static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq) 6308c0984e5SSebastian Reichel { 63172d9cd9cSMichał Mirosław struct bq25890_state new_state; 6328c0984e5SSebastian Reichel int ret; 6338c0984e5SSebastian Reichel 63472d9cd9cSMichał Mirosław ret = bq25890_get_chip_state(bq, &new_state); 63572d9cd9cSMichał Mirosław if (ret < 0) 63672d9cd9cSMichał Mirosław return IRQ_NONE; 6378c0984e5SSebastian Reichel 63872d9cd9cSMichał Mirosław if (!memcmp(&bq->state, &new_state, sizeof(new_state))) 63972d9cd9cSMichał Mirosław return IRQ_NONE; 64072d9cd9cSMichał Mirosław 64172d9cd9cSMichał Mirosław if (!new_state.online && bq->state.online) { /* power removed */ 6428c0984e5SSebastian Reichel /* disable ADC */ 64380211be1SYauhen Kharuzhy ret = bq25890_field_write(bq, F_CONV_RATE, 0); 6448c0984e5SSebastian Reichel if (ret < 0) 6458c0984e5SSebastian Reichel goto error; 64672d9cd9cSMichał Mirosław } else if (new_state.online && !bq->state.online) { /* power inserted */ 6478c0984e5SSebastian Reichel /* enable ADC, to have control of charge current/voltage */ 64880211be1SYauhen Kharuzhy ret = bq25890_field_write(bq, F_CONV_RATE, 1); 6498c0984e5SSebastian Reichel if (ret < 0) 6508c0984e5SSebastian Reichel goto error; 6518c0984e5SSebastian Reichel } 6528c0984e5SSebastian Reichel 65372d9cd9cSMichał Mirosław bq->state = new_state; 65472d9cd9cSMichał Mirosław power_supply_changed(bq->charger); 6558c0984e5SSebastian Reichel 65672d9cd9cSMichał Mirosław return IRQ_HANDLED; 6578c0984e5SSebastian Reichel error: 65872d9cd9cSMichał Mirosław dev_err(bq->dev, "Error communicating with the chip: %pe\n", 65972d9cd9cSMichał Mirosław ERR_PTR(ret)); 66072d9cd9cSMichał Mirosław return IRQ_HANDLED; 6618c0984e5SSebastian Reichel } 6628c0984e5SSebastian Reichel 6638c0984e5SSebastian Reichel static irqreturn_t bq25890_irq_handler_thread(int irq, void *private) 6648c0984e5SSebastian Reichel { 6658c0984e5SSebastian Reichel struct bq25890_device *bq = private; 66672d9cd9cSMichał Mirosław irqreturn_t ret; 6678c0984e5SSebastian Reichel 6688c0984e5SSebastian Reichel mutex_lock(&bq->lock); 66972d9cd9cSMichał Mirosław ret = __bq25890_handle_irq(bq); 6708c0984e5SSebastian Reichel mutex_unlock(&bq->lock); 6718c0984e5SSebastian Reichel 67272d9cd9cSMichał Mirosław return ret; 6738c0984e5SSebastian Reichel } 6748c0984e5SSebastian Reichel 6758c0984e5SSebastian Reichel static int bq25890_chip_reset(struct bq25890_device *bq) 6768c0984e5SSebastian Reichel { 6778c0984e5SSebastian Reichel int ret; 6788c0984e5SSebastian Reichel int rst_check_counter = 10; 6798c0984e5SSebastian Reichel 6808c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_REG_RST, 1); 6818c0984e5SSebastian Reichel if (ret < 0) 6828c0984e5SSebastian Reichel return ret; 6838c0984e5SSebastian Reichel 6848c0984e5SSebastian Reichel do { 6858c0984e5SSebastian Reichel ret = bq25890_field_read(bq, F_REG_RST); 6868c0984e5SSebastian Reichel if (ret < 0) 6878c0984e5SSebastian Reichel return ret; 6888c0984e5SSebastian Reichel 6898c0984e5SSebastian Reichel usleep_range(5, 10); 6908c0984e5SSebastian Reichel } while (ret == 1 && --rst_check_counter); 6918c0984e5SSebastian Reichel 6928c0984e5SSebastian Reichel if (!rst_check_counter) 6938c0984e5SSebastian Reichel return -ETIMEDOUT; 6948c0984e5SSebastian Reichel 6958c0984e5SSebastian Reichel return 0; 6968c0984e5SSebastian Reichel } 6978c0984e5SSebastian Reichel 6987b22a974SHans de Goede static int bq25890_rw_init_data(struct bq25890_device *bq) 6998c0984e5SSebastian Reichel { 70040428bd4SHans de Goede bool write = !bq->read_back_init_data; 7018c0984e5SSebastian Reichel int ret; 7028c0984e5SSebastian Reichel int i; 7038c0984e5SSebastian Reichel 7048c0984e5SSebastian Reichel const struct { 7058c0984e5SSebastian Reichel enum bq25890_fields id; 7067b22a974SHans de Goede u8 *value; 7078c0984e5SSebastian Reichel } init_data[] = { 7087b22a974SHans de Goede {F_ICHG, &bq->init_data.ichg}, 7097b22a974SHans de Goede {F_VREG, &bq->init_data.vreg}, 7107b22a974SHans de Goede {F_ITERM, &bq->init_data.iterm}, 7117b22a974SHans de Goede {F_IPRECHG, &bq->init_data.iprechg}, 7127b22a974SHans de Goede {F_SYSVMIN, &bq->init_data.sysvmin}, 7137b22a974SHans de Goede {F_BOOSTV, &bq->init_data.boostv}, 7147b22a974SHans de Goede {F_BOOSTI, &bq->init_data.boosti}, 7157b22a974SHans de Goede {F_BOOSTF, &bq->init_data.boostf}, 7167b22a974SHans de Goede {F_EN_ILIM, &bq->init_data.ilim_en}, 7177b22a974SHans de Goede {F_TREG, &bq->init_data.treg}, 7187b22a974SHans de Goede {F_BATCMP, &bq->init_data.rbatcomp}, 7197b22a974SHans de Goede {F_VCLAMP, &bq->init_data.vclamp}, 7208c0984e5SSebastian Reichel }; 7218c0984e5SSebastian Reichel 7227b22a974SHans de Goede for (i = 0; i < ARRAY_SIZE(init_data); i++) { 7237b22a974SHans de Goede if (write) { 7247b22a974SHans de Goede ret = bq25890_field_write(bq, init_data[i].id, 7257b22a974SHans de Goede *init_data[i].value); 7267b22a974SHans de Goede } else { 7277b22a974SHans de Goede ret = bq25890_field_read(bq, init_data[i].id); 7287b22a974SHans de Goede if (ret >= 0) 7297b22a974SHans de Goede *init_data[i].value = ret; 7307b22a974SHans de Goede } 7317b22a974SHans de Goede if (ret < 0) { 7327b22a974SHans de Goede dev_dbg(bq->dev, "Accessing init data failed %d\n", ret); 7337b22a974SHans de Goede return ret; 7347b22a974SHans de Goede } 7357b22a974SHans de Goede } 7367b22a974SHans de Goede 7377b22a974SHans de Goede return 0; 7387b22a974SHans de Goede } 7397b22a974SHans de Goede 7407b22a974SHans de Goede static int bq25890_hw_init(struct bq25890_device *bq) 7417b22a974SHans de Goede { 7427b22a974SHans de Goede int ret; 7437b22a974SHans de Goede 7447e3b8e35SHans de Goede if (!bq->skip_reset) { 7458c0984e5SSebastian Reichel ret = bq25890_chip_reset(bq); 7469d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7479d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Reset failed %d\n", ret); 7488c0984e5SSebastian Reichel return ret; 749ad1570d9Skbuild test robot } 750*06c75095SHans de Goede } else { 751*06c75095SHans de Goede /* 752*06c75095SHans de Goede * Ensure charging is enabled, on some boards where the fw 753*06c75095SHans de Goede * takes care of initalizition F_CHG_CFG is set to 0 before 754*06c75095SHans de Goede * handing control over to the OS. 755*06c75095SHans de Goede */ 756*06c75095SHans de Goede ret = bq25890_field_write(bq, F_CHG_CFG, 1); 757*06c75095SHans de Goede if (ret < 0) { 758*06c75095SHans de Goede dev_dbg(bq->dev, "Enabling charging failed %d\n", ret); 759*06c75095SHans de Goede return ret; 760*06c75095SHans de Goede } 7617e3b8e35SHans de Goede } 7628c0984e5SSebastian Reichel 7638c0984e5SSebastian Reichel /* disable watchdog */ 7648c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_WD, 0); 7659d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7669d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Disabling watchdog failed %d\n", ret); 7678c0984e5SSebastian Reichel return ret; 768ad1570d9Skbuild test robot } 7698c0984e5SSebastian Reichel 7708c0984e5SSebastian Reichel /* initialize currents/voltages and other parameters */ 7717b22a974SHans de Goede ret = bq25890_rw_init_data(bq); 7727b22a974SHans de Goede if (ret) 7738c0984e5SSebastian Reichel return ret; 7748c0984e5SSebastian Reichel 77522ad4f99SHans de Goede ret = bq25890_get_chip_state(bq, &bq->state); 77622ad4f99SHans de Goede if (ret < 0) { 77722ad4f99SHans de Goede dev_dbg(bq->dev, "Get state failed %d\n", ret); 77822ad4f99SHans de Goede return ret; 77922ad4f99SHans de Goede } 78022ad4f99SHans de Goede 78121d90edaSMichał Mirosław /* Configure ADC for continuous conversions when charging */ 78221d90edaSMichał Mirosław ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online); 7839d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7849d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Config ADC failed %d\n", ret); 7858c0984e5SSebastian Reichel return ret; 786ad1570d9Skbuild test robot } 7878c0984e5SSebastian Reichel 7888c0984e5SSebastian Reichel return 0; 7898c0984e5SSebastian Reichel } 7908c0984e5SSebastian Reichel 791a6a48facSMichał Mirosław static const enum power_supply_property bq25890_power_supply_props[] = { 7928c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 7932e1a2ddeSAngus Ainslie (Purism) POWER_SUPPLY_PROP_MODEL_NAME, 7948c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 795b302a0aeSMichał Mirosław POWER_SUPPLY_PROP_CHARGE_TYPE, 7968c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 7978c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 7988c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 7998c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 8008c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 801c942656dSMichał Mirosław POWER_SUPPLY_PROP_PRECHARGE_CURRENT, 8028c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, 803478efc79SMichał Mirosław POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 804ae6fe7a3SAngus Ainslie (Purism) POWER_SUPPLY_PROP_VOLTAGE_NOW, 8051e4724d0SMichał Mirosław POWER_SUPPLY_PROP_CURRENT_NOW, 8069652c024SAngus Ainslie POWER_SUPPLY_PROP_TEMP, 8078c0984e5SSebastian Reichel }; 8088c0984e5SSebastian Reichel 8098c0984e5SSebastian Reichel static char *bq25890_charger_supplied_to[] = { 8108c0984e5SSebastian Reichel "main-battery", 8118c0984e5SSebastian Reichel }; 8128c0984e5SSebastian Reichel 8138c0984e5SSebastian Reichel static const struct power_supply_desc bq25890_power_supply_desc = { 8148c0984e5SSebastian Reichel .name = "bq25890-charger", 8158c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_USB, 8168c0984e5SSebastian Reichel .properties = bq25890_power_supply_props, 8178c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(bq25890_power_supply_props), 8188c0984e5SSebastian Reichel .get_property = bq25890_power_supply_get_property, 8198c0984e5SSebastian Reichel }; 8208c0984e5SSebastian Reichel 8218c0984e5SSebastian Reichel static int bq25890_power_supply_init(struct bq25890_device *bq) 8228c0984e5SSebastian Reichel { 8238c0984e5SSebastian Reichel struct power_supply_config psy_cfg = { .drv_data = bq, }; 8248c0984e5SSebastian Reichel 8258c0984e5SSebastian Reichel psy_cfg.supplied_to = bq25890_charger_supplied_to; 8268c0984e5SSebastian Reichel psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to); 8278c0984e5SSebastian Reichel 828d01363daSHans de Goede bq->charger = devm_power_supply_register(bq->dev, 829d01363daSHans de Goede &bq25890_power_supply_desc, 8308c0984e5SSebastian Reichel &psy_cfg); 8318c0984e5SSebastian Reichel 8328c0984e5SSebastian Reichel return PTR_ERR_OR_ZERO(bq->charger); 8338c0984e5SSebastian Reichel } 8348c0984e5SSebastian Reichel 8358c0984e5SSebastian Reichel static void bq25890_usb_work(struct work_struct *data) 8368c0984e5SSebastian Reichel { 8378c0984e5SSebastian Reichel int ret; 8388c0984e5SSebastian Reichel struct bq25890_device *bq = 8398c0984e5SSebastian Reichel container_of(data, struct bq25890_device, usb_work); 8408c0984e5SSebastian Reichel 8418c0984e5SSebastian Reichel switch (bq->usb_event) { 8428c0984e5SSebastian Reichel case USB_EVENT_ID: 8438c0984e5SSebastian Reichel /* Enable boost mode */ 8448c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_OTG_CFG, 1); 8458c0984e5SSebastian Reichel if (ret < 0) 8468c0984e5SSebastian Reichel goto error; 8478c0984e5SSebastian Reichel break; 8488c0984e5SSebastian Reichel 8498c0984e5SSebastian Reichel case USB_EVENT_NONE: 8508c0984e5SSebastian Reichel /* Disable boost mode */ 8518c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_OTG_CFG, 0); 8528c0984e5SSebastian Reichel if (ret < 0) 8538c0984e5SSebastian Reichel goto error; 8548c0984e5SSebastian Reichel 8558c0984e5SSebastian Reichel power_supply_changed(bq->charger); 8568c0984e5SSebastian Reichel break; 8578c0984e5SSebastian Reichel } 8588c0984e5SSebastian Reichel 8598c0984e5SSebastian Reichel return; 8608c0984e5SSebastian Reichel 8618c0984e5SSebastian Reichel error: 8628c0984e5SSebastian Reichel dev_err(bq->dev, "Error switching to boost/charger mode.\n"); 8638c0984e5SSebastian Reichel } 8648c0984e5SSebastian Reichel 8658c0984e5SSebastian Reichel static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, 8668c0984e5SSebastian Reichel void *priv) 8678c0984e5SSebastian Reichel { 8688c0984e5SSebastian Reichel struct bq25890_device *bq = 8698c0984e5SSebastian Reichel container_of(nb, struct bq25890_device, usb_nb); 8708c0984e5SSebastian Reichel 8718c0984e5SSebastian Reichel bq->usb_event = val; 8728c0984e5SSebastian Reichel queue_work(system_power_efficient_wq, &bq->usb_work); 8738c0984e5SSebastian Reichel 8748c0984e5SSebastian Reichel return NOTIFY_OK; 8758c0984e5SSebastian Reichel } 8768c0984e5SSebastian Reichel 877d20267c9SYauhen Kharuzhy static int bq25890_get_chip_version(struct bq25890_device *bq) 878d20267c9SYauhen Kharuzhy { 879d20267c9SYauhen Kharuzhy int id, rev; 880d20267c9SYauhen Kharuzhy 881d20267c9SYauhen Kharuzhy id = bq25890_field_read(bq, F_PN); 882d20267c9SYauhen Kharuzhy if (id < 0) { 883172d0cceSMartin Kepplinger dev_err(bq->dev, "Cannot read chip ID: %d\n", id); 884d20267c9SYauhen Kharuzhy return id; 885d20267c9SYauhen Kharuzhy } 886d20267c9SYauhen Kharuzhy 887d20267c9SYauhen Kharuzhy rev = bq25890_field_read(bq, F_DEV_REV); 888d20267c9SYauhen Kharuzhy if (rev < 0) { 889172d0cceSMartin Kepplinger dev_err(bq->dev, "Cannot read chip revision: %d\n", rev); 890cb619e80SColin Ian King return rev; 891d20267c9SYauhen Kharuzhy } 892d20267c9SYauhen Kharuzhy 893d20267c9SYauhen Kharuzhy switch (id) { 894d20267c9SYauhen Kharuzhy case BQ25890_ID: 895d20267c9SYauhen Kharuzhy bq->chip_version = BQ25890; 896d20267c9SYauhen Kharuzhy break; 897d20267c9SYauhen Kharuzhy 898d20267c9SYauhen Kharuzhy /* BQ25892 and BQ25896 share same ID 0 */ 899d20267c9SYauhen Kharuzhy case BQ25896_ID: 900d20267c9SYauhen Kharuzhy switch (rev) { 901d20267c9SYauhen Kharuzhy case 2: 902d20267c9SYauhen Kharuzhy bq->chip_version = BQ25896; 903d20267c9SYauhen Kharuzhy break; 904d20267c9SYauhen Kharuzhy case 1: 905d20267c9SYauhen Kharuzhy bq->chip_version = BQ25892; 906d20267c9SYauhen Kharuzhy break; 907d20267c9SYauhen Kharuzhy default: 908d20267c9SYauhen Kharuzhy dev_err(bq->dev, 909d20267c9SYauhen Kharuzhy "Unknown device revision %d, assume BQ25892\n", 910d20267c9SYauhen Kharuzhy rev); 911d20267c9SYauhen Kharuzhy bq->chip_version = BQ25892; 912d20267c9SYauhen Kharuzhy } 913d20267c9SYauhen Kharuzhy break; 914d20267c9SYauhen Kharuzhy 915d20267c9SYauhen Kharuzhy case BQ25895_ID: 916d20267c9SYauhen Kharuzhy bq->chip_version = BQ25895; 917d20267c9SYauhen Kharuzhy break; 918d20267c9SYauhen Kharuzhy 919d20267c9SYauhen Kharuzhy default: 920d20267c9SYauhen Kharuzhy dev_err(bq->dev, "Unknown chip ID %d\n", id); 921d20267c9SYauhen Kharuzhy return -ENODEV; 922d20267c9SYauhen Kharuzhy } 923d20267c9SYauhen Kharuzhy 924d20267c9SYauhen Kharuzhy return 0; 925d20267c9SYauhen Kharuzhy } 926d20267c9SYauhen Kharuzhy 9278c0984e5SSebastian Reichel static int bq25890_irq_probe(struct bq25890_device *bq) 9288c0984e5SSebastian Reichel { 9298c0984e5SSebastian Reichel struct gpio_desc *irq; 9308c0984e5SSebastian Reichel 93186775879SAndy Shevchenko irq = devm_gpiod_get(bq->dev, BQ25890_IRQ_PIN, GPIOD_IN); 932172d0cceSMartin Kepplinger if (IS_ERR(irq)) 933172d0cceSMartin Kepplinger return dev_err_probe(bq->dev, PTR_ERR(irq), 934172d0cceSMartin Kepplinger "Could not probe irq pin.\n"); 9358c0984e5SSebastian Reichel 9368c0984e5SSebastian Reichel return gpiod_to_irq(irq); 9378c0984e5SSebastian Reichel } 9388c0984e5SSebastian Reichel 9398c0984e5SSebastian Reichel static int bq25890_fw_read_u32_props(struct bq25890_device *bq) 9408c0984e5SSebastian Reichel { 9418c0984e5SSebastian Reichel int ret; 9428c0984e5SSebastian Reichel u32 property; 9438c0984e5SSebastian Reichel int i; 9448c0984e5SSebastian Reichel struct bq25890_init_data *init = &bq->init_data; 9458c0984e5SSebastian Reichel struct { 9468c0984e5SSebastian Reichel char *name; 9478c0984e5SSebastian Reichel bool optional; 9488c0984e5SSebastian Reichel enum bq25890_table_ids tbl_id; 9498c0984e5SSebastian Reichel u8 *conv_data; /* holds converted value from given property */ 9508c0984e5SSebastian Reichel } props[] = { 9518c0984e5SSebastian Reichel /* required properties */ 9528c0984e5SSebastian Reichel {"ti,charge-current", false, TBL_ICHG, &init->ichg}, 9538c0984e5SSebastian Reichel {"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg}, 9548c0984e5SSebastian Reichel {"ti,termination-current", false, TBL_ITERM, &init->iterm}, 9558c0984e5SSebastian Reichel {"ti,precharge-current", false, TBL_ITERM, &init->iprechg}, 9568c0984e5SSebastian Reichel {"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin}, 9578c0984e5SSebastian Reichel {"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv}, 9588c0984e5SSebastian Reichel {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti}, 9598c0984e5SSebastian Reichel 9608c0984e5SSebastian Reichel /* optional properties */ 96172408329SMichał Mirosław {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg}, 96272408329SMichał Mirosław {"ti,ibatcomp-micro-ohms", true, TBL_RBATCOMP, &init->rbatcomp}, 96372408329SMichał Mirosław {"ti,ibatcomp-clamp-microvolt", true, TBL_VBATCOMP, &init->vclamp}, 9648c0984e5SSebastian Reichel }; 9658c0984e5SSebastian Reichel 9668c0984e5SSebastian Reichel /* initialize data for optional properties */ 9678c0984e5SSebastian Reichel init->treg = 3; /* 120 degrees Celsius */ 96872408329SMichał Mirosław init->rbatcomp = init->vclamp = 0; /* IBAT compensation disabled */ 9698c0984e5SSebastian Reichel 9708c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(props); i++) { 9718c0984e5SSebastian Reichel ret = device_property_read_u32(bq->dev, props[i].name, 9728c0984e5SSebastian Reichel &property); 9738c0984e5SSebastian Reichel if (ret < 0) { 9748c0984e5SSebastian Reichel if (props[i].optional) 9758c0984e5SSebastian Reichel continue; 9768c0984e5SSebastian Reichel 9779d9ae341SAngus Ainslie (Purism) dev_err(bq->dev, "Unable to read property %d %s\n", ret, 9789d9ae341SAngus Ainslie (Purism) props[i].name); 9799d9ae341SAngus Ainslie (Purism) 9808c0984e5SSebastian Reichel return ret; 9818c0984e5SSebastian Reichel } 9828c0984e5SSebastian Reichel 9838c0984e5SSebastian Reichel *props[i].conv_data = bq25890_find_idx(property, 9848c0984e5SSebastian Reichel props[i].tbl_id); 9858c0984e5SSebastian Reichel } 9868c0984e5SSebastian Reichel 9878c0984e5SSebastian Reichel return 0; 9888c0984e5SSebastian Reichel } 9898c0984e5SSebastian Reichel 9908c0984e5SSebastian Reichel static int bq25890_fw_probe(struct bq25890_device *bq) 9918c0984e5SSebastian Reichel { 9928c0984e5SSebastian Reichel int ret; 9938c0984e5SSebastian Reichel struct bq25890_init_data *init = &bq->init_data; 9948c0984e5SSebastian Reichel 9957e3b8e35SHans de Goede bq->skip_reset = device_property_read_bool(bq->dev, "linux,skip-reset"); 99640428bd4SHans de Goede bq->read_back_init_data = device_property_read_bool(bq->dev, 99740428bd4SHans de Goede "linux,read-back-settings"); 99840428bd4SHans de Goede if (bq->read_back_init_data) 99940428bd4SHans de Goede return 0; 10007e3b8e35SHans de Goede 10018c0984e5SSebastian Reichel ret = bq25890_fw_read_u32_props(bq); 10028c0984e5SSebastian Reichel if (ret < 0) 10038c0984e5SSebastian Reichel return ret; 10048c0984e5SSebastian Reichel 10058c0984e5SSebastian Reichel init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin"); 10068c0984e5SSebastian Reichel init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq"); 10078c0984e5SSebastian Reichel 10088c0984e5SSebastian Reichel return 0; 10098c0984e5SSebastian Reichel } 10108c0984e5SSebastian Reichel 10118c0984e5SSebastian Reichel static int bq25890_probe(struct i2c_client *client, 10128c0984e5SSebastian Reichel const struct i2c_device_id *id) 10138c0984e5SSebastian Reichel { 10148c0984e5SSebastian Reichel struct device *dev = &client->dev; 10158c0984e5SSebastian Reichel struct bq25890_device *bq; 10168c0984e5SSebastian Reichel int ret; 10178c0984e5SSebastian Reichel int i; 10188c0984e5SSebastian Reichel 10198c0984e5SSebastian Reichel bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); 10208c0984e5SSebastian Reichel if (!bq) 10218c0984e5SSebastian Reichel return -ENOMEM; 10228c0984e5SSebastian Reichel 10238c0984e5SSebastian Reichel bq->client = client; 10248c0984e5SSebastian Reichel bq->dev = dev; 10258c0984e5SSebastian Reichel 10268c0984e5SSebastian Reichel mutex_init(&bq->lock); 10278c0984e5SSebastian Reichel 10288c0984e5SSebastian Reichel bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config); 1029172d0cceSMartin Kepplinger if (IS_ERR(bq->rmap)) 1030172d0cceSMartin Kepplinger return dev_err_probe(dev, PTR_ERR(bq->rmap), 1031172d0cceSMartin Kepplinger "failed to allocate register map\n"); 10328c0984e5SSebastian Reichel 10338c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) { 10348c0984e5SSebastian Reichel const struct reg_field *reg_fields = bq25890_reg_fields; 10358c0984e5SSebastian Reichel 10368c0984e5SSebastian Reichel bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap, 10378c0984e5SSebastian Reichel reg_fields[i]); 1038172d0cceSMartin Kepplinger if (IS_ERR(bq->rmap_fields[i])) 1039172d0cceSMartin Kepplinger return dev_err_probe(dev, PTR_ERR(bq->rmap_fields[i]), 1040172d0cceSMartin Kepplinger "cannot allocate regmap field\n"); 10418c0984e5SSebastian Reichel } 10428c0984e5SSebastian Reichel 10438c0984e5SSebastian Reichel i2c_set_clientdata(client, bq); 10448c0984e5SSebastian Reichel 1045d20267c9SYauhen Kharuzhy ret = bq25890_get_chip_version(bq); 1046d20267c9SYauhen Kharuzhy if (ret) { 1047172d0cceSMartin Kepplinger dev_err(dev, "Cannot read chip ID or unknown chip: %d\n", ret); 1048d20267c9SYauhen Kharuzhy return ret; 10498c0984e5SSebastian Reichel } 10508c0984e5SSebastian Reichel 10518c0984e5SSebastian Reichel if (!dev->platform_data) { 10528c0984e5SSebastian Reichel ret = bq25890_fw_probe(bq); 10538c0984e5SSebastian Reichel if (ret < 0) { 1054172d0cceSMartin Kepplinger dev_err(dev, "Cannot read device properties: %d\n", 1055172d0cceSMartin Kepplinger ret); 10568c0984e5SSebastian Reichel return ret; 10578c0984e5SSebastian Reichel } 10588c0984e5SSebastian Reichel } else { 10598c0984e5SSebastian Reichel return -ENODEV; 10608c0984e5SSebastian Reichel } 10618c0984e5SSebastian Reichel 10628c0984e5SSebastian Reichel ret = bq25890_hw_init(bq); 10638c0984e5SSebastian Reichel if (ret < 0) { 1064172d0cceSMartin Kepplinger dev_err(dev, "Cannot initialize the chip: %d\n", ret); 10658c0984e5SSebastian Reichel return ret; 10668c0984e5SSebastian Reichel } 10678c0984e5SSebastian Reichel 10688c0984e5SSebastian Reichel if (client->irq <= 0) 10698c0984e5SSebastian Reichel client->irq = bq25890_irq_probe(bq); 10708c0984e5SSebastian Reichel 10718c0984e5SSebastian Reichel if (client->irq < 0) { 10728c0984e5SSebastian Reichel dev_err(dev, "No irq resource found.\n"); 10738c0984e5SSebastian Reichel return client->irq; 10748c0984e5SSebastian Reichel } 10758c0984e5SSebastian Reichel 10768c0984e5SSebastian Reichel /* OTG reporting */ 10778c0984e5SSebastian Reichel bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); 10788c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) { 10798c0984e5SSebastian Reichel INIT_WORK(&bq->usb_work, bq25890_usb_work); 10808c0984e5SSebastian Reichel bq->usb_nb.notifier_call = bq25890_usb_notifier; 10818c0984e5SSebastian Reichel usb_register_notifier(bq->usb_phy, &bq->usb_nb); 10828c0984e5SSebastian Reichel } 10838c0984e5SSebastian Reichel 1084d01363daSHans de Goede ret = bq25890_power_supply_init(bq); 1085d01363daSHans de Goede if (ret < 0) { 1086d01363daSHans de Goede dev_err(dev, "Failed to register power supply\n"); 1087d01363daSHans de Goede goto err_unregister_usb_notifier; 1088d01363daSHans de Goede } 1089d01363daSHans de Goede 10908c0984e5SSebastian Reichel ret = devm_request_threaded_irq(dev, client->irq, NULL, 10918c0984e5SSebastian Reichel bq25890_irq_handler_thread, 10928c0984e5SSebastian Reichel IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 10938c0984e5SSebastian Reichel BQ25890_IRQ_PIN, bq); 10948c0984e5SSebastian Reichel if (ret) 1095d01363daSHans de Goede goto err_unregister_usb_notifier; 10968c0984e5SSebastian Reichel 10978c0984e5SSebastian Reichel return 0; 10988c0984e5SSebastian Reichel 1099d01363daSHans de Goede err_unregister_usb_notifier: 11008c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) 11018c0984e5SSebastian Reichel usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); 11028c0984e5SSebastian Reichel 11038c0984e5SSebastian Reichel return ret; 11048c0984e5SSebastian Reichel } 11058c0984e5SSebastian Reichel 11068c0984e5SSebastian Reichel static int bq25890_remove(struct i2c_client *client) 11078c0984e5SSebastian Reichel { 11088c0984e5SSebastian Reichel struct bq25890_device *bq = i2c_get_clientdata(client); 11098c0984e5SSebastian Reichel 11108c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) 11118c0984e5SSebastian Reichel usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); 11128c0984e5SSebastian Reichel 11137e3b8e35SHans de Goede if (!bq->skip_reset) { 11148c0984e5SSebastian Reichel /* reset all registers to default values */ 11158c0984e5SSebastian Reichel bq25890_chip_reset(bq); 11167e3b8e35SHans de Goede } 11178c0984e5SSebastian Reichel 11188c0984e5SSebastian Reichel return 0; 11198c0984e5SSebastian Reichel } 11208c0984e5SSebastian Reichel 11218c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP 11228c0984e5SSebastian Reichel static int bq25890_suspend(struct device *dev) 11238c0984e5SSebastian Reichel { 11248c0984e5SSebastian Reichel struct bq25890_device *bq = dev_get_drvdata(dev); 11258c0984e5SSebastian Reichel 11268c0984e5SSebastian Reichel /* 11278c0984e5SSebastian Reichel * If charger is removed, while in suspend, make sure ADC is diabled 11288c0984e5SSebastian Reichel * since it consumes slightly more power. 11298c0984e5SSebastian Reichel */ 113021d90edaSMichał Mirosław return bq25890_field_write(bq, F_CONV_RATE, 0); 11318c0984e5SSebastian Reichel } 11328c0984e5SSebastian Reichel 11338c0984e5SSebastian Reichel static int bq25890_resume(struct device *dev) 11348c0984e5SSebastian Reichel { 11358c0984e5SSebastian Reichel int ret; 11368c0984e5SSebastian Reichel struct bq25890_device *bq = dev_get_drvdata(dev); 11378c0984e5SSebastian Reichel 113872d9cd9cSMichał Mirosław mutex_lock(&bq->lock); 113972d9cd9cSMichał Mirosław 114072d9cd9cSMichał Mirosław ret = bq25890_get_chip_state(bq, &bq->state); 11418c0984e5SSebastian Reichel if (ret < 0) 1142cf5701bfSDan Carpenter goto unlock; 11438c0984e5SSebastian Reichel 11448c0984e5SSebastian Reichel /* Re-enable ADC only if charger is plugged in. */ 114572d9cd9cSMichał Mirosław if (bq->state.online) { 114621d90edaSMichał Mirosław ret = bq25890_field_write(bq, F_CONV_RATE, 1); 11478c0984e5SSebastian Reichel if (ret < 0) 1148cf5701bfSDan Carpenter goto unlock; 11498c0984e5SSebastian Reichel } 11508c0984e5SSebastian Reichel 11518c0984e5SSebastian Reichel /* signal userspace, maybe state changed while suspended */ 11528c0984e5SSebastian Reichel power_supply_changed(bq->charger); 11538c0984e5SSebastian Reichel 1154cf5701bfSDan Carpenter unlock: 115572d9cd9cSMichał Mirosław mutex_unlock(&bq->lock); 115672d9cd9cSMichał Mirosław 1157cf5701bfSDan Carpenter return ret; 11588c0984e5SSebastian Reichel } 11598c0984e5SSebastian Reichel #endif 11608c0984e5SSebastian Reichel 11618c0984e5SSebastian Reichel static const struct dev_pm_ops bq25890_pm = { 11628c0984e5SSebastian Reichel SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume) 11638c0984e5SSebastian Reichel }; 11648c0984e5SSebastian Reichel 11658c0984e5SSebastian Reichel static const struct i2c_device_id bq25890_i2c_ids[] = { 11668c0984e5SSebastian Reichel { "bq25890", 0 }, 116746aa27e7SYauhen Kharuzhy { "bq25892", 0 }, 116846aa27e7SYauhen Kharuzhy { "bq25895", 0 }, 116946aa27e7SYauhen Kharuzhy { "bq25896", 0 }, 11708c0984e5SSebastian Reichel {}, 11718c0984e5SSebastian Reichel }; 11728c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids); 11738c0984e5SSebastian Reichel 11748c0984e5SSebastian Reichel static const struct of_device_id bq25890_of_match[] = { 11758c0984e5SSebastian Reichel { .compatible = "ti,bq25890", }, 117646aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25892", }, 117746aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25895", }, 117846aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25896", }, 11798c0984e5SSebastian Reichel { }, 11808c0984e5SSebastian Reichel }; 11818c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, bq25890_of_match); 11828c0984e5SSebastian Reichel 118302067dc9SKrzysztof Kozlowski #ifdef CONFIG_ACPI 11848c0984e5SSebastian Reichel static const struct acpi_device_id bq25890_acpi_match[] = { 11858c0984e5SSebastian Reichel {"BQ258900", 0}, 11868c0984e5SSebastian Reichel {}, 11878c0984e5SSebastian Reichel }; 11888c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match); 118902067dc9SKrzysztof Kozlowski #endif 11908c0984e5SSebastian Reichel 11918c0984e5SSebastian Reichel static struct i2c_driver bq25890_driver = { 11928c0984e5SSebastian Reichel .driver = { 11938c0984e5SSebastian Reichel .name = "bq25890-charger", 11948c0984e5SSebastian Reichel .of_match_table = of_match_ptr(bq25890_of_match), 11958c0984e5SSebastian Reichel .acpi_match_table = ACPI_PTR(bq25890_acpi_match), 11968c0984e5SSebastian Reichel .pm = &bq25890_pm, 11978c0984e5SSebastian Reichel }, 11988c0984e5SSebastian Reichel .probe = bq25890_probe, 11998c0984e5SSebastian Reichel .remove = bq25890_remove, 12008c0984e5SSebastian Reichel .id_table = bq25890_i2c_ids, 12018c0984e5SSebastian Reichel }; 12028c0984e5SSebastian Reichel module_i2c_driver(bq25890_driver); 12038c0984e5SSebastian Reichel 12048c0984e5SSebastian Reichel MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); 12058c0984e5SSebastian Reichel MODULE_DESCRIPTION("bq25890 charger driver"); 12068c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 1207