summaryrefslogtreecommitdiff
path: root/drivers/power/bq24735-charger.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/bq24735-charger.c')
-rw-r--r--drivers/power/bq24735-charger.c146
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");