1 /* 2 * (C) Copyright 2019 Fuzhou Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <asm/gpio.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 BQ25700_ID 0x25700 19 #define BQ25703_ID 0x25703 20 21 #define COMPAT_BQ25700 "ti,bq25700" 22 #define COMPAT_BQ25703 "ti,bq25703" 23 24 #define BQ25700_I2C_SPEED 100000 25 #define BQ25700_CHARGE_CURRENT_1500MA 0x5C0 26 #define BQ25700_SDP_INPUT_CURRENT_500MA 0xA00 27 #define BQ25700_DCP_INPUT_CURRENT_1500MA 0x1E00 28 #define BQ25700_DCP_INPUT_CURRENT_2000MA 0x2800 29 #define BQ25700_DCP_INPUT_CURRENT_3000MA 0x3C00 30 31 #define WATCHDOG_ENSABLE (0x03 << 13) 32 33 #define BQ25700_CHARGEOPTION0_REG 0x12 34 #define BQ25700_CHARGECURREN_REG 0x14 35 #define BQ25700_CHARGERSTAUS_REG 0x20 36 #define BQ25700_INPUTVOLTAGE_REG 0x3D 37 #define BQ25700_INPUTCURREN_REG 0x3F 38 39 #define BQ25703_CHARGEOPTION0_REG 0x00 40 #define BQ25703_CHARGECURREN_REG 0x02 41 #define BQ25703_CHARGERSTAUS_REG 0x20 42 #define BQ25703_INPUTVOLTAGE_REG 0x0A 43 #define BQ25703_INPUTCURREN_REG 0x0E 44 45 enum bq25700_table_ids { 46 /* range tables */ 47 TBL_ICHG, 48 TBL_CHGMAX, 49 TBL_INPUTVOL, 50 TBL_INPUTCUR, 51 TBL_SYSVMIN, 52 TBL_OTGVOL, 53 TBL_OTGCUR, 54 TBL_EXTCON, 55 }; 56 57 struct bq25700 { 58 struct udevice *dev; 59 u32 ichg; 60 u32 chip_id; 61 struct udevice *pd; 62 }; 63 64 struct bq25700_range { 65 u32 min; 66 u32 max; 67 u32 step; 68 }; 69 70 static int bq25700_read(struct bq25700 *charger, uint reg) 71 { 72 u16 val; 73 int ret; 74 75 ret = dm_i2c_read(charger->dev, reg, (u8 *)&val, 2); 76 if (ret) { 77 debug("write error to device: %p register: %#x!", 78 charger->dev, reg); 79 return ret; 80 } 81 82 return val; 83 } 84 85 static int bq25700_write(struct bq25700 *charger, uint reg, u16 val) 86 { 87 int ret; 88 89 ret = dm_i2c_write(charger->dev, reg, (u8 *)&val, 2); 90 if (ret) { 91 debug("write error to device: %p register: %#x!", 92 charger->dev, reg); 93 return ret; 94 } 95 96 return 0; 97 } 98 99 static const union { 100 struct bq25700_range rt; 101 } bq25700_tables[] = { 102 /* range tables */ 103 [TBL_ICHG] = { .rt = {0, 8128000, 64000} }, 104 /* uV */ 105 [TBL_CHGMAX] = { .rt = {0, 19200000, 16000} }, 106 /* uV max charge voltage*/ 107 [TBL_INPUTVOL] = { .rt = {3200000, 19520000, 64000} }, 108 /* uV input charge voltage*/ 109 [TBL_INPUTCUR] = {.rt = {0, 6350000, 50000} }, 110 /*uA input current*/ 111 [TBL_SYSVMIN] = { .rt = {1024000, 16182000, 256000} }, 112 /* uV min system voltage*/ 113 [TBL_OTGVOL] = {.rt = {4480000, 20800000, 64000} }, 114 /*uV OTG volage*/ 115 [TBL_OTGCUR] = {.rt = {0, 6350000, 50000} }, 116 }; 117 118 static u32 bq25700_find_idx(u32 value, enum bq25700_table_ids id) 119 { 120 const struct bq25700_range *rtbl = &bq25700_tables[id].rt; 121 u32 rtbl_size; 122 u32 idx; 123 124 rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1; 125 126 for (idx = 1; 127 idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value); 128 idx++) 129 ; 130 131 return idx - 1; 132 } 133 134 static bool bq25700_charger_status(struct bq25700 *charger) 135 { 136 int state_of_charger; 137 u16 value; 138 139 value = bq25700_read(charger, BQ25700_CHARGERSTAUS_REG); 140 state_of_charger = value >> 15; 141 142 return state_of_charger; 143 } 144 145 static bool bq25703_charger_status(struct bq25700 *charger) 146 { 147 int state_of_charger; 148 u16 value; 149 150 value = bq25700_read(charger, BQ25703_CHARGERSTAUS_REG); 151 state_of_charger = value >> 15; 152 153 return state_of_charger; 154 } 155 156 static bool bq257xx_charger_status(struct udevice *dev) 157 { 158 struct bq25700 *charger = dev_get_priv(dev); 159 160 if (charger->chip_id == BQ25700_ID) 161 return bq25700_charger_status(charger); 162 else 163 return bq25703_charger_status(charger); 164 } 165 166 static int bq25700_charger_capability(struct udevice *dev) 167 { 168 return FG_CAP_CHARGER; 169 } 170 171 static int bq25700_get_usb_type(void) 172 { 173 #ifdef CONFIG_PHY_ROCKCHIP_INNO_USB2 174 return rockchip_chg_get_type(); 175 #else 176 return 0; 177 #endif 178 } 179 180 static int bq25700_get_pd_output_val(struct bq25700 *charger, 181 int *vol, int *cur) 182 { 183 struct power_delivery_data pd_data; 184 int ret; 185 186 if (!charger->pd) 187 return -EINVAL; 188 189 memset(&pd_data, 0, sizeof(pd_data)); 190 ret = power_delivery_get_data(charger->pd, &pd_data); 191 if (ret) 192 return ret; 193 if (!pd_data.online || !pd_data.voltage || !pd_data.current) 194 return -EINVAL; 195 196 *vol = pd_data.voltage; 197 *cur = pd_data.current; 198 199 return 0; 200 } 201 202 static void bq25700_charger_current_init(struct bq25700 *charger) 203 { 204 u16 charge_current = BQ25700_CHARGE_CURRENT_1500MA; 205 u16 sdp_inputcurrent = BQ25700_SDP_INPUT_CURRENT_500MA; 206 u16 dcp_inputcurrent = BQ25700_DCP_INPUT_CURRENT_1500MA; 207 int pd_inputvol, pd_inputcurrent; 208 u16 vol_idx = 0, cur_idx; 209 u16 temp; 210 211 temp = bq25700_read(charger, BQ25700_CHARGEOPTION0_REG); 212 temp &= (~WATCHDOG_ENSABLE); 213 bq25700_write(charger, BQ25700_CHARGEOPTION0_REG, temp); 214 215 if (!bq25700_get_pd_output_val(charger, &pd_inputvol, 216 &pd_inputcurrent)) { 217 printf("%s pd charge input vol:%duv current:%dua\n", 218 __func__, pd_inputvol, pd_inputcurrent); 219 if (pd_inputvol > 5000000) { 220 vol_idx = bq25700_find_idx((pd_inputvol - 1280000 - 3200000), 221 TBL_INPUTVOL); 222 vol_idx = vol_idx << 6; 223 } 224 cur_idx = bq25700_find_idx(pd_inputcurrent, 225 TBL_INPUTCUR); 226 cur_idx = cur_idx << 8; 227 if (pd_inputcurrent != 0) { 228 bq25700_write(charger, BQ25700_INPUTCURREN_REG, 229 cur_idx); 230 if (vol_idx) 231 bq25700_write(charger, BQ25700_INPUTVOLTAGE_REG, 232 vol_idx); 233 charge_current = bq25700_find_idx(charger->ichg, 234 TBL_ICHG); 235 charge_current = charge_current << 8; 236 } 237 } else { 238 if (bq25700_get_usb_type() > 1) 239 bq25700_write(charger, BQ25700_INPUTCURREN_REG, 240 dcp_inputcurrent); 241 else 242 bq25700_write(charger, BQ25700_INPUTCURREN_REG, 243 sdp_inputcurrent); 244 } 245 246 if (bq25700_charger_status(charger)) 247 bq25700_write(charger, BQ25700_CHARGECURREN_REG, 248 charge_current); 249 } 250 251 static void bq25703_charger_current_init(struct bq25700 *charger) 252 { 253 u16 charge_current = BQ25700_CHARGE_CURRENT_1500MA; 254 u16 sdp_inputcurrent = BQ25700_SDP_INPUT_CURRENT_500MA; 255 u16 dcp_inputcurrent = BQ25700_DCP_INPUT_CURRENT_1500MA; 256 int pd_inputvol, pd_inputcurrent; 257 u16 vol_idx = 0, cur_idx; 258 u16 temp; 259 260 temp = bq25700_read(charger, BQ25703_CHARGEOPTION0_REG); 261 temp &= (~WATCHDOG_ENSABLE); 262 bq25700_write(charger, BQ25703_CHARGEOPTION0_REG, temp); 263 264 if (!bq25700_get_pd_output_val(charger, &pd_inputvol, 265 &pd_inputcurrent)) { 266 printf("%s pd charge input vol:%duv current:%dua\n", 267 __func__, pd_inputvol, pd_inputcurrent); 268 if (pd_inputvol > 5000000) { 269 vol_idx = bq25700_find_idx(pd_inputvol - 1280000 - 3200000, 270 TBL_INPUTVOL); 271 vol_idx = vol_idx << 6; 272 } 273 cur_idx = bq25700_find_idx(pd_inputcurrent, 274 TBL_INPUTCUR); 275 cur_idx = cur_idx << 8; 276 if (pd_inputcurrent != 0) { 277 bq25700_write(charger, BQ25703_INPUTCURREN_REG, 278 cur_idx); 279 if (vol_idx) 280 bq25700_write(charger, BQ25703_INPUTVOLTAGE_REG, 281 vol_idx); 282 charge_current = bq25700_find_idx(charger->ichg, 283 TBL_ICHG); 284 charge_current = charge_current << 8; 285 } 286 } else { 287 if (bq25700_get_usb_type() > 1) 288 bq25700_write(charger, BQ25703_INPUTCURREN_REG, 289 dcp_inputcurrent); 290 else 291 bq25700_write(charger, BQ25703_INPUTCURREN_REG, 292 sdp_inputcurrent); 293 } 294 295 if (bq25703_charger_status(charger)) 296 bq25700_write(charger, BQ25703_CHARGECURREN_REG, 297 charge_current); 298 } 299 300 static int bq25700_ofdata_to_platdata(struct udevice *dev) 301 { 302 struct bq25700 *charger = dev_get_priv(dev); 303 const void *blob = gd->fdt_blob; 304 int node, node1; 305 306 charger->dev = dev; 307 308 node = fdt_node_offset_by_compatible(blob, 0, COMPAT_BQ25700); 309 node1 = fdt_node_offset_by_compatible(blob, 0, COMPAT_BQ25703); 310 if ((node < 0) && (node1 < 0)) { 311 printf("Can't find dts node for charger bq25700\n"); 312 return -ENODEV; 313 } 314 315 if (node < 0) { 316 node = node1; 317 charger->chip_id = BQ25703_ID; 318 } else { 319 charger->chip_id = BQ25700_ID; 320 } 321 322 charger->ichg = fdtdec_get_int(blob, node, "ti,charge-current", 0); 323 324 return 0; 325 } 326 327 static int bq25700_probe(struct udevice *dev) 328 { 329 struct bq25700 *charger = dev_get_priv(dev); 330 int ret; 331 332 ret = uclass_get_device(UCLASS_PD, 0, &charger->pd); 333 if (ret) { 334 if (ret == -ENODEV) 335 printf("Can't find PMIC\n"); 336 else 337 printf("Get UCLASS PMIC failed: %d\n", ret); 338 339 charger->pd = NULL; 340 } 341 342 if (charger->chip_id == BQ25700_ID) 343 bq25700_charger_current_init(charger); 344 else 345 bq25703_charger_current_init(charger); 346 347 return 0; 348 } 349 350 static const struct udevice_id charger_ids[] = { 351 { .compatible = "ti,bq25700" }, 352 { .compatible = "ti,bq25703" }, 353 { }, 354 }; 355 356 static struct dm_fuel_gauge_ops charger_ops = { 357 .get_chrg_online = bq257xx_charger_status, 358 .capability = bq25700_charger_capability, 359 }; 360 361 U_BOOT_DRIVER(bq25700_charger) = { 362 .name = "bq25700_charger", 363 .id = UCLASS_FG, 364 .probe = bq25700_probe, 365 .of_match = charger_ids, 366 .ops = &charger_ops, 367 .ofdata_to_platdata = bq25700_ofdata_to_platdata, 368 .priv_auto_alloc_size = sizeof(struct bq25700), 369 }; 370