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 ktime_t ktime; 131 s64 sleep_usec; 132 unsigned int reg_val; 133 s32 acc, smpl_ctr; 134 int ret; 135 136 /* 137 * Enable coulomb-counter before reading Vbat from ADC, so that the CC 138 * samples are from the same time period as the Vbat reading. 139 */ 140 ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG, 141 CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN | CC_CNTL_CC_CTR_EN); 142 if (ret) 143 goto out_err; 144 145 ktime = ktime_get(); 146 147 /* Read Vbat, convert IIO mV to power-supply ųV */ 148 ret = iio_read_channel_processed_scale(chip->vbat_channel, volt, 1000); 149 if (ret < 0) 150 goto out_err; 151 152 ktime = ktime_sub(ktime_get(), ktime); 153 154 /* Sleep at least 3 sample-times + slack to get 3+ CC samples */ 155 sleep_usec = 3 * SMPL_INTVL_US + SLEEP_SLACK_US - ktime_to_us(ktime); 156 if (sleep_usec > 0 && sleep_usec < 1000000) 157 usleep_range(sleep_usec, sleep_usec + SLEEP_SLACK_US); 158 159 /* 160 * The PMIC latches the coulomb- and sample-counters upon reading the 161 * CC_ACC0 register. Reading multiple registers at once is not supported. 162 * 163 * Step 1: Read CC_ACC0 - CC_ACC3 164 */ 165 ret = regmap_read(chip->regmap, DC_TI_CC_ACC0_REG, ®_val); 166 if (ret) 167 goto out_err; 168 169 acc = reg_val; 170 171 ret = regmap_read(chip->regmap, DC_TI_CC_ACC1_REG, ®_val); 172 if (ret) 173 goto out_err; 174 175 acc |= reg_val << 8; 176 177 ret = regmap_read(chip->regmap, DC_TI_CC_ACC2_REG, ®_val); 178 if (ret) 179 goto out_err; 180 181 acc |= reg_val << 16; 182 183 ret = regmap_read(chip->regmap, DC_TI_CC_ACC3_REG, ®_val); 184 if (ret) 185 goto out_err; 186 187 acc |= reg_val << 24; 188 189 /* Step 2: Read SMPL_CTR0 - SMPL_CTR2 */ 190 ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR0_REG, ®_val); 191 if (ret) 192 goto out_err; 193 194 smpl_ctr = reg_val; 195 196 ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR1_REG, ®_val); 197 if (ret) 198 goto out_err; 199 200 smpl_ctr |= reg_val << 8; 201 202 ret = regmap_read(chip->regmap, DC_TI_SMPL_CTR2_REG, ®_val); 203 if (ret) 204 goto out_err; 205 206 smpl_ctr |= reg_val << 16; 207 208 /* Disable the coulumb-counter again */ 209 ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG, 210 CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN); 211 if (ret) 212 goto out_err; 213 214 /* Apply calibration */ 215 acc -= chip->cc_offset * smpl_ctr * SMPL_INTVL_MS / 216 (CC_OFFSET_SMPL_INTVL_MS * CC_OFFSET_DIV); 217 acc = acc * (CC_GAIN_DIV - chip->cc_gain * CC_GAIN_STEP) / CC_GAIN_DIV; 218 *curr = CC_ACC_TO_UA(acc, smpl_ctr); 219 220 return 0; 221 222 out_err: 223 dev_err(chip->dev, "IO-error %d communicating with PMIC\n", ret); 224 return ret; 225 } 226 227 static const struct power_supply_desc dc_ti_battery_psy_desc = { 228 .name = "intel_dc_ti_battery", 229 .type = POWER_SUPPLY_TYPE_BATTERY, 230 .get_property = adc_battery_helper_get_property, 231 .external_power_changed = adc_battery_helper_external_power_changed, 232 .properties = adc_battery_helper_properties, 233 .num_properties = ADC_HELPER_NUM_PROPERTIES, 234 }; 235 236 static int dc_ti_battery_hw_init(struct dc_ti_battery_chip *chip) 237 { 238 u8 pmic_version, cc_trim_rev; 239 unsigned int reg_val; 240 int ret; 241 242 /* Set sample rate to 15 ms and calibrate the coulomb-counter */ 243 ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG, 244 CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN | 245 CC_CNTL_CC_CAL_EN | CC_CNTL_CC_CTR_EN); 246 if (ret) 247 goto out; 248 249 fsleep(CALIBRATION_TIME_US); 250 251 /* Disable coulomb-counter it is only used while getting the current */ 252 ret = regmap_write(chip->regmap, DC_TI_CC_CNTL_REG, 253 CC_CNTL_SMPL_INTVL_15MS | CC_CNTL_CC_OFFSET_EN); 254 if (ret) 255 goto out; 256 257 ret = regmap_read(chip->regmap, DC_TI_PMIC_VERSION_REG, ®_val); 258 if (ret) 259 goto out; 260 261 pmic_version = reg_val; 262 263 /* 264 * As per the PMIC vendor (TI), the calibration offset and gain err 265 * values are stored in EEPROM Bank 0 and Bank 1 of the PMIC. 266 * We need to read the stored offset and gain margins and need 267 * to apply the corrections to the raw coulomb counter value. 268 */ 269 270 /* Unlock the EEPROM Access */ 271 ret = regmap_write(chip->regmap, DC_TI_EEPROM_ACCESS_CONTROL, EEPROM_UNLOCK); 272 if (ret) 273 goto out; 274 275 /* Select Bank 1 to read CC GAIN Err correction */ 276 ret = regmap_write(chip->regmap, DC_TI_EEPROM_CTRL, EEPROM_BANK1_SEL); 277 if (ret) 278 goto out; 279 280 ret = regmap_read(chip->regmap, DC_TI_EEPROM_CC_GAIN_REG, ®_val); 281 if (ret) 282 goto out; 283 284 cc_trim_rev = FIELD_GET(CC_TRIM_REVISION, reg_val); 285 286 dev_dbg(chip->dev, "pmic-ver 0x%02x trim-rev %d\n", pmic_version, cc_trim_rev); 287 288 if (!(pmic_version == PMIC_VERSION_A0 && cc_trim_rev == PMIC_VERSION_A0_TRIM_REV) && 289 !(pmic_version == PMIC_VERSION_A1 && cc_trim_rev >= PMIC_VERSION_A1_MIN_TRIM_REV)) { 290 dev_dbg(chip->dev, "unsupported trim-revision, using uncalibrated CC values\n"); 291 goto out_relock; 292 } 293 294 chip->cc_gain = 1 - (int)FIELD_GET(CC_GAIN_CORRECTION, reg_val); 295 296 /* Select Bank 0 to read CC OFFSET Correction */ 297 ret = regmap_write(chip->regmap, DC_TI_EEPROM_CTRL, EEPROM_BANK0_SEL); 298 if (ret) 299 goto out_relock; 300 301 ret = regmap_read(chip->regmap, DC_TI_EEPROM_CC_OFFSET_REG, ®_val); 302 if (ret) 303 goto out_relock; 304 305 chip->cc_offset = (s8)reg_val; 306 307 dev_dbg(chip->dev, "cc-offset %d cc-gain %d\n", chip->cc_offset, chip->cc_gain); 308 309 out_relock: 310 /* Re-lock the EEPROM Access */ 311 regmap_write(chip->regmap, DC_TI_EEPROM_ACCESS_CONTROL, EEPROM_LOCK); 312 out: 313 if (ret) 314 dev_err(chip->dev, "IO-error %d initializing PMIC\n", ret); 315 316 return ret; 317 } 318 319 static int dc_ti_battery_probe(struct platform_device *pdev) 320 { 321 struct device *dev = &pdev->dev; 322 struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); 323 struct power_supply_config psy_cfg = {}; 324 struct fwnode_reference_args args; 325 struct gpio_desc *charge_finished; 326 struct dc_ti_battery_chip *chip; 327 int ret; 328 329 /* On most devices with a Dollar Cove TI the battery is handled by ACPI */ 330 if (!acpi_quirk_skip_acpi_ac_and_battery()) 331 return -ENODEV; 332 333 /* ACPI glue code adds a "monitored-battery" fwnode, wait for this */ 334 ret = fwnode_property_get_reference_args(dev_fwnode(dev), "monitored-battery", 335 NULL, 0, 0, &args); 336 if (ret) { 337 dev_dbg(dev, "fwnode_property_get_ref() ret %d\n", ret); 338 return dev_err_probe(dev, -EPROBE_DEFER, "Waiting for monitored-battery fwnode\n"); 339 } 340 341 fwnode_handle_put(args.fwnode); 342 343 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 344 if (!chip) 345 return -ENOMEM; 346 347 chip->dev = dev; 348 chip->regmap = pmic->regmap; 349 350 chip->vbat_channel = devm_iio_channel_get(dev, "VBAT"); 351 if (IS_ERR(chip->vbat_channel)) { 352 dev_dbg(dev, "devm_iio_channel_get() ret %ld\n", PTR_ERR(chip->vbat_channel)); 353 return dev_err_probe(dev, -EPROBE_DEFER, "Waiting for VBAT IIO channel\n"); 354 } 355 356 charge_finished = devm_gpiod_get_optional(dev, "charged", GPIOD_IN); 357 if (IS_ERR(charge_finished)) 358 return dev_err_probe(dev, PTR_ERR(charge_finished), "Getting charged GPIO\n"); 359 360 ret = dc_ti_battery_hw_init(chip); 361 if (ret) 362 return ret; 363 364 platform_set_drvdata(pdev, chip); 365 366 psy_cfg.drv_data = chip; 367 chip->psy = devm_power_supply_register(dev, &dc_ti_battery_psy_desc, &psy_cfg); 368 if (IS_ERR(chip->psy)) 369 return PTR_ERR(chip->psy); 370 371 return adc_battery_helper_init(&chip->helper, chip->psy, 372 dc_ti_battery_get_voltage_and_current_now, 373 charge_finished); 374 } 375 376 static DEFINE_RUNTIME_DEV_PM_OPS(dc_ti_battery_pm_ops, adc_battery_helper_suspend, 377 adc_battery_helper_resume, NULL); 378 379 static struct platform_driver dc_ti_battery_driver = { 380 .driver = { 381 .name = DEV_NAME, 382 .pm = pm_sleep_ptr(&dc_ti_battery_pm_ops), 383 }, 384 .probe = dc_ti_battery_probe, 385 }; 386 module_platform_driver(dc_ti_battery_driver); 387 388 MODULE_ALIAS("platform:" DEV_NAME); 389 MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); 390 MODULE_DESCRIPTION("Intel Dollar Cove (TI) battery driver"); 391 MODULE_LICENSE("GPL"); 392