Commit 72d9cd9c authored by Michał Mirosław's avatar Michał Mirosław Committed by Sebastian Reichel
Browse files

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: default avatarMichał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
parent a9c24194
Loading
Loading
Loading
Loading
+26 −56
Original line number Original line Diff line number Diff line
@@ -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