--- zzzz-none-000/linux-3.10.107/drivers/power/sbs-battery.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/power/sbs-battery.c 2021-02-04 17:41:59.000000000 +0000 @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -48,7 +49,10 @@ REG_FULL_CHARGE_CAPACITY_CHARGE, REG_DESIGN_CAPACITY, REG_DESIGN_CAPACITY_CHARGE, - REG_DESIGN_VOLTAGE, + REG_DESIGN_VOLTAGE_MIN, + REG_DESIGN_VOLTAGE_MAX, + REG_MANUFACTURER, + REG_MODEL_NAME, }; /* Battery Mode defines */ @@ -68,6 +72,7 @@ #define BATTERY_FULL_CHARGED 0x20 #define BATTERY_FULL_DISCHARGED 0x10 +/* min_value and max_value are only valid for numerical data */ #define SBS_DATA(_psp, _addr, _min_value, _max_value) { \ .psp = _psp, \ .addr = _addr, \ @@ -111,10 +116,17 @@ SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535), [REG_DESIGN_CAPACITY_CHARGE] = SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, 65535), - [REG_DESIGN_VOLTAGE] = + [REG_DESIGN_VOLTAGE_MIN] = + SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 0x19, 0, 65535), + [REG_DESIGN_VOLTAGE_MAX] = SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535), [REG_SERIAL_NUMBER] = SBS_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), + /* Properties of type `const char *' */ + [REG_MANUFACTURER] = + SBS_DATA(POWER_SUPPLY_PROP_MANUFACTURER, 0x20, 0, 65535), + [REG_MODEL_NAME] = + SBS_DATA(POWER_SUPPLY_PROP_MODEL_NAME, 0x21, 0, 65535) }; static enum power_supply_property sbs_properties[] = { @@ -130,6 +142,7 @@ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_ENERGY_FULL, @@ -137,11 +150,14 @@ POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + /* Properties of type `const char *' */ + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME }; struct sbs_info { struct i2c_client *client; - struct power_supply power_supply; + struct power_supply *power_supply; struct sbs_platform_data *pdata; bool is_present; bool gpio_detect; @@ -153,6 +169,10 @@ int ignore_changes; }; +static char model_name[I2C_SMBUS_BLOCK_MAX + 1]; +static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1]; +static bool force_load; + static int sbs_read_word_data(struct i2c_client *client, u8 address) { struct sbs_info *chip = i2c_get_clientdata(client); @@ -179,6 +199,74 @@ return le16_to_cpu(ret); } +static int sbs_read_string_data(struct i2c_client *client, u8 address, + char *values) +{ + struct sbs_info *chip = i2c_get_clientdata(client); + s32 ret = 0, block_length = 0; + int retries_length = 1, retries_block = 1; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + + if (chip->pdata) { + retries_length = max(chip->pdata->i2c_retry_count + 1, 1); + retries_block = max(chip->pdata->i2c_retry_count + 1, 1); + } + + /* 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; + } + + /* block_buffer[0] == block_length */ + memcpy(values, block_buffer + 1, block_length); + values[block_length] = '\0'; + + return le16_to_cpu(ret); +} + static int sbs_write_word_data(struct i2c_client *client, u8 address, u16 value) { @@ -305,7 +393,7 @@ chip->last_state = val->intval; else if (chip->last_state != val->intval) { cancel_delayed_work_sync(&chip->work); - power_supply_changed(&chip->power_supply); + power_supply_changed(chip->power_supply); chip->poll_time = 0; } } else { @@ -318,6 +406,19 @@ return 0; } +static int sbs_get_battery_string_property(struct i2c_client *client, + int reg_offset, enum power_supply_property psp, char *val) +{ + s32 ret; + + ret = sbs_read_string_data(client, sbs_data[reg_offset].addr, val); + + if (ret < 0) + return ret; + + return 0; +} + static void sbs_unit_adjustment(struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) { @@ -336,6 +437,7 @@ 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_CHARGE_NOW: @@ -456,8 +558,7 @@ union power_supply_propval *val) { int ret = 0; - struct sbs_info *chip = container_of(psy, - struct sbs_info, power_supply); + struct sbs_info *chip = power_supply_get_drvdata(psy); struct i2c_client *client = chip->client; switch (psp) { @@ -497,6 +598,7 @@ case POWER_SUPPLY_PROP_TEMP: 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: ret = sbs_get_property_index(client, psp); if (ret < 0) @@ -505,6 +607,26 @@ ret = sbs_get_battery_property(client, ret, psp, val); break; + case POWER_SUPPLY_PROP_MODEL_NAME: + ret = sbs_get_property_index(client, psp); + if (ret < 0) + break; + + ret = sbs_get_battery_string_property(client, ret, psp, + model_name); + val->strval = model_name; + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + ret = sbs_get_property_index(client, psp); + if (ret < 0) + break; + + ret = sbs_get_battery_string_property(client, ret, psp, + manufacturer); + val->strval = manufacturer; + break; + default: dev_err(&client->dev, "%s: INVALID property\n", __func__); @@ -517,7 +639,7 @@ if (!chip->gpio_detect && chip->is_present != (ret >= 0)) { chip->is_present = (ret >= 0); - power_supply_changed(&chip->power_supply); + power_supply_changed(chip->power_supply); } done: @@ -550,9 +672,7 @@ static void sbs_external_power_changed(struct power_supply *psy) { - struct sbs_info *chip; - - chip = container_of(psy, struct sbs_info, power_supply); + struct sbs_info *chip = power_supply_get_drvdata(psy); if (chip->ignore_changes > 0) { chip->ignore_changes--; @@ -591,7 +711,7 @@ if (chip->last_state != ret) { chip->poll_time = 0; - power_supply_changed(&chip->power_supply); + power_supply_changed(chip->power_supply); return; } if (chip->poll_time > 0) { @@ -675,41 +795,48 @@ } #endif +static const struct power_supply_desc sbs_default_desc = { + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = sbs_properties, + .num_properties = ARRAY_SIZE(sbs_properties), + .get_property = sbs_get_property, + .external_power_changed = sbs_external_power_changed, +}; + static int sbs_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct sbs_info *chip; + struct power_supply_desc *sbs_desc; struct sbs_platform_data *pdata = client->dev.platform_data; + struct power_supply_config psy_cfg = {}; int rc; int irq; - char *name; - name = kasprintf(GFP_KERNEL, "sbs-%s", dev_name(&client->dev)); - if (!name) { - dev_err(&client->dev, "Failed to allocate device name\n"); + sbs_desc = devm_kmemdup(&client->dev, &sbs_default_desc, + sizeof(*sbs_desc), GFP_KERNEL); + if (!sbs_desc) + return -ENOMEM; + + sbs_desc->name = devm_kasprintf(&client->dev, GFP_KERNEL, "sbs-%s", + dev_name(&client->dev)); + if (!sbs_desc->name) return -ENOMEM; - } chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL); - if (!chip) { - rc = -ENOMEM; - goto exit_free_name; - } + if (!chip) + return -ENOMEM; chip->client = client; chip->enable_detection = false; chip->gpio_detect = false; - chip->power_supply.name = name; - chip->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; - chip->power_supply.properties = sbs_properties; - chip->power_supply.num_properties = ARRAY_SIZE(sbs_properties); - chip->power_supply.get_property = sbs_get_property; + psy_cfg.of_node = client->dev.of_node; + psy_cfg.drv_data = chip; /* ignore first notification of external change, it is generated * from the power_supply_register call back */ chip->ignore_changes = 1; chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN; - chip->power_supply.external_power_changed = sbs_external_power_changed; pdata = sbs_of_populate_pdata(client); @@ -748,7 +875,7 @@ rc = request_irq(irq, sbs_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(&client->dev), &chip->power_supply); + dev_name(&client->dev), chip->power_supply); if (rc) { dev_warn(&client->dev, "Failed to request irq: %d\n", rc); gpio_free(pdata->battery_detect); @@ -760,20 +887,25 @@ skip_gpio: /* - * Before we register, we need to make sure we can actually talk + * Before we register, we might need to make sure we can actually talk * to the battery. */ - rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr); - if (rc < 0) { - dev_err(&client->dev, "%s: Failed to get device status\n", - __func__); - goto exit_psupply; + if (!force_load) { + rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr); + + if (rc < 0) { + dev_err(&client->dev, "%s: Failed to get device status\n", + __func__); + goto exit_psupply; + } } - rc = power_supply_register(&client->dev, &chip->power_supply); - if (rc) { + chip->power_supply = power_supply_register(&client->dev, sbs_desc, + &psy_cfg); + if (IS_ERR(chip->power_supply)) { dev_err(&client->dev, "%s: Failed to register power supply\n", __func__); + rc = PTR_ERR(chip->power_supply); goto exit_psupply; } @@ -788,15 +920,12 @@ exit_psupply: if (chip->irq) - free_irq(chip->irq, &chip->power_supply); + free_irq(chip->irq, chip->power_supply); if (chip->gpio_detect) gpio_free(pdata->battery_detect); kfree(chip); -exit_free_name: - kfree(name); - return rc; } @@ -805,15 +934,14 @@ struct sbs_info *chip = i2c_get_clientdata(client); if (chip->irq) - free_irq(chip->irq, &chip->power_supply); + free_irq(chip->irq, chip->power_supply); if (chip->gpio_detect) gpio_free(chip->pdata->battery_detect); - power_supply_unregister(&chip->power_supply); + power_supply_unregister(chip->power_supply); cancel_delayed_work_sync(&chip->work); - kfree(chip->power_supply.name); kfree(chip); chip = NULL; @@ -868,3 +996,7 @@ MODULE_DESCRIPTION("SBS battery monitor driver"); MODULE_LICENSE("GPL"); + +module_param(force_load, bool, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(force_load, + "Attempt to load the driver even if no battery is connected");