Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver
0004  *
0005  * Copyright (C) 2020-2021 Andrejus Basovas <xxx@yyy.tld>
0006  * Copyright (C) 2016-2021 Hans de Goede <hdegoede@redhat.com>
0007  * Copyright (C) 2014 Intel Corporation
0008  *
0009  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0010  */
0011 
0012 #include <linux/acpi.h>
0013 #include <linux/dmi.h>
0014 #include <linux/module.h>
0015 #include <linux/kernel.h>
0016 #include <linux/device.h>
0017 #include <linux/regmap.h>
0018 #include <linux/jiffies.h>
0019 #include <linux/interrupt.h>
0020 #include <linux/mfd/axp20x.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/power_supply.h>
0023 #include <linux/iio/consumer.h>
0024 #include <asm/unaligned.h>
0025 #include <asm/iosf_mbi.h>
0026 
0027 #define PS_STAT_VBUS_TRIGGER            (1 << 0)
0028 #define PS_STAT_BAT_CHRG_DIR            (1 << 2)
0029 #define PS_STAT_VBAT_ABOVE_VHOLD        (1 << 3)
0030 #define PS_STAT_VBUS_VALID          (1 << 4)
0031 #define PS_STAT_VBUS_PRESENT            (1 << 5)
0032 
0033 #define CHRG_STAT_BAT_SAFE_MODE         (1 << 3)
0034 #define CHRG_STAT_BAT_VALID         (1 << 4)
0035 #define CHRG_STAT_BAT_PRESENT           (1 << 5)
0036 #define CHRG_STAT_CHARGING          (1 << 6)
0037 #define CHRG_STAT_PMIC_OTP          (1 << 7)
0038 
0039 #define CHRG_CCCV_CC_MASK           0xf     /* 4 bits */
0040 #define CHRG_CCCV_CC_BIT_POS            0
0041 #define CHRG_CCCV_CC_OFFSET         200     /* 200mA */
0042 #define CHRG_CCCV_CC_LSB_RES            200     /* 200mA */
0043 #define CHRG_CCCV_ITERM_20P         (1 << 4)    /* 20% of CC */
0044 #define CHRG_CCCV_CV_MASK           0x60        /* 2 bits */
0045 #define CHRG_CCCV_CV_BIT_POS            5
0046 #define CHRG_CCCV_CV_4100MV         0x0     /* 4.10V */
0047 #define CHRG_CCCV_CV_4150MV         0x1     /* 4.15V */
0048 #define CHRG_CCCV_CV_4200MV         0x2     /* 4.20V */
0049 #define CHRG_CCCV_CV_4350MV         0x3     /* 4.35V */
0050 #define CHRG_CCCV_CHG_EN            (1 << 7)
0051 
0052 #define FG_CNTL_OCV_ADJ_STAT            (1 << 2)
0053 #define FG_CNTL_OCV_ADJ_EN          (1 << 3)
0054 #define FG_CNTL_CAP_ADJ_STAT            (1 << 4)
0055 #define FG_CNTL_CAP_ADJ_EN          (1 << 5)
0056 #define FG_CNTL_CC_EN               (1 << 6)
0057 #define FG_CNTL_GAUGE_EN            (1 << 7)
0058 
0059 #define FG_15BIT_WORD_VALID         (1 << 15)
0060 #define FG_15BIT_VAL_MASK           0x7fff
0061 
0062 #define FG_REP_CAP_VALID            (1 << 7)
0063 #define FG_REP_CAP_VAL_MASK         0x7F
0064 
0065 #define FG_DES_CAP1_VALID           (1 << 7)
0066 #define FG_DES_CAP_RES_LSB          1456    /* 1.456mAhr */
0067 
0068 #define FG_DES_CC_RES_LSB           1456    /* 1.456mAhr */
0069 
0070 #define FG_OCV_CAP_VALID            (1 << 7)
0071 #define FG_OCV_CAP_VAL_MASK         0x7F
0072 #define FG_CC_CAP_VALID             (1 << 7)
0073 #define FG_CC_CAP_VAL_MASK          0x7F
0074 
0075 #define FG_LOW_CAP_THR1_MASK            0xf0    /* 5% tp 20% */
0076 #define FG_LOW_CAP_THR1_VAL         0xa0    /* 15 perc */
0077 #define FG_LOW_CAP_THR2_MASK            0x0f    /* 0% to 15% */
0078 #define FG_LOW_CAP_WARN_THR         14  /* 14 perc */
0079 #define FG_LOW_CAP_CRIT_THR         4   /* 4 perc */
0080 #define FG_LOW_CAP_SHDN_THR         0   /* 0 perc */
0081 
0082 #define DEV_NAME                "axp288_fuel_gauge"
0083 
0084 /* 1.1mV per LSB expressed in uV */
0085 #define VOLTAGE_FROM_ADC(a)         ((a * 11) / 10)
0086 /* properties converted to uV, uA */
0087 #define PROP_VOLT(a)                ((a) * 1000)
0088 #define PROP_CURR(a)                ((a) * 1000)
0089 
0090 #define AXP288_REG_UPDATE_INTERVAL      (60 * HZ)
0091 #define AXP288_FG_INTR_NUM          6
0092 
0093 #define AXP288_QUIRK_NO_BATTERY         BIT(0)
0094 
0095 static bool no_current_sense_res;
0096 module_param(no_current_sense_res, bool, 0444);
0097 MODULE_PARM_DESC(no_current_sense_res, "No (or broken) current sense resistor");
0098 
0099 enum {
0100     QWBTU_IRQ = 0,
0101     WBTU_IRQ,
0102     QWBTO_IRQ,
0103     WBTO_IRQ,
0104     WL2_IRQ,
0105     WL1_IRQ,
0106 };
0107 
0108 enum {
0109     BAT_CHRG_CURR,
0110     BAT_D_CURR,
0111     BAT_VOLT,
0112     IIO_CHANNEL_NUM
0113 };
0114 
0115 struct axp288_fg_info {
0116     struct device *dev;
0117     struct regmap *regmap;
0118     int irq[AXP288_FG_INTR_NUM];
0119     struct iio_channel *iio_channel[IIO_CHANNEL_NUM];
0120     struct power_supply *bat;
0121     struct mutex lock;
0122     int status;
0123     int max_volt;
0124     int pwr_op;
0125     int low_cap;
0126     struct dentry *debug_file;
0127 
0128     char valid;                 /* zero until following fields are valid */
0129     unsigned long last_updated; /* in jiffies */
0130 
0131     int pwr_stat;
0132     int fg_res;
0133     int bat_volt;
0134     int d_curr;
0135     int c_curr;
0136     int ocv;
0137     int fg_cc_mtr1;
0138     int fg_des_cap1;
0139 };
0140 
0141 static enum power_supply_property fuel_gauge_props[] = {
0142     POWER_SUPPLY_PROP_STATUS,
0143     POWER_SUPPLY_PROP_PRESENT,
0144     POWER_SUPPLY_PROP_HEALTH,
0145     POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
0146     POWER_SUPPLY_PROP_VOLTAGE_NOW,
0147     POWER_SUPPLY_PROP_VOLTAGE_OCV,
0148     POWER_SUPPLY_PROP_CAPACITY,
0149     POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
0150     POWER_SUPPLY_PROP_TECHNOLOGY,
0151     /* The 3 props below are not used when no_current_sense_res is set */
0152     POWER_SUPPLY_PROP_CHARGE_FULL,
0153     POWER_SUPPLY_PROP_CHARGE_NOW,
0154     POWER_SUPPLY_PROP_CURRENT_NOW,
0155 };
0156 
0157 static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
0158 {
0159     unsigned int val;
0160     int ret;
0161 
0162     ret = regmap_read(info->regmap, reg, &val);
0163     if (ret < 0) {
0164         dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
0165         return ret;
0166     }
0167 
0168     return val;
0169 }
0170 
0171 static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
0172 {
0173     int ret;
0174 
0175     ret = regmap_write(info->regmap, reg, (unsigned int)val);
0176 
0177     if (ret < 0)
0178         dev_err(info->dev, "Error writing reg 0x%02x err: %d\n", reg, ret);
0179 
0180     return ret;
0181 }
0182 
0183 static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg)
0184 {
0185     unsigned char buf[2];
0186     int ret;
0187 
0188     ret = regmap_bulk_read(info->regmap, reg, buf, 2);
0189     if (ret < 0) {
0190         dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
0191         return ret;
0192     }
0193 
0194     ret = get_unaligned_be16(buf);
0195     if (!(ret & FG_15BIT_WORD_VALID)) {
0196         dev_err(info->dev, "Error reg 0x%02x contents not valid\n", reg);
0197         return -ENXIO;
0198     }
0199 
0200     return ret & FG_15BIT_VAL_MASK;
0201 }
0202 
0203 static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
0204 {
0205     unsigned char buf[2];
0206     int ret;
0207 
0208     ret = regmap_bulk_read(info->regmap, reg, buf, 2);
0209     if (ret < 0) {
0210         dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
0211         return ret;
0212     }
0213 
0214     /* 12-bit data values have upper 8 bits in buf[0], lower 4 in buf[1] */
0215     return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f);
0216 }
0217 
0218 static int fuel_gauge_update_registers(struct axp288_fg_info *info)
0219 {
0220     int ret;
0221 
0222     if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))
0223         return 0;
0224 
0225     dev_dbg(info->dev, "Fuel Gauge updating register values...\n");
0226 
0227     ret = iosf_mbi_block_punit_i2c_access();
0228     if (ret < 0)
0229         return ret;
0230 
0231     ret = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
0232     if (ret < 0)
0233         goto out;
0234     info->pwr_stat = ret;
0235 
0236     if (no_current_sense_res)
0237         ret = fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG);
0238     else
0239         ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
0240     if (ret < 0)
0241         goto out;
0242     info->fg_res = ret;
0243 
0244     ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &info->bat_volt);
0245     if (ret < 0)
0246         goto out;
0247 
0248     ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
0249     if (ret < 0)
0250         goto out;
0251     info->ocv = ret;
0252 
0253     if (no_current_sense_res)
0254         goto out_no_current_sense_res;
0255 
0256     if (info->pwr_stat & PS_STAT_BAT_CHRG_DIR) {
0257         info->d_curr = 0;
0258         ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &info->c_curr);
0259         if (ret < 0)
0260             goto out;
0261     } else {
0262         info->c_curr = 0;
0263         ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &info->d_curr);
0264         if (ret < 0)
0265             goto out;
0266     }
0267 
0268     ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
0269     if (ret < 0)
0270         goto out;
0271     info->fg_cc_mtr1 = ret;
0272 
0273     ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
0274     if (ret < 0)
0275         goto out;
0276     info->fg_des_cap1 = ret;
0277 
0278 out_no_current_sense_res:
0279     info->last_updated = jiffies;
0280     info->valid = 1;
0281     ret = 0;
0282 out:
0283     iosf_mbi_unblock_punit_i2c_access();
0284     return ret;
0285 }
0286 
0287 static void fuel_gauge_get_status(struct axp288_fg_info *info)
0288 {
0289     int pwr_stat = info->pwr_stat;
0290     int fg_res = info->fg_res;
0291     int curr = info->d_curr;
0292 
0293     /* Report full if Vbus is valid and the reported capacity is 100% */
0294     if (!(pwr_stat & PS_STAT_VBUS_VALID))
0295         goto not_full;
0296 
0297     if (!(fg_res & FG_REP_CAP_VALID))
0298         goto not_full;
0299 
0300     fg_res &= ~FG_REP_CAP_VALID;
0301     if (fg_res == 100) {
0302         info->status = POWER_SUPPLY_STATUS_FULL;
0303         return;
0304     }
0305 
0306     /*
0307      * Sometimes the charger turns itself off before fg-res reaches 100%.
0308      * When this happens the AXP288 reports a not-charging status and
0309      * 0 mA discharge current.
0310      */
0311     if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR) || no_current_sense_res)
0312         goto not_full;
0313 
0314     if (curr == 0) {
0315         info->status = POWER_SUPPLY_STATUS_FULL;
0316         return;
0317     }
0318 
0319 not_full:
0320     if (pwr_stat & PS_STAT_BAT_CHRG_DIR)
0321         info->status = POWER_SUPPLY_STATUS_CHARGING;
0322     else
0323         info->status = POWER_SUPPLY_STATUS_DISCHARGING;
0324 }
0325 
0326 static int fuel_gauge_battery_health(struct axp288_fg_info *info)
0327 {
0328     int vocv = VOLTAGE_FROM_ADC(info->ocv);
0329     int health = POWER_SUPPLY_HEALTH_UNKNOWN;
0330 
0331     if (vocv > info->max_volt)
0332         health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
0333     else
0334         health = POWER_SUPPLY_HEALTH_GOOD;
0335 
0336     return health;
0337 }
0338 
0339 static int fuel_gauge_get_property(struct power_supply *ps,
0340         enum power_supply_property prop,
0341         union power_supply_propval *val)
0342 {
0343     struct axp288_fg_info *info = power_supply_get_drvdata(ps);
0344     int ret, value;
0345 
0346     mutex_lock(&info->lock);
0347 
0348     ret = fuel_gauge_update_registers(info);
0349     if (ret < 0)
0350         goto out;
0351 
0352     switch (prop) {
0353     case POWER_SUPPLY_PROP_STATUS:
0354         fuel_gauge_get_status(info);
0355         val->intval = info->status;
0356         break;
0357     case POWER_SUPPLY_PROP_HEALTH:
0358         val->intval = fuel_gauge_battery_health(info);
0359         break;
0360     case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0361         value = VOLTAGE_FROM_ADC(info->bat_volt);
0362         val->intval = PROP_VOLT(value);
0363         break;
0364     case POWER_SUPPLY_PROP_VOLTAGE_OCV:
0365         value = VOLTAGE_FROM_ADC(info->ocv);
0366         val->intval = PROP_VOLT(value);
0367         break;
0368     case POWER_SUPPLY_PROP_CURRENT_NOW:
0369         if (info->d_curr > 0)
0370             value = -1 * info->d_curr;
0371         else
0372             value = info->c_curr;
0373 
0374         val->intval = PROP_CURR(value);
0375         break;
0376     case POWER_SUPPLY_PROP_PRESENT:
0377         if (info->pwr_op & CHRG_STAT_BAT_PRESENT)
0378             val->intval = 1;
0379         else
0380             val->intval = 0;
0381         break;
0382     case POWER_SUPPLY_PROP_CAPACITY:
0383         if (!(info->fg_res & FG_REP_CAP_VALID))
0384             dev_err(info->dev, "capacity measurement not valid\n");
0385         val->intval = (info->fg_res & FG_REP_CAP_VAL_MASK);
0386         break;
0387     case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
0388         val->intval = (info->low_cap & 0x0f);
0389         break;
0390     case POWER_SUPPLY_PROP_TECHNOLOGY:
0391         val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
0392         break;
0393     case POWER_SUPPLY_PROP_CHARGE_NOW:
0394         val->intval = info->fg_cc_mtr1 * FG_DES_CAP_RES_LSB;
0395         break;
0396     case POWER_SUPPLY_PROP_CHARGE_FULL:
0397         val->intval = info->fg_des_cap1 * FG_DES_CAP_RES_LSB;
0398         break;
0399     case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
0400         val->intval = PROP_VOLT(info->max_volt);
0401         break;
0402     default:
0403         ret = -EINVAL;
0404     }
0405 
0406 out:
0407     mutex_unlock(&info->lock);
0408     return ret;
0409 }
0410 
0411 static int fuel_gauge_set_property(struct power_supply *ps,
0412         enum power_supply_property prop,
0413         const union power_supply_propval *val)
0414 {
0415     struct axp288_fg_info *info = power_supply_get_drvdata(ps);
0416     int new_low_cap, ret = 0;
0417 
0418     mutex_lock(&info->lock);
0419     switch (prop) {
0420     case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
0421         if ((val->intval < 0) || (val->intval > 15)) {
0422             ret = -EINVAL;
0423             break;
0424         }
0425         new_low_cap = info->low_cap;
0426         new_low_cap &= 0xf0;
0427         new_low_cap |= (val->intval & 0xf);
0428         ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, new_low_cap);
0429         if (ret == 0)
0430             info->low_cap = new_low_cap;
0431         break;
0432     default:
0433         ret = -EINVAL;
0434         break;
0435     }
0436 
0437     mutex_unlock(&info->lock);
0438     return ret;
0439 }
0440 
0441 static int fuel_gauge_property_is_writeable(struct power_supply *psy,
0442     enum power_supply_property psp)
0443 {
0444     int ret;
0445 
0446     switch (psp) {
0447     case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
0448         ret = 1;
0449         break;
0450     default:
0451         ret = 0;
0452     }
0453 
0454     return ret;
0455 }
0456 
0457 static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev)
0458 {
0459     struct axp288_fg_info *info = dev;
0460     int i;
0461 
0462     for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
0463         if (info->irq[i] == irq)
0464             break;
0465     }
0466 
0467     if (i >= AXP288_FG_INTR_NUM) {
0468         dev_warn(info->dev, "spurious interrupt!!\n");
0469         return IRQ_NONE;
0470     }
0471 
0472     switch (i) {
0473     case QWBTU_IRQ:
0474         dev_info(info->dev, "Quit Battery under temperature in work mode IRQ (QWBTU)\n");
0475         break;
0476     case WBTU_IRQ:
0477         dev_info(info->dev, "Battery under temperature in work mode IRQ (WBTU)\n");
0478         break;
0479     case QWBTO_IRQ:
0480         dev_info(info->dev, "Quit Battery over temperature in work mode IRQ (QWBTO)\n");
0481         break;
0482     case WBTO_IRQ:
0483         dev_info(info->dev, "Battery over temperature in work mode IRQ (WBTO)\n");
0484         break;
0485     case WL2_IRQ:
0486         dev_info(info->dev, "Low Batt Warning(2) INTR\n");
0487         break;
0488     case WL1_IRQ:
0489         dev_info(info->dev, "Low Batt Warning(1) INTR\n");
0490         break;
0491     default:
0492         dev_warn(info->dev, "Spurious Interrupt!!!\n");
0493     }
0494 
0495     mutex_lock(&info->lock);
0496     info->valid = 0; /* Force updating of the cached registers */
0497     mutex_unlock(&info->lock);
0498 
0499     power_supply_changed(info->bat);
0500     return IRQ_HANDLED;
0501 }
0502 
0503 static void fuel_gauge_external_power_changed(struct power_supply *psy)
0504 {
0505     struct axp288_fg_info *info = power_supply_get_drvdata(psy);
0506 
0507     mutex_lock(&info->lock);
0508     info->valid = 0; /* Force updating of the cached registers */
0509     mutex_unlock(&info->lock);
0510     power_supply_changed(info->bat);
0511 }
0512 
0513 static struct power_supply_desc fuel_gauge_desc = {
0514     .name           = DEV_NAME,
0515     .type           = POWER_SUPPLY_TYPE_BATTERY,
0516     .properties     = fuel_gauge_props,
0517     .num_properties     = ARRAY_SIZE(fuel_gauge_props),
0518     .get_property       = fuel_gauge_get_property,
0519     .set_property       = fuel_gauge_set_property,
0520     .property_is_writeable  = fuel_gauge_property_is_writeable,
0521     .external_power_changed = fuel_gauge_external_power_changed,
0522 };
0523 
0524 /*
0525  * Some devices have no battery (HDMI sticks) and the axp288 battery's
0526  * detection reports one despite it not being there.
0527  * Please keep this listed sorted alphabetically.
0528  */
0529 static const struct dmi_system_id axp288_quirks[] = {
0530     {
0531         /* ACEPC T8 Cherry Trail Z8350 mini PC */
0532         .matches = {
0533             DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
0534             DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
0535             DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "T8"),
0536             /* also match on somewhat unique bios-version */
0537             DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1.000"),
0538         },
0539         .driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
0540     },
0541     {
0542         /* ACEPC T11 Cherry Trail Z8350 mini PC */
0543         .matches = {
0544             DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."),
0545             DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
0546             DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "T11"),
0547             /* also match on somewhat unique bios-version */
0548             DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1.000"),
0549         },
0550         .driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
0551     },
0552     {
0553         /* Intel Cherry Trail Compute Stick, Windows version */
0554         .matches = {
0555             DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
0556             DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"),
0557         },
0558         .driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
0559     },
0560     {
0561         /* Intel Cherry Trail Compute Stick, version without an OS */
0562         .matches = {
0563             DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
0564             DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"),
0565         },
0566         .driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
0567     },
0568     {
0569         /* Meegopad T02 */
0570         .matches = {
0571             DMI_MATCH(DMI_PRODUCT_NAME, "MEEGOPAD T02"),
0572         },
0573         .driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
0574     },
0575     {   /* Mele PCG03 Mini PC */
0576         .matches = {
0577             DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Mini PC"),
0578             DMI_EXACT_MATCH(DMI_BOARD_NAME, "Mini PC"),
0579         },
0580         .driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
0581     },
0582     {
0583         /* Minix Neo Z83-4 mini PC */
0584         .matches = {
0585             DMI_MATCH(DMI_SYS_VENDOR, "MINIX"),
0586             DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
0587         },
0588         .driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
0589     },
0590     {
0591         /*
0592          * One Mix 1, this uses the "T3 MRD" boardname used by
0593          * generic mini PCs, but it is a mini laptop so it does
0594          * actually have a battery!
0595          */
0596         .matches = {
0597             DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
0598             DMI_MATCH(DMI_BIOS_DATE, "06/14/2018"),
0599         },
0600         .driver_data = NULL,
0601     },
0602     {
0603         /*
0604          * Various Ace PC/Meegopad/MinisForum/Wintel Mini-PCs/HDMI-sticks
0605          * This entry must be last because it is generic, this allows
0606          * adding more specifuc quirks overriding this generic entry.
0607          */
0608         .matches = {
0609             DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
0610             DMI_MATCH(DMI_CHASSIS_TYPE, "3"),
0611             DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
0612         },
0613         .driver_data = (void *)AXP288_QUIRK_NO_BATTERY,
0614     },
0615     {}
0616 };
0617 
0618 static int axp288_fuel_gauge_read_initial_regs(struct axp288_fg_info *info)
0619 {
0620     unsigned int val;
0621     int ret;
0622 
0623     /*
0624      * On some devices the fuelgauge and charger parts of the axp288 are
0625      * not used, check that the fuelgauge is enabled (CC_CTRL != 0).
0626      */
0627     ret = regmap_read(info->regmap, AXP20X_CC_CTRL, &val);
0628     if (ret < 0)
0629         return ret;
0630     if (val == 0)
0631         return -ENODEV;
0632 
0633     ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
0634     if (ret < 0)
0635         return ret;
0636 
0637     if (!(ret & FG_DES_CAP1_VALID)) {
0638         dev_err(info->dev, "axp288 not configured by firmware\n");
0639         return -ENODEV;
0640     }
0641 
0642     ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
0643     if (ret < 0)
0644         return ret;
0645     switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
0646     case CHRG_CCCV_CV_4100MV:
0647         info->max_volt = 4100;
0648         break;
0649     case CHRG_CCCV_CV_4150MV:
0650         info->max_volt = 4150;
0651         break;
0652     case CHRG_CCCV_CV_4200MV:
0653         info->max_volt = 4200;
0654         break;
0655     case CHRG_CCCV_CV_4350MV:
0656         info->max_volt = 4350;
0657         break;
0658     }
0659 
0660     ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
0661     if (ret < 0)
0662         return ret;
0663     info->pwr_op = ret;
0664 
0665     ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
0666     if (ret < 0)
0667         return ret;
0668     info->low_cap = ret;
0669 
0670     return 0;
0671 }
0672 
0673 static void axp288_fuel_gauge_release_iio_chans(void *data)
0674 {
0675     struct axp288_fg_info *info = data;
0676     int i;
0677 
0678     for (i = 0; i < IIO_CHANNEL_NUM; i++)
0679         if (!IS_ERR_OR_NULL(info->iio_channel[i]))
0680             iio_channel_release(info->iio_channel[i]);
0681 }
0682 
0683 static int axp288_fuel_gauge_probe(struct platform_device *pdev)
0684 {
0685     struct axp288_fg_info *info;
0686     struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
0687     struct power_supply_config psy_cfg = {};
0688     static const char * const iio_chan_name[] = {
0689         [BAT_CHRG_CURR] = "axp288-chrg-curr",
0690         [BAT_D_CURR] = "axp288-chrg-d-curr",
0691         [BAT_VOLT] = "axp288-batt-volt",
0692     };
0693     const struct dmi_system_id *dmi_id;
0694     struct device *dev = &pdev->dev;
0695     unsigned long quirks = 0;
0696     int i, pirq, ret;
0697 
0698     /*
0699      * Normally the native AXP288 fg/charger drivers are preferred but
0700      * on some devices the ACPI drivers should be used instead.
0701      */
0702     if (!acpi_quirk_skip_acpi_ac_and_battery())
0703         return -ENODEV;
0704 
0705     dmi_id = dmi_first_match(axp288_quirks);
0706     if (dmi_id)
0707         quirks = (unsigned long)dmi_id->driver_data;
0708 
0709     if (quirks & AXP288_QUIRK_NO_BATTERY)
0710         return -ENODEV;
0711 
0712     info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
0713     if (!info)
0714         return -ENOMEM;
0715 
0716     info->dev = dev;
0717     info->regmap = axp20x->regmap;
0718     info->status = POWER_SUPPLY_STATUS_UNKNOWN;
0719     info->valid = 0;
0720 
0721     platform_set_drvdata(pdev, info);
0722 
0723     mutex_init(&info->lock);
0724 
0725     for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
0726         pirq = platform_get_irq(pdev, i);
0727         ret = regmap_irq_get_virq(axp20x->regmap_irqc, pirq);
0728         if (ret < 0)
0729             return dev_err_probe(dev, ret, "getting vIRQ %d\n", pirq);
0730 
0731         info->irq[i] = ret;
0732     }
0733 
0734     for (i = 0; i < IIO_CHANNEL_NUM; i++) {
0735         /*
0736          * Note cannot use devm_iio_channel_get because x86 systems
0737          * lack the device<->channel maps which iio_channel_get will
0738          * try to use when passed a non NULL device pointer.
0739          */
0740         info->iio_channel[i] =
0741             iio_channel_get(NULL, iio_chan_name[i]);
0742         if (IS_ERR(info->iio_channel[i])) {
0743             ret = PTR_ERR(info->iio_channel[i]);
0744             dev_dbg(dev, "error getting iiochan %s: %d\n", iio_chan_name[i], ret);
0745             /* Wait for axp288_adc to load */
0746             if (ret == -ENODEV)
0747                 ret = -EPROBE_DEFER;
0748 
0749             axp288_fuel_gauge_release_iio_chans(info);
0750             return ret;
0751         }
0752     }
0753 
0754     ret = devm_add_action_or_reset(dev, axp288_fuel_gauge_release_iio_chans, info);
0755     if (ret)
0756         return ret;
0757 
0758     ret = iosf_mbi_block_punit_i2c_access();
0759     if (ret < 0)
0760         return ret;
0761 
0762     ret = axp288_fuel_gauge_read_initial_regs(info);
0763     iosf_mbi_unblock_punit_i2c_access();
0764     if (ret < 0)
0765         return ret;
0766 
0767     psy_cfg.drv_data = info;
0768     if (no_current_sense_res)
0769         fuel_gauge_desc.num_properties = ARRAY_SIZE(fuel_gauge_props) - 3;
0770     info->bat = devm_power_supply_register(dev, &fuel_gauge_desc, &psy_cfg);
0771     if (IS_ERR(info->bat)) {
0772         ret = PTR_ERR(info->bat);
0773         dev_err(dev, "failed to register battery: %d\n", ret);
0774         return ret;
0775     }
0776 
0777     for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
0778         ret = devm_request_threaded_irq(dev, info->irq[i], NULL,
0779                         fuel_gauge_thread_handler,
0780                         IRQF_ONESHOT, DEV_NAME, info);
0781         if (ret)
0782             return dev_err_probe(dev, ret, "requesting IRQ %d\n", info->irq[i]);
0783     }
0784 
0785     return 0;
0786 }
0787 
0788 static const struct platform_device_id axp288_fg_id_table[] = {
0789     { .name = DEV_NAME },
0790     {},
0791 };
0792 MODULE_DEVICE_TABLE(platform, axp288_fg_id_table);
0793 
0794 static struct platform_driver axp288_fuel_gauge_driver = {
0795     .probe = axp288_fuel_gauge_probe,
0796     .id_table = axp288_fg_id_table,
0797     .driver = {
0798         .name = DEV_NAME,
0799     },
0800 };
0801 
0802 module_platform_driver(axp288_fuel_gauge_driver);
0803 
0804 MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
0805 MODULE_AUTHOR("Todd Brandt <todd.e.brandt@linux.intel.com>");
0806 MODULE_DESCRIPTION("Xpower AXP288 Fuel Gauge Driver");
0807 MODULE_LICENSE("GPL");