mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-28 23:24:50 +00:00
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: Ulrich Hecht <uli+renesas@fpond.eu> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Signed-off-by: Wolfram Sang <wsa@kernel.org>
This commit is contained in:
parent
b87752528f
commit
82531dfdf1
1 changed files with 78 additions and 6 deletions
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue