Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Battery monitor driver for the uPI uG3105 battery monitor
0004  *
0005  * Note the uG3105 is not a full-featured autonomous fuel-gauge. Instead it is
0006  * expected to be use in combination with some always on microcontroller reading
0007  * its coulomb-counter before it can wrap (must be read every 400 seconds!).
0008  *
0009  * Since Linux does not monitor coulomb-counter changes while the device
0010  * is off or suspended, the coulomb counter is not used atm.
0011  *
0012  * Possible improvements:
0013  * 1. Activate commented out total_coulomb_count code
0014  * 2. Reset total_coulomb_count val to 0 when the battery is as good as empty
0015  *    and remember that we did this (and clear the flag for this on susp/resume)
0016  * 3. When the battery is full check if the flag that we set total_coulomb_count
0017  *    to when the battery was empty is set. If so we now know the capacity,
0018  *    not the design, but actual capacity, of the battery
0019  * 4. Add some mechanism (needs userspace help, or maybe use efivar?) to remember
0020  *    the actual capacity of the battery over reboots
0021  * 5. When we know the actual capacity at probe time, add energy_now and
0022  *    energy_full attributes. Guess boot + resume energy_now value based on ocv
0023  *    and then use total_coulomb_count to report energy_now over time, resetting
0024  *    things to adjust for drift when empty/full. This should give more accurate
0025  *    readings, esp. in the 30-70% range and allow userspace to estimate time
0026  *    remaining till empty/full
0027  * 6. Maybe unregister + reregister the psy device when we learn the actual
0028  *    capacity during run-time ?
0029  *
0030  * The above will also require some sort of mwh_per_unit calculation. Testing
0031  * has shown that an estimated 7404mWh increase of the battery's energy results
0032  * in a total_coulomb_count increase of 3277 units with a 5 milli-ohm sense R.
0033  *
0034  * Copyright (C) 2021 Hans de Goede <hdegoede@redhat.com>
0035  */
0036 
0037 #include <linux/devm-helpers.h>
0038 #include <linux/module.h>
0039 #include <linux/mutex.h>
0040 #include <linux/slab.h>
0041 #include <linux/i2c.h>
0042 #include <linux/mod_devicetable.h>
0043 #include <linux/power_supply.h>
0044 #include <linux/workqueue.h>
0045 
0046 #define UG3105_MOV_AVG_WINDOW                   8
0047 #define UG3105_INIT_POLL_TIME                   (5 * HZ)
0048 #define UG3105_POLL_TIME                    (30 * HZ)
0049 #define UG3105_SETTLE_TIME                  (1 * HZ)
0050 
0051 #define UG3105_INIT_POLL_COUNT                  30
0052 
0053 #define UG3105_REG_MODE                     0x00
0054 #define UG3105_REG_CTRL1                    0x01
0055 #define UG3105_REG_COULOMB_CNT                  0x02
0056 #define UG3105_REG_BAT_VOLT                 0x08
0057 #define UG3105_REG_BAT_CURR                 0x0c
0058 
0059 #define UG3105_MODE_STANDBY                 0x00
0060 #define UG3105_MODE_RUN                     0x10
0061 
0062 #define UG3105_CTRL1_RESET_COULOMB_CNT              0x03
0063 
0064 #define UG3105_CURR_HYST_UA                 65000
0065 
0066 #define UG3105_LOW_BAT_UV                   3700000
0067 #define UG3105_FULL_BAT_HYST_UV                 38000
0068 
0069 struct ug3105_chip {
0070     struct i2c_client *client;
0071     struct power_supply *psy;
0072     struct power_supply_battery_info *info;
0073     struct delayed_work work;
0074     struct mutex lock;
0075     int ocv[UG3105_MOV_AVG_WINDOW];     /* micro-volt */
0076     int intern_res[UG3105_MOV_AVG_WINDOW];  /* milli-ohm */
0077     int poll_count;
0078     int ocv_avg_index;
0079     int ocv_avg;                /* micro-volt */
0080     int intern_res_poll_count;
0081     int intern_res_avg_index;
0082     int intern_res_avg;         /* milli-ohm */
0083     int volt;               /* micro-volt */
0084     int curr;               /* micro-ampere */
0085     int total_coulomb_count;
0086     int uv_per_unit;
0087     int ua_per_unit;
0088     int status;
0089     int capacity;
0090     bool supplied;
0091 };
0092 
0093 static int ug3105_read_word(struct i2c_client *client, u8 reg)
0094 {
0095     int val;
0096 
0097     val = i2c_smbus_read_word_data(client, reg);
0098     if (val < 0)
0099         dev_err(&client->dev, "Error reading reg 0x%02x\n", reg);
0100 
0101     return val;
0102 }
0103 
0104 static int ug3105_get_status(struct ug3105_chip *chip)
0105 {
0106     int full = chip->info->constant_charge_voltage_max_uv - UG3105_FULL_BAT_HYST_UV;
0107 
0108     if (chip->curr > UG3105_CURR_HYST_UA)
0109         return POWER_SUPPLY_STATUS_CHARGING;
0110 
0111     if (chip->curr < -UG3105_CURR_HYST_UA)
0112         return POWER_SUPPLY_STATUS_DISCHARGING;
0113 
0114     if (chip->supplied && chip->ocv_avg > full)
0115         return POWER_SUPPLY_STATUS_FULL;
0116 
0117     return POWER_SUPPLY_STATUS_NOT_CHARGING;
0118 }
0119 
0120 static int ug3105_get_capacity(struct ug3105_chip *chip)
0121 {
0122     /*
0123      * OCV voltages in uV for 0-110% in 5% increments, the 100-110% is
0124      * for LiPo HV (High-Voltage) bateries which can go up to 4.35V
0125      * instead of the usual 4.2V.
0126      */
0127     static const int ocv_capacity_tbl[23] = {
0128         3350000,
0129         3610000,
0130         3690000,
0131         3710000,
0132         3730000,
0133         3750000,
0134         3770000,
0135         3786667,
0136         3803333,
0137         3820000,
0138         3836667,
0139         3853333,
0140         3870000,
0141         3907500,
0142         3945000,
0143         3982500,
0144         4020000,
0145         4075000,
0146         4110000,
0147         4150000,
0148         4200000,
0149         4250000,
0150         4300000,
0151     };
0152     int i, ocv_diff, ocv_step;
0153 
0154     if (chip->ocv_avg < ocv_capacity_tbl[0])
0155         return 0;
0156 
0157     if (chip->status == POWER_SUPPLY_STATUS_FULL)
0158         return 100;
0159 
0160     for (i = 1; i < ARRAY_SIZE(ocv_capacity_tbl); i++) {
0161         if (chip->ocv_avg > ocv_capacity_tbl[i])
0162             continue;
0163 
0164         ocv_diff = ocv_capacity_tbl[i] - chip->ocv_avg;
0165         ocv_step = ocv_capacity_tbl[i] - ocv_capacity_tbl[i - 1];
0166         /* scale 0-110% down to 0-100% for LiPo HV */
0167         if (chip->info->constant_charge_voltage_max_uv >= 4300000)
0168             return (i * 500 - ocv_diff * 500 / ocv_step) / 110;
0169         else
0170             return i * 5 - ocv_diff * 5 / ocv_step;
0171     }
0172 
0173     return 100;
0174 }
0175 
0176 static void ug3105_work(struct work_struct *work)
0177 {
0178     struct ug3105_chip *chip = container_of(work, struct ug3105_chip,
0179                         work.work);
0180     int i, val, curr_diff, volt_diff, res, win_size;
0181     bool prev_supplied = chip->supplied;
0182     int prev_status = chip->status;
0183     int prev_volt = chip->volt;
0184     int prev_curr = chip->curr;
0185     struct power_supply *psy;
0186 
0187     mutex_lock(&chip->lock);
0188 
0189     psy = chip->psy;
0190     if (!psy)
0191         goto out;
0192 
0193     val = ug3105_read_word(chip->client, UG3105_REG_BAT_VOLT);
0194     if (val < 0)
0195         goto out;
0196     chip->volt = val * chip->uv_per_unit;
0197 
0198     val = ug3105_read_word(chip->client, UG3105_REG_BAT_CURR);
0199     if (val < 0)
0200         goto out;
0201     chip->curr = (s16)val * chip->ua_per_unit;
0202 
0203     chip->ocv[chip->ocv_avg_index] =
0204         chip->volt - chip->curr * chip->intern_res_avg / 1000;
0205     chip->ocv_avg_index = (chip->ocv_avg_index + 1) % UG3105_MOV_AVG_WINDOW;
0206     chip->poll_count++;
0207 
0208     /*
0209      * See possible improvements comment above.
0210      *
0211      * Read + reset coulomb counter every 10 polls (every 300 seconds)
0212      * if ((chip->poll_count % 10) == 0) {
0213      *  val = ug3105_read_word(chip->client, UG3105_REG_COULOMB_CNT);
0214      *  if (val < 0)
0215      *      goto out;
0216      *
0217      *  i2c_smbus_write_byte_data(chip->client, UG3105_REG_CTRL1,
0218      *                UG3105_CTRL1_RESET_COULOMB_CNT);
0219      *
0220      *  chip->total_coulomb_count += (s16)val;
0221      *  dev_dbg(&chip->client->dev, "coulomb count %d total %d\n",
0222      *      (s16)val, chip->total_coulomb_count);
0223      * }
0224      */
0225 
0226     chip->ocv_avg = 0;
0227     win_size = min(chip->poll_count, UG3105_MOV_AVG_WINDOW);
0228     for (i = 0; i < win_size; i++)
0229         chip->ocv_avg += chip->ocv[i];
0230     chip->ocv_avg /= win_size;
0231 
0232     chip->supplied = power_supply_am_i_supplied(psy);
0233     chip->status = ug3105_get_status(chip);
0234     chip->capacity = ug3105_get_capacity(chip);
0235 
0236     /*
0237      * Skip internal resistance calc on charger [un]plug and
0238      * when the battery is almost empty (voltage low).
0239      */
0240     if (chip->supplied != prev_supplied ||
0241         chip->volt < UG3105_LOW_BAT_UV ||
0242         chip->poll_count < 2)
0243         goto out;
0244 
0245     /*
0246      * Assuming that the OCV voltage does not change significantly
0247      * between 2 polls, then we can calculate the internal resistance
0248      * on a significant current change by attributing all voltage
0249      * change between the 2 readings to the internal resistance.
0250      */
0251     curr_diff = abs(chip->curr - prev_curr);
0252     if (curr_diff < UG3105_CURR_HYST_UA)
0253         goto out;
0254 
0255     volt_diff = abs(chip->volt - prev_volt);
0256     res = volt_diff * 1000 / curr_diff;
0257 
0258     if ((res < (chip->intern_res_avg * 2 / 3)) ||
0259         (res > (chip->intern_res_avg * 4 / 3))) {
0260         dev_dbg(&chip->client->dev, "Ignoring outlier internal resistance %d mOhm\n", res);
0261         goto out;
0262     }
0263 
0264     dev_dbg(&chip->client->dev, "Internal resistance %d mOhm\n", res);
0265 
0266     chip->intern_res[chip->intern_res_avg_index] = res;
0267     chip->intern_res_avg_index = (chip->intern_res_avg_index + 1) % UG3105_MOV_AVG_WINDOW;
0268     chip->intern_res_poll_count++;
0269 
0270     chip->intern_res_avg = 0;
0271     win_size = min(chip->intern_res_poll_count, UG3105_MOV_AVG_WINDOW);
0272     for (i = 0; i < win_size; i++)
0273         chip->intern_res_avg += chip->intern_res[i];
0274     chip->intern_res_avg /= win_size;
0275 
0276 out:
0277     mutex_unlock(&chip->lock);
0278 
0279     queue_delayed_work(system_wq, &chip->work,
0280                (chip->poll_count <= UG3105_INIT_POLL_COUNT) ?
0281                     UG3105_INIT_POLL_TIME : UG3105_POLL_TIME);
0282 
0283     if (chip->status != prev_status && psy)
0284         power_supply_changed(psy);
0285 }
0286 
0287 static enum power_supply_property ug3105_battery_props[] = {
0288     POWER_SUPPLY_PROP_STATUS,
0289     POWER_SUPPLY_PROP_PRESENT,
0290     POWER_SUPPLY_PROP_TECHNOLOGY,
0291     POWER_SUPPLY_PROP_SCOPE,
0292     POWER_SUPPLY_PROP_VOLTAGE_NOW,
0293     POWER_SUPPLY_PROP_VOLTAGE_OCV,
0294     POWER_SUPPLY_PROP_CURRENT_NOW,
0295     POWER_SUPPLY_PROP_CAPACITY,
0296 };
0297 
0298 static int ug3105_get_property(struct power_supply *psy,
0299                    enum power_supply_property psp,
0300                    union power_supply_propval *val)
0301 {
0302     struct ug3105_chip *chip = power_supply_get_drvdata(psy);
0303     int ret = 0;
0304 
0305     mutex_lock(&chip->lock);
0306 
0307     if (!chip->psy) {
0308         ret = -EAGAIN;
0309         goto out;
0310     }
0311 
0312     switch (psp) {
0313     case POWER_SUPPLY_PROP_STATUS:
0314         val->intval = chip->status;
0315         break;
0316     case POWER_SUPPLY_PROP_PRESENT:
0317         val->intval = 1;
0318         break;
0319     case POWER_SUPPLY_PROP_TECHNOLOGY:
0320         val->intval = chip->info->technology;
0321         break;
0322     case POWER_SUPPLY_PROP_SCOPE:
0323         val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
0324         break;
0325     case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0326         ret = ug3105_read_word(chip->client, UG3105_REG_BAT_VOLT);
0327         if (ret < 0)
0328             break;
0329         val->intval = ret * chip->uv_per_unit;
0330         ret = 0;
0331         break;
0332     case POWER_SUPPLY_PROP_VOLTAGE_OCV:
0333         val->intval = chip->ocv_avg;
0334         break;
0335     case POWER_SUPPLY_PROP_CURRENT_NOW:
0336         ret = ug3105_read_word(chip->client, UG3105_REG_BAT_CURR);
0337         if (ret < 0)
0338             break;
0339         val->intval = (s16)ret * chip->ua_per_unit;
0340         ret = 0;
0341         break;
0342     case POWER_SUPPLY_PROP_CAPACITY:
0343         val->intval = chip->capacity;
0344         break;
0345     default:
0346         ret = -EINVAL;
0347     }
0348 
0349 out:
0350     mutex_unlock(&chip->lock);
0351     return ret;
0352 }
0353 
0354 static void ug3105_external_power_changed(struct power_supply *psy)
0355 {
0356     struct ug3105_chip *chip = power_supply_get_drvdata(psy);
0357 
0358     dev_dbg(&chip->client->dev, "external power changed\n");
0359     mod_delayed_work(system_wq, &chip->work, UG3105_SETTLE_TIME);
0360 }
0361 
0362 static const struct power_supply_desc ug3105_psy_desc = {
0363     .name       = "ug3105_battery",
0364     .type       = POWER_SUPPLY_TYPE_BATTERY,
0365     .get_property   = ug3105_get_property,
0366     .external_power_changed = ug3105_external_power_changed,
0367     .properties = ug3105_battery_props,
0368     .num_properties = ARRAY_SIZE(ug3105_battery_props),
0369 };
0370 
0371 static void ug3105_init(struct ug3105_chip *chip)
0372 {
0373     chip->poll_count = 0;
0374     chip->ocv_avg_index = 0;
0375     chip->total_coulomb_count = 0;
0376     i2c_smbus_write_byte_data(chip->client, UG3105_REG_MODE,
0377                   UG3105_MODE_RUN);
0378     i2c_smbus_write_byte_data(chip->client, UG3105_REG_CTRL1,
0379                   UG3105_CTRL1_RESET_COULOMB_CNT);
0380     queue_delayed_work(system_wq, &chip->work, 0);
0381     flush_delayed_work(&chip->work);
0382 }
0383 
0384 static int ug3105_probe(struct i2c_client *client)
0385 {
0386     struct power_supply_config psy_cfg = {};
0387     struct device *dev = &client->dev;
0388     u32 curr_sense_res_uohm = 10000;
0389     struct power_supply *psy;
0390     struct ug3105_chip *chip;
0391     int ret;
0392 
0393     chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
0394     if (!chip)
0395         return -ENOMEM;
0396 
0397     chip->client = client;
0398     mutex_init(&chip->lock);
0399     ret = devm_delayed_work_autocancel(dev, &chip->work, ug3105_work);
0400     if (ret)
0401         return ret;
0402 
0403     psy_cfg.drv_data = chip;
0404     psy = devm_power_supply_register(dev, &ug3105_psy_desc, &psy_cfg);
0405     if (IS_ERR(psy))
0406         return PTR_ERR(psy);
0407 
0408     ret = power_supply_get_battery_info(psy, &chip->info);
0409     if (ret)
0410         return ret;
0411 
0412     if (chip->info->factory_internal_resistance_uohm == -EINVAL ||
0413         chip->info->constant_charge_voltage_max_uv == -EINVAL) {
0414         dev_err(dev, "error required properties are missing\n");
0415         return -ENODEV;
0416     }
0417 
0418     device_property_read_u32(dev, "upisemi,rsns-microohm", &curr_sense_res_uohm);
0419 
0420     /*
0421      * DAC maximum is 4.5V divided by 65536 steps + an unknown factor of 10
0422      * coming from somewhere for some reason (verified with a volt-meter).
0423      */
0424     chip->uv_per_unit = 45000000/65536;
0425     /* Datasheet says 8.1 uV per unit for the current ADC */
0426     chip->ua_per_unit = 8100000 / curr_sense_res_uohm;
0427 
0428     /* Use provided internal resistance as start point (in milli-ohm) */
0429     chip->intern_res_avg = chip->info->factory_internal_resistance_uohm / 1000;
0430     /* Also add it to the internal resistance moving average window */
0431     chip->intern_res[0] = chip->intern_res_avg;
0432     chip->intern_res_avg_index = 1;
0433     chip->intern_res_poll_count = 1;
0434 
0435     mutex_lock(&chip->lock);
0436     chip->psy = psy;
0437     mutex_unlock(&chip->lock);
0438 
0439     ug3105_init(chip);
0440 
0441     i2c_set_clientdata(client, chip);
0442     return 0;
0443 }
0444 
0445 static int __maybe_unused ug3105_suspend(struct device *dev)
0446 {
0447     struct ug3105_chip *chip = dev_get_drvdata(dev);
0448 
0449     cancel_delayed_work_sync(&chip->work);
0450     i2c_smbus_write_byte_data(chip->client, UG3105_REG_MODE,
0451                   UG3105_MODE_STANDBY);
0452 
0453     return 0;
0454 }
0455 
0456 static int __maybe_unused ug3105_resume(struct device *dev)
0457 {
0458     struct ug3105_chip *chip = dev_get_drvdata(dev);
0459 
0460     ug3105_init(chip);
0461 
0462     return 0;
0463 }
0464 
0465 static SIMPLE_DEV_PM_OPS(ug3105_pm_ops, ug3105_suspend,
0466             ug3105_resume);
0467 
0468 static const struct i2c_device_id ug3105_id[] = {
0469     { "ug3105" },
0470     { }
0471 };
0472 MODULE_DEVICE_TABLE(i2c, ug3105_id);
0473 
0474 static struct i2c_driver ug3105_i2c_driver = {
0475     .driver = {
0476         .name = "ug3105",
0477         .pm = &ug3105_pm_ops,
0478     },
0479     .probe_new = ug3105_probe,
0480     .id_table = ug3105_id,
0481 };
0482 module_i2c_driver(ug3105_i2c_driver);
0483 
0484 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com");
0485 MODULE_DESCRIPTION("uPI uG3105 battery monitor driver");
0486 MODULE_LICENSE("GPL");