Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+111 −1
Original line number Original line Diff line number Diff line
@@ -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,
	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)