From mboxrd@z Thu Jan 1 00:00:00 1970 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 93E7D37F00F; Wed, 15 Apr 2026 05:14:14 +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=1776230056; cv=none; b=ulVBsOphYjQcuXi5kFXXtC4MzcPKxQc3LEJVymudCWrd8+pHRtLlR+g1DSH12oY7dB1klBkEQVZjBE2ZN5Sb0qBPVC/1KJIJxF+klTcwPC3kPt0q00goulVt9KIpX/WU3nUmdOtd63Qy7nxqqldwKjoOCaSHE/ljP3I0GSnpSXY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776230056; c=relaxed/simple; bh=7gdQPuqUllpQHg8Qb2lTDDNbP8FnNu6CN80JWiz5OB0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=tVMFkGEpj3eJowEMfYpPcwrmDad62NgtjIbSj5ALmWduJPJ6I/RWzd+fgNtHDgE6s4BKssUeyO4Y+ai90aze5GHxOqJm/+rOsofwOT7iIqrZh1g9bNChgWOWwogEVWel9jzRwSC4qroW7ngfTP+MkaUJ4VOAawIR7HdSy45mbAo= 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, 15 Apr 2026 13:14:03 +0800 Received: from [127.0.1.1] (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, 15 Apr 2026 13:14:03 +0800 From: Ryan Chen Date: Wed, 15 Apr 2026 13:14:05 +0800 Subject: [PATCH v29 4/4] i2c: ast2600: Add target mode support Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: 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: , , Andi Shyti , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , "Benjamin Herrenschmidt" , Rayn Chen , Philipp Zabel CC: , , , , , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1776230042; l=17308; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=7gdQPuqUllpQHg8Qb2lTDDNbP8FnNu6CN80JWiz5OB0=; b=H5H0qg2uPx6rsvoMysor0xlXGslABVvO7mQFk6VW3v0T2aMxKeu503voehoA+5XVDHlxC3sQ5 /J+M4Jj6g+BBFohwCwb2PmcsFBAq+PMBJbWu3Rh7KmKLplBehFB7Fau X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= Add target mode support to the AST2600 I2C driver. Target mode features implemented include: - Add target interrupt handling - Address match and response logic This complements the existing controller-mode support, enabling dual-role capability. Signed-off-by: Ryan Chen --- Changes in v29: - fix race between unreg_target and IRQ handler. - move i2cs ier enable from ast2600_i2c_init to probe after master ier enable. - remove dma/byte transfer, use buffer mode only. Changes in v28: - fix typo condication -> condition - fix compile error, when disable CONFIG_I2C_SLAVE Changes in v26: - change int to bool target_operate - rename target_operate to target_active - use i2c_bus->target replace require IO - use WRITE_ONCE replace target_operate write. --- drivers/i2c/busses/i2c-ast2600.c | 340 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c index 787ef6bd6244..137aaf7e26b5 100644 --- a/drivers/i2c/busses/i2c-ast2600.c +++ b/drivers/i2c/busses/i2c-ast2600.c @@ -254,6 +254,11 @@ struct ast2600_i2c_bus { size_t buf_size; bool multi_master; void __iomem *buf_base; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* target structure */ + bool target_active; + struct i2c_client *target; +#endif }; static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus) @@ -337,6 +342,243 @@ 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_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); + WRITE_ONCE(i2c_bus->target_active, false); + return; + } + + sts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR); + + if (sts & AST2600_I2CS_SLAVE_MATCH) + WRITE_ONCE(i2c_bus->target_active, true); + + 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)) + WRITE_ONCE(i2c_bus->target_active, false); +} + +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 controller packet done + * So need handle controller 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) + ast2600_i2c_target_packet_buff_irq(i2c_bus, isr); + + return 1; +} +#endif + static int ast2600_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus) { struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; @@ -498,6 +740,20 @@ static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u } break; case AST2600_I2CM_RX_DONE: +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* + * Workaround for controller/target packet mode enable rx done stuck issue + * When controller 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 */ xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + @@ -584,6 +840,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 (i2c_bus->target) { + if (ast2600_i2c_target_irq(i2c_bus)) + return IRQ_HANDLED; + } +#endif return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus)); } @@ -600,12 +862,31 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg return ret; } +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (i2c_bus->target_active) + return -EBUSY; + /* + * Controller and target share the same buffer register. A target + * transaction can update buffer state asynchronously via IRQ, so block + * controller transfers while target is active to avoid buffer corruption. + */ + writel(0, i2c_bus->reg_base + AST2600_I2CS_IER); + if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR) || i2c_bus->target_active) { + 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 condition target is wait and controller wait 1st target operate */ + 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); @@ -624,6 +905,9 @@ static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg * if the bus is still busy. */ if (i2c_bus->multi_master && +#if IS_ENABLED(CONFIG_I2C_SLAVE) + !i2c_bus->target_active && +#endif (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) & AST2600_I2CC_BUS_BUSY_STS)) ast2600_i2c_recover_bus(i2c_bus); @@ -659,8 +943,54 @@ static int 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) + writel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CS_ISR); +#endif + + return 0; +} + +#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); + + 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); + u32 val; + + /* Turn off target mode. */ + val = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); + writel(val & ~AST2600_I2CC_SLAVE_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); + val = readl(i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL); + writel(val & ~AST2600_I2CS_ADDR1_MASK, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL); + + synchronize_irq(i2c_bus->irq); + i2c_bus->target = NULL; + return 0; } +#endif static u32 ast2600_i2c_functionality(struct i2c_adapter *adap) { @@ -670,6 +1000,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) @@ -708,6 +1042,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) + WRITE_ONCE(i2c_bus->target_active, false); +#endif i2c_bus->dev = dev; i2c_bus->multi_master = device_property_read_bool(dev, "multi-master"); @@ -765,6 +1102,9 @@ static int ast2600_i2c_probe(struct platform_device *pdev) writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER, i2c_bus->reg_base + AST2600_I2CM_IER); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + writel(AST2600_I2CS_PKT_DONE, i2c_bus->reg_base + AST2600_I2CS_IER); +#endif ret = devm_i2c_add_adapter(dev, &i2c_bus->adap); if (ret) -- 2.34.1