Commit 82531dfd authored by Ulrich Hecht's avatar Ulrich Hecht Committed by Wolfram Sang
Browse files

i2c: rcar: implement atomic transfers



Implements atomic transfers. Tested by rebooting an r8a7790 Lager board
after connecting the i2c-rcar controller to the PMIC in
arch/arm/boot/dts/r8a7790-lager.dts like so:

		compatible = "i2c-demux-pinctrl";
		pinctrl-names = "default";
		pinctrl-0 = <&pmic_irq_pins>;
-		i2c-parent = <&iic3>, <&i2c3>;
+		i2c-parent = <&i2c3>, <&iic3>;
		i2c-bus-name = "i2c-pwr";
		#address-cells = <1>;
		#size-cells = <0>;

Signed-off-by: default avatarUlrich Hecht <uli+renesas@fpond.eu>
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent b8775252
Loading
Loading
Loading
Loading
+78 −6
Original line number Diff line number Diff line
@@ -141,6 +141,7 @@ struct rcar_i2c_priv {
	enum dma_data_direction dma_direction;

	struct reset_control *rstc;
	bool atomic_xfer;
	int irq;

	struct i2c_client *host_notify_client;
@@ -353,6 +354,8 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
			rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
		rcar_i2c_write(priv, ICMSR, 0);
	}

	if (!priv->atomic_xfer)
		rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
}

@@ -418,7 +421,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
	int len;

	/* Do various checks to see if DMA is feasible at all */
	if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
	if (priv->atomic_xfer || IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
	    !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
		return false;

@@ -646,6 +649,7 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
	/* Nack */
	if (msr & MNR) {
		/* HW automatically sends STOP after received NACK */
		if (!priv->atomic_xfer)
			rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
		priv->flags |= ID_NACK;
		goto out;
@@ -667,6 +671,7 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
	if (priv->flags & ID_DONE) {
		rcar_i2c_write(priv, ICMIER, 0);
		rcar_i2c_write(priv, ICMSR, 0);
		if (!priv->atomic_xfer)
			wake_up(&priv->wait);
	}

@@ -684,6 +689,7 @@ static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr)

	/* Only handle interrupts that are currently enabled */
	msr = rcar_i2c_read(priv, ICMSR);
	if (!priv->atomic_xfer)
		msr &= rcar_i2c_read(priv, ICMIER);

	return rcar_i2c_irq(irq, priv, msr);
@@ -696,6 +702,7 @@ static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr)

	/* Only handle interrupts that are currently enabled */
	msr = rcar_i2c_read(priv, ICMSR);
	if (!priv->atomic_xfer)
		msr &= rcar_i2c_read(priv, ICMIER);

	/*
@@ -804,6 +811,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
	int i, ret;
	long time_left;

	priv->atomic_xfer = false;

	pm_runtime_get_sync(dev);

	/* Check bus state before init otherwise bus busy info will be lost */
@@ -858,6 +867,68 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
	return ret;
}

static int rcar_i2c_master_xfer_atomic(struct i2c_adapter *adap,
				struct i2c_msg *msgs,
				int num)
{
	struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
	struct device *dev = rcar_i2c_priv_to_dev(priv);
	unsigned long j;
	bool time_left;
	int ret;

	priv->atomic_xfer = true;

	pm_runtime_get_sync(dev);

	/* Check bus state before init otherwise bus busy info will be lost */
	ret = rcar_i2c_bus_barrier(priv);
	if (ret < 0)
		goto out;

	rcar_i2c_init(priv);

	/* init first message */
	priv->msg = msgs;
	priv->msgs_left = num;
	priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG;
	rcar_i2c_prepare_msg(priv);

	j = jiffies + num * adap->timeout;
	do {
		u32 msr = rcar_i2c_read(priv, ICMSR);

		msr &= (rcar_i2c_is_recv(priv) ? RCAR_IRQ_RECV : RCAR_IRQ_SEND) | RCAR_IRQ_STOP;

		if (msr) {
			if (priv->devtype < I2C_RCAR_GEN3)
				rcar_i2c_gen2_irq(0, priv);
			else
				rcar_i2c_gen3_irq(0, priv);
		}

		time_left = time_before_eq(jiffies, j);
	} while (!(priv->flags & ID_DONE) && time_left);

	if (!time_left) {
		rcar_i2c_init(priv);
		ret = -ETIMEDOUT;
	} else if (priv->flags & ID_NACK) {
		ret = -ENXIO;
	} else if (priv->flags & ID_ARBLOST) {
		ret = -EAGAIN;
	} else {
		ret = num - priv->msgs_left; /* The number of transfer */
	}
out:
	pm_runtime_put(dev);

	if (ret < 0 && ret != -ENXIO)
		dev_err(dev, "error %d : %x\n", ret, priv->flags);

	return ret;
}

static int rcar_reg_slave(struct i2c_client *slave)
{
	struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
@@ -922,6 +993,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap)

static const struct i2c_algorithm rcar_i2c_algo = {
	.master_xfer	= rcar_i2c_master_xfer,
	.master_xfer_atomic = rcar_i2c_master_xfer_atomic,
	.functionality	= rcar_i2c_func,
	.reg_slave	= rcar_reg_slave,
	.unreg_slave	= rcar_unreg_slave,