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