staticvoid sbs_disable_charger_broadcasts(struct sbs_info *chip)
{ int val = sbs_read_word_data(chip->client, BATTERY_MODE_OFFSET); if (val < 0) gotoexit;
val |= BATTERY_MODE_CHARGER_MASK;
val = sbs_write_word_data(chip->client, BATTERY_MODE_OFFSET, val);
exit: if (val < 0)
dev_err(&chip->client->dev, "Failed to disable charger broadcasting: %d\n", val); else
dev_dbg(&chip->client->dev, "%s\n", __func__);
}
if (!is_present) {
chip->is_present = false; /* Disable PEC when no device is present */
client->flags &= ~I2C_CLIENT_PEC;
sbs_invalidate_cached_props(chip); return0;
}
/* Check if device supports packet error checking and use it */ while (retries > 0) {
ret = i2c_smbus_read_word_data(client, REG_ADDR_SPEC_INFO); if (ret >= 0) break;
dev_warn_once(&client->dev, "I2C adapter does not support I2C_FUNC_SMBUS_READ_BLOCK_DATA.\n" "Fallback method does not support PEC.\n");
/* Adapter needs to support these two functions */ if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK)){ return -ENODEV;
}
/* Get the length of block data */ while (retries_length > 0) {
ret = i2c_smbus_read_byte_data(client, address); if (ret >= 0) break;
retries_length--;
}
if (ret < 0) {
dev_dbg(&client->dev, "%s: i2c read at address 0x%x failed\n",
__func__, address); return ret;
}
/* block_length does not include NULL terminator */
block_length = ret; if (block_length > I2C_SMBUS_BLOCK_MAX) {
dev_err(&client->dev, "%s: Returned block_length is longer than 0x%x\n",
__func__, I2C_SMBUS_BLOCK_MAX); return -EINVAL;
}
/* Get the block data */ while (retries_block > 0) {
ret = i2c_smbus_read_i2c_block_data(
client, address,
block_length + 1, block_buffer); if (ret >= 0) break;
retries_block--;
}
if (ret < 0) {
dev_dbg(&client->dev, "%s: i2c read at address 0x%x failed\n",
__func__, address); return ret;
}
while (retries > 0) {
ret = i2c_smbus_write_word_data(client, address, value); if (ret >= 0) break;
retries--;
}
if (ret < 0) {
dev_dbg(&client->dev, "%s: i2c write to address 0x%x failed\n",
__func__, address); return ret;
}
return0;
}
staticint sbs_status_correct(struct i2c_client *client, int *intval)
{ int ret;
ret = sbs_read_word_data(client, sbs_data[REG_CURRENT_NOW].addr); if (ret < 0) return ret;
ret = (s16)ret;
/* Not drawing current -> not charging (i.e. idle) */ if (*intval != POWER_SUPPLY_STATUS_FULL && ret == 0)
*intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
if (*intval == POWER_SUPPLY_STATUS_FULL) { /* Drawing or providing current when full */ if (ret > 0)
*intval = POWER_SUPPLY_STATUS_CHARGING; elseif (ret < 0)
*intval = POWER_SUPPLY_STATUS_DISCHARGING;
}
return0;
}
staticbool sbs_bat_needs_calibration(struct i2c_client *client)
{ int ret;
ret = sbs_read_word_data(client, sbs_data[REG_BATTERY_MODE].addr); if (ret < 0) returnfalse;
buf = sbs_get_string_buf(chip, psp); if (IS_ERR(buf)) return buf;
if (!buf[0]) {
ret = sbs_get_property_index(chip->client, psp); if (ret < 0) return ERR_PTR(ret);
addr = sbs_data[ret].addr;
ret = sbs_read_string_data(chip->client, addr, buf); if (ret < 0) return ERR_PTR(ret);
}
return buf;
}
staticvoid sbs_unit_adjustment(struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val)
{ #define BASE_UNIT_CONVERSION 1000 #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) #define TIME_UNIT_CONVERSION 60 #define TEMP_KELVIN_TO_CELSIUS 2731 switch (psp) { case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: /* sbs provides energy in units of 10mWh. *ConverttoµWh
*/
val->intval *= BATTERY_MODE_CAP_MULT_WATT; break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval *= BASE_UNIT_CONVERSION; break;
case POWER_SUPPLY_PROP_TEMP: /* sbs provides battery temperature in 0.1K *soconvertitto0.1°C
*/
val->intval -= TEMP_KELVIN_TO_CELSIUS; break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: /* sbs provides time to empty and time to full in minutes. *Converttoseconds
*/
val->intval *= TIME_UNIT_CONVERSION; break;
default:
dev_dbg(&client->dev, "%s: no need for unit conversion %d\n", __func__, psp);
}
}
case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
ret = sbs_get_property_index(client, psp); if (ret < 0) break;
/* sbs_get_battery_capacity() will change the battery mode *temporarilytoreadtherequestedattribute.Ensurewestay *inthedesiredmodeforthedurationoftheattributeread.
*/
mutex_lock(&chip->mode_lock);
ret = sbs_get_battery_capacity(client, ret, psp, val);
mutex_unlock(&chip->mode_lock); break;
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
ret = sbs_get_battery_serial_number(client, val); break;
case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_CAPACITY_LEVEL: case POWER_SUPPLY_PROP_CYCLE_COUNT: case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN:
ret = sbs_get_property_index(client, psp); if (ret < 0) break;
ret = sbs_get_battery_property(client, ret, psp, val); break;
case POWER_SUPPLY_PROP_MODEL_NAME: case POWER_SUPPLY_PROP_MANUFACTURER:
str = sbs_get_constant_string(chip, psp); if (IS_ERR(str))
ret = PTR_ERR(str); else
val->strval = str; break;
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: case POWER_SUPPLY_PROP_MANUFACTURE_DAY:
ret = sbs_get_battery_manufacture_date(client, psp, val); break;
if (!chip->gpio_detect && chip->is_present != (ret >= 0)) { bool old_present = chip->is_present; union power_supply_propval val; int err = sbs_get_battery_presence_and_health(
client, POWER_SUPPLY_PROP_PRESENT, &val);
sbs_update_presence(chip, !err && val.intval);
if (old_present != chip->is_present)
power_supply_changed(chip->power_supply);
}
done: if (!ret) { /* Convert units to match requirements for power supply class */
sbs_unit_adjustment(client, psp, val);
dev_dbg(&client->dev, "%s: property = %d, value = %x\n", __func__,
psp, val->intval);
} elseif (!chip->is_present) { /* battery not present, so return NODATA for properties */
ret = -ENODATA;
} return ret;
}
ret = sbs_read_word_data(chip->client, sbs_data[REG_STATUS].addr); /* if the read failed, give up on this work */ if (ret < 0) {
chip->poll_time = 0; return;
}
if (ret & BATTERY_FULL_CHARGED)
ret = POWER_SUPPLY_STATUS_FULL; elseif (ret & BATTERY_DISCHARGING)
ret = POWER_SUPPLY_STATUS_DISCHARGING; else
ret = POWER_SUPPLY_STATUS_CHARGING;
/* use pdata if available, fall back to DT properties, *orhardcodeddefaultsifnot
*/
rc = device_property_read_u32(&client->dev, "sbs,i2c-retry-count",
&chip->i2c_retry_count); if (rc)
chip->i2c_retry_count = 0;
rc = device_property_read_u32(&client->dev, "sbs,poll-retry-count",
&chip->poll_retry_count); if (rc)
chip->poll_retry_count = 0;
chip->gpio_detect = devm_gpiod_get_optional(&client->dev, "sbs,battery-detect", GPIOD_IN); if (IS_ERR(chip->gpio_detect)) return dev_err_probe(&client->dev, PTR_ERR(chip->gpio_detect), "Failed to get gpio\n");
i2c_set_clientdata(client, chip);
if (!chip->gpio_detect) goto skip_gpio;
irq = gpiod_to_irq(chip->gpio_detect); if (irq <= 0) {
dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); goto skip_gpio;
}
skip_gpio: /* *Beforeweregister,wemightneedtomakesurewecanactuallytalk *tothebattery.
*/ if (!(force_load || chip->gpio_detect)) { union power_supply_propval val;
rc = sbs_get_battery_presence_and_health(
client, POWER_SUPPLY_PROP_PRESENT, &val); if (rc < 0 || !val.intval) return dev_err_probe(&client->dev, -ENODEV, "Failed to get present status\n");
}
rc = devm_delayed_work_autocancel(&client->dev, &chip->work,
sbs_delayed_work); if (rc) return rc;
chip->power_supply = devm_power_supply_register(&client->dev, sbs_desc,
&psy_cfg); if (IS_ERR(chip->power_supply)) return dev_err_probe(&client->dev, PTR_ERR(chip->power_supply), "Failed to register power supply\n");
dev_info(&client->dev, "%s: battery gas gauge device registered\n", client->name);
if (chip->poll_time > 0)
cancel_delayed_work_sync(&chip->work);
if (chip->flags & SBS_FLAGS_TI_BQ20ZX5) { /* Write to manufacturer access with sleep command. */
ret = sbs_write_word_data(client,
sbs_data[REG_MANUFACTURER_DATA].addr,
MANUFACTURER_ACCESS_SLEEP); if (chip->is_present && ret < 0) return ret;
}
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.