Manually merged:
  layerscape/808-i2c-0011-i2c-imx-support-slave-mode-for-imx-I2C-driver.patch
  layerscape/808-i2c-0012-i2c-imx-correct-code-of-errata-A-010650-for-layersca.patch
Remaining modifications made by update_kernel.sh
Build system: x86_64
Build-tested: ipq806x/R7800, ath79/generic, bcm27xx/bcm2711, x86/64 [*],
              ramips/mt7621 [*], ath79/tiny [*], ipq40xx [*], octeon [*],
              realtek [*]
Run-tested: ipq806x/R7800, ramips/mt7621 [*], octeon [*], realtek [*]
No dmesg regressions, everything functional
Signed-off-by: John Audia <graysky@archlinux.us>
Tested-by: Stijn Segers <foss@volatilesystems.org> [*]
		
	
		
			
				
	
	
		
			283 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 865433df5d11aef7cfe5d51b362b6276bddb7a15 Mon Sep 17 00:00:00 2001
 | 
						|
From: Biwen Li <biwen.li@nxp.com>
 | 
						|
Date: Fri, 2 Aug 2019 17:45:56 +0800
 | 
						|
Subject: [PATCH] i2c: imx: support slave mode for imx I2C driver
 | 
						|
 | 
						|
The patch supports slave mode for imx I2C driver
 | 
						|
 | 
						|
Reviewed-by: Clark Wang <xiaoning.wang@nxp.com>
 | 
						|
Signed-off-by: Biwen Li <biwen.li@nxp.com>
 | 
						|
---
 | 
						|
 drivers/i2c/busses/i2c-imx.c | 219 +++++++++++++++++++++++++++++++++++++++----
 | 
						|
 1 file changed, 201 insertions(+), 18 deletions(-)
 | 
						|
 | 
						|
--- a/drivers/i2c/busses/i2c-imx.c
 | 
						|
+++ b/drivers/i2c/busses/i2c-imx.c
 | 
						|
@@ -265,6 +265,9 @@ struct imx_i2c_struct {
 | 
						|
 	int			pmuxcr_set;
 | 
						|
 	int			pmuxcr_endian;
 | 
						|
 	void __iomem		*pmuxcr_addr;
 | 
						|
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
 | 
						|
+	struct i2c_client	*slave;
 | 
						|
+#endif
 | 
						|
 };
 | 
						|
 
 | 
						|
 static const struct imx_i2c_hwdata imx1_i2c_hwdata = {
 | 
						|
@@ -357,6 +360,14 @@ static inline unsigned char imx_i2c_read
 | 
						|
 	return readb(i2c_imx->base + (reg << i2c_imx->hwdata->regshift));
 | 
						|
 }
 | 
						|
 
 | 
						|
+/* Set up i2c controller register and i2c status register to default value. */
 | 
						|
+static void i2c_imx_reset_regs(struct imx_i2c_struct *i2c_imx)
 | 
						|
+{
 | 
						|
+	imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
 | 
						|
+			i2c_imx, IMX_I2C_I2CR);
 | 
						|
+	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
 | 
						|
+}
 | 
						|
+
 | 
						|
 /* Functions for DMA support */
 | 
						|
 static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
 | 
						|
 						dma_addr_t phy_addr)
 | 
						|
@@ -705,21 +716,33 @@ static void i2c_imx_stop(struct imx_i2c_
 | 
						|
 	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
 | 
						|
 }
 | 
						|
 
 | 
						|
-static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
 | 
						|
+/* Clear interrupt flag bit */
 | 
						|
+static void i2c_imx_clr_if_bit(unsigned int status, struct imx_i2c_struct *i2c_imx)
 | 
						|
 {
 | 
						|
-	struct imx_i2c_struct *i2c_imx = dev_id;
 | 
						|
-	unsigned int temp;
 | 
						|
+	status &= ~I2SR_IIF;
 | 
						|
+	status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
 | 
						|
+	imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
 | 
						|
+}
 | 
						|
 
 | 
						|
-	temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
 | 
						|
-	if (temp & I2SR_IIF) {
 | 
						|
-		/* save status register */
 | 
						|
-		i2c_imx->i2csr = temp;
 | 
						|
-		i2c_imx_clear_irq(i2c_imx, I2SR_IIF);
 | 
						|
-		wake_up(&i2c_imx->queue);
 | 
						|
-		return IRQ_HANDLED;
 | 
						|
-	}
 | 
						|
+/* Clear arbitration lost bit */
 | 
						|
+static void i2c_imx_clr_al_bit(unsigned int status, struct imx_i2c_struct *i2c_imx)
 | 
						|
+{
 | 
						|
+	status &= ~I2SR_IAL;
 | 
						|
+	status |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IAL);
 | 
						|
+	imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static irqreturn_t i2c_imx_master_isr(struct imx_i2c_struct *i2c_imx)
 | 
						|
