power: bq25890: protect view of the chip's state

Extend bq->lock over whole updating of the chip's state. Might get
useful later for switching ADC modes correctly.

Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
This commit is contained in:
Michał Mirosław 2020-05-03 17:21:11 +02:00 committed by Sebastian Reichel
parent a9c2419406
commit 72d9cd9cdc

View File

@ -510,74 +510,50 @@ static int bq25890_get_chip_state(struct bq25890_device *bq,
return 0; return 0;
} }
static bool bq25890_state_changed(struct bq25890_device *bq, static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq)
struct bq25890_state *new_state)
{
struct bq25890_state old_state;
mutex_lock(&bq->lock);
old_state = bq->state;
mutex_unlock(&bq->lock);
return (old_state.chrg_status != new_state->chrg_status ||
old_state.chrg_fault != new_state->chrg_fault ||
old_state.online != new_state->online ||
old_state.bat_fault != new_state->bat_fault ||
old_state.boost_fault != new_state->boost_fault ||
old_state.vsys_status != new_state->vsys_status);
}
static void bq25890_handle_state_change(struct bq25890_device *bq,
struct bq25890_state *new_state)
{ {
struct bq25890_state new_state;
int ret; int ret;
struct bq25890_state old_state;
mutex_lock(&bq->lock); ret = bq25890_get_chip_state(bq, &new_state);
old_state = bq->state; if (ret < 0)
mutex_unlock(&bq->lock); return IRQ_NONE;
if (!new_state->online) { /* power removed */ if (!memcmp(&bq->state, &new_state, sizeof(new_state)))
return IRQ_NONE;
if (!new_state.online && bq->state.online) { /* power removed */
/* disable ADC */ /* disable ADC */
ret = bq25890_field_write(bq, F_CONV_START, 0); ret = bq25890_field_write(bq, F_CONV_START, 0);
if (ret < 0) if (ret < 0)
goto error; goto error;
} else if (!old_state.online) { /* power inserted */ } else if (new_state.online && !bq->state.online) { /* power inserted */
/* enable ADC, to have control of charge current/voltage */ /* enable ADC, to have control of charge current/voltage */
ret = bq25890_field_write(bq, F_CONV_START, 1); ret = bq25890_field_write(bq, F_CONV_START, 1);
if (ret < 0) if (ret < 0)
goto error; goto error;
} }
return; bq->state = new_state;
power_supply_changed(bq->charger);
return IRQ_HANDLED;
error: error:
dev_err(bq->dev, "Error communicating with the chip.\n"); dev_err(bq->dev, "Error communicating with the chip: %pe\n",
ERR_PTR(ret));
return IRQ_HANDLED;
} }
static irqreturn_t bq25890_irq_handler_thread(int irq, void *private) static irqreturn_t bq25890_irq_handler_thread(int irq, void *private)
{ {
struct bq25890_device *bq = private; struct bq25890_device *bq = private;
int ret; irqreturn_t ret;
struct bq25890_state state;
ret = bq25890_get_chip_state(bq, &state);
if (ret < 0)
goto handled;
if (!bq25890_state_changed(bq, &state))
goto handled;
bq25890_handle_state_change(bq, &state);
mutex_lock(&bq->lock); mutex_lock(&bq->lock);
bq->state = state; ret = __bq25890_handle_irq(bq);
mutex_unlock(&bq->lock); mutex_unlock(&bq->lock);
power_supply_changed(bq->charger); return ret;
handled:
return IRQ_HANDLED;
} }
static int bq25890_chip_reset(struct bq25890_device *bq) static int bq25890_chip_reset(struct bq25890_device *bq)
@ -607,7 +583,6 @@ static int bq25890_hw_init(struct bq25890_device *bq)
{ {
int ret; int ret;
int i; int i;
struct bq25890_state state;
const struct { const struct {
enum bq25890_fields id; enum bq25890_fields id;
@ -655,16 +630,12 @@ static int bq25890_hw_init(struct bq25890_device *bq)
return ret; return ret;
} }
ret = bq25890_get_chip_state(bq, &state); ret = bq25890_get_chip_state(bq, &bq->state);
if (ret < 0) { if (ret < 0) {
dev_dbg(bq->dev, "Get state failed %d\n", ret); dev_dbg(bq->dev, "Get state failed %d\n", ret);
return ret; return ret;
} }
mutex_lock(&bq->lock);
bq->state = state;
mutex_unlock(&bq->lock);
return 0; return 0;
} }
@ -1001,19 +972,16 @@ static int bq25890_suspend(struct device *dev)
static int bq25890_resume(struct device *dev) static int bq25890_resume(struct device *dev)
{ {
int ret; int ret;
struct bq25890_state state;
struct bq25890_device *bq = dev_get_drvdata(dev); struct bq25890_device *bq = dev_get_drvdata(dev);
ret = bq25890_get_chip_state(bq, &state); mutex_lock(&bq->lock);
ret = bq25890_get_chip_state(bq, &bq->state);
if (ret < 0) if (ret < 0)
return ret; return ret;
mutex_lock(&bq->lock);
bq->state = state;
mutex_unlock(&bq->lock);
/* Re-enable ADC only if charger is plugged in. */ /* Re-enable ADC only if charger is plugged in. */
if (state.online) { if (bq->state.online) {
ret = bq25890_field_write(bq, F_CONV_START, 1); ret = bq25890_field_write(bq, F_CONV_START, 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1022,6 +990,8 @@ static int bq25890_resume(struct device *dev)
/* signal userspace, maybe state changed while suspended */ /* signal userspace, maybe state changed while suspended */
power_supply_changed(bq->charger); power_supply_changed(bq->charger);
mutex_unlock(&bq->lock);
return 0; return 0;
} }
#endif #endif