/* * Most of the val -> idx conversions can be computed, given the minimum, * maximum and the step between values. For the rest of conversions, we use * lookup tables.
*/ enum bq25890_table_ids { /* range tables */
TBL_ICHG,
TBL_ITERM,
TBL_IINLIM,
TBL_VREG,
TBL_BOOSTV,
TBL_SYSVMIN,
TBL_VBUSV,
TBL_VBATCOMP,
TBL_RBATCOMP,
staticbool bq25890_is_adc_property(enum power_supply_property psp)
{ switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_TEMP: returntrue;
mutex_lock(&bq->lock); /* update state in case we lost an interrupt */
__bq25890_handle_irq(bq);
*state = bq->state;
do_adc_conv = (!state->online || state->hiz) && bq25890_is_adc_property(psp); if (do_adc_conv)
bq25890_field_write(bq, F_CONV_START, 1);
mutex_unlock(&bq->lock);
if (do_adc_conv)
regmap_field_read_poll_timeout(bq->rmap_fields[F_CONV_START],
ret, !ret, 25000, 1000000);
}
case POWER_SUPPLY_PROP_CURRENT_NOW: /* I_BAT now */ /* * This is ADC-sampled immediate charge current supplied * from charger to battery. The property name is confusing, * for clarification refer to: * Documentation/ABI/testing/sysfs-class-power * /sys/class/power_supply/<supply_name>/current_now
*/
ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */ if (ret < 0) return ret;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: /* I_BAT user limit */ /* * This is user-configured constant charge current supplied * from charger to battery in first phase of charging, when * battery voltage is below constant charge voltage. * * This value reflects the current hardware setting. * * The POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX is the * maximum value of this property.
*/
ret = bq25890_field_read(bq, F_ICHG); if (ret < 0) return ret;
val->intval = bq25890_find_val(ret, TBL_ICHG);
/* When temperature is too low, charge current is decreased */ if (bq->state.ntc_fault == NTC_FAULT_COOL) {
ret = bq25890_field_read(bq, F_JEITA_ISET); if (ret < 0) return ret;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: /* I_BAT max */ /* * This is maximum allowed constant charge current supplied * from charger to battery in first phase of charging, when * battery voltage is below constant charge voltage. * * This value is constant for each battery and set from DT.
*/
val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG); break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW: /* V_BAT now */ /* * This is ADC-sampled immediate charge voltage supplied * from charger to battery. The property name is confusing, * for clarification refer to: * Documentation/ABI/testing/sysfs-class-power * /sys/class/power_supply/<supply_name>/voltage_now
*/
ret = bq25890_field_read(bq, F_BATV); /* read measured value */ if (ret < 0) return ret;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: /* V_BAT user limit */ /* * This is user-configured constant charge voltage supplied * from charger to battery in second phase of charging, when * battery voltage reached constant charge voltage. * * This value reflects the current hardware setting. * * The POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX is the * maximum value of this property.
*/
ret = bq25890_field_read(bq, F_VREG); if (ret < 0) return ret;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: /* V_BAT max */ /* * This is maximum allowed constant charge voltage supplied * from charger to battery in second phase of charging, when * battery voltage reached constant charge voltage. * * This value is constant for each battery and set from DT.
*/
val->intval = bq25890_find_val(bq->init_data.vreg, TBL_VREG); break;
case POWER_SUPPLY_PROP_TEMP:
ret = bq25890_field_read(bq, F_TSPCT); if (ret < 0) return ret;
/* convert TS percentage into rough temperature */
val->intval = bq25890_find_val(ret, TBL_TSPCT); break;
staticint bq25890_power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp)
{ switch (psp) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: case POWER_SUPPLY_PROP_ONLINE: returntrue; default: returnfalse;
}
}
/* * If there are multiple chargers the maximum current the external power-supply * can deliver needs to be divided over the chargers. This is done according * to the bq->iinlim_percentage setting.
*/ staticint bq25890_charger_get_scaled_iinlim_regval(struct bq25890_device *bq, int iinlim_ua)
{
iinlim_ua = iinlim_ua * bq->iinlim_percentage / 100; return bq25890_find_idx(iinlim_ua, TBL_IINLIM);
}
/* On the BQ25892 try to get charger-type info from our supplier */ staticvoid bq25890_charger_external_power_changed(struct power_supply *psy)
{ struct bq25890_device *bq = power_supply_get_drvdata(psy); union power_supply_propval val; int input_current_limit, ret;
if (bq->chip_version != BQ25892) return;
ret = power_supply_get_property_from_supplier(psy,
POWER_SUPPLY_PROP_USB_TYPE,
&val); if (ret) return;
switch (val.intval) { case POWER_SUPPLY_USB_TYPE_DCP:
input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 2000000); if (bq->pump_express_vbus_max) {
queue_delayed_work(system_power_efficient_wq,
&bq->pump_express_work,
PUMP_EXPRESS_START_DELAY);
} break; case POWER_SUPPLY_USB_TYPE_CDP: case POWER_SUPPLY_USB_TYPE_ACA:
input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 1500000); break; case POWER_SUPPLY_USB_TYPE_SDP: default:
input_current_limit = bq25890_charger_get_scaled_iinlim_regval(bq, 500000);
}
ret = bq25890_get_chip_state(bq, &new_state); if (ret < 0) return IRQ_NONE;
if (!memcmp(&bq->state, &new_state, sizeof(new_state))) return IRQ_NONE;
/* * Restore HiZ bit in case it was set by user. The chip does not retain * this bit on cable replug, hence the bit must be reset manually here.
*/ if (new_state.online && !bq->state.online && bq->force_hiz) {
ret = bq25890_field_write(bq, F_EN_HIZ, bq->force_hiz); if (ret < 0) goto error;
new_state.hiz = 1;
}
/* Should period ADC sampling be enabled? */
adc_conv_rate = bq->state.online && !bq->state.hiz;
new_adc_conv_rate = new_state.online && !new_state.hiz;
if (new_adc_conv_rate != adc_conv_rate) {
ret = bq25890_field_write(bq, F_CONV_RATE, new_adc_conv_rate); if (ret < 0) goto error;
}
for (i = 0; i < ARRAY_SIZE(init_data); i++) { if (write) {
ret = bq25890_field_write(bq, init_data[i].id,
*init_data[i].value);
} else {
ret = bq25890_field_read(bq, init_data[i].id); if (ret >= 0)
*init_data[i].value = ret;
} if (ret < 0) {
dev_dbg(bq->dev, "Accessing init data failed %d\n", ret); return ret;
}
}
return 0;
}
staticint bq25890_hw_init(struct bq25890_device *bq)
{ int ret;
if (!bq->skip_reset) {
ret = bq25890_chip_reset(bq); if (ret < 0) {
dev_dbg(bq->dev, "Reset failed %d\n", ret); return ret;
}
} else { /* * Ensure charging is enabled, on some boards where the fw * takes care of initalizition F_CHG_CFG is set to 0 before * handing control over to the OS.
*/
ret = bq25890_field_write(bq, F_CHG_CFG, 1); if (ret < 0) {
dev_dbg(bq->dev, "Enabling charging failed %d\n", ret); return ret;
}
}
/* Get ID for the device */
mutex_lock(&bq25890_id_mutex);
bq->id = idr_alloc(&bq25890_id, bq, 0, 0, GFP_KERNEL);
mutex_unlock(&bq25890_id_mutex); if (bq->id < 0) return bq->id;
staticint bq25890_set_otg_cfg(struct bq25890_device *bq, u8 val)
{ int ret;
ret = bq25890_field_write(bq, F_OTG_CFG, val); if (ret < 0)
dev_err(bq->dev, "Error switching to boost/charger mode: %d\n", ret);
return ret;
}
staticvoid bq25890_pump_express_work(struct work_struct *data)
{ struct bq25890_device *bq =
container_of(data, struct bq25890_device, pump_express_work.work); union power_supply_propval value; int voltage, i, ret;
dev_dbg(bq->dev, "Start to request input voltage increasing\n");
/* If there is a second charger put in Hi-Z mode */ if (bq->secondary_chrg) {
value.intval = 0;
power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &value);
}
/* Enable current pulse voltage control protocol */
ret = bq25890_field_write(bq, F_PUMPX_EN, 1); if (ret < 0) goto error_print;
for (i = 0; i < PUMP_EXPRESS_MAX_TRIES; i++) {
voltage = bq25890_get_vbus_voltage(bq); if (voltage < 0) goto error_print;
dev_dbg(bq->dev, "input voltage = %d uV\n", voltage);
if ((voltage + PUMP_EXPRESS_VBUS_MARGIN_uV) >
bq->pump_express_vbus_max) break;
ret = bq25890_field_write(bq, F_PUMPX_UP, 1); if (ret < 0) goto error_print;
/* Note a single PUMPX up pulse-sequence takes 2.1s */
ret = regmap_field_read_poll_timeout(bq->rmap_fields[F_PUMPX_UP],
ret, !ret, 100000, 3000000); if (ret < 0) goto error_print;
/* Make sure ADC has sampled Vbus before checking again */
msleep(1000);
}
bq25890_field_write(bq, F_PUMPX_EN, 0);
if (bq->secondary_chrg) {
value.intval = 1;
power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &value);
}
dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n",
voltage);
/* * When enabling 5V boost / Vbus output, we need to put the secondary * charger in Hi-Z mode to avoid it trying to charge the secondary * battery from the 5V boost output.
*/ if (bq->secondary_chrg)
power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &val);
return bq25890_set_otg_cfg(bq, 1);
}
staticint bq25890_vbus_disable(struct regulator_dev *rdev)
{ struct bq25890_device *bq = rdev_get_drvdata(rdev); union power_supply_propval val = {
.intval = 1,
}; int ret;
ret = bq25890_set_otg_cfg(bq, 0); if (ret) return ret;
if (bq->secondary_chrg)
power_supply_set_property(bq->secondary_chrg, POWER_SUPPLY_PROP_ONLINE, &val);
for (i = 0; i < ARRAY_SIZE(props); i++) {
ret = device_property_read_u32(bq->dev, props[i].name,
&property); if (ret < 0) { if (props[i].optional) continue;
dev_err(bq->dev, "Unable to read property %d %s\n", ret,
props[i].name);
/* * This must be before bq25890_power_supply_init(), so that it runs * after devm unregisters the power_supply.
*/
ret = devm_add_action_or_reset(dev, bq25890_non_devm_cleanup, bq); if (ret) return ret;
ret = bq25890_register_regulator(bq); if (ret) return ret;
ret = bq25890_power_supply_init(bq); if (ret < 0) return dev_err_probe(dev, ret, "registering power supply\n");
ret = devm_request_threaded_irq(dev, client->irq, NULL,
bq25890_irq_handler_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
BQ25890_IRQ_PIN, bq); if (ret) return ret;
/* * TODO this if + return should probably be removed, but that would * introduce a function change for boards using the usb-phy framework. * This needs to be tested on such a board before making this change.
*/ if (!IS_ERR_OR_NULL(bq->usb_phy)) return;
/* * Turn off the 5v Boost regulator which outputs Vbus to the device's * Micro-USB or Type-C USB port. Leaving this on drains power and * this avoids the PMIC on some device-models seeing this as Vbus * getting inserted after shutdown, causing the device to immediately * power-up again.
*/
bq25890_set_otg_cfg(bq, 0);
}
/* * If charger is removed, while in suspend, make sure ADC is diabled * since it consumes slightly more power.
*/ return bq25890_field_write(bq, F_CONV_RATE, 0);
}
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.