/* Convert register value to current using lookup table */ staticint hw_to_current(constunsignedint *tbl, size_t size, unsignedint val)
{ if (val >= size) return -EINVAL; return tbl[val];
}
/* Convert current to register value using lookup table */ staticint current_to_hw(constunsignedint *tbl, size_t size, unsignedint val)
{
size_t i;
for (i = 0; i < size; i++) if (val < tbl[i]) break; return i > 0 ? i - 1 : -EINVAL;
}
/** *smb347_update_ps_status-refreshesthepowersourcestatus *@smb:pointertosmb347chargerinstance * *Functioncheckswhetheranypowersourceisconnectedtothechargerand *updatesinternalstateaccordingly.Ifthereisachangetopreviousstate *functionreturns%1,otherwise%0andnegativeerrnoincaseoferrror.
*/ staticint smb347_update_ps_status(struct smb347_charger *smb)
{ bool usb = false; bool dc = false; unsignedint val; int ret;
ret = regmap_read(smb->regmap, IRQSTAT_E, &val); if (ret < 0) return ret;
/* *Dcandusbaresetdependingonwhethertheyareenabledin *platformdata_and_whethercorrespondingundervoltageisset.
*/ if (smb->use_mains)
dc = !(val & IRQSTAT_E_DCIN_UV_STAT); if (smb->use_usb)
usb = !(val & IRQSTAT_E_USBIN_UV_STAT);
ret = smb->mains_online != dc || smb->usb_online != usb;
smb->mains_online = dc;
smb->usb_online = usb;
staticint smb347_start_stop_charging(struct smb347_charger *smb)
{ int ret;
/* *Dependingonwhethervalidpowersourceisconnectedornot,we *disableorenablethecharging.Wedoitmanuallybecauseit *dependsonhowtheplatformhasconfiguredthevalidinputs.
*/ if (smb347_is_ps_online(smb)) {
ret = smb347_charging_enable(smb); if (ret < 0)
dev_err(smb->dev, "failed to enable charging\n");
} else {
ret = smb347_charging_disable(smb); if (ret < 0)
dev_err(smb->dev, "failed to disable charging\n");
}
return ret;
}
staticint smb347_set_charge_current(struct smb347_charger *smb)
{ unsignedint id = smb->id; int ret;
if (smb->max_charge_current) {
ret = current_to_hw(fcc_tbl[id], ARRAY_SIZE(fcc_tbl[id]),
smb->max_charge_current); if (ret < 0) return ret;
ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
CFG_CHARGE_CURRENT_FCC_MASK,
ret << CFG_CHARGE_CURRENT_FCC_SHIFT); if (ret < 0) return ret;
}
if (smb->pre_charge_current) {
ret = current_to_hw(pcc_tbl[id], ARRAY_SIZE(pcc_tbl[id]),
smb->pre_charge_current); if (ret < 0) return ret;
ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
CFG_CHARGE_CURRENT_PCC_MASK,
ret << CFG_CHARGE_CURRENT_PCC_SHIFT); if (ret < 0) return ret;
}
if (smb->termination_current) {
ret = current_to_hw(tc_tbl[id], ARRAY_SIZE(tc_tbl[id]),
smb->termination_current); if (ret < 0) return ret;
ret = regmap_update_bits(smb->regmap, CFG_CHARGE_CURRENT,
CFG_CHARGE_CURRENT_TC_MASK, ret); if (ret < 0) return ret;
}
return0;
}
staticint smb347_set_current_limits(struct smb347_charger *smb)
{ unsignedint id = smb->id; int ret;
if (smb->mains_current_limit) {
ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]),
smb->mains_current_limit); if (ret < 0) return ret;
ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
CFG_CURRENT_LIMIT_DC_MASK,
ret << CFG_CURRENT_LIMIT_DC_SHIFT); if (ret < 0) return ret;
}
if (smb->usb_hc_current_limit) {
ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]),
smb->usb_hc_current_limit); if (ret < 0) return ret;
ret = regmap_update_bits(smb->regmap, CFG_CURRENT_LIMIT,
CFG_CURRENT_LIMIT_USB_MASK, ret); if (ret < 0) return ret;
}
return0;
}
staticint smb347_set_voltage_limits(struct smb347_charger *smb)
{ int ret;
if (smb->pre_to_fast_voltage) {
ret = smb->pre_to_fast_voltage;
/* uV */
ret = clamp_val(ret, 2400000, 3000000) - 2400000;
ret /= 200000;
ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
CFG_FLOAT_VOLTAGE_THRESHOLD_MASK,
ret << CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT); if (ret < 0) return ret;
}
if (smb->max_charge_voltage) {
ret = smb->max_charge_voltage;
/* uV */
ret = clamp_val(ret, 3500000, 4500000) - 3500000;
ret /= 20000;
ret = regmap_update_bits(smb->regmap, CFG_FLOAT_VOLTAGE,
CFG_FLOAT_VOLTAGE_FLOAT_MASK, ret); if (ret < 0) return ret;
}
return0;
}
staticint smb347_set_temp_limits(struct smb347_charger *smb)
{ unsignedint id = smb->id; bool enable_therm_monitor = false; int ret = 0; int val;
if (smb->chip_temp_threshold) {
val = smb->chip_temp_threshold;
/* degree C */
val = clamp_val(val, 100, 130) - 100;
val /= 10;
ret = regmap_update_bits(smb->regmap, CFG_OTG,
CFG_OTG_TEMP_THRESHOLD_MASK,
val << CFG_OTG_TEMP_THRESHOLD_SHIFT); if (ret < 0) return ret;
}
if (smb->soft_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) {
val = smb->soft_cold_temp_limit;
val = clamp_val(val, 0, 15);
val /= 5; /* this goes from higher to lower so invert the value */
val = ~val & 0x3;
ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
CFG_TEMP_LIMIT_SOFT_COLD_MASK,
val << CFG_TEMP_LIMIT_SOFT_COLD_SHIFT); if (ret < 0) return ret;
enable_therm_monitor = true;
}
if (smb->soft_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) {
val = smb->soft_hot_temp_limit;
val = clamp_val(val, 40, 55) - 40;
val /= 5;
ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
CFG_TEMP_LIMIT_SOFT_HOT_MASK,
val << CFG_TEMP_LIMIT_SOFT_HOT_SHIFT); if (ret < 0) return ret;
enable_therm_monitor = true;
}
if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) {
val = smb->hard_cold_temp_limit;
val = clamp_val(val, -5, 10) + 5;
val /= 5; /* this goes from higher to lower so invert the value */
val = ~val & 0x3;
ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
CFG_TEMP_LIMIT_HARD_COLD_MASK,
val << CFG_TEMP_LIMIT_HARD_COLD_SHIFT); if (ret < 0) return ret;
enable_therm_monitor = true;
}
if (smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) {
val = smb->hard_hot_temp_limit;
val = clamp_val(val, 50, 65) - 50;
val /= 5;
ret = regmap_update_bits(smb->regmap, CFG_TEMP_LIMIT,
CFG_TEMP_LIMIT_HARD_HOT_MASK,
val << CFG_TEMP_LIMIT_HARD_HOT_SHIFT); if (ret < 0) return ret;
enable_therm_monitor = true;
}
/* *Ifanyofthetemperaturelimitsareset,wealsoenablethe *thermistormonitoring. * *Whensoftlimitsarehit,thedevicewillstarttocompensate *currentand/orvoltagedependingontheconfiguration. * *Whenhardlimitishit,thedevicewillsuspendcharging *dependingontheconfiguration.
*/ if (enable_therm_monitor) {
ret = regmap_update_bits(smb->regmap, CFG_THERM,
CFG_THERM_MONITOR_DISABLED, 0); if (ret < 0) return ret;
}
if (smb->suspend_on_hard_temp_limit) {
ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0); if (ret < 0) return ret;
}
if (smb->soft_temp_limit_compensation !=
SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT) {
val = smb->soft_temp_limit_compensation & 0x3;
ret = regmap_update_bits(smb->regmap, CFG_THERM,
CFG_THERM_SOFT_HOT_COMPENSATION_MASK,
val << CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT); if (ret < 0) return ret;
ret = regmap_update_bits(smb->regmap, CFG_THERM,
CFG_THERM_SOFT_COLD_COMPENSATION_MASK,
val << CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT); if (ret < 0) return ret;
}
if (smb->charge_current_compensation) {
val = current_to_hw(ccc_tbl[id], ARRAY_SIZE(ccc_tbl[id]),
smb->charge_current_compensation); if (val < 0) return val;
ret = regmap_update_bits(smb->regmap, CFG_OTG,
CFG_OTG_CC_COMPENSATION_MASK,
(val & 0x3) << CFG_OTG_CC_COMPENSATION_SHIFT); if (ret < 0) return ret;
}
if (writable && irq_toggle && !smb->irq_unsupported)
disable_irq(client->irq);
ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
writable ? CMD_A_ALLOW_WRITE : 0);
if ((!writable || ret) && irq_toggle && !smb->irq_unsupported)
enable_irq(client->irq);
return ret;
}
staticint smb347_hw_init(struct smb347_charger *smb)
{ unsignedint val; int ret;
ret = smb347_set_writable(smb, true, false); if (ret < 0) return ret;
/* *Programtheplatformspecificconfigurationvaluestothedevice *first.
*/
ret = smb347_set_charge_current(smb); if (ret < 0) goto fail;
ret = smb347_set_current_limits(smb); if (ret < 0) goto fail;
ret = smb347_set_voltage_limits(smb); if (ret < 0) goto fail;
ret = smb347_set_temp_limits(smb); if (ret < 0) goto fail;
/* If USB charging is disabled we put the USB in suspend mode */ if (!smb->use_usb) {
ret = regmap_update_bits(smb->regmap, CMD_A,
CMD_A_SUSPEND_ENABLED,
CMD_A_SUSPEND_ENABLED); if (ret < 0) goto fail;
}
/* *Ifconfiguredbyplatformdata,weenablehardwareAuto-OTG *supportfordrivingVBUS.Otherwisewedisableit.
*/
ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK,
smb->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0); if (ret < 0) goto fail;
/* Activate pin control, making it writable. */ switch (smb->enable_control) { case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW: case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH:
ret = regmap_set_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL); if (ret < 0) goto fail;
}
/* *Makethechargingfunctionalitycontrollablebyawritetothe *commandregisterunlesspincontrolisspecifiedintheplatform *data.
*/ switch (smb->enable_control) { case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW:
val = CFG_PIN_EN_CTRL_ACTIVE_LOW; break; case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH:
val = CFG_PIN_EN_CTRL_ACTIVE_HIGH; break; default:
val = 0; break;
}
ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL_MASK,
val); if (ret < 0) goto fail;
/* Disable Automatic Power Source Detection (APSD) interrupt. */
ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_APSD_IRQ, 0); if (ret < 0) goto fail;
ret = smb347_update_ps_status(smb); if (ret < 0) goto fail;
/* SMB347 it needs at least 20ms for setting IRQSTAT_E_*IN_UV_IRQ */
usleep_range(25000, 35000);
ret = regmap_read(smb->regmap, STAT_C, &stat_c); if (ret < 0) {
dev_warn(smb->dev, "reading STAT_C failed\n"); return IRQ_NONE;
}
ret = regmap_read(smb->regmap, IRQSTAT_C, &irqstat_c); if (ret < 0) {
dev_warn(smb->dev, "reading IRQSTAT_C failed\n"); return IRQ_NONE;
}
ret = regmap_read(smb->regmap, IRQSTAT_D, &irqstat_d); if (ret < 0) {
dev_warn(smb->dev, "reading IRQSTAT_D failed\n"); return IRQ_NONE;
}
ret = regmap_read(smb->regmap, IRQSTAT_E, &irqstat_e); if (ret < 0) {
dev_warn(smb->dev, "reading IRQSTAT_E failed\n"); return IRQ_NONE;
}
/* *Ifwegetchargererrorwereporttheerrorbacktouser. *Iftheerrorisrecoveredchargingwillresumeagain.
*/ if (stat_c & STAT_C_CHARGER_ERROR) {
dev_err(smb->dev, "charging stopped due to charger error\n"); if (smb->use_mains)
power_supply_changed(smb->mains); if (smb->use_usb)
power_supply_changed(smb->usb);
handled = true;
}
/* *Ifwereachedtheterminationcurrentthebatteryischargedand *wecanupdatethestatusnow.Chargingisautomatically *disabledbythehardware.
*/ if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) { if (smb->use_mains)
power_supply_changed(smb->mains); if (smb->use_usb)
power_supply_changed(smb->usb);
}
dev_dbg(smb->dev, "going to HW maintenance mode\n");
handled = true;
}
/* *IfwegotachargertimeoutINTthatmeansthecharge *fullisnotdetectedwithinchargetimeoutvalue.
*/ if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_IRQ) {
dev_dbg(smb->dev, "total Charge Timeout INT received\n");
if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT)
dev_warn(smb->dev, "charging stopped due to timeout\n"); if (smb->use_mains)
power_supply_changed(smb->mains); if (smb->use_usb)
power_supply_changed(smb->usb);
handled = true;
}
/* *IfwegotanundervoltageinterruptitmeansthatAC/USBinput *wasconnectedordisconnected.
*/ if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { if (smb347_update_ps_status(smb) > 0) {
smb347_start_stop_charging(smb); if (smb->use_mains)
power_supply_changed(smb->mains); if (smb->use_usb)
power_supply_changed(smb->usb);
}
handled = true;
}
return handled ? IRQ_HANDLED : IRQ_NONE;
}
staticint smb347_irq_set(struct smb347_charger *smb, bool enable)
{ int ret;
if (smb->irq_unsupported) return0;
ret = smb347_set_writable(smb, true, true); if (ret < 0) return ret;
if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { if (!smb->usb_online) return POWER_SUPPLY_STATUS_DISCHARGING;
} else { if (!smb->mains_online) return POWER_SUPPLY_STATUS_DISCHARGING;
}
ret = regmap_read(smb->regmap, STAT_C, &val); if (ret < 0) return ret;
if ((val & STAT_C_CHARGER_ERROR) ||
(val & STAT_C_HOLDOFF_STAT)) { /* *settoNOTCHARGINGuponchargererror *orcharginghasstopped.
*/
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
} else { if ((val & STAT_C_CHG_MASK) >> STAT_C_CHG_SHIFT) { /* *settochargingifbatteryisinpre-charge, *fastchargeortaperchargingmode.
*/
status = POWER_SUPPLY_STATUS_CHARGING;
} elseif (val & STAT_C_CHG_TERM) { /* *setthestatustoFULLifbatteryisnotinpre *charge,fastchargeortaperchargingmodeAND *chargingisterminatedatleastonce.
*/
status = POWER_SUPPLY_STATUS_FULL;
} else { /* *inthiscasenochargererrorortermination *occuredbutchargingisnotinprogress!!!
*/
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
}
}
return status;
}
staticint smb347_get_property_locked(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val)
{ struct smb347_charger *smb = power_supply_get_drvdata(psy); int ret;
switch (prop) { case POWER_SUPPLY_PROP_STATUS:
ret = smb347_get_charging_status(smb, psy); if (ret < 0) return ret;
val->intval = ret; break;
case POWER_SUPPLY_PROP_CHARGE_TYPE: if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { if (!smb->usb_online) return -ENODATA;
} else { if (!smb->mains_online) return -ENODATA;
}
staticbool smb347_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case IRQSTAT_A: case IRQSTAT_C: case IRQSTAT_D: case IRQSTAT_E: case IRQSTAT_F: case STAT_A: case STAT_B: case STAT_C: case STAT_E: returntrue;
}
returnfalse;
}
staticbool smb347_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case CFG_CHARGE_CURRENT: case CFG_CURRENT_LIMIT: case CFG_FLOAT_VOLTAGE: case CFG_STAT: case CFG_PIN: case CFG_THERM: case CFG_SYSOK: case CFG_OTHER: case CFG_OTG: case CFG_TEMP_LIMIT: case CFG_FAULT_IRQ: case CFG_STATUS_IRQ: case CFG_ADDRESS: case CMD_A: case CMD_B: case CMD_C: returntrue;
}
if (smb->mains)
supply = smb->mains; else
supply = smb->usb;
err = power_supply_get_battery_info(supply, &info); if (err == -ENXIO || err == -ENODEV) return0; if (err) return err;
if (info->constant_charge_current_max_ua != -EINVAL)
smb->max_charge_current = info->constant_charge_current_max_ua;
if (info->constant_charge_voltage_max_uv != -EINVAL)
smb->max_charge_voltage = info->constant_charge_voltage_max_uv;
if (info->precharge_current_ua != -EINVAL)
smb->pre_charge_current = info->precharge_current_ua;
if (info->charge_term_current_ua != -EINVAL)
smb->termination_current = info->charge_term_current_ua;
if (info->temp_alert_min != INT_MIN)
smb->soft_cold_temp_limit = info->temp_alert_min;
if (info->temp_alert_max != INT_MAX)
smb->soft_hot_temp_limit = info->temp_alert_max;
if (info->temp_min != INT_MIN)
smb->hard_cold_temp_limit = info->temp_min;
if (info->temp_max != INT_MAX)
smb->hard_hot_temp_limit = info->temp_max;
/* Suspend when battery temperature is outside hard limits */ if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT ||
smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT)
smb->suspend_on_hard_temp_limit = true;
ret = smb347_set_writable(smb, true, true); if (ret < 0) return ret;
smb347_charging_disable(smb);
if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) { unsignedint sysok = 0;
if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_LOW)
sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
/* *VBUSwon'tbepoweredifINOKisactive,soweneedto *manuallydisableINOKonsomeplatforms.
*/
ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
CFG_SYSOK_INOK_ACTIVE_HIGH, sysok); if (ret < 0) {
dev_err(smb->dev, "failed to disable INOK\n"); goto done;
}
}
ret = smb347_usb_vbus_get_current_limit(rdev); if (ret < 0) {
dev_err(smb->dev, "failed to get USB VBUS current limit\n"); goto done;
}
max_uA = ret;
ret = smb347_usb_vbus_set_new_current_limit(smb, 250000); if (ret < 0) {
dev_err(smb->dev, "failed to preset USB VBUS current limit\n"); goto done;
}
ret = regmap_set_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED); if (ret < 0) {
dev_err(smb->dev, "failed to enable USB VBUS\n"); goto done;
}
smb->usb_vbus_enabled = true;
ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA); if (ret < 0) {
dev_err(smb->dev, "failed to restore USB VBUS current limit\n"); goto done;
}
done:
smb347_set_writable(smb, false, true);
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.