{"id":2223344,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2223344/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-aspeed/patch/20260415-upstream_i2c-v29-4-317c1a905ae1@aspeedtech.com/","project":{"id":57,"url":"http://patchwork.ozlabs.org/api/1.2/projects/57/?format=json","name":"Linux ASPEED SoC development","link_name":"linux-aspeed","list_id":"linux-aspeed.lists.ozlabs.org","list_email":"linux-aspeed@lists.ozlabs.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20260415-upstream_i2c-v29-4-317c1a905ae1@aspeedtech.com>","list_archive_url":null,"date":"2026-04-15T05:14:05","name":"[v29,4/4] i2c: ast2600: Add target mode support","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"8278d27452c1cb21dfbe282f796fd3aab6b5138a","submitter":{"id":71489,"url":"http://patchwork.ozlabs.org/api/1.2/people/71489/?format=json","name":"Ryan Chen","email":"ryan_chen@aspeedtech.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-aspeed/patch/20260415-upstream_i2c-v29-4-317c1a905ae1@aspeedtech.com/mbox/","series":[{"id":499920,"url":"http://patchwork.ozlabs.org/api/1.2/series/499920/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-aspeed/list/?series=499920","date":"2026-04-15T05:14:01","name":"Add ASPEED AST2600 I2C controller driver","version":29,"mbox":"http://patchwork.ozlabs.org/series/499920/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2223344/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2223344/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <linux-aspeed+bounces-3903-incoming=patchwork.ozlabs.org@lists.ozlabs.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-aspeed@lists.ozlabs.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org\n (client-ip=2404:9400:21b9:f100::1; helo=lists.ozlabs.org;\n envelope-from=linux-aspeed+bounces-3903-incoming=patchwork.ozlabs.org@lists.ozlabs.org;\n receiver=patchwork.ozlabs.org)","lists.ozlabs.org;\n arc=none smtp.remote-ip=211.20.114.72","lists.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com","lists.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=aspeedtech.com\n (client-ip=211.20.114.72; helo=twmbx01.aspeed.com;\n envelope-from=ryan_chen@aspeedtech.com; receiver=lists.ozlabs.org)"],"Received":["from lists.ozlabs.org (lists.ozlabs.org\n [IPv6:2404:9400:21b9:f100::1])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fwTqh6Y74z1yDF\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 15 Apr 2026 15:15:28 +1000 (AEST)","from boromir.ozlabs.org (localhost [127.0.0.1])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 4fwTpb3PG9z300F;\n\tWed, 15 Apr 2026 15:14:31 +1000 (AEST)","from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 4fwTpZ1Y84z304h;\n\tWed, 15 Apr 2026 15:14:30 +1000 (AEST)","from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com\n (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Wed, 15 Apr\n 2026 13:14:03 +0800","from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com\n (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend\n Transport; Wed, 15 Apr 2026 13:14:03 +0800"],"ARC-Seal":"i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1776230071;\n\tcv=none;\n b=D7Mhi9t0ksQqCnz+XKslGzUWQvm4v9kWpL57Gj58o/cVZu7/moUOalCujY3fiCldhPuPL5Pe1cRtgIiVqy2getLTQLYbWBYqtGxSPWUOwjIAyQ1mQIW+cfAX+I3Mitv5Z6yc45Rr7m5s0xlbvIGgn2lQ2sW8ZAb/7hDmMpd+8cEi2pyESzaCh6ndpZ3CWYTOF5iMfAzGEH1xiCSjSu2mIBzlBTK01JZiODo+wkEeCBy8eQZb0T72bu7VyCIOUD5PHzxrtZvEUatmRzP/YC2+cUlPSyWcZE4xSjYY40YoTfOkxSUbvF+mi+lZX6QGBYA8pcz9nhntqPPlYvu2qRePQQ==","ARC-Message-Signature":"i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707;\n\tt=1776230071; c=relaxed/relaxed;\n\tbh=8swGP4fSebuVVwJiC5PHQWhD8prgZ48URqom4QP5BAE=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References:\n\t In-Reply-To:To:CC;\n b=EniR9AmJFr1pUZrHER8rk11xp8CRXwmkiY2uPA5LsxX2Lgum3k5hbV/lKB6mFokZMiPjLDANhg1hekwTiTHBRWbaFtH2CefAe+opuX+YHMqj7A7FBH0HXrGdIdY46tY8gaXGRuAkdW9g/39eN3ZNuiIIwzEJisqTWiXPU0OWGur3lyZShz32qvjLPQWiCp2f8v6n0NvOGyoVVvropOP7X+sD4hKHI8yOx68VfTNxAF+hTk+GpSR/ydRe9CZzKNM3rj4Sf9Xxi5yd0PO8vytm0SZ2RMjDeUmU9jhy6lBkVP8fxviNaIVWYDWO3Klx6u6XkquytXjBVI5W69j0GFeBoA==","ARC-Authentication-Results":"i=1; lists.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com;\n spf=pass (client-ip=211.20.114.72; helo=twmbx01.aspeed.com;\n envelope-from=ryan_chen@aspeedtech.com;\n receiver=lists.ozlabs.org) smtp.mailfrom=aspeedtech.com","From":"Ryan Chen <ryan_chen@aspeedtech.com>","Date":"Wed, 15 Apr 2026 13:14:05 +0800","Subject":"[PATCH v29 4/4] i2c: ast2600: Add target mode support","X-Mailing-List":"linux-aspeed@lists.ozlabs.org","List-Id":"<linux-aspeed.lists.ozlabs.org>","List-Help":"<mailto:linux-aspeed+help@lists.ozlabs.org>","List-Owner":"<mailto:linux-aspeed+owner@lists.ozlabs.org>","List-Post":"<mailto:linux-aspeed@lists.ozlabs.org>","List-Archive":"<https://lore.kernel.org/linux-aspeed/>,\n  <https://lists.ozlabs.org/pipermail/linux-aspeed/>","List-Subscribe":"<mailto:linux-aspeed+subscribe@lists.ozlabs.org>,\n  <mailto:linux-aspeed+subscribe-digest@lists.ozlabs.org>,\n  <mailto:linux-aspeed+subscribe-nomail@lists.ozlabs.org>","List-Unsubscribe":"<mailto:linux-aspeed+unsubscribe@lists.ozlabs.org>","Precedence":"list","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-ID":"<20260415-upstream_i2c-v29-4-317c1a905ae1@aspeedtech.com>","References":"<20260415-upstream_i2c-v29-0-317c1a905ae1@aspeedtech.com>","In-Reply-To":"<20260415-upstream_i2c-v29-0-317c1a905ae1@aspeedtech.com>","To":"<jk@codeconstruct.com.au>, <andriy.shevchenko@linux.intel.com>, Andi Shyti\n\t<andi.shyti@kernel.org>, Rob Herring <robh@kernel.org>, Krzysztof Kozlowski\n\t<krzk+dt@kernel.org>, Conor Dooley <conor+dt@kernel.org>, Joel Stanley\n\t<joel@jms.id.au>, Andrew Jeffery <andrew@codeconstruct.com.au>, \"Benjamin\n Herrenschmidt\" <benh@kernel.crashing.org>, Rayn Chen\n\t<rayn_chen@aspeedtech.com>, Philipp Zabel <p.zabel@pengutronix.de>","CC":"<linux-i2c@vger.kernel.org>, <devicetree@vger.kernel.org>,\n\t<linux-arm-kernel@lists.infradead.org>, <linux-aspeed@lists.ozlabs.org>,\n\t<linux-kernel@vger.kernel.org>, <openbmc@lists.ozlabs.org>, Ryan Chen\n\t<ryan_chen@aspeedtech.com>","X-Mailer":"b4 0.14.3","X-Developer-Signature":"v=1; a=ed25519-sha256; t=1776230042; l=17308;\n i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id;\n bh=7gdQPuqUllpQHg8Qb2lTDDNbP8FnNu6CN80JWiz5OB0=;\n b=H5H0qg2uPx6rsvoMysor0xlXGslABVvO7mQFk6VW3v0T2aMxKeu503voehoA+5XVDHlxC3sQ5\n /J+M4Jj6g+BBFohwCwb2PmcsFBAq+PMBJbWu3Rh7KmKLplBehFB7Fau","X-Developer-Key":"i=ryan_chen@aspeedtech.com; a=ed25519;\n pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc=","X-Spam-Status":"No, score=0.0 required=5.0 tests=SPF_HELO_FAIL,SPF_PASS\n\tautolearn=disabled version=4.0.1","X-Spam-Checker-Version":"SpamAssassin 4.0.1 (2024-03-25) on lists.ozlabs.org"},"content":"Add target mode support to the AST2600 I2C driver.\n\nTarget mode features implemented include:\n- Add target interrupt handling\n- Address match and response logic\n\nThis complements the existing controller-mode support, enabling\ndual-role capability.\n\nSigned-off-by: Ryan Chen <ryan_chen@aspeedtech.com>\n---\nChanges in v29:\n- fix race between unreg_target and IRQ handler.\n- move i2cs ier enable from ast2600_i2c_init to probe after master ier enable.\n- remove dma/byte transfer, use buffer mode only.\n\nChanges in v28:\n- fix typo condication -> condition\n- fix compile error, when disable CONFIG_I2C_SLAVE\n\nChanges in v26:\n- change int to bool target_operate\n- rename target_operate to target_active\n- use i2c_bus->target replace require IO\n- use WRITE_ONCE replace target_operate write.\n---\n drivers/i2c/busses/i2c-ast2600.c | 340 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 340 insertions(+)","diff":"diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c\nindex 787ef6bd6244..137aaf7e26b5 100644\n--- a/drivers/i2c/busses/i2c-ast2600.c\n+++ b/drivers/i2c/busses/i2c-ast2600.c\n@@ -254,6 +254,11 @@ struct ast2600_i2c_bus {\n \tsize_t\t\t\tbuf_size;\n \tbool\t\t\tmulti_master;\n \tvoid __iomem\t\t*buf_base;\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\t/* target structure */\n+\tbool\t\t\ttarget_active;\n+\tstruct i2c_client\t*target;\n+#endif\n };\n \n static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus)\n@@ -337,6 +342,243 @@ static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)\n \treturn ret;\n }\n \n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+static void ast2600_i2c_target_packet_buff_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)\n+{\n+\tint target_rx_len = 0;\n+\tu32 cmd = 0;\n+\tu8 value;\n+\tint i;\n+\n+\t/* due to controller target is common buffer, need force the master stop not issue */\n+\tif (readl(i2c_bus->reg_base + AST2600_I2CM_CMD_STS) & GENMASK(15, 0)) {\n+\t\twritel(0, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);\n+\t\ti2c_bus->cmd_err = -EBUSY;\n+\t\twritel(0, i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\tcomplete(&i2c_bus->cmd_complete);\n+\t}\n+\n+\t/* Handle i2c target timeout condition */\n+\tif (AST2600_I2CS_INACTIVE_TO & sts) {\n+\t\t/* Reset timeout counter */\n+\t\tu32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) &\n+\t\t\t\tAST2600_I2CC_AC_TIMING_MASK;\n+\n+\t\twritel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);\n+\t\tac_timing |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);\n+\t\twritel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);\n+\t\twritel(TARGET_TRIGGER_CMD, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);\n+\t\twritel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);\n+\t\tWRITE_ONCE(i2c_bus->target_active, false);\n+\t\treturn;\n+\t}\n+\n+\tsts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR);\n+\n+\tif (sts & AST2600_I2CS_SLAVE_MATCH)\n+\t\tWRITE_ONCE(i2c_bus->target_active, true);\n+\n+\tswitch (sts) {\n+\tcase AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |\n+\t\t AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:\n+\tcase AST2600_I2CS_SLAVE_PENDING |\n+\t\t AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:\n+\tcase AST2600_I2CS_SLAVE_PENDING |\n+\t\t AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_STOP:\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);\n+\t\tfallthrough;\n+\tcase AST2600_I2CS_SLAVE_PENDING |\n+\t\t AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:\n+\tcase AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:\n+\tcase AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH:\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);\n+\t\tcmd = TARGET_TRIGGER_CMD;\n+\t\tif (sts & AST2600_I2CS_RX_DONE) {\n+\t\t\ttarget_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t       AST2600_I2CC_BUFF_CTRL));\n+\t\t\tfor (i = 0; i < target_rx_len; i++) {\n+\t\t\t\tvalue = readb(i2c_bus->buf_base + 0x10 + i);\n+\t\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);\n+\t\t\t}\n+\t\t}\n+\t\tif (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) & AST2600_I2CS_RX_BUFF_EN)\n+\t\t\tcmd = 0;\n+\t\telse\n+\t\t\tcmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;\n+\n+\t\twritel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),\n+\t\t       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\tbreak;\n+\tcase AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_RX_DONE:\n+\t\tcmd = TARGET_TRIGGER_CMD;\n+\t\ttarget_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t       AST2600_I2CC_BUFF_CTRL));\n+\t\tfor (i = 0; i < target_rx_len; i++) {\n+\t\t\tvalue = readb(i2c_bus->buf_base + 0x10 + i);\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);\n+\t\t}\n+\t\tcmd |= AST2600_I2CS_RX_BUFF_EN;\n+\t\twritel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),\n+\t\t       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\tbreak;\n+\tcase AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA |\n+\t\t\t\tAST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:\n+\t\tcmd = TARGET_TRIGGER_CMD;\n+\t\ttarget_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t\t AST2600_I2CC_BUFF_CTRL));\n+\t\tfor (i = 0; i < target_rx_len; i++) {\n+\t\t\tvalue = readb(i2c_bus->buf_base + 0x10 + i);\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);\n+\t\t}\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);\n+\t\tcmd |= AST2600_I2CS_RX_BUFF_EN;\n+\t\twritel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),\n+\t\t       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\tbreak;\n+\tcase AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:\n+\t\tcmd = TARGET_TRIGGER_CMD;\n+\t\ttarget_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t\t AST2600_I2CC_BUFF_CTRL));\n+\t\tfor (i = 0; i < target_rx_len; i++) {\n+\t\t\tvalue = readb(i2c_bus->buf_base + 0x10 + i);\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);\n+\t\t}\n+\t\t/* workaround for avoid next start with len != 0 */\n+\t\twritel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);\n+\t\tbreak;\n+\tcase AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP:\n+\t\tcmd = TARGET_TRIGGER_CMD;\n+\t\ttarget_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t\t AST2600_I2CC_BUFF_CTRL));\n+\t\tfor (i = 0; i < target_rx_len; i++) {\n+\t\t\tvalue = readb(i2c_bus->buf_base + 0x10 + i);\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);\n+\t\t}\n+\t\t/* workaround for avoid next start with len != 0 */\n+\t\twritel(BIT(0), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);\n+\t\tbreak;\n+\tcase AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_RX_DONE |\n+\t     AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_STOP:\n+\t\ttarget_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t\t AST2600_I2CC_BUFF_CTRL));\n+\t\tfor (i = 0; i < target_rx_len; i++) {\n+\t\t\tvalue = readb(i2c_bus->buf_base + 0x10 + i);\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);\n+\t\t}\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);\n+\t\twriteb(value, i2c_bus->buf_base);\n+\t\tbreak;\n+\tcase AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_SLAVE_MATCH:\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);\n+\t\twriteb(value, i2c_bus->buf_base);\n+\t\twritel(AST2600_I2CC_SET_TX_BUF_LEN(1),\n+\t\t       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\tcmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;\n+\t\tbreak;\n+\tcase AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_STOP |\n+\t     AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:\n+\tcase AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_STOP |\n+\t     AST2600_I2CS_TX_NAK | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);\n+\t\ttarget_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\tAST2600_I2CC_BUFF_CTRL));\n+\t\tfor (i = 0; i < target_rx_len; i++) {\n+\t\t\tvalue = readb(i2c_bus->buf_base + 0x10 + i);\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);\n+\t\t}\n+\t\twritel(AST2600_I2CC_SET_RX_BUF_LEN(i2c_bus->buf_size),\n+\t\t       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\tcmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;\n+\t\tbreak;\n+\tcase AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:\n+\tcase AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_RX_DONE:\n+\tcase AST2600_I2CS_WAIT_TX_DMA:\n+\t\tif (sts & AST2600_I2CS_SLAVE_MATCH)\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);\n+\n+\t\tif (sts & AST2600_I2CS_RX_DONE) {\n+\t\t\ttarget_rx_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\tAST2600_I2CC_BUFF_CTRL));\n+\t\t\tfor (i = 0; i < target_rx_len; i++) {\n+\t\t\t\tvalue = readb(i2c_bus->buf_base + 0x10 + i);\n+\t\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value);\n+\t\t\t}\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value);\n+\t\t} else {\n+\t\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED, &value);\n+\t\t}\n+\t\twriteb(value, i2c_bus->buf_base);\n+\t\twritel(AST2600_I2CC_SET_TX_BUF_LEN(1),\n+\t\t       i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t\tcmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_BUFF_EN;\n+\t\tbreak;\n+\t/* workaround : trigger the cmd twice to fix next state keep 1000000 */\n+\tcase AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE:\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value);\n+\t\tcmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_BUFF_EN;\n+\t\twritel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);\n+\t\tbreak;\n+\tcase AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP:\n+\tcase AST2600_I2CS_STOP:\n+\t\tcmd = TARGET_TRIGGER_CMD;\n+\t\ti2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value);\n+\t\tbreak;\n+\tdefault:\n+\t\tdev_dbg(i2c_bus->dev, \"unhandled target isr case %x, sts %x\\n\", sts,\n+\t\t\treadl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));\n+\t\tbreak;\n+\t}\n+\n+\tif (cmd)\n+\t\twritel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);\n+\twritel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_ISR);\n+\treadl(i2c_bus->reg_base + AST2600_I2CS_ISR);\n+\n+\tif ((sts & AST2600_I2CS_STOP) && !(sts & AST2600_I2CS_SLAVE_PENDING))\n+\t\tWRITE_ONCE(i2c_bus->target_active, false);\n+}\n+\n+static int ast2600_i2c_target_irq(struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tu32 ier = readl(i2c_bus->reg_base + AST2600_I2CS_IER);\n+\tu32 isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR);\n+\n+\tif (!(isr & ier))\n+\t\treturn 0;\n+\n+\t/*\n+\t * Target interrupt coming after controller packet done\n+\t * So need handle controller first.\n+\t */\n+\tif (readl(i2c_bus->reg_base + AST2600_I2CM_ISR) & AST2600_I2CM_PKT_DONE)\n+\t\treturn 0;\n+\n+\tisr &= ~(AST2600_I2CS_ADDR_INDICATE_MASK);\n+\n+\tif (AST2600_I2CS_ADDR1_NAK & isr)\n+\t\tisr &= ~AST2600_I2CS_ADDR1_NAK;\n+\n+\tif (AST2600_I2CS_ADDR2_NAK & isr)\n+\t\tisr &= ~AST2600_I2CS_ADDR2_NAK;\n+\n+\tif (AST2600_I2CS_ADDR3_NAK & isr)\n+\t\tisr &= ~AST2600_I2CS_ADDR3_NAK;\n+\n+\tif (AST2600_I2CS_ADDR_MASK & isr)\n+\t\tisr &= ~AST2600_I2CS_ADDR_MASK;\n+\n+\tif (AST2600_I2CS_PKT_DONE & isr)\n+\t\tast2600_i2c_target_packet_buff_irq(i2c_bus, isr);\n+\n+\treturn 1;\n+}\n+#endif\n+\n static int ast2600_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)\n {\n \tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n@@ -498,6 +740,20 @@ static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u\n \t\t}\n \t\tbreak;\n \tcase AST2600_I2CM_RX_DONE:\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\t\t/*\n+\t\t * Workaround for controller/target packet mode enable rx done stuck issue\n+\t\t * When controller go for first read (RX_DONE), target mode will also effect\n+\t\t * Then controller will send nack, not operate anymore.\n+\t\t */\n+\t\tif (readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS) & AST2600_I2CS_PKT_MODE_EN) {\n+\t\t\tu32 target_cmd = readl(i2c_bus->reg_base + AST2600_I2CS_CMD_STS);\n+\n+\t\t\twritel(0, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);\n+\t\t\twritel(target_cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);\n+\t\t}\n+\t\tfallthrough;\n+#endif\n \tcase AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:\n \t\t/* do next rx */\n \t\txfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n@@ -584,6 +840,12 @@ static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id)\n {\n \tstruct ast2600_i2c_bus *i2c_bus = dev_id;\n \n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\tif (i2c_bus->target) {\n+\t\tif (ast2600_i2c_target_irq(i2c_bus))\n+\t\t\treturn IRQ_HANDLED;\n+\t}\n+#endif\n \treturn IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));\n }\n \n@@ -600,12 +862,31 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg\n \t\t\treturn ret;\n \t}\n \n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\tif (i2c_bus->target_active)\n+\t\treturn -EBUSY;\n+\t/*\n+\t * Controller and target share the same buffer register. A target\n+\t * transaction can update buffer state asynchronously via IRQ, so block\n+\t * controller transfers while target is active to avoid buffer corruption.\n+\t */\n+\twritel(0, i2c_bus->reg_base + AST2600_I2CS_IER);\n+\tif (readl(i2c_bus->reg_base + AST2600_I2CS_ISR) || i2c_bus->target_active) {\n+\t\twritel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);\n+\t\treturn -EBUSY;\n+\t}\n+#endif\n+\n \ti2c_bus->cmd_err = 0;\n \ti2c_bus->msgs = msgs;\n \ti2c_bus->msgs_index = 0;\n \ti2c_bus->msgs_count = num;\n \treinit_completion(&i2c_bus->cmd_complete);\n \tret = ast2600_i2c_do_start(i2c_bus);\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\t/* avoid race condition target is wait and controller wait 1st target operate */\n+\twritel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);\n+#endif\n \tif (ret)\n \t\tgoto controller_out;\n \ttimeout = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);\n@@ -624,6 +905,9 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg\n \t\t * if the bus is still busy.\n \t\t */\n \t\tif (i2c_bus->multi_master &&\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\t\t    !i2c_bus->target_active &&\n+#endif\n \t\t    (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &\n \t\t    AST2600_I2CC_BUS_BUSY_STS))\n \t\t\tast2600_i2c_recover_bus(i2c_bus);\n@@ -659,8 +943,54 @@ static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus)\n \t/* Clear Interrupt */\n \twritel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);\n \n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\twritel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CS_ISR);\n+#endif\n+\n+\treturn 0;\n+}\n+\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+static int ast2600_i2c_reg_target(struct i2c_client *client)\n+{\n+\tstruct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);\n+\tu32 cmd = TARGET_TRIGGER_CMD;\n+\n+\tif (i2c_bus->target)\n+\t\treturn -EINVAL;\n+\n+\tdev_dbg(i2c_bus->dev, \"target addr %x\\n\", client->addr);\n+\n+\twritel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);\n+\twritel(AST2600_I2CC_SLAVE_EN | readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL),\n+\t       i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\n+\twritel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS);\n+\ti2c_bus->target = client;\n+\t/* Set target addr. */\n+\twritel(client->addr | AST2600_I2CS_ADDR1_ENABLE,\n+\t       i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);\n+\n+\treturn 0;\n+}\n+\n+static int ast2600_i2c_unreg_target(struct i2c_client *client)\n+{\n+\tstruct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(client->adapter);\n+\tu32 val;\n+\n+\t/* Turn off target mode. */\n+\tval = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\twritel(val & ~AST2600_I2CC_SLAVE_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\tval = readl(i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);\n+\twritel(val & ~AST2600_I2CS_ADDR1_MASK, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);\n+\n+\tsynchronize_irq(i2c_bus->irq);\n+\ti2c_bus->target = NULL;\n+\n \treturn 0;\n }\n+#endif\n \n static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)\n {\n@@ -670,6 +1000,10 @@ static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)\n static const struct i2c_algorithm i2c_ast2600_algorithm = {\n \t.xfer = ast2600_i2c_controller_xfer,\n \t.functionality = ast2600_i2c_functionality,\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\t.reg_target = ast2600_i2c_reg_target,\n+\t.unreg_target = ast2600_i2c_unreg_target,\n+#endif\n };\n \n static int ast2600_i2c_probe(struct platform_device *pdev)\n@@ -708,6 +1042,9 @@ static int ast2600_i2c_probe(struct platform_device *pdev)\n \t\tregmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);\n \t}\n \n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\tWRITE_ONCE(i2c_bus->target_active, false);\n+#endif\n \ti2c_bus->dev = dev;\n \ti2c_bus->multi_master = device_property_read_bool(dev, \"multi-master\");\n \n@@ -765,6 +1102,9 @@ static int ast2600_i2c_probe(struct platform_device *pdev)\n \n \twritel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,\n \t       i2c_bus->reg_base + AST2600_I2CM_IER);\n+#if IS_ENABLED(CONFIG_I2C_SLAVE)\n+\twritel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER);\n+#endif\n \n \tret = devm_i2c_add_adapter(dev, &i2c_bus->adap);\n \tif (ret)\n","prefixes":["v29","4/4"]}