xref: /rk3399_rockchip-uboot/drivers/power/charge/bq25890_charger.c (revision fb929110ea1b43783b160c6ccc091b30d96a9b92)
11e349be1SChen Shunqing /*
21e349be1SChen Shunqing  * (C) Copyright 2022 Rockchip Electronics Co., Ltd
31e349be1SChen Shunqing  *
41e349be1SChen Shunqing  * SPDX-License-Identifier:	GPL-2.0+
51e349be1SChen Shunqing  */
61e349be1SChen Shunqing 
71e349be1SChen Shunqing #include <common.h>
81e349be1SChen Shunqing #include <dm.h>
91e349be1SChen Shunqing #include <dm/device.h>
101e349be1SChen Shunqing #include <dm/uclass.h>
111e349be1SChen Shunqing #include <power/fuel_gauge.h>
121e349be1SChen Shunqing #include <power/pmic.h>
131e349be1SChen Shunqing #include <power/power_delivery/power_delivery.h>
141e349be1SChen Shunqing #include <linux/usb/phy-rockchip-usb2.h>
151e349be1SChen Shunqing 
161e349be1SChen Shunqing DECLARE_GLOBAL_DATA_PTR;
171e349be1SChen Shunqing 
181e349be1SChen Shunqing #define BQ25890_CHARGE_CURRENT_1500MA		1500
191e349be1SChen Shunqing #define BQ25890_SDP_INPUT_CURRENT_500MA		0x45
201e349be1SChen Shunqing #define BQ25890_DCP_INPUT_CURRENT_1500MA	0x4f
211e349be1SChen Shunqing #define BQ25890_DCP_INPUT_CURRENT_2000MA	0x54
221e349be1SChen Shunqing #define BQ25890_DCP_INPUT_CURRENT_3000MA	0x5e
231e349be1SChen Shunqing 
241e349be1SChen Shunqing #define WATCHDOG_ENSABLE			(0x03 << 4)
251e349be1SChen Shunqing 
261e349be1SChen Shunqing #define BQ25890_CHARGEOPTION0_REG		0x07
271e349be1SChen Shunqing #define BQ25890_CHARGECURREN_REG		0x04
281e349be1SChen Shunqing #define BQ25890_CHARGERSTAUS_REG		0x0B
291e349be1SChen Shunqing #define BQ25890_INPUTVOLTAGE_REG		0x0D
301e349be1SChen Shunqing #define BQ25890_INPUTCURREN_REG			0x00
310f0dc534SChen Shunqing #define BQ25890_AUTO_DPDM_REG			0x02
321e349be1SChen Shunqing 
331e349be1SChen Shunqing /*
341e349be1SChen Shunqing  * Most of the val -> idx conversions can be computed, given the minimum,
351e349be1SChen Shunqing  * maximum and the step between values. For the rest of conversions, we use
361e349be1SChen Shunqing  * lookup tables.
371e349be1SChen Shunqing  */
381e349be1SChen Shunqing enum bq25890_table_ids {
391e349be1SChen Shunqing 	/* range tables */
401e349be1SChen Shunqing 	TBL_ICHG,
411e349be1SChen Shunqing 	TBL_ITERM,
421e349be1SChen Shunqing 	TBL_IPRECHG,
431e349be1SChen Shunqing 	TBL_VREG,
441e349be1SChen Shunqing 	TBL_BATCMP,
451e349be1SChen Shunqing 	TBL_VCLAMP,
461e349be1SChen Shunqing 	TBL_BOOSTV,
471e349be1SChen Shunqing 	TBL_SYSVMIN,
481e349be1SChen Shunqing 	TBL_VINDPM,
491e349be1SChen Shunqing 	TBL_IINLIM,
501e349be1SChen Shunqing 
511e349be1SChen Shunqing 	/* lookup tables */
521e349be1SChen Shunqing 	TBL_TREG,
531e349be1SChen Shunqing 	TBL_BOOSTI,
541e349be1SChen Shunqing };
551e349be1SChen Shunqing 
561e349be1SChen Shunqing struct bq25890 {
571e349be1SChen Shunqing 	struct udevice *dev;
581e349be1SChen Shunqing 	u32 ichg;
591e349be1SChen Shunqing 	u32 chip_id;
601e349be1SChen Shunqing 	struct udevice *pd;
61*fb929110SChen Shunqing 	bool pd_online;
621e349be1SChen Shunqing };
631e349be1SChen Shunqing 
641e349be1SChen Shunqing /* Thermal Regulation Threshold lookup table, in degrees Celsius */
651e349be1SChen Shunqing static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 };
661e349be1SChen Shunqing 
671e349be1SChen Shunqing #define BQ25890_TREG_TBL_SIZE		ARRAY_SIZE(bq25890_treg_tbl)
681e349be1SChen Shunqing 
691e349be1SChen Shunqing /* Boost mode current limit lookup table, in uA */
701e349be1SChen Shunqing static const u32 bq25890_boosti_tbl[] = {
711e349be1SChen Shunqing 	500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000
721e349be1SChen Shunqing };
731e349be1SChen Shunqing 
741e349be1SChen Shunqing #define BQ25890_BOOSTI_TBL_SIZE		ARRAY_SIZE(bq25890_boosti_tbl)
751e349be1SChen Shunqing 
761e349be1SChen Shunqing struct bq25890_range {
771e349be1SChen Shunqing 	u32 min;
781e349be1SChen Shunqing 	u32 max;
791e349be1SChen Shunqing 	u32 step;
801e349be1SChen Shunqing };
811e349be1SChen Shunqing 
821e349be1SChen Shunqing struct bq25890_lookup {
831e349be1SChen Shunqing 	const u32 *tbl;
841e349be1SChen Shunqing 	u32 size;
851e349be1SChen Shunqing };
861e349be1SChen Shunqing 
871e349be1SChen Shunqing static const union {
881e349be1SChen Shunqing 	struct bq25890_range  rt;
891e349be1SChen Shunqing 	struct bq25890_lookup lt;
901e349be1SChen Shunqing } bq25890_tables[] = {
911e349be1SChen Shunqing 	/* range tables */
921e349be1SChen Shunqing 	[TBL_ICHG] =	{ .rt = {0,	  5056000, 64000} },	 /* uA */
931e349be1SChen Shunqing 	[TBL_ITERM] =	{ .rt = {64000,   1024000, 64000} },	 /* uA */
941e349be1SChen Shunqing 	[TBL_VREG] =	{ .rt = {3840000, 4608000, 16000} },	 /* uV */
951e349be1SChen Shunqing 	[TBL_BATCMP] =	{ .rt = {0,	  140,     20} },	 /* mOhm */
961e349be1SChen Shunqing 	[TBL_VCLAMP] =	{ .rt = {0,	  224000,  32000} },	 /* uV */
971e349be1SChen Shunqing 	[TBL_BOOSTV] =	{ .rt = {4550000, 5510000, 64000} },	 /* uV */
981e349be1SChen Shunqing 	[TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} },	 /* uV */
991e349be1SChen Shunqing 	[TBL_VINDPM] = { .rt = {2600000, 15300000, 100000} }, /* uV */
1001e349be1SChen Shunqing 	[TBL_IINLIM] = { .rt = {100, 3250000, 100000} }, /* uA */
1011e349be1SChen Shunqing 
1021e349be1SChen Shunqing 	/* lookup tables */
1031e349be1SChen Shunqing 	[TBL_TREG] =	{ .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
1041e349be1SChen Shunqing 	[TBL_BOOSTI] =	{ .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }
1051e349be1SChen Shunqing };
1061e349be1SChen Shunqing 
bq25890_read(struct bq25890 * charger,uint reg)1071e349be1SChen Shunqing static int bq25890_read(struct bq25890 *charger, uint reg)
1081e349be1SChen Shunqing {
1091e349be1SChen Shunqing 	u16 val;
1101e349be1SChen Shunqing 	int ret;
1111e349be1SChen Shunqing 
1121e349be1SChen Shunqing 	ret = dm_i2c_read(charger->dev, reg, (u8 *)&val, 1);
1131e349be1SChen Shunqing 	if (ret) {
1141e349be1SChen Shunqing 		printf("bq25890: read %#x error, ret=%d", reg, ret);
1151e349be1SChen Shunqing 		return ret;
1161e349be1SChen Shunqing 	}
1171e349be1SChen Shunqing 
1181e349be1SChen Shunqing 	return val;
1191e349be1SChen Shunqing }
1201e349be1SChen Shunqing 
bq25890_write(struct bq25890 * charger,uint reg,u16 val)1211e349be1SChen Shunqing static int bq25890_write(struct bq25890 *charger, uint reg, u16 val)
1221e349be1SChen Shunqing {
1231e349be1SChen Shunqing 	int ret;
1241e349be1SChen Shunqing 
1251e349be1SChen Shunqing 	ret = dm_i2c_write(charger->dev, reg, (u8 *)&val, 1);
1261e349be1SChen Shunqing 	if (ret) {
1271e349be1SChen Shunqing 		printf("bq25890: write %#x error, ret=%d", reg, ret);
1281e349be1SChen Shunqing 		return ret;
1291e349be1SChen Shunqing 	}
1301e349be1SChen Shunqing 
1311e349be1SChen Shunqing 	return 0;
1321e349be1SChen Shunqing }
1331e349be1SChen Shunqing 
bq25890_find_idx(u32 value,enum bq25890_table_ids id)1341e349be1SChen Shunqing static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id)
1351e349be1SChen Shunqing {
1361e349be1SChen Shunqing 	u8 idx;
1371e349be1SChen Shunqing 
1381e349be1SChen Shunqing 	if (id >= TBL_TREG) {
1391e349be1SChen Shunqing 		const u32 *tbl = bq25890_tables[id].lt.tbl;
1401e349be1SChen Shunqing 		u32 tbl_size = bq25890_tables[id].lt.size;
1411e349be1SChen Shunqing 
1421e349be1SChen Shunqing 		for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++)
1431e349be1SChen Shunqing 			;
1441e349be1SChen Shunqing 	} else {
1451e349be1SChen Shunqing 		const struct bq25890_range *rtbl = &bq25890_tables[id].rt;
1461e349be1SChen Shunqing 		u8 rtbl_size;
1471e349be1SChen Shunqing 
1481e349be1SChen Shunqing 		rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1;
1491e349be1SChen Shunqing 
1501e349be1SChen Shunqing 		for (idx = 1;
1511e349be1SChen Shunqing 		     idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value);
1521e349be1SChen Shunqing 		     idx++)
1531e349be1SChen Shunqing 			;
1541e349be1SChen Shunqing 	}
1551e349be1SChen Shunqing 
1561e349be1SChen Shunqing 	return idx - 1;
1571e349be1SChen Shunqing }
1581e349be1SChen Shunqing 
bq25890_charger_status(struct bq25890 * charger)1591e349be1SChen Shunqing static bool bq25890_charger_status(struct bq25890 *charger)
1601e349be1SChen Shunqing {
1611e349be1SChen Shunqing 	int state_of_charger;
1621e349be1SChen Shunqing 	u16 value;
163*fb929110SChen Shunqing 	int i = 0;
1641e349be1SChen Shunqing 
165*fb929110SChen Shunqing __retry:
1661e349be1SChen Shunqing 	value = bq25890_read(charger, BQ25890_CHARGERSTAUS_REG);
1671e349be1SChen Shunqing 	state_of_charger = value & 0x04;
168*fb929110SChen Shunqing 	if (!state_of_charger && charger->pd_online) {
169*fb929110SChen Shunqing 		if (i < 3) {
170*fb929110SChen Shunqing 			i++;
171*fb929110SChen Shunqing 			mdelay(20);
172*fb929110SChen Shunqing 			goto __retry;
173*fb929110SChen Shunqing 		}
174*fb929110SChen Shunqing 	}
1751e349be1SChen Shunqing 
1761e349be1SChen Shunqing 	return state_of_charger;
1771e349be1SChen Shunqing }
1781e349be1SChen Shunqing 
bq257xx_charger_status(struct udevice * dev)1791e349be1SChen Shunqing static bool bq257xx_charger_status(struct udevice *dev)
1801e349be1SChen Shunqing {
1811e349be1SChen Shunqing 	struct bq25890 *charger = dev_get_priv(dev);
1821e349be1SChen Shunqing 
1831e349be1SChen Shunqing 	return bq25890_charger_status(charger);
1841e349be1SChen Shunqing }
1851e349be1SChen Shunqing 
bq25890_charger_capability(struct udevice * dev)1861e349be1SChen Shunqing static int bq25890_charger_capability(struct udevice *dev)
1871e349be1SChen Shunqing {
1881e349be1SChen Shunqing 	return FG_CAP_CHARGER;
1891e349be1SChen Shunqing }
1901e349be1SChen Shunqing 
bq25890_get_usb_type(void)1911e349be1SChen Shunqing static int bq25890_get_usb_type(void)
1921e349be1SChen Shunqing {
1931e349be1SChen Shunqing #ifdef CONFIG_PHY_ROCKCHIP_INNO_USB2
1941e349be1SChen Shunqing 	return rockchip_chg_get_type();
1951e349be1SChen Shunqing #else
1961e349be1SChen Shunqing 	return 0;
1971e349be1SChen Shunqing #endif
1981e349be1SChen Shunqing }
1991e349be1SChen Shunqing 
bq25890_get_pd_output_val(struct bq25890 * charger,int * vol,int * cur)2001e349be1SChen Shunqing static int bq25890_get_pd_output_val(struct bq25890 *charger,
2011e349be1SChen Shunqing 				     int *vol, int *cur)
2021e349be1SChen Shunqing {
2031e349be1SChen Shunqing 	struct power_delivery_data pd_data;
2041e349be1SChen Shunqing 	int ret;
2051e349be1SChen Shunqing 
2061e349be1SChen Shunqing 	if (!charger->pd)
2071e349be1SChen Shunqing 		return -EINVAL;
2081e349be1SChen Shunqing 
2091e349be1SChen Shunqing 	memset(&pd_data, 0, sizeof(pd_data));
2101e349be1SChen Shunqing 	ret = power_delivery_get_data(charger->pd, &pd_data);
2111e349be1SChen Shunqing 	if (ret)
2121e349be1SChen Shunqing 		return ret;
2131e349be1SChen Shunqing 	if (!pd_data.online || !pd_data.voltage || !pd_data.current)
2141e349be1SChen Shunqing 		return -EINVAL;
2151e349be1SChen Shunqing 
2161e349be1SChen Shunqing 	*vol = pd_data.voltage;
2171e349be1SChen Shunqing 	*cur = pd_data.current;
218*fb929110SChen Shunqing 	charger->pd_online = pd_data.online;
2191e349be1SChen Shunqing 
2201e349be1SChen Shunqing 	return 0;
2211e349be1SChen Shunqing }
2221e349be1SChen Shunqing 
bq25890_set_auto_dpdm_detect(struct bq25890 * charger,bool enable)2230f0dc534SChen Shunqing static void bq25890_set_auto_dpdm_detect(struct bq25890 *charger, bool enable)
2240f0dc534SChen Shunqing {
2250f0dc534SChen Shunqing 	u8 value;
2260f0dc534SChen Shunqing 
2270f0dc534SChen Shunqing 	value = bq25890_read(charger, BQ25890_AUTO_DPDM_REG);
2280f0dc534SChen Shunqing 	value &= 0xfe;
2290f0dc534SChen Shunqing 	value |= enable;
2300f0dc534SChen Shunqing 	bq25890_write(charger, BQ25890_AUTO_DPDM_REG, value);
2310f0dc534SChen Shunqing }
2320f0dc534SChen Shunqing 
bq25890_charger_current_init(struct bq25890 * charger)2331e349be1SChen Shunqing static void bq25890_charger_current_init(struct bq25890 *charger)
2341e349be1SChen Shunqing {
2351e349be1SChen Shunqing 	u8 charge_current =  bq25890_find_idx(BQ25890_CHARGE_CURRENT_1500MA * 1000, TBL_ICHG);
2361e349be1SChen Shunqing 	u8 sdp_inputcurrent = BQ25890_SDP_INPUT_CURRENT_500MA;
2371e349be1SChen Shunqing 	u8 dcp_inputcurrent = BQ25890_DCP_INPUT_CURRENT_1500MA;
2381e349be1SChen Shunqing 	int pd_inputvol,  pd_inputcurrent;
2391e349be1SChen Shunqing 	u16 vol_idx = 0, cur_idx;
2401e349be1SChen Shunqing 	u8 temp;
2411e349be1SChen Shunqing 
2421e349be1SChen Shunqing 	temp = bq25890_read(charger, BQ25890_CHARGEOPTION0_REG);
2431e349be1SChen Shunqing 	temp &= (~WATCHDOG_ENSABLE);
2441e349be1SChen Shunqing 	bq25890_write(charger, BQ25890_CHARGEOPTION0_REG, temp);
2451e349be1SChen Shunqing 
2461e349be1SChen Shunqing 	if (!bq25890_get_pd_output_val(charger, &pd_inputvol,
2471e349be1SChen Shunqing 				       &pd_inputcurrent)) {
2481e349be1SChen Shunqing 		printf("bq25890: pd charge %duV, %duA\n", pd_inputvol, pd_inputcurrent);
2491e349be1SChen Shunqing 		if (pd_inputvol > 5000000) {
2501e349be1SChen Shunqing 			vol_idx = bq25890_find_idx((pd_inputvol - 1280000 - 3200000),
2511e349be1SChen Shunqing 						   TBL_VINDPM);
2521e349be1SChen Shunqing 			vol_idx = vol_idx << 6;
2531e349be1SChen Shunqing 		}
2541e349be1SChen Shunqing 		cur_idx = bq25890_find_idx(pd_inputcurrent,
2551e349be1SChen Shunqing 					   TBL_IINLIM);
2561e349be1SChen Shunqing 		cur_idx  = cur_idx << 8;
2571e349be1SChen Shunqing 		if (pd_inputcurrent != 0) {
2580f0dc534SChen Shunqing 			bq25890_set_auto_dpdm_detect(charger, false);
2591e349be1SChen Shunqing 			bq25890_write(charger, BQ25890_INPUTCURREN_REG,
2601e349be1SChen Shunqing 				      cur_idx);
2611e349be1SChen Shunqing 			if (vol_idx)
2621e349be1SChen Shunqing 				bq25890_write(charger, BQ25890_INPUTVOLTAGE_REG,
2631e349be1SChen Shunqing 					      vol_idx);
2641e349be1SChen Shunqing 			charge_current = bq25890_find_idx(charger->ichg,
2651e349be1SChen Shunqing 							  TBL_ICHG);
2661e349be1SChen Shunqing 		}
2671e349be1SChen Shunqing 	} else {
2681e349be1SChen Shunqing 		if (bq25890_get_usb_type() > 1)
2691e349be1SChen Shunqing 			bq25890_write(charger, BQ25890_INPUTCURREN_REG,
2701e349be1SChen Shunqing 				      dcp_inputcurrent);
2711e349be1SChen Shunqing 		else
2721e349be1SChen Shunqing 			bq25890_write(charger, BQ25890_INPUTCURREN_REG,
2731e349be1SChen Shunqing 				      sdp_inputcurrent);
2741e349be1SChen Shunqing 	}
2751e349be1SChen Shunqing 
2761e349be1SChen Shunqing 	if (bq25890_charger_status(charger))
2771e349be1SChen Shunqing 		bq25890_write(charger, BQ25890_CHARGECURREN_REG,
2781e349be1SChen Shunqing 			      charge_current);
2791e349be1SChen Shunqing }
2801e349be1SChen Shunqing 
bq25890_ofdata_to_platdata(struct udevice * dev)2811e349be1SChen Shunqing static int bq25890_ofdata_to_platdata(struct udevice *dev)
2821e349be1SChen Shunqing {
2831e349be1SChen Shunqing 	struct bq25890 *charger = dev_get_priv(dev);
2841e349be1SChen Shunqing 
2851e349be1SChen Shunqing 	charger->dev = dev;
2861e349be1SChen Shunqing 	charger->ichg = dev_read_u32_default(dev, "ti,charge-current", 0);
2871e349be1SChen Shunqing 
2881e349be1SChen Shunqing 	return 0;
2891e349be1SChen Shunqing }
2901e349be1SChen Shunqing 
bq25890_probe(struct udevice * dev)2911e349be1SChen Shunqing static int bq25890_probe(struct udevice *dev)
2921e349be1SChen Shunqing {
2931e349be1SChen Shunqing 	struct bq25890 *charger = dev_get_priv(dev);
2941e349be1SChen Shunqing 	int ret;
2951e349be1SChen Shunqing 
2961e349be1SChen Shunqing 	ret = uclass_get_device(UCLASS_PD, 0, &charger->pd);
2971e349be1SChen Shunqing 	if (ret) {
2981e349be1SChen Shunqing 		if (ret == -ENODEV)
2991e349be1SChen Shunqing 			printf("Can't find PD\n");
3001e349be1SChen Shunqing 		else
3011e349be1SChen Shunqing 			printf("Get UCLASS PD failed: %d\n", ret);
3021e349be1SChen Shunqing 
3031e349be1SChen Shunqing 		charger->pd = NULL;
3041e349be1SChen Shunqing 	}
3051e349be1SChen Shunqing 
3061e349be1SChen Shunqing 	bq25890_charger_current_init(charger);
3071e349be1SChen Shunqing 
3081e349be1SChen Shunqing 	return 0;
3091e349be1SChen Shunqing }
3101e349be1SChen Shunqing 
3111e349be1SChen Shunqing static const struct udevice_id charger_ids[] = {
3121e349be1SChen Shunqing 	{ .compatible = "ti,bq25890" },
3131e349be1SChen Shunqing 	{ .compatible = "sy,sy6970" },
3141e349be1SChen Shunqing 	{ },
3151e349be1SChen Shunqing };
3161e349be1SChen Shunqing 
3171e349be1SChen Shunqing static struct dm_fuel_gauge_ops charger_ops = {
3181e349be1SChen Shunqing 	.get_chrg_online = bq257xx_charger_status,
3191e349be1SChen Shunqing 	.capability = bq25890_charger_capability,
3201e349be1SChen Shunqing };
3211e349be1SChen Shunqing 
3221e349be1SChen Shunqing U_BOOT_DRIVER(bq25890_charger) = {
3231e349be1SChen Shunqing 	.name = "bq25890_charger",
3241e349be1SChen Shunqing 	.id = UCLASS_FG,
3251e349be1SChen Shunqing 	.probe = bq25890_probe,
3261e349be1SChen Shunqing 	.of_match = charger_ids,
3271e349be1SChen Shunqing 	.ops = &charger_ops,
3281e349be1SChen Shunqing 	.ofdata_to_platdata = bq25890_ofdata_to_platdata,
3291e349be1SChen Shunqing 	.priv_auto_alloc_size = sizeof(struct bq25890),
3301e349be1SChen Shunqing };
331