|
@@ -71,6 +71,7 @@ struct em_i2c_device {
|
|
|
struct i2c_adapter adap;
|
|
|
struct completion msg_done;
|
|
|
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)
|
|
@@ -226,22 +227,131 @@ static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|
|
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)
|
|
|
{
|
|
|
struct em_i2c_device *priv = dev_id;
|
|
|
|
|
|
+ if (em_i2c_slave_irq(priv))
|
|
|
+ return IRQ_HANDLED;
|
|
|
+
|
|
|
complete(&priv->msg_done);
|
|
|
+
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
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 = {
|
|
|
.master_xfer = em_i2c_xfer,
|
|
|
.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)
|