/* Enumeration as in datasheet. Individual bits are mutually exclusive. */ enum ltc4162l_state {
battery_detection = 2048,
charger_suspended = 256,
precharge = 128, /* trickle on low bat voltage */
cc_cv_charge = 64, /* normal charge */
ntc_pause = 32,
timer_term = 16,
c_over_x_term = 8, /* battery is full */
max_charge_time_fault = 4,
bat_missing_fault = 2,
bat_short_fault = 1
};
/* Individual bits are mutually exclusive. Only active in charging states.*/ enum ltc4162l_charge_status {
ilim_reg_active = 32,
thermal_reg_active = 16,
vin_uvcl_active = 8,
iin_limit_active = 4,
constant_current = 2,
constant_voltage = 1,
charger_off = 0
};
/* Magic number to write to ARM_SHIP_MODE register */ #define LTC4162L_ARM_SHIP_MODE_MAGIC 21325
struct ltc4162l_info;
struct ltc4162l_chip_info { constchar *name; int (*get_vbat)(struct ltc4162l_info *info, unsignedint reg, union power_supply_propval *val); int (*get_vcharge)(struct ltc4162l_info *info, unsignedint reg, union power_supply_propval *val); int (*set_vcharge)(struct ltc4162l_info *info, unsignedint reg, unsignedint value); int (*get_die_temp)(struct ltc4162l_info *info, union power_supply_propval *val); unsignedint ibat_resolution_pv; unsignedint vin_resolution_uv;
u8 telemetry_mask;
};
struct ltc4162l_info { struct i2c_client *client; struct regmap *regmap; struct power_supply *charger; conststruct ltc4162l_chip_info *chip_info;
u32 rsnsb; /* Series resistor that sets charge current, microOhm */
u32 rsnsi; /* Series resistor to measure input current, microOhm */
u8 cell_count; /* Number of connected cells, 0 while unknown */
};
static u8 ltc4162l_get_cell_count(struct ltc4162l_info *info)
{ int ret; unsignedint val;
/* Once read successfully */ if (info->cell_count) return info->cell_count;
ret = regmap_read(info->regmap, LTC4162L_CHEM_CELLS_REG, &val); if (ret) return 0;
/* Lower 4 bits is the cell count, or 0 if the chip doesn't know yet */
val &= 0x0f; if (!val) return 0;
/* Once determined, keep the value */
info->cell_count = val;
return val;
};
static u8 ltc4162l_get_chem_type(struct ltc4162l_info *info)
{ int ret; unsignedint val;
ret = regmap_read(info->regmap, LTC4162L_CHEM_CELLS_REG, &val); if (ret) return ret;
return FIELD_GET(LTC4162L_CHEM_MASK, val);
};
/* Convert enum value to POWER_SUPPLY_STATUS value */ staticint ltc4162l_state_decode(enum ltc4162l_state value)
{ switch (value) { case precharge: case cc_cv_charge: return POWER_SUPPLY_STATUS_CHARGING; case c_over_x_term: return POWER_SUPPLY_STATUS_FULL; case bat_missing_fault: case bat_short_fault: return POWER_SUPPLY_STATUS_UNKNOWN; default: return POWER_SUPPLY_STATUS_NOT_CHARGING;
}
};
staticint ltc4162l_get_status(struct ltc4162l_info *info, union power_supply_propval *val)
{ unsignedint regval; int ret;
ret = regmap_read(info->regmap, LTC4162L_CHARGER_STATE, ®val); if (ret) {
dev_err(&info->client->dev, "Failed to read CHARGER_STATE\n"); return ret;
}
val->intval = ltc4162l_state_decode(regval);
return 0;
}
staticint ltc4162l_charge_status_decode(enum ltc4162l_charge_status value)
{ if (!value) return POWER_SUPPLY_CHARGE_TYPE_NONE;
/* constant voltage/current and input_current limit are "fast" modes */ if (value <= iin_limit_active) return POWER_SUPPLY_CHARGE_TYPE_FAST;
/* Anything that's not fast we'll return as trickle */ return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
}
staticint ltc4162l_get_charge_type(struct ltc4162l_info *info, union power_supply_propval *val)
{ unsignedint regval; int ret;
ret = regmap_read(info->regmap, LTC4162L_CHARGE_STATUS, ®val); if (ret) return ret;
staticint ltc4162l_state_to_health(enum ltc4162l_state value)
{ switch (value) { case ntc_pause: return POWER_SUPPLY_HEALTH_OVERHEAT; case timer_term: return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; case max_charge_time_fault: return POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE; case bat_missing_fault: return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; case bat_short_fault: return POWER_SUPPLY_HEALTH_DEAD; default: return POWER_SUPPLY_HEALTH_GOOD;
}
}
staticint ltc4162l_get_health(struct ltc4162l_info *info, union power_supply_propval *val)
{ unsignedint regval; int ret;
ret = regmap_read(info->regmap, LTC4162L_CHARGER_STATE, ®val); if (ret) return ret;
val->intval = ltc4162l_state_to_health(regval);
return 0;
}
staticint ltc4162l_get_online(struct ltc4162l_info *info, union power_supply_propval *val)
{ unsignedint regval; int ret;
ret = regmap_read(info->regmap, LTC4162L_SYSTEM_STATUS_REG, ®val); if (ret) return ret;
/* BIT(2) indicates if input voltage is sufficient to charge */
val->intval = !!(regval & BIT(2));
return 0;
}
staticint ltc4162l_get_vbat(struct ltc4162l_info *info, unsignedint reg, union power_supply_propval *val)
{ unsignedint regval, chem_type; int ret;
ret = regmap_read(info->regmap, reg, ®val); if (ret) return ret;
/* * cell_count × scaling factor * For ltc4162-s, it uses a cell_count value of 2 for each group of 3 * physical (2V) cells, thus will return 2, 4, 6, 8 for 6V, 12V, 18V, * and 24V respectively, and has to divide by 2 to multiply the scale * factor by 1, 2, 3, or 4 to represent a 6V, 12V, 18V, or 24V battery * respectively.
*/
chem_type = ltc4162l_get_chem_type(info); switch (chem_type) { case ltc4162_lad ... ltc4162_fst:
regval *= 1924;
regval *= ltc4162l_get_cell_count(info);
regval /= 10;
val->intval = regval;
staticint ltc4162l_get_input_current(struct ltc4162l_info *info, union power_supply_propval *val)
{ conststruct ltc4162l_chip_info *chip_info = info->chip_info; unsignedint regval; int ret;
ret = regmap_read(info->regmap, LTC4162L_IIN, ®val); if (ret) return ret;
ret = (s16)(regval & 0xFFFF);
ret *= chip_info->ibat_resolution_pv;
ret /= info->rsnsi;
val->intval = ret;
return 0;
}
staticint ltc4162l_get_icharge(struct ltc4162l_info *info, unsignedint reg, union power_supply_propval *val)
{ unsignedint regval; int ret;
ret = regmap_read(info->regmap, reg, ®val); if (ret) return ret;
regval &= GENMASK(5, 0);
/* The charge current servo level: (icharge_dac + 1) × 1mV/RSNSB */
++regval;
val->intval = 10000u * mult_frac(regval, 100000u, info->rsnsb);
return 0;
}
staticint ltc4162l_set_icharge(struct ltc4162l_info *info, unsignedint reg, unsignedint value)
{
value = mult_frac(value, info->rsnsb, 100000u);
value /= 10000u;
/* Round to lowest possible */ if (value)
--value;
if (value > 31) return -EINVAL;
return regmap_write(info->regmap, reg, value);
}
staticint ltc4162l_get_vcharge(struct ltc4162l_info *info, unsignedint reg, union power_supply_propval *val)
{ unsignedint regval, chem_type; int ret;
u32 voltage;
ret = regmap_read(info->regmap, reg, ®val); if (ret) return ret;
regval &= GENMASK(5, 0);
/* * charge voltage setting can be computed from * cell_count × (vcharge_setting × a + b) * where vcharge_setting ranges from 0 to c (d). * for ltc4162l: a = 12.5mV , b = 3.8125V, c = 31, d = 4.2Vmax * for ltc4162f: a = 12.5mV , b = 3.4125V, c = 31, d = 3.8Vmax * * for ltc4162s, the charge voltage setting can be computed from * N x (vcharge_setting x 28.571mV + 6.0V) * where N is 1, 2, 3, or 4 for 6V, 12V, 18V, or 24V battery respectively, * and vcharge_setting ranges from 0 to 31
*/
chem_type = ltc4162l_get_chem_type(info); switch (chem_type) { case ltc4162_lad ... ltc4162_l40:
voltage = 3812500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0; case ltc4162_fad ... ltc4162_fst:
voltage = 3412500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0; case ltc4162_sst ... ltc4162_sad:
voltage = 6000000 + (regval * 28571);
voltage *= ltc4162l_get_cell_count(info) / 2;
val->intval = voltage;
return 0; default: return -EINVAL;
}
}
staticint ltc4015_get_vcharge(struct ltc4162l_info *info, unsignedint reg, union power_supply_propval *val)
{ unsignedint regval, chem_type; int ret;
u32 voltage;
ret = regmap_read(info->regmap, reg, ®val); if (ret) return ret;
regval &= GENMASK(5, 0);
/* * charge voltage setting can be computed from: * cell_count × (vcharge_setting × a + b) * where vcharge_setting ranges from 0 to c (d). * Li-Ion: a = 1/80V, b = 3.8125V, c = 31, d = 4.2Vmax * LiFePO4: a = 1/80V, b = 3.4125V, c = 31, d = 3.8Vmax * Lead Acid: a = 1/105V, b = 2V, c = 35, d = 2.6Vmax
*/
chem_type = ltc4162l_get_chem_type(info); switch (chem_type) { case ltc4162_lad ... ltc4162_l40:
voltage = 3812500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0; case ltc4162_fad ... ltc4162_fst:
voltage = 3412500 + (regval * 12500);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
return 0; case ltc4162_sst - 1 ... ltc4162_sad - 1:
voltage = 2000000 + mult_frac(regval, 1000000, 105);
voltage *= ltc4162l_get_cell_count(info);
val->intval = voltage;
ret = regmap_read(info->regmap, LTC4162L_CHARGE_STATUS, ®val); if (ret) return ret;
/* Only one bit is set according to datasheet, let's be safe here */ for (mask = 32, index = 0; mask != 0; mask >>= 1, ++index) { if (regval & mask) {
result = ltc4162l_charge_status_name[index]; break;
}
}
staticbool ltc4162l_is_writeable_reg(struct device *dev, unsignedint reg)
{ /* all registers up to this one are writeable */ if (reg <= LTC4162L_CHARGER_CONFIG_BITS) returntrue;
/* The ALERTS registers can be written to clear alerts */ if (reg >= LTC4162L_LIMIT_ALERTS_REG &&
reg <= LTC4162L_CHARGE_STATUS_ALERTS_REG) returntrue;
returnfalse;
}
staticbool ltc4162l_is_volatile_reg(struct device *dev, unsignedint reg)
{ /* all registers after this one are read-only status registers */ return reg > LTC4162L_CHARGER_CONFIG_BITS;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
dev_err(dev, "No support for SMBUS_WORD_DATA\n"); return -ENODEV;
}
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM;
/* Duplicate the default descriptor to set name based on chip_info. */
desc = devm_kmemdup(dev, <c4162l_desc, sizeof(struct power_supply_desc), GFP_KERNEL); if (!desc) return -ENOMEM;
desc->name = chip_info->name;
info->charger = devm_power_supply_register(dev, desc, <c4162l_config); if (IS_ERR(info->charger)) {
dev_err(dev, "Failed to register charger\n"); return PTR_ERR(info->charger);
}
/* Disable the threshold alerts, we're not using them */
regmap_write(info->regmap, LTC4162L_EN_LIMIT_ALERTS_REG, 0);
/* Enable interrupts on all status changes */
regmap_write(info->regmap, LTC4162L_EN_CHARGER_STATE_ALERTS_REG,
0x1fff);
regmap_write(info->regmap, LTC4162L_EN_CHARGE_STATUS_ALERTS_REG, 0x1f);
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.