+{
 | 
						|
+	unsigned int status;
 | 
						|
+
 | 
						|
+	/* Save status register */
 | 
						|
+	status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
 | 
						|
+	i2c_imx->i2csr = status | I2SR_IIF;
 | 
						|
+
 | 
						|
+	wake_up(&i2c_imx->queue);
 | 
						|
 
 | 
						|
-	return IRQ_NONE;
 | 
						|
+	return IRQ_HANDLED;
 | 
						|
 }
 | 
						|
 
 | 
						|
 static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
 | 
						|
@@ -1094,6 +1117,13 @@ static int i2c_imx_xfer(struct i2c_adapt
 | 
						|
 
 | 
						|
 	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
 | 
						|
 
 | 
						|
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
 | 
						|
+	if (i2c_imx->slave) {
 | 
						|
+		dev_err(&i2c_imx->adapter.dev, "Please not do operations of master mode in slave mode");
 | 
						|
+		return -EBUSY;
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
 	if (!pm_runtime_enabled(i2c_imx->adapter.dev.parent)) {
 | 
						|
 		pm_runtime_enable(i2c_imx->adapter.dev.parent);
 | 
						|
 		enable_runtime_pm = true;
 | 
						|
@@ -1307,11 +1337,169 @@ static u32 i2c_imx_func(struct i2c_adapt
 | 
						|
 		| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
 | 
						|
 }
 | 
						|
 
 | 
						|
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
 | 
						|
+static int i2c_imx_slave_init(struct imx_i2c_struct *i2c_imx)
 | 
						|
+{
 | 
						|
+	int temp;
 | 
						|
+
 | 
						|
+	/* Resume */
 | 
						|
+	temp = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
 | 
						|
+	if (temp < 0) {
 | 
						|
+		dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller");
 | 
						|
+		return temp;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Set slave addr. */
 | 
						|
+	imx_i2c_write_reg((i2c_imx->slave->addr << 1), i2c_imx, IMX_I2C_IADR);
 | 
						|
+
 | 
						|
+	i2c_imx_reset_regs(i2c_imx);
 | 
						|
+
 | 
						|
+	/* Enable module */
 | 
						|
+	temp = i2c_imx->hwdata->i2cr_ien_opcode;
 | 
						|
+	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
 | 
						|
+
 | 
						|
+	/* Enable interrupt from i2c module */
 | 
						|
+	temp |= I2CR_IIEN;
 | 
						|
+	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
 | 
						|
+
 | 
						|
+	/* Wait controller to be stable */
 | 
						|
+	usleep_range(50, 150);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static irqreturn_t i2c_imx_slave_isr(struct imx_i2c_struct *i2c_imx)
 | 
						|
+{
 | 
						|
+	unsigned int status, ctl;
 | 
						|
+	u8 value;
 | 
						|
+
 | 
						|
+	if (!i2c_imx->slave) {
 | 
						|
+		dev_err(&i2c_imx->adapter.dev, "cannot deal with slave irq,i2c_imx->slave is null");
 | 
						|
+		return IRQ_NONE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
 | 
						|
+	ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
 | 
						|
+	if (status & I2SR_IAL) { /* Arbitration lost */
 | 
						|
+		i2c_imx_clr_al_bit(status, i2c_imx);
 | 
						|
+	} else if (status & I2SR_IAAS) { /* Addressed as a slave */
 | 
						|
+		if (status & I2SR_SRW) { /* Master wants to read from us*/
 | 
						|
+			dev_dbg(&i2c_imx->adapter.dev, "read requested");
 | 
						|
+			i2c_slave_event(i2c_imx->slave, I2C_SLAVE_READ_REQUESTED, &value);
 | 
						|
+
 | 
						|
+			/* Slave transmit */
 | 
						|
+			ctl |= I2CR_MTX;
 | 
						|
+			imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
 | 
						|
+
 | 
						|
+			/* Send data */
 | 
						|
+			imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
 | 
						|
+		} else { /* Master wants to write to us */
 | 
						|
+			dev_dbg(&i2c_imx->adapter.dev, "write requested");
 | 
						|
+			i2c_slave_event(i2c_imx->slave,	I2C_SLAVE_WRITE_REQUESTED, &value);
 | 
						|
+
 | 
						|
+			/* Slave receive */
 | 
						|
+			ctl &= ~I2CR_MTX;
 | 
						|
+			imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
 | 
						|
+			/* Dummy read */
 | 
						|
+			imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
 | 
						|
+		}
 | 
						|
+	} else if (!(ctl & I2CR_MTX)) { /* Receive mode */
 | 
						|
+			if (status & I2SR_IBB) { /* No STOP signal detected */
 | 
						|
+				ctl &= ~I2CR_MTX;
 | 
						|
+				imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
 | 
						|
+
 | 
						|
+				value = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
 | 
						|
+				i2c_slave_event(i2c_imx->slave,	I2C_SLAVE_WRITE_RECEIVED, &value);
 | 
						|
+			} else { /* STOP signal is detected */
 | 
						|
+				dev_dbg(&i2c_imx->adapter.dev,
 | 
						|
+					"STOP signal detected");
 | 
						|
+				i2c_slave_event(i2c_imx->slave, I2C_SLAVE_STOP, &value);
 | 
						|
+			}
 | 
						|
+	} else if (!(status & I2SR_RXAK)) {	/* Transmit mode received ACK */
 | 
						|
+		ctl |= I2CR_MTX;
 | 
						|
+		imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
 | 
						|
+
 | 
						|
+		i2c_slave_event(i2c_imx->slave,	I2C_SLAVE_READ_PROCESSED, &value);
 | 
						|
+
 | 
						|
+		imx_i2c_write_reg(value, i2c_imx, IMX_I2C_I2DR);
 | 
						|
+	} else { /* Transmit mode received NAK */
 | 
						|
+		ctl &= ~I2CR_MTX;
 | 
						|
+		imx_i2c_write_reg(ctl, i2c_imx, IMX_I2C_I2CR);
 | 
						|
+		imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR);
 | 
						|
+	}
 | 
						|
+	return IRQ_HANDLED;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int i2c_imx_reg_slave(struct i2c_client *client)
 | 
						|
+{
 | 
						|
+	struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
 | 
						|
+	int ret;
 | 
						|
+	if (i2c_imx->slave)
 | 
						|
+		return -EBUSY;
 | 
						|
+
 | 
						|
+	i2c_imx->slave = client;
 | 
						|
+
 | 
						|
+	ret = i2c_imx_slave_init(i2c_imx);
 | 
						|
+	if (ret < 0)
 | 
						|
+		dev_err(&i2c_imx->adapter.dev, "failed to switch to slave mode");
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int i2c_imx_unreg_slave(struct i2c_client *client)
 | 
						|
+{
 | 
						|
+	struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(client->adapter);
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	if (!i2c_imx->slave)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	/* Reset slave address. */
 | 
						|
+	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);
 | 
						|
+
 | 
						|
+	i2c_imx_reset_regs(i2c_imx);
 | 
						|
+
 | 
						|
+	i2c_imx->slave = NULL;
 | 
						|
+
 | 
						|
+	/* Suspend */
 | 
						|
+	ret = pm_runtime_put_sync(i2c_imx->adapter.dev.parent);
 | 
						|
+	if (ret < 0)
 | 
						|
+		dev_err(&i2c_imx->adapter.dev, "failed to suspend i2c controller");
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
 static const struct i2c_algorithm i2c_imx_algo = {
 | 
						|
 	.master_xfer	= i2c_imx_xfer,
 | 
						|
 	.functionality	= i2c_imx_func,
 | 
						|
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
 | 
						|
+	.reg_slave	= i2c_imx_reg_slave,
 | 
						|
+	.unreg_slave	= i2c_imx_unreg_slave,
 | 
						|
+#endif
 | 
						|
 };
 | 
						|
 
 | 
						|
+static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
 | 
						|
+{
 | 
						|
+	struct imx_i2c_struct *i2c_imx = dev_id;
 | 
						|
+	unsigned int status, ctl;
 | 
						|
+	irqreturn_t irq_status = IRQ_NONE;
 | 
						|
+
 | 
						|
+	status = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
 | 
						|
+	ctl = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
 | 
						|
+
 | 
						|
+	if (status & I2SR_IIF) {
 | 
						|
+		i2c_imx_clr_if_bit(status, i2c_imx);
 | 
						|
+		if (ctl & I2CR_MSTA)
 | 
						|
+			irq_status = i2c_imx_master_isr(i2c_imx);
 | 
						|
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
 | 
						|
+		else
 | 
						|
+			irq_status = i2c_imx_slave_isr(i2c_imx);
 | 
						|
+#endif
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return irq_status;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static int i2c_imx_probe(struct platform_device *pdev)
 | 
						|
 {
 | 
						|
 	struct imx_i2c_struct *i2c_imx;
 | 
						|
@@ -1420,10 +1608,7 @@ static int i2c_imx_probe(struct platform
 | 
						|
 	if (is_imx7d_i2c(i2c_imx) && i2c_imx->bitrate > IMX_I2C_MAX_E_BIT_RATE)
 | 
						|
 		i2c_imx->bitrate = IMX_I2C_MAX_E_BIT_RATE;
 | 
						|
 
 | 
						|
-	/* Set up chip registers to defaults */
 | 
						|
-	imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
 | 
						|
-			i2c_imx, IMX_I2C_I2CR);
 | 
						|
-	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
 | 
						|
+	i2c_imx_reset_regs(i2c_imx);
 | 
						|
 
 | 
						|
 	/* Init optional bus recovery */
 | 
						|
 	if (of_match_node(pinmux_of_match, pdev->dev.of_node))
 |