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 { 438c0984e5SSebastian Reichel F_EN_HIZ, F_EN_ILIM, F_IILIM, /* 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; 978c0984e5SSebastian Reichel }; 988c0984e5SSebastian Reichel 998c0984e5SSebastian Reichel struct bq25890_device { 1008c0984e5SSebastian Reichel struct i2c_client *client; 1018c0984e5SSebastian Reichel struct device *dev; 1028c0984e5SSebastian Reichel struct power_supply *charger; 1038c0984e5SSebastian Reichel 1048c0984e5SSebastian Reichel struct usb_phy *usb_phy; 1058c0984e5SSebastian Reichel struct notifier_block usb_nb; 1068c0984e5SSebastian Reichel struct work_struct usb_work; 1078c0984e5SSebastian Reichel unsigned long usb_event; 1088c0984e5SSebastian Reichel 1098c0984e5SSebastian Reichel struct regmap *rmap; 1108c0984e5SSebastian Reichel struct regmap_field *rmap_fields[F_MAX_FIELDS]; 1118c0984e5SSebastian Reichel 112d20267c9SYauhen Kharuzhy enum bq25890_chip_version chip_version; 1138c0984e5SSebastian Reichel struct bq25890_init_data init_data; 1148c0984e5SSebastian Reichel struct bq25890_state state; 1158c0984e5SSebastian Reichel 1168c0984e5SSebastian Reichel struct mutex lock; /* protect state data */ 1178c0984e5SSebastian Reichel }; 1188c0984e5SSebastian Reichel 1198c0984e5SSebastian Reichel static const struct regmap_range bq25890_readonly_reg_ranges[] = { 1208c0984e5SSebastian Reichel regmap_reg_range(0x0b, 0x0c), 1218c0984e5SSebastian Reichel regmap_reg_range(0x0e, 0x13), 1228c0984e5SSebastian Reichel }; 1238c0984e5SSebastian Reichel 1248c0984e5SSebastian Reichel static const struct regmap_access_table bq25890_writeable_regs = { 1258c0984e5SSebastian Reichel .no_ranges = bq25890_readonly_reg_ranges, 1268c0984e5SSebastian Reichel .n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges), 1278c0984e5SSebastian Reichel }; 1288c0984e5SSebastian Reichel 1298c0984e5SSebastian Reichel static const struct regmap_range bq25890_volatile_reg_ranges[] = { 1308c0984e5SSebastian Reichel regmap_reg_range(0x00, 0x00), 13121d90edaSMichał Mirosław regmap_reg_range(0x02, 0x02), 1328c0984e5SSebastian Reichel regmap_reg_range(0x09, 0x09), 133d20267c9SYauhen Kharuzhy regmap_reg_range(0x0b, 0x14), 1348c0984e5SSebastian Reichel }; 1358c0984e5SSebastian Reichel 1368c0984e5SSebastian Reichel static const struct regmap_access_table bq25890_volatile_regs = { 1378c0984e5SSebastian Reichel .yes_ranges = bq25890_volatile_reg_ranges, 1388c0984e5SSebastian Reichel .n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges), 1398c0984e5SSebastian Reichel }; 1408c0984e5SSebastian Reichel 1418c0984e5SSebastian Reichel static const struct regmap_config bq25890_regmap_config = { 1428c0984e5SSebastian Reichel .reg_bits = 8, 1438c0984e5SSebastian Reichel .val_bits = 8, 1448c0984e5SSebastian Reichel 1458c0984e5SSebastian Reichel .max_register = 0x14, 1468c0984e5SSebastian Reichel .cache_type = REGCACHE_RBTREE, 1478c0984e5SSebastian Reichel 1488c0984e5SSebastian Reichel .wr_table = &bq25890_writeable_regs, 1498c0984e5SSebastian Reichel .volatile_table = &bq25890_volatile_regs, 1508c0984e5SSebastian Reichel }; 1518c0984e5SSebastian Reichel 1528c0984e5SSebastian Reichel static const struct reg_field bq25890_reg_fields[] = { 1538c0984e5SSebastian Reichel /* REG00 */ 1548c0984e5SSebastian Reichel [F_EN_HIZ] = REG_FIELD(0x00, 7, 7), 1558c0984e5SSebastian Reichel [F_EN_ILIM] = REG_FIELD(0x00, 6, 6), 1568c0984e5SSebastian Reichel [F_IILIM] = REG_FIELD(0x00, 0, 5), 1578c0984e5SSebastian Reichel /* REG01 */ 1588c0984e5SSebastian Reichel [F_BHOT] = REG_FIELD(0x01, 6, 7), 1598c0984e5SSebastian Reichel [F_BCOLD] = REG_FIELD(0x01, 5, 5), 1608c0984e5SSebastian Reichel [F_VINDPM_OFS] = REG_FIELD(0x01, 0, 4), 1618c0984e5SSebastian Reichel /* REG02 */ 1628c0984e5SSebastian Reichel [F_CONV_START] = REG_FIELD(0x02, 7, 7), 1638c0984e5SSebastian Reichel [F_CONV_RATE] = REG_FIELD(0x02, 6, 6), 1648c0984e5SSebastian Reichel [F_BOOSTF] = REG_FIELD(0x02, 5, 5), 1658c0984e5SSebastian Reichel [F_ICO_EN] = REG_FIELD(0x02, 4, 4), 1662e1a2ddeSAngus Ainslie (Purism) [F_HVDCP_EN] = REG_FIELD(0x02, 3, 3), // reserved on BQ25896 1672e1a2ddeSAngus Ainslie (Purism) [F_MAXC_EN] = REG_FIELD(0x02, 2, 2), // reserved on BQ25896 1688c0984e5SSebastian Reichel [F_FORCE_DPM] = REG_FIELD(0x02, 1, 1), 1698c0984e5SSebastian Reichel [F_AUTO_DPDM_EN] = REG_FIELD(0x02, 0, 0), 1708c0984e5SSebastian Reichel /* REG03 */ 1718c0984e5SSebastian Reichel [F_BAT_LOAD_EN] = REG_FIELD(0x03, 7, 7), 1728c0984e5SSebastian Reichel [F_WD_RST] = REG_FIELD(0x03, 6, 6), 1738c0984e5SSebastian Reichel [F_OTG_CFG] = REG_FIELD(0x03, 5, 5), 1748c0984e5SSebastian Reichel [F_CHG_CFG] = REG_FIELD(0x03, 4, 4), 1758c0984e5SSebastian Reichel [F_SYSVMIN] = REG_FIELD(0x03, 1, 3), 176d20267c9SYauhen Kharuzhy [F_MIN_VBAT_SEL] = REG_FIELD(0x03, 0, 0), // BQ25896 only 1778c0984e5SSebastian Reichel /* REG04 */ 1788c0984e5SSebastian Reichel [F_PUMPX_EN] = REG_FIELD(0x04, 7, 7), 1798c0984e5SSebastian Reichel [F_ICHG] = REG_FIELD(0x04, 0, 6), 1808c0984e5SSebastian Reichel /* REG05 */ 1818c0984e5SSebastian Reichel [F_IPRECHG] = REG_FIELD(0x05, 4, 7), 1828c0984e5SSebastian Reichel [F_ITERM] = REG_FIELD(0x05, 0, 3), 1838c0984e5SSebastian Reichel /* REG06 */ 1848c0984e5SSebastian Reichel [F_VREG] = REG_FIELD(0x06, 2, 7), 1858c0984e5SSebastian Reichel [F_BATLOWV] = REG_FIELD(0x06, 1, 1), 1868c0984e5SSebastian Reichel [F_VRECHG] = REG_FIELD(0x06, 0, 0), 1878c0984e5SSebastian Reichel /* REG07 */ 1888c0984e5SSebastian Reichel [F_TERM_EN] = REG_FIELD(0x07, 7, 7), 1898c0984e5SSebastian Reichel [F_STAT_DIS] = REG_FIELD(0x07, 6, 6), 1908c0984e5SSebastian Reichel [F_WD] = REG_FIELD(0x07, 4, 5), 1918c0984e5SSebastian Reichel [F_TMR_EN] = REG_FIELD(0x07, 3, 3), 1928c0984e5SSebastian Reichel [F_CHG_TMR] = REG_FIELD(0x07, 1, 2), 1935c35ba9bSAngus Ainslie (Purism) [F_JEITA_ISET] = REG_FIELD(0x07, 0, 0), // reserved on BQ25895 1948c0984e5SSebastian Reichel /* REG08 */ 19595809139SMichał Mirosław [F_BATCMP] = REG_FIELD(0x08, 5, 7), 1968c0984e5SSebastian Reichel [F_VCLAMP] = REG_FIELD(0x08, 2, 4), 1978c0984e5SSebastian Reichel [F_TREG] = REG_FIELD(0x08, 0, 1), 1988c0984e5SSebastian Reichel /* REG09 */ 1998c0984e5SSebastian Reichel [F_FORCE_ICO] = REG_FIELD(0x09, 7, 7), 2008c0984e5SSebastian Reichel [F_TMR2X_EN] = REG_FIELD(0x09, 6, 6), 2018c0984e5SSebastian Reichel [F_BATFET_DIS] = REG_FIELD(0x09, 5, 5), 2025c35ba9bSAngus Ainslie (Purism) [F_JEITA_VSET] = REG_FIELD(0x09, 4, 4), // reserved on BQ25895 2038c0984e5SSebastian Reichel [F_BATFET_DLY] = REG_FIELD(0x09, 3, 3), 2048c0984e5SSebastian Reichel [F_BATFET_RST_EN] = REG_FIELD(0x09, 2, 2), 2058c0984e5SSebastian Reichel [F_PUMPX_UP] = REG_FIELD(0x09, 1, 1), 2068c0984e5SSebastian Reichel [F_PUMPX_DN] = REG_FIELD(0x09, 0, 0), 2078c0984e5SSebastian Reichel /* REG0A */ 2088c0984e5SSebastian Reichel [F_BOOSTV] = REG_FIELD(0x0A, 4, 7), 2095c35ba9bSAngus Ainslie (Purism) [F_BOOSTI] = REG_FIELD(0x0A, 0, 2), // reserved on BQ25895 210d20267c9SYauhen Kharuzhy [F_PFM_OTG_DIS] = REG_FIELD(0x0A, 3, 3), // BQ25896 only 2118c0984e5SSebastian Reichel /* REG0B */ 2128c0984e5SSebastian Reichel [F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7), 2138c0984e5SSebastian Reichel [F_CHG_STAT] = REG_FIELD(0x0B, 3, 4), 2148c0984e5SSebastian Reichel [F_PG_STAT] = REG_FIELD(0x0B, 2, 2), 2152e1a2ddeSAngus Ainslie (Purism) [F_SDP_STAT] = REG_FIELD(0x0B, 1, 1), // reserved on BQ25896 2168c0984e5SSebastian Reichel [F_VSYS_STAT] = REG_FIELD(0x0B, 0, 0), 2178c0984e5SSebastian Reichel /* REG0C */ 2188c0984e5SSebastian Reichel [F_WD_FAULT] = REG_FIELD(0x0C, 7, 7), 2198c0984e5SSebastian Reichel [F_BOOST_FAULT] = REG_FIELD(0x0C, 6, 6), 2208c0984e5SSebastian Reichel [F_CHG_FAULT] = REG_FIELD(0x0C, 4, 5), 2218c0984e5SSebastian Reichel [F_BAT_FAULT] = REG_FIELD(0x0C, 3, 3), 2228c0984e5SSebastian Reichel [F_NTC_FAULT] = REG_FIELD(0x0C, 0, 2), 2238c0984e5SSebastian Reichel /* REG0D */ 2248c0984e5SSebastian Reichel [F_FORCE_VINDPM] = REG_FIELD(0x0D, 7, 7), 2258c0984e5SSebastian Reichel [F_VINDPM] = REG_FIELD(0x0D, 0, 6), 2268c0984e5SSebastian Reichel /* REG0E */ 2278c0984e5SSebastian Reichel [F_THERM_STAT] = REG_FIELD(0x0E, 7, 7), 2288c0984e5SSebastian Reichel [F_BATV] = REG_FIELD(0x0E, 0, 6), 2298c0984e5SSebastian Reichel /* REG0F */ 2308c0984e5SSebastian Reichel [F_SYSV] = REG_FIELD(0x0F, 0, 6), 2318c0984e5SSebastian Reichel /* REG10 */ 2328c0984e5SSebastian Reichel [F_TSPCT] = REG_FIELD(0x10, 0, 6), 2338c0984e5SSebastian Reichel /* REG11 */ 2348c0984e5SSebastian Reichel [F_VBUS_GD] = REG_FIELD(0x11, 7, 7), 2358c0984e5SSebastian Reichel [F_VBUSV] = REG_FIELD(0x11, 0, 6), 2368c0984e5SSebastian Reichel /* REG12 */ 2378c0984e5SSebastian Reichel [F_ICHGR] = REG_FIELD(0x12, 0, 6), 2388c0984e5SSebastian Reichel /* REG13 */ 2398c0984e5SSebastian Reichel [F_VDPM_STAT] = REG_FIELD(0x13, 7, 7), 2408c0984e5SSebastian Reichel [F_IDPM_STAT] = REG_FIELD(0x13, 6, 6), 2418c0984e5SSebastian Reichel [F_IDPM_LIM] = REG_FIELD(0x13, 0, 5), 2428c0984e5SSebastian Reichel /* REG14 */ 2438c0984e5SSebastian Reichel [F_REG_RST] = REG_FIELD(0x14, 7, 7), 2448c0984e5SSebastian Reichel [F_ICO_OPTIMIZED] = REG_FIELD(0x14, 6, 6), 2458c0984e5SSebastian Reichel [F_PN] = REG_FIELD(0x14, 3, 5), 2468c0984e5SSebastian Reichel [F_TS_PROFILE] = REG_FIELD(0x14, 2, 2), 2478c0984e5SSebastian Reichel [F_DEV_REV] = REG_FIELD(0x14, 0, 1) 2488c0984e5SSebastian Reichel }; 2498c0984e5SSebastian Reichel 2508c0984e5SSebastian Reichel /* 2518c0984e5SSebastian Reichel * Most of the val -> idx conversions can be computed, given the minimum, 2528c0984e5SSebastian Reichel * maximum and the step between values. For the rest of conversions, we use 2538c0984e5SSebastian Reichel * lookup tables. 2548c0984e5SSebastian Reichel */ 2558c0984e5SSebastian Reichel enum bq25890_table_ids { 2568c0984e5SSebastian Reichel /* range tables */ 2578c0984e5SSebastian Reichel TBL_ICHG, 2588c0984e5SSebastian Reichel TBL_ITERM, 259478efc79SMichał Mirosław TBL_IILIM, 2608c0984e5SSebastian Reichel TBL_VREG, 2618c0984e5SSebastian Reichel TBL_BOOSTV, 2628c0984e5SSebastian Reichel TBL_SYSVMIN, 26372408329SMichał Mirosław TBL_VBATCOMP, 26472408329SMichał Mirosław TBL_RBATCOMP, 2658c0984e5SSebastian Reichel 2668c0984e5SSebastian Reichel /* lookup tables */ 2678c0984e5SSebastian Reichel TBL_TREG, 2688c0984e5SSebastian Reichel TBL_BOOSTI, 269*9652c024SAngus Ainslie TBL_TSPCT, 2708c0984e5SSebastian Reichel }; 2718c0984e5SSebastian Reichel 2728c0984e5SSebastian Reichel /* Thermal Regulation Threshold lookup table, in degrees Celsius */ 2738c0984e5SSebastian Reichel static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 }; 2748c0984e5SSebastian Reichel 2758c0984e5SSebastian Reichel #define BQ25890_TREG_TBL_SIZE ARRAY_SIZE(bq25890_treg_tbl) 2768c0984e5SSebastian Reichel 2778c0984e5SSebastian Reichel /* Boost mode current limit lookup table, in uA */ 2788c0984e5SSebastian Reichel static const u32 bq25890_boosti_tbl[] = { 2798c0984e5SSebastian Reichel 500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000 2808c0984e5SSebastian Reichel }; 2818c0984e5SSebastian Reichel 2828c0984e5SSebastian Reichel #define BQ25890_BOOSTI_TBL_SIZE ARRAY_SIZE(bq25890_boosti_tbl) 2838c0984e5SSebastian Reichel 284*9652c024SAngus Ainslie /* NTC 10K temperature lookup table in tenths of a degree */ 285*9652c024SAngus Ainslie static const u32 bq25890_tspct_tbl[] = { 286*9652c024SAngus Ainslie 850, 840, 830, 820, 810, 800, 790, 780, 287*9652c024SAngus Ainslie 770, 760, 750, 740, 730, 720, 710, 700, 288*9652c024SAngus Ainslie 690, 685, 680, 675, 670, 660, 650, 645, 289*9652c024SAngus Ainslie 640, 630, 620, 615, 610, 600, 590, 585, 290*9652c024SAngus Ainslie 580, 570, 565, 560, 550, 540, 535, 530, 291*9652c024SAngus Ainslie 520, 515, 510, 500, 495, 490, 480, 475, 292*9652c024SAngus Ainslie 470, 460, 455, 450, 440, 435, 430, 425, 293*9652c024SAngus Ainslie 420, 410, 405, 400, 390, 385, 380, 370, 294*9652c024SAngus Ainslie 365, 360, 355, 350, 340, 335, 330, 320, 295*9652c024SAngus Ainslie 310, 305, 300, 290, 285, 280, 275, 270, 296*9652c024SAngus Ainslie 260, 250, 245, 240, 230, 225, 220, 210, 297*9652c024SAngus Ainslie 205, 200, 190, 180, 175, 170, 160, 150, 298*9652c024SAngus Ainslie 145, 140, 130, 120, 115, 110, 100, 90, 299*9652c024SAngus Ainslie 80, 70, 60, 50, 40, 30, 20, 10, 300*9652c024SAngus Ainslie 0, -10, -20, -30, -40, -60, -70, -80, 301*9652c024SAngus Ainslie -90, -10, -120, -140, -150, -170, -190, -210, 302*9652c024SAngus Ainslie }; 303*9652c024SAngus Ainslie 304*9652c024SAngus Ainslie #define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl) 305*9652c024SAngus Ainslie 3068c0984e5SSebastian Reichel struct bq25890_range { 3078c0984e5SSebastian Reichel u32 min; 3088c0984e5SSebastian Reichel u32 max; 3098c0984e5SSebastian Reichel u32 step; 3108c0984e5SSebastian Reichel }; 3118c0984e5SSebastian Reichel 3128c0984e5SSebastian Reichel struct bq25890_lookup { 3138c0984e5SSebastian Reichel const u32 *tbl; 3148c0984e5SSebastian Reichel u32 size; 3158c0984e5SSebastian Reichel }; 3168c0984e5SSebastian Reichel 3178c0984e5SSebastian Reichel static const union { 3188c0984e5SSebastian Reichel struct bq25890_range rt; 3198c0984e5SSebastian Reichel struct bq25890_lookup lt; 3208c0984e5SSebastian Reichel } bq25890_tables[] = { 3218c0984e5SSebastian Reichel /* range tables */ 322d20267c9SYauhen Kharuzhy /* TODO: BQ25896 has max ICHG 3008 mA */ 3238c0984e5SSebastian Reichel [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ 3248c0984e5SSebastian Reichel [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ 325d4e9e7b6SSebastian Krzyszkowiak [TBL_IILIM] = { .rt = {100000, 3250000, 50000} }, /* uA */ 3268c0984e5SSebastian Reichel [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ 3278c0984e5SSebastian Reichel [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ 3288c0984e5SSebastian Reichel [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ 32972408329SMichał Mirosław [TBL_VBATCOMP] ={ .rt = {0, 224000, 32000} }, /* uV */ 33072408329SMichał Mirosław [TBL_RBATCOMP] ={ .rt = {0, 140000, 20000} }, /* uOhm */ 3318c0984e5SSebastian Reichel 3328c0984e5SSebastian Reichel /* lookup tables */ 3338c0984e5SSebastian Reichel [TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} }, 334*9652c024SAngus Ainslie [TBL_BOOSTI] = { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }, 335*9652c024SAngus Ainslie [TBL_TSPCT] = { .lt = {bq25890_tspct_tbl, BQ25890_TSPCT_TBL_SIZE} } 3368c0984e5SSebastian Reichel }; 3378c0984e5SSebastian Reichel 3388c0984e5SSebastian Reichel static int bq25890_field_read(struct bq25890_device *bq, 3398c0984e5SSebastian Reichel enum bq25890_fields field_id) 3408c0984e5SSebastian Reichel { 3418c0984e5SSebastian Reichel int ret; 3428c0984e5SSebastian Reichel int val; 3438c0984e5SSebastian Reichel 3448c0984e5SSebastian Reichel ret = regmap_field_read(bq->rmap_fields[field_id], &val); 3458c0984e5SSebastian Reichel if (ret < 0) 3468c0984e5SSebastian Reichel return ret; 3478c0984e5SSebastian Reichel 3488c0984e5SSebastian Reichel return val; 3498c0984e5SSebastian Reichel } 3508c0984e5SSebastian Reichel 3518c0984e5SSebastian Reichel static int bq25890_field_write(struct bq25890_device *bq, 3528c0984e5SSebastian Reichel enum bq25890_fields field_id, u8 val) 3538c0984e5SSebastian Reichel { 3548c0984e5SSebastian Reichel return regmap_field_write(bq->rmap_fields[field_id], val); 3558c0984e5SSebastian Reichel } 3568c0984e5SSebastian Reichel 3578c0984e5SSebastian Reichel static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id) 3588c0984e5SSebastian Reichel { 3598c0984e5SSebastian Reichel u8 idx; 3608c0984e5SSebastian Reichel 3618c0984e5SSebastian Reichel if (id >= TBL_TREG) { 3628c0984e5SSebastian Reichel const u32 *tbl = bq25890_tables[id].lt.tbl; 3638c0984e5SSebastian Reichel u32 tbl_size = bq25890_tables[id].lt.size; 3648c0984e5SSebastian Reichel 3658c0984e5SSebastian Reichel for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++) 3668c0984e5SSebastian Reichel ; 3678c0984e5SSebastian Reichel } else { 3688c0984e5SSebastian Reichel const struct bq25890_range *rtbl = &bq25890_tables[id].rt; 3698c0984e5SSebastian Reichel u8 rtbl_size; 3708c0984e5SSebastian Reichel 3718c0984e5SSebastian Reichel rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1; 3728c0984e5SSebastian Reichel 3738c0984e5SSebastian Reichel for (idx = 1; 3748c0984e5SSebastian Reichel idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value); 3758c0984e5SSebastian Reichel idx++) 3768c0984e5SSebastian Reichel ; 3778c0984e5SSebastian Reichel } 3788c0984e5SSebastian Reichel 3798c0984e5SSebastian Reichel return idx - 1; 3808c0984e5SSebastian Reichel } 3818c0984e5SSebastian Reichel 3828c0984e5SSebastian Reichel static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id) 3838c0984e5SSebastian Reichel { 3848c0984e5SSebastian Reichel const struct bq25890_range *rtbl; 3858c0984e5SSebastian Reichel 3868c0984e5SSebastian Reichel /* lookup table? */ 3878c0984e5SSebastian Reichel if (id >= TBL_TREG) 3888c0984e5SSebastian Reichel return bq25890_tables[id].lt.tbl[idx]; 3898c0984e5SSebastian Reichel 3908c0984e5SSebastian Reichel /* range table */ 3918c0984e5SSebastian Reichel rtbl = &bq25890_tables[id].rt; 3928c0984e5SSebastian Reichel 3938c0984e5SSebastian Reichel return (rtbl->min + idx * rtbl->step); 3948c0984e5SSebastian Reichel } 3958c0984e5SSebastian Reichel 3968c0984e5SSebastian Reichel enum bq25890_status { 3978c0984e5SSebastian Reichel STATUS_NOT_CHARGING, 3988c0984e5SSebastian Reichel STATUS_PRE_CHARGING, 3998c0984e5SSebastian Reichel STATUS_FAST_CHARGING, 4008c0984e5SSebastian Reichel STATUS_TERMINATION_DONE, 4018c0984e5SSebastian Reichel }; 4028c0984e5SSebastian Reichel 4038c0984e5SSebastian Reichel enum bq25890_chrg_fault { 4048c0984e5SSebastian Reichel CHRG_FAULT_NORMAL, 4058c0984e5SSebastian Reichel CHRG_FAULT_INPUT, 4068c0984e5SSebastian Reichel CHRG_FAULT_THERMAL_SHUTDOWN, 4078c0984e5SSebastian Reichel CHRG_FAULT_TIMER_EXPIRED, 4088c0984e5SSebastian Reichel }; 4098c0984e5SSebastian Reichel 41021d90edaSMichał Mirosław static bool bq25890_is_adc_property(enum power_supply_property psp) 41121d90edaSMichał Mirosław { 41221d90edaSMichał Mirosław switch (psp) { 41321d90edaSMichał Mirosław case POWER_SUPPLY_PROP_VOLTAGE_NOW: 41421d90edaSMichał Mirosław case POWER_SUPPLY_PROP_CURRENT_NOW: 415*9652c024SAngus Ainslie case POWER_SUPPLY_PROP_TEMP: 41621d90edaSMichał Mirosław return true; 41721d90edaSMichał Mirosław 41821d90edaSMichał Mirosław default: 41921d90edaSMichał Mirosław return false; 42021d90edaSMichał Mirosław } 42121d90edaSMichał Mirosław } 42221d90edaSMichał Mirosław 4233b4df57bSMichał Mirosław static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq); 4243b4df57bSMichał Mirosław 4258c0984e5SSebastian Reichel static int bq25890_power_supply_get_property(struct power_supply *psy, 4268c0984e5SSebastian Reichel enum power_supply_property psp, 4278c0984e5SSebastian Reichel union power_supply_propval *val) 4288c0984e5SSebastian Reichel { 4298c0984e5SSebastian Reichel struct bq25890_device *bq = power_supply_get_drvdata(psy); 4308c0984e5SSebastian Reichel struct bq25890_state state; 43121d90edaSMichał Mirosław bool do_adc_conv; 43221d90edaSMichał Mirosław int ret; 4338c0984e5SSebastian Reichel 4348c0984e5SSebastian Reichel mutex_lock(&bq->lock); 4353b4df57bSMichał Mirosław /* update state in case we lost an interrupt */ 4363b4df57bSMichał Mirosław __bq25890_handle_irq(bq); 4378c0984e5SSebastian Reichel state = bq->state; 43821d90edaSMichał Mirosław do_adc_conv = !state.online && bq25890_is_adc_property(psp); 43921d90edaSMichał Mirosław if (do_adc_conv) 44021d90edaSMichał Mirosław bq25890_field_write(bq, F_CONV_START, 1); 4418c0984e5SSebastian Reichel mutex_unlock(&bq->lock); 4428c0984e5SSebastian Reichel 44321d90edaSMichał Mirosław if (do_adc_conv) 44421d90edaSMichał Mirosław regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START], 44521d90edaSMichał Mirosław ret, !ret, 25000, 1000000); 44621d90edaSMichał Mirosław 4478c0984e5SSebastian Reichel switch (psp) { 4488c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 4498c0984e5SSebastian Reichel if (!state.online) 4508c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 4518c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_NOT_CHARGING) 4528c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 4538c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_PRE_CHARGING || 4548c0984e5SSebastian Reichel state.chrg_status == STATUS_FAST_CHARGING) 4558c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_CHARGING; 4568c0984e5SSebastian Reichel else if (state.chrg_status == STATUS_TERMINATION_DONE) 4578c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_FULL; 4588c0984e5SSebastian Reichel else 4598c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 4608c0984e5SSebastian Reichel 4618c0984e5SSebastian Reichel break; 4628c0984e5SSebastian Reichel 463b302a0aeSMichał Mirosław case POWER_SUPPLY_PROP_CHARGE_TYPE: 464b302a0aeSMichał Mirosław if (!state.online || state.chrg_status == STATUS_NOT_CHARGING || 465b302a0aeSMichał Mirosław state.chrg_status == STATUS_TERMINATION_DONE) 466b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 467b302a0aeSMichał Mirosław else if (state.chrg_status == STATUS_PRE_CHARGING) 468b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; 469b302a0aeSMichał Mirosław else if (state.chrg_status == STATUS_FAST_CHARGING) 470b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 471b302a0aeSMichał Mirosław else /* unreachable */ 472b302a0aeSMichał Mirosław val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 473b302a0aeSMichał Mirosław break; 474b302a0aeSMichał Mirosław 4758c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_MANUFACTURER: 4768c0984e5SSebastian Reichel val->strval = BQ25890_MANUFACTURER; 4778c0984e5SSebastian Reichel break; 4788c0984e5SSebastian Reichel 4792e1a2ddeSAngus Ainslie (Purism) case POWER_SUPPLY_PROP_MODEL_NAME: 4805956fca7SMichał Mirosław val->strval = bq25890_chip_name[bq->chip_version]; 4812e1a2ddeSAngus Ainslie (Purism) break; 4822e1a2ddeSAngus Ainslie (Purism) 4838c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_ONLINE: 4848c0984e5SSebastian Reichel val->intval = state.online; 4858c0984e5SSebastian Reichel break; 4868c0984e5SSebastian Reichel 4878c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH: 4888c0984e5SSebastian Reichel if (!state.chrg_fault && !state.bat_fault && !state.boost_fault) 4898c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_GOOD; 4908c0984e5SSebastian Reichel else if (state.bat_fault) 4918c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 4928c0984e5SSebastian Reichel else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED) 4938c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; 4948c0984e5SSebastian Reichel else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN) 4958c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 4968c0984e5SSebastian Reichel else 4978c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 4988c0984e5SSebastian Reichel break; 4998c0984e5SSebastian Reichel 5008c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 501f83a6eceSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG); 5028c0984e5SSebastian Reichel break; 5038c0984e5SSebastian Reichel 5048c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 5058c0984e5SSebastian Reichel if (!state.online) { 5068c0984e5SSebastian Reichel val->intval = 0; 5078c0984e5SSebastian Reichel break; 5088c0984e5SSebastian Reichel } 5098c0984e5SSebastian Reichel 5108c0984e5SSebastian Reichel ret = bq25890_field_read(bq, F_BATV); /* read measured value */ 5118c0984e5SSebastian Reichel if (ret < 0) 5128c0984e5SSebastian Reichel return ret; 5138c0984e5SSebastian Reichel 5148c0984e5SSebastian Reichel /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */ 5158c0984e5SSebastian Reichel val->intval = 2304000 + ret * 20000; 5168c0984e5SSebastian Reichel break; 5178c0984e5SSebastian Reichel 5188c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 519f83a6eceSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG); 5208c0984e5SSebastian Reichel break; 5218c0984e5SSebastian Reichel 522c942656dSMichał Mirosław case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: 523c942656dSMichał Mirosław val->intval = bq25890_find_val(bq->init_data.iprechg, TBL_ITERM); 524c942656dSMichał Mirosław break; 525c942656dSMichał Mirosław 5268c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 5278c0984e5SSebastian Reichel val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM); 5288c0984e5SSebastian Reichel break; 5298c0984e5SSebastian Reichel 530478efc79SMichał Mirosław case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 531478efc79SMichał Mirosław ret = bq25890_field_read(bq, F_IILIM); 532478efc79SMichał Mirosław if (ret < 0) 533478efc79SMichał Mirosław return ret; 534478efc79SMichał Mirosław 535478efc79SMichał Mirosław val->intval = bq25890_find_val(ret, TBL_IILIM); 536478efc79SMichał Mirosław break; 537478efc79SMichał Mirosław 538ae6fe7a3SAngus Ainslie (Purism) case POWER_SUPPLY_PROP_VOLTAGE_NOW: 539ae6fe7a3SAngus Ainslie (Purism) ret = bq25890_field_read(bq, F_SYSV); /* read measured value */ 540ae6fe7a3SAngus Ainslie (Purism) if (ret < 0) 541ae6fe7a3SAngus Ainslie (Purism) return ret; 542ae6fe7a3SAngus Ainslie (Purism) 543ae6fe7a3SAngus Ainslie (Purism) /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */ 544ae6fe7a3SAngus Ainslie (Purism) val->intval = 2304000 + ret * 20000; 545ae6fe7a3SAngus Ainslie (Purism) break; 546ae6fe7a3SAngus Ainslie (Purism) 5471e4724d0SMichał Mirosław case POWER_SUPPLY_PROP_CURRENT_NOW: 5481e4724d0SMichał Mirosław ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */ 5491e4724d0SMichał Mirosław if (ret < 0) 5501e4724d0SMichał Mirosław return ret; 5511e4724d0SMichał Mirosław 5521e4724d0SMichał Mirosław /* converted_val = ADC_val * 50mA (table 10.3.19) */ 5531e4724d0SMichał Mirosław val->intval = ret * -50000; 5541e4724d0SMichał Mirosław break; 5551e4724d0SMichał Mirosław 556*9652c024SAngus Ainslie case POWER_SUPPLY_PROP_TEMP: 557*9652c024SAngus Ainslie ret = bq25890_field_read(bq, F_TSPCT); 558*9652c024SAngus Ainslie if (ret < 0) 559*9652c024SAngus Ainslie return ret; 560*9652c024SAngus Ainslie 561*9652c024SAngus Ainslie /* convert TS percentage into rough temperature */ 562*9652c024SAngus Ainslie val->intval = bq25890_find_val(ret, TBL_TSPCT); 563*9652c024SAngus Ainslie break; 564*9652c024SAngus Ainslie 5658c0984e5SSebastian Reichel default: 5668c0984e5SSebastian Reichel return -EINVAL; 5678c0984e5SSebastian Reichel } 5688c0984e5SSebastian Reichel 5698c0984e5SSebastian Reichel return 0; 5708c0984e5SSebastian Reichel } 5718c0984e5SSebastian Reichel 5728c0984e5SSebastian Reichel static int bq25890_get_chip_state(struct bq25890_device *bq, 5738c0984e5SSebastian Reichel struct bq25890_state *state) 5748c0984e5SSebastian Reichel { 5758c0984e5SSebastian Reichel int i, ret; 5768c0984e5SSebastian Reichel 5778c0984e5SSebastian Reichel struct { 5788c0984e5SSebastian Reichel enum bq25890_fields id; 5798c0984e5SSebastian Reichel u8 *data; 5808c0984e5SSebastian Reichel } state_fields[] = { 5818c0984e5SSebastian Reichel {F_CHG_STAT, &state->chrg_status}, 5828c0984e5SSebastian Reichel {F_PG_STAT, &state->online}, 5838c0984e5SSebastian Reichel {F_VSYS_STAT, &state->vsys_status}, 5848c0984e5SSebastian Reichel {F_BOOST_FAULT, &state->boost_fault}, 5858c0984e5SSebastian Reichel {F_BAT_FAULT, &state->bat_fault}, 5868c0984e5SSebastian Reichel {F_CHG_FAULT, &state->chrg_fault} 5878c0984e5SSebastian Reichel }; 5888c0984e5SSebastian Reichel 5898c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(state_fields); i++) { 5908c0984e5SSebastian Reichel ret = bq25890_field_read(bq, state_fields[i].id); 5918c0984e5SSebastian Reichel if (ret < 0) 5928c0984e5SSebastian Reichel return ret; 5938c0984e5SSebastian Reichel 5948c0984e5SSebastian Reichel *state_fields[i].data = ret; 5958c0984e5SSebastian Reichel } 5968c0984e5SSebastian Reichel 5978c0984e5SSebastian Reichel dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT=%d/%d/%d\n", 5988c0984e5SSebastian Reichel state->chrg_status, state->online, state->vsys_status, 5998c0984e5SSebastian Reichel state->chrg_fault, state->boost_fault, state->bat_fault); 6008c0984e5SSebastian Reichel 6018c0984e5SSebastian Reichel return 0; 6028c0984e5SSebastian Reichel } 6038c0984e5SSebastian Reichel 60472d9cd9cSMichał Mirosław static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq) 6058c0984e5SSebastian Reichel { 60672d9cd9cSMichał Mirosław struct bq25890_state new_state; 6078c0984e5SSebastian Reichel int ret; 6088c0984e5SSebastian Reichel 60972d9cd9cSMichał Mirosław ret = bq25890_get_chip_state(bq, &new_state); 61072d9cd9cSMichał Mirosław if (ret < 0) 61172d9cd9cSMichał Mirosław return IRQ_NONE; 6128c0984e5SSebastian Reichel 61372d9cd9cSMichał Mirosław if (!memcmp(&bq->state, &new_state, sizeof(new_state))) 61472d9cd9cSMichał Mirosław return IRQ_NONE; 61572d9cd9cSMichał Mirosław 61672d9cd9cSMichał Mirosław if (!new_state.online && bq->state.online) { /* power removed */ 6178c0984e5SSebastian Reichel /* disable ADC */ 6188c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_CONV_START, 0); 6198c0984e5SSebastian Reichel if (ret < 0) 6208c0984e5SSebastian Reichel goto error; 62172d9cd9cSMichał Mirosław } else if (new_state.online && !bq->state.online) { /* power inserted */ 6228c0984e5SSebastian Reichel /* enable ADC, to have control of charge current/voltage */ 6238c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_CONV_START, 1); 6248c0984e5SSebastian Reichel if (ret < 0) 6258c0984e5SSebastian Reichel goto error; 6268c0984e5SSebastian Reichel } 6278c0984e5SSebastian Reichel 62872d9cd9cSMichał Mirosław bq->state = new_state; 62972d9cd9cSMichał Mirosław power_supply_changed(bq->charger); 6308c0984e5SSebastian Reichel 63172d9cd9cSMichał Mirosław return IRQ_HANDLED; 6328c0984e5SSebastian Reichel error: 63372d9cd9cSMichał Mirosław dev_err(bq->dev, "Error communicating with the chip: %pe\n", 63472d9cd9cSMichał Mirosław ERR_PTR(ret)); 63572d9cd9cSMichał Mirosław return IRQ_HANDLED; 6368c0984e5SSebastian Reichel } 6378c0984e5SSebastian Reichel 6388c0984e5SSebastian Reichel static irqreturn_t bq25890_irq_handler_thread(int irq, void *private) 6398c0984e5SSebastian Reichel { 6408c0984e5SSebastian Reichel struct bq25890_device *bq = private; 64172d9cd9cSMichał Mirosław irqreturn_t ret; 6428c0984e5SSebastian Reichel 6438c0984e5SSebastian Reichel mutex_lock(&bq->lock); 64472d9cd9cSMichał Mirosław ret = __bq25890_handle_irq(bq); 6458c0984e5SSebastian Reichel mutex_unlock(&bq->lock); 6468c0984e5SSebastian Reichel 64772d9cd9cSMichał Mirosław return ret; 6488c0984e5SSebastian Reichel } 6498c0984e5SSebastian Reichel 6508c0984e5SSebastian Reichel static int bq25890_chip_reset(struct bq25890_device *bq) 6518c0984e5SSebastian Reichel { 6528c0984e5SSebastian Reichel int ret; 6538c0984e5SSebastian Reichel int rst_check_counter = 10; 6548c0984e5SSebastian Reichel 6558c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_REG_RST, 1); 6568c0984e5SSebastian Reichel if (ret < 0) 6578c0984e5SSebastian Reichel return ret; 6588c0984e5SSebastian Reichel 6598c0984e5SSebastian Reichel do { 6608c0984e5SSebastian Reichel ret = bq25890_field_read(bq, F_REG_RST); 6618c0984e5SSebastian Reichel if (ret < 0) 6628c0984e5SSebastian Reichel return ret; 6638c0984e5SSebastian Reichel 6648c0984e5SSebastian Reichel usleep_range(5, 10); 6658c0984e5SSebastian Reichel } while (ret == 1 && --rst_check_counter); 6668c0984e5SSebastian Reichel 6678c0984e5SSebastian Reichel if (!rst_check_counter) 6688c0984e5SSebastian Reichel return -ETIMEDOUT; 6698c0984e5SSebastian Reichel 6708c0984e5SSebastian Reichel return 0; 6718c0984e5SSebastian Reichel } 6728c0984e5SSebastian Reichel 6738c0984e5SSebastian Reichel static int bq25890_hw_init(struct bq25890_device *bq) 6748c0984e5SSebastian Reichel { 6758c0984e5SSebastian Reichel int ret; 6768c0984e5SSebastian Reichel int i; 6778c0984e5SSebastian Reichel 6788c0984e5SSebastian Reichel const struct { 6798c0984e5SSebastian Reichel enum bq25890_fields id; 6808c0984e5SSebastian Reichel u32 value; 6818c0984e5SSebastian Reichel } init_data[] = { 6828c0984e5SSebastian Reichel {F_ICHG, bq->init_data.ichg}, 6838c0984e5SSebastian Reichel {F_VREG, bq->init_data.vreg}, 6848c0984e5SSebastian Reichel {F_ITERM, bq->init_data.iterm}, 6858c0984e5SSebastian Reichel {F_IPRECHG, bq->init_data.iprechg}, 6868c0984e5SSebastian Reichel {F_SYSVMIN, bq->init_data.sysvmin}, 6878c0984e5SSebastian Reichel {F_BOOSTV, bq->init_data.boostv}, 6888c0984e5SSebastian Reichel {F_BOOSTI, bq->init_data.boosti}, 6898c0984e5SSebastian Reichel {F_BOOSTF, bq->init_data.boostf}, 6908c0984e5SSebastian Reichel {F_EN_ILIM, bq->init_data.ilim_en}, 69172408329SMichał Mirosław {F_TREG, bq->init_data.treg}, 69272408329SMichał Mirosław {F_BATCMP, bq->init_data.rbatcomp}, 69372408329SMichał Mirosław {F_VCLAMP, bq->init_data.vclamp}, 6948c0984e5SSebastian Reichel }; 6958c0984e5SSebastian Reichel 6968c0984e5SSebastian Reichel ret = bq25890_chip_reset(bq); 6979d9ae341SAngus Ainslie (Purism) if (ret < 0) { 6989d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Reset failed %d\n", ret); 6998c0984e5SSebastian Reichel return ret; 700ad1570d9Skbuild test robot } 7018c0984e5SSebastian Reichel 7028c0984e5SSebastian Reichel /* disable watchdog */ 7038c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_WD, 0); 7049d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7059d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Disabling watchdog failed %d\n", ret); 7068c0984e5SSebastian Reichel return ret; 707ad1570d9Skbuild test robot } 7088c0984e5SSebastian Reichel 7098c0984e5SSebastian Reichel /* initialize currents/voltages and other parameters */ 7108c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(init_data); i++) { 7118c0984e5SSebastian Reichel ret = bq25890_field_write(bq, init_data[i].id, 7128c0984e5SSebastian Reichel init_data[i].value); 7139d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7149d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Writing init data failed %d\n", ret); 7158c0984e5SSebastian Reichel return ret; 716ad1570d9Skbuild test robot } 7178c0984e5SSebastian Reichel } 7188c0984e5SSebastian Reichel 71922ad4f99SHans de Goede ret = bq25890_get_chip_state(bq, &bq->state); 72022ad4f99SHans de Goede if (ret < 0) { 72122ad4f99SHans de Goede dev_dbg(bq->dev, "Get state failed %d\n", ret); 72222ad4f99SHans de Goede return ret; 72322ad4f99SHans de Goede } 72422ad4f99SHans de Goede 72521d90edaSMichał Mirosław /* Configure ADC for continuous conversions when charging */ 72621d90edaSMichał Mirosław ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online); 7279d9ae341SAngus Ainslie (Purism) if (ret < 0) { 7289d9ae341SAngus Ainslie (Purism) dev_dbg(bq->dev, "Config ADC failed %d\n", ret); 7298c0984e5SSebastian Reichel return ret; 730ad1570d9Skbuild test robot } 7318c0984e5SSebastian Reichel 7328c0984e5SSebastian Reichel return 0; 7338c0984e5SSebastian Reichel } 7348c0984e5SSebastian Reichel 735a6a48facSMichał Mirosław static const enum power_supply_property bq25890_power_supply_props[] = { 7368c0984e5SSebastian Reichel POWER_SUPPLY_PROP_MANUFACTURER, 7372e1a2ddeSAngus Ainslie (Purism) POWER_SUPPLY_PROP_MODEL_NAME, 7388c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 739b302a0aeSMichał Mirosław POWER_SUPPLY_PROP_CHARGE_TYPE, 7408c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 7418c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 7428c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 7438c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 7448c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 745c942656dSMichał Mirosław POWER_SUPPLY_PROP_PRECHARGE_CURRENT, 7468c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, 747478efc79SMichał Mirosław POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 748ae6fe7a3SAngus Ainslie (Purism) POWER_SUPPLY_PROP_VOLTAGE_NOW, 7491e4724d0SMichał Mirosław POWER_SUPPLY_PROP_CURRENT_NOW, 750*9652c024SAngus Ainslie POWER_SUPPLY_PROP_TEMP, 7518c0984e5SSebastian Reichel }; 7528c0984e5SSebastian Reichel 7538c0984e5SSebastian Reichel static char *bq25890_charger_supplied_to[] = { 7548c0984e5SSebastian Reichel "main-battery", 7558c0984e5SSebastian Reichel }; 7568c0984e5SSebastian Reichel 7578c0984e5SSebastian Reichel static const struct power_supply_desc bq25890_power_supply_desc = { 7588c0984e5SSebastian Reichel .name = "bq25890-charger", 7598c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_USB, 7608c0984e5SSebastian Reichel .properties = bq25890_power_supply_props, 7618c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(bq25890_power_supply_props), 7628c0984e5SSebastian Reichel .get_property = bq25890_power_supply_get_property, 7638c0984e5SSebastian Reichel }; 7648c0984e5SSebastian Reichel 7658c0984e5SSebastian Reichel static int bq25890_power_supply_init(struct bq25890_device *bq) 7668c0984e5SSebastian Reichel { 7678c0984e5SSebastian Reichel struct power_supply_config psy_cfg = { .drv_data = bq, }; 7688c0984e5SSebastian Reichel 7698c0984e5SSebastian Reichel psy_cfg.supplied_to = bq25890_charger_supplied_to; 7708c0984e5SSebastian Reichel psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to); 7718c0984e5SSebastian Reichel 772d01363daSHans de Goede bq->charger = devm_power_supply_register(bq->dev, 773d01363daSHans de Goede &bq25890_power_supply_desc, 7748c0984e5SSebastian Reichel &psy_cfg); 7758c0984e5SSebastian Reichel 7768c0984e5SSebastian Reichel return PTR_ERR_OR_ZERO(bq->charger); 7778c0984e5SSebastian Reichel } 7788c0984e5SSebastian Reichel 7798c0984e5SSebastian Reichel static void bq25890_usb_work(struct work_struct *data) 7808c0984e5SSebastian Reichel { 7818c0984e5SSebastian Reichel int ret; 7828c0984e5SSebastian Reichel struct bq25890_device *bq = 7838c0984e5SSebastian Reichel container_of(data, struct bq25890_device, usb_work); 7848c0984e5SSebastian Reichel 7858c0984e5SSebastian Reichel switch (bq->usb_event) { 7868c0984e5SSebastian Reichel case USB_EVENT_ID: 7878c0984e5SSebastian Reichel /* Enable boost mode */ 7888c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_OTG_CFG, 1); 7898c0984e5SSebastian Reichel if (ret < 0) 7908c0984e5SSebastian Reichel goto error; 7918c0984e5SSebastian Reichel break; 7928c0984e5SSebastian Reichel 7938c0984e5SSebastian Reichel case USB_EVENT_NONE: 7948c0984e5SSebastian Reichel /* Disable boost mode */ 7958c0984e5SSebastian Reichel ret = bq25890_field_write(bq, F_OTG_CFG, 0); 7968c0984e5SSebastian Reichel if (ret < 0) 7978c0984e5SSebastian Reichel goto error; 7988c0984e5SSebastian Reichel 7998c0984e5SSebastian Reichel power_supply_changed(bq->charger); 8008c0984e5SSebastian Reichel break; 8018c0984e5SSebastian Reichel } 8028c0984e5SSebastian Reichel 8038c0984e5SSebastian Reichel return; 8048c0984e5SSebastian Reichel 8058c0984e5SSebastian Reichel error: 8068c0984e5SSebastian Reichel dev_err(bq->dev, "Error switching to boost/charger mode.\n"); 8078c0984e5SSebastian Reichel } 8088c0984e5SSebastian Reichel 8098c0984e5SSebastian Reichel static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, 8108c0984e5SSebastian Reichel void *priv) 8118c0984e5SSebastian Reichel { 8128c0984e5SSebastian Reichel struct bq25890_device *bq = 8138c0984e5SSebastian Reichel container_of(nb, struct bq25890_device, usb_nb); 8148c0984e5SSebastian Reichel 8158c0984e5SSebastian Reichel bq->usb_event = val; 8168c0984e5SSebastian Reichel queue_work(system_power_efficient_wq, &bq->usb_work); 8178c0984e5SSebastian Reichel 8188c0984e5SSebastian Reichel return NOTIFY_OK; 8198c0984e5SSebastian Reichel } 8208c0984e5SSebastian Reichel 821d20267c9SYauhen Kharuzhy static int bq25890_get_chip_version(struct bq25890_device *bq) 822d20267c9SYauhen Kharuzhy { 823d20267c9SYauhen Kharuzhy int id, rev; 824d20267c9SYauhen Kharuzhy 825d20267c9SYauhen Kharuzhy id = bq25890_field_read(bq, F_PN); 826d20267c9SYauhen Kharuzhy if (id < 0) { 827172d0cceSMartin Kepplinger dev_err(bq->dev, "Cannot read chip ID: %d\n", id); 828d20267c9SYauhen Kharuzhy return id; 829d20267c9SYauhen Kharuzhy } 830d20267c9SYauhen Kharuzhy 831d20267c9SYauhen Kharuzhy rev = bq25890_field_read(bq, F_DEV_REV); 832d20267c9SYauhen Kharuzhy if (rev < 0) { 833172d0cceSMartin Kepplinger dev_err(bq->dev, "Cannot read chip revision: %d\n", rev); 834cb619e80SColin Ian King return rev; 835d20267c9SYauhen Kharuzhy } 836d20267c9SYauhen Kharuzhy 837d20267c9SYauhen Kharuzhy switch (id) { 838d20267c9SYauhen Kharuzhy case BQ25890_ID: 839d20267c9SYauhen Kharuzhy bq->chip_version = BQ25890; 840d20267c9SYauhen Kharuzhy break; 841d20267c9SYauhen Kharuzhy 842d20267c9SYauhen Kharuzhy /* BQ25892 and BQ25896 share same ID 0 */ 843d20267c9SYauhen Kharuzhy case BQ25896_ID: 844d20267c9SYauhen Kharuzhy switch (rev) { 845d20267c9SYauhen Kharuzhy case 2: 846d20267c9SYauhen Kharuzhy bq->chip_version = BQ25896; 847d20267c9SYauhen Kharuzhy break; 848d20267c9SYauhen Kharuzhy case 1: 849d20267c9SYauhen Kharuzhy bq->chip_version = BQ25892; 850d20267c9SYauhen Kharuzhy break; 851d20267c9SYauhen Kharuzhy default: 852d20267c9SYauhen Kharuzhy dev_err(bq->dev, 853d20267c9SYauhen Kharuzhy "Unknown device revision %d, assume BQ25892\n", 854d20267c9SYauhen Kharuzhy rev); 855d20267c9SYauhen Kharuzhy bq->chip_version = BQ25892; 856d20267c9SYauhen Kharuzhy } 857d20267c9SYauhen Kharuzhy break; 858d20267c9SYauhen Kharuzhy 859d20267c9SYauhen Kharuzhy case BQ25895_ID: 860d20267c9SYauhen Kharuzhy bq->chip_version = BQ25895; 861d20267c9SYauhen Kharuzhy break; 862d20267c9SYauhen Kharuzhy 863d20267c9SYauhen Kharuzhy default: 864d20267c9SYauhen Kharuzhy dev_err(bq->dev, "Unknown chip ID %d\n", id); 865d20267c9SYauhen Kharuzhy return -ENODEV; 866d20267c9SYauhen Kharuzhy } 867d20267c9SYauhen Kharuzhy 868d20267c9SYauhen Kharuzhy return 0; 869d20267c9SYauhen Kharuzhy } 870d20267c9SYauhen Kharuzhy 8718c0984e5SSebastian Reichel static int bq25890_irq_probe(struct bq25890_device *bq) 8728c0984e5SSebastian Reichel { 8738c0984e5SSebastian Reichel struct gpio_desc *irq; 8748c0984e5SSebastian Reichel 87586775879SAndy Shevchenko irq = devm_gpiod_get(bq->dev, BQ25890_IRQ_PIN, GPIOD_IN); 876172d0cceSMartin Kepplinger if (IS_ERR(irq)) 877172d0cceSMartin Kepplinger return dev_err_probe(bq->dev, PTR_ERR(irq), 878172d0cceSMartin Kepplinger "Could not probe irq pin.\n"); 8798c0984e5SSebastian Reichel 8808c0984e5SSebastian Reichel return gpiod_to_irq(irq); 8818c0984e5SSebastian Reichel } 8828c0984e5SSebastian Reichel 8838c0984e5SSebastian Reichel static int bq25890_fw_read_u32_props(struct bq25890_device *bq) 8848c0984e5SSebastian Reichel { 8858c0984e5SSebastian Reichel int ret; 8868c0984e5SSebastian Reichel u32 property; 8878c0984e5SSebastian Reichel int i; 8888c0984e5SSebastian Reichel struct bq25890_init_data *init = &bq->init_data; 8898c0984e5SSebastian Reichel struct { 8908c0984e5SSebastian Reichel char *name; 8918c0984e5SSebastian Reichel bool optional; 8928c0984e5SSebastian Reichel enum bq25890_table_ids tbl_id; 8938c0984e5SSebastian Reichel u8 *conv_data; /* holds converted value from given property */ 8948c0984e5SSebastian Reichel } props[] = { 8958c0984e5SSebastian Reichel /* required properties */ 8968c0984e5SSebastian Reichel {"ti,charge-current", false, TBL_ICHG, &init->ichg}, 8978c0984e5SSebastian Reichel {"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg}, 8988c0984e5SSebastian Reichel {"ti,termination-current", false, TBL_ITERM, &init->iterm}, 8998c0984e5SSebastian Reichel {"ti,precharge-current", false, TBL_ITERM, &init->iprechg}, 9008c0984e5SSebastian Reichel {"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin}, 9018c0984e5SSebastian Reichel {"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv}, 9028c0984e5SSebastian Reichel {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti}, 9038c0984e5SSebastian Reichel 9048c0984e5SSebastian Reichel /* optional properties */ 90572408329SMichał Mirosław {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg}, 90672408329SMichał Mirosław {"ti,ibatcomp-micro-ohms", true, TBL_RBATCOMP, &init->rbatcomp}, 90772408329SMichał Mirosław {"ti,ibatcomp-clamp-microvolt", true, TBL_VBATCOMP, &init->vclamp}, 9088c0984e5SSebastian Reichel }; 9098c0984e5SSebastian Reichel 9108c0984e5SSebastian Reichel /* initialize data for optional properties */ 9118c0984e5SSebastian Reichel init->treg = 3; /* 120 degrees Celsius */ 91272408329SMichał Mirosław init->rbatcomp = init->vclamp = 0; /* IBAT compensation disabled */ 9138c0984e5SSebastian Reichel 9148c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(props); i++) { 9158c0984e5SSebastian Reichel ret = device_property_read_u32(bq->dev, props[i].name, 9168c0984e5SSebastian Reichel &property); 9178c0984e5SSebastian Reichel if (ret < 0) { 9188c0984e5SSebastian Reichel if (props[i].optional) 9198c0984e5SSebastian Reichel continue; 9208c0984e5SSebastian Reichel 9219d9ae341SAngus Ainslie (Purism) dev_err(bq->dev, "Unable to read property %d %s\n", ret, 9229d9ae341SAngus Ainslie (Purism) props[i].name); 9239d9ae341SAngus Ainslie (Purism) 9248c0984e5SSebastian Reichel return ret; 9258c0984e5SSebastian Reichel } 9268c0984e5SSebastian Reichel 9278c0984e5SSebastian Reichel *props[i].conv_data = bq25890_find_idx(property, 9288c0984e5SSebastian Reichel props[i].tbl_id); 9298c0984e5SSebastian Reichel } 9308c0984e5SSebastian Reichel 9318c0984e5SSebastian Reichel return 0; 9328c0984e5SSebastian Reichel } 9338c0984e5SSebastian Reichel 9348c0984e5SSebastian Reichel static int bq25890_fw_probe(struct bq25890_device *bq) 9358c0984e5SSebastian Reichel { 9368c0984e5SSebastian Reichel int ret; 9378c0984e5SSebastian Reichel struct bq25890_init_data *init = &bq->init_data; 9388c0984e5SSebastian Reichel 9398c0984e5SSebastian Reichel ret = bq25890_fw_read_u32_props(bq); 9408c0984e5SSebastian Reichel if (ret < 0) 9418c0984e5SSebastian Reichel return ret; 9428c0984e5SSebastian Reichel 9438c0984e5SSebastian Reichel init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin"); 9448c0984e5SSebastian Reichel init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq"); 9458c0984e5SSebastian Reichel 9468c0984e5SSebastian Reichel return 0; 9478c0984e5SSebastian Reichel } 9488c0984e5SSebastian Reichel 9498c0984e5SSebastian Reichel static int bq25890_probe(struct i2c_client *client, 9508c0984e5SSebastian Reichel const struct i2c_device_id *id) 9518c0984e5SSebastian Reichel { 9528c0984e5SSebastian Reichel struct device *dev = &client->dev; 9538c0984e5SSebastian Reichel struct bq25890_device *bq; 9548c0984e5SSebastian Reichel int ret; 9558c0984e5SSebastian Reichel int i; 9568c0984e5SSebastian Reichel 9578c0984e5SSebastian Reichel bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); 9588c0984e5SSebastian Reichel if (!bq) 9598c0984e5SSebastian Reichel return -ENOMEM; 9608c0984e5SSebastian Reichel 9618c0984e5SSebastian Reichel bq->client = client; 9628c0984e5SSebastian Reichel bq->dev = dev; 9638c0984e5SSebastian Reichel 9648c0984e5SSebastian Reichel mutex_init(&bq->lock); 9658c0984e5SSebastian Reichel 9668c0984e5SSebastian Reichel bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config); 967172d0cceSMartin Kepplinger if (IS_ERR(bq->rmap)) 968172d0cceSMartin Kepplinger return dev_err_probe(dev, PTR_ERR(bq->rmap), 969172d0cceSMartin Kepplinger "failed to allocate register map\n"); 9708c0984e5SSebastian Reichel 9718c0984e5SSebastian Reichel for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) { 9728c0984e5SSebastian Reichel const struct reg_field *reg_fields = bq25890_reg_fields; 9738c0984e5SSebastian Reichel 9748c0984e5SSebastian Reichel bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap, 9758c0984e5SSebastian Reichel reg_fields[i]); 976172d0cceSMartin Kepplinger if (IS_ERR(bq->rmap_fields[i])) 977172d0cceSMartin Kepplinger return dev_err_probe(dev, PTR_ERR(bq->rmap_fields[i]), 978172d0cceSMartin Kepplinger "cannot allocate regmap field\n"); 9798c0984e5SSebastian Reichel } 9808c0984e5SSebastian Reichel 9818c0984e5SSebastian Reichel i2c_set_clientdata(client, bq); 9828c0984e5SSebastian Reichel 983d20267c9SYauhen Kharuzhy ret = bq25890_get_chip_version(bq); 984d20267c9SYauhen Kharuzhy if (ret) { 985172d0cceSMartin Kepplinger dev_err(dev, "Cannot read chip ID or unknown chip: %d\n", ret); 986d20267c9SYauhen Kharuzhy return ret; 9878c0984e5SSebastian Reichel } 9888c0984e5SSebastian Reichel 9898c0984e5SSebastian Reichel if (!dev->platform_data) { 9908c0984e5SSebastian Reichel ret = bq25890_fw_probe(bq); 9918c0984e5SSebastian Reichel if (ret < 0) { 992172d0cceSMartin Kepplinger dev_err(dev, "Cannot read device properties: %d\n", 993172d0cceSMartin Kepplinger ret); 9948c0984e5SSebastian Reichel return ret; 9958c0984e5SSebastian Reichel } 9968c0984e5SSebastian Reichel } else { 9978c0984e5SSebastian Reichel return -ENODEV; 9988c0984e5SSebastian Reichel } 9998c0984e5SSebastian Reichel 10008c0984e5SSebastian Reichel ret = bq25890_hw_init(bq); 10018c0984e5SSebastian Reichel if (ret < 0) { 1002172d0cceSMartin Kepplinger dev_err(dev, "Cannot initialize the chip: %d\n", ret); 10038c0984e5SSebastian Reichel return ret; 10048c0984e5SSebastian Reichel } 10058c0984e5SSebastian Reichel 10068c0984e5SSebastian Reichel if (client->irq <= 0) 10078c0984e5SSebastian Reichel client->irq = bq25890_irq_probe(bq); 10088c0984e5SSebastian Reichel 10098c0984e5SSebastian Reichel if (client->irq < 0) { 10108c0984e5SSebastian Reichel dev_err(dev, "No irq resource found.\n"); 10118c0984e5SSebastian Reichel return client->irq; 10128c0984e5SSebastian Reichel } 10138c0984e5SSebastian Reichel 10148c0984e5SSebastian Reichel /* OTG reporting */ 10158c0984e5SSebastian Reichel bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); 10168c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) { 10178c0984e5SSebastian Reichel INIT_WORK(&bq->usb_work, bq25890_usb_work); 10188c0984e5SSebastian Reichel bq->usb_nb.notifier_call = bq25890_usb_notifier; 10198c0984e5SSebastian Reichel usb_register_notifier(bq->usb_phy, &bq->usb_nb); 10208c0984e5SSebastian Reichel } 10218c0984e5SSebastian Reichel 1022d01363daSHans de Goede ret = bq25890_power_supply_init(bq); 1023d01363daSHans de Goede if (ret < 0) { 1024d01363daSHans de Goede dev_err(dev, "Failed to register power supply\n"); 1025d01363daSHans de Goede goto err_unregister_usb_notifier; 1026d01363daSHans de Goede } 1027d01363daSHans de Goede 10288c0984e5SSebastian Reichel ret = devm_request_threaded_irq(dev, client->irq, NULL, 10298c0984e5SSebastian Reichel bq25890_irq_handler_thread, 10308c0984e5SSebastian Reichel IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 10318c0984e5SSebastian Reichel BQ25890_IRQ_PIN, bq); 10328c0984e5SSebastian Reichel if (ret) 1033d01363daSHans de Goede goto err_unregister_usb_notifier; 10348c0984e5SSebastian Reichel 10358c0984e5SSebastian Reichel return 0; 10368c0984e5SSebastian Reichel 1037d01363daSHans de Goede err_unregister_usb_notifier: 10388c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) 10398c0984e5SSebastian Reichel usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); 10408c0984e5SSebastian Reichel 10418c0984e5SSebastian Reichel return ret; 10428c0984e5SSebastian Reichel } 10438c0984e5SSebastian Reichel 10448c0984e5SSebastian Reichel static int bq25890_remove(struct i2c_client *client) 10458c0984e5SSebastian Reichel { 10468c0984e5SSebastian Reichel struct bq25890_device *bq = i2c_get_clientdata(client); 10478c0984e5SSebastian Reichel 10488c0984e5SSebastian Reichel if (!IS_ERR_OR_NULL(bq->usb_phy)) 10498c0984e5SSebastian Reichel usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); 10508c0984e5SSebastian Reichel 10518c0984e5SSebastian Reichel /* reset all registers to default values */ 10528c0984e5SSebastian Reichel bq25890_chip_reset(bq); 10538c0984e5SSebastian Reichel 10548c0984e5SSebastian Reichel return 0; 10558c0984e5SSebastian Reichel } 10568c0984e5SSebastian Reichel 10578c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP 10588c0984e5SSebastian Reichel static int bq25890_suspend(struct device *dev) 10598c0984e5SSebastian Reichel { 10608c0984e5SSebastian Reichel struct bq25890_device *bq = dev_get_drvdata(dev); 10618c0984e5SSebastian Reichel 10628c0984e5SSebastian Reichel /* 10638c0984e5SSebastian Reichel * If charger is removed, while in suspend, make sure ADC is diabled 10648c0984e5SSebastian Reichel * since it consumes slightly more power. 10658c0984e5SSebastian Reichel */ 106621d90edaSMichał Mirosław return bq25890_field_write(bq, F_CONV_RATE, 0); 10678c0984e5SSebastian Reichel } 10688c0984e5SSebastian Reichel 10698c0984e5SSebastian Reichel static int bq25890_resume(struct device *dev) 10708c0984e5SSebastian Reichel { 10718c0984e5SSebastian Reichel int ret; 10728c0984e5SSebastian Reichel struct bq25890_device *bq = dev_get_drvdata(dev); 10738c0984e5SSebastian Reichel 107472d9cd9cSMichał Mirosław mutex_lock(&bq->lock); 107572d9cd9cSMichał Mirosław 107672d9cd9cSMichał Mirosław ret = bq25890_get_chip_state(bq, &bq->state); 10778c0984e5SSebastian Reichel if (ret < 0) 1078cf5701bfSDan Carpenter goto unlock; 10798c0984e5SSebastian Reichel 10808c0984e5SSebastian Reichel /* Re-enable ADC only if charger is plugged in. */ 108172d9cd9cSMichał Mirosław if (bq->state.online) { 108221d90edaSMichał Mirosław ret = bq25890_field_write(bq, F_CONV_RATE, 1); 10838c0984e5SSebastian Reichel if (ret < 0) 1084cf5701bfSDan Carpenter goto unlock; 10858c0984e5SSebastian Reichel } 10868c0984e5SSebastian Reichel 10878c0984e5SSebastian Reichel /* signal userspace, maybe state changed while suspended */ 10888c0984e5SSebastian Reichel power_supply_changed(bq->charger); 10898c0984e5SSebastian Reichel 1090cf5701bfSDan Carpenter unlock: 109172d9cd9cSMichał Mirosław mutex_unlock(&bq->lock); 109272d9cd9cSMichał Mirosław 1093cf5701bfSDan Carpenter return ret; 10948c0984e5SSebastian Reichel } 10958c0984e5SSebastian Reichel #endif 10968c0984e5SSebastian Reichel 10978c0984e5SSebastian Reichel static const struct dev_pm_ops bq25890_pm = { 10988c0984e5SSebastian Reichel SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume) 10998c0984e5SSebastian Reichel }; 11008c0984e5SSebastian Reichel 11018c0984e5SSebastian Reichel static const struct i2c_device_id bq25890_i2c_ids[] = { 11028c0984e5SSebastian Reichel { "bq25890", 0 }, 110346aa27e7SYauhen Kharuzhy { "bq25892", 0 }, 110446aa27e7SYauhen Kharuzhy { "bq25895", 0 }, 110546aa27e7SYauhen Kharuzhy { "bq25896", 0 }, 11068c0984e5SSebastian Reichel {}, 11078c0984e5SSebastian Reichel }; 11088c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids); 11098c0984e5SSebastian Reichel 11108c0984e5SSebastian Reichel static const struct of_device_id bq25890_of_match[] = { 11118c0984e5SSebastian Reichel { .compatible = "ti,bq25890", }, 111246aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25892", }, 111346aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25895", }, 111446aa27e7SYauhen Kharuzhy { .compatible = "ti,bq25896", }, 11158c0984e5SSebastian Reichel { }, 11168c0984e5SSebastian Reichel }; 11178c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, bq25890_of_match); 11188c0984e5SSebastian Reichel 111902067dc9SKrzysztof Kozlowski #ifdef CONFIG_ACPI 11208c0984e5SSebastian Reichel static const struct acpi_device_id bq25890_acpi_match[] = { 11218c0984e5SSebastian Reichel {"BQ258900", 0}, 11228c0984e5SSebastian Reichel {}, 11238c0984e5SSebastian Reichel }; 11248c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match); 112502067dc9SKrzysztof Kozlowski #endif 11268c0984e5SSebastian Reichel 11278c0984e5SSebastian Reichel static struct i2c_driver bq25890_driver = { 11288c0984e5SSebastian Reichel .driver = { 11298c0984e5SSebastian Reichel .name = "bq25890-charger", 11308c0984e5SSebastian Reichel .of_match_table = of_match_ptr(bq25890_of_match), 11318c0984e5SSebastian Reichel .acpi_match_table = ACPI_PTR(bq25890_acpi_match), 11328c0984e5SSebastian Reichel .pm = &bq25890_pm, 11338c0984e5SSebastian Reichel }, 11348c0984e5SSebastian Reichel .probe = bq25890_probe, 11358c0984e5SSebastian Reichel .remove = bq25890_remove, 11368c0984e5SSebastian Reichel .id_table = bq25890_i2c_ids, 11378c0984e5SSebastian Reichel }; 11388c0984e5SSebastian Reichel module_i2c_driver(bq25890_driver); 11398c0984e5SSebastian Reichel 11408c0984e5SSebastian Reichel MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); 11418c0984e5SSebastian Reichel MODULE_DESCRIPTION("bq25890 charger driver"); 11428c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 1143