mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 14:04:26 -04:00 
			
		
		
		
	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))
 |