1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Battery driver for the coulomb-counter of the Intel Dollar Cove TI PMIC 4 * 5 * Note the Intel Dollar Cove TI PMIC coulomb-counter is not a full-featured 6 * autonomous fuel-gauge. It is intended to work together with an always on 7 * micro-controller monitoring it. 8 * 9 * Since Linux does not monitor coulomb-counter changes while the device 10 * is off or suspended, voltage based capacity estimation from 11 * the adc-battery-helper code is used. 12 * 13 * Copyright (C) 2024 Hans de Goede <hansg@kernel.org> 14 * 15 * Register definitions and calibration code was taken from 16 * kernel/drivers/platform/x86/dc_ti_cc.c from the Acer A1-840 Android kernel 17 * which has the following copyright header: 18 * 19 * Copyright (C) 2014 Intel Corporation 20 * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com> 21 * 22 * dc_ti_cc.c is part of the Acer A1-840 Android kernel source-code archive 23 * named: "App. Guide_Acer_20151221_A_A.zip" 24 * which is distributed by Acer from the Acer A1-840 support page: 25 * https://www.acer.com/us-en/support/product-support/A1-840/downloads 26 */ 27 28 #include <linux/acpi.h> 29 #include <linux/bits.h> 30 #include <linux/bitfield.h> 31 #include <linux/cleanup.h> 32 #include <linux/err.h> 33 #include <linux/gpio/consumer.h> 34 #include <linux/iio/consumer.h> 35 #include <linux/mfd/intel_soc_pmic.h> 36 #include <linux/module.h> 37 #include <linux/platform_device.h> 38 #include <linux/pm_runtime.h> 39 #include <linux/power_supply.h> 40 #include <linux/property.h> 41 #include <linux/regmap.h> 42 #include <linux/timekeeping.h> 43 44 #include "adc-battery-helper.h" 45 46 #define DC_TI_PMIC_VERSION_REG 0x00 47 #define PMIC_VERSION_A0 0xC0 48 #define PMIC_VERSION_A1 0xC1 49 50 #define DC_TI_CC_CNTL_REG 0x60 51 #define CC_CNTL_CC_CTR_EN BIT(0) 52 #define CC_CNTL_CC_CLR_EN BIT(1) 53 #define CC_CNTL_CC_CAL_EN BIT(2) 54 #define CC_CNTL_CC_OFFSET_EN BIT(3) 55 #define CC_CNTL_SMPL_INTVL GENMASK(5, 4) 56 #define CC_CNTL_SMPL_INTVL_15MS FIELD_PREP(CC_CNTL_SMPL_INTVL, 0) 57 #define CC_CNTL_SMPL_INTVL_62MS FIELD_PREP(CC_CNTL_SMPL_INTVL, 1) 58 #define CC_CNTL_SMPL_INTVL_125MS FIELD_PREP(CC_CNTL_SMPL_INTVL, 2) 59 #define CC_CNTL_SMPL_INTVL_250MS FIELD_PREP(CC_CNTL_SMPL_INTVL, 3) 60 61 #define DC_TI_SMPL_CTR0_REG 0x69 62 #define DC_TI_SMPL_CTR1_REG 0x68 63 #define DC_TI_SMPL_CTR2_REG 0x67 64 65 #define DC_TI_CC_OFFSET_HI_REG 0x61 66 #define CC_OFFSET_HI_MASK 0x3F 67 #define DC_TI_CC_OFFSET_LO_REG 0x62 68 69 #define DC_TI_SW_OFFSET_REG 0x6C 70 71 #define DC_TI_CC_ACC3_REG 0x63 72 #define DC_TI_CC_ACC2_REG 0x64 73 #define DC_TI_CC_ACC1_REG 0x65 74 #define DC_TI_CC_ACC0_REG 0x66 75 76 #define DC_TI_CC_INTG1_REG 0x6A 77 #define DC_TI_CC_INTG1_MASK 0x3F 78 #define DC_TI_CC_INTG0_REG 0x6B 79 80 #define DC_TI_EEPROM_ACCESS_CONTROL 0x88 81 #define EEPROM_UNLOCK 0xDA 82 #define EEPROM_LOCK 0x00 83 84 #define DC_TI_EEPROM_CC_GAIN_REG 0xF4 85 #define CC_TRIM_REVISION GENMASK(3, 0) 86 #define CC_GAIN_CORRECTION GENMASK(7, 4) 87 88 #define PMIC_VERSION_A0_TRIM_REV 3 89 #define PMIC_VERSION_A1_MIN_TRIM_REV 1 90 91 #define DC_TI_EEPROM_CC_OFFSET_REG 0xFD 92 93 #define DC_TI_EEPROM_CTRL 0xFE 94 #define EEPROM_BANK0_SEL 0x01 95 #define EEPROM_BANK1_SEL 0x02 96 97 #define SMPL_INTVL_US 15000 98 #define SMPL_INTVL_MS (SMPL_INTVL_US / USEC_PER_MSEC) 99 #define CALIBRATION_TIME_US (10 * SMPL_INTVL_US) 100 #define SLEEP_SLACK_US 2500 101 102 /* CC gain correction is in 0.0025 increments */ 103 #define CC_GAIN_STEP 25 104 #define CC_GAIN_DIV 10000 105 106 /* CC offset is in 0.5 units per 250ms (default sample interval) */ 107 #define CC_OFFSET_DIV 2 108 #define CC_OFFSET_SMPL_INTVL_MS 250 109 110 /* CC accumulator scale is 366.2 ųCoulumb / unit */ 111 #define CC_ACC_TO_UA(acc, smpl_ctr) \ 112 ((acc) * (3662 * MSEC_PER_SEC / 10) / ((smpl_ctr) * SMPL_INTVL_MS)) 113 114 #define DEV_NAME "chtdc_ti_battery" 115 116 struct dc_ti_battery_chip { 117 /* Must be the first member see adc-battery-helper documentation */ 118 struct adc_battery_helper helper; 119 struct device *dev; 120 struct regmap *regmap; 121 struct iio_channel *vbat_channel; 122 struct power_supply *psy; 123 int cc_gain; 124 int cc_offset; 125 }; 126 127 static int dc_ti_battery_get_voltage_and_current_now(struct power_supply *psy, int *volt, int *curr) 128 { 129 struct dc_ti_battery_chip *chip = power_supply_get_drvdata(psy); 130 s64 cnt_start_usec, now_usec, sleep_usec; 131 unsigned int reg_val; 132 s32 acc, smpl_ctr; 133 int ret; 134 135 /* 136 * Enable coulomb-counter before reading Vbat from ADC, so that the CC 137 * samples are from the same time period as the Vbat reading. 138 */ 139 ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG, 140 CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN | CC_CNTL_CC_CTR_EN); 141 if (ret) 142 goto out_err; 143 144 cnt_start_usec = ktime_get_ns() / NSEC_PER_USEC; 145 146 /* Read Vbat, convert IIO mV to power-supply ųV */ 147 ret = iio_read_channel_processed_scale(chip->vbat_channel, volt, 1000); 148 if (ret < 0) 149 goto out_err; 150 151 /* Sleep at least 3 sample-times + slack to get 3+ CC samples */ 152 now_usec = ktime_get_ns() / NSEC_PER_USEC; 153 sleep_usec = 3 * SMPL_INTVL_US + SLEEP_SLACK_US - (now_usec - cnt_start_usec); 154 if (sleep_usec > 0 && sleep_usec < 1000000) 155 usleep_range(sleep_usec, sleep_usec + SLEEP_SLACK_US); 156 157 /* 158 * The PMIC latches the coulomb- and sample-counters upon reading the 159 * CC_ACC0 register. Reading multiple registers at once is not supported. 160 * 161 * Step 1: Read CC_ACC0 - CC_ACC3 162 */ 163 ret = regmap_read(chip->regmap, DC_TI_CC_ACC0_REG, ®_val); 164 if (ret) 165 goto out_err; 166 167 acc = reg_val; 168 169 ret = regmap_read(chip->regmap, DC_TI_CC_ACC1_REG, ®_val); 170 if (ret) 171 goto out_err; 172 173 acc |= reg_val << 8; 174 175 ret = regmap_read(chip->regmap, DC_TI_CC_ACC2_REG, ®_val); 176 if (ret) 177 goto out_err; 178 179 acc |= reg_val << 16; 180 181 ret = regmap_read(chip->regmap, DC_TI_CC_ACC3_REG, ®_val); 182 if (ret) 183 goto out_err; 184 185 acc |= reg_val << 24; 186 187 /* Step 2: Read SMPL_CTR0 - SMPL_CTR2 */ 188 ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR0_REG, ®_val); 189 if (ret) 190 goto out_err; 191 192 smpl_ctr = reg_val; 193 194 ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR1_REG, ®_val); 195 if (ret) 196 goto out_err; 197 198 smpl_ctr |= reg_val << 8; 199 200 ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR2_REG, ®_val); 201 if (ret) 202 goto out_err; 203 204 smpl_ctr |= reg_val << 16; 205 206 /* Disable the coulumb-counter again */ 207 ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG, 208 CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN); 209 if (ret) 210 goto out_err; 211 212 /* Apply calibration */ 213 acc -= chip->cc_offset * smpl_ctr * SMPL_INTVL_MS / 214 (CC_OFFSET_SMPL_INTVL_MS * CC_OFFSET_DIV); 215 acc = acc * (CC_GAIN_DIV - chip->cc_gain * CC_GAIN_STEP) / CC_GAIN_DIV; 216 *curr = CC_ACC_TO_UA(acc, smpl_ctr); 217 218 return 0; 219 220 out_err: 221 dev_err(chip->dev, "IO-error %d communicating with PMIC\n", ret); 222 return ret; 223 } 224 225 static const struct power_supply_desc dc_ti_battery_psy_desc = { 226 .name = "intel_dc_ti_battery", 227 .type = POWER_SUPPLY_TYPE_BATTERY, 228 .get_property = adc_battery_helper_get_property, 229 .external_power_changed = adc_battery_helper_external_power_changed, 230 .properties = adc_battery_helper_properties, 231 .num_properties = ADC_HELPER_NUM_PROPERTIES, 232 }; 233 234 static int dc_ti_battery_hw_init(struct dc_ti_battery_chip *chip) 235 { 236 u8 pmic_version, cc_trim_rev; 237 unsigned int reg_val; 238 int ret; 239 240 /* Set sample rate to 15 ms and calibrate the coulomb-counter */ 241 ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG, 242 CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN | 243 CC_CNTL_CC_CAL_EN | CC_CNTL_CC_CTR_EN); 244 if (ret) 245 goto out; 246 247 fsleep(CALIBRATION_TIME_US); 248 249 /* Disable coulomb-counter it is only used while getting the current */ 250 ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG, 251 CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN); 252 if (ret) 253 goto out; 254 255 ret = regmap_read(chip->regmap, DC_TI_PMIC_VERSION_REG, ®_val); 256 if (ret) 257 goto out; 258 259 pmic_version = reg_val; 260 261 /* 262 * As per the PMIC vendor (TI), the calibration offset and gain err 263 * values are stored in EEPROM Bank 0 and Bank 1 of the PMIC. 264 * We need to read the stored offset and gain margins and need 265 * to apply the corrections to the raw coulomb counter value. 266 */ 267 268 /* Unlock the EEPROM Access */ 269 ret = regmap_write(chip->regmap, DC_TI_EEPROM_ACCESS_CONTROL, EEPROM_UNLOCK); 270 if (ret) 271 goto out; 272 273 /* Select Bank 1 to read CC GAIN Err correction */ 274 ret = regmap_write(chip->regmap, DC_TI_EEPROM_CTRL, EEPROM_BANK1_SEL); 275 if (ret) 276 goto out; 277 278 ret = regmap_read(chip->regmap, DC_TI_EEPROM_CC_GAIN_REG, ®_val); 279 if (ret) 280 goto out; 281 282 cc_trim_rev = FIELD_GET(CC_TRIM_REVISION, reg_val); 283 284 dev_dbg(chip->dev, "pmic-ver 0x%02x trim-rev %d\n", pmic_version, cc_trim_rev); 285 286 if (!(pmic_version == PMIC_VERSION_A0 && cc_trim_rev == PMIC_VERSION_A0_TRIM_REV) && 287 !(pmic_version == PMIC_VERSION_A1 && cc_trim_rev >= PMIC_VERSION_A1_MIN_TRIM_REV)) { 288 dev_dbg(chip->dev, "unsupported trim-revision, using uncalibrated CC values\n"); 289 goto out_relock; 290 } 291 292 chip->cc_gain = 1 - (int)FIELD_GET(CC_GAIN_CORRECTION, reg_val); 293 294 /* Select Bank 0 to read CC OFFSET Correction */ 295 ret = regmap_write(chip->regmap, DC_TI_EEPROM_CTRL, EEPROM_BANK0_SEL); 296 if (ret) 297 goto out_relock; 298 299 ret = regmap_read(chip->regmap, DC_TI_EEPROM_CC_OFFSET_REG, ®_val); 300 if (ret) 301 goto out_relock; 302 303 chip->cc_offset = (s8)reg_val; 304 305 dev_dbg(chip->dev, "cc-offset %d cc-gain %d\n", chip->cc_offset, chip->cc_gain); 306 307 out_relock: 308 /* Re-lock the EEPROM Access */ 309 regmap_write(chip->regmap, DC_TI_EEPROM_ACCESS_CONTROL, EEPROM_LOCK); 310 out: 311 if (ret) 312 dev_err(chip->dev, "IO-error %d initializing PMIC\n", ret); 313 314 return ret; 315 } 316 317 static int dc_ti_battery_probe(struct platform_device *pdev) 318 { 319 struct device *dev = &pdev->dev; 320 struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); 321 struct power_supply_config psy_cfg = {}; 322 struct fwnode_reference_args args; 323 struct gpio_desc *charge_finished; 324 struct dc_ti_battery_chip *chip; 325 int ret; 326 327 /* On most devices with a Dollar Cove TI the battery is handled by ACPI */ 328 if (!acpi_quirk_skip_acpi_ac_and_battery()) 329 return -ENODEV; 330 331 /* ACPI glue code adds a "monitored-battery" fwnode, wait for this */ 332 ret = fwnode_property_get_reference_args(dev_fwnode(dev), "monitored-battery", 333 NULL, 0, 0, &args); 334 if (ret) { 335 dev_dbg(dev, "fwnode_property_get_ref() ret %d\n", ret); 336 return dev_err_probe(dev, -EPROBE_DEFER, "Waiting for monitored-battery fwnode\n"); 337 } 338 339 fwnode_handle_put(args.fwnode); 340 341 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 342 if (!chip) 343 return -ENOMEM; 344 345 chip->dev = dev; 346 chip->regmap = pmic->regmap; 347 348 chip->vbat_channel = devm_iio_channel_get(dev, "VBAT"); 349 if (IS_ERR(chip->vbat_channel)) { 350 dev_dbg(dev, "devm_iio_channel_get() ret %ld\n", PTR_ERR(chip->vbat_channel)); 351 return dev_err_probe(dev, -EPROBE_DEFER, "Waiting for VBAT IIO channel\n"); 352 } 353 354 charge_finished = devm_gpiod_get_optional(dev, "charged", GPIOD_IN); 355 if (IS_ERR(charge_finished)) 356 return dev_err_probe(dev, PTR_ERR(charge_finished), "Getting charged GPIO\n"); 357 358 ret = dc_ti_battery_hw_init(chip); 359 if (ret) 360 return ret; 361 362 platform_set_drvdata(pdev, chip); 363 364 psy_cfg.drv_data = chip; 365 chip->psy = devm_power_supply_register(dev, &dc_ti_battery_psy_desc, &psy_cfg); 366 if (IS_ERR(chip->psy)) 367 return PTR_ERR(chip->psy); 368 369 return adc_battery_helper_init(&chip->helper, chip->psy, 370 dc_ti_battery_get_voltage_and_current_now, 371 charge_finished); 372 } 373 374 static DEFINE_RUNTIME_DEV_PM_OPS(dc_ti_battery_pm_ops, adc_battery_helper_suspend, 375 adc_battery_helper_resume, NULL); 376 377 static struct platform_driver dc_ti_battery_driver = { 378 .driver = { 379 .name = DEV_NAME, 380 .pm = pm_sleep_ptr(&dc_ti_battery_pm_ops), 381 }, 382 .probe = dc_ti_battery_probe, 383 }; 384 module_platform_driver(dc_ti_battery_driver); 385 386 MODULE_ALIAS("platform:" DEV_NAME); 387 MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); 388 MODULE_DESCRIPTION("Intel Dollar Cove (TI) battery driver"); 389 MODULE_LICENSE("GPL"); 390