lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID:
 <OS8PR06MB75419AAE072E44CB2F1839C0F207A@OS8PR06MB7541.apcprd06.prod.outlook.com>
Date: Mon, 1 Sep 2025 01:06:28 +0000
From: Ryan Chen <ryan_chen@...eedtech.com>
To: Ryan Chen <ryan_chen@...eedtech.com>, "benh@...nel.crashing.org"
	<benh@...nel.crashing.org>, "joel@....id.au" <joel@....id.au>,
	"andi.shyti@...nel.org" <andi.shyti@...nel.org>, "robh@...nel.org"
	<robh@...nel.org>, "krzk+dt@...nel.org" <krzk+dt@...nel.org>,
	"conor+dt@...nel.org" <conor+dt@...nel.org>, "andrew@...econstruct.com.au"
	<andrew@...econstruct.com.au>, "p.zabel@...gutronix.de"
	<p.zabel@...gutronix.de>, "andriy.shevchenko@...ux.intel.com"
	<andriy.shevchenko@...ux.intel.com>, "naresh.solanki@...ements.com"
	<naresh.solanki@...ements.com>, "linux-i2c@...r.kernel.org"
	<linux-i2c@...r.kernel.org>, "openbmc@...ts.ozlabs.org"
	<openbmc@...ts.ozlabs.org>, "devicetree@...r.kernel.org"
	<devicetree@...r.kernel.org>, "linux-arm-kernel@...ts.infradead.org"
	<linux-arm-kernel@...ts.infradead.org>, "linux-aspeed@...ts.ozlabs.org"
	<linux-aspeed@...ts.ozlabs.org>, "linux-kernel@...r.kernel.org"
	<linux-kernel@...r.kernel.org>, "andriy.shevchenko@...ux.intel.com"
	<andriy.shevchenko@...ux.intel.com>
Subject: RE: [PATCH v18 3/3] i2c: ast2600: Add target mode support

> Subject: [PATCH v18 3/3] i2c: ast2600: Add target mode support
Hello Andy,
	sorry to bother you, do you have time to review this patch?
Ryan 

