Bryan Huntsman | 9a702c5 | 2019-01-11 16:49:48 -0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Guru Das Srinagesh | 62006d117 | 2018-11-13 10:00:42 -0800 | [diff] [blame] | 2 | /* |
Anirudh Ghayal | c16382a | 2020-01-02 16:05:08 +0530 | [diff] [blame] | 3 | * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. |
Guru Das Srinagesh | 62006d117 | 2018-11-13 10:00:42 -0800 | [diff] [blame] | 4 | */ |
| 5 | |
| 6 | #include <linux/alarmtimer.h> |
| 7 | #include <linux/cdev.h> |
| 8 | #include <linux/device.h> |
| 9 | #include <linux/interrupt.h> |
| 10 | #include <linux/power_supply.h> |
| 11 | #include <linux/regmap.h> |
| 12 | #include <linux/rtc.h> |
| 13 | #include <linux/iio/consumer.h> |
| 14 | #include <uapi/linux/qg.h> |
| 15 | #include "qg-sdam.h" |
| 16 | #include "qg-core.h" |
| 17 | #include "qg-reg.h" |
| 18 | #include "qg-defs.h" |
| 19 | #include "qg-util.h" |
| 20 | |
| 21 | static inline bool is_sticky_register(u32 addr) |
| 22 | { |
| 23 | if ((addr & 0xFF) == QG_STATUS2_REG) |
| 24 | return true; |
| 25 | |
| 26 | return false; |
| 27 | } |
| 28 | |
| 29 | int qg_read(struct qpnp_qg *chip, u32 addr, u8 *val, int len) |
| 30 | { |
| 31 | int rc, i; |
| 32 | u32 dummy = 0; |
| 33 | |
| 34 | rc = regmap_bulk_read(chip->regmap, addr, val, len); |
| 35 | if (rc < 0) { |
| 36 | pr_err("Failed regmap_read for address %04x rc=%d\n", addr, rc); |
| 37 | return rc; |
| 38 | } |
| 39 | |
| 40 | if (is_sticky_register(addr)) { |
| 41 | /* write to the sticky register to clear it */ |
| 42 | rc = regmap_write(chip->regmap, addr, dummy); |
| 43 | if (rc < 0) { |
| 44 | pr_err("Failed regmap_write for %04x rc=%d\n", |
| 45 | addr, rc); |
| 46 | return rc; |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | if (*chip->debug_mask & QG_DEBUG_BUS_READ) { |
| 51 | pr_info("length %d addr=%04x\n", len, addr); |
| 52 | for (i = 0; i < len; i++) |
| 53 | pr_info("val[%d]: %02x\n", i, val[i]); |
| 54 | } |
| 55 | |
| 56 | return 0; |
| 57 | } |
| 58 | |
| 59 | int qg_write(struct qpnp_qg *chip, u32 addr, u8 *val, int len) |
| 60 | { |
| 61 | int rc, i; |
| 62 | |
| 63 | mutex_lock(&chip->bus_lock); |
| 64 | |
| 65 | if (len > 1) |
| 66 | rc = regmap_bulk_write(chip->regmap, addr, val, len); |
| 67 | else |
| 68 | rc = regmap_write(chip->regmap, addr, *val); |
| 69 | |
| 70 | if (rc < 0) { |
| 71 | pr_err("Failed regmap_write for address %04x rc=%d\n", |
| 72 | addr, rc); |
| 73 | goto out; |
| 74 | } |
| 75 | |
| 76 | if (*chip->debug_mask & QG_DEBUG_BUS_WRITE) { |
| 77 | pr_info("length %d addr=%04x\n", len, addr); |
| 78 | for (i = 0; i < len; i++) |
| 79 | pr_info("val[%d]: %02x\n", i, val[i]); |
| 80 | } |
| 81 | out: |
| 82 | mutex_unlock(&chip->bus_lock); |
| 83 | return rc; |
| 84 | } |
| 85 | |
| 86 | int qg_masked_write(struct qpnp_qg *chip, int addr, u32 mask, u32 val) |
| 87 | { |
| 88 | int rc; |
| 89 | |
| 90 | mutex_lock(&chip->bus_lock); |
| 91 | |
| 92 | rc = regmap_update_bits(chip->regmap, addr, mask, val); |
| 93 | if (rc < 0) { |
| 94 | pr_err("Failed regmap_update_bits for address %04x rc=%d\n", |
| 95 | addr, rc); |
| 96 | goto out; |
| 97 | } |
| 98 | |
| 99 | if (*chip->debug_mask & QG_DEBUG_BUS_WRITE) |
| 100 | pr_info("addr=%04x mask: %02x val: %02x\n", addr, mask, val); |
| 101 | |
| 102 | out: |
| 103 | mutex_unlock(&chip->bus_lock); |
| 104 | return rc; |
| 105 | } |
| 106 | |
| 107 | int qg_read_raw_data(struct qpnp_qg *chip, int addr, u32 *data) |
| 108 | { |
| 109 | int rc; |
| 110 | u8 reg[2] = {0}; |
| 111 | |
| 112 | rc = qg_read(chip, chip->qg_base + addr, ®[0], 2); |
| 113 | if (rc < 0) { |
| 114 | pr_err("Failed to read QG addr %d rc=%d\n", addr, rc); |
| 115 | return rc; |
| 116 | } |
| 117 | |
| 118 | *data = reg[0] | (reg[1] << 8); |
| 119 | |
| 120 | return rc; |
| 121 | } |
| 122 | |
| 123 | s64 qg_iraw_to_ua(struct qpnp_qg *chip, int iraw) |
| 124 | { |
| 125 | if (chip->qg_subtype == QG_ADC_IBAT_5A) |
| 126 | return div_s64(152588LL * (s64)iraw, 1000); |
| 127 | else |
| 128 | return div_s64(305176LL * (s64)iraw, 1000); |
| 129 | } |
| 130 | |
| 131 | int get_fifo_length(struct qpnp_qg *chip, u32 *fifo_length, bool rt) |
| 132 | { |
| 133 | int rc; |
| 134 | u8 reg = 0; |
| 135 | u32 addr; |
| 136 | |
| 137 | addr = rt ? QG_STATUS3_REG : QG_S2_NORMAL_MEAS_CTL2_REG; |
| 138 | rc = qg_read(chip, chip->qg_base + addr, ®, 1); |
| 139 | if (rc < 0) { |
| 140 | pr_err("Failed to read FIFO length rc=%d\n", rc); |
| 141 | return rc; |
| 142 | } |
| 143 | |
| 144 | if (rt) { |
| 145 | *fifo_length = reg & COUNT_FIFO_RT_MASK; |
| 146 | } else { |
| 147 | *fifo_length = (reg & FIFO_LENGTH_MASK) >> FIFO_LENGTH_SHIFT; |
| 148 | *fifo_length += 1; |
| 149 | } |
| 150 | |
| 151 | return rc; |
| 152 | } |
| 153 | |
| 154 | int get_sample_count(struct qpnp_qg *chip, u32 *sample_count) |
| 155 | { |
| 156 | int rc; |
| 157 | u8 reg = 0; |
| 158 | |
| 159 | rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL2_REG, |
| 160 | ®, 1); |
| 161 | if (rc < 0) { |
| 162 | pr_err("Failed to read FIFO sample count rc=%d\n", rc); |
| 163 | return rc; |
| 164 | } |
| 165 | |
| 166 | *sample_count = 1 << ((reg & NUM_OF_ACCUM_MASK) + 1); |
| 167 | |
| 168 | return rc; |
| 169 | } |
| 170 | |
| 171 | #define QG_CLK_RATE 32000 |
| 172 | #define QG_ACTUAL_CLK_RATE 32764 |
| 173 | int get_sample_interval(struct qpnp_qg *chip, u32 *sample_interval) |
| 174 | { |
| 175 | int rc; |
| 176 | u8 reg = 0; |
| 177 | |
| 178 | rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL3_REG, |
| 179 | ®, 1); |
| 180 | if (rc < 0) { |
| 181 | pr_err("Failed to read FIFO sample interval rc=%d\n", rc); |
| 182 | return rc; |
| 183 | } |
| 184 | |
| 185 | *sample_interval = reg * 10; |
| 186 | |
| 187 | if (chip->wa_flags & QG_CLK_ADJUST_WA) { |
| 188 | *sample_interval = DIV_ROUND_CLOSEST( |
| 189 | *sample_interval * QG_CLK_RATE, QG_ACTUAL_CLK_RATE); |
| 190 | } |
| 191 | |
| 192 | return rc; |
| 193 | } |
| 194 | |
| 195 | int get_rtc_time(unsigned long *rtc_time) |
| 196 | { |
| 197 | struct rtc_time tm; |
| 198 | struct rtc_device *rtc; |
| 199 | int rc; |
| 200 | |
| 201 | rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); |
| 202 | if (rtc == NULL) { |
| 203 | pr_err("Failed to open rtc device (%s)\n", |
| 204 | CONFIG_RTC_HCTOSYS_DEVICE); |
| 205 | return -EINVAL; |
| 206 | } |
| 207 | |
| 208 | rc = rtc_read_time(rtc, &tm); |
| 209 | if (rc) { |
| 210 | pr_err("Failed to read rtc time (%s) : %d\n", |
| 211 | CONFIG_RTC_HCTOSYS_DEVICE, rc); |
| 212 | goto close_time; |
| 213 | } |
| 214 | |
| 215 | rc = rtc_valid_tm(&tm); |
| 216 | if (rc) { |
| 217 | pr_err("Invalid RTC time (%s): %d\n", |
| 218 | CONFIG_RTC_HCTOSYS_DEVICE, rc); |
| 219 | goto close_time; |
| 220 | } |
| 221 | rtc_tm_to_time(&tm, rtc_time); |
| 222 | |
| 223 | close_time: |
| 224 | rtc_class_close(rtc); |
| 225 | return rc; |
| 226 | } |
| 227 | |
| 228 | int get_fifo_done_time(struct qpnp_qg *chip, bool rt, int *time_ms) |
| 229 | { |
| 230 | int rc, length = 0; |
| 231 | u32 sample_count = 0, sample_interval = 0, acc_count = 0; |
| 232 | |
| 233 | rc = get_fifo_length(chip, &length, rt ? true : false); |
| 234 | if (rc < 0) |
| 235 | return rc; |
| 236 | |
| 237 | rc = get_sample_count(chip, &sample_count); |
| 238 | if (rc < 0) |
| 239 | return rc; |
| 240 | |
| 241 | rc = get_sample_interval(chip, &sample_interval); |
| 242 | if (rc < 0) |
| 243 | return rc; |
| 244 | |
| 245 | *time_ms = length * sample_count * sample_interval; |
| 246 | |
| 247 | if (rt) { |
| 248 | rc = qg_read(chip, chip->qg_base + QG_ACCUM_CNT_RT_REG, |
| 249 | (u8 *)&acc_count, 1); |
| 250 | if (rc < 0) |
| 251 | return rc; |
| 252 | |
| 253 | *time_ms += ((sample_count - acc_count) * sample_interval); |
| 254 | } |
| 255 | |
| 256 | return 0; |
| 257 | } |
| 258 | |
| 259 | static bool is_usb_available(struct qpnp_qg *chip) |
| 260 | { |
| 261 | if (chip->usb_psy) |
| 262 | return true; |
| 263 | |
| 264 | chip->usb_psy = power_supply_get_by_name("usb"); |
| 265 | if (!chip->usb_psy) |
| 266 | return false; |
| 267 | |
| 268 | return true; |
| 269 | } |
| 270 | |
Anirudh Ghayal | 0875fed | 2019-01-15 15:54:30 +0530 | [diff] [blame] | 271 | static bool is_dc_available(struct qpnp_qg *chip) |
| 272 | { |
| 273 | if (chip->dc_psy) |
| 274 | return true; |
| 275 | |
| 276 | chip->dc_psy = power_supply_get_by_name("dc"); |
| 277 | if (!chip->dc_psy) |
| 278 | return false; |
| 279 | |
| 280 | return true; |
| 281 | } |
| 282 | |
Guru Das Srinagesh | 62006d117 | 2018-11-13 10:00:42 -0800 | [diff] [blame] | 283 | bool is_usb_present(struct qpnp_qg *chip) |
| 284 | { |
| 285 | union power_supply_propval pval = {0, }; |
| 286 | |
| 287 | if (is_usb_available(chip)) |
| 288 | power_supply_get_property(chip->usb_psy, |
| 289 | POWER_SUPPLY_PROP_PRESENT, &pval); |
| 290 | |
| 291 | return pval.intval ? true : false; |
| 292 | } |
| 293 | |
Anirudh Ghayal | 0875fed | 2019-01-15 15:54:30 +0530 | [diff] [blame] | 294 | bool is_dc_present(struct qpnp_qg *chip) |
| 295 | { |
| 296 | union power_supply_propval pval = {0, }; |
| 297 | |
| 298 | if (is_dc_available(chip)) |
| 299 | power_supply_get_property(chip->dc_psy, |
| 300 | POWER_SUPPLY_PROP_PRESENT, &pval); |
| 301 | |
| 302 | return pval.intval ? true : false; |
| 303 | } |
| 304 | |
| 305 | bool is_input_present(struct qpnp_qg *chip) |
| 306 | { |
| 307 | return is_usb_present(chip) || is_dc_present(chip); |
| 308 | } |
| 309 | |
Guru Das Srinagesh | 62006d117 | 2018-11-13 10:00:42 -0800 | [diff] [blame] | 310 | static bool is_parallel_available(struct qpnp_qg *chip) |
| 311 | { |
| 312 | if (chip->parallel_psy) |
| 313 | return true; |
| 314 | |
| 315 | chip->parallel_psy = power_supply_get_by_name("parallel"); |
| 316 | if (!chip->parallel_psy) |
| 317 | return false; |
| 318 | |
| 319 | return true; |
| 320 | } |
| 321 | |
| 322 | bool is_parallel_enabled(struct qpnp_qg *chip) |
| 323 | { |
| 324 | union power_supply_propval pval = {0, }; |
| 325 | |
| 326 | if (is_parallel_available(chip)) { |
| 327 | power_supply_get_property(chip->parallel_psy, |
| 328 | POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval); |
| 329 | } |
| 330 | |
| 331 | return pval.intval ? true : false; |
| 332 | } |
| 333 | |
| 334 | int qg_write_monotonic_soc(struct qpnp_qg *chip, int msoc) |
| 335 | { |
| 336 | u8 reg = 0; |
| 337 | int rc; |
| 338 | |
| 339 | reg = (msoc * 255) / 100; |
| 340 | rc = qg_write(chip, chip->qg_base + QG_SOC_MONOTONIC_REG, |
| 341 | ®, 1); |
| 342 | if (rc < 0) |
| 343 | pr_err("Failed to update QG_SOC_MONOTINIC reg rc=%d\n", rc); |
| 344 | |
| 345 | return rc; |
| 346 | } |
| 347 | |
| 348 | int qg_get_battery_temp(struct qpnp_qg *chip, int *temp) |
| 349 | { |
| 350 | int rc = 0; |
| 351 | |
| 352 | if (chip->battery_missing) { |
| 353 | *temp = 250; |
| 354 | return 0; |
| 355 | } |
| 356 | |
| 357 | rc = iio_read_channel_processed(chip->batt_therm_chan, temp); |
| 358 | if (rc < 0) { |
| 359 | pr_err("Failed reading BAT_TEMP over ADC rc=%d\n", rc); |
| 360 | return rc; |
| 361 | } |
| 362 | pr_debug("batt_temp = %d\n", *temp); |
| 363 | |
Anirudh Ghayal | dac6898 | 2019-01-28 13:10:46 +0530 | [diff] [blame] | 364 | return 0; |
Guru Das Srinagesh | 62006d117 | 2018-11-13 10:00:42 -0800 | [diff] [blame] | 365 | } |
| 366 | |
| 367 | int qg_get_battery_current(struct qpnp_qg *chip, int *ibat_ua) |
| 368 | { |
| 369 | int rc = 0, last_ibat = 0; |
| 370 | |
| 371 | if (chip->battery_missing) { |
| 372 | *ibat_ua = 0; |
| 373 | return 0; |
| 374 | } |
| 375 | |
Anirudh Ghayal | 496bb46 | 2020-03-10 22:49:07 +0530 | [diff] [blame] | 376 | if (chip->qg_mode == QG_V_MODE) { |
| 377 | *ibat_ua = chip->qg_v_ibat; |
| 378 | return 0; |
| 379 | } |
| 380 | |
Guru Das Srinagesh | 62006d117 | 2018-11-13 10:00:42 -0800 | [diff] [blame] | 381 | /* hold data */ |
| 382 | rc = qg_masked_write(chip, chip->qg_base + QG_DATA_CTL2_REG, |
| 383 | BURST_AVG_HOLD_FOR_READ_BIT, |
| 384 | BURST_AVG_HOLD_FOR_READ_BIT); |
| 385 | if (rc < 0) { |
| 386 | pr_err("Failed to hold burst-avg data rc=%d\n", rc); |
| 387 | goto release; |
| 388 | } |
| 389 | |
| 390 | rc = qg_read(chip, chip->qg_base + QG_LAST_BURST_AVG_I_DATA0_REG, |
| 391 | (u8 *)&last_ibat, 2); |
| 392 | if (rc < 0) { |
| 393 | pr_err("Failed to read LAST_BURST_AVG_I reg, rc=%d\n", rc); |
| 394 | goto release; |
| 395 | } |
| 396 | |
| 397 | last_ibat = sign_extend32(last_ibat, 15); |
| 398 | *ibat_ua = qg_iraw_to_ua(chip, last_ibat); |
| 399 | |
| 400 | release: |
| 401 | /* release */ |
| 402 | qg_masked_write(chip, chip->qg_base + QG_DATA_CTL2_REG, |
| 403 | BURST_AVG_HOLD_FOR_READ_BIT, 0); |
| 404 | return rc; |
| 405 | } |
| 406 | |
| 407 | int qg_get_battery_voltage(struct qpnp_qg *chip, int *vbat_uv) |
| 408 | { |
| 409 | int rc = 0; |
| 410 | u64 last_vbat = 0; |
| 411 | |
| 412 | if (chip->battery_missing) { |
| 413 | *vbat_uv = 3700000; |
| 414 | return 0; |
| 415 | } |
| 416 | |
| 417 | rc = qg_read(chip, chip->qg_base + QG_LAST_ADC_V_DATA0_REG, |
| 418 | (u8 *)&last_vbat, 2); |
| 419 | if (rc < 0) { |
| 420 | pr_err("Failed to read LAST_ADV_V reg, rc=%d\n", rc); |
| 421 | return rc; |
| 422 | } |
| 423 | |
| 424 | *vbat_uv = V_RAW_TO_UV(last_vbat); |
| 425 | |
| 426 | return rc; |
| 427 | } |
Anirudh Ghayal | 0b54c38 | 2019-01-14 15:15:55 +0530 | [diff] [blame] | 428 | |
| 429 | int qg_get_vbat_avg(struct qpnp_qg *chip, int *vbat_uv) |
| 430 | { |
| 431 | int rc = 0; |
| 432 | u64 last_vbat = 0; |
| 433 | |
| 434 | rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_AVG_V_DATA0_REG, |
| 435 | (u8 *)&last_vbat, 2); |
| 436 | if (rc < 0) { |
| 437 | pr_err("Failed to read S2_NORMAL_AVG_V reg, rc=%d\n", rc); |
| 438 | return rc; |
| 439 | } |
| 440 | |
| 441 | *vbat_uv = V_RAW_TO_UV(last_vbat); |
| 442 | |
| 443 | return 0; |
| 444 | } |
Anirudh Ghayal | c16382a | 2020-01-02 16:05:08 +0530 | [diff] [blame] | 445 | |
| 446 | int qg_get_ibat_avg(struct qpnp_qg *chip, int *ibat_ua) |
| 447 | { |
| 448 | int rc = 0; |
| 449 | int last_ibat = 0; |
| 450 | |
| 451 | rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_AVG_I_DATA0_REG, |
| 452 | (u8 *)&last_ibat, 2); |
| 453 | if (rc < 0) { |
| 454 | pr_err("Failed to read S2_NORMAL_AVG_I reg, rc=%d\n", rc); |
| 455 | return rc; |
| 456 | } |
| 457 | |
Anirudh Ghayal | 6395941 | 2020-04-06 12:31:44 +0530 | [diff] [blame] | 458 | if (last_ibat == FIFO_I_RESET_VAL) { |
| 459 | /* First FIFO is not complete, read instantaneous IBAT */ |
| 460 | rc = qg_get_battery_current(chip, ibat_ua); |
| 461 | if (rc < 0) |
| 462 | pr_err("Failed to read inst. IBAT rc=%d\n", rc); |
| 463 | |
| 464 | return rc; |
| 465 | } |
| 466 | |
Anirudh Ghayal | c16382a | 2020-01-02 16:05:08 +0530 | [diff] [blame] | 467 | last_ibat = sign_extend32(last_ibat, 15); |
| 468 | *ibat_ua = qg_iraw_to_ua(chip, last_ibat); |
| 469 | |
| 470 | return 0; |
| 471 | } |