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

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
...@@ -141,6 +141,7 @@ struct rcar_i2c_priv { ...@@ -141,6 +141,7 @@ struct rcar_i2c_priv {
enum dma_data_direction dma_direction; enum dma_data_direction dma_direction;
struct reset_control *rstc; struct reset_control *rstc;
bool atomic_xfer;
int irq; int irq;
struct i2c_client *host_notify_client; struct i2c_client *host_notify_client;
...@@ -353,7 +354,9 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv) ...@@ -353,7 +354,9 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START); rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
rcar_i2c_write(priv, ICMSR, 0); rcar_i2c_write(priv, ICMSR, 0);
} }
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
if (!priv->atomic_xfer)
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
} }
static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv) static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
...@@ -418,7 +421,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv) ...@@ -418,7 +421,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
int len; int len;
/* Do various checks to see if DMA is feasible at all */ /* 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)) !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
return false; return false;
...@@ -646,7 +649,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr) ...@@ -646,7 +649,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
/* Nack */ /* Nack */
if (msr & MNR) { if (msr & MNR) {
/* HW automatically sends STOP after received NACK */ /* HW automatically sends STOP after received NACK */
rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP); if (!priv->atomic_xfer)
rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
priv->flags |= ID_NACK; priv->flags |= ID_NACK;
goto out; goto out;
} }
...@@ -667,7 +671,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr) ...@@ -667,7 +671,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
if (priv->flags & ID_DONE) { if (priv->flags & ID_DONE) {
rcar_i2c_write(priv, ICMIER, 0); rcar_i2c_write(priv, ICMIER, 0);
rcar_i2c_write(priv, ICMSR, 0); rcar_i2c_write(priv, ICMSR, 0);
wake_up(&priv->wait); if (!priv->atomic_xfer)
wake_up(&priv->wait);
} }
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -684,7 +689,8 @@ static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr) ...@@ -684,7 +689,8 @@ static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr)
/* Only handle interrupts that are currently enabled */ /* Only handle interrupts that are currently enabled */
msr = rcar_i2c_read(priv, ICMSR); msr = rcar_i2c_read(priv, ICMSR);
msr &= rcar_i2c_read(priv, ICMIER); if (!priv->atomic_xfer)
msr &= rcar_i2c_read(priv, ICMIER);
return rcar_i2c_irq(irq, priv, msr); return rcar_i2c_irq(irq, priv, msr);
} }
...@@ -696,7 +702,8 @@ static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr) ...@@ -696,7 +702,8 @@ static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr)
/* Only handle interrupts that are currently enabled */ /* Only handle interrupts that are currently enabled */
msr = rcar_i2c_read(priv, ICMSR); msr = rcar_i2c_read(priv, ICMSR);
msr &= rcar_i2c_read(priv, ICMIER); if (!priv->atomic_xfer)
msr &= rcar_i2c_read(priv, ICMIER);
/* /*
* Clear START or STOP immediately, except for REPSTART after read or * Clear START or STOP immediately, except for REPSTART after read or
...@@ -804,6 +811,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, ...@@ -804,6 +811,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
int i, ret; int i, ret;
long time_left; long time_left;
priv->atomic_xfer = false;
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
/* Check bus state before init otherwise bus busy info will be lost */ /* 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, ...@@ -858,6 +867,68 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
return ret; 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) static int rcar_reg_slave(struct i2c_client *slave)
{ {
struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
...@@ -922,6 +993,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) ...@@ -922,6 +993,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap)
static const struct i2c_algorithm rcar_i2c_algo = { static const struct i2c_algorithm rcar_i2c_algo = {
.master_xfer = rcar_i2c_master_xfer, .master_xfer = rcar_i2c_master_xfer,
.master_xfer_atomic = rcar_i2c_master_xfer_atomic,
.functionality = rcar_i2c_func, .functionality = rcar_i2c_func,
.reg_slave = rcar_reg_slave, .reg_slave = rcar_reg_slave,
.unreg_slave = rcar_unreg_slave, .unreg_slave = rcar_unreg_slave,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment