xref: /openbmc/linux/drivers/power/supply/bq25890_charger.c (revision 7e3b8e357f230f96473a84581cfa18899f860338)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * TI BQ25890 charger driver
48c0984e5SSebastian Reichel  *
58c0984e5SSebastian Reichel  * Copyright (C) 2015 Intel Corporation
68c0984e5SSebastian Reichel  */
78c0984e5SSebastian Reichel 
88c0984e5SSebastian Reichel #include <linux/module.h>
98c0984e5SSebastian Reichel #include <linux/i2c.h>
108c0984e5SSebastian Reichel #include <linux/power_supply.h>
118c0984e5SSebastian Reichel #include <linux/regmap.h>
128c0984e5SSebastian Reichel #include <linux/types.h>
138c0984e5SSebastian Reichel #include <linux/gpio/consumer.h>
148c0984e5SSebastian Reichel #include <linux/interrupt.h>
158c0984e5SSebastian Reichel #include <linux/delay.h>
168c0984e5SSebastian Reichel #include <linux/usb/phy.h>
178c0984e5SSebastian Reichel 
188c0984e5SSebastian Reichel #include <linux/acpi.h>
198c0984e5SSebastian Reichel #include <linux/of.h>
208c0984e5SSebastian Reichel 
218c0984e5SSebastian Reichel #define BQ25890_MANUFACTURER		"Texas Instruments"
228c0984e5SSebastian Reichel #define BQ25890_IRQ_PIN			"bq25890_irq"
238c0984e5SSebastian Reichel 
248c0984e5SSebastian Reichel #define BQ25890_ID			3
255c35ba9bSAngus Ainslie (Purism) #define BQ25895_ID			7
262e1a2ddeSAngus Ainslie (Purism) #define BQ25896_ID			0
278c0984e5SSebastian Reichel 
28d20267c9SYauhen Kharuzhy enum bq25890_chip_version {
29d20267c9SYauhen Kharuzhy 	BQ25890,
30d20267c9SYauhen Kharuzhy 	BQ25892,
31d20267c9SYauhen Kharuzhy 	BQ25895,
32d20267c9SYauhen Kharuzhy 	BQ25896,
33d20267c9SYauhen Kharuzhy };
34d20267c9SYauhen Kharuzhy 
355956fca7SMichał Mirosław static const char *const bq25890_chip_name[] = {
365956fca7SMichał Mirosław 	"BQ25890",
375956fca7SMichał Mirosław 	"BQ25892",
385956fca7SMichał Mirosław 	"BQ25895",
395956fca7SMichał Mirosław 	"BQ25896",
405956fca7SMichał Mirosław };
415956fca7SMichał Mirosław 
428c0984e5SSebastian Reichel enum bq25890_fields {
43766873c1SYauhen Kharuzhy 	F_EN_HIZ, F_EN_ILIM, F_IINLIM,				     /* Reg00 */
448c0984e5SSebastian Reichel 	F_BHOT, F_BCOLD, F_VINDPM_OFS,				     /* Reg01 */
458c0984e5SSebastian Reichel 	F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN,
468c0984e5SSebastian Reichel 	F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN,	     /* Reg02 */
47d20267c9SYauhen Kharuzhy 	F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN,
48d20267c9SYauhen Kharuzhy 	F_MIN_VBAT_SEL,						     /* Reg03 */
498c0984e5SSebastian Reichel 	F_PUMPX_EN, F_ICHG,					     /* Reg04 */
508c0984e5SSebastian Reichel 	F_IPRECHG, F_ITERM,					     /* Reg05 */
518c0984e5SSebastian Reichel 	F_VREG, F_BATLOWV, F_VRECHG,				     /* Reg06 */
528c0984e5SSebastian Reichel 	F_TERM_EN, F_STAT_DIS, F_WD, F_TMR_EN, F_CHG_TMR,
538c0984e5SSebastian Reichel 	F_JEITA_ISET,						     /* Reg07 */
548c0984e5SSebastian Reichel 	F_BATCMP, F_VCLAMP, F_TREG,				     /* Reg08 */
558c0984e5SSebastian Reichel 	F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET,
568c0984e5SSebastian Reichel 	F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN,	     /* Reg09 */
57d20267c9SYauhen Kharuzhy 	F_BOOSTV, F_PFM_OTG_DIS, F_BOOSTI,			     /* Reg0A */
58d20267c9SYauhen Kharuzhy 	F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_0B_RSVD,
59d20267c9SYauhen Kharuzhy 	F_VSYS_STAT,						     /* Reg0B */
608c0984e5SSebastian Reichel 	F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT,
618c0984e5SSebastian Reichel 	F_NTC_FAULT,						     /* Reg0C */
628c0984e5SSebastian Reichel 	F_FORCE_VINDPM, F_VINDPM,				     /* Reg0D */
638c0984e5SSebastian Reichel 	F_THERM_STAT, F_BATV,					     /* Reg0E */
648c0984e5SSebastian Reichel 	F_SYSV,							     /* Reg0F */
658c0984e5SSebastian Reichel 	F_TSPCT,						     /* Reg10 */
668c0984e5SSebastian Reichel 	F_VBUS_GD, F_VBUSV,					     /* Reg11 */
678c0984e5SSebastian Reichel 	F_ICHGR,						     /* Reg12 */
688c0984e5SSebastian Reichel 	F_VDPM_STAT, F_IDPM_STAT, F_IDPM_LIM,			     /* Reg13 */
698c0984e5SSebastian Reichel 	F_REG_RST, F_ICO_OPTIMIZED, F_PN, F_TS_PROFILE, F_DEV_REV,   /* Reg14 */
708c0984e5SSebastian Reichel 
718c0984e5SSebastian Reichel 	F_MAX_FIELDS
728c0984e5SSebastian Reichel };
738c0984e5SSebastian Reichel 
748c0984e5SSebastian Reichel /* initial field values, converted to register values */
758c0984e5SSebastian Reichel struct bq25890_init_data {
768c0984e5SSebastian Reichel 	u8 ichg;	/* charge current		*/
778c0984e5SSebastian Reichel 	u8 vreg;	/* regulation voltage		*/
788c0984e5SSebastian Reichel 	u8 iterm;	/* termination current		*/
798c0984e5SSebastian Reichel 	u8 iprechg;	/* precharge current		*/
808c0984e5SSebastian Reichel 	u8 sysvmin;	/* minimum system voltage limit */
818c0984e5SSebastian Reichel 	u8 boostv;	/* boost regulation voltage	*/
828c0984e5SSebastian Reichel 	u8 boosti;	/* boost current limit		*/
838c0984e5SSebastian Reichel 	u8 boostf;	/* boost frequency		*/
848c0984e5SSebastian Reichel 	u8 ilim_en;	/* enable ILIM pin		*/
858c0984e5SSebastian Reichel 	u8 treg;	/* thermal regulation threshold */
8672408329SMichał Mirosław 	u8 rbatcomp;	/* IBAT sense resistor value    */
8772408329SMichał Mirosław 	u8 vclamp;	/* IBAT compensation voltage limit */
888c0984e5SSebastian Reichel };
898c0984e5SSebastian Reichel 
908c0984e5SSebastian Reichel struct bq25890_state {
918c0984e5SSebastian Reichel 	u8 online;
928c0984e5SSebastian Reichel 	u8 chrg_status;
938c0984e5SSebastian Reichel 	u8 chrg_fault;
948c0984e5SSebastian Reichel 	u8 vsys_status;
958c0984e5SSebastian Reichel 	u8 boost_fault;
968c0984e5SSebastian Reichel 	u8 bat_fault;
97c562a43aSYauhen Kharuzhy 	u8 ntc_fault;
988c0984e5SSebastian Reichel };
998c0984e5SSebastian Reichel 
1008c0984e5SSebastian Reichel struct bq25890_device {
1018c0984e5SSebastian Reichel 	struct i2c_client *client;
1028c0984e5SSebastian Reichel 	struct device *dev;
1038c0984e5SSebastian Reichel 	struct power_supply *charger;
1048c0984e5SSebastian Reichel 
1058c0984e5SSebastian Reichel 	struct usb_phy *usb_phy;
1068c0984e5SSebastian Reichel 	struct notifier_block usb_nb;
1078c0984e5SSebastian Reichel 	struct work_struct usb_work;
1088c0984e5SSebastian Reichel 	unsigned long usb_event;
1098c0984e5SSebastian Reichel 
1108c0984e5SSebastian Reichel 	struct regmap *rmap;
1118c0984e5SSebastian Reichel 	struct regmap_field *rmap_fields[F_MAX_FIELDS];
1128c0984e5SSebastian Reichel 
113*7e3b8e35SHans de Goede 	bool skip_reset;
114d20267c9SYauhen Kharuzhy 	enum bq25890_chip_version chip_version;
1158c0984e5SSebastian Reichel 	struct bq25890_init_data init_data;
1168c0984e5SSebastian Reichel 	struct bq25890_state state;
1178c0984e5SSebastian Reichel 
1188c0984e5SSebastian Reichel 	struct mutex lock; /* protect state data */
1198c0984e5SSebastian Reichel };
1208c0984e5SSebastian Reichel 
1218c0984e5SSebastian Reichel static const struct regmap_range bq25890_readonly_reg_ranges[] = {
1228c0984e5SSebastian Reichel 	regmap_reg_range(0x0b, 0x0c),
1238c0984e5SSebastian Reichel 	regmap_reg_range(0x0e, 0x13),
1248c0984e5SSebastian Reichel };
1258c0984e5SSebastian Reichel 
1268c0984e5SSebastian Reichel static const struct regmap_access_table bq25890_writeable_regs = {
1278c0984e5SSebastian Reichel 	.no_ranges = bq25890_readonly_reg_ranges,
1288c0984e5SSebastian Reichel 	.n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges),
1298c0984e5SSebastian Reichel };
1308c0984e5SSebastian Reichel 
1318c0984e5SSebastian Reichel static const struct regmap_range bq25890_volatile_reg_ranges[] = {
1328c0984e5SSebastian Reichel 	regmap_reg_range(0x00, 0x00),
13321d90edaSMichał Mirosław 	regmap_reg_range(0x02, 0x02),
1348c0984e5SSebastian Reichel 	regmap_reg_range(0x09, 0x09),
135d20267c9SYauhen Kharuzhy 	regmap_reg_range(0x0b, 0x14),
1368c0984e5SSebastian Reichel };
1378c0984e5SSebastian Reichel 
1388c0984e5SSebastian Reichel static const struct regmap_access_table bq25890_volatile_regs = {
1398c0984e5SSebastian Reichel 	.yes_ranges = bq25890_volatile_reg_ranges,
1408c0984e5SSebastian Reichel 	.n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges),
1418c0984e5SSebastian Reichel };
1428c0984e5SSebastian Reichel 
1438c0984e5SSebastian Reichel static const struct regmap_config bq25890_regmap_config = {
1448c0984e5SSebastian Reichel 	.reg_bits = 8,
1458c0984e5SSebastian Reichel 	.val_bits = 8,
1468c0984e5SSebastian Reichel 
1478c0984e5SSebastian Reichel 	.max_register = 0x14,
1488c0984e5SSebastian Reichel 	.cache_type = REGCACHE_RBTREE,
1498c0984e5SSebastian Reichel 
1508c0984e5SSebastian Reichel 	.wr_table = &bq25890_writeable_regs,
1518c0984e5SSebastian Reichel 	.volatile_table = &bq25890_volatile_regs,
1528c0984e5SSebastian Reichel };
1538c0984e5SSebastian Reichel 
1548c0984e5SSebastian Reichel static const struct reg_field bq25890_reg_fields[] = {
1558c0984e5SSebastian Reichel 	/* REG00 */
1568c0984e5SSebastian Reichel 	[F_EN_HIZ]		= REG_FIELD(0x00, 7, 7),
1578c0984e5SSebastian Reichel 	[F_EN_ILIM]		= REG_FIELD(0x00, 6, 6),
158766873c1SYauhen Kharuzhy 	[F_IINLIM]		= REG_FIELD(0x00, 0, 5),
1598c0984e5SSebastian Reichel 	/* REG01 */
1608c0984e5SSebastian Reichel 	[F_BHOT]		= REG_FIELD(0x01, 6, 7),
1618c0984e5SSebastian Reichel 	[F_BCOLD]		= REG_FIELD(0x01, 5, 5),
1628c0984e5SSebastian Reichel 	[F_VINDPM_OFS]		= REG_FIELD(0x01, 0, 4),
1638c0984e5SSebastian Reichel 	/* REG02 */
1648c0984e5SSebastian Reichel 	[F_CONV_START]		= REG_FIELD(0x02, 7, 7),
1658c0984e5SSebastian Reichel 	[F_CONV_RATE]		= REG_FIELD(0x02, 6, 6),
1668c0984e5SSebastian Reichel 	[F_BOOSTF]		= REG_FIELD(0x02, 5, 5),
1678c0984e5SSebastian Reichel 	[F_ICO_EN]		= REG_FIELD(0x02, 4, 4),
1682e1a2ddeSAngus Ainslie (Purism) 	[F_HVDCP_EN]		= REG_FIELD(0x02, 3, 3),  // reserved on BQ25896
1692e1a2ddeSAngus Ainslie (Purism) 	[F_MAXC_EN]		= REG_FIELD(0x02, 2, 2),  // reserved on BQ25896
1708c0984e5SSebastian Reichel 	[F_FORCE_DPM]		= REG_FIELD(0x02, 1, 1),
1718c0984e5SSebastian Reichel 	[F_AUTO_DPDM_EN]	= REG_FIELD(0x02, 0, 0),
1728c0984e5SSebastian Reichel 	/* REG03 */
1738c0984e5SSebastian Reichel 	[F_BAT_LOAD_EN]		= REG_FIELD(0x03, 7, 7),
1748c0984e5SSebastian Reichel 	[F_WD_RST]		= REG_FIELD(0x03, 6, 6),
1758c0984e5SSebastian Reichel 	[F_OTG_CFG]		= REG_FIELD(0x03, 5, 5),
1768c0984e5SSebastian Reichel 	[F_CHG_CFG]		= REG_FIELD(0x03, 4, 4),
1778c0984e5SSebastian Reichel 	[F_SYSVMIN]		= REG_FIELD(0x03, 1, 3),
178d20267c9SYauhen Kharuzhy 	[F_MIN_VBAT_SEL]	= REG_FIELD(0x03, 0, 0), // BQ25896 only
1798c0984e5SSebastian Reichel 	/* REG04 */
1808c0984e5SSebastian Reichel 	[F_PUMPX_EN]		= REG_FIELD(0x04, 7, 7),
1818c0984e5SSebastian Reichel 	[F_ICHG]		= REG_FIELD(0x04, 0, 6),
1828c0984e5SSebastian Reichel 	/* REG05 */
1838c0984e5SSebastian Reichel 	[F_IPRECHG]		= REG_FIELD(0x05, 4, 7),
1848c0984e5SSebastian Reichel 	[F_ITERM]		= REG_FIELD(0x05, 0, 3),
1858c0984e5SSebastian Reichel 	/* REG06 */
1868c0984e5SSebastian Reichel 	[F_VREG]		= REG_FIELD(0x06, 2, 7),
1878c0984e5SSebastian Reichel 	[F_BATLOWV]		= REG_FIELD(0x06, 1, 1),
1888c0984e5SSebastian Reichel 	[F_VRECHG]		= REG_FIELD(0x06, 0, 0),
1898c0984e5SSebastian Reichel 	/* REG07 */
1908c0984e5SSebastian Reichel 	[F_TERM_EN]		= REG_FIELD(0x07, 7, 7),
1918c0984e5SSebastian Reichel 	[F_STAT_DIS]		= REG_FIELD(0x07, 6, 6),
1928c0984e5SSebastian Reichel 	[F_WD]			= REG_FIELD(0x07, 4, 5),
1938c0984e5SSebastian Reichel 	[F_TMR_EN]		= REG_FIELD(0x07, 3, 3),
1948c0984e5SSebastian Reichel 	[F_CHG_TMR]		= REG_FIELD(0x07, 1, 2),
1955c35ba9bSAngus Ainslie (Purism) 	[F_JEITA_ISET]		= REG_FIELD(0x07, 0, 0), // reserved on BQ25895
1968c0984e5SSebastian Reichel 	/* REG08 */
19795809139SMichał Mirosław 	[F_BATCMP]		= REG_FIELD(0x08, 5, 7),
1988c0984e5SSebastian Reichel 	[F_VCLAMP]		= REG_FIELD(0x08, 2, 4),
1998c0984e5SSebastian Reichel 	[F_TREG]		= REG_FIELD(0x08, 0, 1),
2008c0984e5SSebastian Reichel 	/* REG09 */
2018c0984e5SSebastian Reichel 	[F_FORCE_ICO]		= REG_FIELD(0x09, 7, 7),
2028c0984e5SSebastian Reichel 	[F_TMR2X_EN]		= REG_FIELD(0x09, 6, 6),
2038c0984e5SSebastian Reichel 	[F_BATFET_DIS]		= REG_FIELD(0x09, 5, 5),
2045c35ba9bSAngus Ainslie (Purism) 	[F_JEITA_VSET]		= REG_FIELD(0x09, 4, 4), // reserved on BQ25895
2058c0984e5SSebastian Reichel 	[F_BATFET_DLY]		= REG_FIELD(0x09, 3, 3),
2068c0984e5SSebastian Reichel 	[F_BATFET_RST_EN]	= REG_FIELD(0x09, 2, 2),
2078c0984e5SSebastian Reichel 	[F_PUMPX_UP]		= REG_FIELD(0x09, 1, 1),
2088c0984e5SSebastian Reichel 	[F_PUMPX_DN]		= REG_FIELD(0x09, 0, 0),
2098c0984e5SSebastian Reichel 	/* REG0A */
2108c0984e5SSebastian Reichel 	[F_BOOSTV]		= REG_FIELD(0x0A, 4, 7),
2115c35ba9bSAngus Ainslie (Purism) 	[F_BOOSTI]		= REG_FIELD(0x0A, 0, 2), // reserved on BQ25895
212d20267c9SYauhen Kharuzhy 	[F_PFM_OTG_DIS]		= REG_FIELD(0x0A, 3, 3), // BQ25896 only
2138c0984e5SSebastian Reichel 	/* REG0B */
2148c0984e5SSebastian Reichel 	[F_VBUS_STAT]		= REG_FIELD(0x0B, 5, 7),
2158c0984e5SSebastian Reichel 	[F_CHG_STAT]		= REG_FIELD(0x0B, 3, 4),
2168c0984e5SSebastian Reichel 	[F_PG_STAT]		= REG_FIELD(0x0B, 2, 2),
2172e1a2ddeSAngus Ainslie (Purism) 	[F_SDP_STAT]		= REG_FIELD(0x0B, 1, 1), // reserved on BQ25896
2188c0984e5SSebastian Reichel 	[F_VSYS_STAT]		= REG_FIELD(0x0B, 0, 0),
2198c0984e5SSebastian Reichel 	/* REG0C */
2208c0984e5SSebastian Reichel 	[F_WD_FAULT]		= REG_FIELD(0x0C, 7, 7),
2218c0984e5SSebastian Reichel 	[F_BOOST_FAULT]		= REG_FIELD(0x0C, 6, 6),
2228c0984e5SSebastian Reichel 	[F_CHG_FAULT]		= REG_FIELD(0x0C, 4, 5),
2238c0984e5SSebastian Reichel 	[F_BAT_FAULT]		= REG_FIELD(0x0C, 3, 3),
2248c0984e5SSebastian Reichel 	[F_NTC_FAULT]		= REG_FIELD(0x0C, 0, 2),
2258c0984e5SSebastian Reichel 	/* REG0D */
2268c0984e5SSebastian Reichel 	[F_FORCE_VINDPM]	= REG_FIELD(0x0D, 7, 7),
2278c0984e5SSebastian Reichel 	[F_VINDPM]		= REG_FIELD(0x0D, 0, 6),
2288c0984e5SSebastian Reichel 	/* REG0E */
2298c0984e5SSebastian Reichel 	[F_THERM_STAT]		= REG_FIELD(0x0E, 7, 7),
2308c0984e5SSebastian Reichel 	[F_BATV]		= REG_FIELD(0x0E, 0, 6),
2318c0984e5SSebastian Reichel 	/* REG0F */
2328c0984e5SSebastian Reichel 	[F_SYSV]		= REG_FIELD(0x0F, 0, 6),
2338c0984e5SSebastian Reichel 	/* REG10 */
2348c0984e5SSebastian Reichel 	[F_TSPCT]		= REG_FIELD(0x10, 0, 6),
2358c0984e5SSebastian Reichel 	/* REG11 */
2368c0984e5SSebastian Reichel 	[F_VBUS_GD]		= REG_FIELD(0x11, 7, 7),
2378c0984e5SSebastian Reichel 	[F_VBUSV]		= REG_FIELD(0x11, 0, 6),
2388c0984e5SSebastian Reichel 	/* REG12 */
2398c0984e5SSebastian Reichel 	[F_ICHGR]		= REG_FIELD(0x12, 0, 6),
2408c0984e5SSebastian Reichel 	/* REG13 */
2418c0984e5SSebastian Reichel 	[F_VDPM_STAT]		= REG_FIELD(0x13, 7, 7),
2428c0984e5SSebastian Reichel 	[F_IDPM_STAT]		= REG_FIELD(0x13, 6, 6),
2438c0984e5SSebastian Reichel 	[F_IDPM_LIM]		= REG_FIELD(0x13, 0, 5),
2448c0984e5SSebastian Reichel 	/* REG14 */
2458c0984e5SSebastian Reichel 	[F_REG_RST]		= REG_FIELD(0x14, 7, 7),
2468c0984e5SSebastian Reichel 	[F_ICO_OPTIMIZED]	= REG_FIELD(0x14, 6, 6),
2478c0984e5SSebastian Reichel 	[F_PN]			= REG_FIELD(0x14, 3, 5),
2488c0984e5SSebastian Reichel 	[F_TS_PROFILE]		= REG_FIELD(0x14, 2, 2),
2498c0984e5SSebastian Reichel 	[F_DEV_REV]		= REG_FIELD(0x14, 0, 1)
2508c0984e5SSebastian Reichel };
2518c0984e5SSebastian Reichel 
2528c0984e5SSebastian Reichel /*
2538c0984e5SSebastian Reichel  * Most of the val -> idx conversions can be computed, given the minimum,
2548c0984e5SSebastian Reichel  * maximum and the step between values. For the rest of conversions, we use
2558c0984e5SSebastian Reichel  * lookup tables.
2568c0984e5SSebastian Reichel  */
2578c0984e5SSebastian Reichel enum bq25890_table_ids {
2588c0984e5SSebastian Reichel 	/* range tables */
2598c0984e5SSebastian Reichel 	TBL_ICHG,
2608c0984e5SSebastian Reichel 	TBL_ITERM,
261766873c1SYauhen Kharuzhy 	TBL_IINLIM,
2628c0984e5SSebastian Reichel 	TBL_VREG,
2638c0984e5SSebastian Reichel 	TBL_BOOSTV,
2648c0984e5SSebastian Reichel 	TBL_SYSVMIN,
26572408329SMichał Mirosław 	TBL_VBATCOMP,
26672408329SMichał Mirosław 	TBL_RBATCOMP,
2678c0984e5SSebastian Reichel 
2688c0984e5SSebastian Reichel 	/* lookup tables */
2698c0984e5SSebastian Reichel 	TBL_TREG,
2708c0984e5SSebastian Reichel 	TBL_BOOSTI,
2719652c024SAngus Ainslie 	TBL_TSPCT,
2728c0984e5SSebastian Reichel };
2738c0984e5SSebastian Reichel 
2748c0984e5SSebastian Reichel /* Thermal Regulation Threshold lookup table, in degrees Celsius */
2758c0984e5SSebastian Reichel static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 };
2768c0984e5SSebastian Reichel 
2778c0984e5SSebastian Reichel #define BQ25890_TREG_TBL_SIZE		ARRAY_SIZE(bq25890_treg_tbl)
2788c0984e5SSebastian Reichel 
2798c0984e5SSebastian Reichel /* Boost mode current limit lookup table, in uA */
2808c0984e5SSebastian Reichel static const u32 bq25890_boosti_tbl[] = {
2818c0984e5SSebastian Reichel 	500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000
2828c0984e5SSebastian Reichel };
2838c0984e5SSebastian Reichel 
2848c0984e5SSebastian Reichel #define BQ25890_BOOSTI_TBL_SIZE		ARRAY_SIZE(bq25890_boosti_tbl)
2858c0984e5SSebastian Reichel 
2869652c024SAngus Ainslie /* NTC 10K temperature lookup table in tenths of a degree */
2879652c024SAngus Ainslie static const u32 bq25890_tspct_tbl[] = {
2889652c024SAngus Ainslie 	850, 840, 830, 820, 810, 800, 790, 780,
2899652c024SAngus Ainslie 	770, 760, 750, 740, 730, 720, 710, 700,
2909652c024SAngus Ainslie 	690, 685, 680, 675, 670, 660, 650, 645,
2919652c024SAngus Ainslie 	640, 630, 620, 615, 610, 600, 590, 585,
2929652c024SAngus Ainslie 	580, 570, 565, 560, 550, 540, 535, 530,
2939652c024SAngus Ainslie 	520, 515, 510, 500, 495, 490, 480, 475,
2949652c024SAngus Ainslie 	470, 460, 455, 450, 440, 435, 430, 425,
2959652c024SAngus Ainslie 	420, 410, 405, 400, 390, 385, 380, 370,
2969652c024SAngus Ainslie 	365, 360, 355, 350, 340, 335, 330, 320,
2979652c024SAngus Ainslie 	310, 305, 300, 290, 285, 280, 275, 270,
2989652c024SAngus Ainslie 	260, 250, 245, 240, 230, 225, 220, 210,
2999652c024SAngus Ainslie 	205, 200, 190, 180, 175, 170, 160, 150,
3009652c024SAngus Ainslie 	145, 140, 130, 120, 115, 110, 100, 90,
3019652c024SAngus Ainslie 	80, 70, 60, 50, 40, 30, 20, 10,
3029652c024SAngus Ainslie 	0, -10, -20, -30, -40, -60, -70, -80,
3039652c024SAngus Ainslie 	-90, -10, -120, -140, -150, -170, -190, -210,
3049652c024SAngus Ainslie };
3059652c024SAngus Ainslie 
3069652c024SAngus Ainslie #define BQ25890_TSPCT_TBL_SIZE		ARRAY_SIZE(bq25890_tspct_tbl)
3079652c024SAngus Ainslie 
3088c0984e5SSebastian Reichel struct bq25890_range {
3098c0984e5SSebastian Reichel 	u32 min;
3108c0984e5SSebastian Reichel 	u32 max;
3118c0984e5SSebastian Reichel 	u32 step;
3128c0984e5SSebastian Reichel };
3138c0984e5SSebastian Reichel 
3148c0984e5SSebastian Reichel struct bq25890_lookup {
3158c0984e5SSebastian Reichel 	const u32 *tbl;
3168c0984e5SSebastian Reichel 	u32 size;
3178c0984e5SSebastian Reichel };
3188c0984e5SSebastian Reichel 
3198c0984e5SSebastian Reichel static const union {
3208c0984e5SSebastian Reichel 	struct bq25890_range  rt;
3218c0984e5SSebastian Reichel 	struct bq25890_lookup lt;
3228c0984e5SSebastian Reichel } bq25890_tables[] = {
3238c0984e5SSebastian Reichel 	/* range tables */
324d20267c9SYauhen Kharuzhy 	/* TODO: BQ25896 has max ICHG 3008 mA */
3258c0984e5SSebastian Reichel 	[TBL_ICHG] =	{ .rt = {0,	  5056000, 64000} },	 /* uA */
3268c0984e5SSebastian Reichel 	[TBL_ITERM] =	{ .rt = {64000,   1024000, 64000} },	 /* uA */
327766873c1SYauhen Kharuzhy 	[TBL_IINLIM] =  { .rt = {100000,  3250000, 50000} },	 /* uA */
3288c0984e5SSebastian Reichel 	[TBL_VREG] =	{ .rt = {3840000, 4608000, 16000} },	 /* uV */
3298c0984e5SSebastian Reichel 	[TBL_BOOSTV] =	{ .rt = {4550000, 5510000, 64000} },	 /* uV */
3308c0984e5SSebastian Reichel 	[TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} },	 /* uV */
33172408329SMichał Mirosław 	[TBL_VBATCOMP] ={ .rt = {0,        224000, 32000} },	 /* uV */
33272408329SMichał Mirosław 	[TBL_RBATCOMP] ={ .rt = {0,        140000, 20000} },	 /* uOhm */
3338c0984e5SSebastian Reichel 
3348c0984e5SSebastian Reichel 	/* lookup tables */
3358c0984e5SSebastian Reichel 	[TBL_TREG] =	{ .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
3369652c024SAngus Ainslie 	[TBL_BOOSTI] =	{ .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} },
3379652c024SAngus Ainslie 	[TBL_TSPCT] =	{ .lt = {bq25890_tspct_tbl, BQ25890_TSPCT_TBL_SIZE} }
3388c0984e5SSebastian Reichel };
3398c0984e5SSebastian Reichel 
3408c0984e5SSebastian Reichel static int bq25890_field_read(struct bq25890_device *bq,
3418c0984e5SSebastian Reichel 			      enum bq25890_fields field_id)
3428c0984e5SSebastian Reichel {
3438c0984e5SSebastian Reichel 	int ret;
3448c0984e5SSebastian Reichel 	int val;
3458c0984e5SSebastian Reichel 
3468c0984e5SSebastian Reichel 	ret = regmap_field_read(bq->rmap_fields[field_id], &val);
3478c0984e5SSebastian Reichel 	if (ret < 0)
3488c0984e5SSebastian Reichel 		return ret;
3498c0984e5SSebastian Reichel 
3508c0984e5SSebastian Reichel 	return val;
3518c0984e5SSebastian Reichel }
3528c0984e5SSebastian Reichel 
3538c0984e5SSebastian Reichel static int bq25890_field_write(struct bq25890_device *bq,
3548c0984e5SSebastian Reichel 			       enum bq25890_fields field_id, u8 val)
3558c0984e5SSebastian Reichel {
3568c0984e5SSebastian Reichel 	return regmap_field_write(bq->rmap_fields[field_id], val);
3578c0984e5SSebastian Reichel }
3588c0984e5SSebastian Reichel 
3598c0984e5SSebastian Reichel static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id)
3608c0984e5SSebastian Reichel {
3618c0984e5SSebastian Reichel 	u8 idx;
3628c0984e5SSebastian Reichel 
3638c0984e5SSebastian Reichel 	if (id >= TBL_TREG) {
3648c0984e5SSebastian Reichel 		const u32 *tbl = bq25890_tables[id].lt.tbl;
3658c0984e5SSebastian Reichel 		u32 tbl_size = bq25890_tables[id].lt.size;
3668c0984e5SSebastian Reichel 
3678c0984e5SSebastian Reichel 		for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++)
3688c0984e5SSebastian Reichel 			;
3698c0984e5SSebastian Reichel 	} else {
3708c0984e5SSebastian Reichel 		const struct bq25890_range *rtbl = &bq25890_tables[id].rt;
3718c0984e5SSebastian Reichel 		u8 rtbl_size;
3728c0984e5SSebastian Reichel 
3738c0984e5SSebastian Reichel 		rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1;
3748c0984e5SSebastian Reichel 
3758c0984e5SSebastian Reichel 		for (idx = 1;
3768c0984e5SSebastian Reichel 		     idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value);
3778c0984e5SSebastian Reichel 		     idx++)
3788c0984e5SSebastian Reichel 			;
3798c0984e5SSebastian Reichel 	}
3808c0984e5SSebastian Reichel 
3818c0984e5SSebastian Reichel 	return idx - 1;
3828c0984e5SSebastian Reichel }
3838c0984e5SSebastian Reichel 
3848c0984e5SSebastian Reichel static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id)
3858c0984e5SSebastian Reichel {
3868c0984e5SSebastian Reichel 	const struct bq25890_range *rtbl;
3878c0984e5SSebastian Reichel 
3888c0984e5SSebastian Reichel 	/* lookup table? */
3898c0984e5SSebastian Reichel 	if (id >= TBL_TREG)
3908c0984e5SSebastian Reichel 		return bq25890_tables[id].lt.tbl[idx];
3918c0984e5SSebastian Reichel 
3928c0984e5SSebastian Reichel 	/* range table */
3938c0984e5SSebastian Reichel 	rtbl = &bq25890_tables[id].rt;
3948c0984e5SSebastian Reichel 
3958c0984e5SSebastian Reichel 	return (rtbl->min + idx * rtbl->step);
3968c0984e5SSebastian Reichel }
3978c0984e5SSebastian Reichel 
3988c0984e5SSebastian Reichel enum bq25890_status {
3998c0984e5SSebastian Reichel 	STATUS_NOT_CHARGING,
4008c0984e5SSebastian Reichel 	STATUS_PRE_CHARGING,
4018c0984e5SSebastian Reichel 	STATUS_FAST_CHARGING,
4028c0984e5SSebastian Reichel 	STATUS_TERMINATION_DONE,
4038c0984e5SSebastian Reichel };
4048c0984e5SSebastian Reichel 
4058c0984e5SSebastian Reichel enum bq25890_chrg_fault {
4068c0984e5SSebastian Reichel 	CHRG_FAULT_NORMAL,
4078c0984e5SSebastian Reichel 	CHRG_FAULT_INPUT,
4088c0984e5SSebastian Reichel 	CHRG_FAULT_THERMAL_SHUTDOWN,
4098c0984e5SSebastian Reichel 	CHRG_FAULT_TIMER_EXPIRED,
4108c0984e5SSebastian Reichel };
4118c0984e5SSebastian Reichel 
412c562a43aSYauhen Kharuzhy enum bq25890_ntc_fault {
413c562a43aSYauhen Kharuzhy 	NTC_FAULT_NORMAL = 0,
414c562a43aSYauhen Kharuzhy 	NTC_FAULT_WARM = 2,
415c562a43aSYauhen Kharuzhy 	NTC_FAULT_COOL = 3,
416c562a43aSYauhen Kharuzhy 	NTC_FAULT_COLD = 5,
417c562a43aSYauhen Kharuzhy 	NTC_FAULT_HOT = 6,
418c562a43aSYauhen Kharuzhy };
419c562a43aSYauhen Kharuzhy 
42021d90edaSMichał Mirosław static bool bq25890_is_adc_property(enum power_supply_property psp)
42121d90edaSMichał Mirosław {
42221d90edaSMichał Mirosław 	switch (psp) {
42321d90edaSMichał Mirosław 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
42421d90edaSMichał Mirosław 	case POWER_SUPPLY_PROP_CURRENT_NOW:
4259652c024SAngus Ainslie 	case POWER_SUPPLY_PROP_TEMP:
42621d90edaSMichał Mirosław 		return true;
42721d90edaSMichał Mirosław 
42821d90edaSMichał Mirosław 	default:
42921d90edaSMichał Mirosław 		return false;
43021d90edaSMichał Mirosław 	}
43121d90edaSMichał Mirosław }
43221d90edaSMichał Mirosław 
4333b4df57bSMichał Mirosław static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq);
4343b4df57bSMichał Mirosław 
4358c0984e5SSebastian Reichel static int bq25890_power_supply_get_property(struct power_supply *psy,
4368c0984e5SSebastian Reichel 					     enum power_supply_property psp,
4378c0984e5SSebastian Reichel 					     union power_supply_propval *val)
4388c0984e5SSebastian Reichel {
4398c0984e5SSebastian Reichel 	struct bq25890_device *bq = power_supply_get_drvdata(psy);
4408c0984e5SSebastian Reichel 	struct bq25890_state state;
44121d90edaSMichał Mirosław 	bool do_adc_conv;
44221d90edaSMichał Mirosław 	int ret;
4438c0984e5SSebastian Reichel 
4448c0984e5SSebastian Reichel 	mutex_lock(&bq->lock);
4453b4df57bSMichał Mirosław 	/* update state in case we lost an interrupt */
4463b4df57bSMichał Mirosław 	__bq25890_handle_irq(bq);
4478c0984e5SSebastian Reichel 	state = bq->state;
44821d90edaSMichał Mirosław 	do_adc_conv = !state.online && bq25890_is_adc_property(psp);
44921d90edaSMichał Mirosław 	if (do_adc_conv)
45021d90edaSMichał Mirosław 		bq25890_field_write(bq, F_CONV_START, 1);
4518c0984e5SSebastian Reichel 	mutex_unlock(&bq->lock);
4528c0984e5SSebastian Reichel 
45321d90edaSMichał Mirosław 	if (do_adc_conv)
45421d90edaSMichał Mirosław 		regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START],
45521d90edaSMichał Mirosław 			ret, !ret, 25000, 1000000);
45621d90edaSMichał Mirosław 
4578c0984e5SSebastian Reichel 	switch (psp) {
4588c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
4598c0984e5SSebastian Reichel 		if (!state.online)
4608c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
4618c0984e5SSebastian Reichel 		else if (state.chrg_status == STATUS_NOT_CHARGING)
4628c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
4638c0984e5SSebastian Reichel 		else if (state.chrg_status == STATUS_PRE_CHARGING ||
4648c0984e5SSebastian Reichel 			 state.chrg_status == STATUS_FAST_CHARGING)
4658c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
4668c0984e5SSebastian Reichel 		else if (state.chrg_status == STATUS_TERMINATION_DONE)
4678c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_FULL;
4688c0984e5SSebastian Reichel 		else
4698c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
4708c0984e5SSebastian Reichel 
4718c0984e5SSebastian Reichel 		break;
4728c0984e5SSebastian Reichel 
473b302a0aeSMichał Mirosław 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
474b302a0aeSMichał Mirosław 		if (!state.online || state.chrg_status == STATUS_NOT_CHARGING ||
475b302a0aeSMichał Mirosław 		    state.chrg_status == STATUS_TERMINATION_DONE)
476b302a0aeSMichał Mirosław 			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
477b302a0aeSMichał Mirosław 		else if (state.chrg_status == STATUS_PRE_CHARGING)
478b302a0aeSMichał Mirosław 			val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
479b302a0aeSMichał Mirosław 		else if (state.chrg_status == STATUS_FAST_CHARGING)
480b302a0aeSMichał Mirosław 			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
481b302a0aeSMichał Mirosław 		else /* unreachable */
482b302a0aeSMichał Mirosław 			val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
483b302a0aeSMichał Mirosław 		break;
484b302a0aeSMichał Mirosław 
4858c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_MANUFACTURER:
4868c0984e5SSebastian Reichel 		val->strval = BQ25890_MANUFACTURER;
4878c0984e5SSebastian Reichel 		break;
4888c0984e5SSebastian Reichel 
4892e1a2ddeSAngus Ainslie (Purism) 	case POWER_SUPPLY_PROP_MODEL_NAME:
4905956fca7SMichał Mirosław 		val->strval = bq25890_chip_name[bq->chip_version];
4912e1a2ddeSAngus Ainslie (Purism) 		break;
4922e1a2ddeSAngus Ainslie (Purism) 
4938c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
4948c0984e5SSebastian Reichel 		val->intval = state.online;
4958c0984e5SSebastian Reichel 		break;
4968c0984e5SSebastian Reichel 
4978c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_HEALTH:
4988c0984e5SSebastian Reichel 		if (!state.chrg_fault && !state.bat_fault && !state.boost_fault)
4998c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_GOOD;
5008c0984e5SSebastian Reichel 		else if (state.bat_fault)
5018c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
5028c0984e5SSebastian Reichel 		else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED)
5038c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
5048c0984e5SSebastian Reichel 		else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN)
5058c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
5068c0984e5SSebastian Reichel 		else
5078c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
5088c0984e5SSebastian Reichel 		break;
5098c0984e5SSebastian Reichel 
5108c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
511f83a6eceSMichał Mirosław 		val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG);
512c562a43aSYauhen Kharuzhy 
513c562a43aSYauhen Kharuzhy 		/* When temperature is too low, charge current is decreased */
514c562a43aSYauhen Kharuzhy 		if (bq->state.ntc_fault == NTC_FAULT_COOL) {
515c562a43aSYauhen Kharuzhy 			ret = bq25890_field_read(bq, F_JEITA_ISET);
516c562a43aSYauhen Kharuzhy 			if (ret < 0)
517c562a43aSYauhen Kharuzhy 				return ret;
518c562a43aSYauhen Kharuzhy 
519c562a43aSYauhen Kharuzhy 			if (ret)
520c562a43aSYauhen Kharuzhy 				val->intval /= 5;
521c562a43aSYauhen Kharuzhy 			else
522c562a43aSYauhen Kharuzhy 				val->intval /= 2;
523c562a43aSYauhen Kharuzhy 		}
5248c0984e5SSebastian Reichel 		break;
5258c0984e5SSebastian Reichel 
5268c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
5278c0984e5SSebastian Reichel 		if (!state.online) {
5288c0984e5SSebastian Reichel 			val->intval = 0;
5298c0984e5SSebastian Reichel 			break;
5308c0984e5SSebastian Reichel 		}
5318c0984e5SSebastian Reichel 
5328c0984e5SSebastian Reichel 		ret = bq25890_field_read(bq, F_BATV); /* read measured value */
5338c0984e5SSebastian Reichel 		if (ret < 0)
5348c0984e5SSebastian Reichel 			return ret;
5358c0984e5SSebastian Reichel 
5368c0984e5SSebastian Reichel 		/* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */
5378c0984e5SSebastian Reichel 		val->intval = 2304000 + ret * 20000;
5388c0984e5SSebastian Reichel 		break;
5398c0984e5SSebastian Reichel 
5408c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
541f83a6eceSMichał Mirosław 		val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG);
5428c0984e5SSebastian Reichel 		break;
5438c0984e5SSebastian Reichel 
544c942656dSMichał Mirosław 	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
545c942656dSMichał Mirosław 		val->intval = bq25890_find_val(bq->init_data.iprechg, TBL_ITERM);
546c942656dSMichał Mirosław 		break;
547c942656dSMichał Mirosław 
5488c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
5498c0984e5SSebastian Reichel 		val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM);
5508c0984e5SSebastian Reichel 		break;
5518c0984e5SSebastian Reichel 
552478efc79SMichał Mirosław 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
553766873c1SYauhen Kharuzhy 		ret = bq25890_field_read(bq, F_IINLIM);
554478efc79SMichał Mirosław 		if (ret < 0)
555478efc79SMichał Mirosław 			return ret;
556478efc79SMichał Mirosław 
557766873c1SYauhen Kharuzhy 		val->intval = bq25890_find_val(ret, TBL_IINLIM);
558478efc79SMichał Mirosław 		break;
559478efc79SMichał Mirosław 
560ae6fe7a3SAngus Ainslie (Purism) 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
561ae6fe7a3SAngus Ainslie (Purism) 		ret = bq25890_field_read(bq, F_SYSV); /* read measured value */
562ae6fe7a3SAngus Ainslie (Purism) 		if (ret < 0)
563ae6fe7a3SAngus Ainslie (Purism) 			return ret;
564ae6fe7a3SAngus Ainslie (Purism) 
565ae6fe7a3SAngus Ainslie (Purism) 		/* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */
566ae6fe7a3SAngus Ainslie (Purism) 		val->intval = 2304000 + ret * 20000;
567ae6fe7a3SAngus Ainslie (Purism) 		break;
568ae6fe7a3SAngus Ainslie (Purism) 
5691e4724d0SMichał Mirosław 	case POWER_SUPPLY_PROP_CURRENT_NOW:
5701e4724d0SMichał Mirosław 		ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */
5711e4724d0SMichał Mirosław 		if (ret < 0)
5721e4724d0SMichał Mirosław 			return ret;
5731e4724d0SMichał Mirosław 
5741e4724d0SMichał Mirosław 		/* converted_val = ADC_val * 50mA (table 10.3.19) */
5751e4724d0SMichał Mirosław 		val->intval = ret * -50000;
5761e4724d0SMichał Mirosław 		break;
5771e4724d0SMichał Mirosław 
5789652c024SAngus Ainslie 	case POWER_SUPPLY_PROP_TEMP:
5799652c024SAngus Ainslie 		ret = bq25890_field_read(bq, F_TSPCT);
5809652c024SAngus Ainslie 		if (ret < 0)
5819652c024SAngus Ainslie 			return ret;
5829652c024SAngus Ainslie 
5839652c024SAngus Ainslie 		/* convert TS percentage into rough temperature */
5849652c024SAngus Ainslie 		val->intval = bq25890_find_val(ret, TBL_TSPCT);
5859652c024SAngus Ainslie 		break;
5869652c024SAngus Ainslie 
5878c0984e5SSebastian Reichel 	default:
5888c0984e5SSebastian Reichel 		return -EINVAL;
5898c0984e5SSebastian Reichel 	}
5908c0984e5SSebastian Reichel 
5918c0984e5SSebastian Reichel 	return 0;
5928c0984e5SSebastian Reichel }
5938c0984e5SSebastian Reichel 
5948c0984e5SSebastian Reichel static int bq25890_get_chip_state(struct bq25890_device *bq,
5958c0984e5SSebastian Reichel 				  struct bq25890_state *state)
5968c0984e5SSebastian Reichel {
5978c0984e5SSebastian Reichel 	int i, ret;
5988c0984e5SSebastian Reichel 
5998c0984e5SSebastian Reichel 	struct {
6008c0984e5SSebastian Reichel 		enum bq25890_fields id;
6018c0984e5SSebastian Reichel 		u8 *data;
6028c0984e5SSebastian Reichel 	} state_fields[] = {
6038c0984e5SSebastian Reichel 		{F_CHG_STAT,	&state->chrg_status},
6048c0984e5SSebastian Reichel 		{F_PG_STAT,	&state->online},
6058c0984e5SSebastian Reichel 		{F_VSYS_STAT,	&state->vsys_status},
6068c0984e5SSebastian Reichel 		{F_BOOST_FAULT, &state->boost_fault},
6078c0984e5SSebastian Reichel 		{F_BAT_FAULT,	&state->bat_fault},
608c562a43aSYauhen Kharuzhy 		{F_CHG_FAULT,	&state->chrg_fault},
609c562a43aSYauhen Kharuzhy 		{F_NTC_FAULT,	&state->ntc_fault}
6108c0984e5SSebastian Reichel 	};
6118c0984e5SSebastian Reichel 
6128c0984e5SSebastian Reichel 	for (i = 0; i < ARRAY_SIZE(state_fields); i++) {
6138c0984e5SSebastian Reichel 		ret = bq25890_field_read(bq, state_fields[i].id);
6148c0984e5SSebastian Reichel 		if (ret < 0)
6158c0984e5SSebastian Reichel 			return ret;
6168c0984e5SSebastian Reichel 
6178c0984e5SSebastian Reichel 		*state_fields[i].data = ret;
6188c0984e5SSebastian Reichel 	}
6198c0984e5SSebastian Reichel 
620c562a43aSYauhen Kharuzhy 	dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n",
6218c0984e5SSebastian Reichel 		state->chrg_status, state->online, state->vsys_status,
622c562a43aSYauhen Kharuzhy 		state->chrg_fault, state->boost_fault, state->bat_fault,
623c562a43aSYauhen Kharuzhy 		state->ntc_fault);
6248c0984e5SSebastian Reichel 
6258c0984e5SSebastian Reichel 	return 0;
6268c0984e5SSebastian Reichel }
6278c0984e5SSebastian Reichel 
62872d9cd9cSMichał Mirosław static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq)
6298c0984e5SSebastian Reichel {
63072d9cd9cSMichał Mirosław 	struct bq25890_state new_state;
6318c0984e5SSebastian Reichel 	int ret;
6328c0984e5SSebastian Reichel 
63372d9cd9cSMichał Mirosław 	ret = bq25890_get_chip_state(bq, &new_state);
63472d9cd9cSMichał Mirosław 	if (ret < 0)
63572d9cd9cSMichał Mirosław 		return IRQ_NONE;
6368c0984e5SSebastian Reichel 
63772d9cd9cSMichał Mirosław 	if (!memcmp(&bq->state, &new_state, sizeof(new_state)))
63872d9cd9cSMichał Mirosław 		return IRQ_NONE;
63972d9cd9cSMichał Mirosław 
64072d9cd9cSMichał Mirosław 	if (!new_state.online && bq->state.online) {	    /* power removed */
6418c0984e5SSebastian Reichel 		/* disable ADC */
64280211be1SYauhen Kharuzhy 		ret = bq25890_field_write(bq, F_CONV_RATE, 0);
6438c0984e5SSebastian Reichel 		if (ret < 0)
6448c0984e5SSebastian Reichel 			goto error;
64572d9cd9cSMichał Mirosław 	} else if (new_state.online && !bq->state.online) { /* power inserted */
6468c0984e5SSebastian Reichel 		/* enable ADC, to have control of charge current/voltage */
64780211be1SYauhen Kharuzhy 		ret = bq25890_field_write(bq, F_CONV_RATE, 1);
6488c0984e5SSebastian Reichel 		if (ret < 0)
6498c0984e5SSebastian Reichel 			goto error;
6508c0984e5SSebastian Reichel 	}
6518c0984e5SSebastian Reichel 
65272d9cd9cSMichał Mirosław 	bq->state = new_state;
65372d9cd9cSMichał Mirosław 	power_supply_changed(bq->charger);
6548c0984e5SSebastian Reichel 
65572d9cd9cSMichał Mirosław 	return IRQ_HANDLED;
6568c0984e5SSebastian Reichel error:
65772d9cd9cSMichał Mirosław 	dev_err(bq->dev, "Error communicating with the chip: %pe\n",
65872d9cd9cSMichał Mirosław 		ERR_PTR(ret));
65972d9cd9cSMichał Mirosław 	return IRQ_HANDLED;
6608c0984e5SSebastian Reichel }
6618c0984e5SSebastian Reichel 
6628c0984e5SSebastian Reichel static irqreturn_t bq25890_irq_handler_thread(int irq, void *private)
6638c0984e5SSebastian Reichel {
6648c0984e5SSebastian Reichel 	struct bq25890_device *bq = private;
66572d9cd9cSMichał Mirosław 	irqreturn_t ret;
6668c0984e5SSebastian Reichel 
6678c0984e5SSebastian Reichel 	mutex_lock(&bq->lock);
66872d9cd9cSMichał Mirosław 	ret = __bq25890_handle_irq(bq);
6698c0984e5SSebastian Reichel 	mutex_unlock(&bq->lock);
6708c0984e5SSebastian Reichel 
67172d9cd9cSMichał Mirosław 	return ret;
6728c0984e5SSebastian Reichel }
6738c0984e5SSebastian Reichel 
6748c0984e5SSebastian Reichel static int bq25890_chip_reset(struct bq25890_device *bq)
6758c0984e5SSebastian Reichel {
6768c0984e5SSebastian Reichel 	int ret;
6778c0984e5SSebastian Reichel 	int rst_check_counter = 10;
6788c0984e5SSebastian Reichel 
6798c0984e5SSebastian Reichel 	ret = bq25890_field_write(bq, F_REG_RST, 1);
6808c0984e5SSebastian Reichel 	if (ret < 0)
6818c0984e5SSebastian Reichel 		return ret;
6828c0984e5SSebastian Reichel 
6838c0984e5SSebastian Reichel 	do {
6848c0984e5SSebastian Reichel 		ret = bq25890_field_read(bq, F_REG_RST);
6858c0984e5SSebastian Reichel 		if (ret < 0)
6868c0984e5SSebastian Reichel 			return ret;
6878c0984e5SSebastian Reichel 
6888c0984e5SSebastian Reichel 		usleep_range(5, 10);
6898c0984e5SSebastian Reichel 	} while (ret == 1 && --rst_check_counter);
6908c0984e5SSebastian Reichel 
6918c0984e5SSebastian Reichel 	if (!rst_check_counter)
6928c0984e5SSebastian Reichel 		return -ETIMEDOUT;
6938c0984e5SSebastian Reichel 
6948c0984e5SSebastian Reichel 	return 0;
6958c0984e5SSebastian Reichel }
6968c0984e5SSebastian Reichel 
6977b22a974SHans de Goede static int bq25890_rw_init_data(struct bq25890_device *bq)
6988c0984e5SSebastian Reichel {
6997b22a974SHans de Goede 	bool write = true;
7008c0984e5SSebastian Reichel 	int ret;
7018c0984e5SSebastian Reichel 	int i;
7028c0984e5SSebastian Reichel 
7038c0984e5SSebastian Reichel 	const struct {
7048c0984e5SSebastian Reichel 		enum bq25890_fields id;
7057b22a974SHans de Goede 		u8 *value;
7068c0984e5SSebastian Reichel 	} init_data[] = {
7077b22a974SHans de Goede 		{F_ICHG,	 &bq->init_data.ichg},
7087b22a974SHans de Goede 		{F_VREG,	 &bq->init_data.vreg},
7097b22a974SHans de Goede 		{F_ITERM,	 &bq->init_data.iterm},
7107b22a974SHans de Goede 		{F_IPRECHG,	 &bq->init_data.iprechg},
7117b22a974SHans de Goede 		{F_SYSVMIN,	 &bq->init_data.sysvmin},
7127b22a974SHans de Goede 		{F_BOOSTV,	 &bq->init_data.boostv},
7137b22a974SHans de Goede 		{F_BOOSTI,	 &bq->init_data.boosti},
7147b22a974SHans de Goede 		{F_BOOSTF,	 &bq->init_data.boostf},
7157b22a974SHans de Goede 		{F_EN_ILIM,	 &bq->init_data.ilim_en},
7167b22a974SHans de Goede 		{F_TREG,	 &bq->init_data.treg},
7177b22a974SHans de Goede 		{F_BATCMP,	 &bq->init_data.rbatcomp},
7187b22a974SHans de Goede 		{F_VCLAMP,	 &bq->init_data.vclamp},
7198c0984e5SSebastian Reichel 	};
7208c0984e5SSebastian Reichel 
7217b22a974SHans de Goede 	for (i = 0; i < ARRAY_SIZE(init_data); i++) {
7227b22a974SHans de Goede 		if (write) {
7237b22a974SHans de Goede 			ret = bq25890_field_write(bq, init_data[i].id,
7247b22a974SHans de Goede 						  *init_data[i].value);
7257b22a974SHans de Goede 		} else {
7267b22a974SHans de Goede 			ret = bq25890_field_read(bq, init_data[i].id);
7277b22a974SHans de Goede 			if (ret >= 0)
7287b22a974SHans de Goede 				*init_data[i].value = ret;
7297b22a974SHans de Goede 		}
7307b22a974SHans de Goede 		if (ret < 0) {
7317b22a974SHans de Goede 			dev_dbg(bq->dev, "Accessing init data failed %d\n", ret);
7327b22a974SHans de Goede 			return ret;
7337b22a974SHans de Goede 		}
7347b22a974SHans de Goede 	}
7357b22a974SHans de Goede 
7367b22a974SHans de Goede 	return 0;
7377b22a974SHans de Goede }
7387b22a974SHans de Goede 
7397b22a974SHans de Goede static int bq25890_hw_init(struct bq25890_device *bq)
7407b22a974SHans de Goede {
7417b22a974SHans de Goede 	int ret;
7427b22a974SHans de Goede 
743*7e3b8e35SHans de Goede 	if (!bq->skip_reset) {
7448c0984e5SSebastian Reichel 		ret = bq25890_chip_reset(bq);
7459d9ae341SAngus Ainslie (Purism) 		if (ret < 0) {
7469d9ae341SAngus Ainslie (Purism) 			dev_dbg(bq->dev, "Reset failed %d\n", ret);
7478c0984e5SSebastian Reichel 			return ret;
748ad1570d9Skbuild test robot 		}
749*7e3b8e35SHans de Goede 	}
7508c0984e5SSebastian Reichel 
7518c0984e5SSebastian Reichel 	/* disable watchdog */
7528c0984e5SSebastian Reichel 	ret = bq25890_field_write(bq, F_WD, 0);
7539d9ae341SAngus Ainslie (Purism) 	if (ret < 0) {
7549d9ae341SAngus Ainslie (Purism) 		dev_dbg(bq->dev, "Disabling watchdog failed %d\n", ret);
7558c0984e5SSebastian Reichel 		return ret;
756ad1570d9Skbuild test robot 	}
7578c0984e5SSebastian Reichel 
7588c0984e5SSebastian Reichel 	/* initialize currents/voltages and other parameters */
7597b22a974SHans de Goede 	ret = bq25890_rw_init_data(bq);
7607b22a974SHans de Goede 	if (ret)
7618c0984e5SSebastian Reichel 		return ret;
7628c0984e5SSebastian Reichel 
76322ad4f99SHans de Goede 	ret = bq25890_get_chip_state(bq, &bq->state);
76422ad4f99SHans de Goede 	if (ret < 0) {
76522ad4f99SHans de Goede 		dev_dbg(bq->dev, "Get state failed %d\n", ret);
76622ad4f99SHans de Goede 		return ret;
76722ad4f99SHans de Goede 	}
76822ad4f99SHans de Goede 
76921d90edaSMichał Mirosław 	/* Configure ADC for continuous conversions when charging */
77021d90edaSMichał Mirosław 	ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online);
7719d9ae341SAngus Ainslie (Purism) 	if (ret < 0) {
7729d9ae341SAngus Ainslie (Purism) 		dev_dbg(bq->dev, "Config ADC failed %d\n", ret);
7738c0984e5SSebastian Reichel 		return ret;
774ad1570d9Skbuild test robot 	}
7758c0984e5SSebastian Reichel 
7768c0984e5SSebastian Reichel 	return 0;
7778c0984e5SSebastian Reichel }
7788c0984e5SSebastian Reichel 
779a6a48facSMichał Mirosław static const enum power_supply_property bq25890_power_supply_props[] = {
7808c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_MANUFACTURER,
7812e1a2ddeSAngus Ainslie (Purism) 	POWER_SUPPLY_PROP_MODEL_NAME,
7828c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
783b302a0aeSMichał Mirosław 	POWER_SUPPLY_PROP_CHARGE_TYPE,
7848c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ONLINE,
7858c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_HEALTH,
7868c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
7878c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
7888c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
789c942656dSMichał Mirosław 	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
7908c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
791478efc79SMichał Mirosław 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
792ae6fe7a3SAngus Ainslie (Purism) 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
7931e4724d0SMichał Mirosław 	POWER_SUPPLY_PROP_CURRENT_NOW,
7949652c024SAngus Ainslie 	POWER_SUPPLY_PROP_TEMP,
7958c0984e5SSebastian Reichel };
7968c0984e5SSebastian Reichel 
7978c0984e5SSebastian Reichel static char *bq25890_charger_supplied_to[] = {
7988c0984e5SSebastian Reichel 	"main-battery",
7998c0984e5SSebastian Reichel };
8008c0984e5SSebastian Reichel 
8018c0984e5SSebastian Reichel static const struct power_supply_desc bq25890_power_supply_desc = {
8028c0984e5SSebastian Reichel 	.name = "bq25890-charger",
8038c0984e5SSebastian Reichel 	.type = POWER_SUPPLY_TYPE_USB,
8048c0984e5SSebastian Reichel 	.properties = bq25890_power_supply_props,
8058c0984e5SSebastian Reichel 	.num_properties = ARRAY_SIZE(bq25890_power_supply_props),
8068c0984e5SSebastian Reichel 	.get_property = bq25890_power_supply_get_property,
8078c0984e5SSebastian Reichel };
8088c0984e5SSebastian Reichel 
8098c0984e5SSebastian Reichel static int bq25890_power_supply_init(struct bq25890_device *bq)
8108c0984e5SSebastian Reichel {
8118c0984e5SSebastian Reichel 	struct power_supply_config psy_cfg = { .drv_data = bq, };
8128c0984e5SSebastian Reichel 
8138c0984e5SSebastian Reichel 	psy_cfg.supplied_to = bq25890_charger_supplied_to;
8148c0984e5SSebastian Reichel 	psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to);
8158c0984e5SSebastian Reichel 
816d01363daSHans de Goede 	bq->charger = devm_power_supply_register(bq->dev,
817d01363daSHans de Goede 						 &bq25890_power_supply_desc,
8188c0984e5SSebastian Reichel 						 &psy_cfg);
8198c0984e5SSebastian Reichel 
8208c0984e5SSebastian Reichel 	return PTR_ERR_OR_ZERO(bq->charger);
8218c0984e5SSebastian Reichel }
8228c0984e5SSebastian Reichel 
8238c0984e5SSebastian Reichel static void bq25890_usb_work(struct work_struct *data)
8248c0984e5SSebastian Reichel {
8258c0984e5SSebastian Reichel 	int ret;
8268c0984e5SSebastian Reichel 	struct bq25890_device *bq =
8278c0984e5SSebastian Reichel 			container_of(data, struct bq25890_device, usb_work);
8288c0984e5SSebastian Reichel 
8298c0984e5SSebastian Reichel 	switch (bq->usb_event) {
8308c0984e5SSebastian Reichel 	case USB_EVENT_ID:
8318c0984e5SSebastian Reichel 		/* Enable boost mode */
8328c0984e5SSebastian Reichel 		ret = bq25890_field_write(bq, F_OTG_CFG, 1);
8338c0984e5SSebastian Reichel 		if (ret < 0)
8348c0984e5SSebastian Reichel 			goto error;
8358c0984e5SSebastian Reichel 		break;
8368c0984e5SSebastian Reichel 
8378c0984e5SSebastian Reichel 	case USB_EVENT_NONE:
8388c0984e5SSebastian Reichel 		/* Disable boost mode */
8398c0984e5SSebastian Reichel 		ret = bq25890_field_write(bq, F_OTG_CFG, 0);
8408c0984e5SSebastian Reichel 		if (ret < 0)
8418c0984e5SSebastian Reichel 			goto error;
8428c0984e5SSebastian Reichel 
8438c0984e5SSebastian Reichel 		power_supply_changed(bq->charger);
8448c0984e5SSebastian Reichel 		break;
8458c0984e5SSebastian Reichel 	}
8468c0984e5SSebastian Reichel 
8478c0984e5SSebastian Reichel 	return;
8488c0984e5SSebastian Reichel 
8498c0984e5SSebastian Reichel error:
8508c0984e5SSebastian Reichel 	dev_err(bq->dev, "Error switching to boost/charger mode.\n");
8518c0984e5SSebastian Reichel }
8528c0984e5SSebastian Reichel 
8538c0984e5SSebastian Reichel static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
8548c0984e5SSebastian Reichel 				void *priv)
8558c0984e5SSebastian Reichel {
8568c0984e5SSebastian Reichel 	struct bq25890_device *bq =
8578c0984e5SSebastian Reichel 			container_of(nb, struct bq25890_device, usb_nb);
8588c0984e5SSebastian Reichel 
8598c0984e5SSebastian Reichel 	bq->usb_event = val;
8608c0984e5SSebastian Reichel 	queue_work(system_power_efficient_wq, &bq->usb_work);
8618c0984e5SSebastian Reichel 
8628c0984e5SSebastian Reichel 	return NOTIFY_OK;
8638c0984e5SSebastian Reichel }
8648c0984e5SSebastian Reichel 
865d20267c9SYauhen Kharuzhy static int bq25890_get_chip_version(struct bq25890_device *bq)
866d20267c9SYauhen Kharuzhy {
867d20267c9SYauhen Kharuzhy 	int id, rev;
868d20267c9SYauhen Kharuzhy 
869d20267c9SYauhen Kharuzhy 	id = bq25890_field_read(bq, F_PN);
870d20267c9SYauhen Kharuzhy 	if (id < 0) {
871172d0cceSMartin Kepplinger 		dev_err(bq->dev, "Cannot read chip ID: %d\n", id);
872d20267c9SYauhen Kharuzhy 		return id;
873d20267c9SYauhen Kharuzhy 	}
874d20267c9SYauhen Kharuzhy 
875d20267c9SYauhen Kharuzhy 	rev = bq25890_field_read(bq, F_DEV_REV);
876d20267c9SYauhen Kharuzhy 	if (rev < 0) {
877172d0cceSMartin Kepplinger 		dev_err(bq->dev, "Cannot read chip revision: %d\n", rev);
878cb619e80SColin Ian King 		return rev;
879d20267c9SYauhen Kharuzhy 	}
880d20267c9SYauhen Kharuzhy 
881d20267c9SYauhen Kharuzhy 	switch (id) {
882d20267c9SYauhen Kharuzhy 	case BQ25890_ID:
883d20267c9SYauhen Kharuzhy 		bq->chip_version = BQ25890;
884d20267c9SYauhen Kharuzhy 		break;
885d20267c9SYauhen Kharuzhy 
886d20267c9SYauhen Kharuzhy 	/* BQ25892 and BQ25896 share same ID 0 */
887d20267c9SYauhen Kharuzhy 	case BQ25896_ID:
888d20267c9SYauhen Kharuzhy 		switch (rev) {
889d20267c9SYauhen Kharuzhy 		case 2:
890d20267c9SYauhen Kharuzhy 			bq->chip_version = BQ25896;
891d20267c9SYauhen Kharuzhy 			break;
892d20267c9SYauhen Kharuzhy 		case 1:
893d20267c9SYauhen Kharuzhy 			bq->chip_version = BQ25892;
894d20267c9SYauhen Kharuzhy 			break;
895d20267c9SYauhen Kharuzhy 		default:
896d20267c9SYauhen Kharuzhy 			dev_err(bq->dev,
897d20267c9SYauhen Kharuzhy 				"Unknown device revision %d, assume BQ25892\n",
898d20267c9SYauhen Kharuzhy 				rev);
899d20267c9SYauhen Kharuzhy 			bq->chip_version = BQ25892;
900d20267c9SYauhen Kharuzhy 		}
901d20267c9SYauhen Kharuzhy 		break;
902d20267c9SYauhen Kharuzhy 
903d20267c9SYauhen Kharuzhy 	case BQ25895_ID:
904d20267c9SYauhen Kharuzhy 		bq->chip_version = BQ25895;
905d20267c9SYauhen Kharuzhy 		break;
906d20267c9SYauhen Kharuzhy 
907d20267c9SYauhen Kharuzhy 	default:
908d20267c9SYauhen Kharuzhy 		dev_err(bq->dev, "Unknown chip ID %d\n", id);
909d20267c9SYauhen Kharuzhy 		return -ENODEV;
910d20267c9SYauhen Kharuzhy 	}
911d20267c9SYauhen Kharuzhy 
912d20267c9SYauhen Kharuzhy 	return 0;
913d20267c9SYauhen Kharuzhy }
914d20267c9SYauhen Kharuzhy 
9158c0984e5SSebastian Reichel static int bq25890_irq_probe(struct bq25890_device *bq)
9168c0984e5SSebastian Reichel {
9178c0984e5SSebastian Reichel 	struct gpio_desc *irq;
9188c0984e5SSebastian Reichel 
91986775879SAndy Shevchenko 	irq = devm_gpiod_get(bq->dev, BQ25890_IRQ_PIN, GPIOD_IN);
920172d0cceSMartin Kepplinger 	if (IS_ERR(irq))
921172d0cceSMartin Kepplinger 		return dev_err_probe(bq->dev, PTR_ERR(irq),
922172d0cceSMartin Kepplinger 				     "Could not probe irq pin.\n");
9238c0984e5SSebastian Reichel 
9248c0984e5SSebastian Reichel 	return gpiod_to_irq(irq);
9258c0984e5SSebastian Reichel }
9268c0984e5SSebastian Reichel 
9278c0984e5SSebastian Reichel static int bq25890_fw_read_u32_props(struct bq25890_device *bq)
9288c0984e5SSebastian Reichel {
9298c0984e5SSebastian Reichel 	int ret;
9308c0984e5SSebastian Reichel 	u32 property;
9318c0984e5SSebastian Reichel 	int i;
9328c0984e5SSebastian Reichel 	struct bq25890_init_data *init = &bq->init_data;
9338c0984e5SSebastian Reichel 	struct {
9348c0984e5SSebastian Reichel 		char *name;
9358c0984e5SSebastian Reichel 		bool optional;
9368c0984e5SSebastian Reichel 		enum bq25890_table_ids tbl_id;
9378c0984e5SSebastian Reichel 		u8 *conv_data; /* holds converted value from given property */
9388c0984e5SSebastian Reichel 	} props[] = {
9398c0984e5SSebastian Reichel 		/* required properties */
9408c0984e5SSebastian Reichel 		{"ti,charge-current", false, TBL_ICHG, &init->ichg},
9418c0984e5SSebastian Reichel 		{"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg},
9428c0984e5SSebastian Reichel 		{"ti,termination-current", false, TBL_ITERM, &init->iterm},
9438c0984e5SSebastian Reichel 		{"ti,precharge-current", false, TBL_ITERM, &init->iprechg},
9448c0984e5SSebastian Reichel 		{"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin},
9458c0984e5SSebastian Reichel 		{"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv},
9468c0984e5SSebastian Reichel 		{"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti},
9478c0984e5SSebastian Reichel 
9488c0984e5SSebastian Reichel 		/* optional properties */
94972408329SMichał Mirosław 		{"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg},
95072408329SMichał Mirosław 		{"ti,ibatcomp-micro-ohms", true, TBL_RBATCOMP, &init->rbatcomp},
95172408329SMichał Mirosław 		{"ti,ibatcomp-clamp-microvolt", true, TBL_VBATCOMP, &init->vclamp},
9528c0984e5SSebastian Reichel 	};
9538c0984e5SSebastian Reichel 
9548c0984e5SSebastian Reichel 	/* initialize data for optional properties */
9558c0984e5SSebastian Reichel 	init->treg = 3; /* 120 degrees Celsius */
95672408329SMichał Mirosław 	init->rbatcomp = init->vclamp = 0; /* IBAT compensation disabled */
9578c0984e5SSebastian Reichel 
9588c0984e5SSebastian Reichel 	for (i = 0; i < ARRAY_SIZE(props); i++) {
9598c0984e5SSebastian Reichel 		ret = device_property_read_u32(bq->dev, props[i].name,
9608c0984e5SSebastian Reichel 					       &property);
9618c0984e5SSebastian Reichel 		if (ret < 0) {
9628c0984e5SSebastian Reichel 			if (props[i].optional)
9638c0984e5SSebastian Reichel 				continue;
9648c0984e5SSebastian Reichel 
9659d9ae341SAngus Ainslie (Purism) 			dev_err(bq->dev, "Unable to read property %d %s\n", ret,
9669d9ae341SAngus Ainslie (Purism) 				props[i].name);
9679d9ae341SAngus Ainslie (Purism) 
9688c0984e5SSebastian Reichel 			return ret;
9698c0984e5SSebastian Reichel 		}
9708c0984e5SSebastian Reichel 
9718c0984e5SSebastian Reichel 		*props[i].conv_data = bq25890_find_idx(property,
9728c0984e5SSebastian Reichel 						       props[i].tbl_id);
9738c0984e5SSebastian Reichel 	}
9748c0984e5SSebastian Reichel 
9758c0984e5SSebastian Reichel 	return 0;
9768c0984e5SSebastian Reichel }
9778c0984e5SSebastian Reichel 
9788c0984e5SSebastian Reichel static int bq25890_fw_probe(struct bq25890_device *bq)
9798c0984e5SSebastian Reichel {
9808c0984e5SSebastian Reichel 	int ret;
9818c0984e5SSebastian Reichel 	struct bq25890_init_data *init = &bq->init_data;
9828c0984e5SSebastian Reichel 
983*7e3b8e35SHans de Goede 	bq->skip_reset = device_property_read_bool(bq->dev, "linux,skip-reset");
984*7e3b8e35SHans de Goede 
9858c0984e5SSebastian Reichel 	ret = bq25890_fw_read_u32_props(bq);
9868c0984e5SSebastian Reichel 	if (ret < 0)
9878c0984e5SSebastian Reichel 		return ret;
9888c0984e5SSebastian Reichel 
9898c0984e5SSebastian Reichel 	init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin");
9908c0984e5SSebastian Reichel 	init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq");
9918c0984e5SSebastian Reichel 
9928c0984e5SSebastian Reichel 	return 0;
9938c0984e5SSebastian Reichel }
9948c0984e5SSebastian Reichel 
9958c0984e5SSebastian Reichel static int bq25890_probe(struct i2c_client *client,
9968c0984e5SSebastian Reichel 			 const struct i2c_device_id *id)
9978c0984e5SSebastian Reichel {
9988c0984e5SSebastian Reichel 	struct device *dev = &client->dev;
9998c0984e5SSebastian Reichel 	struct bq25890_device *bq;
10008c0984e5SSebastian Reichel 	int ret;
10018c0984e5SSebastian Reichel 	int i;
10028c0984e5SSebastian Reichel 
10038c0984e5SSebastian Reichel 	bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
10048c0984e5SSebastian Reichel 	if (!bq)
10058c0984e5SSebastian Reichel 		return -ENOMEM;
10068c0984e5SSebastian Reichel 
10078c0984e5SSebastian Reichel 	bq->client = client;
10088c0984e5SSebastian Reichel 	bq->dev = dev;
10098c0984e5SSebastian Reichel 
10108c0984e5SSebastian Reichel 	mutex_init(&bq->lock);
10118c0984e5SSebastian Reichel 
10128c0984e5SSebastian Reichel 	bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config);
1013172d0cceSMartin Kepplinger 	if (IS_ERR(bq->rmap))
1014172d0cceSMartin Kepplinger 		return dev_err_probe(dev, PTR_ERR(bq->rmap),
1015172d0cceSMartin Kepplinger 				     "failed to allocate register map\n");
10168c0984e5SSebastian Reichel 
10178c0984e5SSebastian Reichel 	for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) {
10188c0984e5SSebastian Reichel 		const struct reg_field *reg_fields = bq25890_reg_fields;
10198c0984e5SSebastian Reichel 
10208c0984e5SSebastian Reichel 		bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
10218c0984e5SSebastian Reichel 							     reg_fields[i]);
1022172d0cceSMartin Kepplinger 		if (IS_ERR(bq->rmap_fields[i]))
1023172d0cceSMartin Kepplinger 			return dev_err_probe(dev, PTR_ERR(bq->rmap_fields[i]),
1024172d0cceSMartin Kepplinger 					     "cannot allocate regmap field\n");
10258c0984e5SSebastian Reichel 	}
10268c0984e5SSebastian Reichel 
10278c0984e5SSebastian Reichel 	i2c_set_clientdata(client, bq);
10288c0984e5SSebastian Reichel 
1029d20267c9SYauhen Kharuzhy 	ret = bq25890_get_chip_version(bq);
1030d20267c9SYauhen Kharuzhy 	if (ret) {
1031172d0cceSMartin Kepplinger 		dev_err(dev, "Cannot read chip ID or unknown chip: %d\n", ret);
1032d20267c9SYauhen Kharuzhy 		return ret;
10338c0984e5SSebastian Reichel 	}
10348c0984e5SSebastian Reichel 
10358c0984e5SSebastian Reichel 	if (!dev->platform_data) {
10368c0984e5SSebastian Reichel 		ret = bq25890_fw_probe(bq);
10378c0984e5SSebastian Reichel 		if (ret < 0) {
1038172d0cceSMartin Kepplinger 			dev_err(dev, "Cannot read device properties: %d\n",
1039172d0cceSMartin Kepplinger 				ret);
10408c0984e5SSebastian Reichel 			return ret;
10418c0984e5SSebastian Reichel 		}
10428c0984e5SSebastian Reichel 	} else {
10438c0984e5SSebastian Reichel 		return -ENODEV;
10448c0984e5SSebastian Reichel 	}
10458c0984e5SSebastian Reichel 
10468c0984e5SSebastian Reichel 	ret = bq25890_hw_init(bq);
10478c0984e5SSebastian Reichel 	if (ret < 0) {
1048172d0cceSMartin Kepplinger 		dev_err(dev, "Cannot initialize the chip: %d\n", ret);
10498c0984e5SSebastian Reichel 		return ret;
10508c0984e5SSebastian Reichel 	}
10518c0984e5SSebastian Reichel 
10528c0984e5SSebastian Reichel 	if (client->irq <= 0)
10538c0984e5SSebastian Reichel 		client->irq = bq25890_irq_probe(bq);
10548c0984e5SSebastian Reichel 
10558c0984e5SSebastian Reichel 	if (client->irq < 0) {
10568c0984e5SSebastian Reichel 		dev_err(dev, "No irq resource found.\n");
10578c0984e5SSebastian Reichel 		return client->irq;
10588c0984e5SSebastian Reichel 	}
10598c0984e5SSebastian Reichel 
10608c0984e5SSebastian Reichel 	/* OTG reporting */
10618c0984e5SSebastian Reichel 	bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
10628c0984e5SSebastian Reichel 	if (!IS_ERR_OR_NULL(bq->usb_phy)) {
10638c0984e5SSebastian Reichel 		INIT_WORK(&bq->usb_work, bq25890_usb_work);
10648c0984e5SSebastian Reichel 		bq->usb_nb.notifier_call = bq25890_usb_notifier;
10658c0984e5SSebastian Reichel 		usb_register_notifier(bq->usb_phy, &bq->usb_nb);
10668c0984e5SSebastian Reichel 	}
10678c0984e5SSebastian Reichel 
1068d01363daSHans de Goede 	ret = bq25890_power_supply_init(bq);
1069d01363daSHans de Goede 	if (ret < 0) {
1070d01363daSHans de Goede 		dev_err(dev, "Failed to register power supply\n");
1071d01363daSHans de Goede 		goto err_unregister_usb_notifier;
1072d01363daSHans de Goede 	}
1073d01363daSHans de Goede 
10748c0984e5SSebastian Reichel 	ret = devm_request_threaded_irq(dev, client->irq, NULL,
10758c0984e5SSebastian Reichel 					bq25890_irq_handler_thread,
10768c0984e5SSebastian Reichel 					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
10778c0984e5SSebastian Reichel 					BQ25890_IRQ_PIN, bq);
10788c0984e5SSebastian Reichel 	if (ret)
1079d01363daSHans de Goede 		goto err_unregister_usb_notifier;
10808c0984e5SSebastian Reichel 
10818c0984e5SSebastian Reichel 	return 0;
10828c0984e5SSebastian Reichel 
1083d01363daSHans de Goede err_unregister_usb_notifier:
10848c0984e5SSebastian Reichel 	if (!IS_ERR_OR_NULL(bq->usb_phy))
10858c0984e5SSebastian Reichel 		usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
10868c0984e5SSebastian Reichel 
10878c0984e5SSebastian Reichel 	return ret;
10888c0984e5SSebastian Reichel }
10898c0984e5SSebastian Reichel 
10908c0984e5SSebastian Reichel static int bq25890_remove(struct i2c_client *client)
10918c0984e5SSebastian Reichel {
10928c0984e5SSebastian Reichel 	struct bq25890_device *bq = i2c_get_clientdata(client);
10938c0984e5SSebastian Reichel 
10948c0984e5SSebastian Reichel 	if (!IS_ERR_OR_NULL(bq->usb_phy))
10958c0984e5SSebastian Reichel 		usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
10968c0984e5SSebastian Reichel 
1097*7e3b8e35SHans de Goede 	if (!bq->skip_reset) {
10988c0984e5SSebastian Reichel 		/* reset all registers to default values */
10998c0984e5SSebastian Reichel 		bq25890_chip_reset(bq);
1100*7e3b8e35SHans de Goede 	}
11018c0984e5SSebastian Reichel 
11028c0984e5SSebastian Reichel 	return 0;
11038c0984e5SSebastian Reichel }
11048c0984e5SSebastian Reichel 
11058c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP
11068c0984e5SSebastian Reichel static int bq25890_suspend(struct device *dev)
11078c0984e5SSebastian Reichel {
11088c0984e5SSebastian Reichel 	struct bq25890_device *bq = dev_get_drvdata(dev);
11098c0984e5SSebastian Reichel 
11108c0984e5SSebastian Reichel 	/*
11118c0984e5SSebastian Reichel 	 * If charger is removed, while in suspend, make sure ADC is diabled
11128c0984e5SSebastian Reichel 	 * since it consumes slightly more power.
11138c0984e5SSebastian Reichel 	 */
111421d90edaSMichał Mirosław 	return bq25890_field_write(bq, F_CONV_RATE, 0);
11158c0984e5SSebastian Reichel }
11168c0984e5SSebastian Reichel 
11178c0984e5SSebastian Reichel static int bq25890_resume(struct device *dev)
11188c0984e5SSebastian Reichel {
11198c0984e5SSebastian Reichel 	int ret;
11208c0984e5SSebastian Reichel 	struct bq25890_device *bq = dev_get_drvdata(dev);
11218c0984e5SSebastian Reichel 
112272d9cd9cSMichał Mirosław 	mutex_lock(&bq->lock);
112372d9cd9cSMichał Mirosław 
112472d9cd9cSMichał Mirosław 	ret = bq25890_get_chip_state(bq, &bq->state);
11258c0984e5SSebastian Reichel 	if (ret < 0)
1126cf5701bfSDan Carpenter 		goto unlock;
11278c0984e5SSebastian Reichel 
11288c0984e5SSebastian Reichel 	/* Re-enable ADC only if charger is plugged in. */
112972d9cd9cSMichał Mirosław 	if (bq->state.online) {
113021d90edaSMichał Mirosław 		ret = bq25890_field_write(bq, F_CONV_RATE, 1);
11318c0984e5SSebastian Reichel 		if (ret < 0)
1132cf5701bfSDan Carpenter 			goto unlock;
11338c0984e5SSebastian Reichel 	}
11348c0984e5SSebastian Reichel 
11358c0984e5SSebastian Reichel 	/* signal userspace, maybe state changed while suspended */
11368c0984e5SSebastian Reichel 	power_supply_changed(bq->charger);
11378c0984e5SSebastian Reichel 
1138cf5701bfSDan Carpenter unlock:
113972d9cd9cSMichał Mirosław 	mutex_unlock(&bq->lock);
114072d9cd9cSMichał Mirosław 
1141cf5701bfSDan Carpenter 	return ret;
11428c0984e5SSebastian Reichel }
11438c0984e5SSebastian Reichel #endif
11448c0984e5SSebastian Reichel 
11458c0984e5SSebastian Reichel static const struct dev_pm_ops bq25890_pm = {
11468c0984e5SSebastian Reichel 	SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume)
11478c0984e5SSebastian Reichel };
11488c0984e5SSebastian Reichel 
11498c0984e5SSebastian Reichel static const struct i2c_device_id bq25890_i2c_ids[] = {
11508c0984e5SSebastian Reichel 	{ "bq25890", 0 },
115146aa27e7SYauhen Kharuzhy 	{ "bq25892", 0 },
115246aa27e7SYauhen Kharuzhy 	{ "bq25895", 0 },
115346aa27e7SYauhen Kharuzhy 	{ "bq25896", 0 },
11548c0984e5SSebastian Reichel 	{},
11558c0984e5SSebastian Reichel };
11568c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids);
11578c0984e5SSebastian Reichel 
11588c0984e5SSebastian Reichel static const struct of_device_id bq25890_of_match[] = {
11598c0984e5SSebastian Reichel 	{ .compatible = "ti,bq25890", },
116046aa27e7SYauhen Kharuzhy 	{ .compatible = "ti,bq25892", },
116146aa27e7SYauhen Kharuzhy 	{ .compatible = "ti,bq25895", },
116246aa27e7SYauhen Kharuzhy 	{ .compatible = "ti,bq25896", },
11638c0984e5SSebastian Reichel 	{ },
11648c0984e5SSebastian Reichel };
11658c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, bq25890_of_match);
11668c0984e5SSebastian Reichel 
116702067dc9SKrzysztof Kozlowski #ifdef CONFIG_ACPI
11688c0984e5SSebastian Reichel static const struct acpi_device_id bq25890_acpi_match[] = {
11698c0984e5SSebastian Reichel 	{"BQ258900", 0},
11708c0984e5SSebastian Reichel 	{},
11718c0984e5SSebastian Reichel };
11728c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match);
117302067dc9SKrzysztof Kozlowski #endif
11748c0984e5SSebastian Reichel 
11758c0984e5SSebastian Reichel static struct i2c_driver bq25890_driver = {
11768c0984e5SSebastian Reichel 	.driver = {
11778c0984e5SSebastian Reichel 		.name = "bq25890-charger",
11788c0984e5SSebastian Reichel 		.of_match_table = of_match_ptr(bq25890_of_match),
11798c0984e5SSebastian Reichel 		.acpi_match_table = ACPI_PTR(bq25890_acpi_match),
11808c0984e5SSebastian Reichel 		.pm = &bq25890_pm,
11818c0984e5SSebastian Reichel 	},
11828c0984e5SSebastian Reichel 	.probe = bq25890_probe,
11838c0984e5SSebastian Reichel 	.remove = bq25890_remove,
11848c0984e5SSebastian Reichel 	.id_table = bq25890_i2c_ids,
11858c0984e5SSebastian Reichel };
11868c0984e5SSebastian Reichel module_i2c_driver(bq25890_driver);
11878c0984e5SSebastian Reichel 
11888c0984e5SSebastian Reichel MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
11898c0984e5SSebastian Reichel MODULE_DESCRIPTION("bq25890 charger driver");
11908c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
1191