> 
> Add target mode support to the AST2600 I2C driver using new register layout.
> 
> Target mode features implemented include:
> - Add target interrupt handling
> - Address match and response logic
> - Separate Tx/Rx DMA address and length configuration
> 
> This complements the existing controller-mode support, enabling dual-role
> capability.
> 
> Signed-off-by: Ryan Chen <ryan_chen@...eedtech.com>
> ---
>  drivers/i2c/busses/i2c-ast2600.c | 560 +++++++++++++++++++++++++++++++
>  1 file changed, 560 insertions(+)
> 
> diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
> index 885b451030ac..81d1eb51334a 100644
> --- a/drivers/i2c/busses/i2c-ast2600.c
> +++ b/drivers/i2c/busses/i2c-ast2600.c
> @@ -81,6 +81,7 @@
>  #define AST2600_I2CC_THDDAT(x)			(((x) & GENMASK(1, 0)) <<
> 10)
>  #define AST2600_I2CC_TOUTBASECLK(x)			(((x) & GENMASK(1, 0))
> << 8)
>  #define AST2600_I2CC_TBASECLK(x)			((x) & GENMASK(3, 0))
> +#define AST2600_I2CC_AC_TIMING_MASK		GENMASK(23, 0)
> 
>  /* 0x08 : I2CC Controller/Target Transmit/Receive Byte Buffer Register */
>  #define AST2600_I2CC_STS_AND_BUFF		0x08
> @@ -271,6 +272,13 @@ struct ast2600_i2c_bus {
>  	/* Buffer mode */
>  	void __iomem		*buf_base;
>  	struct i2c_smbus_alert_setup	alert_data;
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +	/* target structure */
> +	int			target_operate;
> +	unsigned char	*target_dma_buf;
> +	dma_addr_t		target_dma_addr;
> +	struct i2c_client	*target;
> +#endif
>  };
> 
>  static u32 ast2600_select_i2c_clock(struct ast2600_i2c_bus *i2c_bus) @@
> -361,6 +369,423 @@ static int ast2600_i2c_recover_bus(struct
> ast2600_i2c_bus *i2c_bus)
>  	return ret;
>  }
> 
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static void ast2600_i2c_target_packet_dma_irq(struct ast2600_i2c_bus
> +*i2c_bus, u32 sts) {
> +	int target_rx_len = 0;
> +	u32 cmd = 0;
> +	u8 value;
> +	int i;
> +
> +	sts &= ~(AST2600_I2CS_SLAVE_PENDING);
> +	/* Handle i2c target timeout condition */
> +	if (AST2600_I2CS_INACTIVE_TO & sts) {
> +		cmd = TARGET_TRIGGER_CMD;
> +		cmd |= AST2600_I2CS_RX_DMA_EN;
> +
> 	writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +		writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
> +		writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base +
> AST2600_I2CS_ISR);
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		return;
> +	}
> +
> +	sts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR);
> +
> +	switch (sts) {
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_WAIT_RX_DMA:
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_RX_DMA:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED,
> &value);
> +		target_rx_len =
> AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
> +						      AST2600_I2CS_DMA_LEN_STS));
> +		for (i = 0; i < target_rx_len; i++) {
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> +					&i2c_bus->target_dma_buf[i]);
> +		}
> +
> 	writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
> +		break;
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_STOP:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +
> 	writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
> +		break;
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE_NAK |
> +			AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_RX_DMA |
> +			AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
> +	case AST2600_I2CS_RX_DONE_NAK | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_STOP:
> +	case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA |
> AST2600_I2CS_STOP:
> +	case AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
> +	case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA:
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_STOP:
> +		if (sts & AST2600_I2CS_SLAVE_MATCH)
> +			i2c_slave_event(i2c_bus->target,
> I2C_SLAVE_WRITE_REQUESTED, &value);
> +
> +		target_rx_len =
> AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
> +						      AST2600_I2CS_DMA_LEN_STS));
> +		for (i = 0; i < target_rx_len; i++) {
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> +					&i2c_bus->target_dma_buf[i]);
> +		}
> +
> 	writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +		if (sts & AST2600_I2CS_STOP)
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
> +		break;
> +
> +	/* it is Mw data Mr coming -> it need send tx */
> +	case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_TX_DMA:
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_WAIT_TX_DMA:
> +		/* it should be repeat start read */
> +		if (sts & AST2600_I2CS_SLAVE_MATCH)
> +			i2c_slave_event(i2c_bus->target,
> I2C_SLAVE_WRITE_REQUESTED, &value);
> +
> +		target_rx_len =
> AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
> +						      AST2600_I2CS_DMA_LEN_STS));
> +		for (i = 0; i < target_rx_len; i++) {
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> +					&i2c_bus->target_dma_buf[i]);
> +		}
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED,
> +				&i2c_bus->target_dma_buf[0]);
> +		writel(0, i2c_bus->reg_base + AST2600_I2CS_DMA_LEN_STS);
> +		writel(AST2600_I2CS_SET_TX_DMA_LEN(1),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN;
> +		break;
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA:
> +		/* First Start read */
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED,
> +				&i2c_bus->target_dma_buf[0]);
> +		writel(AST2600_I2CS_SET_TX_DMA_LEN(1),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN;
> +		break;
> +	case AST2600_I2CS_WAIT_TX_DMA:
> +		/* it should be next start read */
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED,
> +				&i2c_bus->target_dma_buf[0]);
> +		writel(0, i2c_bus->reg_base + AST2600_I2CS_DMA_LEN_STS);
> +		writel(AST2600_I2CS_SET_TX_DMA_LEN(1),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN;
> +		break;
> +	case AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP:
> +		/* it just tx complete */
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		writel(0, i2c_bus->reg_base + AST2600_I2CS_DMA_LEN_STS);
> +
> 	writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN;
> +		break;
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
> +		cmd = 0;
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED,
> &value);
> +		break;
> +	case AST2600_I2CS_STOP:
> +		cmd = 0;
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		break;
> +	default:
> +		dev_dbg(i2c_bus->dev, "unhandled target isr case %x, sts %x\n", sts,
> +			readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
> +		break;
> +	}
> +
> +	if (cmd)
> +		writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
> +	writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base +
> AST2600_I2CS_ISR);
> +	readl(i2c_bus->reg_base + AST2600_I2CS_ISR); }
> +
> +static void ast2600_i2c_target_packet_buff_irq(struct ast2600_i2c_bus
> +*i2c_bus, u32 sts) {
> +	int target_rx_len = 0;
> +	u32 cmd = 0;
> +	u8 value;
> +	int i;
> +
> +	/* due to controller target is common buffer, need force the master stop
> not issue */
> +	if (readl(i2c_bus->reg_base + AST2600_I2CM_CMD_STS) & GENMASK(15,
> 0)) {
> +		writel(0, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);
> +		i2c_bus->cmd_err = -EBUSY;
> +		writel(0, i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		complete(&i2c_bus->cmd_complete);
> +	}
> +
> +	/* Handle i2c target timeout condition */
> +	if (AST2600_I2CS_INACTIVE_TO & sts) {
> +		/* Reset timeout counter */
> +		u32 ac_timing = readl(i2c_bus->reg_base +
> AST2600_I2CC_AC_TIMING) &
> +				AST2600_I2CC_AC_TIMING_MASK;
> +
> +		writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
> +		ac_timing |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);
> +		writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);
> +		writel(TARGET_TRIGGER_CMD, i2c_bus->reg_base +
> AST2600_I2CS_CMD_STS);
> +		writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base +
> AST2600_I2CS_ISR);
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		i2c_bus->target_operate = 0;
> +		return;
> +	}
> +
> +	sts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR);
> +
> +	if (sts & AST2600_I2CS_SLAVE_MATCH)
> +		i2c_bus->target_operate = 1;
> +
> +	switch (sts) {
> +	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
> +		 AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_STOP:
> +	case AST2600_I2CS_SLAVE_PENDING |
> +		 AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_STOP:
> +	case AST2600_I2CS_SLAVE_PENDING |
> +		 AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_STOP:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		fallthrough;
> +	case AST2600_I2CS_SLAVE_PENDING |
> +		 AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH |
> AST2600_I2CS_RX_DONE:
> +	case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH |
> AST2600_I2CS_RX_DONE:
> +	case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED,
> &value);
> +		cmd = TARGET_TRIGGER_CMD;
> +		if (sts & AST2600_I2CS_RX_DONE) {
> +			target_rx_len =
> AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> +							       AST2600_I2CC_BUFF_CTRL));
> +			for (i = 0; i < target_rx_len; i++) {
> +				value = readb(i2c_bus->buf_base + 0x10 + i);
> +				i2c_slave_event(i2c_bus->target,
> I2C_SLAVE_WRITE_RECEIVED, &value);
> +			}
> +		}
> +		if (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) &
> AST2600_I2CS_RX_BUFF_EN)
> +			cmd = 0;
> +		else
> +			cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
> +
> +		writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
> +		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		break;
> +	case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_RX_DONE:
> +		cmd = TARGET_TRIGGER_CMD;
> +		target_rx_len =
> AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> +						       AST2600_I2CC_BUFF_CTRL));
> +		for (i = 0; i < target_rx_len; i++) {
> +			value = readb(i2c_bus->buf_base + 0x10 + i);
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> &value);
> +		}
> +		cmd |= AST2600_I2CS_RX_BUFF_EN;
> +		writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
> +		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		break;
> +	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
> +				AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
> +		cmd = TARGET_TRIGGER_CMD;
> +		target_rx_len =
> AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> +								 AST2600_I2CC_BUFF_CTRL));
> +		for (i = 0; i < target_rx_len; i++) {
> +			value = readb(i2c_bus->buf_base + 0x10 + i);
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> &value);
> +		}
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		cmd |= AST2600_I2CS_RX_BUFF_EN;
> +		writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
> +		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		break;
> +	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_STOP:
> +		cmd = TARGET_TRIGGER_CMD;
> +		target_rx_len =
> AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> +								 AST2600_I2CC_BUFF_CTRL));
> +		for (i = 0; i < target_rx_len; i++) {
> +			value = readb(i2c_bus->buf_base + 0x10 + i);
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> &value);
> +		}
> +		/* workaround for avoid next start with len != 0 */
> +		writel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		break;
> +	case AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:
> +		cmd = TARGET_TRIGGER_CMD;
> +		target_rx_len =
> AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> +								 AST2600_I2CC_BUFF_CTRL));
> +		for (i = 0; i < target_rx_len; i++) {
> +			value = readb(i2c_bus->buf_base + 0x10 + i);
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> &value);
> +		}
> +		/* workaround for avoid next start with len != 0 */
> +		writel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		break;
> +	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE |
> +	     AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_STOP:
> +		target_rx_len =
> AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> +								 AST2600_I2CC_BUFF_CTRL));
> +		for (i = 0; i < target_rx_len; i++) {
> +			value = readb(i2c_bus->buf_base + 0x10 + i);
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> &value);
> +		}
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED,
> &value);
> +		writeb(value, i2c_bus->buf_base);
> +		break;
> +	case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_SLAVE_MATCH:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED,
> &value);
> +		writeb(value, i2c_bus->buf_base);
> +		writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
> +		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
> +		break;
> +	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_STOP |
> +	     AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH |
> AST2600_I2CS_RX_DONE:
> +	case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |
> AST2600_I2CS_STOP |
> +	     AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH |
> AST2600_I2CS_RX_DONE:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED,
> &value);
> +		target_rx_len =
> AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> +						AST2600_I2CC_BUFF_CTRL));
> +		for (i = 0; i < target_rx_len; i++) {
> +			value = readb(i2c_bus->buf_base + 0x10 + i);
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> &value);
> +		}
> +		writel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),
> +		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
> +		break;
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA |
> AST2600_I2CS_RX_DONE:
> +	case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:
> +	case AST2600_I2CS_WAIT_TX_DMA:
> +		if (sts & AST2600_I2CS_SLAVE_MATCH)
> +			i2c_slave_event(i2c_bus->target,
> I2C_SLAVE_WRITE_REQUESTED, &value);
> +
> +		if (sts & AST2600_I2CS_RX_DONE) {
> +			target_rx_len =
> AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> +							AST2600_I2CC_BUFF_CTRL));
> +			for (i = 0; i < target_rx_len; i++) {
> +				value = readb(i2c_bus->buf_base + 0x10 + i);
> +				i2c_slave_event(i2c_bus->target,
> I2C_SLAVE_WRITE_RECEIVED, &value);
> +			}
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED,
> &value);
> +		} else {
> +			i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED,
> &value);
> +		}
> +		writeb(value, i2c_bus->buf_base);
> +		writel(AST2600_I2CC_SET_TX_BUF_LEN(1),
> +		       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;
> +		break;
> +	/* workaround : trigger the cmd twice to fix next state keep 1000000 */
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED,
> &value);
> +		cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;
> +		writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
> +		break;
> +
> +	case AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP:
> +	case AST2600_I2CS_STOP:
> +		cmd = TARGET_TRIGGER_CMD;
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		break;
> +	default:
> +		dev_dbg(i2c_bus->dev, "unhandled target isr case %x, sts %x\n", sts,
> +			readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));
> +		break;
> +	}
> +
> +	if (cmd)
> +		writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
> +	writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base +
> AST2600_I2CS_ISR);
> +	readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
> +
> +	if ((sts & AST2600_I2CS_STOP) && !(sts &
> AST2600_I2CS_SLAVE_PENDING))
> +		i2c_bus->target_operate = 0;
> +}
> +
> +static void ast2600_i2c_target_byte_irq(struct ast2600_i2c_bus
> +*i2c_bus, u32 sts) {
> +	u32 i2c_buff = readl(i2c_bus->reg_base +
> AST2600_I2CC_STS_AND_BUFF);
> +	u32 cmd = AST2600_I2CS_ACTIVE_ALL;
> +	u8 byte_data;
> +	u8 value;
> +
> +	switch (sts) {
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_WAIT_RX_DMA:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED,
> &value);
> +		/* first address match is address */
> +		byte_data = AST2600_I2CC_GET_RX_BUFF(i2c_buff);
> +		break;
> +	case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA:
> +		byte_data = AST2600_I2CC_GET_RX_BUFF(i2c_buff);
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED,
> &byte_data);
> +		break;
> +	case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE |
> AST2600_I2CS_WAIT_TX_DMA:
> +		cmd |= AST2600_I2CS_TX_CMD;
> +		byte_data = AST2600_I2CC_GET_RX_BUFF(i2c_buff);
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED,
> &byte_data);
> +		writel(byte_data, i2c_bus->reg_base +
> AST2600_I2CC_STS_AND_BUFF);
> +		break;
> +	case AST2600_I2CS_TX_ACK | AST2600_I2CS_WAIT_TX_DMA:
> +		cmd |= AST2600_I2CS_TX_CMD;
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED,
> &byte_data);
> +		writel(byte_data, i2c_bus->reg_base +
> AST2600_I2CC_STS_AND_BUFF);
> +		break;
> +	case AST2600_I2CS_STOP:
> +	case AST2600_I2CS_STOP | AST2600_I2CS_TX_NAK:
> +		i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);
> +		break;
> +	default:
> +		dev_dbg(i2c_bus->dev, "unhandled pkt isr %x\n", sts);
> +		break;
> +	}
> +	writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
> +	writel(sts, i2c_bus->reg_base + AST2600_I2CS_ISR);
> +	readl(i2c_bus->reg_base + AST2600_I2CS_ISR); }
> +
> +static int ast2600_i2c_target_irq(struct ast2600_i2c_bus *i2c_bus) {
> +	u32 ier = readl(i2c_bus->reg_base + AST2600_I2CS_IER);
> +	u32 isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR);
> +
> +	if (!(isr & ier))
> +		return 0;
> +
> +	/*
> +	 * Target interrupt coming after Master package done
> +	 * So need handle master first.
> +	 */
> +	if (readl(i2c_bus->reg_base + AST2600_I2CM_ISR) &
> AST2600_I2CM_PKT_DONE)
> +		return 0;
> +
> +	isr &= ~(AST2600_I2CS_ADDR_INDICATE_MASK);
> +
> +	if (AST2600_I2CS_ADDR1_NAK & isr)
> +		isr &= ~AST2600_I2CS_ADDR1_NAK;
> +
> +	if (AST2600_I2CS_ADDR2_NAK & isr)
> +		isr &= ~AST2600_I2CS_ADDR2_NAK;
> +
> +	if (AST2600_I2CS_ADDR3_NAK & isr)
> +		isr &= ~AST2600_I2CS_ADDR3_NAK;
> +
> +	if (AST2600_I2CS_ADDR_MASK & isr)
> +		isr &= ~AST2600_I2CS_ADDR_MASK;
> +
> +	if (AST2600_I2CS_PKT_DONE & isr) {
> +		if (i2c_bus->mode == DMA_MODE)
> +			ast2600_i2c_target_packet_dma_irq(i2c_bus, isr);
> +		else
> +			ast2600_i2c_target_packet_buff_irq(i2c_bus, isr);
> +	} else {
> +		ast2600_i2c_target_byte_irq(i2c_bus, isr);
> +	}
> +
> +	return 1;
> +}
> +#endif
> +
>  static int ast2600_i2c_setup_dma_tx(u32 cmd, struct ast2600_i2c_bus
> *i2c_bus)  {
>  	struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
> @@ -690,6 +1115,20 @@ static void
> ast2600_i2c_controller_package_irq(struct ast2600_i2c_bus *i2c_bus,
>  		}
>  		break;
>  	case AST2600_I2CM_RX_DONE:
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +		/*
> +		 * Workaround for controller/target package mode enable rx done
> stuck issue
> +		 * When master go for first read (RX_DONE), target mode will also
> effect
> +		 * Then controller will send nack, not operate anymore.
> +		 */
> +		if (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) &
> AST2600_I2CS_PKT_MODE_EN) {
> +			u32 target_cmd = readl(i2c_bus->reg_base +
> AST2600_I2CS_CMD_STS);
> +
> +			writel(0, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
> +			writel(target_cmd, i2c_bus->reg_base +
> AST2600_I2CS_CMD_STS);
> +		}
> +		fallthrough;
> +#endif
>  	case AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:
>  		/* do next rx */
>  		if (i2c_bus->mode == DMA_MODE) {
> @@ -799,6 +1238,12 @@ static irqreturn_t ast2600_i2c_bus_irq(int irq, void
> *dev_id)  {
>  	struct ast2600_i2c_bus *i2c_bus = dev_id;
> 
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +	if (readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL) &
> AST2600_I2CC_SLAVE_EN) {
> +		if (ast2600_i2c_target_irq(i2c_bus))
> +			return IRQ_HANDLED;
> +	}
> +#endif
>  	return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));
>  }
> 
> @@ -815,12 +1260,30 @@ static int ast2600_i2c_controller_xfer(struct
> i2c_adapter *adap, struct i2c_msg
>  			return ret;
>  	}
> 
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +	if (i2c_bus->mode == BUFF_MODE) {
> +		if (i2c_bus->target_operate)
> +			return -EBUSY;
> +		/* disable target isr */
> +		writel(0, i2c_bus->reg_base + AST2600_I2CS_IER);
> +		if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR) ||
> i2c_bus->target_operate) {
> +			writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base +
> AST2600_I2CS_IER);
> +			return -EBUSY;
> +		}
> +	}
> +#endif
> +
>  	i2c_bus->cmd_err = 0;
>  	i2c_bus->msgs = msgs;
>  	i2c_bus->msgs_index = 0;
>  	i2c_bus->msgs_count = num;
>  	reinit_completion(&i2c_bus->cmd_complete);
>  	ret = ast2600_i2c_do_start(i2c_bus);
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +	/* avoid race condication target is wait and master wait 1st target operate
> */
> +	if (i2c_bus->mode == BUFF_MODE)
> +		writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base +
> AST2600_I2CS_IER);
> +#endif
>  	if (ret)
>  		goto controller_out;
>  	timeout = wait_for_completion_timeout(&i2c_bus->cmd_complete,
> i2c_bus->adap.timeout); @@ -837,7 +1300,26 @@ static int
> ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg
>  		    (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &
>  		    AST2600_I2CC_BUS_BUSY_STS))
>  			ast2600_i2c_recover_bus(i2c_bus);
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +		if (ctrl & AST2600_I2CC_SLAVE_EN) {
> +			u32 cmd = TARGET_TRIGGER_CMD;
> 
> +			if (i2c_bus->mode == DMA_MODE) {
> +				cmd |= AST2600_I2CS_RX_DMA_EN;
> +				writel(i2c_bus->target_dma_addr,
> +				       i2c_bus->reg_base + AST2600_I2CS_RX_DMA);
> +				writel(i2c_bus->target_dma_addr,
> +				       i2c_bus->reg_base + AST2600_I2CS_TX_DMA);
> +
> 	writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
> +				       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +			} else if (i2c_bus->mode == BUFF_MODE) {
> +				cmd = TARGET_TRIGGER_CMD;
> +			} else {
> +				cmd &= ~AST2600_I2CS_PKT_MODE_EN;
> +			}
> +			writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
> +		}
> +#endif
>  		ret = -ETIMEDOUT;
>  	} else {
>  		ret = i2c_bus->cmd_err;
> @@ -886,7 +1368,78 @@ static void ast2600_i2c_init(struct ast2600_i2c_bus
> *i2c_bus)
> 
>  	/* Clear Interrupt */
>  	writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +	/* for memory buffer initial */
> +	if (i2c_bus->mode == DMA_MODE) {
> +		i2c_bus->target_dma_buf =
> +			dmam_alloc_coherent(i2c_bus->dev,
> I2C_TARGET_MSG_BUF_SIZE,
> +					    &i2c_bus->target_dma_addr, GFP_KERNEL);
> +		if (!i2c_bus->target_dma_buf)
> +			return;
> +	}
> +
> +	writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CS_ISR);
> +
> +	if (i2c_bus->mode == BYTE_MODE)
> +		writel(GENMASK(15, 0), i2c_bus->reg_base + AST2600_I2CS_IER);
> +	else
> +		writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base +
> AST2600_I2CS_IER);
> +#endif }
> +
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +static int ast2600_i2c_reg_target(struct i2c_client *client) {
> +	struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);
> +	u32 cmd = TARGET_TRIGGER_CMD;
> +
> +	if (i2c_bus->target)
> +		return -EINVAL;
> +
> +	dev_dbg(i2c_bus->dev, "target addr %x\n", client->addr);
> +
> +	writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
> +	writel(AST2600_I2CC_SLAVE_EN | readl(i2c_bus->reg_base +
> AST2600_I2CC_FUN_CTRL),
> +	       i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> +
> +	/* trigger rx buffer */
> +	if (i2c_bus->mode == DMA_MODE) {
> +		cmd |= AST2600_I2CS_RX_DMA_EN;
> +		writel(i2c_bus->target_dma_addr, i2c_bus->reg_base +
> AST2600_I2CS_RX_DMA);
> +		writel(i2c_bus->target_dma_addr, i2c_bus->reg_base +
> AST2600_I2CS_TX_DMA);
> +
> 	writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE),
> +		       i2c_bus->reg_base + AST2600_I2CS_DMA_LEN);
> +	} else if (i2c_bus->mode == BUFF_MODE) {
> +		cmd = TARGET_TRIGGER_CMD;
> +	} else {
> +		cmd &= ~AST2600_I2CS_PKT_MODE_EN;
> +	}
> +
> +	writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);
> +	i2c_bus->target = client;
> +	/* Set target addr. */
> +	writel(client->addr | AST2600_I2CS_ADDR1_ENABLE,
> +	       i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
> +
> +	return 0;
> +}
> +
> +static int ast2600_i2c_unreg_target(struct i2c_client *client) {
> +	struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);
> +
> +	/* Turn off target mode. */
> +	writel(~AST2600_I2CC_SLAVE_EN & readl(i2c_bus->reg_base +
> AST2600_I2CC_FUN_CTRL),
> +	       i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> +	writel(readl(i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL) &
> ~AST2600_I2CS_ADDR1_MASK,
> +	       i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
> +
> +	i2c_bus->target = NULL;
> +
> +	return 0;
>  }
> +#endif
> 
>  static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)  { @@ -896,6
> +1449,10 @@ static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)
> static const struct i2c_algorithm i2c_ast2600_algorithm = {
>  	.xfer = ast2600_i2c_controller_xfer,
>  	.functionality = ast2600_i2c_functionality,
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +	.reg_target = ast2600_i2c_reg_target,
> +	.unreg_target = ast2600_i2c_unreg_target, #endif
>  };
> 
>  static int ast2600_i2c_probe(struct platform_device *pdev) @@ -930,6
> +1487,9 @@ static int ast2600_i2c_probe(struct platform_device *pdev)
>  		regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL,
> I2CCG_DIV_CTRL);
>  	}
> 
> +#if IS_ENABLED(CONFIG_I2C_SLAVE)
> +	i2c_bus->target_operate = 0;
> +#endif
>  	i2c_bus->dev = dev;
>  	i2c_bus->mode = BUFF_MODE;
>  	if (!device_property_read_string(dev, "aspeed,transfer-mode",
> &xfer_mode)) {
> --
> 2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