xref: /openbmc/linux/drivers/power/supply/bq25890_charger.c (revision 766873c139a9185f2ee33c434df85140d67356d1)
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 {
43*766873c1SYauhen 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;
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),
156*766873c1SYauhen Kharuzhy 	[F_IINLIM]		= 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,
259*766873c1SYauhen Kharuzhy 	TBL_IINLIM,
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,
2699652c024SAngus 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 
2849652c024SAngus Ainslie /* NTC 10K temperature lookup table in tenths of a degree */
2859652c024SAngus Ainslie static const u32 bq25890_tspct_tbl[] = {
2869652c024SAngus Ainslie 	850, 840, 830, 820, 810, 800, 790, 780,
2879652c024SAngus Ainslie 	770, 760, 750, 740, 730, 720, 710, 700,
2889652c024SAngus Ainslie 	690, 685, 680, 675, 670, 660, 650, 645,
2899652c024SAngus Ainslie 	640, 630, 620, 615, 610, 600, 590, 585,
2909652c024SAngus Ainslie 	580, 570, 565, 560, 550, 540, 535, 530,
2919652c024SAngus Ainslie 	520, 515, 510, 500, 495, 490, 480, 475,
2929652c024SAngus Ainslie 	470, 460, 455, 450, 440, 435, 430, 425,
2939652c024SAngus Ainslie 	420, 410, 405, 400, 390, 385, 380, 370,
2949652c024SAngus Ainslie 	365, 360, 355, 350, 340, 335, 330, 320,
2959652c024SAngus Ainslie 	310, 305, 300, 290, 285, 280, 275, 270,
2969652c024SAngus Ainslie 	260, 250, 245, 240, 230, 225, 220, 210,
2979652c024SAngus Ainslie 	205, 200, 190, 180, 175, 170, 160, 150,
2989652c024SAngus Ainslie 	145, 140, 130, 120, 115, 110, 100, 90,
2999652c024SAngus Ainslie 	80, 70, 60, 50, 40, 30, 20, 10,
3009652c024SAngus Ainslie 	0, -10, -20, -30, -40, -60, -70, -80,
3019652c024SAngus Ainslie 	-90, -10, -120, -140, -150, -170, -190, -210,
3029652c024SAngus Ainslie };
3039652c024SAngus Ainslie 
3049652c024SAngus Ainslie #define BQ25890_TSPCT_TBL_SIZE		ARRAY_SIZE(bq25890_tspct_tbl)
3059652c024SAngus 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 */
325*766873c1SYauhen Kharuzhy 	[TBL_IINLIM] =  { .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} },
3349652c024SAngus Ainslie 	[TBL_BOOSTI] =	{ .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} },
3359652c024SAngus 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:
4159652c024SAngus 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:
531*766873c1SYauhen Kharuzhy 		ret = bq25890_field_read(bq, F_IINLIM);
532478efc79SMichał Mirosław 		if (ret < 0)
533478efc79SMichał Mirosław 			return ret;
534478efc79SMichał Mirosław 
535*766873c1SYauhen Kharuzhy 		val->intval = bq25890_find_val(ret, TBL_IINLIM);
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 
5569652c024SAngus Ainslie 	case POWER_SUPPLY_PROP_TEMP:
5579652c024SAngus Ainslie 		ret = bq25890_field_read(bq, F_TSPCT);
5589652c024SAngus Ainslie 		if (ret < 0)
5599652c024SAngus Ainslie 			return ret;
5609652c024SAngus Ainslie 
5619652c024SAngus Ainslie 		/* convert TS percentage into rough temperature */
5629652c024SAngus Ainslie 		val->intval = bq25890_find_val(ret, TBL_TSPCT);
5639652c024SAngus Ainslie 		break;
5649652c024SAngus 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 */
61880211be1SYauhen Kharuzhy 		ret = bq25890_field_write(bq, F_CONV_RATE, 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 */
62380211be1SYauhen Kharuzhy 		ret = bq25890_field_write(bq, F_CONV_RATE, 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,
7509652c024SAngus 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