From nobody Sat Oct 4 06:37:49 2025 Received: from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F3972286411; Wed, 20 Aug 2025 05:18:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755667132; cv=none; b=eOjussur/0g5PL1OOVtiHql5HAmF8Kp3tvpfdknvMctngFJm3OPG1+ux9XxEwBLmUHIS65nFMprpNH78+R/GjuMXn1ttWNYuIHdBrqPc4RBdlDUQCs4dchynZkAqivYxOegA6ntScXgJD6mupjNz5uxrBix/V/CwQgAYmA6a7rM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755667132; c=relaxed/simple; bh=MMCuDlj23UhgNm8+uHMPeKLC+gUYSjEbARwIpB6wSn8=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=G0Dq4a+mxuFWKxFsxGGaSe+06JaDhio16jQZ5o8Oyvks2AZgiKUz7edNvYE0Ssk/5zzZCYdjNjUsTr8uhcIXVe+EOIJgefDh5wrM2Kfv6sOuU4N6pEM/cJgm4FKU1W1o+W8lzxd8MSsjC8R7cMuxJLlPRfzi27l0ogeg9un9Q7c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 20 Aug 2025 13:18:33 +0800 Received: from twmbx02.aspeed.com (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Wed, 20 Aug 2025 13:18:33 +0800 From: Ryan Chen To: , , , , , , , , , , , , , , , , Subject: [PATCH v18 3/3] i2c: ast2600: Add target mode support Date: Wed, 20 Aug 2025 13:18:32 +0800 Message-ID: <20250820051832.3605405-4-ryan_chen@aspeedtech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250820051832.3605405-1-ryan_chen@aspeedtech.com> References: <20250820051832.3605405-1-ryan_chen@aspeedtech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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-ast2= 600.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) =20 /* 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 }; =20 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; } =20 +#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 =3D 0; + u32 cmd =3D 0; + u8 value; + int i; + + sts &=3D ~(AST2600_I2CS_SLAVE_PENDING); + /* Handle i2c target timeout condition */ + if (AST2600_I2CS_INACTIVE_TO & sts) { + cmd =3D TARGET_TRIGGER_CMD; + cmd |=3D 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 &=3D ~(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 =3D AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + + AST2600_I2CS_DMA_LEN_STS)); + for (i =3D 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 =3D 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 =3D 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 =3D AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + + AST2600_I2CS_DMA_LEN_STS)); + for (i =3D 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 =3D 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 =3D AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + + AST2600_I2CS_DMA_LEN_STS)); + for (i =3D 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 =3D 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 =3D 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 =3D 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 =3D TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; + break; + case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE: + cmd =3D 0; + i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); + break; + case AST2600_I2CS_STOP: + cmd =3D 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 =3D 0; + u32 cmd =3D 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 =3D -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 =3D 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 |=3D 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 =3D 0; + return; + } + + sts &=3D ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR); + + if (sts & AST2600_I2CS_SLAVE_MATCH) + i2c_bus->target_operate =3D 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_D= ONE: + case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_R= X_DONE: + case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH: + i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); + cmd =3D TARGET_TRIGGER_CMD; + if (sts & AST2600_I2CS_RX_DONE) { + target_rx_len =3D AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); + for (i =3D 0; i < target_rx_len; i++) { + value =3D 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_BU= FF_EN) + cmd =3D 0; + else + cmd =3D 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 =3D TARGET_TRIGGER_CMD; + target_rx_len =3D AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); + for (i =3D 0; i < target_rx_len; i++) { + value =3D readb(i2c_bus->buf_base + 0x10 + i); + i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); + } + cmd |=3D 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 =3D TARGET_TRIGGER_CMD; + target_rx_len =3D AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); + for (i =3D 0; i < target_rx_len; i++) { + value =3D 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 |=3D 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_STO= P: + cmd =3D TARGET_TRIGGER_CMD; + target_rx_len =3D AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); + for (i =3D 0; i < target_rx_len; i++) { + value =3D 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 !=3D 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 =3D TARGET_TRIGGER_CMD; + target_rx_len =3D AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); + for (i =3D 0; i < target_rx_len; i++) { + value =3D 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 !=3D 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 =3D AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); + for (i =3D 0; i < target_rx_len; i++) { + value =3D 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 =3D 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_DON= E: + case AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS= _STOP | + AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DON= E: + i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); + i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); + target_rx_len =3D AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); + for (i =3D 0; i < target_rx_len; i++) { + value =3D 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 =3D TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN; + break; + case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_R= X_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 =3D AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + + AST2600_I2CC_BUFF_CTRL)); + for (i =3D 0; i < target_rx_len; i++) { + value =3D 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 =3D 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 =3D 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 =3D 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 =3D 0; +} + +static void ast2600_i2c_target_byte_irq(struct ast2600_i2c_bus *i2c_bus, u= 32 sts) +{ + u32 i2c_buff =3D readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF); + u32 cmd =3D 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 =3D AST2600_I2CC_GET_RX_BUFF(i2c_buff); + break; + case AST2600_I2CS_RX_DONE | AST2600_I2CS_WAIT_RX_DMA: + byte_data =3D 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 |=3D AST2600_I2CS_TX_CMD; + byte_data =3D 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 |=3D 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 =3D readl(i2c_bus->reg_base + AST2600_I2CS_IER); + u32 isr =3D 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 &=3D ~(AST2600_I2CS_ADDR_INDICATE_MASK); + + if (AST2600_I2CS_ADDR1_NAK & isr) + isr &=3D ~AST2600_I2CS_ADDR1_NAK; + + if (AST2600_I2CS_ADDR2_NAK & isr) + isr &=3D ~AST2600_I2CS_ADDR2_NAK; + + if (AST2600_I2CS_ADDR3_NAK & isr) + isr &=3D ~AST2600_I2CS_ADDR3_NAK; + + if (AST2600_I2CS_ADDR_MASK & isr) + isr &=3D ~AST2600_I2CS_ADDR_MASK; + + if (AST2600_I2CS_PKT_DONE & isr) { + if (i2c_bus->mode =3D=3D 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_b= us) { struct i2c_msg *msg =3D &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 is= sue + * 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_M= ODE_EN) { + u32 target_cmd =3D 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 =3D=3D DMA_MODE) { @@ -799,6 +1238,12 @@ static irqreturn_t ast2600_i2c_bus_irq(int irq, void = *dev_id) { struct ast2600_i2c_bus *i2c_bus =3D dev_id; =20 +#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)); } =20 @@ -815,12 +1260,30 @@ static int ast2600_i2c_controller_xfer(struct i2c_ad= apter *adap, struct i2c_msg return ret; } =20 +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (i2c_bus->mode =3D=3D 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_opera= te) { + writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER); + return -EBUSY; + } + } +#endif + i2c_bus->cmd_err =3D 0; i2c_bus->msgs =3D msgs; i2c_bus->msgs_index =3D 0; i2c_bus->msgs_count =3D num; reinit_completion(&i2c_bus->cmd_complete); ret =3D ast2600_i2c_do_start(i2c_bus); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* avoid race condication target is wait and master wait 1st target opera= te */ + if (i2c_bus->mode =3D=3D BUFF_MODE) + writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER); +#endif if (ret) goto controller_out; timeout =3D wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->= adap.timeout); @@ -837,7 +1300,26 @@ static int ast2600_i2c_controller_xfer(struct i2c_ada= pter *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 =3D TARGET_TRIGGER_CMD; =20 + if (i2c_bus->mode =3D=3D DMA_MODE) { + cmd |=3D 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 =3D=3D BUFF_MODE) { + cmd =3D TARGET_TRIGGER_CMD; + } else { + cmd &=3D ~AST2600_I2CS_PKT_MODE_EN; + } + writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS); + } +#endif ret =3D -ETIMEDOUT; } else { ret =3D i2c_bus->cmd_err; @@ -886,7 +1368,78 @@ static void ast2600_i2c_init(struct ast2600_i2c_bus *= i2c_bus) =20 /* 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 =3D=3D DMA_MODE) { + i2c_bus->target_dma_buf =3D + 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 =3D=3D 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 =3D i2c_get_adapdata(client->adapter); + u32 cmd =3D 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 =3D=3D DMA_MODE) { + cmd |=3D 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 =3D=3D BUFF_MODE) { + cmd =3D TARGET_TRIGGER_CMD; + } else { + cmd &=3D ~AST2600_I2CS_PKT_MODE_EN; + } + + writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS); + i2c_bus->target =3D 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 =3D i2c_get_adapdata(client->adapter); + + /* Turn off target mode. */ + writel(~AST2600_I2CC_SLAVE_EN & readl(i2c_bus->reg_base + AST2600_I2CC_FU= N_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 =3D NULL; + + return 0; } +#endif =20 static u32 ast2600_i2c_functionality(struct i2c_adapter *adap) { @@ -896,6 +1449,10 @@ static u32 ast2600_i2c_functionality(struct i2c_adapt= er *adap) static const struct i2c_algorithm i2c_ast2600_algorithm =3D { .xfer =3D ast2600_i2c_controller_xfer, .functionality =3D ast2600_i2c_functionality, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_target =3D ast2600_i2c_reg_target, + .unreg_target =3D ast2600_i2c_unreg_target, +#endif }; =20 static int ast2600_i2c_probe(struct platform_device *pdev) @@ -930,6 +1487,9 @@ static int ast2600_i2c_probe(struct platform_device *p= dev) regmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_= CTRL); } =20 +#if IS_ENABLED(CONFIG_I2C_SLAVE) + i2c_bus->target_operate =3D 0; +#endif i2c_bus->dev =3D dev; i2c_bus->mode =3D BUFF_MODE; if (!device_property_read_string(dev, "aspeed,transfer-mode", &xfer_mode)= ) { --=20 2.34.1