1 /* 2 * (C) Copyright 2022 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <dm.h> 9 #include <dm/device.h> 10 #include <dm/uclass.h> 11 #include <power/fuel_gauge.h> 12 #include <power/pmic.h> 13 #include <power/power_delivery/power_delivery.h> 14 #include <linux/usb/phy-rockchip-usb2.h> 15 16 DECLARE_GLOBAL_DATA_PTR; 17 18 #define BQ25890_CHARGE_CURRENT_1500MA 1500 19 #define BQ25890_SDP_INPUT_CURRENT_500MA 0x45 20 #define BQ25890_DCP_INPUT_CURRENT_1500MA 0x4f 21 #define BQ25890_DCP_INPUT_CURRENT_2000MA 0x54 22 #define BQ25890_DCP_INPUT_CURRENT_3000MA 0x5e 23 24 #define WATCHDOG_ENSABLE (0x03 << 4) 25 26 #define BQ25890_CHARGEOPTION0_REG 0x07 27 #define BQ25890_CHARGECURREN_REG 0x04 28 #define BQ25890_CHARGERSTAUS_REG 0x0B 29 #define BQ25890_INPUTVOLTAGE_REG 0x0D 30 #define BQ25890_INPUTCURREN_REG 0x00 31 #define BQ25890_AUTO_DPDM_REG 0x02 32 33 /* 34 * Most of the val -> idx conversions can be computed, given the minimum, 35 * maximum and the step between values. For the rest of conversions, we use 36 * lookup tables. 37 */ 38 enum bq25890_table_ids { 39 /* range tables */ 40 TBL_ICHG, 41 TBL_ITERM, 42 TBL_IPRECHG, 43 TBL_VREG, 44 TBL_BATCMP, 45 TBL_VCLAMP, 46 TBL_BOOSTV, 47 TBL_SYSVMIN, 48 TBL_VINDPM, 49 TBL_IINLIM, 50 51 /* lookup tables */ 52 TBL_TREG, 53 TBL_BOOSTI, 54 }; 55 56 struct bq25890 { 57 struct udevice *dev; 58 u32 ichg; 59 u32 chip_id; 60 struct udevice *pd; 61 bool pd_online; 62 }; 63 64 /* Thermal Regulation Threshold lookup table, in degrees Celsius */ 65 static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 }; 66 67 #define BQ25890_TREG_TBL_SIZE ARRAY_SIZE(bq25890_treg_tbl) 68 69 /* Boost mode current limit lookup table, in uA */ 70 static const u32 bq25890_boosti_tbl[] = { 71 500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000 72 }; 73 74 #define BQ25890_BOOSTI_TBL_SIZE ARRAY_SIZE(bq25890_boosti_tbl) 75 76 struct bq25890_range { 77 u32 min; 78 u32 max; 79 u32 step; 80 }; 81 82 struct bq25890_lookup { 83 const u32 *tbl; 84 u32 size; 85 }; 86 87 static const union { 88 struct bq25890_range rt; 89 struct bq25890_lookup lt; 90 } bq25890_tables[] = { 91 /* range tables */ 92 [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ 93 [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ 94 [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ 95 [TBL_BATCMP] = { .rt = {0, 140, 20} }, /* mOhm */ 96 [TBL_VCLAMP] = { .rt = {0, 224000, 32000} }, /* uV */ 97 [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ 98 [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ 99 [TBL_VINDPM] = { .rt = {2600000, 15300000, 100000} }, /* uV */ 100 [TBL_IINLIM] = { .rt = {100, 3250000, 100000} }, /* uA */ 101 102 /* lookup tables */ 103 [TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} }, 104 [TBL_BOOSTI] = { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} } 105 }; 106 107 static int bq25890_read(struct bq25890 *charger, uint reg) 108 { 109 u16 val; 110 int ret; 111 112 ret = dm_i2c_read(charger->dev, reg, (u8 *)&val, 1); 113 if (ret) { 114 printf("bq25890: read %#x error, ret=%d", reg, ret); 115 return ret; 116 } 117 118 return val; 119 } 120 121 static int bq25890_write(struct bq25890 *charger, uint reg, u16 val) 122 { 123 int ret; 124 125 ret = dm_i2c_write(charger->dev, reg, (u8 *)&val, 1); 126 if (ret) { 127 printf("bq25890: write %#x error, ret=%d", reg, ret); 128 return ret; 129 } 130 131 return 0; 132 } 133 134 static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id) 135 { 136 u8 idx; 137 138 if (id >= TBL_TREG) { 139 const u32 *tbl = bq25890_tables[id].lt.tbl; 140 u32 tbl_size = bq25890_tables[id].lt.size; 141 142 for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++) 143 ; 144 } else { 145 const struct bq25890_range *rtbl = &bq25890_tables[id].rt; 146 u8 rtbl_size; 147 148 rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1; 149 150 for (idx = 1; 151 idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value); 152 idx++) 153 ; 154 } 155 156 return idx - 1; 157 } 158 159 static bool bq25890_charger_status(struct bq25890 *charger) 160 { 161 int state_of_charger; 162 u16 value; 163 int i = 0; 164 165 __retry: 166 value = bq25890_read(charger, BQ25890_CHARGERSTAUS_REG); 167 state_of_charger = value & 0x04; 168 if (!state_of_charger && charger->pd_online) { 169 if (i < 3) { 170 i++; 171 mdelay(20); 172 goto __retry; 173 } 174 } 175 176 return state_of_charger; 177 } 178 179 static bool bq257xx_charger_status(struct udevice *dev) 180 { 181 struct bq25890 *charger = dev_get_priv(dev); 182 183 return bq25890_charger_status(charger); 184 } 185 186 static int bq25890_charger_capability(struct udevice *dev) 187 { 188 return FG_CAP_CHARGER; 189 } 190 191 static int bq25890_get_usb_type(void) 192 { 193 #ifdef CONFIG_PHY_ROCKCHIP_INNO_USB2 194 return rockchip_chg_get_type(); 195 #else 196 return 0; 197 #endif 198 } 199 200 static int bq25890_get_pd_output_val(struct bq25890 *charger, 201 int *vol, int *cur) 202 { 203 struct power_delivery_data pd_data; 204 int ret; 205 206 if (!charger->pd) 207 return -EINVAL; 208 209 memset(&pd_data, 0, sizeof(pd_data)); 210 ret = power_delivery_get_data(charger->pd, &pd_data); 211 if (ret) 212 return ret; 213 if (!pd_data.online || !pd_data.voltage || !pd_data.current) 214 return -EINVAL; 215 216 *vol = pd_data.voltage; 217 *cur = pd_data.current; 218 charger->pd_online = pd_data.online; 219 220 return 0; 221 } 222 223 static void bq25890_set_auto_dpdm_detect(struct bq25890 *charger, bool enable) 224 { 225 u8 value; 226 227 value = bq25890_read(charger, BQ25890_AUTO_DPDM_REG); 228 value &= 0xfe; 229 value |= enable; 230 bq25890_write(charger, BQ25890_AUTO_DPDM_REG, value); 231 } 232 233 static void bq25890_charger_current_init(struct bq25890 *charger) 234 { 235 u8 charge_current = bq25890_find_idx(BQ25890_CHARGE_CURRENT_1500MA * 1000, TBL_ICHG); 236 u8 sdp_inputcurrent = BQ25890_SDP_INPUT_CURRENT_500MA; 237 u8 dcp_inputcurrent = BQ25890_DCP_INPUT_CURRENT_1500MA; 238 int pd_inputvol, pd_inputcurrent; 239 u16 vol_idx = 0, cur_idx; 240 u8 temp; 241 242 temp = bq25890_read(charger, BQ25890_CHARGEOPTION0_REG); 243 temp &= (~WATCHDOG_ENSABLE); 244 bq25890_write(charger, BQ25890_CHARGEOPTION0_REG, temp); 245 246 if (!bq25890_get_pd_output_val(charger, &pd_inputvol, 247 &pd_inputcurrent)) { 248 printf("bq25890: pd charge %duV, %duA\n", pd_inputvol, pd_inputcurrent); 249 if (pd_inputvol > 5000000) { 250 vol_idx = bq25890_find_idx((pd_inputvol - 1280000 - 3200000), 251 TBL_VINDPM); 252 vol_idx = vol_idx << 6; 253 } 254 cur_idx = bq25890_find_idx(pd_inputcurrent, 255 TBL_IINLIM); 256 cur_idx = cur_idx << 8; 257 if (pd_inputcurrent != 0) { 258 bq25890_set_auto_dpdm_detect(charger, false); 259 bq25890_write(charger, BQ25890_INPUTCURREN_REG, 260 cur_idx); 261 if (vol_idx) 262 bq25890_write(charger, BQ25890_INPUTVOLTAGE_REG, 263 vol_idx); 264 charge_current = bq25890_find_idx(charger->ichg, 265 TBL_ICHG); 266 } 267 } else { 268 if (bq25890_get_usb_type() > 1) 269 bq25890_write(charger, BQ25890_INPUTCURREN_REG, 270 dcp_inputcurrent); 271 else 272 bq25890_write(charger, BQ25890_INPUTCURREN_REG, 273 sdp_inputcurrent); 274 } 275 276 if (bq25890_charger_status(charger)) 277 bq25890_write(charger, BQ25890_CHARGECURREN_REG, 278 charge_current); 279 } 280 281 static int bq25890_ofdata_to_platdata(struct udevice *dev) 282 { 283 struct bq25890 *charger = dev_get_priv(dev); 284 285 charger->dev = dev; 286 charger->ichg = dev_read_u32_default(dev, "ti,charge-current", 0); 287 288 return 0; 289 } 290 291 static int bq25890_probe(struct udevice *dev) 292 { 293 struct bq25890 *charger = dev_get_priv(dev); 294 int ret; 295 296 ret = uclass_get_device(UCLASS_PD, 0, &charger->pd); 297 if (ret) { 298 if (ret == -ENODEV) 299 printf("Can't find PD\n"); 300 else 301 printf("Get UCLASS PD failed: %d\n", ret); 302 303 charger->pd = NULL; 304 } 305 306 bq25890_charger_current_init(charger); 307 308 return 0; 309 } 310 311 static const struct udevice_id charger_ids[] = { 312 { .compatible = "ti,bq25890" }, 313 { .compatible = "sy,sy6970" }, 314 { }, 315 }; 316 317 static struct dm_fuel_gauge_ops charger_ops = { 318 .get_chrg_online = bq257xx_charger_status, 319 .capability = bq25890_charger_capability, 320 }; 321 322 U_BOOT_DRIVER(bq25890_charger) = { 323 .name = "bq25890_charger", 324 .id = UCLASS_FG, 325 .probe = bq25890_probe, 326 .of_match = charger_ids, 327 .ops = &charger_ops, 328 .ofdata_to_platdata = bq25890_ofdata_to_platdata, 329 .priv_auto_alloc_size = sizeof(struct bq25890), 330 }; 331