diff options
Diffstat (limited to 'drivers/power/bq24735-charger.c')
-rw-r--r-- | drivers/power/bq24735-charger.c | 146 |
1 files changed, 124 insertions, 22 deletions
diff --git a/drivers/power/bq24735-charger.c b/drivers/power/bq24735-charger.c index eb2b3689de97..fa454c19ce17 100644 --- a/drivers/power/bq24735-charger.c +++ b/drivers/power/bq24735-charger.c @@ -48,6 +48,8 @@ struct bq24735 { struct power_supply_desc charger_desc; struct i2c_client *client; struct bq24735_platform *pdata; + struct mutex lock; + bool charging; }; static inline struct bq24735 *to_bq24735(struct power_supply *psy) @@ -56,9 +58,23 @@ static inline struct bq24735 *to_bq24735(struct power_supply *psy) } static enum power_supply_property bq24735_charger_properties[] = { + POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, }; +static int bq24735_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + return 1; + default: + break; + } + + return 0; +} + static inline int bq24735_write_word(struct i2c_client *client, u8 reg, u16 value) { @@ -90,6 +106,9 @@ static int bq24735_update_word(struct i2c_client *client, u8 reg, static inline int bq24735_enable_charging(struct bq24735 *charger) { + if (charger->pdata->ext_control) + return 0; + return bq24735_update_word(charger->client, BQ24735_CHG_OPT, BQ24735_CHG_OPT_CHARGE_DISABLE, ~BQ24735_CHG_OPT_CHARGE_DISABLE); @@ -97,6 +116,9 @@ static inline int bq24735_enable_charging(struct bq24735 *charger) static inline int bq24735_disable_charging(struct bq24735 *charger) { + if (charger->pdata->ext_control) + return 0; + return bq24735_update_word(charger->client, BQ24735_CHG_OPT, BQ24735_CHG_OPT_CHARGE_DISABLE, BQ24735_CHG_OPT_CHARGE_DISABLE); @@ -108,6 +130,9 @@ static int bq24735_config_charger(struct bq24735 *charger) int ret; u16 value; + if (pdata->ext_control) + return 0; + if (pdata->charge_current) { value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK; @@ -174,16 +199,30 @@ static bool bq24735_charger_is_present(struct bq24735 *charger) return false; } +static int bq24735_charger_is_charging(struct bq24735 *charger) +{ + int ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT); + + if (ret < 0) + return ret; + + return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE); +} + static irqreturn_t bq24735_charger_isr(int irq, void *devid) { struct power_supply *psy = devid; struct bq24735 *charger = to_bq24735(psy); - if (bq24735_charger_is_present(charger)) + mutex_lock(&charger->lock); + + if (charger->charging && bq24735_charger_is_present(charger)) bq24735_enable_charging(charger); else bq24735_disable_charging(charger); + mutex_unlock(&charger->lock); + power_supply_changed(psy); return IRQ_HANDLED; @@ -199,6 +238,19 @@ static int bq24735_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ONLINE: val->intval = bq24735_charger_is_present(charger) ? 1 : 0; break; + case POWER_SUPPLY_PROP_STATUS: + switch (bq24735_charger_is_charging(charger)) { + case 1: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case 0: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + break; default: return -EINVAL; } @@ -206,6 +258,46 @@ static int bq24735_charger_get_property(struct power_supply *psy, return 0; } +static int bq24735_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq24735 *charger = to_bq24735(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + switch (val->intval) { + case POWER_SUPPLY_STATUS_CHARGING: + mutex_lock(&charger->lock); + charger->charging = true; + ret = bq24735_enable_charging(charger); + mutex_unlock(&charger->lock); + if (ret) + return ret; + bq24735_config_charger(charger); + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + case POWER_SUPPLY_STATUS_NOT_CHARGING: + mutex_lock(&charger->lock); + charger->charging = false; + ret = bq24735_disable_charging(charger); + mutex_unlock(&charger->lock); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + power_supply_changed(psy); + break; + default: + return -EPERM; + } + + return 0; +} + static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client) { struct bq24735_platform *pdata; @@ -239,6 +331,8 @@ static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client) if (!ret) pdata->input_current = val; + pdata->ext_control = of_property_read_bool(np, "ti,external-control"); + return pdata; } @@ -255,6 +349,8 @@ static int bq24735_charger_probe(struct i2c_client *client, if (!charger) return -ENOMEM; + mutex_init(&charger->lock); + charger->charging = true; charger->pdata = client->dev.platform_data; if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node) @@ -285,6 +381,9 @@ static int bq24735_charger_probe(struct i2c_client *client, supply_desc->properties = bq24735_charger_properties; supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties); supply_desc->get_property = bq24735_charger_get_property; + supply_desc->set_property = bq24735_charger_set_property; + supply_desc->property_is_writeable = + bq24735_charger_property_is_writeable; psy_cfg.supplied_to = charger->pdata->supplied_to; psy_cfg.num_supplicants = charger->pdata->num_supplicants; @@ -293,27 +392,6 @@ static int bq24735_charger_probe(struct i2c_client *client, i2c_set_clientdata(client, charger); - ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID); - if (ret < 0) { - dev_err(&client->dev, "Failed to read manufacturer id : %d\n", - ret); - return ret; - } else if (ret != 0x0040) { - dev_err(&client->dev, - "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret); - return -ENODEV; - } - - ret = bq24735_read_word(client, BQ24735_DEVICE_ID); - if (ret < 0) { - dev_err(&client->dev, "Failed to read device id : %d\n", ret); - return ret; - } else if (ret != 0x000B) { - dev_err(&client->dev, - "device id mismatch. 0x000b != 0x%04x\n", ret); - return -ENODEV; - } - if (gpio_is_valid(charger->pdata->status_gpio)) { ret = devm_gpio_request(&client->dev, charger->pdata->status_gpio, @@ -327,6 +405,30 @@ static int bq24735_charger_probe(struct i2c_client *client, charger->pdata->status_gpio_valid = !ret; } + if (!charger->pdata->status_gpio_valid + || bq24735_charger_is_present(charger)) { + ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID); + if (ret < 0) { + dev_err(&client->dev, "Failed to read manufacturer id : %d\n", + ret); + return ret; + } else if (ret != 0x0040) { + dev_err(&client->dev, + "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret); + return -ENODEV; + } + + ret = bq24735_read_word(client, BQ24735_DEVICE_ID); + if (ret < 0) { + dev_err(&client->dev, "Failed to read device id : %d\n", ret); + return ret; + } else if (ret != 0x000B) { + dev_err(&client->dev, + "device id mismatch. 0x000b != 0x%04x\n", ret); + return -ENODEV; + } + } + ret = bq24735_config_charger(charger); if (ret < 0) { dev_err(&client->dev, "failed in configuring charger"); |