1 /* 2 * (C) Copyright 2008-2015 Fuzhou Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 #include <common.h> 7 #include <asm/gpio.h> 8 #include <dm.h> 9 #include <dm/device.h> 10 #include <errno.h> 11 #include <fdtdec.h> 12 #include <i2c.h> 13 #include <linux/usb/phy-rockchip-inno-usb2.h> 14 #include <malloc.h> 15 #include <power/battery.h> 16 #include <power/fuel_gauge.h> 17 #include <power/pmic.h> 18 #include "fg_regs.h" 19 20 DECLARE_GLOBAL_DATA_PTR; 21 22 #define COMPAT_ROCKCHIP_CW201X "cw201x" 23 24 #define REG_VERSION 0x0 25 #define REG_VCELL 0x2 26 #define REG_SOC 0x4 27 #define REG_RRT_ALERT 0x6 28 #define REG_CONFIG 0x8 29 #define REG_MODE 0xA 30 #define REG_BATINFO 0x10 31 32 #define MODE_SLEEP_MASK (0x3 << 6) 33 #define MODE_SLEEP (0x3 << 6) 34 #define MODE_NORMAL (0x0 << 6) 35 #define MODE_QUICK_START (0x3 << 4) 36 #define MODE_RESTART (0xf << 0) 37 38 #define CONFIG_UPDATE_FLG (0x1 << 1) 39 #define ATHD (0x0 << 3) 40 41 enum charger_type { 42 CHARGER_TYPE_NO = 0, 43 CHARGER_TYPE_USB, 44 CHARGER_TYPE_AC, 45 CHARGER_TYPE_DC, 46 CHARGER_TYPE_UNDEF, 47 }; 48 49 struct cw201x_info { 50 struct udevice *dev; 51 int capacity; 52 u32 *cw_bat_config_info; 53 int divider_res1; 54 int divider_res2; 55 int hw_id_check; 56 int hw_id0; 57 int hw_id1; 58 int support_dc_adp; 59 int dc_det_gpio; 60 int dc_det_flag; 61 }; 62 63 static u8 cw201x_read(struct cw201x_info *cw201x, u8 reg) 64 { 65 u8 val; 66 int ret; 67 68 ret = dm_i2c_read(cw201x->dev, reg, &val, 1); 69 if (ret) { 70 debug("write error to device: %p register: %#x!", 71 cw201x->dev, reg); 72 return ret; 73 } 74 75 return val; 76 } 77 78 static int cw201x_write(struct cw201x_info *cw201x, u8 reg, u8 val) 79 { 80 int ret; 81 82 ret = dm_i2c_write(cw201x->dev, reg, &val, 1); 83 if (ret) { 84 debug("write error to device: %p register: %#x!", 85 cw201x->dev, reg); 86 return ret; 87 } 88 89 return 0; 90 } 91 92 static u16 cw201x_read_half_word(struct cw201x_info *cw201x, int reg) 93 { 94 u8 vall, valh; 95 u16 val; 96 97 valh = cw201x_read(cw201x, reg); 98 vall = cw201x_read(cw201x, reg + 1); 99 val = ((u16)valh << 8) | vall; 100 101 return val; 102 } 103 104 static int cw201x_ofdata_to_platdata(struct udevice *dev) 105 { 106 const void *blob = gd->fdt_blob; 107 int node = dev_of_offset(dev); 108 struct cw201x_info *cw201x = dev_get_priv(dev); 109 int ret; 110 int len, size; 111 int hw_id0_val, hw_id1_val; 112 113 if (fdt_getprop(blob, node, "bat_config_info", &len)) { 114 len /= sizeof(u32); 115 size = sizeof(*cw201x->cw_bat_config_info) * len; 116 cw201x->cw_bat_config_info = calloc(size, 1); 117 if (!cw201x->cw_bat_config_info) { 118 debug("calloc cw_bat_config_info fail\n"); 119 return -EINVAL; 120 } 121 ret = fdtdec_get_int_array(blob, node, 122 "bat_config_info", 123 cw201x->cw_bat_config_info, len); 124 if (ret) { 125 debug("fdtdec_get cw_bat_config_info fail\n"); 126 return -EINVAL; 127 } 128 } 129 130 cw201x->support_dc_adp = fdtdec_get_int(blob, node, 131 "support_dc_adp", 0); 132 if (cw201x->support_dc_adp) { 133 cw201x->dc_det_gpio = fdtdec_get_int(blob, node, 134 "dc_det_gpio", 0); 135 if (!cw201x->dc_det_gpio) 136 return -EINVAL; 137 gpio_request(cw201x->dc_det_gpio, "dc_det_gpio"); 138 gpio_direction_input(cw201x->dc_det_gpio); 139 140 cw201x->dc_det_flag = fdtdec_get_int(blob, node, 141 "dc_det_flag", 0); 142 } 143 144 cw201x->hw_id_check = fdtdec_get_int(blob, node, "hw_id_check", 0); 145 if (cw201x->hw_id_check) { 146 cw201x->hw_id0 = fdtdec_get_int(blob, node, "hw_id0_gpio", 0); 147 if (!cw201x->hw_id0) 148 return -EINVAL; 149 gpio_request(cw201x->hw_id0, "hw_id0_gpio"); 150 gpio_direction_input(cw201x->hw_id0); 151 hw_id0_val = gpio_get_value(cw201x->hw_id0); 152 153 cw201x->hw_id1 = fdtdec_get_int(blob, node, "hw_id1_gpio", 0); 154 if (!cw201x->hw_id1) 155 return -EINVAL; 156 gpio_request(cw201x->hw_id1, "hw_id1_gpio"); 157 gpio_direction_input(cw201x->hw_id1); 158 hw_id1_val = gpio_get_value(cw201x->hw_id1); 159 160 /* ID1 = 0, ID0 = 1 : Battery */ 161 if (!hw_id0_val || hw_id1_val) 162 return -EINVAL; 163 } 164 165 cw201x->divider_res1 = fdtdec_get_int(blob, node, "divider_res1", 0); 166 cw201x->divider_res2 = fdtdec_get_int(blob, node, "divider_res2", 0); 167 168 return 0; 169 } 170 171 static int cw201x_get_vol(struct cw201x_info *cw201x) 172 { 173 u16 value16, value16_1, value16_2, value16_3; 174 int voltage; 175 int res1, res2; 176 177 value16 = cw201x_read_half_word(cw201x, REG_VCELL); 178 if (value16 < 0) 179 return -1; 180 181 value16_1 = cw201x_read_half_word(cw201x, REG_VCELL); 182 if (value16_1 < 0) 183 return -1; 184 185 value16_2 = cw201x_read_half_word(cw201x, REG_VCELL); 186 if (value16_2 < 0) 187 return -1; 188 189 if (value16 > value16_1) { 190 value16_3 = value16; 191 value16 = value16_1; 192 value16_1 = value16_3; 193 } 194 195 if (value16_1 > value16_2) { 196 value16_3 = value16_1; 197 value16_1 = value16_2; 198 value16_2 = value16_3; 199 } 200 201 if (value16 > value16_1) { 202 value16_3 = value16; 203 value16 = value16_1; 204 value16_1 = value16_3; 205 } 206 207 voltage = value16_1 * 312 / 1024; 208 209 if (cw201x->divider_res1 && 210 cw201x->divider_res2) { 211 res1 = cw201x->divider_res1; 212 res2 = cw201x->divider_res2; 213 voltage = voltage * (res1 + res2) / res2; 214 } 215 216 debug("the cw201x voltage=%d\n", voltage); 217 return voltage; 218 } 219 220 static int cw201x_dwc_otg_check_dpdm(void) 221 { 222 #ifdef CONFIG_PHY_ROCKCHIP_INNO_USB2 223 return rockchip_chg_get_type(); 224 #else 225 debug("rockchip_chg_get_type() is not implement\n"); 226 return CHARGER_TYPE_NO; 227 #endif 228 } 229 230 static int cw201x_get_usb_state(struct cw201x_info *cw201x) 231 { 232 int charger_type; 233 234 switch (cw201x_dwc_otg_check_dpdm()) { 235 case 0: 236 charger_type = CHARGER_TYPE_NO; 237 break; 238 case 1: 239 case 3: 240 charger_type = CHARGER_TYPE_USB; 241 break; 242 case 2: 243 charger_type = CHARGER_TYPE_AC; 244 break; 245 default: 246 charger_type = CHARGER_TYPE_NO; 247 break; 248 } 249 250 return charger_type; 251 } 252 253 static bool cw201x_get_dc_state(struct cw201x_info *cw201x) 254 { 255 if (gpio_get_value(cw201x->dc_det_gpio) == cw201x->dc_det_flag) 256 return true; 257 258 return false; 259 } 260 261 static bool cw201x_check_charge(struct cw201x_info *cw201x) 262 { 263 if (cw201x_get_usb_state(cw201x) != CHARGER_TYPE_NO) 264 return true; 265 if (cw201x_get_dc_state(cw201x)) 266 return true; 267 268 return false; 269 } 270 271 static int cw201x_get_soc(struct cw201x_info *cw201x) 272 { 273 int cap; 274 275 cap = cw201x_read(cw201x, REG_SOC); 276 if ((cap < 0) || (cap > 100)) 277 cap = cw201x->capacity; 278 279 cw201x->capacity = cap; 280 return cw201x->capacity; 281 } 282 283 static int cw201x_update_get_soc(struct udevice *dev) 284 { 285 struct cw201x_info *cw201x = dev_get_priv(dev); 286 287 return cw201x_get_soc(cw201x); 288 } 289 290 static int cw201x_update_get_voltage(struct udevice *dev) 291 { 292 struct cw201x_info *cw201x = dev_get_priv(dev); 293 294 return cw201x_get_vol(cw201x); 295 } 296 297 static bool cw201x_update_get_chrg_online(struct udevice *dev) 298 { 299 struct cw201x_info *cw201x = dev_get_priv(dev); 300 301 return cw201x_check_charge(cw201x); 302 } 303 304 static struct dm_fuel_gauge_ops cw201x_fg_ops = { 305 .get_soc = cw201x_update_get_soc, 306 .get_voltage = cw201x_update_get_voltage, 307 .get_chrg_online = cw201x_update_get_chrg_online, 308 }; 309 310 static int cw201x_fg_cfg(struct cw201x_info *cw201x) 311 { 312 u8 val = MODE_SLEEP; 313 int i; 314 315 if ((val & MODE_SLEEP_MASK) == MODE_SLEEP) { 316 val = MODE_NORMAL; 317 cw201x_write(cw201x, REG_MODE, val); 318 } 319 320 for (i = 0; i < 64; i++) { 321 cw201x_write(cw201x, REG_BATINFO + i, 322 (u8)cw201x->cw_bat_config_info[i]); 323 } 324 325 return 0; 326 } 327 328 static int cw201x_fg_probe(struct udevice *dev) 329 { 330 struct cw201x_info *cw201x = dev_get_priv(dev); 331 332 cw201x->dev = dev; 333 cw201x_fg_cfg(cw201x); 334 335 debug("vol: %d, soc: %d\n", 336 cw201x_get_vol(cw201x), cw201x_get_soc(cw201x)); 337 338 return 0; 339 } 340 341 static const struct udevice_id cw201x_ids[] = { 342 { .compatible = "cw201x" }, 343 { } 344 }; 345 346 U_BOOT_DRIVER(cw201x_fg) = { 347 .name = "cw201x_fg", 348 .id = UCLASS_FG, 349 .of_match = cw201x_ids, 350 .probe = cw201x_fg_probe, 351 .ofdata_to_platdata = cw201x_ofdata_to_platdata, 352 .ops = &cw201x_fg_ops, 353 .priv_auto_alloc_size = sizeof(struct cw201x_info), 354 }; 355