staticbool no_current_sense_res;
module_param(no_current_sense_res, bool, 0444);
MODULE_PARM_DESC(no_current_sense_res, "No (or broken) current sense resistor");
struct axp288_fg_info { struct device *dev; struct regmap *regmap; int irq[AXP288_FG_INTR_NUM]; struct iio_channel *iio_channel[IIO_CHANNEL_NUM]; struct power_supply *bat; struct mutex lock; int status; int max_volt; int pwr_op; int low_cap; struct dentry *debug_file;
char valid; /* zero until following fields are valid */ unsignedlong last_updated; /* in jiffies */
int pwr_stat; int fg_res; int bat_volt; int d_curr; int c_curr; int ocv; int fg_cc_mtr1; int fg_des_cap1;
};
staticenum power_supply_property fuel_gauge_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
POWER_SUPPLY_PROP_TECHNOLOGY, /* The 3 props below are not used when no_current_sense_res is set */
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
staticint fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
{ unsignedint val; int ret;
ret = iosf_mbi_block_punit_i2c_access(); if (ret < 0) return ret;
ret = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); if (ret < 0) goto out;
info->pwr_stat = ret;
if (no_current_sense_res)
ret = fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG); else
ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES); if (ret < 0) goto out;
info->fg_res = ret;
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &info->bat_volt); if (ret < 0) goto out;
ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG); if (ret < 0) goto out;
info->ocv = ret;
if (no_current_sense_res) goto out_no_current_sense_res;
if (info->pwr_stat & PS_STAT_BAT_CHRG_DIR) {
info->d_curr = 0;
ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &info->c_curr); if (ret < 0) goto out;
} else {
info->c_curr = 0;
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &info->d_curr); if (ret < 0) goto out;
}
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG); if (ret < 0) goto out;
info->fg_cc_mtr1 = ret;
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG); if (ret < 0) goto out;
info->fg_des_cap1 = ret;
staticint fuel_gauge_battery_health(struct axp288_fg_info *info)
{ int vocv = VOLTAGE_FROM_ADC(info->ocv); int health = POWER_SUPPLY_HEALTH_UNKNOWN;
if (vocv > info->max_volt)
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; else
health = POWER_SUPPLY_HEALTH_GOOD;
return health;
}
staticint fuel_gauge_get_property(struct power_supply *ps, enum power_supply_property prop, union power_supply_propval *val)
{ struct axp288_fg_info *info = power_supply_get_drvdata(ps); int ret, value;
mutex_lock(&info->lock);
ret = fuel_gauge_update_registers(info); if (ret < 0) goto out;
switch (prop) { case POWER_SUPPLY_PROP_STATUS:
fuel_gauge_get_status(info);
val->intval = info->status; break; case POWER_SUPPLY_PROP_HEALTH:
val->intval = fuel_gauge_battery_health(info); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW:
value = VOLTAGE_FROM_ADC(info->bat_volt);
val->intval = PROP_VOLT(value); break; case POWER_SUPPLY_PROP_VOLTAGE_OCV:
value = VOLTAGE_FROM_ADC(info->ocv);
val->intval = PROP_VOLT(value); break; case POWER_SUPPLY_PROP_CURRENT_NOW: if (info->d_curr > 0)
value = -1 * info->d_curr; else
value = info->c_curr;
val->intval = PROP_CURR(value); break; case POWER_SUPPLY_PROP_PRESENT: if (info->pwr_op & CHRG_STAT_BAT_PRESENT)
val->intval = 1; else
val->intval = 0; break; case POWER_SUPPLY_PROP_CAPACITY: if (!(info->fg_res & FG_REP_CAP_VALID))
dev_err(info->dev, "capacity measurement not valid\n");
val->intval = (info->fg_res & FG_REP_CAP_VAL_MASK); break; case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
val->intval = (info->low_cap & 0x0f); break; case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; case POWER_SUPPLY_PROP_CHARGE_NOW:
val->intval = info->fg_cc_mtr1 * FG_DES_CAP_RES_LSB; break; case POWER_SUPPLY_PROP_CHARGE_FULL:
val->intval = info->fg_des_cap1 * FG_DES_CAP_RES_LSB; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = PROP_VOLT(info->max_volt); break; default:
ret = -EINVAL;
}
for (i = 0; i < AXP288_FG_INTR_NUM; i++) { if (info->irq[i] == irq) break;
}
if (i >= AXP288_FG_INTR_NUM) {
dev_warn(info->dev, "spurious interrupt!!\n"); return IRQ_NONE;
}
switch (i) { case QWBTU_IRQ:
dev_info(info->dev, "Quit Battery under temperature in work mode IRQ (QWBTU)\n"); break; case WBTU_IRQ:
dev_info(info->dev, "Battery under temperature in work mode IRQ (WBTU)\n"); break; case QWBTO_IRQ:
dev_info(info->dev, "Quit Battery over temperature in work mode IRQ (QWBTO)\n"); break; case WBTO_IRQ:
dev_info(info->dev, "Battery over temperature in work mode IRQ (WBTO)\n"); break; case WL2_IRQ:
dev_info(info->dev, "Low Batt Warning(2) INTR\n"); break; case WL1_IRQ:
dev_info(info->dev, "Low Batt Warning(1) INTR\n"); break; default:
dev_warn(info->dev, "Spurious Interrupt!!!\n");
}
mutex_lock(&info->lock);
info->valid = 0; /* Force updating of the cached registers */
mutex_unlock(&info->lock);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.