Commit c31d0a00 authored by Niklas Söderlund's avatar Niklas Söderlund Committed by Wolfram Sang

i2c: emev2: add slave support

Add I2C slave provider using the generic slave interface.
Signed-off-by: default avatarNiklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 7a852b02
...@@ -71,6 +71,7 @@ struct em_i2c_device { ...@@ -71,6 +71,7 @@ struct em_i2c_device {
struct i2c_adapter adap; struct i2c_adapter adap;
struct completion msg_done; struct completion msg_done;
struct clk *sclk; struct clk *sclk;
struct i2c_client *slave;
}; };
static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg) static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg)
...@@ -226,22 +227,131 @@ static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ...@@ -226,22 +227,131 @@ static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
return num; return num;
} }
static bool em_i2c_slave_irq(struct em_i2c_device *priv)
{
u8 status, value;
enum i2c_slave_event event;
int ret;
if (!priv->slave)
return false;
status = readb(priv->base + I2C_OFS_IICSE0);
/* Extension code, do not participate */
if (status & I2C_BIT_EXC0) {
em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0);
return true;
}
/* Stop detected, we don't know if it's for slave or master */
if (status & I2C_BIT_SPD0) {
/* Notify slave device */
i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value);
/* Pretend we did not handle the interrupt */
return false;
}
/* Only handle interrupts addressed to us */
if (!(status & I2C_BIT_COI0))
return false;
/* Enable stop interrupts */
em_clear_set_bit(priv, 0, I2C_BIT_SPIE0, I2C_OFS_IICC0);
/* Transmission or Reception */
if (status & I2C_BIT_TRC0) {
if (status & I2C_BIT_ACKD0) {
/* 9 bit interrupt mode */
em_clear_set_bit(priv, 0, I2C_BIT_WTIM0, I2C_OFS_IICC0);
/* Send data */
event = status & I2C_BIT_STD0 ?
I2C_SLAVE_READ_REQUESTED :
I2C_SLAVE_READ_PROCESSED;
i2c_slave_event(priv->slave, event, &value);
writeb(value, priv->base + I2C_OFS_IIC0);
} else {
/* NACK, stop transmitting */
em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0);
}
} else {
/* 8 bit interrupt mode */
em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_ACKE0,
I2C_OFS_IICC0);
em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_WREL0,
I2C_OFS_IICC0);
if (status & I2C_BIT_STD0) {
i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED,
&value);
} else {
/* Recv data */
value = readb(priv->base + I2C_OFS_IIC0);
ret = i2c_slave_event(priv->slave,
I2C_SLAVE_WRITE_RECEIVED, &value);
if (ret < 0)
em_clear_set_bit(priv, I2C_BIT_ACKE0, 0,
I2C_OFS_IICC0);
}
}
return true;
}
static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id) static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id)
{ {
struct em_i2c_device *priv = dev_id; struct em_i2c_device *priv = dev_id;
if (em_i2c_slave_irq(priv))
return IRQ_HANDLED;
complete(&priv->msg_done); complete(&priv->msg_done);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static u32 em_i2c_func(struct i2c_adapter *adap) static u32 em_i2c_func(struct i2c_adapter *adap)
{ {
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE;
}
static int em_i2c_reg_slave(struct i2c_client *slave)
{
struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter);
if (priv->slave)
return -EBUSY;
if (slave->flags & I2C_CLIENT_TEN)
return -EAFNOSUPPORT;
priv->slave = slave;
/* Set slave address */
writeb(slave->addr << 1, priv->base + I2C_OFS_SVA0);
return 0;
}
static int em_i2c_unreg_slave(struct i2c_client *slave)
{
struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter);
WARN_ON(!priv->slave);
writeb(0, priv->base + I2C_OFS_SVA0);
priv->slave = NULL;
return 0;
} }
static struct i2c_algorithm em_i2c_algo = { static struct i2c_algorithm em_i2c_algo = {
.master_xfer = em_i2c_xfer, .master_xfer = em_i2c_xfer,
.functionality = em_i2c_func, .functionality = em_i2c_func,
.reg_slave = em_i2c_reg_slave,
.unreg_slave = em_i2c_unreg_slave,
}; };
static int em_i2c_probe(struct platform_device *pdev) static int em_i2c_probe(struct platform_device *pdev)
......
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