From dylan_hung at aspeedtech.com  Wed May  1 22:24:38 2024
From: dylan_hung at aspeedtech.com (Dylan Hung)
Date: Thu, 2 May 2024 05:24:38 +0000
Subject: [PATCH] i3c: dw: Disable IBI IRQ depends on hot-join and SIR
 enabling
In-Reply-To: <563ad5613e9c5f0671e1f49f2d9ba71d8735799b.camel@codeconstruct.com.au>
References: <20240119054547.983693-1-dylan_hung@aspeedtech.com>
 <563ad5613e9c5f0671e1f49f2d9ba71d8735799b.camel@codeconstruct.com.au>
Message-ID: <TYZPR06MB65675E5D43EE265DD702E5689C182@TYZPR06MB6567.apcprd06.prod.outlook.com>

Hi Jeremy,

> -----Original Message-----
> From: Jeremy Kerr <jk at codeconstruct.com.au>
> Sent: Wednesday, May 1, 2024 2:22 PM
> To: Dylan Hung <dylan_hung at aspeedtech.com>;
> alexandre.belloni at bootlin.com; joel at jms.id.au; u.kleine-
> koenig at pengutronix.de; gustavoars at kernel.org;
> krzysztof.kozlowski at linaro.org; zenghuchen at google.com;
> matt at codeconstruct.com.au; linux-i3c at lists.infradead.org; linux-
> kernel at vger.kernel.org
> Cc: BMC-SW <BMC-SW at aspeedtech.com>
> Subject: Re: [PATCH] i3c: dw: Disable IBI IRQ depends on hot-join and SIR
> enabling
> 
> Hi Dylan,
> 
> Just a question on a prior patch you sent:
> 
> > Disable IBI IRQ signal and status only when hot-join and SIR enabling
> > of all target devices attached to the bus are disabled.
> >
> > Fixes: e389b1d72a62 ("i3c: dw: Add support for in-band interrupts")
> 
> [...]
> 
> > --- a/drivers/i3c/master/dw-i3c-master.c
> > +++ b/drivers/i3c/master/dw-i3c-master.c
> > @@ -1163,8 +1163,10 @@ static void
> > dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
> > ????????????????global = reg == 0xffffffff;
> > ????????????????reg &= ~BIT(idx);
> > ????????} else {
> > -???????????????global = reg == 0;
> > +???????????????bool hj_rejected = !!(readl(master->regs +
> > +DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK);
> > +
> > ????????????????reg |= BIT(idx);
> > +???????????????global = (reg == 0xffffffff) && hj_rejected;
> > ????????}
> > ????????writel(reg, master->regs + IBI_SIR_REQ_REJECT);
> >
>

```
	if (global)
		dw_i3c_master_enable_sir_signal(master, enable);
```

> My interpretation of this change is that we keep the "global" IBI irq enabled if
> hot-join-nack is set (ie, always, because we don't support hot join, and
> configure the hardware to nack all hot join requests).
> 
I would like to clarify the control logic, incorporating the principle of disabling the SIR interrupt signal:

Case 1:
When `DEV_CTRL_HOT_JOIN_NACK` is set, indicating `hj_rejected` is true, it signifies the controller's non-receptiveness to the hot-join event. Consequently, we can safely disable the SIR interrupt signal if none of the target devices request SIR (reg == 0xffffffff).

Case 2:
When `DEV_CTRL_HOT_JOIN_NACK` is unset, indicating `hj_rejected` is false, this indicates the controller's readiness to engage with the hot-join event. Therefore, it's imperative to keep the SIR interrupt signal enabled, even if not all target devices request SIR. In this case, `global` is false and `enable` is false.

Here's a summary table to illustrate the logic:

| enable | reg==0xffffffff? | hj_rejected? | global | result                            |
|---         |---                          |---                   |---         |---                                  |
| false     | true                      | true                | true     | disable SIR interrupt |
| false     | false                     | true                | false     | keep SIR interrupt    |
| false     | true                      | false               | false     | keep SIR interrupt    |
| false     | false                     | false               | false     | keep SIR interrupt    |

> However, we never enable the hot-join NACK interrupt - IBI_QUEUE_CTRL bit
> 0 is never set. So I can't see how we would ever get an interrupt for the hot
> join NACK case anyway. Because of that, this patch is just keeping the IBI
> threshold interrupt always enabled for no reason.
> 

Billy recently submitted a change to implement the hot-join enabling/disabling. Therefore, it is timely to consider the hot-join functionality.
https://patchwork.kernel.org/project/linux-i3c/patch/20240429073624.256830-1-billy_tsai at aspeedtech.com/

> Or is something else happening here? Is there another cause for the IBI
> threshold IRQs?
> 
> Cheers,
> 
> 
> Jeremy

From jk at codeconstruct.com.au  Sun May  5 22:09:35 2024
From: jk at codeconstruct.com.au (Jeremy Kerr)
Date: Mon, 06 May 2024 13:09:35 +0800
Subject: [PATCH] i3c: dw: Disable IBI IRQ depends on hot-join and SIR
 enabling
In-Reply-To: <TYZPR06MB65675E5D43EE265DD702E5689C182@TYZPR06MB6567.apcprd06.prod.outlook.com>
References: <20240119054547.983693-1-dylan_hung@aspeedtech.com>
	 <563ad5613e9c5f0671e1f49f2d9ba71d8735799b.camel@codeconstruct.com.au>
	 <TYZPR06MB65675E5D43EE265DD702E5689C182@TYZPR06MB6567.apcprd06.prod.outlook.com>
Message-ID: <645d4f645b1296d54573c4fe734768adab160035.camel@codeconstruct.com.au>

Hi Dylan,

Thanks for the response! I have a couple of follow-up things though:

> > My interpretation of this change is that we keep the "global" IBI irq enabled if
> > hot-join-nack is set (ie, always, because we don't support hot join, and
> > configure the hardware to nack all hot join requests).
> > 
> I would like to clarify the control logic, incorporating the principle
> of disabling the SIR interrupt signal:
> 
> Case 1:
> When `DEV_CTRL_HOT_JOIN_NACK` is set, indicating `hj_rejected` is
> true, it signifies the controller's non-receptiveness to the hot-join
> event. Consequently, we can safely disable the SIR interrupt signal if
> none of the target devices request SIR (reg == 0xffffffff).
> 
> Case 2:
> When `DEV_CTRL_HOT_JOIN_NACK` is unset, indicating `hj_rejected` is
> false, this indicates the controller's readiness to engage with the
> hot-join event. Therefore, it's imperative to keep the SIR interrupt
> signal enabled, even if not all target devices request SIR. In this
> case, `global` is false and `enable` is false.

Yep, I see what you're doing there, but it looks like the correct state
would never be set if we're not enabling/disabling the IBIs separately;
with this code, we would only ever enable the SIR for the HJ if we
*also* happen to enable IBIs.

The initial state would be to have all SIRs masked.

> Billy recently submitted a change to implement the hot-join enabling/disabling. Therefore, it is timely to consider the hot-join functionality.
> https://patchwork.kernel.org/project/linux-i3c/patch/20240429073624.256830-1-billy_tsai at aspeedtech.com/

Yep, I saw that, excellent! It's next on my list to take a look at.

It's just a little unusual that we're enabling the HJ interrupt before
actually having the HJ support though.

Cheers,


Jeremy


From dylan_hung at aspeedtech.com  Sun May  5 22:48:49 2024
From: dylan_hung at aspeedtech.com (Dylan Hung)
Date: Mon, 6 May 2024 05:48:49 +0000
Subject: [PATCH] i3c: dw: Disable IBI IRQ depends on hot-join and SIR
 enabling
In-Reply-To: <645d4f645b1296d54573c4fe734768adab160035.camel@codeconstruct.com.au>
References: <20240119054547.983693-1-dylan_hung@aspeedtech.com>
	 <563ad5613e9c5f0671e1f49f2d9ba71d8735799b.camel@codeconstruct.com.au>
	 <TYZPR06MB65675E5D43EE265DD702E5689C182@TYZPR06MB6567.apcprd06.prod.outlook.com>
 <645d4f645b1296d54573c4fe734768adab160035.camel@codeconstruct.com.au>
Message-ID: <TYZPR06MB65678EE7611DDB825E5404C19C1C2@TYZPR06MB6567.apcprd06.prod.outlook.com>

Hi Jeremy,

> -----Original Message-----
> From: Jeremy Kerr <jk at codeconstruct.com.au>
> Sent: Monday, May 6, 2024 1:10 PM
> To: Dylan Hung <dylan_hung at aspeedtech.com>;
> alexandre.belloni at bootlin.com; joel at jms.id.au; u.kleine-
> koenig at pengutronix.de; gustavoars at kernel.org;
> krzysztof.kozlowski at linaro.org; zenghuchen at google.com;
> matt at codeconstruct.com.au; linux-i3c at lists.infradead.org; linux-
> kernel at vger.kernel.org
> Cc: BMC-SW <BMC-SW at aspeedtech.com>
> Subject: Re: [PATCH] i3c: dw: Disable IBI IRQ depends on hot-join and SIR
> enabling
> 
> Hi Dylan,
> 
> Thanks for the response! I have a couple of follow-up things though:
> 
> > > My interpretation of this change is that we keep the "global" IBI
> > > irq enabled if hot-join-nack is set (ie, always, because we don't
> > > support hot join, and configure the hardware to nack all hot join
> requests).
> > >
> > I would like to clarify the control logic, incorporating the principle
> > of disabling the SIR interrupt signal:
> >
> > Case 1:
> > When `DEV_CTRL_HOT_JOIN_NACK` is set, indicating `hj_rejected` is
> > true, it signifies the controller's non-receptiveness to the hot-join
> > event. Consequently, we can safely disable the SIR interrupt signal if
> > none of the target devices request SIR (reg == 0xffffffff).
> >
> > Case 2:
> > When `DEV_CTRL_HOT_JOIN_NACK` is unset, indicating `hj_rejected` is
> > false, this indicates the controller's readiness to engage with the
> > hot-join event. Therefore, it's imperative to keep the SIR interrupt
> > signal enabled, even if not all target devices request SIR. In this
> > case, `global` is false and `enable` is false.
> 
> Yep, I see what you're doing there, but it looks like the correct state would
> never be set if we're not enabling/disabling the IBIs separately; with this code,
> we would only ever enable the SIR for the HJ if we
> *also* happen to enable IBIs.

When we want to enable the SIR, the current code doesn't check whether the
hot-join is enable or not. The modification I made pertains to disabling the SIR interrupt.
It doesn't alter the logic for enabling the SIR.

```
	reg = readl(master->regs + IBI_SIR_REQ_REJECT);
	if (enable) {
		global = reg == 0xffffffff;
		reg &= ~BIT(idx);
	}
```

> 
> The initial state would be to have all SIRs masked.
> 

Yes, indeed. The "global" variable is also true because "reg == 0xffffffff" is true.
Therefore, the INTR_IBI_THLD_STAT bit will be set in the following code.

> > Billy recently submitted a change to implement the hot-join
> enabling/disabling. Therefore, it is timely to consider the hot-join
> functionality.
> > https://patchwork.kernel.org/project/linux-i3c/patch/20240429073624.25
> > 6830-1-billy_tsai at aspeedtech.com/
> 
> Yep, I saw that, excellent! It's next on my list to take a look at.
> 
> It's just a little unusual that we're enabling the HJ interrupt before actually
> having the HJ support though.
> 
> Cheers,
> 
> 
> Jeremy

From jk at codeconstruct.com.au  Sun May  5 23:58:06 2024
From: jk at codeconstruct.com.au (Jeremy Kerr)
Date: Mon, 06 May 2024 14:58:06 +0800
Subject: [PATCH] i3c: dw: Disable IBI IRQ depends on hot-join and SIR
 enabling
In-Reply-To: <TYZPR06MB65678EE7611DDB825E5404C19C1C2@TYZPR06MB6567.apcprd06.prod.outlook.com>
References: <20240119054547.983693-1-dylan_hung@aspeedtech.com>
	 <563ad5613e9c5f0671e1f49f2d9ba71d8735799b.camel@codeconstruct.com.au>
	 <TYZPR06MB65675E5D43EE265DD702E5689C182@TYZPR06MB6567.apcprd06.prod.outlook.com>
	 <645d4f645b1296d54573c4fe734768adab160035.camel@codeconstruct.com.au>
	 <TYZPR06MB65678EE7611DDB825E5404C19C1C2@TYZPR06MB6567.apcprd06.prod.outlook.com>
Message-ID: <52f4e4afef87c336f054ca1c43eec58d2e44a5b1.camel@codeconstruct.com.au>

Hi Dylan,

> > The initial state would be to have all SIRs masked.
> > 
> 
> Yes, indeed. The "global" variable is also true because "reg ==
> 0xffffffff" is true.
> Therefore, the INTR_IBI_THLD_STAT bit will be set in the following
> code.

That's mainly my point - none of this code is ever run unless the
->enable_ibi or ->disable_ibi controller callback is invoked.

So we'll end up with the HJ interrupt only being enabled if some i3c
device driver enables IBIs, which is a bit of a weird side-effect.

It probably makes more sense when the rest of the HJ code is added, but
not so much as a standalone patch.

Cheers,


Jeremy


From miquel.raynal at bootlin.com  Mon May  6 01:54:51 2024
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Mon, 6 May 2024 10:54:51 +0200
Subject: [PATCH 1/2] i3c: master: svc: change ENXIO to EAGAIN when IBI
 occurs during start frame
In-Reply-To: <20240424190030.857632-1-Frank.Li@nxp.com>
References: <20240424190030.857632-1-Frank.Li@nxp.com>
Message-ID: <20240506105451.26e40778@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Wed, 24 Apr 2024 15:00:29 -0400:

> svc_i3c_master_xfer() returns error ENXIO if an In-Band Interrupt (IBI)
> occurs when the host starts the frame.
> 
> Change error code to EAGAIN to inform the client driver that this
> situation has occurred and to try again sometime later.

This changes slightly the user API, but feels legitimate at the same
time. Maybe a comment somewhere in the i3c headers to clarify this
possibility might be welcome?

> Fixes: 5e5e3c92e748 ("i3c: master: svc: fix wrong data return when IBI happen during start frame")
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/i3c/master/svc-i3c-master.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 5ee4db68988e2..a2298ab460a37 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -1080,7 +1080,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
>  	 * and yield the above events handler.
>  	 */
>  	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
> -		ret = -ENXIO;
> +		ret = -EAGAIN;
>  		*actual_len = 0;
>  		goto emit_stop;
>  	}


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Mon May  6 01:59:40 2024
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Mon, 6 May 2024 10:59:40 +0200
Subject: [PATCH 2/2] i3c: master: svc: fix invalidate IBI type and miss
 call client IBI handler
In-Reply-To: <20240424190030.857632-2-Frank.Li@nxp.com>
References: <20240424190030.857632-1-Frank.Li@nxp.com>
	<20240424190030.857632-2-Frank.Li@nxp.com>
Message-ID: <20240506105940.5d8d2308@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Wed, 24 Apr 2024 15:00:30 -0400:

> In an In-Band Interrupt (IBI) handle, the code logic is as follows:
> 
> 1: writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI | SVC_I3C_MCTRL_IBIRESP_AUTO,
> 	  master->regs + SVC_I3C_MCTRL);
> 
> 2: ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
>                                     SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
> 	...
> 3: ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
>    ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);
> 
> SVC_I3C_MSTATUS_IBIWON may be set before step 1. Thus, step 2 will return
> immediately, and the I3C controller has not sent out the 9th SCL yet.
> Consequently, ibitype and ibiaddr are 0, resulting in an unknown IBI type
> occurrence and missing call I3C client driver's IBI handler.
> 
> A typical case is that SVC_I3C_MSTATUS_IBIWON is set when an IBI occurs
> during the controller send start frame in svc_i3c_master_xfer().
> 
> Clear SVC_I3C_MSTATUS_IBIWON before issue SVC_I3C_MCTRL_REQUEST_AUTO_IBI
> to fix this issue.
> 
> Cc: stable at vger.kernel.org
> Fixes: 5e5e3c92e748 ("i3c: master: svc: fix wrong data return when IBI happen during start frame")
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/i3c/master/svc-i3c-master.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index a2298ab460a37..3bfe8e694f840 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -415,6 +415,9 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
>  	int ret;
>  
>  	mutex_lock(&master->lock);
> +	/* Clear the interrupt status */

Could you improve the comment to explain why this is needed here?

Otherwise seems okay, but I don't have any strong feelings here.

> +	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
> +
>  	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
>  	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
>  	       SVC_I3C_MCTRL_IBIRESP_AUTO,
> @@ -429,9 +432,6 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
>  		goto reenable_ibis;
>  	}
>  
> -	/* Clear the interrupt status */
> -	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
> -
>  	status = readl(master->regs + SVC_I3C_MSTATUS);
>  	ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
>  	ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);

Thanks,
Miqu?l


From dylan_hung at aspeedtech.com  Mon May  6 03:51:18 2024
From: dylan_hung at aspeedtech.com (Dylan Hung)
Date: Mon, 6 May 2024 18:51:18 +0800
Subject: [PATCH 0/2] [RFC] i3c: ast2600: Add MCTP-over-I3C support
Message-ID: <20240506105120.3028083-1-dylan_hung@aspeedtech.com>

Add Aspeed AST2600 I3C controllers and add minimal changes to validate
the functionality for MCTP-over-I3C.

Dylan Hung (2):
  [RFC] ARM: dts: aspeed-g6: Add AST2600 I3Cs
  [RFC] i3c: ast2600: Validate AST2600 I3C for MCTP-over-I3C

 arch/arm/boot/dts/aspeed/aspeed-g6.dtsi | 137 +++++++
 drivers/i3c/master/ast2600-i3c-master.c | 137 +++++++
 drivers/i3c/master/dw-i3c-master.c      | 513 ++++++++++++++++++++----
 drivers/i3c/master/dw-i3c-master.h      |  12 +
 4 files changed, 718 insertions(+), 81 deletions(-)

-- 
2.25.1



From dylan_hung at aspeedtech.com  Mon May  6 03:51:20 2024
From: dylan_hung at aspeedtech.com (Dylan Hung)
Date: Mon, 6 May 2024 18:51:20 +0800
Subject: [PATCH 2/2] [RFC] i3c: ast2600: Validate AST2600 I3C for MCTP-over-I3C
In-Reply-To: <20240506105120.3028083-1-dylan_hung@aspeedtech.com>
References: <20240506105120.3028083-1-dylan_hung@aspeedtech.com>
Message-ID: <20240506105120.3028083-3-dylan_hung@aspeedtech.com>

This patch introduces minimal changes to validate the functionality of
Aspeed AST2600 I3C controllers for MCTP-over-I3C.

Signed-off-by: Dylan Hung <dylan_hung at aspeedtech.com>
---
 drivers/i3c/master/ast2600-i3c-master.c | 137 +++++++
 drivers/i3c/master/dw-i3c-master.c      | 513 ++++++++++++++++++++----
 drivers/i3c/master/dw-i3c-master.h      |  12 +
 3 files changed, 581 insertions(+), 81 deletions(-)

diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast2600-i3c-master.c
index 01a47d3dd499..a0a48f10bccb 100644
--- a/drivers/i3c/master/ast2600-i3c-master.c
+++ b/drivers/i3c/master/ast2600-i3c-master.c
@@ -10,6 +10,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/bitfield.h>
 
 #include "dw-i3c-master.h"
 
@@ -33,9 +34,29 @@
 #define AST2600_I3CG_REG1_SA_EN			BIT(15)
 #define AST2600_I3CG_REG1_INST_ID_MASK		GENMASK(19, 16)
 #define AST2600_I3CG_REG1_INST_ID(x)		(((x) << 16) & AST2600_I3CG_REG1_INST_ID_MASK)
+#define SCL_SW_MODE_OE				BIT(20)
+#define SCL_OUT_SW_MODE_VAL			BIT(21)
+#define SCL_IN_SW_MODE_VAL			BIT(23)
+#define SDA_SW_MODE_OE				BIT(24)
+#define SDA_OUT_SW_MODE_VAL			BIT(25)
+#define SDA_IN_SW_MODE_VAL			BIT(27)
+#define SCL_IN_SW_MODE_EN			BIT(28)
+#define SDA_IN_SW_MODE_EN			BIT(29)
+#define SCL_OUT_SW_MODE_EN			BIT(30)
+#define SDA_OUT_SW_MODE_EN			BIT(31)
 
 #define AST2600_DEFAULT_SDA_PULLUP_OHMS		2000
 
+/* dw-i3c registers */
+#define IBI_QUEUE_STATUS			0x18
+
+#define PRESENT_STATE				0x54
+#define   CM_TFR_STS				GENMASK(13, 8)
+#define     CM_TFR_STS_MASTER_SERV_IBI		0xe
+#define   SDA_LINE_SIGNAL_LEVEL			BIT(1)
+#define   SCL_LINE_SIGNAL_LEVEL			BIT(0)
+
+/* DAT */
 #define DEV_ADDR_TABLE_IBI_PEC			BIT(11)
 
 struct ast2600_i3c {
@@ -117,9 +138,125 @@ static void ast2600_i3c_set_dat_ibi(struct dw_i3c_master *i3c,
 	}
 }
 
+static int aspeed_i3c_bus_recovery(struct dw_i3c_master *dw)
+{
+	struct ast2600_i3c *i3c = to_ast2600_i3c(dw);
+	int i, ret = -1;
+
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SCL_OUT_SW_MODE_VAL, SCL_OUT_SW_MODE_VAL);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SCL_SW_MODE_OE, SCL_SW_MODE_OE);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SCL_OUT_SW_MODE_EN, SCL_OUT_SW_MODE_EN);
+
+	for (i = 0; i < 19; i++) {
+		regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+				  SCL_OUT_SW_MODE_VAL, 0);
+		regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+				  SCL_OUT_SW_MODE_VAL, SCL_OUT_SW_MODE_VAL);
+		if (readl(dw->regs + PRESENT_STATE) & SDA_LINE_SIGNAL_LEVEL) {
+			ret = 0;
+			break;
+		}
+	}
+
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SCL_OUT_SW_MODE_EN, 0);
+	if (ret)
+		dev_err(&dw->base.dev, "Failed to recover the bus\n");
+
+	return ret;
+}
+
+static void ast2600_i3c_gen_target_reset_pattern(struct dw_i3c_master *dw)
+{
+	struct ast2600_i3c *i3c = to_ast2600_i3c(dw);
+	int i;
+
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_OUT_SW_MODE_VAL | SCL_OUT_SW_MODE_VAL,
+			  SDA_OUT_SW_MODE_VAL | SCL_OUT_SW_MODE_VAL);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_SW_MODE_OE | SCL_SW_MODE_OE,
+			  SDA_SW_MODE_OE | SCL_SW_MODE_OE);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_OUT_SW_MODE_EN | SCL_OUT_SW_MODE_EN,
+			  SDA_OUT_SW_MODE_EN | SCL_OUT_SW_MODE_EN);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_IN_SW_MODE_VAL | SCL_IN_SW_MODE_VAL,
+			  SDA_IN_SW_MODE_VAL | SCL_IN_SW_MODE_VAL);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_IN_SW_MODE_EN | SCL_IN_SW_MODE_EN,
+			  SDA_IN_SW_MODE_EN | SCL_IN_SW_MODE_EN);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SCL_OUT_SW_MODE_VAL, 0);
+	for (i = 0; i < 7; i++) {
+		regmap_write_bits(i3c->global_regs,
+				  AST2600_I3CG_REG1(i3c->global_idx),
+				  SDA_OUT_SW_MODE_VAL, 0);
+		regmap_write_bits(i3c->global_regs,
+				  AST2600_I3CG_REG1(i3c->global_idx),
+				  SDA_OUT_SW_MODE_VAL, SDA_OUT_SW_MODE_VAL);
+	}
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SCL_OUT_SW_MODE_VAL, SCL_OUT_SW_MODE_VAL);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_OUT_SW_MODE_VAL, 0);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_OUT_SW_MODE_VAL, SDA_OUT_SW_MODE_VAL);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_OUT_SW_MODE_EN | SCL_OUT_SW_MODE_EN, 0);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_IN_SW_MODE_EN | SCL_IN_SW_MODE_EN, 0);
+}
+
+static bool ast2600_i3c_fsm_exit_serv_ibi(struct dw_i3c_master *dw)
+{
+	u32 state;
+
+	/*
+	 * Clear the IBI queue to enable the hardware to generate SCL and
+	 * begin detecting the T-bit low to stop reading IBI data.
+	 */
+	readl(dw->regs + IBI_QUEUE_STATUS);
+	state = FIELD_GET(CM_TFR_STS, readl(dw->regs + PRESENT_STATE));
+	if (state == CM_TFR_STS_MASTER_SERV_IBI)
+		return false;
+
+	return true;
+}
+
+static void ast2600_i3c_gen_tbits_in(struct dw_i3c_master *dw)
+{
+	struct ast2600_i3c *i3c = to_ast2600_i3c(dw);
+	bool is_idle;
+	int ret;
+
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_IN_SW_MODE_VAL, SDA_IN_SW_MODE_VAL);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_IN_SW_MODE_EN, SDA_IN_SW_MODE_EN);
+
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_IN_SW_MODE_VAL, 0);
+	ret = readx_poll_timeout_atomic(ast2600_i3c_fsm_exit_serv_ibi, dw,
+					is_idle, is_idle, 0, 2000000);
+	regmap_write_bits(i3c->global_regs, AST2600_I3CG_REG1(i3c->global_idx),
+			  SDA_IN_SW_MODE_EN, 0);
+	if (ret)
+		dev_err(&dw->base.dev,
+			"Failed to exit the I3C fsm from %lx(MASTER_SERV_IBI): %d",
+			FIELD_GET(CM_TFR_STS, readl(dw->regs + PRESENT_STATE)),
+			ret);
+}
+
 static const struct dw_i3c_platform_ops ast2600_i3c_ops = {
 	.init = ast2600_i3c_init,
 	.set_dat_ibi = ast2600_i3c_set_dat_ibi,
+	.gen_target_reset_pattern = ast2600_i3c_gen_target_reset_pattern,
+	.gen_tbits_in = ast2600_i3c_gen_tbits_in,
+	.bus_recovery = aspeed_i3c_bus_recovery,
 };
 
 static int ast2600_i3c_probe(struct platform_device *pdev)
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 0ec00e644bd4..a9fe886ee840 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/completion.h>
 #include <linux/err.h>
@@ -26,8 +27,10 @@
 #define DEVICE_CTRL			0x0
 #define DEV_CTRL_ENABLE			BIT(31)
 #define DEV_CTRL_RESUME			BIT(30)
+#define DEV_CTRL_ABORT			BIT(29)
 #define DEV_CTRL_HOT_JOIN_NACK		BIT(8)
 #define DEV_CTRL_I2C_SLAVE_PRESENT	BIT(7)
+#define DEV_CTRL_IBA_INCLUDE		BIT(0)
 
 #define DEVICE_ADDR			0x4
 #define DEV_ADDR_DYNAMIC_ADDR_VALID	BIT(31)
@@ -40,6 +43,16 @@
 #define COMMAND_PORT_SDAP		BIT(27)
 #define COMMAND_PORT_ROC		BIT(26)
 #define COMMAND_PORT_SPEED(x)		(((x) << 21) & GENMASK(23, 21))
+#define   SPEED_I3C_SDR0		0x0
+#define   SPEED_I3C_SDR1		0x1
+#define   SPEED_I3C_SDR2		0x2
+#define   SPEED_I3C_SDR3		0x3
+#define   SPEED_I3C_SDR4		0x4
+#define   SPEED_I3C_HDR_TS		0x5
+#define   SPEED_I3C_HDR_DDR		0x6
+#define   SPEED_I3C_I2C_FM		0x7
+#define   SPEED_I2C_FM			0x0
+#define   SPEED_I2C_FMP			0x1
 #define COMMAND_PORT_DEV_INDEX(x)	(((x) << 16) & GENMASK(20, 16))
 #define COMMAND_PORT_CP			BIT(15)
 #define COMMAND_PORT_CMD(x)		(((x) << 7) & GENMASK(14, 7))
@@ -76,6 +89,7 @@
 
 #define RX_TX_DATA_PORT			0x14
 #define IBI_QUEUE_STATUS		0x18
+#define IBI_QUEUE_STATUS_RSP_NACK	BIT(31)
 #define IBI_QUEUE_STATUS_IBI_ID(x)	(((x) & GENMASK(15, 8)) >> 8)
 #define IBI_QUEUE_STATUS_DATA_LEN(x)	((x) & GENMASK(7, 0))
 #define IBI_QUEUE_IBI_ADDR(x)		(IBI_QUEUE_STATUS_IBI_ID(x) >> 1)
@@ -96,7 +110,8 @@
 #define QUEUE_THLD_CTRL_RESP_BUF(x)	(((x) - 1) << 8)
 
 #define DATA_BUFFER_THLD_CTRL		0x20
-#define DATA_BUFFER_THLD_CTRL_RX_BUF	GENMASK(11, 8)
+#define DATA_BUFFER_THLD_TX_START	GENMASK(18, 16)
+#define DATA_BUFFER_THLD_CTRL_RX_BUF	GENMASK(10, 8)
 
 #define IBI_QUEUE_CTRL			0x24
 #define IBI_MR_REQ_REJECT		0x2C
@@ -156,6 +171,12 @@
 #define DATA_BUFFER_STATUS_LEVEL_TX(x)	((x) & GENMASK(7, 0))
 
 #define PRESENT_STATE			0x54
+#define   CM_TFR_STS			GENMASK(13, 8)
+#define     CM_TFR_STS_MASTER_SERV_IBI	0xe
+#define     CM_TFR_STS_MASTER_HALT	0xf
+#define   SDA_LINE_SIGNAL_LEVEL		BIT(1)
+#define   SCL_LINE_SIGNAL_LEVEL		BIT(0)
+
 #define CCC_DEVICE_STATUS		0x58
 #define DEVICE_ADDR_TABLE_POINTER	0x5c
 #define DEVICE_ADDR_TABLE_DEPTH(x)	(((x) & GENMASK(31, 16)) >> 16)
@@ -173,17 +194,17 @@
 #define DEVICE_CTRL_EXTENDED		0xb0
 #define SCL_I3C_OD_TIMING		0xb4
 #define SCL_I3C_PP_TIMING		0xb8
-#define SCL_I3C_TIMING_HCNT(x)		(((x) << 16) & GENMASK(23, 16))
-#define SCL_I3C_TIMING_LCNT(x)		((x) & GENMASK(7, 0))
-#define SCL_I3C_TIMING_CNT_MIN		5
+#define   SCL_I3C_TIMING_HCNT		GENMASK(23, 16)
+#define   SCL_I3C_TIMING_LCNT		GENMASK(7, 0)
+#define     SCL_I3C_TIMING_CNT_MIN	5
 
 #define SCL_I2C_FM_TIMING		0xbc
-#define SCL_I2C_FM_TIMING_HCNT(x)	(((x) << 16) & GENMASK(31, 16))
-#define SCL_I2C_FM_TIMING_LCNT(x)	((x) & GENMASK(15, 0))
+#define   SCL_I2C_FM_TIMING_HCNT	GENMASK(31, 16)
+#define   SCL_I2C_FM_TIMING_LCNT	GENMASK(15, 0)
 
 #define SCL_I2C_FMP_TIMING		0xc0
-#define SCL_I2C_FMP_TIMING_HCNT(x)	(((x) << 16) & GENMASK(23, 16))
-#define SCL_I2C_FMP_TIMING_LCNT(x)	((x) & GENMASK(15, 0))
+#define   SCL_I2C_FMP_TIMING_HCNT	GENMASK(23, 16)
+#define   SCL_I2C_FMP_TIMING_LCNT	GENMASK(15, 0)
 
 #define SCL_EXT_LCNT_TIMING		0xc8
 #define SCL_EXT_LCNT_4(x)		(((x) << 24) & GENMASK(31, 24))
@@ -192,8 +213,14 @@
 #define SCL_EXT_LCNT_1(x)		((x) & GENMASK(7, 0))
 
 #define SCL_EXT_TERMN_LCNT_TIMING	0xcc
+#define SDA_HOLD_SWITCH_DLY_TIMING	0xd0
+#define   SDA_TX_HOLD			GENMASK(18, 16)
+#define     SDA_TX_HOLD_MIN		0b001
+#define     SDA_TX_HOLD_MAX		0b111
 #define BUS_FREE_TIMING			0xd4
-#define BUS_I3C_MST_FREE(x)		((x) & GENMASK(15, 0))
+#define   BUS_AVAIL_TIME		GENMASK(31, 16)
+#define     MAX_BUS_AVAIL_CNT		0xffffU
+#define   BUS_I3C_MST_FREE		GENMASK(15, 0)
 
 #define BUS_IDLE_TIMING			0xd8
 #define I3C_VER_ID			0xe0
@@ -201,10 +228,11 @@
 #define EXTENDED_CAPABILITY		0xe8
 #define SLAVE_CONFIG			0xec
 
-#define DEV_ADDR_TABLE_IBI_MDB		BIT(12)
-#define DEV_ADDR_TABLE_SIR_REJECT	BIT(13)
 #define DEV_ADDR_TABLE_LEGACY_I2C_DEV	BIT(31)
 #define DEV_ADDR_TABLE_DYNAMIC_ADDR(x)	(((x) << 16) & GENMASK(23, 16))
+#define DEV_ADDR_TABLE_MR_REJECT	BIT(14)
+#define DEV_ADDR_TABLE_SIR_REJECT	BIT(13)
+#define DEV_ADDR_TABLE_IBI_MDB		BIT(12)
 #define DEV_ADDR_TABLE_STATIC_ADDR(x)	((x) & GENMASK(6, 0))
 #define DEV_ADDR_TABLE_LOC(start, idx)	((start) + ((idx) << 2))
 
@@ -212,8 +240,19 @@
 #define I3C_BUS_SDR2_SCL_RATE		6000000
 #define I3C_BUS_SDR3_SCL_RATE		4000000
 #define I3C_BUS_SDR4_SCL_RATE		2000000
+#define I3C_BUS_I2C_STD_SCL_RATE	100000
+#define I3C_BUS_I2C_STD_TLOW_MIN_NS	4700
+#define I3C_BUS_I2C_STD_THIGH_MIN_NS	4000
+#define I3C_BUS_I2C_STD_TR_MAX_NS	1000
+#define I3C_BUS_I2C_STD_TF_MAX_NS	300
 #define I3C_BUS_I2C_FM_TLOW_MIN_NS	1300
+#define I3C_BUS_I2C_FM_THIGH_MIN_NS	600
+#define I3C_BUS_I2C_FM_TR_MAX_NS	300
+#define I3C_BUS_I2C_FM_TF_MAX_NS	300
 #define I3C_BUS_I2C_FMP_TLOW_MIN_NS	500
+#define I3C_BUS_I2C_FMP_THIGH_MIN_NS	260
+#define I3C_BUS_I2C_FMP_TR_MAX_NS	120
+#define I3C_BUS_I2C_FMP_TF_MAX_NS	120
 #define I3C_BUS_THIGH_MAX_NS		41
 
 #define XFER_TIMEOUT (msecs_to_jiffies(1000))
@@ -292,6 +331,18 @@ to_dw_i3c_master(struct i3c_master_controller *master)
 	return container_of(master, struct dw_i3c_master, base);
 }
 
+static void dw_i3c_master_set_iba(struct dw_i3c_master *master, bool enable)
+{
+	u32 reg;
+
+	reg = readl(master->regs + DEVICE_CTRL);
+	reg &= ~DEV_CTRL_IBA_INCLUDE;
+	if (enable)
+		reg |= DEV_CTRL_IBA_INCLUDE;
+
+	writel(reg, master->regs + DEVICE_CTRL);
+}
+
 static void dw_i3c_master_disable(struct dw_i3c_master *master)
 {
 	writel(readl(master->regs + DEVICE_CTRL) & ~DEV_CTRL_ENABLE,
@@ -304,6 +355,50 @@ static void dw_i3c_master_enable(struct dw_i3c_master *master)
 	       master->regs + DEVICE_CTRL);
 }
 
+static int dw_i3c_master_exit_halt(struct dw_i3c_master *master)
+{
+	u32 status;
+	u32 halt_state = CM_TFR_STS_MASTER_HALT;
+	int ret;
+
+	writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_RESUME,
+	       master->regs + DEVICE_CTRL);
+
+	ret = readl_poll_timeout_atomic(master->regs + PRESENT_STATE, status,
+					FIELD_GET(CM_TFR_STS, status) != halt_state,
+					10, 1000000);
+
+	if (ret)
+		dev_err(&master->base.dev,
+			"Exit halt state failed: %d %#x %#x\n", ret,
+			readl(master->regs + PRESENT_STATE),
+			readl(master->regs + QUEUE_STATUS_LEVEL));
+	return ret;
+}
+
+static int dw_i3c_master_enter_halt(struct dw_i3c_master *master, bool by_sw)
+{
+	u32 status;
+	u32 halt_state = CM_TFR_STS_MASTER_HALT;
+	int ret;
+
+	if (by_sw)
+		writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_ABORT,
+		       master->regs + DEVICE_CTRL);
+
+	ret = readl_poll_timeout_atomic(master->regs + PRESENT_STATE, status,
+					FIELD_GET(CM_TFR_STS, status) == halt_state,
+					10, 1000000);
+
+	if (ret)
+		dev_err(&master->base.dev,
+			"Enter halt state failed: %d %#x %#x\n", ret,
+			readl(master->regs + PRESENT_STATE),
+			readl(master->regs + QUEUE_STATUS_LEVEL));
+
+	return ret;
+}
+
 static int dw_i3c_master_get_addr_pos(struct dw_i3c_master *master, u8 addr)
 {
 	int pos;
@@ -506,9 +601,14 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
 	complete(&xfer->comp);
 
 	if (ret < 0) {
+		/*
+		 * The controller will enter the HALT state if an error occurs.
+		 * Therefore, there is no need to manually halt the controller
+		 * through software.
+		 */
+		dw_i3c_master_enter_halt(master, false);
 		dw_i3c_master_dequeue_xfer_locked(master, xfer);
-		writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_RESUME,
-		       master->regs + DEVICE_CTRL);
+		dw_i3c_master_exit_halt(master);
 	}
 
 	xfer = list_first_entry_or_null(&master->xferqueue.list,
@@ -521,41 +621,76 @@ static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr)
 	dw_i3c_master_start_xfer_locked(master);
 }
 
+static int calc_i2c_clk(struct dw_i3c_master *master, unsigned long fscl,
+			u16 *hcnt, u16 *lcnt)
+{
+	unsigned long core_rate, core_period;
+	u32 period_cnt, margin;
+	u32 hcnt_min, lcnt_min;
+
+	core_rate = master->timing.core_rate;
+	core_period = master->timing.core_period;
+
+	if (fscl <= I3C_BUS_I2C_STD_SCL_RATE) {
+		lcnt_min = DIV_ROUND_UP(I3C_BUS_I2C_STD_TLOW_MIN_NS +
+						I3C_BUS_I2C_STD_TF_MAX_NS,
+					core_period);
+		hcnt_min = DIV_ROUND_UP(I3C_BUS_I2C_STD_THIGH_MIN_NS +
+						I3C_BUS_I2C_STD_TR_MAX_NS,
+					core_period);
+	} else if (fscl <= I3C_BUS_I2C_FM_SCL_RATE) {
+		lcnt_min = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS +
+						I3C_BUS_I2C_FM_TF_MAX_NS,
+					core_period);
+		hcnt_min = DIV_ROUND_UP(I3C_BUS_I2C_FM_THIGH_MIN_NS +
+						I3C_BUS_I2C_FM_TR_MAX_NS,
+					core_period);
+	} else {
+		lcnt_min = DIV_ROUND_UP(I3C_BUS_I2C_FMP_TLOW_MIN_NS +
+						I3C_BUS_I2C_FMP_TF_MAX_NS,
+					core_period);
+		hcnt_min = DIV_ROUND_UP(I3C_BUS_I2C_FMP_THIGH_MIN_NS +
+						I3C_BUS_I2C_FMP_TR_MAX_NS,
+					core_period);
+	}
+
+	period_cnt = DIV_ROUND_UP(core_rate, fscl);
+	margin = (period_cnt - hcnt_min - lcnt_min) >> 1;
+	*lcnt = lcnt_min + margin;
+	*hcnt = max(period_cnt - *lcnt, hcnt_min);
+
+	return 0;
+}
+
 static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
 {
 	unsigned long core_rate, core_period;
 	u32 scl_timing;
 	u8 hcnt, lcnt;
 
-	core_rate = clk_get_rate(master->core_clk);
-	if (!core_rate)
-		return -EINVAL;
-
-	core_period = DIV_ROUND_UP(1000000000, core_rate);
-
-	hcnt = DIV_ROUND_UP(I3C_BUS_THIGH_MAX_NS, core_period) - 1;
-	if (hcnt < SCL_I3C_TIMING_CNT_MIN)
-		hcnt = SCL_I3C_TIMING_CNT_MIN;
+	core_rate = master->timing.core_rate;
+	core_period = master->timing.core_period;
 
-	lcnt = DIV_ROUND_UP(core_rate, master->base.bus.scl_rate.i3c) - hcnt;
-	if (lcnt < SCL_I3C_TIMING_CNT_MIN)
-		lcnt = SCL_I3C_TIMING_CNT_MIN;
+	if (master->timing.i3c_pp_scl_high && master->timing.i3c_pp_scl_low) {
+		hcnt = DIV_ROUND_CLOSEST(master->timing.i3c_pp_scl_high,
+					 core_period);
+		lcnt = DIV_ROUND_CLOSEST(master->timing.i3c_pp_scl_low,
+					 core_period);
+	} else {
+		hcnt = DIV_ROUND_UP(I3C_BUS_THIGH_MAX_NS, core_period) - 1;
+		if (hcnt < SCL_I3C_TIMING_CNT_MIN)
+			hcnt = SCL_I3C_TIMING_CNT_MIN;
+
+		lcnt = DIV_ROUND_UP(core_rate, master->base.bus.scl_rate.i3c) -
+		       hcnt;
+		if (lcnt < SCL_I3C_TIMING_CNT_MIN)
+			lcnt = SCL_I3C_TIMING_CNT_MIN;
+	}
 
-	scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
+	scl_timing = FIELD_PREP(SCL_I3C_TIMING_HCNT, hcnt) |
+		     FIELD_PREP(SCL_I3C_TIMING_LCNT, lcnt);
 	writel(scl_timing, master->regs + SCL_I3C_PP_TIMING);
 
-	/*
-	 * In pure i3c mode, MST_FREE represents tCAS. In shared mode, this
-	 * will be set up by dw_i2c_clk_cfg as tLOW.
-	 */
-	if (master->base.bus.mode == I3C_BUS_MODE_PURE)
-		writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
-
-	lcnt = max_t(u8,
-		     DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period), lcnt);
-	scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt);
-	writel(scl_timing, master->regs + SCL_I3C_OD_TIMING);
-
 	lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR1_SCL_RATE) - hcnt;
 	scl_timing = SCL_EXT_LCNT_1(lcnt);
 	lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR2_SCL_RATE) - hcnt;
@@ -566,6 +701,20 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master)
 	scl_timing |= SCL_EXT_LCNT_4(lcnt);
 	writel(scl_timing, master->regs + SCL_EXT_LCNT_TIMING);
 
+	if (master->timing.i3c_od_scl_high && master->timing.i3c_od_scl_low) {
+		hcnt = DIV_ROUND_CLOSEST(master->timing.i3c_od_scl_high,
+					 core_period);
+		lcnt = DIV_ROUND_CLOSEST(master->timing.i3c_od_scl_low,
+					 core_period);
+	} else {
+		lcnt = max_t(u8,
+			     DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period),
+			     lcnt);
+	}
+	scl_timing = FIELD_PREP(SCL_I3C_TIMING_HCNT, hcnt) |
+		     FIELD_PREP(SCL_I3C_TIMING_LCNT, lcnt);
+	writel(scl_timing, master->regs + SCL_I3C_OD_TIMING);
+
 	return 0;
 }
 
@@ -575,27 +724,56 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master)
 	u16 hcnt, lcnt;
 	u32 scl_timing;
 
-	core_rate = clk_get_rate(master->core_clk);
-	if (!core_rate)
-		return -EINVAL;
+	core_rate = master->timing.core_rate;
+	core_period = master->timing.core_period;
 
-	core_period = DIV_ROUND_UP(1000000000, core_rate);
-
-	lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FMP_TLOW_MIN_NS, core_period);
-	hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_PLUS_SCL_RATE) - lcnt;
-	scl_timing = SCL_I2C_FMP_TIMING_HCNT(hcnt) |
-		     SCL_I2C_FMP_TIMING_LCNT(lcnt);
+	calc_i2c_clk(master, I3C_BUS_I2C_FM_PLUS_SCL_RATE, &hcnt, &lcnt);
+	scl_timing = FIELD_PREP(SCL_I2C_FMP_TIMING_HCNT, hcnt) |
+		     FIELD_PREP(SCL_I2C_FMP_TIMING_LCNT, lcnt);
 	writel(scl_timing, master->regs + SCL_I2C_FMP_TIMING);
 
-	lcnt = DIV_ROUND_UP(I3C_BUS_I2C_FM_TLOW_MIN_NS, core_period);
-	hcnt = DIV_ROUND_UP(core_rate, I3C_BUS_I2C_FM_SCL_RATE) - lcnt;
-	scl_timing = SCL_I2C_FM_TIMING_HCNT(hcnt) |
-		     SCL_I2C_FM_TIMING_LCNT(lcnt);
+	calc_i2c_clk(master, master->base.bus.scl_rate.i2c, &hcnt, &lcnt);
+	scl_timing = FIELD_PREP(SCL_I2C_FM_TIMING_HCNT, hcnt) |
+		     FIELD_PREP(SCL_I2C_FM_TIMING_LCNT, lcnt);
 	writel(scl_timing, master->regs + SCL_I2C_FM_TIMING);
 
-	writel(BUS_I3C_MST_FREE(lcnt), master->regs + BUS_FREE_TIMING);
-	writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_I2C_SLAVE_PRESENT,
-	       master->regs + DEVICE_CTRL);
+	return 0;
+}
+
+static int dw_i3c_bus_clk_cfg(struct i3c_master_controller *m)
+{
+	struct dw_i3c_master *master = to_dw_i3c_master(m);
+	struct i3c_bus *bus = i3c_master_get_bus(m);
+	int ret;
+	u16 lcnt;
+
+	ret = dw_i2c_clk_cfg(master);
+	if (ret)
+		return ret;
+
+	ret = dw_i3c_clk_cfg(master);
+	if (ret)
+		return ret;
+
+	/*
+	 * I3C register 0xd4[15:0] BUS_FREE_TIMING used to control several parameters:
+	 * - bus free time between a STOP condition and a START condition
+	 *
+	 * The constraints of these parameters differ in various bus contexts:
+	 * MIPI I3C, pure bus : BUS_FREE_TIMING = I3C PP SCL low period
+	 * MIPI I3C, mixed bus: BUS_FREE_TIMING = I2C FM SCL low period
+	 */
+	if (bus->mode == I3C_BUS_MODE_PURE) {
+		lcnt = FIELD_GET(SCL_I3C_TIMING_LCNT,
+				 readl(master->regs + SCL_I3C_PP_TIMING));
+	} else {
+		writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_I2C_SLAVE_PRESENT,
+		       master->regs + DEVICE_CTRL);
+		lcnt = FIELD_GET(SCL_I2C_FM_TIMING_LCNT,
+				 readl(master->regs + SCL_I2C_FM_TIMING));
+	}
+
+	writel(FIELD_PREP(BUS_I3C_MST_FREE, lcnt), master->regs + BUS_FREE_TIMING);
 
 	return 0;
 }
@@ -603,7 +781,6 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master)
 static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
 {
 	struct dw_i3c_master *master = to_dw_i3c_master(m);
-	struct i3c_bus *bus = i3c_master_get_bus(m);
 	struct i3c_device_info info = { };
 	u32 thld_ctrl;
 	int ret;
@@ -612,21 +789,11 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
 	if (ret)
 		return ret;
 
-	switch (bus->mode) {
-	case I3C_BUS_MODE_MIXED_FAST:
-	case I3C_BUS_MODE_MIXED_LIMITED:
-		ret = dw_i2c_clk_cfg(master);
-		if (ret)
-			return ret;
-		fallthrough;
-	case I3C_BUS_MODE_PURE:
-		ret = dw_i3c_clk_cfg(master);
-		if (ret)
-			return ret;
-		break;
-	default:
-		return -EINVAL;
-	}
+	spin_lock_init(&master->devs_lock);
+
+	ret = dw_i3c_bus_clk_cfg(m);
+	if (ret)
+		return ret;
 
 	thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL);
 	thld_ctrl &= ~(QUEUE_THLD_CTRL_RESP_BUF_MASK |
@@ -682,6 +849,7 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master,
 {
 	struct dw_i3c_xfer *xfer;
 	struct dw_i3c_cmd *cmd;
+	u32 sda_lvl_pre, sda_lvl_post;
 	int ret, pos = 0;
 
 	if (ccc->id & I3C_CCC_DIRECT) {
@@ -707,9 +875,21 @@ static int dw_i3c_ccc_set(struct dw_i3c_master *master,
 		      COMMAND_PORT_TOC |
 		      COMMAND_PORT_ROC;
 
+	sda_lvl_pre = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+				readl(master->regs + PRESENT_STATE));
 	dw_i3c_master_enqueue_xfer(master, xfer);
-	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
+	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT)) {
+		dw_i3c_master_enter_halt(master, true);
 		dw_i3c_master_dequeue_xfer(master, xfer);
+		sda_lvl_post = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+					 readl(master->regs + PRESENT_STATE));
+		if (sda_lvl_pre == 0 && sda_lvl_post == 0) {
+			dev_warn(&master->base.dev,
+				 "SDA stuck low! Try to recover the bus...\n");
+			master->platform_ops->bus_recovery(master);
+		}
+		dw_i3c_master_exit_halt(master);
+	}
 
 	ret = xfer->ret;
 	if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK)
@@ -724,6 +904,7 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
 {
 	struct dw_i3c_xfer *xfer;
 	struct dw_i3c_cmd *cmd;
+	u32 sda_lvl_pre, sda_lvl_post;
 	int ret, pos;
 
 	pos = dw_i3c_master_get_addr_pos(master, ccc->dests[0].addr);
@@ -748,9 +929,21 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
 		      COMMAND_PORT_TOC |
 		      COMMAND_PORT_ROC;
 
+	sda_lvl_pre = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+				readl(master->regs + PRESENT_STATE));
 	dw_i3c_master_enqueue_xfer(master, xfer);
-	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
+	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT)) {
+		dw_i3c_master_enter_halt(master, true);
 		dw_i3c_master_dequeue_xfer(master, xfer);
+		sda_lvl_post = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+					 readl(master->regs + PRESENT_STATE));
+		if (sda_lvl_pre == 0 && sda_lvl_post == 0) {
+			dev_warn(&master->base.dev,
+				 "SDA stuck low! Try to recover the bus...\n");
+			master->platform_ops->bus_recovery(master);
+		}
+		dw_i3c_master_exit_halt(master);
+	}
 
 	ret = xfer->ret;
 	if (xfer->cmds[0].error == RESPONSE_ERROR_IBA_NACK)
@@ -782,7 +975,7 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
 	struct dw_i3c_master *master = to_dw_i3c_master(m);
 	struct dw_i3c_xfer *xfer;
 	struct dw_i3c_cmd *cmd;
-	u32 olddevs, newdevs;
+	u32 olddevs, newdevs, sda_lvl_pre, sda_lvl_post;
 	u8 p, last_addr = 0;
 	int ret, pos;
 
@@ -825,9 +1018,21 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
 		      COMMAND_PORT_TOC |
 		      COMMAND_PORT_ROC;
 
+	sda_lvl_pre = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+				readl(master->regs + PRESENT_STATE));
 	dw_i3c_master_enqueue_xfer(master, xfer);
-	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
+	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT)) {
+		dw_i3c_master_enter_halt(master, true);
 		dw_i3c_master_dequeue_xfer(master, xfer);
+		sda_lvl_post = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+					 readl(master->regs + PRESENT_STATE));
+		if (sda_lvl_pre == 0 && sda_lvl_post == 0) {
+			dev_warn(&master->base.dev,
+				 "SDA stuck low! Try to recover the bus...\n");
+			master->platform_ops->bus_recovery(master);
+		}
+		dw_i3c_master_exit_halt(master);
+	}
 
 	newdevs = GENMASK(master->maxdevs - cmd->rx_len - 1, 0);
 	newdevs &= ~olddevs;
@@ -835,6 +1040,16 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
 	for (pos = 0; pos < master->maxdevs; pos++) {
 		if (newdevs & BIT(pos))
 			i3c_master_add_i3c_dev_locked(m, master->devs[pos].addr);
+
+		/* cleanup the free HW DATs */
+		if (master->free_pos & BIT(pos)) {
+			u32 dat = DEV_ADDR_TABLE_SIR_REJECT |
+				  DEV_ADDR_TABLE_MR_REJECT;
+
+			writel(dat,
+			       master->regs +
+			       DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
+		}
 	}
 
 	dw_i3c_master_free_xfer(xfer);
@@ -851,6 +1066,7 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 	struct dw_i3c_master *master = to_dw_i3c_master(m);
 	unsigned int nrxwords = 0, ntxwords = 0;
 	struct dw_i3c_xfer *xfer;
+	u32 sda_lvl_pre, sda_lvl_post;
 	int i, ret = 0;
 
 	if (!i3c_nxfers)
@@ -901,9 +1117,21 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 			cmd->cmd_lo |= COMMAND_PORT_TOC;
 	}
 
+	sda_lvl_pre = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+				readl(master->regs + PRESENT_STATE));
 	dw_i3c_master_enqueue_xfer(master, xfer);
-	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
+	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT)) {
+		dw_i3c_master_enter_halt(master, true);
 		dw_i3c_master_dequeue_xfer(master, xfer);
+		sda_lvl_post = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+					 readl(master->regs + PRESENT_STATE));
+		if (sda_lvl_pre == 0 && sda_lvl_post == 0) {
+			dev_warn(&master->base.dev,
+				 "SDA stuck low! Try to recover the bus...\n");
+			master->platform_ops->bus_recovery(master);
+		}
+		dw_i3c_master_exit_halt(master);
+	}
 
 	for (i = 0; i < i3c_nxfers; i++) {
 		struct dw_i3c_cmd *cmd = &xfer->cmds[i];
@@ -1002,6 +1230,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 	struct dw_i3c_master *master = to_dw_i3c_master(m);
 	unsigned int nrxwords = 0, ntxwords = 0;
 	struct dw_i3c_xfer *xfer;
+	u32 sda_lvl_pre, sda_lvl_post;
 	int i, ret = 0;
 
 	if (!i2c_nxfers)
@@ -1021,6 +1250,12 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 	    nrxwords > master->caps.datafifodepth)
 		return -ENOTSUPP;
 
+	if (ntxwords == 0 && nrxwords == 0) {
+		dev_warn(&master->base.dev,
+			 "Transfers w/o data bytes are not supported");
+		return -EOPNOTSUPP;
+	}
+
 	xfer = dw_i3c_master_alloc_xfer(master, i2c_nxfers);
 	if (!xfer)
 		return -ENOMEM;
@@ -1048,9 +1283,21 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 			cmd->cmd_lo |= COMMAND_PORT_TOC;
 	}
 
+	sda_lvl_pre = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+				readl(master->regs + PRESENT_STATE));
 	dw_i3c_master_enqueue_xfer(master, xfer);
-	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT))
+	if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT)) {
+		dw_i3c_master_enter_halt(master, true);
 		dw_i3c_master_dequeue_xfer(master, xfer);
+		sda_lvl_post = FIELD_GET(SDA_LINE_SIGNAL_LEVEL,
+					 readl(master->regs + PRESENT_STATE));
+		if (sda_lvl_pre == 0 && sda_lvl_post == 0) {
+			dev_warn(&master->base.dev,
+				 "SDA stuck low! Try to recover the bus...\n");
+			master->platform_ops->bus_recovery(master);
+		}
+		dw_i3c_master_exit_halt(master);
+	}
 
 	ret = xfer->ret;
 	dw_i3c_master_free_xfer(xfer);
@@ -1272,8 +1519,10 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
 	struct i3c_ibi_slot *slot;
 	struct i3c_dev_desc *dev;
 	unsigned long flags;
+	u32 state;
 	u8 addr, len;
 	int idx;
+	bool terminate_ibi = false;
 
 	addr = IBI_QUEUE_IBI_ADDR(status);
 	len = IBI_QUEUE_STATUS_DATA_LEN(status);
@@ -1316,6 +1565,7 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
 		dev_dbg_ratelimited(&master->base.dev,
 				    "IBI payload len %d greater than max %d\n",
 				    len, dev->ibi->max_payload_len);
+		terminate_ibi = true;
 		goto err_drain;
 	}
 
@@ -1331,6 +1581,9 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
 
 err_drain:
 	dw_i3c_master_drain_ibi_queue(master, len);
+	state = FIELD_GET(CM_TFR_STS, readl(master->regs + PRESENT_STATE));
+	if (terminate_ibi && state == CM_TFR_STS_MASTER_SERV_IBI)
+		master->platform_ops->gen_tbits_in(master);
 
 	spin_unlock_irqrestore(&master->devs_lock, flags);
 }
@@ -1343,15 +1596,30 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master)
 {
 	unsigned int i, len, n_ibis;
 	u32 reg;
+	int ret;
 
 	reg = readl(master->regs + QUEUE_STATUS_LEVEL);
 	n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg);
 	if (!n_ibis)
 		return;
 
+	if (n_ibis > 16) {
+		dev_err(&master->base.dev,
+			"The n_ibis %d surpasses the tolerance level for the IBI buffer\n",
+			n_ibis);
+		goto ibi_fifo_clear;
+	}
+
 	for (i = 0; i < n_ibis; i++) {
 		reg = readl(master->regs + IBI_QUEUE_STATUS);
 
+		if (reg & IBI_QUEUE_STATUS_RSP_NACK) {
+			dev_dbg_ratelimited(&master->base.dev,
+					    "Nacked IBI from non-requested dev addr %02lx\n",
+					    IBI_QUEUE_IBI_ADDR(reg));
+			goto ibi_fifo_clear;
+		}
+
 		if (IBI_TYPE_SIRQ(reg)) {
 			dw_i3c_master_handle_ibi_sir(master, reg);
 		} else if (IBI_TYPE_HJ(reg)) {
@@ -1364,6 +1632,19 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master)
 			dw_i3c_master_drain_ibi_queue(master, len);
 		}
 	}
+
+	return;
+
+ibi_fifo_clear:
+	dw_i3c_master_enter_halt(master, true);
+	writel(RESET_CTRL_IBI_QUEUE, master->regs + RESET_CTRL);
+	ret = readl_poll_timeout_atomic(master->regs + RESET_CTRL, reg, !reg,
+					10, 1000000);
+	if (ret)
+		dev_err(&master->base.dev,
+			"Timeout waiting for IBI FIFO reset\n");
+
+	dw_i3c_master_exit_halt(master);
 }
 
 static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
@@ -1378,11 +1659,14 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
 		return IRQ_NONE;
 	}
 
-	spin_lock(&master->xferqueue.lock);
-	dw_i3c_master_end_xfer_locked(master, status);
-	if (status & INTR_TRANSFER_ERR_STAT)
-		writel(INTR_TRANSFER_ERR_STAT, master->regs + INTR_STATUS);
-	spin_unlock(&master->xferqueue.lock);
+	if (status & INTR_RESP_READY_STAT ||
+	    status & INTR_TRANSFER_ERR_STAT) {
+		spin_lock(&master->xferqueue.lock);
+		dw_i3c_master_end_xfer_locked(master, status);
+		if (status & INTR_TRANSFER_ERR_STAT)
+			writel(INTR_TRANSFER_ERR_STAT, master->regs + INTR_STATUS);
+		spin_unlock(&master->xferqueue.lock);
+	}
 
 	if (status & INTR_IBI_THLD_STAT)
 		dw_i3c_master_irq_handle_ibis(master);
@@ -1439,11 +1723,70 @@ static void dw_i3c_platform_set_dat_ibi_nop(struct dw_i3c_master *i3c,
 {
 }
 
+static void dw_i3c_gen_target_reset_pattern_nop(struct dw_i3c_master *i3c)
+{
+}
+
+static void dw_i3c_gen_tbits_in_nop(struct dw_i3c_master *i3c)
+{
+}
+
+static int dw_i3c_bus_recovery_nop(struct dw_i3c_master *i3c)
+{
+	return 0;
+}
+
 static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = {
 	.init = dw_i3c_platform_init_nop,
 	.set_dat_ibi = dw_i3c_platform_set_dat_ibi_nop,
+	.gen_target_reset_pattern = dw_i3c_gen_target_reset_pattern_nop,
+	.gen_tbits_in = dw_i3c_gen_tbits_in_nop,
+	.bus_recovery = dw_i3c_bus_recovery_nop,
 };
 
+static int dw_i3c_of_populate_bus_timing(struct dw_i3c_master *master,
+					 struct device_node *np)
+{
+	u32 val, reg, sda_tx_hold_ns;
+
+	master->timing.core_rate = clk_get_rate(master->core_clk);
+	if (!master->timing.core_rate) {
+		dev_err(&master->base.dev, "core clock rate not found\n");
+		return -EINVAL;
+	}
+
+	/* core_period is in nanosecond */
+	master->timing.core_period =
+		DIV_ROUND_UP(1000000000, master->timing.core_rate);
+
+	/* Parse configurations from the device tree */
+	if (!of_property_read_u32(np, "i3c-pp-scl-hi-period-ns", &val))
+		master->timing.i3c_pp_scl_high = val;
+
+	if (!of_property_read_u32(np, "i3c-pp-scl-lo-period-ns", &val))
+		master->timing.i3c_pp_scl_low = val;
+
+	if (!of_property_read_u32(np, "i3c-od-scl-hi-period-ns", &val))
+		master->timing.i3c_od_scl_high = val;
+
+	if (!of_property_read_u32(np, "i3c-od-scl-lo-period-ns", &val))
+		master->timing.i3c_od_scl_low = val;
+
+	sda_tx_hold_ns = SDA_TX_HOLD_MIN * master->timing.core_period;
+	if (!of_property_read_u32(np, "sda-tx-hold-ns", &val))
+		sda_tx_hold_ns = val;
+
+	val = clamp((u32)DIV_ROUND_CLOSEST(sda_tx_hold_ns,
+					   master->timing.core_period),
+		    (u32)SDA_TX_HOLD_MIN, (u32)SDA_TX_HOLD_MAX);
+	reg = readl(master->regs + SDA_HOLD_SWITCH_DLY_TIMING);
+	reg &= ~SDA_TX_HOLD;
+	reg |= FIELD_PREP(SDA_TX_HOLD, val);
+	writel(reg, master->regs + SDA_HOLD_SWITCH_DLY_TIMING);
+
+	return 0;
+}
+
 static void dw_i3c_hj_work(struct work_struct *work)
 {
 	struct dw_i3c_master *master =
@@ -1456,6 +1799,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
 			struct platform_device *pdev)
 {
 	const struct i3c_master_controller_ops *ops;
+	struct device_node *np;
 	int ret, irq;
 
 	if (!master->platform_ops)
@@ -1493,6 +1837,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
 
 	platform_set_drvdata(pdev, master);
 
+	np = pdev->dev.of_node;
+	ret = dw_i3c_of_populate_bus_timing(master, np);
+	if (ret)
+		goto err_assert_rst;
+
 	/* Information regarding the FIFOs/QUEUEs depth */
 	ret = readl(master->regs + QUEUE_STATUS_LEVEL);
 	master->caps.cmdfifodepth = QUEUE_STATUS_LEVEL_CMD(ret);
@@ -1514,6 +1863,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
 	if (ret)
 		goto err_assert_rst;
 
+	dw_i3c_master_set_iba(master, true);
+
 	return 0;
 
 err_assert_rst:
diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h
index 4ab94aa72252..4c41bbd6490a 100644
--- a/drivers/i3c/master/dw-i3c-master.h
+++ b/drivers/i3c/master/dw-i3c-master.h
@@ -58,6 +58,15 @@ struct dw_i3c_master {
 	/* platform-specific data */
 	const struct dw_i3c_platform_ops *platform_ops;
 
+	struct {
+		unsigned long core_rate;
+		unsigned long core_period;
+		u32 i3c_od_scl_low;
+		u32 i3c_od_scl_high;
+		u32 i3c_pp_scl_low;
+		u32 i3c_pp_scl_high;
+		u32 timed_reset_scl_low_ns;
+	} timing;
 	struct work_struct hj_work;
 };
 
@@ -78,6 +87,9 @@ struct dw_i3c_platform_ops {
 	 */
 	void (*set_dat_ibi)(struct dw_i3c_master *i3c,
 			    struct i3c_dev_desc *dev, bool enable, u32 *reg);
+	void (*gen_target_reset_pattern)(struct dw_i3c_master *i3c);
+	void (*gen_tbits_in)(struct dw_i3c_master *i3c);
+	int (*bus_recovery)(struct dw_i3c_master *i3c);
 };
 
 extern int dw_i3c_common_probe(struct dw_i3c_master *master,
-- 
2.25.1



From dylan_hung at aspeedtech.com  Mon May  6 03:51:19 2024
From: dylan_hung at aspeedtech.com (Dylan Hung)
Date: Mon, 6 May 2024 18:51:19 +0800
Subject: [PATCH 1/2] [RFC] ARM: dts: aspeed-g6: Add AST2600 I3Cs
In-Reply-To: <20240506105120.3028083-1-dylan_hung@aspeedtech.com>
References: <20240506105120.3028083-1-dylan_hung@aspeedtech.com>
Message-ID: <20240506105120.3028083-2-dylan_hung@aspeedtech.com>

The AST2600 has 6 I3Cs which are based on dw-i3c along with Aspeed
proprietary enhancements.

Signed-off-by: Dylan Hung <dylan_hung at aspeedtech.com>
---
 arch/arm/boot/dts/aspeed/aspeed-g6.dtsi | 137 ++++++++++++++++++++++++
 1 file changed, 137 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
index 29f94696d8b1..d0d685747b74 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
+++ b/arch/arm/boot/dts/aspeed/aspeed-g6.dtsi
@@ -13,6 +13,12 @@ / {
 	interrupt-parent = <&gic>;
 
 	aliases {
+		i3c0 = &i3c0;
+		i3c1 = &i3c1;
+		i3c2 = &i3c2;
+		i3c3 = &i3c3;
+		i3c4 = &i3c4;
+		i3c5 = &i3c5;
 		i2c0 = &i2c0;
 		i2c1 = &i2c1;
 		i2c2 = &i2c2;
@@ -579,6 +585,13 @@ wdt4: watchdog at 1e7850c0 {
 				status = "disabled";
 			};
 
+			i3c: bus at 1e7a0000 {
+				compatible = "simple-bus";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				ranges = <0 0x1e7a0000 0x8000>;
+			};
+
 			peci0: peci-controller at 1e78b000 {
 				compatible = "aspeed,ast2600-peci";
 				reg = <0x1e78b000 0x100>;
@@ -1125,3 +1138,127 @@ i2c15: i2c-bus at 800 {
 		status = "disabled";
 	};
 };
+
+&i3c {
+	i3c_global: i3c-global {
+		compatible = "aspeed,ast2600-i3c-global", "simple-mfd", "syscon";
+		resets = <&syscon ASPEED_RESET_I3C>;
+		reg = <0x0 0x1000>;
+	};
+
+	i3c0: i3c at 2000 {
+		compatible = "aspeed,ast2600-i3c";
+		reg = <0x2000 0x1000>;
+		clocks = <&syscon ASPEED_CLK_GATE_I3C0CLK>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_i3c1_default>;
+		interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+		aspeed,global-regs = <&i3c_global 0>;
+		status = "disabled";
+
+		i2c-scl-hz = <1000000>;
+		i3c-scl-hz = <2000000>;
+		i3c-od-scl-hi-period-ns = <380>;
+		i3c-od-scl-lo-period-ns = <620>;
+		i3c-pp-scl-hi-period-ns = <250>;
+		i3c-pp-scl-lo-period-ns = <250>;
+		sda-tx-hold-ns = <70>;
+		sda-pullup-ohms = <2000>;
+	};
+
+	i3c1: i3c at 3000 {
+		compatible = "aspeed,ast2600-i3c";
+		reg = <0x3000 0x1000>;
+		clocks = <&syscon ASPEED_CLK_GATE_I3C1CLK>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_i3c2_default>;
+		interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+		aspeed,global-regs = <&i3c_global 1>;
+		status = "disabled";
+
+		i2c-scl-hz = <1000000>;
+		i3c-scl-hz = <2000000>;
+		i3c-od-scl-hi-period-ns = <380>;
+		i3c-od-scl-lo-period-ns = <620>;
+		i3c-pp-scl-hi-period-ns = <250>;
+		i3c-pp-scl-lo-period-ns = <250>;
+		sda-tx-hold-ns = <70>;
+		sda-pullup-ohms = <2000>;
+	};
+
+	i3c2: i3c at 4000 {
+		compatible = "aspeed,ast2600-i3c";
+		reg = <0x4000 0x1000>;
+		clocks = <&syscon ASPEED_CLK_GATE_I3C2CLK>;
+		interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
+		aspeed,global-regs = <&i3c_global 2>;
+		status = "disabled";
+
+		i2c-scl-hz = <1000000>;
+		i3c-scl-hz = <2000000>;
+		i3c-od-scl-hi-period-ns = <380>;
+		i3c-od-scl-lo-period-ns = <620>;
+		i3c-pp-scl-hi-period-ns = <250>;
+		i3c-pp-scl-lo-period-ns = <250>;
+		sda-tx-hold-ns = <70>;
+		sda-pullup-ohms = <2000>;
+	};
+
+	i3c3: i3c at 5000 {
+		compatible = "aspeed,ast2600-i3c";
+		reg = <0x5000 0x1000>;
+		clocks = <&syscon ASPEED_CLK_GATE_I3C3CLK>;
+		interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
+		aspeed,global-regs = <&i3c_global 3>;
+		status = "disabled";
+
+		i2c-scl-hz = <1000000>;
+		i3c-scl-hz = <2000000>;
+		i3c-od-scl-hi-period-ns = <380>;
+		i3c-od-scl-lo-period-ns = <620>;
+		i3c-pp-scl-hi-period-ns = <250>;
+		i3c-pp-scl-lo-period-ns = <250>;
+		sda-tx-hold-ns = <70>;
+		sda-pullup-ohms = <2000>;
+	};
+
+	i3c4: i3c at 6000 {
+		compatible = "aspeed,ast2600-i3c";
+		reg = <0x6000 0x1000>;
+		clocks = <&syscon ASPEED_CLK_GATE_I3C4CLK>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_i3c5_default>;
+		interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
+		aspeed,global-regs = <&i3c_global 4>;
+		status = "disabled";
+
+		i2c-scl-hz = <1000000>;
+		i3c-scl-hz = <2000000>;
+		i3c-od-scl-hi-period-ns = <380>;
+		i3c-od-scl-lo-period-ns = <620>;
+		i3c-pp-scl-hi-period-ns = <250>;
+		i3c-pp-scl-lo-period-ns = <250>;
+		sda-tx-hold-ns = <70>;
+		sda-pullup-ohms = <2000>;
+	};
+
+	i3c5: i3c at 7000 {
+		compatible = "aspeed,ast2600-i3c";
+		reg = <0x7000 0x1000>;
+		clocks = <&syscon ASPEED_CLK_GATE_I3C5CLK>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_i3c6_default>;
+		interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
+		aspeed,global-regs = <&i3c_global 5>;
+		status = "disabled";
+
+		i2c-scl-hz = <1000000>;
+		i3c-scl-hz = <2000000>;
+		i3c-od-scl-hi-period-ns = <380>;
+		i3c-od-scl-lo-period-ns = <620>;
+		i3c-pp-scl-hi-period-ns = <250>;
+		i3c-pp-scl-lo-period-ns = <250>;
+		sda-tx-hold-ns = <70>;
+		sda-pullup-ohms = <2000>;
+	};
+};
-- 
2.25.1



From jk at codeconstruct.com.au  Mon May  6 06:46:50 2024
From: jk at codeconstruct.com.au (Jeremy Kerr)
Date: Mon, 06 May 2024 21:46:50 +0800
Subject: [PATCH 0/2] [RFC] i3c: ast2600: Add MCTP-over-I3C support
In-Reply-To: <20240506105120.3028083-1-dylan_hung@aspeedtech.com>
References: <20240506105120.3028083-1-dylan_hung@aspeedtech.com>
Message-ID: <42ec2da02cc1260fd1c05bdeb620f770c8ecc767.camel@codeconstruct.com.au>

Hi Dylan,

> Add Aspeed AST2600 I3C controllers and add minimal changes to
> validate
> the functionality for MCTP-over-I3C.
> 
> Dylan Hung (2):
> ? [RFC] ARM: dts: aspeed-g6: Add AST2600 I3Cs

I'd sent a similar patch earlier:

  https://lists.ozlabs.org/pipermail/linux-aspeed/2024-May/012193.html

And a v2 to account for some feedback on the dts layout:

  https://lists.ozlabs.org/pipermail/linux-aspeed/2024-May/012213.html

> ? [RFC] i3c: ast2600: Validate AST2600 I3C for MCTP-over-I3C

Looks interesting, I'll check this out soon.

Cheers,


Jeremy


From Frank.Li at nxp.com  Mon May  6 09:40:07 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Mon,  6 May 2024 12:40:07 -0400
Subject: [PATCH v2 1/3] i3c: Add comment for -EAGAIN in i3c_device_do_priv_xfers()
Message-ID: <20240506164009.21375-1-Frank.Li@nxp.com>

In accordance with I3C spec ver 1.1.1 09-Jun-2021, section: 5.1.2.2.3, if
a target requests hot join (HJ), In-Band Interrupt (IBI), or controller
role request (CRR) during the emission of an I3C address in
i3c_device_do_priv_xfers(), the target may win bus arbitration. In such
cases, it is imperative to notify the I3C client driver and retry
i3c_device_do_priv_xfers() after some delay.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Change from v1 to v2
    - new patch

 drivers/i3c/device.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c
index 1a6a8703dbc3a..b04a55a1337d4 100644
--- a/drivers/i3c/device.c
+++ b/drivers/i3c/device.c
@@ -27,6 +27,10 @@
  * This function can sleep and thus cannot be called in atomic context.
  *
  * Return: 0 in case of success, a negative error core otherwise.
+ *	   -EAGAIN: controller lost address arbitration. Target
+ *		    (IBI, HJ or controller role request) win the bus. Client
+ *		    driver need resend this 'xfers' some time later.
+ *		    See I3C spec ver 1.1.1 09-Jun-2021. Section: 5.1.2.2.3.
  */
 int i3c_device_do_priv_xfers(struct i3c_device *dev,
 			     struct i3c_priv_xfer *xfers,
-- 
2.34.1



From Frank.Li at nxp.com  Mon May  6 09:40:08 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Mon,  6 May 2024 12:40:08 -0400
Subject: [PATCH v2 2/3] i3c: master: svc: change ENXIO to EAGAIN when IBI occurs during start frame
In-Reply-To: <20240506164009.21375-1-Frank.Li@nxp.com>
References: <20240506164009.21375-1-Frank.Li@nxp.com>
Message-ID: <20240506164009.21375-2-Frank.Li@nxp.com>

svc_i3c_master_xfer() returns error ENXIO if an In-Band Interrupt (IBI)
occurs when the host starts the frame.

Change error code to EAGAIN to inform the client driver that this
situation has occurred and to try again sometime later.

Fixes: 5e5e3c92e748 ("i3c: master: svc: fix wrong data return when IBI happen during start frame")
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    change from v1 to v2
    - none

 drivers/i3c/master/svc-i3c-master.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index fd8de54b13667..94e4954509558 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1080,7 +1080,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	 * and yield the above events handler.
 	 */
 	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
-		ret = -ENXIO;
+		ret = -EAGAIN;
 		*actual_len = 0;
 		goto emit_stop;
 	}
-- 
2.34.1



From Frank.Li at nxp.com  Mon May  6 09:40:09 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Mon,  6 May 2024 12:40:09 -0400
Subject: [PATCH v2 3/3] i3c: master: svc: fix invalidate IBI type and miss call client IBI handler
In-Reply-To: <20240506164009.21375-1-Frank.Li@nxp.com>
References: <20240506164009.21375-1-Frank.Li@nxp.com>
Message-ID: <20240506164009.21375-3-Frank.Li@nxp.com>

In an In-Band Interrupt (IBI) handle, the code logic is as follows:

1: writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI | SVC_I3C_MCTRL_IBIRESP_AUTO,
	  master->regs + SVC_I3C_MCTRL);

2: ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
                                    SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
	...
3: ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
   ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);

SVC_I3C_MSTATUS_IBIWON may be set before step 1. Thus, step 2 will return
immediately, and the I3C controller has not sent out the 9th SCL yet.
Consequently, ibitype and ibiaddr are 0, resulting in an unknown IBI type
occurrence and missing call I3C client driver's IBI handler.

A typical case is that SVC_I3C_MSTATUS_IBIWON is set when an IBI occurs
during the controller send start frame in svc_i3c_master_xfer().

Clear SVC_I3C_MSTATUS_IBIWON before issue SVC_I3C_MCTRL_REQUEST_AUTO_IBI
to fix this issue.

Cc: stable at vger.kernel.org
Fixes: 5e5e3c92e748 ("i3c: master: svc: fix wrong data return when IBI happen during start frame")
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Change from v1 to v2
    - improve comments.

 drivers/i3c/master/svc-i3c-master.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 94e4954509558..032fe032ec433 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -415,6 +415,19 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 	int ret;
 
 	mutex_lock(&master->lock);
+	/*
+	 * IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing
+	 * readl_relaxed_poll_timeout() to return immediately. Consequently,
+	 * ibitype will be 0 since it was last updated only after the 8th SCL
+	 * cycle, leading to missed client IBI handlers.
+	 *
+	 * A typical scenario is when IBIWON occurs and bus arbitration is lost
+	 * at svc_i3c_master_priv_xfers().
+	 *
+	 * Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
+	 */
+	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
 	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
 	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
 	       SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -429,9 +442,6 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 		goto reenable_ibis;
 	}
 
-	/* Clear the interrupt status */
-	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
-
 	status = readl(master->regs + SVC_I3C_MSTATUS);
 	ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
 	ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);
-- 
2.34.1



From miquel.raynal at bootlin.com  Tue May  7 01:03:19 2024
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 7 May 2024 10:03:19 +0200
Subject: [PATCH v2 1/3] i3c: Add comment for -EAGAIN in
 i3c_device_do_priv_xfers()
In-Reply-To: <20240506164009.21375-1-Frank.Li@nxp.com>
References: <20240506164009.21375-1-Frank.Li@nxp.com>
Message-ID: <20240507100319.04d8039f@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon,  6 May 2024 12:40:07 -0400:

> In accordance with I3C spec ver 1.1.1 09-Jun-2021, section: 5.1.2.2.3, if
> a target requests hot join (HJ), In-Band Interrupt (IBI), or controller
> role request (CRR) during the emission of an I3C address in
> i3c_device_do_priv_xfers(), the target may win bus arbitration. In such
> cases, it is imperative to notify the I3C client driver and retry
> i3c_device_do_priv_xfers() after some delay.
> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>     Change from v1 to v2
>     - new patch
> 
>  drivers/i3c/device.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c
> index 1a6a8703dbc3a..b04a55a1337d4 100644
> --- a/drivers/i3c/device.c
> +++ b/drivers/i3c/device.c
> @@ -27,6 +27,10 @@
>   * This function can sleep and thus cannot be called in atomic context.
>   *
>   * Return: 0 in case of success, a negative error core otherwise.
> + *	   -EAGAIN: controller lost address arbitration. Target
> + *		    (IBI, HJ or controller role request) win the bus. Client
> + *		    driver need resend this 'xfers' some time later.

			 needs to resend the 'xfers'

> + *		    See I3C spec ver 1.1.1 09-Jun-2021. Section: 5.1.2.2.3.
>   */
>  int i3c_device_do_priv_xfers(struct i3c_device *dev,
>  			     struct i3c_priv_xfer *xfers,


With this little nit,

Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue May  7 01:03:40 2024
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 7 May 2024 10:03:40 +0200
Subject: [PATCH v2 2/3] i3c: master: svc: change ENXIO to EAGAIN when
 IBI occurs during start frame
In-Reply-To: <20240506164009.21375-2-Frank.Li@nxp.com>
References: <20240506164009.21375-1-Frank.Li@nxp.com>
	<20240506164009.21375-2-Frank.Li@nxp.com>
Message-ID: <20240507100340.674f9acc@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon,  6 May 2024 12:40:08 -0400:

> svc_i3c_master_xfer() returns error ENXIO if an In-Band Interrupt (IBI)
> occurs when the host starts the frame.
> 
> Change error code to EAGAIN to inform the client driver that this
> situation has occurred and to try again sometime later.
> 
> Fixes: 5e5e3c92e748 ("i3c: master: svc: fix wrong data return when IBI happen during start frame")
> Signed-off-by: Frank Li <Frank.Li at nxp.com>

Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue May  7 01:04:24 2024
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 7 May 2024 10:04:24 +0200
Subject: [PATCH v2 3/3] i3c: master: svc: fix invalidate IBI type and
 miss call client IBI handler
In-Reply-To: <20240506164009.21375-3-Frank.Li@nxp.com>
References: <20240506164009.21375-1-Frank.Li@nxp.com>
	<20240506164009.21375-3-Frank.Li@nxp.com>
Message-ID: <20240507100424.611181f1@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon,  6 May 2024 12:40:09 -0400:

> In an In-Band Interrupt (IBI) handle, the code logic is as follows:
> 
> 1: writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI | SVC_I3C_MCTRL_IBIRESP_AUTO,
> 	  master->regs + SVC_I3C_MCTRL);
> 
> 2: ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
>                                     SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
> 	...
> 3: ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
>    ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);
> 
> SVC_I3C_MSTATUS_IBIWON may be set before step 1. Thus, step 2 will return
> immediately, and the I3C controller has not sent out the 9th SCL yet.
> Consequently, ibitype and ibiaddr are 0, resulting in an unknown IBI type
> occurrence and missing call I3C client driver's IBI handler.
> 
> A typical case is that SVC_I3C_MSTATUS_IBIWON is set when an IBI occurs
> during the controller send start frame in svc_i3c_master_xfer().
> 
> Clear SVC_I3C_MSTATUS_IBIWON before issue SVC_I3C_MCTRL_REQUEST_AUTO_IBI
> to fix this issue.
> 
> Cc: stable at vger.kernel.org
> Fixes: 5e5e3c92e748 ("i3c: master: svc: fix wrong data return when IBI happen during start frame")
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>     Change from v1 to v2
>     - improve comments.
> 
>  drivers/i3c/master/svc-i3c-master.c | 16 +++++++++++++---
>  1 file changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 94e4954509558..032fe032ec433 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -415,6 +415,19 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
>  	int ret;
>  
>  	mutex_lock(&master->lock);
> +	/*
> +	 * IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing
> +	 * readl_relaxed_poll_timeout() to return immediately. Consequently,
> +	 * ibitype will be 0 since it was last updated only after the 8th SCL
> +	 * cycle, leading to missed client IBI handlers.
> +	 *
> +	 * A typical scenario is when IBIWON occurs and bus arbitration is lost
> +	 * at svc_i3c_master_priv_xfers().
> +	 *
> +	 * Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
> +	 */

Thanks a lot.

Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

Thanks,
Miqu?l


From alexandre.belloni at bootlin.com  Tue May  7 13:27:54 2024
From: alexandre.belloni at bootlin.com (Alexandre Belloni)
Date: Tue, 7 May 2024 22:27:54 +0200
Subject: [PATCH v2 1/3] i3c: Add comment for -EAGAIN in
 i3c_device_do_priv_xfers()
In-Reply-To: <20240506164009.21375-1-Frank.Li@nxp.com>
References: <20240506164009.21375-1-Frank.Li@nxp.com>
Message-ID: <171511365446.810643.1784657313942616005.b4-ty@bootlin.com>

On Mon, 06 May 2024 12:40:07 -0400, Frank Li wrote:
> In accordance with I3C spec ver 1.1.1 09-Jun-2021, section: 5.1.2.2.3, if
> a target requests hot join (HJ), In-Band Interrupt (IBI), or controller
> role request (CRR) during the emission of an I3C address in
> i3c_device_do_priv_xfers(), the target may win bus arbitration. In such
> cases, it is imperative to notify the I3C client driver and retry
> i3c_device_do_priv_xfers() after some delay.
> 
> [...]

Applied, thanks!

[1/3] i3c: Add comment for -EAGAIN in i3c_device_do_priv_xfers()
      https://git.kernel.org/abelloni/c/5e45614ef8ae
[2/3] i3c: master: svc: change ENXIO to EAGAIN when IBI occurs during start frame
      https://git.kernel.org/abelloni/c/75cb32046b5d
[3/3] i3c: master: svc: fix invalidate IBI type and miss call client IBI handler
      https://git.kernel.org/abelloni/c/677a7b0e3ae4

Best regards,

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


From Frank.Li at nxp.com  Tue May  7 13:43:03 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:03 -0400
Subject: [PATCH resend v9 0/8] I3C target mode support
Message-ID: <20240507204311.2898714-1-Frank.Li@nxp.com>

This  patch introduces support for I3C target mode, which is referenced
with a PCIe Endpoint system. It also establishes a configuration framework
(configfs) for the I3C target controller driver and the I3C target function
driver

Typic usage as

The user can configure the i3c-target-tty device using configfs entry. In
order to change the vendorid, the following commands can be used

        # echo 0x011b > functions/tty/func1/vendor_id
        # echo 0x1000 > functions/tty/func1/part_id
        # echo 0x6 > functions/tty/t/bcr

Binding i3c-target-tty Device to target Controller
------------------------------------------------

In order for the target function device to be useful, it has to be bound to
a I3C target controller driver. Use the configfs to bind the function
device to one of the controller driver present in the system::

        # ln -s functions/pci_epf_test/func1 controllers/44330000.i3c-target/

Host side:
        cat /dev/ttyI3C0
Taret side:
        echo abc >/dev/ttyI3C0

Notes about header files.

About Ilpo J?rvinen's header file comments, it can pass build, some header
should be included by other header files.

I added some, but I am not sure that is all needs. There may have tools,
which can help check, but I don't know.

Chagne from v2 to v3
- using 'mode' distingiush master and target.
- move svc-i3c-target.c to under master,
- built together with svc-i3c-master.c

Change from v1 to v2
- change "slave" to "target"
- include master side tty patch
- fixed dtbcheck problem
- fixed kerne-doc check warning

Some review comment may be lost since it is quite long time since v1. Now
master side dependent patches already in linux-next. So sent target side
patches with tty support again.

No sure why an additional "\r\n" appended.

Frank Li (8):
  i3c: add target mode support
  dt-bindings: i3c: svc: add proptery mode
  Documentation: i3c: Add I3C target mode controller and function
  i3c: svc: Add svc-i3c-main.c and svc-i3c.h
  i3c: target: add svc target controller support
  i3c: target: func: add tty driver
  i3c: add API i3c_dev_gettstatus_format1() to get target device status
  tty: i3c: add TTY over I3C master support

 .../bindings/i3c/silvaco,i3c-master.yaml      |  11 +-
 Documentation/driver-api/i3c/index.rst        |   1 +
 .../driver-api/i3c/target/i3c-target-cfs.rst  | 109 +++
 .../driver-api/i3c/target/i3c-target.rst      | 189 +++++
 .../driver-api/i3c/target/i3c-tty-howto.rst   | 109 +++
 Documentation/driver-api/i3c/target/index.rst |  13 +
 drivers/i3c/Kconfig                           |  31 +-
 drivers/i3c/Makefile                          |   3 +
 drivers/i3c/device.c                          |  24 +
 drivers/i3c/func/Kconfig                      |   9 +
 drivers/i3c/func/Makefile                     |   3 +
 drivers/i3c/func/tty.c                        | 481 +++++++++++
 drivers/i3c/i3c-cfs.c                         | 389 +++++++++
 drivers/i3c/internals.h                       |   1 +
 drivers/i3c/master.c                          |  26 +
 drivers/i3c/master/Makefile                   |   3 +-
 drivers/i3c/master/svc-i3c-main.c             |  80 ++
 drivers/i3c/master/svc-i3c-master.c           |  34 +-
 drivers/i3c/master/svc-i3c-target.c           | 776 ++++++++++++++++++
 drivers/i3c/master/svc-i3c.h                  |  15 +
 drivers/i3c/target.c                          | 453 ++++++++++
 drivers/tty/Kconfig                           |  13 +
 drivers/tty/Makefile                          |   1 +
 drivers/tty/i3c_tty.c                         | 432 ++++++++++
 include/linux/i3c/device.h                    |   1 +
 include/linux/i3c/target.h                    | 548 +++++++++++++
 26 files changed, 3724 insertions(+), 31 deletions(-)
 create mode 100644 Documentation/driver-api/i3c/target/i3c-target-cfs.rst
 create mode 100644 Documentation/driver-api/i3c/target/i3c-target.rst
 create mode 100644 Documentation/driver-api/i3c/target/i3c-tty-howto.rst
 create mode 100644 Documentation/driver-api/i3c/target/index.rst
 create mode 100644 drivers/i3c/func/Kconfig
 create mode 100644 drivers/i3c/func/Makefile
 create mode 100644 drivers/i3c/func/tty.c
 create mode 100644 drivers/i3c/i3c-cfs.c
 create mode 100644 drivers/i3c/master/svc-i3c-main.c
 create mode 100644 drivers/i3c/master/svc-i3c-target.c
 create mode 100644 drivers/i3c/master/svc-i3c.h
 create mode 100644 drivers/i3c/target.c
 create mode 100644 drivers/tty/i3c_tty.c
 create mode 100644 include/linux/i3c/target.h

-- 
2.34.1



From Frank.Li at nxp.com  Tue May  7 13:43:04 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:04 -0400
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <20240507204311.2898714-1-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
Message-ID: <20240507204311.2898714-2-Frank.Li@nxp.com>

Introduce a new target core layer in order to support target functions in
linux kernel. This comprises the controller library and function library.
Controller library implements functions specific to an target controller
and function library implements functions specific to an target function.

Introduce a new configfs entry to configure the target function configuring
and bind the target function with target controller.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Change from v8 to v9
    -none
    
    Change from v7 to v8
    Added missed head files
    
    Change from v5 to v6
    - fixed build error when have not CONFIG_TARGET_CONFIGFS
    | Reported-by: kernel test robot <lkp at intel.com>
    | Closes: https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-lkp at intel.com/
    
    Chagne from v4 to v5
    - add include <linux/slab.h> to fix build error
    | Reported-by: kernel test robot <lkp at intel.com>
    | Closes: https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-lkp at intel.com/
    
    Chagne from v4 to v8
    - add include <linux/slab.h> to fix build error
    | Reported-by: kernel test robot <lkp at intel.com>
    | Closes: https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-lkp at intel.com/

 drivers/i3c/Kconfig        |  28 +-
 drivers/i3c/Makefile       |   2 +
 drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
 drivers/i3c/target.c       | 453 ++++++++++++++++++++++++++++++
 include/linux/i3c/target.h | 548 +++++++++++++++++++++++++++++++++++++
 5 files changed, 1419 insertions(+), 1 deletion(-)
 create mode 100644 drivers/i3c/i3c-cfs.c
 create mode 100644 drivers/i3c/target.c
 create mode 100644 include/linux/i3c/target.h

diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index 30a441506f61c..d59a7eb83d13a 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -10,7 +10,7 @@ menuconfig I3C
 	  support for high speed transfers and native interrupt support
 	  without the need for extra pins.
 
-	  The I3C protocol also standardizes the slave device types and is
+	  The I3C protocol also standardizes the target device types and is
 	  mainly designed to communicate with sensors.
 
 	  If you want I3C support, you should say Y here and also to the
@@ -22,3 +22,29 @@ menuconfig I3C
 if I3C
 source "drivers/i3c/master/Kconfig"
 endif # I3C
+
+config I3C_TARGET
+	bool "I3C Target Support"
+	help
+	  Support I3C Target Mode.
+
+	  Enable this configuration option to support configurable I3C target.
+	  This should be enabled if the platform has a I3C controller that can
+	  operate in target mode.
+
+	  Enabling this option will build the I3C target library, which includes
+	  target controller library and target function library.
+
+	  If in doubt, say "N" to disable target support.
+
+config I3C_TARGET_CONFIGFS
+	bool "I3C Target Configfs Support"
+	depends on I3C_TARGET
+	select CONFIGFS_FS
+	help
+	  Configfs entry for target function and controller.
+
+	  This will enable the configfs entry that can be used to configure
+	  the target function and used to bind the function with a target
+	  controller.
+
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 11982efbc6d91..c275aeae8970c 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 i3c-y				:= device.o master.o
 obj-$(CONFIG_I3C)		+= i3c.o
+obj-$(CONFIG_I3C_TARGET)                += target.o
+obj-$(CONFIG_I3C_TARGET_CONFIGFS)       += i3c-cfs.o
 obj-$(CONFIG_I3C)		+= master/
diff --git a/drivers/i3c/i3c-cfs.c b/drivers/i3c/i3c-cfs.c
new file mode 100644
index 0000000000000..5178b978ebca7
--- /dev/null
+++ b/drivers/i3c/i3c-cfs.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Configfs to configure the I3C Slave
+ *
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li at nxp.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/i3c/target.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(functions_mutex);
+static struct config_group *functions_group;
+static struct config_group *controllers_group;
+
+struct i3c_target_func_group {
+	struct config_group group;
+	struct i3c_target_func *func;
+};
+
+struct i3c_target_ctrl_group {
+	struct config_group group;
+	struct i3c_target_ctrl *ctrl;
+};
+
+static inline struct i3c_target_func_group *to_i3c_target_func_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct i3c_target_func_group, group);
+}
+
+static inline struct i3c_target_ctrl_group *to_i3c_target_ctrl_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct i3c_target_ctrl_group, group);
+}
+
+static int i3c_target_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg)
+{
+	struct i3c_target_func_group *func_group = to_i3c_target_func_group(func_cfg);
+	struct i3c_target_ctrl_group *ctrl_group = to_i3c_target_ctrl_group(ctrl_cfg);
+	struct i3c_target_ctrl *ctrl = ctrl_group->ctrl;
+	struct i3c_target_func *func = func_group->func;
+	int ret;
+
+	ret = i3c_target_ctrl_add_func(ctrl, func);
+	if (ret)
+		return ret;
+
+	ret = i3c_target_func_bind(func);
+	if (ret) {
+		i3c_target_ctrl_remove_func(ctrl, func);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void i3c_target_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg)
+{
+	struct i3c_target_func_group *func_group = to_i3c_target_func_group(func_cfg->ci_parent);
+	struct i3c_target_ctrl_group *ctrl_group = to_i3c_target_ctrl_group(ctrl_cfg);
+	struct i3c_target_ctrl *ctrl = ctrl_group->ctrl;
+	struct i3c_target_func *func = func_group->func;
+
+	i3c_target_func_unbind(func);
+	i3c_target_ctrl_remove_func(ctrl, func);
+}
+
+static ssize_t i3c_target_ctrl_hotjoin_store(struct config_item *item, const char *page, size_t len)
+{
+	struct i3c_target_ctrl_group *ctrl_group = to_i3c_target_ctrl_group(item);
+	struct i3c_target_ctrl *ctrl;
+	int ret;
+
+	ctrl = ctrl_group->ctrl;
+
+	ret = i3c_target_ctrl_hotjoin(ctrl);
+	if (ret) {
+		dev_err(&ctrl->dev, "failed to hotjoin i3c target controller\n");
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+static ssize_t i3c_target_ctrl_hotjoin_show(struct config_item *item, char *page)
+{
+	return sysfs_emit(page, "%d\n", 0);
+}
+
+CONFIGFS_ATTR(i3c_target_ctrl_, hotjoin);
+
+static struct configfs_item_operations i3c_target_ctrl_item_ops = {
+	.allow_link     = i3c_target_ctrl_func_link,
+	.drop_link      = i3c_target_ctrl_func_unlink,
+};
+
+static struct configfs_attribute *i3c_target_ctrl_attrs[] = {
+	&i3c_target_ctrl_attr_hotjoin,
+	NULL,
+};
+
+static const struct config_item_type i3c_target_ctrl_type = {
+	.ct_item_ops    = &i3c_target_ctrl_item_ops,
+	.ct_attrs	= i3c_target_ctrl_attrs,
+	.ct_owner       = THIS_MODULE,
+};
+
+/**
+ * i3c_target_cfs_add_ctrl_group() - add I3C target controller group
+ * @ctrl: I3C target controller device
+ *
+ * Return: Pointer to struct config_group
+ */
+struct config_group *i3c_target_cfs_add_ctrl_group(struct i3c_target_ctrl *ctrl)
+{
+	struct i3c_target_ctrl_group *ctrl_group;
+	struct config_group *group;
+	int ret;
+
+	ctrl_group = kzalloc(sizeof(*ctrl_group), GFP_KERNEL);
+	if (!ctrl_group) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	group = &ctrl_group->group;
+
+	config_group_init_type_name(group, dev_name(&ctrl->dev), &i3c_target_ctrl_type);
+	ret = configfs_register_group(controllers_group, group);
+	if (ret) {
+		pr_err("failed to register configfs group for %s\n", dev_name(&ctrl->dev));
+		goto err_register_group;
+	}
+
+	ctrl_group->ctrl = ctrl;
+
+	return group;
+
+err_register_group:
+	kfree(ctrl_group);
+
+err:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(i3c_target_cfs_add_ctrl_group);
+
+/**
+ * i3c_target_cfs_remove_ctrl_group() - remove I3C target controller group
+ * @group: the group to be removed
+ */
+void i3c_target_cfs_remove_ctrl_group(struct config_group *group)
+{
+	struct i3c_target_ctrl_group *ctrl_group;
+
+	if (!group)
+		return;
+
+	ctrl_group = container_of(group, struct i3c_target_ctrl_group, group);
+	i3c_target_ctrl_put(ctrl_group->ctrl);
+	configfs_unregister_group(&ctrl_group->group);
+	kfree(ctrl_group);
+}
+EXPORT_SYMBOL(i3c_target_cfs_remove_ctrl_group);
+
+#define I3C_SLAVE_ATTR_R(_name)                                                \
+static ssize_t i3c_target_func_##_name##_show(struct config_item *item, char *page)    \
+{                                                                              \
+	struct i3c_target_func *func = to_i3c_target_func_group(item)->func;                     \
+	return sysfs_emit(page, "0x%04x\n", func->_name);               \
+}
+
+#define I3C_SLAVE_ATTR_W(_name, _u)                                            \
+static ssize_t i3c_target_func_##_name##_store(struct config_item *item,               \
+				       const char *page, size_t len)           \
+{                                                                              \
+	_u val;                                                               \
+	struct i3c_target_func *func = to_i3c_target_func_group(item)->func;                     \
+	if (kstrto##_u(page, 0, &val) < 0)                                      \
+		return -EINVAL;                                                \
+	func->_name = val;                                              \
+	return len;                                                            \
+}
+
+I3C_SLAVE_ATTR_R(vendor_id);
+I3C_SLAVE_ATTR_W(vendor_id, u16);
+CONFIGFS_ATTR(i3c_target_func_, vendor_id);
+
+I3C_SLAVE_ATTR_R(vendor_info);
+I3C_SLAVE_ATTR_W(vendor_info, u16);
+CONFIGFS_ATTR(i3c_target_func_, vendor_info);
+
+I3C_SLAVE_ATTR_R(part_id);
+I3C_SLAVE_ATTR_W(part_id, u16);
+CONFIGFS_ATTR(i3c_target_func_, part_id);
+
+I3C_SLAVE_ATTR_R(instance_id);
+I3C_SLAVE_ATTR_W(instance_id, u8);
+CONFIGFS_ATTR(i3c_target_func_, instance_id);
+
+I3C_SLAVE_ATTR_R(ext_id);
+I3C_SLAVE_ATTR_W(ext_id, u16);
+CONFIGFS_ATTR(i3c_target_func_, ext_id);
+
+I3C_SLAVE_ATTR_R(max_write_len);
+I3C_SLAVE_ATTR_W(max_write_len, u16);
+CONFIGFS_ATTR(i3c_target_func_, max_write_len);
+
+I3C_SLAVE_ATTR_R(max_read_len);
+I3C_SLAVE_ATTR_W(max_read_len, u16);
+CONFIGFS_ATTR(i3c_target_func_, max_read_len);
+
+I3C_SLAVE_ATTR_R(bcr);
+I3C_SLAVE_ATTR_W(bcr, u8);
+CONFIGFS_ATTR(i3c_target_func_, bcr);
+
+I3C_SLAVE_ATTR_R(dcr);
+I3C_SLAVE_ATTR_W(dcr, u8);
+CONFIGFS_ATTR(i3c_target_func_, dcr);
+
+static struct configfs_attribute *i3c_target_func_attrs[] = {
+	&i3c_target_func_attr_vendor_id,
+	&i3c_target_func_attr_vendor_info,
+	&i3c_target_func_attr_part_id,
+	&i3c_target_func_attr_instance_id,
+	&i3c_target_func_attr_ext_id,
+	&i3c_target_func_attr_max_write_len,
+	&i3c_target_func_attr_max_read_len,
+	&i3c_target_func_attr_bcr,
+	&i3c_target_func_attr_dcr,
+	NULL,
+};
+
+static const struct config_item_type i3c_target_func_type = {
+	.ct_attrs       = i3c_target_func_attrs,
+	.ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *i3c_target_func_make(struct config_group *group, const char *name)
+{
+	struct i3c_target_func_group *func_group;
+	struct i3c_target_func *func;
+	int err;
+
+	func_group = kzalloc(sizeof(*func_group), GFP_KERNEL);
+	if (!func_group)
+		return ERR_PTR(-ENOMEM);
+
+	config_group_init_type_name(&func_group->group, name, &i3c_target_func_type);
+
+	func = i3c_target_func_create(group->cg_item.ci_name, name);
+	if (IS_ERR(func)) {
+		pr_err("failed to create i3c target function device\n");
+		err = -EINVAL;
+		goto free_group;
+	}
+
+	func->group = &func_group->group;
+
+	func_group->func = func;
+
+	return &func_group->group;
+
+free_group:
+	kfree(func_group);
+
+	return ERR_PTR(err);
+}
+
+static void i3c_target_func_drop(struct config_group *group, struct config_item *item)
+{
+	config_item_put(item);
+}
+
+static struct configfs_group_operations i3c_target_func_group_ops = {
+	.make_group     = &i3c_target_func_make,
+	.drop_item      = &i3c_target_func_drop,
+};
+
+static const struct config_item_type i3c_target_func_group_type = {
+	.ct_group_ops   = &i3c_target_func_group_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+/**
+ * i3c_target_cfs_add_func_group() - add I3C target function group
+ * @name: group name
+ *
+ * Return: Pointer to struct config_group
+ */
+struct config_group *i3c_target_cfs_add_func_group(const char *name)
+{
+	struct config_group *group;
+
+	group = configfs_register_default_group(functions_group, name,
+						&i3c_target_func_group_type);
+	if (IS_ERR(group))
+		pr_err("failed to register configfs group for %s function\n",
+		       name);
+
+	return group;
+}
+EXPORT_SYMBOL(i3c_target_cfs_add_func_group);
+
+/**
+ * i3c_target_cfs_remove_func_group() - add I3C target function group
+ * @group: group to be removed
+ */
+void i3c_target_cfs_remove_func_group(struct config_group *group)
+{
+	if (IS_ERR_OR_NULL(group))
+		return;
+
+	configfs_unregister_default_group(group);
+}
+EXPORT_SYMBOL(i3c_target_cfs_remove_func_group);
+
+static const struct config_item_type i3c_target_controllers_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static const struct config_item_type i3c_target_functions_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static const struct config_item_type i3c_target_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static struct configfs_subsystem i3c_target_cfs_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "i3c_target",
+			.ci_type = &i3c_target_type,
+		},
+	},
+	.su_mutex = __MUTEX_INITIALIZER(i3c_target_cfs_subsys.su_mutex),
+};
+
+static int __init i3c_target_cfs_init(void)
+{
+	int ret;
+	struct config_group *root = &i3c_target_cfs_subsys.su_group;
+
+	config_group_init(root);
+
+	ret = configfs_register_subsystem(&i3c_target_cfs_subsys);
+	if (ret) {
+		pr_err("Error %d while registering subsystem %s\n",
+		       ret, root->cg_item.ci_namebuf);
+		goto err;
+	}
+
+	functions_group = configfs_register_default_group(root, "functions",
+							  &i3c_target_functions_type);
+	if (IS_ERR(functions_group)) {
+		ret = PTR_ERR(functions_group);
+		pr_err("Error %d while registering functions group\n",
+		       ret);
+		goto err_functions_group;
+	}
+
+	controllers_group =
+		configfs_register_default_group(root, "controllers",
+						&i3c_target_controllers_type);
+	if (IS_ERR(controllers_group)) {
+		ret = PTR_ERR(controllers_group);
+		pr_err("Error %d while registering controllers group\n",
+		       ret);
+		goto err_controllers_group;
+	}
+
+	return 0;
+
+err_controllers_group:
+	configfs_unregister_default_group(functions_group);
+
+err_functions_group:
+	configfs_unregister_subsystem(&i3c_target_cfs_subsys);
+
+err:
+	return ret;
+}
+module_init(i3c_target_cfs_init);
+
+MODULE_DESCRIPTION("I3C FUNC CONFIGFS");
+MODULE_AUTHOR("Frank Li <Frank.Li at nxp.com>");
diff --git a/drivers/i3c/target.c b/drivers/i3c/target.c
new file mode 100644
index 0000000000000..a2738b8341f7a
--- /dev/null
+++ b/drivers/i3c/target.c
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * configfs to configure the I3C Slave
+ *
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li at nxp.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i3c/target.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(func_lock);
+static struct class *i3c_target_ctrl_class;
+
+static void i3c_target_func_dev_release(struct device *dev)
+{
+	struct i3c_target_func *func = to_i3c_target_func(dev);
+
+	kfree(func->name);
+	kfree(func);
+}
+
+static const struct device_type i3c_target_func_type = {
+	.release        = i3c_target_func_dev_release,
+};
+
+static int i3c_target_func_match_driver(struct device *dev, struct device_driver *drv)
+{
+	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
+}
+
+static int i3c_target_func_device_probe(struct device *dev)
+{
+	struct i3c_target_func *func = to_i3c_target_func(dev);
+	struct i3c_target_func_driver *driver = to_i3c_target_func_driver(dev->driver);
+
+	if (!driver->probe)
+		return -ENODEV;
+
+	func->driver = driver;
+
+	return driver->probe(func);
+}
+
+static void i3c_target_func_device_remove(struct device *dev)
+{
+	struct i3c_target_func *func = to_i3c_target_func(dev);
+	struct i3c_target_func_driver *driver = to_i3c_target_func_driver(dev->driver);
+
+	if (driver->remove)
+		driver->remove(func);
+	func->driver = NULL;
+}
+
+static const struct bus_type i3c_target_func_bus_type = {
+	.name = "i3c_target_func",
+	.probe = i3c_target_func_device_probe,
+	.remove = i3c_target_func_device_remove,
+	.match = i3c_target_func_match_driver,
+};
+
+static void i3c_target_ctrl_release(struct device *dev)
+{
+	kfree(to_i3c_target_ctrl(dev));
+}
+
+static void devm_i3c_target_ctrl_release(struct device *dev, void *res)
+{
+	struct i3c_target_ctrl *ctrl = *(struct i3c_target_ctrl **)res;
+
+	i3c_target_ctrl_destroy(ctrl);
+}
+
+struct i3c_target_ctrl *
+__devm_i3c_target_ctrl_create(struct device *dev, const struct i3c_target_ctrl_ops *ops,
+			     struct module *owner)
+{
+	struct i3c_target_ctrl **ptr, *ctrl;
+
+	ptr = devres_alloc(devm_i3c_target_ctrl_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ctrl = __i3c_target_ctrl_create(dev, ops, owner);
+	if (!IS_ERR(ctrl)) {
+		*ptr = ctrl;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return ctrl;
+}
+
+static int devm_i3c_target_ctrl_match(struct device *dev, void *res, void *match_data)
+{
+	struct i3c_target_ctrl **ptr = res;
+
+	return *ptr == match_data;
+}
+
+/**
+ * __i3c_target_ctrl_create() - create a new target controller device
+ * @dev: device that is creating the new target controller
+ * @ops: function pointers for performing target controller  operations
+ * @owner: the owner of the module that creates the target controller device
+ *
+ * Return: Pointer to struct i3c_target_ctrl
+ */
+struct i3c_target_ctrl *
+__i3c_target_ctrl_create(struct device *dev, const struct i3c_target_ctrl_ops *ops,
+			struct module *owner)
+{
+	struct i3c_target_ctrl *ctrl;
+	int ret;
+
+	if (WARN_ON(!dev))
+		return ERR_PTR(-EINVAL);
+
+	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return ERR_PTR(-ENOMEM);
+
+	device_initialize(&ctrl->dev);
+	ctrl->dev.class = i3c_target_ctrl_class;
+	ctrl->dev.parent = dev;
+	ctrl->dev.release = i3c_target_ctrl_release;
+	ctrl->ops = ops;
+
+	ret = dev_set_name(&ctrl->dev, "%s", dev_name(dev));
+	if (ret)
+		goto put_dev;
+
+	ret = device_add(&ctrl->dev);
+	if (ret)
+		goto put_dev;
+
+	ctrl->group = i3c_target_cfs_add_ctrl_group(ctrl);
+	if (!ctrl->group)
+		goto put_dev;
+
+	return ctrl;
+
+put_dev:
+	put_device(&ctrl->dev);
+	kfree(ctrl);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(__i3c_target_ctrl_create);
+
+/**
+ * devm_i3c_target_ctrl_destroy() - destroy the target controller device
+ * @dev: device that hat has to be destroy
+ * @ctrl: the target controller device that has to be destroy
+ *
+ * Invoke to create a new target controller device and add it to i3c_target class. While at that, it
+ * also associates the device with the i3c_target using devres. On driver detach, release function
+ * is invoked on the devres data, then devres data is freed.
+ */
+void devm_i3c_target_ctrl_destroy(struct device *dev, struct i3c_target_ctrl *ctrl)
+{
+	int r;
+
+	r = devres_destroy(dev, devm_i3c_target_ctrl_release, devm_i3c_target_ctrl_match,
+			   ctrl);
+	dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_i3c_target_ctrl_destroy);
+
+/**
+ * i3c_target_ctrl_destroy() - destroy the target controller device
+ * @ctrl: the target controller device that has to be destroyed
+ *
+ * Invoke to destroy the I3C target device
+ */
+void i3c_target_ctrl_destroy(struct i3c_target_ctrl *ctrl)
+{
+	i3c_target_cfs_remove_ctrl_group(ctrl->group);
+	device_unregister(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(i3c_target_ctrl_destroy);
+
+/**
+ * i3c_target_ctrl_add_func() - bind I3C target function to an target controller
+ * @ctrl: the controller device to which the target function should be added
+ * @func: the target function to be added
+ *
+ * An I3C target device can have only one functions.
+ */
+int i3c_target_ctrl_add_func(struct i3c_target_ctrl *ctrl, struct i3c_target_func *func)
+{
+	if (ctrl->func)
+		return -EBUSY;
+
+	ctrl->func = func;
+	func->ctrl = ctrl;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(i3c_target_ctrl_add_func);
+
+/**
+ * i3c_target_ctrl_remove_func() - unbind I3C target function to an target controller
+ * @ctrl: the controller device to which the target function should be removed
+ * @func: the target function to be removed
+ *
+ * An I3C target device can have only one functions.
+ */
+void i3c_target_ctrl_remove_func(struct i3c_target_ctrl *ctrl, struct i3c_target_func *func)
+{
+	ctrl->func = NULL;
+}
+EXPORT_SYMBOL_GPL(i3c_target_ctrl_remove_func);
+
+/**
+ * i3c_target_ctrl_get() - get the I3C target controller
+ * @name: device name of the target controller
+ *
+ * Invoke to get struct i3c_target_ctrl * corresponding to the device name of the
+ * target controller
+ */
+struct i3c_target_ctrl *i3c_target_ctrl_get(const char *name)
+{
+	int ret = -EINVAL;
+	struct i3c_target_ctrl *ctrl;
+	struct device *dev;
+	struct class_dev_iter iter;
+
+	class_dev_iter_init(&iter, i3c_target_ctrl_class, NULL, NULL);
+	while ((dev = class_dev_iter_next(&iter))) {
+		if (strcmp(name, dev_name(dev)))
+			continue;
+
+		ctrl = to_i3c_target_ctrl(dev);
+		if (!try_module_get(ctrl->ops->owner)) {
+			ret = -EINVAL;
+			goto err;
+		}
+
+		class_dev_iter_exit(&iter);
+		get_device(&ctrl->dev);
+		return ctrl;
+	}
+
+err:
+	class_dev_iter_exit(&iter);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(i3c_target_ctrl_get);
+
+/**
+ * i3c_target_ctrl_put() - release the I3C endpoint controller
+ * @ctrl: target controller returned by pci_target_get()
+ *
+ * release the refcount the caller obtained by invoking i3c_target_ctrl_get()
+ */
+void i3c_target_ctrl_put(struct i3c_target_ctrl *ctrl)
+{
+	if (!ctrl || IS_ERR(ctrl))
+		return;
+
+	module_put(ctrl->ops->owner);
+	put_device(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(i3c_target_ctrl_put);
+
+/**
+ * i3c_target_ctrl_hotjoin() - trigger device hotjoin
+ * @ctrl:  target controller
+ *
+ * return: 0: success, others failure
+ */
+int i3c_target_ctrl_hotjoin(struct i3c_target_ctrl *ctrl)
+{
+	if (!ctrl || IS_ERR(ctrl))
+		return -EINVAL;
+
+	if (!ctrl->ops->hotjoin)
+		return -EINVAL;
+
+	return ctrl->ops->hotjoin(ctrl);
+}
+EXPORT_SYMBOL_GPL(i3c_target_ctrl_hotjoin);
+
+/**
+ * i3c_target_func_bind() - Notify the function driver that the function device has been bound to a
+ *			   controller device
+ * @func: the function device which has been bound to the controller device
+ *
+ * Invoke to notify the function driver that it has been bound to a controller device
+ */
+int i3c_target_func_bind(struct i3c_target_func *func)
+{
+	struct device *dev = &func->dev;
+	int ret;
+
+	if (!func->driver) {
+		dev_WARN(dev, "func device not bound to driver\n");
+		return -EINVAL;
+	}
+
+	if (!try_module_get(func->driver->owner))
+		return -EAGAIN;
+
+	mutex_lock(&func->lock);
+	ret = func->driver->ops->bind(func);
+	if (!ret)
+		func->is_bound = true;
+	mutex_unlock(&func->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_target_func_bind);
+
+/**
+ * i3c_target_func_unbind() - Notify the function driver that the binding between the function
+ *			      device and controller device has been lost.
+ * @func: the function device which has lost the binding with the controller device.
+ *
+ * Invoke to notify the function driver that the binding between the function device and controller
+ * device has been lost.
+ */
+void i3c_target_func_unbind(struct i3c_target_func *func)
+{
+	if (!func->driver) {
+		dev_WARN(&func->dev, "func device not bound to driver\n");
+		return;
+	}
+
+	mutex_lock(&func->lock);
+	if (func->is_bound)
+		func->driver->ops->unbind(func);
+	mutex_unlock(&func->lock);
+
+	module_put(func->driver->owner);
+}
+EXPORT_SYMBOL_GPL(i3c_target_func_unbind);
+
+/**
+ * i3c_target_func_create() - create a new I3C function device
+ * @drv_name: the driver name of the I3C function device.
+ * @name: the name of the function device.
+ *
+ * Invoke to create a new I3C function device by providing the name of the function device.
+ */
+struct i3c_target_func *i3c_target_func_create(const char *drv_name, const char *name)
+{
+	struct i3c_target_func *func;
+	struct device *dev;
+	int ret;
+
+	func = kzalloc(sizeof(*func), GFP_KERNEL);
+	if (!func)
+		return ERR_PTR(-ENOMEM);
+
+	dev = &func->dev;
+	device_initialize(dev);
+	dev->bus = &i3c_target_func_bus_type;
+	dev->type = &i3c_target_func_type;
+	mutex_init(&func->lock);
+
+	ret = dev_set_name(dev, "%s.%s", drv_name, name);
+	if (ret) {
+		put_device(dev);
+		return ERR_PTR(ret);
+	}
+
+	ret = device_add(dev);
+	if (ret) {
+		put_device(dev);
+		return ERR_PTR(ret);
+	}
+
+	return func;
+}
+EXPORT_SYMBOL_GPL(i3c_target_func_create);
+
+/**
+ * __i3c_target_func_register_driver() - register a new I3C function driver
+ * @driver: structure representing I3C function driver
+ * @owner: the owner of the module that registers the I3C function driver
+ *
+ * Invoke to register a new I3C function driver.
+ */
+int __i3c_target_func_register_driver(struct i3c_target_func_driver *driver, struct module *owner)
+{
+	int ret = -EEXIST;
+
+	if (!driver->ops)
+		return -EINVAL;
+
+	if (!driver->ops->bind || !driver->ops->unbind)
+		return -EINVAL;
+
+	driver->driver.bus = &i3c_target_func_bus_type;
+	driver->driver.owner = owner;
+
+	ret = driver_register(&driver->driver);
+	if (ret)
+		return ret;
+
+	i3c_target_cfs_add_func_group(driver->driver.name);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__i3c_target_func_register_driver);
+
+/**
+ * i3c_target_func_unregister_driver() - unregister the I3C function driver
+ * @fd: the I3C function driver that has to be unregistered
+ *
+ * Invoke to unregister the I3C function driver.
+ */
+void i3c_target_func_unregister_driver(struct i3c_target_func_driver *fd)
+{
+	mutex_lock(&func_lock);
+	mutex_unlock(&func_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_target_func_unregister_driver);
+
+static int __init i3c_target_init(void)
+{
+	int ret;
+
+	i3c_target_ctrl_class = class_create("i3c_target");
+	if (IS_ERR(i3c_target_ctrl_class)) {
+		pr_err("failed to create i3c target class --> %ld\n",
+		       PTR_ERR(i3c_target_ctrl_class));
+		return PTR_ERR(i3c_target_ctrl_class);
+	}
+
+	ret = bus_register(&i3c_target_func_bus_type);
+	if (ret) {
+		class_destroy(i3c_target_ctrl_class);
+		pr_err("failed to register i3c target func bus --> %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+module_init(i3c_target_init);
+
+static void __exit i3c_target_exit(void)
+{
+	class_destroy(i3c_target_ctrl_class);
+	bus_unregister(&i3c_target_func_bus_type);
+}
+module_exit(i3c_target_exit);
+
diff --git a/include/linux/i3c/target.h b/include/linux/i3c/target.h
new file mode 100644
index 0000000000000..b0bf06685834c
--- /dev/null
+++ b/include/linux/i3c/target.h
@@ -0,0 +1,548 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li at nxp.com>
+ */
+
+#ifndef I3C_TARGET_H
+#define I3C_TARGET_H
+
+#include <linux/device.h>
+#include <linux/slab.h>
+
+struct i3c_target_func;
+struct i3c_target_ctrl;
+
+/**
+ * struct i3c_target_func_ops - set of function pointers for performing i3c target function
+ *				operations
+ * @bind: ops to perform when a controller device has been bound to function device
+ * @unbind: ops to perform when a binding has been lost between a controller device and function
+ *	    device
+ */
+struct i3c_target_func_ops {
+	int (*bind)(struct i3c_target_func *func);
+	void (*unbind)(struct i3c_target_func *func);
+};
+
+/**
+ * struct i3c_target_func_driver - represents the I3C function driver
+ * @probe: ops to perform when a new function device has been bound to the function driver
+ * @remove: ops to perform when the binding between the function device and function driver is
+ *	    broken
+ * @name: I3C Function driver name
+ * @driver: I3C Function driver
+ * @ops: set of function pointers for performing function operations
+ * @owner: the owner of the module that registers the I3C function driver
+ * @epf_group: list of configfs group corresponding to the I3C function driver
+ */
+struct i3c_target_func_driver {
+	int (*probe)(struct i3c_target_func *func);
+	void (*remove)(struct i3c_target_func *func);
+
+	char *name;
+	struct device_driver driver;
+	struct i3c_target_func_ops *ops;
+	struct module *owner;
+};
+
+/**
+ * struct i3c_target_func - represents the I3C function device
+ * @dev: the I3C function device
+ * @name: the name of the I3C function device
+ * @driver: the function driver to which this function device is bound
+ * @group: configfs group associated with the EPF device
+ * @lock: mutex to protect i3c_target_func_ops
+ * @ctrl: binded I3C controller device
+ * @is_bound: indicates if bind notification to function driver has been invoked
+ * @vendor_id: vendor id
+ * @part_id: part id
+ * @instance_id: instance id
+ * @ext_id: ext id
+ * @vendor_info: vendor info
+ * @static_addr: static address for I2C. It is 0 for I3C.
+ * @max_write_len: maximum write length
+ * @max_read_len: maximum read length
+ * @bcr: bus characteristics register (BCR)
+ * @dcr: device characteristics register (DCR)
+ */
+struct i3c_target_func {
+	struct device dev;
+	char *name;
+	struct i3c_target_func_driver *driver;
+	struct config_group *group;
+	/* mutex to protect against concurrent access of i3c_target_func_ops */
+	struct mutex lock;
+	struct i3c_target_ctrl *ctrl;
+	bool is_bound;
+
+	u16 vendor_id;
+	u16 part_id;
+	u8 instance_id;
+	u16 ext_id;
+	u8 vendor_info;
+	u16 static_addr;
+	u16 max_write_len;	//0 is hardware default max value
+	u16 max_read_len;	//0 is hardware default max value
+	u8 bcr;
+	u8 dcr;
+};
+
+enum i3c_request_stat {
+	I3C_REQUEST_OKAY,
+	I3C_REQUEST_PARTIAL,
+	I3C_REQUEST_ERR,
+	I3C_REQUEST_CANCEL,
+};
+
+/**
+ * struct i3c_request - represents the an I3C transfer request
+ * @buf: data buffer
+ * @length: data length
+ * @complete: call back function when request finished or cancelled
+ * @context: general data for complete callback function
+ * @list: link list
+ * @status: transfer status
+ * @actual: how much actually transferred
+ * @ctrl: I3C target controller associate with this request
+ * @tx: transfer direction, 1: target to master, 0: master to target
+ */
+struct i3c_request {
+	void *buf;
+	unsigned int length;
+
+	void (*complete)(struct i3c_request *req);
+	void *context;
+	struct list_head list;
+
+	enum i3c_request_stat status;
+	unsigned int actual;
+	struct i3c_target_ctrl *ctrl;
+	bool tx;
+};
+
+/**
+ * struct i3c_target_ctrl_features - represents I3C target controller features.
+ * @tx_fifo_sz: tx hardware fifo size
+ * @rx_fifo_sz: rx hardware fifo size
+ */
+struct i3c_target_ctrl_features {
+	u32 tx_fifo_sz;
+	u32 rx_fifo_sz;
+};
+
+/**
+ * struct i3c_target_ctrl_ops - set of function pointers for performing controller operations
+ * @set_config: set I3C controller configuration
+ * @enable: enable I3C controller
+ * @disable: disable I3C controller
+ * @raise_ibi: raise IBI interrupt to master
+ * @alloc_request: allocate a i3c_request, optional, default use kmalloc
+ * @free_request: free a i3c_request, default use kfree
+ * @queue: queue an I3C transfer
+ * @dequeue: dequeue an I3C transfer
+ * @cancel_all_reqs: call all pending requests
+ * @fifo_status: current FIFO status
+ * @fifo_flush: flush hardware FIFO
+ * @hotjoin: raise hotjoin request to master
+ * @set_status_format1: set i3c status format1
+ * @get_status_format1: get i3c status format1
+ * @get_addr: get i3c dynmatic address
+ * @get_features: ops to get the features supported by the I3C target controller
+ * @owner: the module owner containing the ops
+ */
+struct i3c_target_ctrl_ops {
+	int (*set_config)(struct i3c_target_ctrl *ctrl, struct i3c_target_func *func);
+	int (*enable)(struct i3c_target_ctrl *ctrl);
+	int (*disable)(struct i3c_target_ctrl *ctrl);
+	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8 size);
+
+	struct i3c_request *(*alloc_request)(struct i3c_target_ctrl *ctrl, gfp_t gfp_flags);
+	void (*free_request)(struct i3c_request *req);
+
+	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
+	int (*dequeue)(struct i3c_request *req);
+
+	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool tx);
+
+	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
+	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
+	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
+	int (*set_status_format1)(struct i3c_target_ctrl *ctrl, u16 status);
+	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
+	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
+	const struct i3c_target_ctrl_features *(*get_features)(struct i3c_target_ctrl *ctrl);
+	struct module *owner;
+};
+
+/**
+ * struct i3c_target_ctrl - represents the I3C target device
+ * @dev: I3C target device
+ * @ops: function pointers for performing endpoint operations
+ * @func: target functions present in this controller device
+ * @group: configfs group representing the I3C controller device
+ */
+struct i3c_target_ctrl {
+	struct device dev;
+	const struct i3c_target_ctrl_ops *ops;
+	struct i3c_target_func *func;
+	struct config_group *group;
+};
+
+/**
+ * i3c_target_ctrl_raise_ibi() - Raise IBI to master
+ * @ctrl: I3C target controller
+ * @p: optional data for IBI
+ * @size: size of optional data
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int i3c_target_ctrl_raise_ibi(struct i3c_target_ctrl *ctrl, void *p, u8 size)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->raise_ibi)
+		return ctrl->ops->raise_ibi(ctrl, p, size);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_target_ctrl_cancel_all_reqs() - Cancel all pending request
+ * @ctrl: I3C target controller
+ * @tx: Transfer diretion queue
+ */
+static inline void i3c_target_ctrl_cancel_all_reqs(struct i3c_target_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->cancel_all_reqs)
+		ctrl->ops->cancel_all_reqs(ctrl, tx);
+}
+
+/**
+ * i3c_target_ctrl_set_config() - Set controller configuration
+ * @ctrl: I3C target controller device
+ * @func: Function device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_target_ctrl_set_config(struct i3c_target_ctrl *ctrl, struct i3c_target_func *func)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->set_config)
+		return ctrl->ops->set_config(ctrl, func);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_target_ctrl_enable() - Enable I3C controller
+ * @ctrl: I3C target controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_target_ctrl_enable(struct i3c_target_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->enable)
+		return ctrl->ops->enable(ctrl);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_target_ctrl_disable() - Disable I3C controller
+ * @ctrl: I3C target controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_target_ctrl_disable(struct i3c_target_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->disable)
+		return ctrl->ops->disable(ctrl);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_target_ctrl_alloc_request() - Alloc an I3C transfer
+ * @ctrl: I3C target controller device
+ * @gfp_flags: additional gfp flags used when allocating the buffers
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline struct i3c_request *
+i3c_target_ctrl_alloc_request(struct i3c_target_ctrl *ctrl, gfp_t gfp_flags)
+{
+	struct i3c_request *req = NULL;
+
+	if (ctrl && ctrl->ops && ctrl->ops->alloc_request)
+		req = ctrl->ops->alloc_request(ctrl, gfp_flags);
+	else
+		req = kzalloc(sizeof(*req), gfp_flags);
+
+	if (req)
+		req->ctrl = ctrl;
+
+	return req;
+}
+
+/**
+ * i3c_target_ctrl_free_request() - Free an I3C transfer
+ * @req: I3C transfer request
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline void
+i3c_target_ctrl_free_request(struct i3c_request *req)
+{
+	struct i3c_target_ctrl *ctrl;
+
+	if (!req)
+		return;
+
+	ctrl = req->ctrl;
+	if (ctrl && ctrl->ops && ctrl->ops->free_request)
+		ctrl->ops->free_request(req);
+	else
+		kfree(req);
+}
+
+/**
+ * i3c_target_ctrl_queue() - Queue an I3C transfer
+ * @req: I3C transfer request
+ * @gfp_flags: additional gfp flags used when allocating the buffers
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_target_ctrl_queue(struct i3c_request *req, gfp_t gfp_flags)
+{
+	struct i3c_target_ctrl *ctrl;
+	int ret = -EINVAL;
+
+	if (!req)
+		return -EINVAL;
+
+	ctrl = req->ctrl;
+
+	req->actual = 0;
+	req->status = 0;
+	if (ctrl && ctrl->ops && ctrl->ops->queue)
+		ret = ctrl->ops->queue(req, gfp_flags);
+
+	return ret;
+}
+
+/**
+ * i3c_target_ctrl_dequeue() - Dequeue an I3C transfer
+ * @req: I3C transfer request
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_target_ctrl_dequeue(struct i3c_request *req)
+{
+	struct i3c_target_ctrl *ctrl;
+	int ret = -EINVAL;
+
+	if (!req)
+		return -EINVAL;
+
+	ctrl = req->ctrl;
+	if (ctrl && ctrl->ops && ctrl->ops->dequeue)
+		ret = ctrl->ops->dequeue(req);
+
+	return ret;
+}
+
+/**
+ * i3c_target_ctrl_fifo_status() - Get controller FIFO status
+ * @ctrl: I3C target controller device
+ * @tx: 1: Target to master, 0: master to target
+ *
+ * Returns: How much data in FIFO
+ */
+static inline int
+i3c_target_ctrl_fifo_status(struct i3c_target_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->fifo_status)
+		return ctrl->ops->fifo_status(ctrl, tx);
+
+	return 0;
+}
+
+/**
+ * i3c_target_ctrl_fifo_flush() - Flush controller FIFO
+ * @ctrl: I3C target controller device
+ * @tx: 1: Target to master, 0: master to target
+ *
+ */
+static inline void
+i3c_target_ctrl_fifo_flush(struct i3c_target_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->fifo_flush)
+		return ctrl->ops->fifo_flush(ctrl, tx);
+}
+
+/**
+ * i3c_target_ctrl_get_features() - Get controller supported features
+ * @ctrl: I3C target controller device
+ *
+ * Returns: The pointer to struct i3c_target_ctrl_features
+ */
+static inline const struct i3c_target_ctrl_features*
+i3c_target_ctrl_get_features(struct i3c_target_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->get_features)
+		return ctrl->ops->get_features(ctrl);
+
+	return NULL;
+}
+
+/**
+ * i3c_target_ctrl_set_status_format1() - Set controller supported features
+ * @ctrl: I3C target controller device
+ * @status: I3C GETSTATUS format1
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_target_ctrl_set_status_format1(struct i3c_target_ctrl *ctrl, u16 status)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->set_status_format1)
+		return ctrl->ops->set_status_format1(ctrl, status);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_target_ctrl_get_status_format1() - Get controller supported features
+ * @ctrl: I3C target controller device
+ *
+ * Return: I3C GETSTATUS format1
+ */
+static inline u16
+i3c_target_ctrl_get_status_format1(struct i3c_target_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->get_status_format1)
+		return ctrl->ops->get_status_format1(ctrl);
+
+	return 0;
+}
+
+/**
+ * i3c_target_ctrl_get_addr() - Get controller address
+ * @ctrl: I3C target controller device
+ *
+ * Return: address
+ */
+static inline u8 i3c_target_ctrl_get_addr(struct i3c_target_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->get_addr)
+		return ctrl->ops->get_addr(ctrl);
+
+	return 0;
+}
+
+#define to_i3c_target_ctrl(device) container_of((device), struct i3c_target_ctrl, dev)
+
+#define to_i3c_target_func(func_dev) container_of((func_dev), struct i3c_target_func, dev)
+#define to_i3c_target_func_driver(drv) (container_of((drv), struct i3c_target_func_driver, driver))
+
+#define i3c_target_ctrl_create(dev, ops) \
+		__i3c_target_ctrl_create((dev), (ops), THIS_MODULE)
+#define devm_i3c_target_ctrl_create(dev, ops) \
+		__devm_i3c_target_ctrl_create((dev), (ops), THIS_MODULE)
+
+#ifdef CONFIG_I3C_TARGET
+struct i3c_target_ctrl *
+__devm_i3c_target_ctrl_create(struct device *dev, const struct i3c_target_ctrl_ops *ops,
+			     struct module *owner);
+struct i3c_target_ctrl *
+__i3c_target_ctrl_create(struct device *dev, const struct i3c_target_ctrl_ops *ops,
+			struct module *owner);
+
+void devm_i3c_target_ctrl_destroy(struct device *dev, struct i3c_target_ctrl *epc);
+void i3c_target_ctrl_destroy(struct i3c_target_ctrl *epc);
+
+int i3c_target_ctrl_add_func(struct i3c_target_ctrl *ctrl, struct i3c_target_func *func);
+void i3c_target_ctrl_remove_func(struct i3c_target_ctrl *ctrl, struct i3c_target_func *func);
+int i3c_target_ctrl_hotjoin(struct i3c_target_ctrl *ctrl);
+
+struct i3c_target_ctrl *i3c_target_ctrl_get(const char *name);
+void i3c_target_ctrl_put(struct i3c_target_ctrl *ctrl);
+
+int i3c_target_func_bind(struct i3c_target_func *func);
+void i3c_target_func_unbind(struct i3c_target_func *func);
+struct i3c_target_func *i3c_target_func_create(const char *drv_name, const char *name);
+
+#define i3c_target_func_register_driver(drv) \
+	__i3c_target_func_register_driver(drv, THIS_MODULE)
+
+int __i3c_target_func_register_driver(struct i3c_target_func_driver *drv, struct module *owner);
+void i3c_target_func_unregister_driver(struct i3c_target_func_driver *drv);
+#else
+static inline struct i3c_target_ctrl *
+__devm_i3c_target_ctrl_create(struct device *dev, const struct i3c_target_ctrl_ops *ops,
+			     struct module *owner)
+{
+	return NULL;
+}
+
+static inline struct i3c_target_ctrl *
+__i3c_target_ctrl_create(struct device *dev, const struct i3c_target_ctrl_ops *ops,
+			struct module *owner)
+{
+	return NULL;
+}
+#endif
+
+#ifdef CONFIG_I3C_TARGET_CONFIGFS
+struct config_group *i3c_target_cfs_add_ctrl_group(struct i3c_target_ctrl *ctrl);
+void i3c_target_cfs_remove_ctrl_group(struct config_group *group);
+struct config_group *i3c_target_cfs_add_func_group(const char *name);
+void i3c_target_cfs_remove_func_group(struct config_group *group);
+#else
+static inline struct config_group *i3c_target_cfs_add_ctrl_group(struct i3c_target_ctrl *ctrl)
+{
+	return NULL;
+}
+
+static inline void i3c_target_cfs_remove_ctrl_group(struct config_group *group)
+{
+}
+
+static inline struct config_group *i3c_target_cfs_add_func_group(const char *name)
+{
+	return NULL;
+}
+
+static inline void i3c_target_cfs_remove_func_group(struct config_group *group)
+{
+}
+#endif
+
+#define DECLARE_I3C_TARGET_FUNC(_name, _probe, _remove, _ops)			\
+	static struct i3c_target_func_driver _name ## i3c_func = {		\
+		.driver.name = __stringify(_name),				\
+		.owner  = THIS_MODULE,						\
+		.probe = _probe,						\
+		.remove = _remove,						\
+		.ops = _ops							\
+	};									\
+	MODULE_ALIAS("i3cfunc:" __stringify(_name))
+
+#define DECLARE_I3C_TARGET_INIT(_name, _probe, _remove, _ops)			\
+	DECLARE_I3C_TARGET_FUNC(_name, _probe, _remove, _ops);			\
+	static int __init _name ## mod_init(void)				\
+	{									\
+		return i3c_target_func_register_driver(&_name ## i3c_func);	\
+	}									\
+	static void __exit _name ## mod_exit(void)				\
+	{									\
+		i3c_target_func_unregister_driver(&_name ## i3c_func);		\
+	}									\
+	module_init(_name ## mod_init);						\
+	module_exit(_name ## mod_exit)
+
+#endif
-- 
2.34.1



From Frank.Li at nxp.com  Tue May  7 13:43:05 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:05 -0400
Subject: [PATCH resend v9 2/8] dt-bindings: i3c: svc: add proptery mode
In-Reply-To: <20240507204311.2898714-1-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
Message-ID: <20240507204311.2898714-3-Frank.Li@nxp.com>

Add 'mode' property to distinguish between 'controller' and 'target' modes.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski at linaro.org>
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Change from v6 to v9
    - none
    Change from v4 to v5
    - add Krzysztof Kozlowski's review tag
    
    Change from v3 to v4
    -fix dtb check error
    
    Change from v2 to v3
        - using 'mode' distringuish master and target mode

 .../devicetree/bindings/i3c/silvaco,i3c-master.yaml   | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
index 133855f11b4f5..3af77d143f018 100644
--- a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
+++ b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
@@ -4,7 +4,7 @@
 $id: http://devicetree.org/schemas/i3c/silvaco,i3c-master.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: Silvaco I3C master
+title: Silvaco I3C master/target
 
 maintainers:
   - Conor Culhane <conor.culhane at silvaco.com>
@@ -22,6 +22,15 @@ properties:
   interrupts:
     maxItems: 1
 
+  mode:
+    description:
+      Tells Dual-Role I3C controllers that we want to work on a particular
+      mode. In case this attribute isn't passed via DT, I3C controllers
+      should default to 'controller'.
+    $ref: /schemas/types.yaml#/definitions/string
+    enum: [controller, target]
+    default: controller
+
   clocks:
     items:
       - description: system clock
-- 
2.34.1



From Frank.Li at nxp.com  Tue May  7 13:43:06 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:06 -0400
Subject: [PATCH resend v9 3/8] Documentation: i3c: Add I3C target mode controller and function
In-Reply-To: <20240507204311.2898714-1-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
Message-ID: <20240507204311.2898714-4-Frank.Li@nxp.com>

Add I3C target mode and tty over i3c func driver document.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 Documentation/driver-api/i3c/index.rst        |   1 +
 .../driver-api/i3c/target/i3c-target-cfs.rst  | 109 ++++++++++
 .../driver-api/i3c/target/i3c-target.rst      | 189 ++++++++++++++++++
 .../driver-api/i3c/target/i3c-tty-howto.rst   | 109 ++++++++++
 Documentation/driver-api/i3c/target/index.rst |  13 ++
 5 files changed, 421 insertions(+)
 create mode 100644 Documentation/driver-api/i3c/target/i3c-target-cfs.rst
 create mode 100644 Documentation/driver-api/i3c/target/i3c-target.rst
 create mode 100644 Documentation/driver-api/i3c/target/i3c-tty-howto.rst
 create mode 100644 Documentation/driver-api/i3c/target/index.rst

diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
index 783d6dad054b6..345a43c9f61b0 100644
--- a/Documentation/driver-api/i3c/index.rst
+++ b/Documentation/driver-api/i3c/index.rst
@@ -9,3 +9,4 @@ I3C subsystem
    protocol
    device-driver-api
    master-driver-api
+   target/index
diff --git a/Documentation/driver-api/i3c/target/i3c-target-cfs.rst b/Documentation/driver-api/i3c/target/i3c-target-cfs.rst
new file mode 100644
index 0000000000000..1fcf829dc4ae2
--- /dev/null
+++ b/Documentation/driver-api/i3c/target/i3c-target-cfs.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================
+Configuring I3C Target Using CONFIGFS
+=======================================
+
+:Author: Frank Li <Frank.Li at nxp.com>
+
+The I3C Target Core exposes configfs entry (i3c_target) to configure the I3C
+target function and to bind the target function with the target controller.
+(For introducing other mechanisms to configure the I3C Target Function refer to
+[1]).
+
+Mounting configfs
+=================
+
+The I3C Target Core layer creates i3c_target directory in the mounted configfs
+directory. configfs can be mounted using the following command::
+
+	mount -t configfs none /sys/kernel/config
+
+Directory Structure
+===================
+
+The i3c_target configfs has two directories at its root: controllers and
+functions. Every Controller device present in the system will have an entry in
+the *controllers* directory and every Function driver present in the system will
+have an entry in the *functions* directory.
+::
+
+	/sys/kernel/config/i3c_target/
+		.. controllers/
+		.. functions/
+
+Creating Function Device
+===================
+
+Every registered Function driver will be listed in controllers directory. The
+entries corresponding to Function driver will be created by the Function core.
+::
+
+	/sys/kernel/config/i3c_target/functions/
+		.. <Function Driver1>/
+			... <Function Device 11>/
+			... <Function Device 21>/
+			... <Function Device 31>/
+		.. <Function Driver2>/
+			... <Function Device 12>/
+			... <Function Device 22>/
+
+In order to create a <Function device> of the type probed by <Function Driver>,
+the user has to create a directory inside <Function DriverN>.
+
+Every <Function device> directory consists of the following entries that can be
+used to configure the standard configuration header of the target function.
+(These entries are created by the framework when any new <Function Device> is
+created)
+::
+
+		.. <Function Driver1>/
+			... <Function Device 11>/
+				... vendor_id
+				... part_id
+				... bcr
+				... dcr
+				... ext_id
+				... instance_id
+				... max_read_len
+				... max_write_len
+				... vendor_info
+
+Controller Device
+==========
+
+Every registered Controller device will be listed in controllers directory. The
+entries corresponding to Controller device will be created by the Controller
+core.
+::
+
+	/sys/kernel/config/i3c_target/controllers/
+		.. <Controller Device1>/
+			... <Symlink Function Device11>/
+		.. <Controller Device2>/
+			... <Symlink Function Device21>/
+
+The <Controller Device> directory will have a list of symbolic links to
+<Function Device>. These symbolic links should be created by the user to
+represent the functions present in the target device. Only <Function Device>
+that represents a physical function can be linked to a Controller device.
+
+::
+
+			 | controllers/
+				| <Directory: Controller name>/
+					| <Symbolic Link: Function>
+			 | functions/
+				| <Directory: Function driver>/
+					| <Directory: Function device>/
+						| vendor_id
+						| part_id
+						| bcr
+						| dcr
+						| ext_id
+						| instance_id
+						| max_read_len
+						| max_write_len
+						| vendor_info
+
+[1] Documentation/I3C/target/pci-target.rst
diff --git a/Documentation/driver-api/i3c/target/i3c-target.rst b/Documentation/driver-api/i3c/target/i3c-target.rst
new file mode 100644
index 0000000000000..09ae26b1f311a
--- /dev/null
+++ b/Documentation/driver-api/i3c/target/i3c-target.rst
@@ -0,0 +1,189 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Author: Frank Li <Frank.Li at nxp.com>
+
+This document is a guide to use the I3C Target Framework in order to create
+target controller driver, target function driver, and using configfs interface
+to bind the function driver to the controller driver.
+
+Introduction
+============
+
+Linux has a comprehensive I3C subsystem to support I3C controllers that
+operates in master mode. The subsystem has capability to scan I3C bus,assign
+i3c device address, load I3C driver (based on Manufacturer ID, part ID),
+support other services like hot-join, In-Band Interrupt(IBI).
+
+However the I3C controller IP integrated in some SoCs is capable of operating
+either in Master mode or Target mode. I3C Target Framework will add target mode
+support in Linux. This will help to run Linux in an target system which can
+have a wide variety of use cases from testing or validation, co-processor
+accelerator, etc.
+
+I3C Target Core
+=================
+
+The I3C Target Core layer comprises 3 components: the Target Controller
+library, the Target Function library, and the configfs layer to bind the target
+function with the target controller.
+
+I3C Target Controller Library
+------------------------------------
+
+The Controller library provides APIs to be used by the controller that can
+operate in target mode. It also provides APIs to be used by function
+driver/library in order to implement a particular target function.
+
+APIs for the I3C Target controller Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Target core provides to be used by the
+I3C controller driver.
+
+* devm_i3c_target_ctrl_create()/i3c_target_ctrl_create()
+
+   The I3C controller driver should implement the following ops:
+
+	* set_config: ops to set i3c configuration
+	* enable: ops to enable controller
+	* disable: ops to disable controller
+	* raise_ibi: ops to raise IBI to master controller
+	* alloc_request: ops to alloc a transfer request
+	* free_request: ops to free a transfer request
+	* queue: ops to queue a request to transfer queue
+	* dequeue: ops to dequeue a request from transfer queue
+	* cancel_all_reqs: ops to cancel all request from transfer queue
+        * fifo_status: ops to get fifo status
+        * fifo_flush: ops to flush hardware fifo
+	* get_features: ops to get controller supported features
+
+   The I3C controller driver can then create a new Controller device by
+   invoking devm_i3c_target_ctrl_create()/i3c_target_ctrl_create().
+
+* devm_i3c_target_ctrl_destroy()/i3c_target_ctrl_destroy()
+
+   The I3C controller driver can destroy the Controller device created by
+   either devm_i3c_target_ctrl_create() or i3c_target_ctrl_create() using
+   devm_i3c_target_ctrl_destroy() or i3c_target_ctrl_destroy().
+
+I3C Target Controller APIs for the I3C Target Function Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Target core provides to be used by the
+I3C target function driver.
+
+* i3c_target_ctrl_set_config()
+
+   The I3C target function driver should use i3c_target_ctrl_set_config() to
+   write i3c configuration to the target controller.
+
+* i3c_target_ctrl_enable()/i3c_target_ctrl_disable()
+
+   The I3C target function driver should use i3c_target_ctrl_enable()/
+   i3c_target_ctrl_disable() to enable/disable i3c target controller.
+
+* i3c_target_ctrl_alloc_request()/i3c_target_ctrl_free_request()
+
+   The I3C target function driver should usei3c_target_ctrl_alloc_request() /
+   i3c_target_ctrl_free_request() to alloc/free a i3c request.
+
+* i3c_target_ctrl_raise_ibi()
+
+   The I3C target function driver should use i3c_target_ctrl_raise_ibi() to
+   raise IBI.
+
+* i3c_target_ctrl_queue()/i3c_target_ctrl_dequeue()
+
+   The I3C target function driver should use i3c_target_ctrl_queue()/
+   i3c_target_ctrl_dequeue(), to queue/dequeue I3C transfer to/from transfer
+   queue.
+
+* i3c_target_ctrl_get_features()
+
+   The I3C target function driver should use i3c_target_ctrl_get_features() to
+   get I3C target controller supported features.
+
+Other I3C Target Controller APIs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are other APIs provided by the Controller library. These are used for
+binding the I3C Target Function device with Controlller device. i3c-cfs.c can
+be used as reference for using these APIs.
+
+* i3c_target_ctrl_get()
+
+   Get a reference to the I3C target controller based on the device name of
+   the controller.
+
+* i3c_target_ctrl_put()
+
+   Release the reference to the I3C target controller obtained using
+   i3c_target_ctrl_get()
+
+* i3c_target_ctrl_add_func()
+
+   Add a I3C target function to a I3C target controller.
+
+* i3c_target_ctrl_remove_func()
+
+   Remove the I3C target function from I3C target controller.
+
+I3C Target Function Library
+----------------------------------
+
+The I3C Target Function library provides APIs to be used by the function driver
+and the Controller library to provide target mode functionality.
+
+I3C Target Function APIs for the I3C Target Function Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Target core provides to be used
+by the I3C target function driver.
+
+* i3c_target_func_register_driver()
+
+   The I3C Target Function driver should implement the following ops:
+	 * bind: ops to perform when a Controller device has been bound to
+	   Function device
+	 * unbind: ops to perform when a binding has been lost between a
+	   Controller device and Function device
+
+  The I3C Function driver can then register the I3C Function driver by using
+  i3c_target_func_register_driver().
+
+* i3c_target_func_unregister_driver()
+
+  The I3C Function driver can unregister the I3C Function driver by using
+  i3c_epf_unregister_driver().
+
+APIs for the I3C Target Controller Library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Target core provides to be used by the
+I3C target controller library.
+
+Other I3C Target APIs
+~~~~~~~~~~~~~~~~~~~~
+
+There are other APIs provided by the Function library. These are used to notify
+the function driver when the Function device is bound to the EPC device.
+i3c-cfs.c can be used as reference for using these APIs.
+
+* i3c_target_func_create()
+
+   Create a new I3C Function device by passing the name of the I3C EPF device.
+   This name will be used to bind the Function device to a Function driver.
+
+* i3c_target_func_destroy()
+
+   Destroy the created I3C Function device.
+
+* i3c_target_func_bind()
+
+   i3c_target_func_bind() should be invoked when the EPF device has been bound
+   to a Controller device.
+
+* i3c_target_func_unbind()
+
+   i3c_target_func_unbind() should be invoked when the binding between EPC
+   device and function device is lost.
diff --git a/Documentation/driver-api/i3c/target/i3c-tty-howto.rst b/Documentation/driver-api/i3c/target/i3c-tty-howto.rst
new file mode 100644
index 0000000000000..43a129b18e938
--- /dev/null
+++ b/Documentation/driver-api/i3c/target/i3c-tty-howto.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+I3C TTY User Guide
+===================
+
+:Author: Frank Li <Frank.Li at nxp.com>
+
+This document is a guide to help users use i3c-target-tty function driver and
+i3ctty master driver for testing I3C. The list of steps to be followed in the
+master side and target side is given below.
+
+Endpoint Device
+===============
+
+Endpoint Controller Devices
+---------------------------
+
+To find the list of target controller devices in the system::
+
+	# ls  /sys/class/i3c_target/
+	  44330000.i3c-target
+
+If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
+
+	# ls /sys/kernel/config/i3c_target/controllers/
+	  44330000.i3c-target
+
+
+Endpoint Function Drivers
+-------------------------
+
+To find the list of target function drivers in the system::
+
+	# ls /sys/bus/i3c_target_func/drivers
+	  tty
+
+If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
+
+	# ls /sys/kernel/config/i3c_target/functions
+	  tty
+
+
+Creating i3c-target-tty Device
+----------------------------
+
+I3C target function device can be created using the configfs. To create
+i3c-target-tty device, the following commands can be used::
+
+	# mount -t configfs none /sys/kernel/config
+	# cd /sys/kernel/config/i3c_target/
+	# mkdir functions/tty/func1
+
+The "mkdir func1" above creates the i3c-target-tty function device that will
+be probed by i3c tty driver.
+
+The I3C target framework populates the directory with the following
+configurable fields::
+
+	# ls functions/tty/func1
+	bcr  dcr  ext_id  instance_id  max_read_len  max_write_len
+	part_id  vendor_id  vendor_info
+
+The I3C target function driver populates these entries with default values when
+the device is bound to the driver. The i3c-target-tty driver populates vendorid
+with 0xffff and interrupt_pin with 0x0001::
+
+	# cat functions/tty/func1/vendor_id
+	  0x0
+
+Configuring i3c-target-tty Device
+-------------------------------
+
+The user can configure the i3c-target-tty device using configfs entry. In order
+to change the vendorid, the following commands can be used::
+
+	# echo 0x011b > functions/tty/func1/vendor_id
+	# echo 0x1000 > functions/tty/func1/part_id
+	# echo 0x6 > functions/tty/t/bcr
+
+Binding i3c-target-tty Device to target Controller
+------------------------------------------------
+
+In order for the target function device to be useful, it has to be bound to a
+I3C target controller driver. Use the configfs to bind the function device to
+one of the controller driver present in the system::
+
+	# ln -s functions/tty/func1 controllers/44330000.i3c-target/
+
+I3C Master Device
+================
+
+Check I3C tty device is probed
+
+	# ls /sys/bus/i3c/devices/0-23610000000
+	0-23610000000:0  bcr  dcr  driver  dynamic_address  hdrcap
+	modalias  pid  power  subsystem  tty  uevent
+
+Using Target TTY function Device
+-----------------------------------
+
+Host side:
+	cat /dev/ttyI3C0
+Target side
+	echo abc >/dev/ttyI3C0
+
+You will see "abc" show at console.
+
+You can use other tty tool to test I3C target tty device.
diff --git a/Documentation/driver-api/i3c/target/index.rst b/Documentation/driver-api/i3c/target/index.rst
new file mode 100644
index 0000000000000..56eabfae83aa4
--- /dev/null
+++ b/Documentation/driver-api/i3c/target/index.rst
@@ -0,0 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+I3C Target Framework
+======================
+
+.. toctree::
+   :maxdepth: 2
+
+   i3c-target
+   i3c-target-cfs
+   i3c-tty-howto
+
-- 
2.34.1



From Frank.Li at nxp.com  Tue May  7 13:43:07 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:07 -0400
Subject: [PATCH resend v9 4/8] i3c: svc: Add svc-i3c-main.c and svc-i3c.h
In-Reply-To: <20240507204311.2898714-1-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
Message-ID: <20240507204311.2898714-5-Frank.Li@nxp.com>

SVC i3c is a dual role controller. Move probe() into svc-i3c-main.c. This
prepares to support target probe depending on dts "mode" settings.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Change from v7 to v9
    -none
    Change from v6 to v7
    - using /* SPDX-License-Identifier */ for svc-i3c.h
    Change from v5 to v6
    - none
    Change from v4 to v5
    - add new line at end of file
    
    New file from v4

 drivers/i3c/master/Makefile         |  3 +-
 drivers/i3c/master/svc-i3c-main.c   | 53 +++++++++++++++++++++++++++++
 drivers/i3c/master/svc-i3c-master.c | 34 ++++--------------
 drivers/i3c/master/svc-i3c.h        | 12 +++++++
 4 files changed, 73 insertions(+), 29 deletions(-)
 create mode 100644 drivers/i3c/master/svc-i3c-main.c
 create mode 100644 drivers/i3c/master/svc-i3c.h

diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile
index 3e97960160bc8..484cb81f45821 100644
--- a/drivers/i3c/master/Makefile
+++ b/drivers/i3c/master/Makefile
@@ -2,5 +2,6 @@
 obj-$(CONFIG_CDNS_I3C_MASTER)		+= i3c-master-cdns.o
 obj-$(CONFIG_DW_I3C_MASTER)		+= dw-i3c-master.o
 obj-$(CONFIG_AST2600_I3C_MASTER)	+= ast2600-i3c-master.o
-obj-$(CONFIG_SVC_I3C_MASTER)		+= svc-i3c-master.o
+svc-i3c-objs				+= svc-i3c-main.o svc-i3c-master.o
+obj-$(CONFIG_SVC_I3C_MASTER)		+= svc-i3c.o
 obj-$(CONFIG_MIPI_I3C_HCI)		+= mipi-i3c-hci/
diff --git a/drivers/i3c/master/svc-i3c-main.c b/drivers/i3c/master/svc-i3c-main.c
new file mode 100644
index 0000000000000..6be6a576cdf7a
--- /dev/null
+++ b/drivers/i3c/master/svc-i3c-main.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "svc-i3c.h"
+
+static int svc_i3c_probe(struct platform_device *pdev)
+{
+	return svc_i3c_master_probe(pdev);
+}
+
+static void svc_i3c_remove(struct platform_device *pdev)
+{
+	svc_i3c_master_remove(pdev);
+}
+
+static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
+{
+	return svc_i3c_master_runtime_suspend(dev);
+}
+
+static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
+{
+	return svc_i3c_master_runtime_resume(dev);
+}
+
+static const struct dev_pm_ops svc_i3c_pm_ops = {
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				      pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(svc_i3c_runtime_suspend,
+			   svc_i3c_runtime_resume, NULL)
+};
+
+static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
+	{ .compatible = "silvaco,i3c-master-v1"},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
+
+static struct platform_driver svc_i3c_master = {
+	.probe = svc_i3c_probe,
+	.remove_new = svc_i3c_remove,
+	.driver = {
+		.name = "silvaco-i3c-master",
+		.of_match_table = svc_i3c_master_of_match_tbl,
+		.pm = &svc_i3c_pm_ops,
+	},
+};
+module_platform_driver(svc_i3c_master);
+
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 5ee4db68988e2..4dfe85ab17fd2 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -21,6 +21,8 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 
+#include "svc-i3c.h"
+
 /* Master Mode Registers */
 #define SVC_I3C_MCONFIG      0x000
 #define   SVC_I3C_MCONFIG_MASTER_EN BIT(0)
@@ -1613,7 +1615,7 @@ static void svc_i3c_master_unprepare_clks(struct svc_i3c_master *master)
 	clk_disable_unprepare(master->sclk);
 }
 
-static int svc_i3c_master_probe(struct platform_device *pdev)
+int svc_i3c_master_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct svc_i3c_master *master;
@@ -1706,7 +1708,7 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 	return ret;
 }
 
-static void svc_i3c_master_remove(struct platform_device *pdev)
+void svc_i3c_master_remove(struct platform_device *pdev)
 {
 	struct svc_i3c_master *master = platform_get_drvdata(pdev);
 
@@ -1733,7 +1735,7 @@ static void svc_i3c_restore_regs(struct svc_i3c_master *master)
 	}
 }
 
-static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
+int svc_i3c_master_runtime_suspend(struct device *dev)
 {
 	struct svc_i3c_master *master = dev_get_drvdata(dev);
 
@@ -1744,7 +1746,7 @@ static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
 	return 0;
 }
 
-static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
+int svc_i3c_master_runtime_resume(struct device *dev)
 {
 	struct svc_i3c_master *master = dev_get_drvdata(dev);
 
@@ -1756,30 +1758,6 @@ static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
 	return 0;
 }
 
-static const struct dev_pm_ops svc_i3c_pm_ops = {
-	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-				      pm_runtime_force_resume)
-	SET_RUNTIME_PM_OPS(svc_i3c_runtime_suspend,
-			   svc_i3c_runtime_resume, NULL)
-};
-
-static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
-	{ .compatible = "silvaco,i3c-master-v1"},
-	{ /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
-
-static struct platform_driver svc_i3c_master = {
-	.probe = svc_i3c_master_probe,
-	.remove_new = svc_i3c_master_remove,
-	.driver = {
-		.name = "silvaco-i3c-master",
-		.of_match_table = svc_i3c_master_of_match_tbl,
-		.pm = &svc_i3c_pm_ops,
-	},
-};
-module_platform_driver(svc_i3c_master);
-
 MODULE_AUTHOR("Conor Culhane <conor.culhane at silvaco.com>");
 MODULE_AUTHOR("Miquel Raynal <miquel.raynal at bootlin.com>");
 MODULE_DESCRIPTION("Silvaco dual-role I3C master driver");
diff --git a/drivers/i3c/master/svc-i3c.h b/drivers/i3c/master/svc-i3c.h
new file mode 100644
index 0000000000000..b9e1dce4b2109
--- /dev/null
+++ b/drivers/i3c/master/svc-i3c.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef SVC_I3C_H
+#define SVC_I3C_H
+
+int svc_i3c_master_probe(struct platform_device *pdev);
+void svc_i3c_master_remove(struct platform_device *pdev);
+int svc_i3c_master_runtime_suspend(struct device *dev);
+int svc_i3c_master_runtime_resume(struct device *dev);
+
+#endif
+
-- 
2.34.1



From Frank.Li at nxp.com  Tue May  7 13:43:08 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:08 -0400
Subject: [PATCH resend v9 5/8] i3c: target: add svc target controller support
In-Reply-To: <20240507204311.2898714-1-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
Message-ID: <20240507204311.2898714-6-Frank.Li@nxp.com>

Add Silvaco I3C target controller support

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Change from v8 to v9
    - fix register macro define space and tab problem
    
    Chagne from v7 to v8
    -reorder header files
    - add missed header files
    
    Change from v3 to v7
    - none
    Change from v2 to v3
    - fix build warning
    
    Chagne from v7 to v8
    -reorder header files
    - add missed header files
    
    Change from v3 to v7
    - none
    Change from v2 to v3
    - fix build warning

 drivers/i3c/master/Makefile         |   2 +-
 drivers/i3c/master/svc-i3c-main.c   |  35 +-
 drivers/i3c/master/svc-i3c-target.c | 776 ++++++++++++++++++++++++++++
 drivers/i3c/master/svc-i3c.h        |   3 +
 4 files changed, 811 insertions(+), 5 deletions(-)
 create mode 100644 drivers/i3c/master/svc-i3c-target.c

diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile
index 484cb81f45821..b9ed6c9e7be13 100644
--- a/drivers/i3c/master/Makefile
+++ b/drivers/i3c/master/Makefile
@@ -2,6 +2,6 @@
 obj-$(CONFIG_CDNS_I3C_MASTER)		+= i3c-master-cdns.o
 obj-$(CONFIG_DW_I3C_MASTER)		+= dw-i3c-master.o
 obj-$(CONFIG_AST2600_I3C_MASTER)	+= ast2600-i3c-master.o
-svc-i3c-objs				+= svc-i3c-main.o svc-i3c-master.o
+svc-i3c-objs				+= svc-i3c-main.o svc-i3c-master.o svc-i3c-target.o
 obj-$(CONFIG_SVC_I3C_MASTER)		+= svc-i3c.o
 obj-$(CONFIG_MIPI_I3C_HCI)		+= mipi-i3c-hci/
diff --git a/drivers/i3c/master/svc-i3c-main.c b/drivers/i3c/master/svc-i3c-main.c
index 6be6a576cdf7a..e34b128ab274a 100644
--- a/drivers/i3c/master/svc-i3c-main.c
+++ b/drivers/i3c/master/svc-i3c-main.c
@@ -7,24 +7,51 @@
 
 #include "svc-i3c.h"
 
+static bool svc_i3c_is_master(struct device *dev)
+{
+	const char *mode = NULL;
+
+	device_property_read_string(dev, "mode", &mode);
+
+	if (!mode)
+		return true;
+
+	if (strncmp(mode, "target", 6) == 0)
+		return false;
+
+	return true;
+}
+
 static int svc_i3c_probe(struct platform_device *pdev)
 {
-	return svc_i3c_master_probe(pdev);
+	if (svc_i3c_is_master(&pdev->dev))
+		return svc_i3c_master_probe(pdev);
+
+	return svc_i3c_target_probe(pdev);
 }
 
 static void svc_i3c_remove(struct platform_device *pdev)
 {
-	svc_i3c_master_remove(pdev);
+	if (svc_i3c_is_master(&pdev->dev))
+		return svc_i3c_master_remove(pdev);
+
+	svc_i3c_target_remove(pdev);
 }
 
 static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
 {
-	return svc_i3c_master_runtime_suspend(dev);
+	if (svc_i3c_is_master(dev))
+		return svc_i3c_master_runtime_suspend(dev);
+
+	return -EINVAL;
 }
 
 static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
 {
-	return svc_i3c_master_runtime_resume(dev);
+	if (svc_i3c_is_master(dev))
+		return svc_i3c_master_runtime_resume(dev);
+
+	return -EINVAL;
 }
 
 static const struct dev_pm_ops svc_i3c_pm_ops = {
diff --git a/drivers/i3c/master/svc-i3c-target.c b/drivers/i3c/master/svc-i3c-target.c
new file mode 100644
index 0000000000000..5e04f5181eb27
--- /dev/null
+++ b/drivers/i3c/master/svc-i3c-target.c
@@ -0,0 +1,776 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li at nxp.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/target.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include "svc-i3c.h"
+
+enum i3c_clks {
+	PCLK,
+	FCLK,
+	SCLK,
+	MAXCLK,
+};
+
+struct svc_i3c_target {
+	struct device *dev;
+	void __iomem *regs;
+	int irq;
+	struct clk_bulk_data clks[MAXCLK];
+
+	struct list_head txq;
+	spinlock_t txq_lock; /* protect tx queue */
+	struct list_head rxq;
+	spinlock_t rxq_lock; /* protect rx queue */
+	struct list_head cq;
+	spinlock_t cq_lock; /* protect complete queue */
+
+	struct work_struct work;
+	struct workqueue_struct *workqueue;
+
+	struct completion dacomplete;
+	struct i3c_target_ctrl_features features;
+
+	spinlock_t ctrl_lock; /* protext access SCTRL register */
+};
+
+#define	I3C_SCONFIG	0x4
+#define	  I3C_SCONFIG_SLVENA_MASK	BIT(0)
+#define	  I3C_SCONFIG_OFFLINE_MASK	BIT(9)
+#define	  I3C_SCONFIG_SADDR_MASK	GENMASK(31, 25)
+
+#define	I3C_SSTATUS	0x8
+#define	  I3C_SSTATUS_STNOTSTOP_MASK	BIT(0)
+#define	  I3C_SSTATUS_STOP_MASK		BIT(10)
+#define	  I3C_SSTATUS_RX_PEND_MASK	BIT(11)
+#define	  I3C_SSTATUS_TXNOTFULL_MASK	BIT(12)
+#define	  I3C_SSTATUS_DACHG_MASK	BIT(13)
+#define	  I3C_SSTATUS_EVDET_MASK	GENMASK(21, 20)
+#define	  I3C_SSTATUS_EVDET_ACKED	0x3
+#define	  I3C_SSTATUS_IBIDIS_MASK	BIT(24)
+#define	  I3C_SSTATUS_HJDIS_MASK	BIT(27)
+
+#define	I3C_SCTRL	0xc
+#define	  I3C_SCTRL_EVENT_MASK		GENMASK(1, 0)
+#define	  I3C_SCTRL_EVENT_IBI		0x1
+#define	  I3C_SCTRL_EVENT_HOTJOIN	0x3
+#define	  I3C_SCTRL_EXTDATA_MASK	BIT(3)
+#define	  I3C_SCTRL_IBIDATA_MASK	GENMASK(15, 8)
+
+#define	I3C_SINTSET	0x10
+#define	I3C_SINTCLR	0x14
+#define	  I3C_SINT_START	BIT(8)
+#define	  I3C_SINT_MATCHED	BIT(9)
+#define	  I3C_SINT_STOP		BIT(10)
+#define	  I3C_SINT_RXPEND	BIT(11)
+#define	  I3C_SINT_TXSEND	BIT(12)
+#define	  I3C_SINT_DACHG	BIT(13)
+#define	  I3C_SINT_CCC		BIT(14)
+#define	  I3C_SINT_ERRWARN	BIT(15)
+#define	  I3C_SINT_DDRMAATCHED	BIT(16)
+#define	  I3C_SINT_CHANDLED	BIT(17)
+#define	  I3C_SINT_EVENT	BIT(18)
+#define	  I3C_SINT_SLVRST	BIT(19)
+
+#define	I3C_SDATACTRL	0x2c
+#define	  I3C_SDATACTRL_RXEMPTY_MASK	BIT(31)
+#define	  I3C_SDATACTRL_TXFULL_MASK	BIT(30)
+#define	  I3C_SDATACTRL_RXCOUNT_MASK	GENMASK(28, 24)
+#define	  I3C_SDATACTRL_TXCOUNT_MASK	GENMASK(20, 16)
+#define	  I3C_SDATACTRL_FLUSHFB_MASK	BIT(1)
+#define	  I3C_SDATACTRL_FLUSHTB_MASK	BIT(0)
+
+#define	I3C_SWDATAB	0x30
+#define	  I3C_SWDATAB_END_ALSO_MASK	BIT(16)
+#define	  I3C_SWDATAB_END_MASK		BIT(8)
+
+#define	I3C_SWDATAE	0x34
+#define	I3C_SRDATAB	0x40
+
+#define	I3C_SCAPABILITIES 0x60
+#define	  I3C_SCAPABILITIES_FIFOTX_MASK     GENMASK(27, 26)
+#define	  I3C_SCAPABILITIES_FIFORX_MASK     GENMASK(29, 28)
+
+#define	I3C_SMAXLIMITS	0x68
+#define	  I3C_SMAXLIMITS_MAXRD_MASK  GENMASK(11, 0)
+#define	  I3C_SMAXLIMITS_MAXWR_MASK  GENMASK(27, 16)
+
+#define	I3C_SIDPARTNO	0x6c
+
+#define	I3C_SIDEXT	0x70
+#define	  I3C_SIDEXT_BCR_MASK	GENMASK(23, 16)
+#define	  I3C_SIDEXT_DCR_MASK	GENMASK(15, 8)
+#define	I3C_SVENDORID	0x74
+
+#define	I3C_SMAPCTRL0	0x11c
+#define	  I3C_SMAPCTRL0_ENA_MASK	BIT(0)
+#define	  I3C_SMAPCTRL0_DA_MASK	GENMASK(7, 1)
+
+#define	I3C_IBIEXT1	0x140
+#define	  I3C_IBIEXT1_CNT_MASK	GEN_MASK(2, 0)
+#define	  I3C_IBIEXT1_MAX_MASK	GEN_MASK(4, 6)
+#define	  I3C_IBIEXT1_EXT1_SHIFT	8
+#define	  I3C_IBIEXT1_EXT2_SHIFT	16
+#define	  I3C_IBIEXT1_EXT3_SHIFT	24
+
+#define	I3C_IBIEXT2	0x144
+#define	  I3C_IBIEXT2_EXT4_SHIFT	0
+#define	  I3C_IBIEXT2_EXT5_SHIFT	8
+#define	  I3C_IBIEXT2_EXT6_SHIFT	16
+#define	  I3C_IBIEXT2_EXT7_SHIFT	24
+
+static int svc_i3c_target_enable(struct i3c_target_ctrl *ctrl)
+{
+	struct svc_i3c_target *svc;
+	u32 val;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	val = readl_relaxed(svc->regs + I3C_SCONFIG);
+	val |= I3C_SCONFIG_SLVENA_MASK;
+	writel_relaxed(val, svc->regs + I3C_SCONFIG);
+
+	return 0;
+}
+
+static int svc_i3c_target_disable(struct i3c_target_ctrl *ctrl)
+{
+	struct svc_i3c_target *svc;
+	u32 val;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	val = readl_relaxed(svc->regs + I3C_SCONFIG);
+	val &= ~I3C_SCONFIG_SLVENA_MASK;
+	writel_relaxed(val, svc->regs + I3C_SCONFIG);
+
+	return 0;
+}
+
+static int svc_i3c_target_set_config(struct i3c_target_ctrl *ctrl, struct i3c_target_func *func)
+{
+	struct svc_i3c_target *svc;
+	u32 val;
+	u32 wm, rm;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	if (func->static_addr > 0x7F)
+		return -EINVAL;
+
+	val = readl_relaxed(svc->regs + I3C_SCONFIG);
+	val &= ~I3C_SCONFIG_SLVENA_MASK;
+	val |= FIELD_PREP(I3C_SCONFIG_SADDR_MASK, func->static_addr);
+	writel_relaxed(val, svc->regs + I3C_SCONFIG);
+
+	if (func->part_id)
+		writel_relaxed((func->part_id << 16) |
+				((func->instance_id << 12) & GENMASK(15, 12)) |
+				(func->ext_id & GENMASK(11, 0)), svc->regs + I3C_SIDPARTNO);
+
+	writel_relaxed(FIELD_PREP(I3C_SIDEXT_BCR_MASK, func->bcr) |
+		       FIELD_PREP(I3C_SIDEXT_DCR_MASK, func->dcr),
+		       svc->regs + I3C_SIDEXT);
+
+	wm = func->max_write_len == 0 ?
+	     FIELD_GET(I3C_SMAXLIMITS_MAXWR_MASK, I3C_SMAXLIMITS_MAXWR_MASK) : func->max_write_len;
+
+	wm = max_t(u32, val, 8);
+
+	rm = func->max_read_len == 0 ?
+	     FIELD_GET(I3C_SMAXLIMITS_MAXRD_MASK, I3C_SMAXLIMITS_MAXRD_MASK) : func->max_read_len;
+	rm = max_t(u32, val, 16);
+
+	val = FIELD_PREP(I3C_SMAXLIMITS_MAXRD_MASK, rm) | FIELD_PREP(I3C_SMAXLIMITS_MAXWR_MASK, wm);
+	writel_relaxed(val, svc->regs + I3C_SMAXLIMITS);
+
+	writel_relaxed(func->vendor_id, svc->regs + I3C_SVENDORID);
+	return 0;
+}
+
+static const struct i3c_target_ctrl_features *svc_i3c_get_features(struct i3c_target_ctrl *ctrl)
+{
+	struct svc_i3c_target *svc;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	if (!svc)
+		return NULL;
+
+	return &svc->features;
+}
+
+static void svc_i3c_queue_complete(struct svc_i3c_target *svc, struct i3c_request *complete)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&svc->cq_lock, flags);
+	list_add_tail(&complete->list, &svc->cq);
+	spin_unlock_irqrestore(&svc->cq_lock, flags);
+	queue_work(svc->workqueue, &svc->work);
+}
+
+static void svc_i3c_fill_txfifo(struct svc_i3c_target *svc)
+{
+	struct i3c_request *req, *complete = NULL;
+	unsigned long flags;
+	int val;
+
+	spin_lock_irqsave(&svc->txq_lock, flags);
+	while ((!!(req = list_first_entry_or_null(&svc->txq, struct i3c_request, list))) &&
+	       !((readl_relaxed(svc->regs + I3C_SDATACTRL) & I3C_SDATACTRL_TXFULL_MASK))) {
+		while (!(readl_relaxed(svc->regs + I3C_SDATACTRL)
+					& I3C_SDATACTRL_TXFULL_MASK)) {
+			val = *(u8 *)(req->buf + req->actual);
+
+			if (req->actual + 1 == req->length)
+				writel_relaxed(val, svc->regs + I3C_SWDATAE);
+			else
+				writel_relaxed(val, svc->regs + I3C_SWDATAB);
+
+			req->actual++;
+
+			if (req->actual == req->length) {
+				list_del(&req->list);
+				complete = req;
+				spin_unlock_irqrestore(&svc->txq_lock, flags);
+
+				svc_i3c_queue_complete(svc, complete);
+				return;
+
+				spin_lock_irqsave(&svc->txq_lock, flags);
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&svc->txq_lock, flags);
+}
+
+static int svc_i3c_target_queue(struct i3c_request *req, gfp_t gfp)
+{
+	struct svc_i3c_target *svc;
+	struct list_head *q;
+	unsigned long flags;
+	spinlock_t *lk;
+
+	svc = dev_get_drvdata(&req->ctrl->dev);
+	if (!svc)
+		return -EINVAL;
+
+	if (req->tx) {
+		q = &svc->txq;
+		lk = &svc->txq_lock;
+	} else {
+		q = &svc->rxq;
+		lk = &svc->rxq_lock;
+	}
+
+	spin_lock_irqsave(lk, flags);
+	list_add_tail(&req->list, q);
+	spin_unlock_irqrestore(lk, flags);
+
+	if (req->tx)
+		svc_i3c_fill_txfifo(svc);
+
+	if (req->tx)
+		writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTSET);
+	else
+		writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTSET);
+
+	return 0;
+}
+
+static int svc_i3c_dequeue(struct i3c_request *req)
+{
+	struct svc_i3c_target *svc;
+	unsigned long flags;
+	spinlock_t *lk;
+
+	svc = dev_get_drvdata(&req->ctrl->dev);
+	if (!svc)
+		return -EINVAL;
+
+	if (req->tx)
+		lk = &svc->txq_lock;
+	else
+		lk = &svc->rxq_lock;
+
+	spin_lock_irqsave(lk, flags);
+	list_del(&req->list);
+	spin_unlock_irqrestore(lk, flags);
+
+	return 0;
+}
+
+static void svc_i3c_target_fifo_flush(struct i3c_target_ctrl *ctrl, bool tx)
+{
+	struct svc_i3c_target *svc;
+	u32 val;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	val = readl_relaxed(svc->regs + I3C_SDATACTRL);
+
+	val |= tx ? I3C_SDATACTRL_FLUSHTB_MASK : I3C_SDATACTRL_FLUSHFB_MASK;
+
+	writel_relaxed(val, svc->regs + I3C_SDATACTRL);
+}
+
+static int
+svc_i3c_target_raise_ibi(struct i3c_target_ctrl *ctrl, void *p, u8 size)
+{
+	struct svc_i3c_target *svc;
+	unsigned long flags;
+	u8 *ibidata = p;
+	u32 ext1 = 0, ext2 = 0;
+	u32 val;
+	int ret;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	if (size && !p)
+		return -EINVAL;
+
+	if (size > 8)
+		return -EINVAL;
+
+	val = readl_relaxed(svc->regs + I3C_SSTATUS);
+	if (val & I3C_SSTATUS_IBIDIS_MASK)
+		return -EINVAL;
+
+	ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+					 !(val & I3C_SCTRL_EVENT_MASK), 0, 10000);
+	if (ret) {
+		dev_err(&ctrl->dev, "Timeout when polling for NO event pending");
+		val &= ~I3C_SCTRL_EVENT_MASK;
+		writel_relaxed(val, svc->regs + I3C_SCTRL);
+		return -ENAVAIL;
+	}
+
+	spin_lock_irqsave(&svc->ctrl_lock, flags);
+
+	val = readl_relaxed(svc->regs + I3C_SCTRL);
+
+	val &= ~I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK;
+	val |= FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_IBI);
+
+	if (size) {
+		val |= FIELD_PREP(I3C_SCTRL_IBIDATA_MASK, *ibidata);
+		ibidata++;
+
+		if (size > 1)
+			val |= I3C_SCTRL_EXTDATA_MASK;
+
+		size--;
+		if (size > 0) {
+			ext1 |= (size + 2);
+			ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT1_SHIFT;
+			size--;
+		}
+
+		if (size > 0) {
+			ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT2_SHIFT;
+			size--;
+		}
+
+		if (size > 0) {
+			ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT3_SHIFT;
+			size--;
+		}
+
+		writel_relaxed(ext1, svc->regs + I3C_IBIEXT1);
+
+		if (size > 0) {
+			ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT4_SHIFT;
+			size--;
+		}
+
+		if (size > 0) {
+			ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT5_SHIFT;
+			size--;
+		}
+
+		if (size > 0) {
+			ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT6_SHIFT;
+			size--;
+		}
+
+		if (size > 0) {
+			ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT7_SHIFT;
+			size--;
+		}
+
+		writeb_relaxed(ext2, svc->regs + I3C_IBIEXT2);
+	}
+
+	/* Issue IBI*/
+	writel_relaxed(val, svc->regs + I3C_SCTRL);
+	spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+	ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+					 !(val & I3C_SCTRL_EVENT_MASK), 0, 1000000);
+	if (ret) {
+		dev_err(&ctrl->dev, "Timeout when polling for IBI finish\n");
+
+		//clear event to above hang bus
+		spin_lock_irqsave(&svc->ctrl_lock, flags);
+		val = readl_relaxed(svc->regs + I3C_SCTRL);
+		val &= ~I3C_SCTRL_EVENT_MASK;
+		writel_relaxed(val, svc->regs + I3C_SCTRL);
+		spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+		return -ENAVAIL;
+	}
+
+	return 0;
+}
+
+static void svc_i3c_target_complete(struct work_struct *work)
+{
+	struct svc_i3c_target *svc = container_of(work, struct svc_i3c_target, work);
+	struct i3c_request *req;
+	unsigned long flags;
+
+	spin_lock_irqsave(&svc->cq_lock, flags);
+	while (!list_empty(&svc->cq)) {
+		req = list_first_entry(&svc->cq, struct i3c_request, list);
+		list_del(&req->list);
+		spin_unlock_irqrestore(&svc->cq_lock, flags);
+		req->complete(req);
+
+		spin_lock_irqsave(&svc->cq_lock, flags);
+	}
+	spin_unlock_irqrestore(&svc->cq_lock, flags);
+}
+
+static irqreturn_t svc_i3c_target_irq_handler(int irq, void *dev_id)
+{
+	struct i3c_request *req, *complete = NULL;
+	struct svc_i3c_target *svc = dev_id;
+	unsigned long flags;
+	u32 statusFlags;
+
+	statusFlags = readl(svc->regs + I3C_SSTATUS);
+	writel(statusFlags, svc->regs + I3C_SSTATUS);
+
+	if (statusFlags & I3C_SSTATUS_DACHG_MASK)
+		complete_all(&svc->dacomplete);
+
+	if (statusFlags & I3C_SSTATUS_RX_PEND_MASK) {
+		spin_lock_irqsave(&svc->rxq_lock, flags);
+		req = list_first_entry_or_null(&svc->rxq, struct i3c_request, list);
+
+		if (!req) {
+			writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTCLR);
+		} else {
+			while (!(readl_relaxed(svc->regs + I3C_SDATACTRL) &
+					       I3C_SDATACTRL_RXEMPTY_MASK)) {
+				*(u8 *)(req->buf + req->actual) =
+							readl_relaxed(svc->regs + I3C_SRDATAB);
+				req->actual++;
+
+				if (req->actual == req->length) {
+					complete = req;
+					list_del(&req->list);
+					break;
+				}
+			}
+
+			if (req->actual != req->length && (statusFlags & I3C_SSTATUS_STOP_MASK)) {
+				complete = req;
+				list_del(&req->list);
+			}
+		}
+		spin_unlock_irqrestore(&svc->rxq_lock, flags);
+
+		if (complete) {
+			spin_lock_irqsave(&svc->cq_lock, flags);
+			list_add_tail(&complete->list, &svc->cq);
+			spin_unlock_irqrestore(&svc->cq_lock, flags);
+			queue_work(svc->workqueue, &svc->work);
+			complete = NULL;
+		}
+	}
+
+	if (statusFlags & I3C_SSTATUS_TXNOTFULL_MASK) {
+		svc_i3c_fill_txfifo(svc);
+		spin_lock_irqsave(&svc->txq_lock, flags);
+		if (list_empty(&svc->txq))
+			writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTCLR);
+		spin_unlock_irqrestore(&svc->txq_lock, flags);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void svc_i3c_cancel_all_reqs(struct i3c_target_ctrl *ctrl, bool tx)
+{
+	struct svc_i3c_target *svc;
+	struct i3c_request *req;
+	struct list_head *q;
+	unsigned long flags;
+	spinlock_t *lk;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+	if (!svc)
+		return;
+
+	if (tx) {
+		q = &svc->txq;
+		lk = &svc->txq_lock;
+	} else {
+		q = &svc->rxq;
+		lk = &svc->rxq_lock;
+	}
+
+	spin_lock_irqsave(lk, flags);
+	while (!list_empty(q)) {
+		req = list_first_entry(q, struct i3c_request, list);
+		list_del(&req->list);
+		spin_unlock_irqrestore(lk, flags);
+
+		req->status = I3C_REQUEST_CANCEL;
+		req->complete(req);
+		spin_lock_irqsave(lk, flags);
+	}
+	spin_unlock_irqrestore(lk, flags);
+}
+
+static int svc_i3c_hotjoin(struct i3c_target_ctrl *ctrl)
+{
+	struct svc_i3c_target *svc;
+	int ret;
+	u32 val;
+	u32 cfg;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+	if (!svc)
+		return -EINVAL;
+
+	reinit_completion(&svc->dacomplete);
+
+	val = readl_relaxed(svc->regs + I3C_SSTATUS);
+	if (val & I3C_SSTATUS_HJDIS_MASK) {
+		dev_err(&ctrl->dev, "Hotjoin disabled by i3c master\n");
+		return -EINVAL;
+	}
+
+	ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+					 !(val & I3C_SCTRL_EVENT_MASK), 0, 10000);
+	if (ret) {
+		dev_err(&ctrl->dev, "Timeout when polling for none event pending");
+		return -ENAVAIL;
+	}
+
+	cfg = readl_relaxed(svc->regs + I3C_SCONFIG);
+	cfg |= I3C_SCONFIG_OFFLINE_MASK;
+	writel_relaxed(cfg, svc->regs + I3C_SCONFIG);
+
+	val &= ~(I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK);
+	val |= FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_HOTJOIN);
+	/* Issue hotjoin*/
+	writel_relaxed(val, svc->regs + I3C_SCTRL);
+
+	ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+					 !(val & I3C_SCTRL_EVENT_MASK), 0, 100000);
+	if (ret) {
+		val &= ~FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_MASK);
+		writel_relaxed(val, svc->regs + I3C_SCTRL);
+		dev_err(&ctrl->dev, "Timeout when polling for HOTJOIN finish\n");
+		return -EINVAL;
+	}
+
+	val = readl_relaxed(svc->regs + I3C_SSTATUS);
+	val = FIELD_GET(I3C_SSTATUS_EVDET_MASK, val);
+	if (val != I3C_SSTATUS_EVDET_ACKED) {
+		dev_err(&ctrl->dev, "Master NACKED hotjoin request\n");
+		return -EINVAL;
+	}
+
+	writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTSET);
+	ret = wait_for_completion_timeout(&svc->dacomplete, msecs_to_jiffies(100));
+	writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTCLR);
+	if (!ret) {
+		dev_err(&ctrl->dev, "wait for da assignment timeout\n");
+		return -EIO;
+	}
+
+	val = readl_relaxed(svc->regs + I3C_SMAPCTRL0);
+	val = FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val);
+	dev_info(&ctrl->dev, "Get dynamtic address 0x%x\n", val);
+	return 0;
+}
+
+static int svc_i3c_set_status_format1(struct i3c_target_ctrl *ctrl, u16 status)
+{
+	struct svc_i3c_target *svc;
+	unsigned long flags;
+	u32 val;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	spin_lock_irqsave(&svc->ctrl_lock, flags);
+	val = readl_relaxed(svc->regs + I3C_SCTRL);
+	val &= 0xFFFF;
+	val |= status << 16;
+	writel_relaxed(val, svc->regs + I3C_SCTRL);
+	spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+	return 0;
+}
+
+static u16 svc_i3c_get_status_format1(struct i3c_target_ctrl *ctrl)
+{
+	struct svc_i3c_target *svc;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	return readl_relaxed(svc->regs + I3C_SCTRL) >> 16;
+}
+
+static u8 svc_i3c_get_addr(struct i3c_target_ctrl *ctrl)
+{
+	struct svc_i3c_target *svc;
+	int val;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	val = readl_relaxed(svc->regs + I3C_SMAPCTRL0);
+
+	if (val & I3C_SMAPCTRL0_ENA_MASK)
+		return FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val);
+
+	return 0;
+}
+
+static int svc_i3c_fifo_status(struct i3c_target_ctrl *ctrl, bool tx)
+{
+	struct svc_i3c_target *svc;
+	int val;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	val = readl_relaxed(svc->regs + I3C_SDATACTRL);
+
+	if (tx)
+		return FIELD_GET(I3C_SDATACTRL_TXCOUNT_MASK, val);
+	else
+		return FIELD_GET(I3C_SDATACTRL_RXCOUNT_MASK, val);
+}
+
+static struct i3c_target_ctrl_ops svc_i3c_target_ops = {
+	.set_config = svc_i3c_target_set_config,
+	.enable = svc_i3c_target_enable,
+	.disable = svc_i3c_target_disable,
+	.queue = svc_i3c_target_queue,
+	.dequeue = svc_i3c_dequeue,
+	.raise_ibi = svc_i3c_target_raise_ibi,
+	.fifo_flush = svc_i3c_target_fifo_flush,
+	.cancel_all_reqs = svc_i3c_cancel_all_reqs,
+	.get_features = svc_i3c_get_features,
+	.hotjoin = svc_i3c_hotjoin,
+	.fifo_status = svc_i3c_fifo_status,
+	.set_status_format1 = svc_i3c_set_status_format1,
+	.get_status_format1 = svc_i3c_get_status_format1,
+	.get_addr = svc_i3c_get_addr,
+};
+
+int svc_i3c_target_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct i3c_target_ctrl *target;
+	struct svc_i3c_target *svc;
+	int ret;
+	u32 val;
+
+	svc = devm_kzalloc(dev, sizeof(*svc), GFP_KERNEL);
+	if (!svc)
+		return -ENOMEM;
+
+	svc->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(svc->regs))
+		return PTR_ERR(svc->regs);
+
+	svc->clks[PCLK].id = "pclk";
+	svc->clks[FCLK].id = "fast_clk";
+	svc->clks[SCLK].id = "slow_clk";
+
+	ret = devm_clk_bulk_get(dev, MAXCLK, svc->clks);
+	if (ret < 0) {
+		dev_err(dev, "fail get clks: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_bulk_prepare_enable(MAXCLK, svc->clks);
+	if (ret < 0) {
+		dev_err(dev, "fail enable clks: %d\n", ret);
+		return ret;
+	}
+
+	svc->irq = platform_get_irq(pdev, 0);
+	if (svc->irq < 0)
+		return svc->irq;
+
+	INIT_LIST_HEAD(&svc->txq);
+	INIT_LIST_HEAD(&svc->rxq);
+	INIT_LIST_HEAD(&svc->cq);
+	spin_lock_init(&svc->txq_lock);
+	spin_lock_init(&svc->rxq_lock);
+	spin_lock_init(&svc->cq_lock);
+	spin_lock_init(&svc->ctrl_lock);
+
+	init_completion(&svc->dacomplete);
+
+	INIT_WORK(&svc->work, svc_i3c_target_complete);
+	svc->workqueue = alloc_workqueue("%s-cq", 0, 0, dev_name(dev));
+	if (!svc->workqueue)
+		return -ENOMEM;
+
+	/* Disable all IRQ */
+	writel_relaxed(0xFFFFFFFF, svc->regs + I3C_SINTCLR);
+
+	val = readl_relaxed(svc->regs + I3C_SCAPABILITIES);
+	svc->features.tx_fifo_sz  = FIELD_GET(I3C_SCAPABILITIES_FIFOTX_MASK, val);
+	svc->features.tx_fifo_sz = 2 << svc->features.tx_fifo_sz;
+
+	svc->features.rx_fifo_sz = FIELD_GET(I3C_SCAPABILITIES_FIFORX_MASK, val);
+	svc->features.rx_fifo_sz = 2 << svc->features.rx_fifo_sz;
+
+	ret = devm_request_irq(dev, svc->irq, svc_i3c_target_irq_handler, 0, "svc-i3c-irq", svc);
+	if (ret)
+		return -ENOENT;
+
+	target = devm_i3c_target_ctrl_create(dev, &svc_i3c_target_ops);
+	if (!target)
+		return -ENOMEM;
+
+	dev_set_drvdata(&target->dev, svc);
+
+	return 0;
+}
+
+void svc_i3c_target_remove(struct platform_device *pdev)
+{
+}
diff --git a/drivers/i3c/master/svc-i3c.h b/drivers/i3c/master/svc-i3c.h
index b9e1dce4b2109..1bb77474f36eb 100644
--- a/drivers/i3c/master/svc-i3c.h
+++ b/drivers/i3c/master/svc-i3c.h
@@ -8,5 +8,8 @@ void svc_i3c_master_remove(struct platform_device *pdev);
 int svc_i3c_master_runtime_suspend(struct device *dev);
 int svc_i3c_master_runtime_resume(struct device *dev);
 
+int svc_i3c_target_probe(struct platform_device *pdev);
+void svc_i3c_target_remove(struct platform_device *pdev);
+
 #endif
 
-- 
2.34.1



From Frank.Li at nxp.com  Tue May  7 13:43:09 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:09 -0400
Subject: [PATCH resend v9 6/8] i3c: target: func: add tty driver
In-Reply-To: <20240507204311.2898714-1-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
Message-ID: <20240507204311.2898714-7-Frank.Li@nxp.com>

Add tty over I3C target function driver.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Change from v8 to v9
    - none
    
    Change from v7 to v8
    - reorder header file
    - add missed header files
    
    Change from v5 to v7
    - none
    Change from v4 to v5
    - remove void*
    - include bitfield.h
    - remove extra ()
    - oneline for struct ttyi3c_port *sport

 drivers/i3c/Kconfig       |   3 +
 drivers/i3c/Makefile      |   1 +
 drivers/i3c/func/Kconfig  |   9 +
 drivers/i3c/func/Makefile |   3 +
 drivers/i3c/func/tty.c    | 481 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 497 insertions(+)
 create mode 100644 drivers/i3c/func/Kconfig
 create mode 100644 drivers/i3c/func/Makefile
 create mode 100644 drivers/i3c/func/tty.c

diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index d59a7eb83d13a..fca808cda87b3 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -48,3 +48,6 @@ config I3C_TARGET_CONFIGFS
 	  the target function and used to bind the function with a target
 	  controller.
 
+if I3C_TARGET
+source "drivers/i3c/func/Kconfig"
+endif # I3C_TARGET
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index c275aeae8970c..11f026d6876fe 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_I3C)		+= i3c.o
 obj-$(CONFIG_I3C_TARGET)                += target.o
 obj-$(CONFIG_I3C_TARGET_CONFIGFS)       += i3c-cfs.o
 obj-$(CONFIG_I3C)		+= master/
+obj-$(CONFIG_I3C_TARGET)	+= func/
diff --git a/drivers/i3c/func/Kconfig b/drivers/i3c/func/Kconfig
new file mode 100644
index 0000000000000..7115129eb7d5a
--- /dev/null
+++ b/drivers/i3c/func/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config I3C_TARGET_FUNC_TTY
+	tristate "I3C target tty driver"
+	depends on I3C_TARGET
+	help
+	  I3C Target TTY Function Driver.
+
+	  General TTY over I3C target controller function drivers.
diff --git a/drivers/i3c/func/Makefile b/drivers/i3c/func/Makefile
new file mode 100644
index 0000000000000..16b3b9301496b
--- /dev/null
+++ b/drivers/i3c/func/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_I3C_TARGET_FUNC_TTY)              += tty.o
diff --git a/drivers/i3c/func/tty.c b/drivers/i3c/func/tty.c
new file mode 100644
index 0000000000000..2cf25c8d49f2b
--- /dev/null
+++ b/drivers/i3c/func/tty.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li at nxp.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/idr.h>
+#include <linux/iopoll.h>
+#include <linux/i3c/target.h>
+#include <linux/module.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tty_flip.h>
+#include <linux/workqueue.h>
+
+static DEFINE_IDR(i3c_tty_minors);
+
+static struct tty_driver *i3c_tty_driver;
+
+#define I3C_TTY_MINORS		8
+
+#define I3C_TX_NOEMPTY		BIT(0)
+#define I3C_TTY_TRANS_SIZE	16
+#define I3C_TTY_IBI_TX		BIT(0)
+
+struct ttyi3c_port {
+	struct tty_port port;
+	int minor;
+	struct i3c_target_func *i3cdev;
+	struct completion txcomplete;
+	spinlock_t xlock;
+	void *buffer;
+	struct work_struct work;
+	u16 status;
+	struct i3c_request *req;
+};
+
+static void i3c_target_tty_rx_complete(struct i3c_request *req)
+{
+	struct ttyi3c_port *port = req->context;
+
+	if (req->status == I3C_REQUEST_CANCEL) {
+		i3c_target_ctrl_free_request(req);
+		return;
+	}
+
+	tty_insert_flip_string(&port->port, req->buf, req->actual);
+	tty_flip_buffer_push(&port->port);
+
+	req->actual = 0;
+	req->status = 0;
+	i3c_target_ctrl_queue(req, GFP_KERNEL);
+}
+
+static void i3c_target_tty_tx_complete(struct i3c_request *req)
+{
+	struct ttyi3c_port *sport = req->context;
+	unsigned long flags;
+
+	if (req->status == I3C_REQUEST_CANCEL) {
+		i3c_target_ctrl_free_request(req);
+		return;
+	}
+
+	spin_lock_irqsave(&sport->xlock, flags);
+	kfifo_dma_out_finish(&sport->port.xmit_fifo, req->actual);
+	sport->req = NULL;
+
+	if (kfifo_is_empty(&sport->port.xmit_fifo))
+		complete(&sport->txcomplete);
+	else
+		queue_work(system_unbound_wq, &sport->work);
+
+	if (kfifo_len(&sport->port.xmit_fifo) < WAKEUP_CHARS)
+		tty_port_tty_wakeup(&sport->port);
+	spin_unlock_irqrestore(&sport->xlock, flags);
+
+	i3c_target_ctrl_free_request(req);
+}
+
+static void i3c_target_tty_i3c_work(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, work);
+	struct i3c_request *req = sport->req;
+	struct scatterlist sg[1];
+	unsigned int nents;
+	u8 ibi;
+
+	if (kfifo_is_empty(&sport->port.xmit_fifo))
+		return;
+
+	if (!req) {
+		req = i3c_target_ctrl_alloc_request(sport->i3cdev->ctrl, GFP_KERNEL);
+		if (!req)
+			return;
+
+		sg_init_table(sg, ARRAY_SIZE(sg));
+		nents = kfifo_dma_out_prepare(&sport->port.xmit_fifo, sg, ARRAY_SIZE(sg),
+					      UART_XMIT_SIZE);
+		if (!nents)
+			goto err;
+
+		req->length = sg->length;
+		req->buf =  sg_virt(sg);
+
+		req->complete = i3c_target_tty_tx_complete;
+		req->context = sport;
+		req->tx = true;
+
+		if (i3c_target_ctrl_queue(req, GFP_KERNEL))
+			goto err;
+
+		sport->req = req;
+	}
+
+	ibi = I3C_TTY_IBI_TX;
+	i3c_target_ctrl_raise_ibi(sport->i3cdev->ctrl, &ibi, 1);
+
+	return;
+
+err:
+	i3c_target_ctrl_free_request(req);
+}
+
+static int i3c_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+	const struct i3c_target_ctrl_features *feature;
+	struct i3c_target_func *func = sport->i3cdev;
+	struct i3c_request *req;
+	int rxfifo_size;
+	int offset = 0;
+	int ret;
+
+	feature = i3c_target_ctrl_get_features(func->ctrl);
+	if (!feature)
+		return -EINVAL;
+
+	ret = tty_port_alloc_xmit_buf(port);
+	if (ret)
+		return ret;
+
+	sport->buffer = (void *)get_zeroed_page(GFP_KERNEL);
+	if (!sport->buffer)
+		goto err_alloc_rx_buf;
+
+	rxfifo_size = feature->rx_fifo_sz;
+
+	if (!rxfifo_size)
+		rxfifo_size = I3C_TTY_TRANS_SIZE;
+
+	do {
+		req = i3c_target_ctrl_alloc_request(func->ctrl, GFP_KERNEL);
+		if (!req)
+			goto err_alloc_req;
+
+		req->buf = sport->buffer + offset;
+		req->length = rxfifo_size;
+		req->context = sport;
+		req->complete = i3c_target_tty_rx_complete;
+		offset += rxfifo_size;
+
+		if (i3c_target_ctrl_queue(req, GFP_KERNEL))
+			goto err_alloc_req;
+	} while (req && offset + rxfifo_size < UART_XMIT_SIZE);
+
+	reinit_completion(&sport->txcomplete);
+
+	return 0;
+
+err_alloc_req:
+	i3c_target_ctrl_cancel_all_reqs(func->ctrl, false);
+	free_page((unsigned long)sport->buffer);
+err_alloc_rx_buf:
+	tty_port_free_xmit_buf(port);
+	return -ENOMEM;
+}
+
+static void i3c_port_shutdown(struct tty_port *port)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	cancel_work_sync(&sport->work);
+
+	i3c_target_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, true);
+	i3c_target_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, false);
+
+	i3c_target_ctrl_fifo_flush(sport->i3cdev->ctrl, true);
+	i3c_target_ctrl_fifo_flush(sport->i3cdev->ctrl, false);
+
+	tty_port_free_xmit_buf(port);
+	free_page((unsigned long)sport->buffer);
+}
+
+static void i3c_port_destruct(struct tty_port *port)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	idr_remove(&i3c_tty_minors, sport->minor);
+}
+
+static const struct tty_port_operations i3c_port_ops = {
+	.shutdown = i3c_port_shutdown,
+	.activate = i3c_port_activate,
+	.destruct = i3c_port_destruct,
+};
+
+static int i3c_target_tty_bind(struct i3c_target_func *func)
+{
+	struct ttyi3c_port *sport;
+	struct device *tty_dev;
+	int minor;
+	int ret;
+
+	sport = dev_get_drvdata(&func->dev);
+
+	if (i3c_target_ctrl_set_config(func->ctrl, func)) {
+		dev_err(&func->dev, "failed to set i3c config\n");
+		return -EINVAL;
+	}
+
+	spin_lock_init(&sport->xlock);
+	init_completion(&sport->txcomplete);
+
+	ret = minor = idr_alloc(&i3c_tty_minors, sport, 0, I3C_TTY_MINORS, GFP_KERNEL);
+
+	if (minor < 0)
+		goto err_idr_alloc;
+
+	tty_port_init(&sport->port);
+	sport->port.ops = &i3c_port_ops;
+
+	tty_dev = tty_port_register_device(&sport->port, i3c_tty_driver, minor,
+					   &func->dev);
+	if (IS_ERR(tty_dev)) {
+		ret = PTR_ERR(tty_dev);
+		goto err_register_port;
+	}
+
+	sport->minor = minor;
+	ret = i3c_target_ctrl_enable(func->ctrl);
+	if (ret)
+		goto err_ctrl_enable;
+
+	return 0;
+
+err_ctrl_enable:
+	tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
+err_register_port:
+	idr_remove(&i3c_tty_minors, sport->minor);
+err_idr_alloc:
+	i3c_target_ctrl_cancel_all_reqs(func->ctrl, false);
+	dev_err(&func->dev, "bind failure\n");
+
+	return ret;
+}
+
+static void i3c_target_tty_unbind(struct i3c_target_func *func)
+{
+	struct ttyi3c_port *sport;
+
+	sport = dev_get_drvdata(&func->dev);
+
+	cancel_work_sync(&sport->work);
+
+	i3c_target_ctrl_disable(func->ctrl);
+	i3c_target_ctrl_cancel_all_reqs(func->ctrl, 0);
+	i3c_target_ctrl_cancel_all_reqs(func->ctrl, 1);
+
+	tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
+
+	free_page((unsigned long)sport->buffer);
+}
+
+static struct i3c_target_func_ops i3c_func_ops = {
+	.bind   = i3c_target_tty_bind,
+	.unbind = i3c_target_tty_unbind,
+};
+
+static int i3c_tty_probe(struct i3c_target_func *func)
+{
+	struct device *dev = &func->dev;
+	struct ttyi3c_port *port;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->i3cdev = func;
+	dev_set_drvdata(&func->dev, port);
+
+	INIT_WORK(&port->work, i3c_target_tty_i3c_work);
+
+	return 0;
+}
+
+static ssize_t i3c_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+	unsigned long flags;
+	bool is_empty;
+	int ret = 0;
+
+	spin_lock_irqsave(&sport->xlock, flags);
+	ret = kfifo_in(&sport->port.xmit_fifo, buf, count);
+	is_empty = kfifo_is_empty(&sport->port.xmit_fifo);
+	i3c_target_ctrl_set_status_format1(sport->i3cdev->ctrl, sport->status | I3C_TX_NOEMPTY);
+	spin_unlock_irqrestore(&sport->xlock, flags);
+
+	if (!is_empty)
+		queue_work(system_unbound_wq, &sport->work);
+
+	return ret;
+}
+
+static int i3c_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&sport->xlock, flags);
+	ret = kfifo_put(&sport->port.xmit_fifo, ch);
+	spin_unlock_irqrestore(&sport->xlock, flags);
+
+	return ret;
+}
+
+static void i3c_flush_chars(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->xlock, flags);
+	if (!kfifo_is_empty(&sport->port.xmit_fifo))
+		queue_work(system_unbound_wq, &sport->work);
+	spin_unlock_irqrestore(&sport->xlock, flags);
+}
+
+static unsigned int i3c_write_room(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	return kfifo_avail(&sport->port.xmit_fifo);
+}
+
+static void i3c_throttle(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	i3c_target_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, false);
+}
+
+static void i3c_unthrottle(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	i3c_port_activate(&sport->port, tty);
+}
+
+static int i3c_open(struct tty_struct *tty, struct file *filp)
+{
+	struct ttyi3c_port *sport = container_of(tty->port, struct ttyi3c_port, port);
+	int ret;
+
+	tty->driver_data = sport;
+
+	if (!i3c_target_ctrl_get_addr(sport->i3cdev->ctrl)) {
+		dev_dbg(&sport->i3cdev->dev, "No target addr assigned, try hotjoin");
+		ret = i3c_target_ctrl_hotjoin(sport->i3cdev->ctrl);
+		if (ret) {
+			dev_err(&sport->i3cdev->dev, "Hotjoin failure, check connection");
+			return ret;
+		}
+	}
+
+	return tty_port_open(&sport->port, tty, filp);
+}
+
+static void i3c_close(struct tty_struct *tty, struct file *filp)
+{
+	tty_port_close(tty->port, tty, filp);
+}
+
+static void i3c_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+	int val;
+	int ret;
+	u8 ibi = I3C_TTY_IBI_TX;
+	int retry = 100;
+
+	if (!kfifo_is_empty(&sport->port.xmit_fifo)) {
+		do {
+			ret = wait_for_completion_timeout(&sport->txcomplete, timeout / 100);
+			if (ret)
+				break;
+			i3c_target_ctrl_raise_ibi(sport->i3cdev->ctrl, &ibi, 1);
+		} while (retry--);
+
+		reinit_completion(&sport->txcomplete);
+	}
+
+	read_poll_timeout(i3c_target_ctrl_fifo_status, val, !val, 100, timeout, false,
+			  sport->i3cdev->ctrl, true);
+
+	i3c_target_ctrl_set_status_format1(sport->i3cdev->ctrl, sport->status & ~I3C_TX_NOEMPTY);
+}
+
+static const struct tty_operations i3c_tty_ops = {
+	.open = i3c_open,
+	.close = i3c_close,
+	.write = i3c_write,
+	.put_char = i3c_put_char,
+	.flush_chars = i3c_flush_chars,
+	.write_room = i3c_write_room,
+	.throttle = i3c_throttle,
+	.unthrottle = i3c_unthrottle,
+	.wait_until_sent = i3c_wait_until_sent,
+};
+
+DECLARE_I3C_TARGET_FUNC(tty, i3c_tty_probe, NULL, &i3c_func_ops);
+
+static int __init i3c_tty_init(void)
+{
+	int ret;
+
+	i3c_tty_driver = tty_alloc_driver(
+		I3C_TTY_MINORS, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
+
+	if (IS_ERR(i3c_tty_driver))
+		return PTR_ERR(i3c_tty_driver);
+
+	i3c_tty_driver->driver_name = "ttySI3C", i3c_tty_driver->name = "ttySI3C",
+	i3c_tty_driver->minor_start = 0,
+	i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
+	i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
+	i3c_tty_driver->init_termios = tty_std_termios;
+	i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
+					       CLOCAL;
+	i3c_tty_driver->init_termios.c_lflag = 0;
+
+	tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
+
+	ret = tty_register_driver(i3c_tty_driver);
+	if (ret)
+		goto err_register_tty_driver;
+
+	ret = i3c_target_func_register_driver(&ttyi3c_func);
+	if (ret)
+		goto err_register_i3c_driver;
+
+	return 0;
+
+err_register_i3c_driver:
+	tty_unregister_driver(i3c_tty_driver);
+
+err_register_tty_driver:
+	tty_driver_kref_put(i3c_tty_driver);
+
+	return ret;
+}
+
+static void __exit i3c_tty_exit(void)
+{
+	i3c_target_func_unregister_driver(&ttyi3c_func);
+	tty_unregister_driver(i3c_tty_driver);
+	tty_driver_kref_put(i3c_tty_driver);
+	idr_destroy(&i3c_tty_minors);
+}
+
+module_init(i3c_tty_init);
+module_exit(i3c_tty_exit);
+
+MODULE_LICENSE("GPL");
+
-- 
2.34.1



From Frank.Li at nxp.com  Tue May  7 13:43:10 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:10 -0400
Subject: [PATCH resend v9 7/8] i3c: add API i3c_dev_gettstatus_format1() to get target device status
In-Reply-To: <20240507204311.2898714-1-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
Message-ID: <20240507204311.2898714-8-Frank.Li@nxp.com>

I3C standard 5.1.9.3.15 Get Device Status (GETSTATUS):
Get request for one I3C Target Device to return its current status.

Add API to fetch it with format1.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/device.c       | 24 ++++++++++++++++++++++++
 drivers/i3c/internals.h    |  1 +
 drivers/i3c/master.c       | 26 ++++++++++++++++++++++++++
 include/linux/i3c/device.h |  1 +
 4 files changed, 52 insertions(+)

diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c
index 1a6a8703dbc3a..aa26cf50ab9c6 100644
--- a/drivers/i3c/device.c
+++ b/drivers/i3c/device.c
@@ -196,6 +196,30 @@ void i3c_device_free_ibi(struct i3c_device *dev)
 }
 EXPORT_SYMBOL_GPL(i3c_device_free_ibi);
 
+/**
+ * i3c_device_getstatus_format1() - Get device status with format 1.
+ * @dev: device for which you want to get status.
+ * @status: I3C status format 1
+ *
+ * Return: 0 in case of success, a negative error core otherwise.
+ */
+int i3c_device_getstatus_format1(struct i3c_device *dev, u16 *status)
+{
+	int ret = -EINVAL;
+
+	if (!status)
+		return -EINVAL;
+
+	i3c_bus_normaluse_lock(dev->bus);
+	if (dev->desc)
+		ret = i3c_dev_getstatus_format1_locked(dev->desc, status);
+
+	i3c_bus_normaluse_unlock(dev->bus);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(i3c_device_getstatus_format1);
+
 /**
  * i3cdev_to_dev() - Returns the device embedded in @i3cdev
  * @i3cdev: I3C device
diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h
index 908a807badaf9..976ad26ca79c2 100644
--- a/drivers/i3c/internals.h
+++ b/drivers/i3c/internals.h
@@ -24,4 +24,5 @@ int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev);
 int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
 			       const struct i3c_ibi_setup *req);
 void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev);
+int i3c_dev_getstatus_format1_locked(struct i3c_dev_desc *dev, u16 *status);
 #endif /* I3C_INTERNAL_H */
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 3afa530c5e322..ff61a218bcc6d 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2988,6 +2988,32 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
 	dev->ibi = NULL;
 }
 
+int i3c_dev_getstatus_format1_locked(struct i3c_dev_desc *dev, u16 *status)
+{
+	struct i3c_master_controller *master = i3c_dev_get_master(dev);
+	struct i3c_ccc_getstatus *format1;
+	struct i3c_ccc_cmd_dest dest;
+	struct i3c_ccc_cmd cmd;
+	int ret;
+
+	format1 = i3c_ccc_cmd_dest_init(&dest, dev->info.dyn_addr, sizeof(*format1));
+	if (!format1)
+		return -ENOMEM;
+
+	i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETSTATUS, &dest, 1);
+
+	ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
+	if (ret)
+		goto out;
+
+	*status = be16_to_cpu(format1->status);
+
+out:
+	i3c_ccc_cmd_dest_cleanup(&dest);
+
+	return ret;
+}
+
 static int __init i3c_init(void)
 {
 	int res;
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index e119f11948efe..66920045ff042 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -345,4 +345,5 @@ void i3c_device_free_ibi(struct i3c_device *dev);
 int i3c_device_enable_ibi(struct i3c_device *dev);
 int i3c_device_disable_ibi(struct i3c_device *dev);
 
+int i3c_device_getstatus_format1(struct i3c_device *dev, u16 *status);
 #endif /* I3C_DEV_H */
-- 
2.34.1



From Frank.Li at nxp.com  Tue May  7 13:43:11 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Tue,  7 May 2024 16:43:11 -0400
Subject: [PATCH resend v9 8/8] tty: i3c: add TTY over I3C master support
In-Reply-To: <20240507204311.2898714-1-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
Message-ID: <20240507204311.2898714-9-Frank.Li@nxp.com>

In typical embedded Linux systems, UART consoles require at least two pins,
TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
present, we can save these two pins by using this driver. Pins is crucial
resources, especially in small chip packages.

This introduces support for using the I3C bus to transfer console tty data,
effectively replacing the need for dedicated UART pins. This not only
conserves valuable pin resources but also facilitates testing of I3C's
advanced features, including early termination, in-band interrupt (IBI)
support, and the creation of more complex data patterns. Additionally,
it aids in identifying and addressing issues within the I3C controller
driver.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Notes:
        Version number use i3c target patches.
        Change from v8 to v9
        - none
    
        Change from v7 to v8
        - reorder head files
        - add missed head files
    
        Change from v6 to v7
        - try to fixed build warning
    	https://lore.kernel.org/imx/20240202230925.1000659-1-Frank.Li at nxp.com/T/#m4e35da31a4226186b3dc6bc8f2fba28f58be1188
        but I can't reproduce this error in my machine with powerpc-linux-gcc (GCC) 13.2.0
        kernel test robot have not show reproduce step.
        But anyway, status may not init if code through continue branch. So
        Move status = I3C_TTY_TARGET_RX_READY; before continue branch.
    
        Change from v5 to v6
        -none
    
        Change from v4 to v5
        - add missed header file
        - add define for BIT(0)
        - oneline for struct ttyi3c_port *sport
    
        Change from v3 to v4
        - add static at i3c_remove()
        Change v2
        - using system_unbound_wq working queue
        - fixed accoring to Jiri Slaby's comments
    
        Change before send with i3c target support
    
        Change from v4 to v5
        - send in i3c improvememtn patches.
    
        Change from v2 to v4
        - none
    
        Change from v1 to v2
        - update commit message.
        - using goto for err handle
        - using one working queue for all tty-i3c device
        - fixed typo found by js
        - update kconfig help
        - using kfifo
    
        Still below items not be fixed (according to Jiri Slaby's comments)
        - rxwork thread: need trigger from two position.
        - common thread queue: need some suggestion
    
    Notes:
        Version number use i3c target patches.
        Change from v3 to v4
        - add static at i3c_remove()
        Change v2
        - using system_unbound_wq working queue
        - fixed accoring to Jiri Slaby's comments
    
        Change before send with i3c target support
    
        Change from v4 to v5
        - send in i3c improvememtn patches.
    
        Change from v2 to v4
        - none
    
        Change from v1 to v2
        - update commit message.
        - using goto for err handle
        - using one working queue for all tty-i3c device
        - fixed typo found by js
        - update kconfig help
        - using kfifo
    
        Still below items not be fixed (according to Jiri Slaby's comments)
        - rxwork thread: need trigger from two position.
        - common thread queue: need some suggestion

 drivers/tty/Kconfig   |  13 ++
 drivers/tty/Makefile  |   1 +
 drivers/tty/i3c_tty.c | 432 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 446 insertions(+)
 create mode 100644 drivers/tty/i3c_tty.c

diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 5646dc6242cd9..9ab4cd480e9f8 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -412,6 +412,19 @@ config RPMSG_TTY
 	  To compile this driver as a module, choose M here: the module will be
 	  called rpmsg_tty.
 
+config I3C_TTY
+	tristate "TTY over I3C"
+	depends on I3C
+	help
+	  Select this option to use TTY over I3C master controller.
+
+	  This makes it possible for user-space programs to send and receive
+	  data as a standard tty protocol. I3C provide relatively higher data
+	  transfer rate and less pin numbers, SDA/SCL are shared with other
+	  devices.
+
+	  If unsure, say N
+
 endif # TTY
 
 source "drivers/tty/serdev/Kconfig"
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 07aca5184a55d..f329f9c7d308a 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -27,5 +27,6 @@ obj-$(CONFIG_GOLDFISH_TTY)	+= goldfish.o
 obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o
 obj-$(CONFIG_VCC)		+= vcc.o
 obj-$(CONFIG_RPMSG_TTY)		+= rpmsg_tty.o
+obj-$(CONFIG_I3C_TTY)		+= i3c_tty.o
 
 obj-y += ipwireless/
diff --git a/drivers/tty/i3c_tty.c b/drivers/tty/i3c_tty.c
new file mode 100644
index 0000000000000..235cd0524b0b7
--- /dev/null
+++ b/drivers/tty/i3c_tty.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li at nxp.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tty_flip.h>
+#include <linux/tty_port.h>
+#include <linux/workqueue.h>
+
+static DEFINE_IDR(i3c_tty_minors);
+static DEFINE_MUTEX(i3c_tty_minors_lock);
+
+static struct tty_driver *i3c_tty_driver;
+
+#define I3C_TTY_MINORS		8
+#define I3C_TTY_TRANS_SIZE	16
+#define I3C_TTY_RX_STOP		0
+#define I3C_TTY_RETRY		20
+#define I3C_TTY_YIELD_US	100
+#define I3C_TTY_TARGET_RX_READY	BIT(0)
+
+struct ttyi3c_port {
+	struct tty_port port;
+	int minor;
+	spinlock_t xlock; /* protect xmit */
+	u8 tx_buff[I3C_TTY_TRANS_SIZE];
+	u8 rx_buff[I3C_TTY_TRANS_SIZE];
+	struct i3c_device *i3cdev;
+	struct work_struct txwork;
+	struct work_struct rxwork;
+	struct completion txcomplete;
+	unsigned long status;
+	u32 buf_overrun;
+};
+
+static const struct i3c_device_id i3c_ids[] = {
+	I3C_DEVICE(0x011B, 0x1000, NULL),
+	{ /* sentinel */ },
+};
+
+static int i3c_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+	int ret;
+
+	ret = tty_port_alloc_xmit_buf(port);
+	if (ret < 0)
+		return ret;
+
+	sport->status = 0;
+
+	ret = i3c_device_enable_ibi(sport->i3cdev);
+	if (ret) {
+		tty_port_free_xmit_buf(port);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void i3c_port_shutdown(struct tty_port *port)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	i3c_device_disable_ibi(sport->i3cdev);
+	tty_port_free_xmit_buf(port);
+}
+
+static void i3c_port_destruct(struct tty_port *port)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	mutex_lock(&i3c_tty_minors_lock);
+	idr_remove(&i3c_tty_minors, sport->minor);
+	mutex_unlock(&i3c_tty_minors_lock);
+}
+
+static const struct tty_port_operations i3c_port_ops = {
+	.shutdown = i3c_port_shutdown,
+	.activate = i3c_port_activate,
+	.destruct = i3c_port_destruct,
+};
+
+static ssize_t i3c_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+	unsigned long flags;
+	bool is_empty;
+	int ret;
+
+	spin_lock_irqsave(&sport->xlock, flags);
+	ret = kfifo_in(&sport->port.xmit_fifo, buf, count);
+	is_empty = kfifo_is_empty(&sport->port.xmit_fifo);
+	spin_unlock_irqrestore(&sport->xlock, flags);
+
+	if (!is_empty)
+		queue_work(system_unbound_wq, &sport->txwork);
+
+	return ret;
+}
+
+static int i3c_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&sport->xlock, flags);
+	ret = kfifo_put(&sport->port.xmit_fifo, ch);
+	spin_unlock_irqrestore(&sport->xlock, flags);
+
+	return ret;
+}
+
+static void i3c_flush_chars(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	queue_work(system_unbound_wq, &sport->txwork);
+}
+
+static unsigned int i3c_write_room(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	return kfifo_avail(&sport->port.xmit_fifo);
+}
+
+static void i3c_throttle(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	clear_bit(I3C_TTY_RX_STOP, &sport->status);
+}
+
+static void i3c_unthrottle(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	set_bit(I3C_TTY_RX_STOP, &sport->status);
+
+	queue_work(system_unbound_wq, &sport->rxwork);
+}
+
+static int i3c_open(struct tty_struct *tty, struct file *filp)
+{
+	struct ttyi3c_port *sport = container_of(tty->port, struct ttyi3c_port, port);
+
+	tty->driver_data = sport;
+
+	return tty_port_open(&sport->port, tty, filp);
+}
+
+static void i3c_close(struct tty_struct *tty, struct file *filp)
+{
+	tty_port_close(tty->port, tty, filp);
+}
+
+static const struct tty_operations i3c_tty_ops = {
+	.open = i3c_open,
+	.close = i3c_close,
+	.write = i3c_write,
+	.put_char = i3c_put_char,
+	.flush_chars = i3c_flush_chars,
+	.write_room = i3c_write_room,
+	.throttle = i3c_throttle,
+	.unthrottle = i3c_unthrottle,
+};
+
+static void i3c_controller_irq_handler(struct i3c_device *dev,
+				       const struct i3c_ibi_payload *payload)
+{
+	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
+
+	/* i3c_unthrottle also queue the work to fetch pending data in target side */
+	queue_work(system_unbound_wq, &sport->rxwork);
+}
+
+static void tty_i3c_rxwork(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, rxwork);
+	struct i3c_priv_xfer xfers;
+	u32 retry = I3C_TTY_RETRY;
+	u16 status;
+	int ret;
+
+	memset(&xfers, 0, sizeof(xfers));
+	xfers.data.in = sport->rx_buff;
+	xfers.len = I3C_TTY_TRANS_SIZE;
+	xfers.rnw = 1;
+
+	do {
+		if (test_bit(I3C_TTY_RX_STOP, &sport->status))
+			break;
+
+		i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
+
+		status = I3C_TTY_TARGET_RX_READY;
+
+		if (xfers.actual_len) {
+			ret = tty_insert_flip_string(&sport->port, sport->rx_buff,
+						     xfers.actual_len);
+			if (ret < xfers.actual_len)
+				sport->buf_overrun++;
+
+			retry = I3C_TTY_RETRY;
+			continue;
+		}
+
+		i3c_device_getstatus_format1(sport->i3cdev, &status);
+		/*
+		 * Target side needs some time to fill data into fifo. Target side may not
+		 * have hardware update status in real time. Software update status always
+		 * needs some delays.
+		 *
+		 * Generally, target side have circular buffer in memory, it will be moved
+		 * into FIFO by CPU or DMA. 'status' just show if circular buffer empty. But
+		 * there are gap, especially CPU have not response irq to fill FIFO in time.
+		 * So xfers.actual will be zero, wait for little time to avoid flood
+		 * transfer in i3c bus.
+		 */
+		usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
+		retry--;
+
+	} while (retry && (status & I3C_TTY_TARGET_RX_READY));
+
+	tty_flip_buffer_push(&sport->port);
+}
+
+static void tty_i3c_txwork(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, txwork);
+	struct i3c_priv_xfer xfers;
+	u32 retry = I3C_TTY_RETRY;
+	unsigned long flags;
+	int ret;
+
+	xfers.rnw = 0;
+	xfers.data.out = sport->tx_buff;
+
+	while (!kfifo_is_empty(&sport->port.xmit_fifo)) {
+		spin_lock_irqsave(&sport->xlock, flags);
+		xfers.len = kfifo_out_peek(&sport->port.xmit_fifo, sport->tx_buff,
+					   I3C_TTY_TRANS_SIZE);
+		spin_unlock_irqrestore(&sport->xlock, flags);
+		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
+		if (ret) {
+			/*
+			 * Target side may not move data out of FIFO. delay can't resolve problem,
+			 * just reduce some possiblity. Target can't end I3C SDR mode write
+			 * transfer, discard data is reasonable when FIFO overrun.
+			 */
+			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
+			retry--;
+		} else {
+			retry = I3C_TTY_RETRY;
+		}
+
+		if (ret == 0 || retry == 0) {
+			/* when retry == 0, means need discard the data */
+			spin_lock_irqsave(&sport->xlock, flags);
+			ret = kfifo_out(&sport->port.xmit_fifo, sport->tx_buff, xfers.len);
+			spin_unlock_irqrestore(&sport->xlock, flags);
+		}
+	}
+
+	spin_lock_irqsave(&sport->xlock, flags);
+	if (kfifo_len(&sport->port.xmit_fifo) < WAKEUP_CHARS)
+		tty_port_tty_wakeup(&sport->port);
+	spin_unlock_irqrestore(&sport->xlock, flags);
+}
+
+static int i3c_probe(struct i3c_device *i3cdev)
+{
+	struct ttyi3c_port *sport;
+	struct device *tty_dev;
+	struct i3c_ibi_setup req;
+	int minor;
+	int ret;
+
+	sport = devm_kzalloc(&i3cdev->dev, sizeof(*sport), GFP_KERNEL);
+	if (!sport)
+		return -ENOMEM;
+
+	sport->i3cdev = i3cdev;
+
+	dev_set_drvdata(&i3cdev->dev, sport);
+
+	req.max_payload_len = 8;
+	req.num_slots = 4;
+	req.handler = &i3c_controller_irq_handler;
+
+	ret = i3c_device_request_ibi(i3cdev, &req);
+	if (ret)
+		return -EINVAL;
+
+	mutex_lock(&i3c_tty_minors_lock);
+	minor = idr_alloc(&i3c_tty_minors, sport, 0, I3C_TTY_MINORS, GFP_KERNEL);
+	mutex_unlock(&i3c_tty_minors_lock);
+
+	if (minor < 0) {
+		ret = -EINVAL;
+		goto err_idr_alloc;
+	}
+
+	spin_lock_init(&sport->xlock);
+	INIT_WORK(&sport->txwork, tty_i3c_txwork);
+	INIT_WORK(&sport->rxwork, tty_i3c_rxwork);
+	init_completion(&sport->txcomplete);
+
+	tty_port_init(&sport->port);
+	sport->port.ops = &i3c_port_ops;
+
+	tty_dev = tty_port_register_device(&sport->port, i3c_tty_driver, minor,
+					   &i3cdev->dev);
+	if (IS_ERR(tty_dev)) {
+		ret = PTR_ERR(tty_dev);
+		goto err_tty_port_register;
+	}
+
+	sport->minor = minor;
+
+	return 0;
+
+err_tty_port_register:
+	tty_port_put(&sport->port);
+
+	mutex_lock(&i3c_tty_minors_lock);
+	idr_remove(&i3c_tty_minors, minor);
+	mutex_unlock(&i3c_tty_minors_lock);
+
+err_idr_alloc:
+	i3c_device_free_ibi(i3cdev);
+
+	return ret;
+}
+
+static void i3c_remove(struct i3c_device *dev)
+{
+	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
+
+	tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
+	cancel_work_sync(&sport->txwork);
+
+	tty_port_put(&sport->port);
+
+	mutex_lock(&i3c_tty_minors_lock);
+	idr_remove(&i3c_tty_minors, sport->minor);
+	mutex_unlock(&i3c_tty_minors_lock);
+
+	i3c_device_free_ibi(sport->i3cdev);
+}
+
+static struct i3c_driver i3c_driver = {
+	.driver = {
+		.name = "ttyi3c",
+	},
+	.probe = i3c_probe,
+	.remove = i3c_remove,
+	.id_table = i3c_ids,
+};
+
+static int __init i3c_tty_init(void)
+{
+	int ret;
+
+	i3c_tty_driver = tty_alloc_driver(I3C_TTY_MINORS,
+					  TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
+
+	if (IS_ERR(i3c_tty_driver))
+		return PTR_ERR(i3c_tty_driver);
+
+	i3c_tty_driver->driver_name = "ttyI3C";
+	i3c_tty_driver->name = "ttyI3C";
+	i3c_tty_driver->minor_start = 0,
+	i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
+	i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
+	i3c_tty_driver->init_termios = tty_std_termios;
+	i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
+					       CLOCAL;
+	i3c_tty_driver->init_termios.c_lflag = 0;
+
+	tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
+
+	ret = tty_register_driver(i3c_tty_driver);
+	if (ret)
+		goto err_tty_register_driver;
+
+	ret = i3c_driver_register(&i3c_driver);
+	if (ret)
+		goto err_i3c_driver_register;
+
+	return 0;
+
+err_i3c_driver_register:
+	tty_unregister_driver(i3c_tty_driver);
+
+err_tty_register_driver:
+	tty_driver_kref_put(i3c_tty_driver);
+
+	return ret;
+}
+
+static void __exit i3c_tty_exit(void)
+{
+	i3c_driver_unregister(&i3c_driver);
+	tty_unregister_driver(i3c_tty_driver);
+	tty_driver_kref_put(i3c_tty_driver);
+	idr_destroy(&i3c_tty_minors);
+}
+
+module_init(i3c_tty_init);
+module_exit(i3c_tty_exit);
+
+MODULE_LICENSE("GPL");
-- 
2.34.1



From alexandre.belloni at bootlin.com  Tue May  7 14:05:40 2024
From: alexandre.belloni at bootlin.com (Alexandre Belloni)
Date: Tue, 7 May 2024 23:05:40 +0200
Subject: [PATCH v1] i3c: master: Enable runtime PM for master controller
In-Reply-To: <20240228093407.4038399-1-quic_msavaliy@quicinc.com>
References: <20240228093407.4038399-1-quic_msavaliy@quicinc.com>
Message-ID: <171511593074.988529.13434042703638402613.b4-ty@bootlin.com>

On Wed, 28 Feb 2024 15:04:07 +0530, Mukesh Kumar Savaliya wrote:
> Enable runtime PM for i3c master node during master registration time.
> 
> Sometimes i3c client device driver may want to control the PM of the
> parent (master) to perform the transactions and save the power in an
> efficient way by controlling the session. Hence device can call PM
> APIs by passing the parent node.
> 
> [...]

Applied, thanks!

[1/1] i3c: master: Enable runtime PM for master controller
      https://git.kernel.org/abelloni/c/fe23b69d78d7

Best regards,

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


From alexandre.belloni at bootlin.com  Tue May  7 14:10:57 2024
From: alexandre.belloni at bootlin.com (Alexandre Belloni)
Date: Tue, 7 May 2024 23:10:57 +0200
Subject: [PATCH] i3c: dw: Add hot-join support.
In-Reply-To: <20240429073624.256830-1-billy_tsai@aspeedtech.com>
References: <20240429073624.256830-1-billy_tsai@aspeedtech.com>
Message-ID: <171511624412.989201.16500909870135488623.b4-ty@bootlin.com>

On Mon, 29 Apr 2024 15:36:24 +0800, Billy Tsai wrote:
> Add hot-join support for dw i3c master controller.
> By default, the hot-join acknowledgment is disabled, and the hardware will
> automatically send the DISEC CCC when it receives the hot-join request.
> Users can use the sys entry to enable it.
> 
> 

Applied, thanks!

[1/1] i3c: dw: Add hot-join support.
      https://git.kernel.org/abelloni/c/acec16dbb338

Best regards,

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


From joshua.yeong at starfivetech.com  Sun May 12 20:16:05 2024
From: joshua.yeong at starfivetech.com (Joshua Yeong)
Date: Mon, 13 May 2024 03:16:05 +0000
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <20240507204311.2898714-2-Frank.Li@nxp.com>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
 <20240507204311.2898714-2-Frank.Li@nxp.com>
Message-ID: <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>

Joshua Yeong wrote:
> 
> Introduce a new target core layer in order to support target functions in linux
> kernel. This comprises the controller library and function library.
> Controller library implements functions specific to an target controller and
> function library implements functions specific to an target function.
> 
> Introduce a new configfs entry to configure the target function configuring
> and bind the target function with target controller.
> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>     Change from v8 to v9
>     -none
> 
>     Change from v7 to v8
>     Added missed head files
> 
>     Change from v5 to v6
>     - fixed build error when have not CONFIG_TARGET_CONFIGFS
>     | Reported-by: kernel test robot <lkp at intel.com>
>     | Closes: https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-
> lkp at intel.com/
> 
>     Chagne from v4 to v5
>     - add include <linux/slab.h> to fix build error
>     | Reported-by: kernel test robot <lkp at intel.com>
>     | Closes: https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> lkp at intel.com/
> 
>     Chagne from v4 to v8
>     - add include <linux/slab.h> to fix build error
>     | Reported-by: kernel test robot <lkp at intel.com>
>     | Closes: https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> lkp at intel.com/
> 
>  drivers/i3c/Kconfig        |  28 +-
>  drivers/i3c/Makefile       |   2 +
>  drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
>  drivers/i3c/target.c       | 453 ++++++++++++++++++++++++++++++
>  include/linux/i3c/target.h | 548
> +++++++++++++++++++++++++++++++++++++
>
> ...
>
> diff --git a/include/linux/i3c/target.h b/include/linux/i3c/target.h new file
> mode 100644 index 0000000000000..b0bf06685834c
> --- /dev/null
> +++ b/include/linux/i3c/target.h
>
> ...
>
> +/**
> + * struct i3c_target_ctrl_ops - set of function pointers for performing
> +controller operations
> + * @set_config: set I3C controller configuration
> + * @enable: enable I3C controller
> + * @disable: disable I3C controller
> + * @raise_ibi: raise IBI interrupt to master
> + * @alloc_request: allocate a i3c_request, optional, default use
> +kmalloc
> + * @free_request: free a i3c_request, default use kfree
> + * @queue: queue an I3C transfer
> + * @dequeue: dequeue an I3C transfer
> + * @cancel_all_reqs: call all pending requests
> + * @fifo_status: current FIFO status
> + * @fifo_flush: flush hardware FIFO
> + * @hotjoin: raise hotjoin request to master
> + * @set_status_format1: set i3c status format1
> + * @get_status_format1: get i3c status format1
> + * @get_addr: get i3c dynmatic address
> + * @get_features: ops to get the features supported by the I3C target
> +controller
> + * @owner: the module owner containing the ops  */ struct
> +i3c_target_ctrl_ops {
> +	int (*set_config)(struct i3c_target_ctrl *ctrl, struct i3c_target_func
> *func);
> +	int (*enable)(struct i3c_target_ctrl *ctrl);
> +	int (*disable)(struct i3c_target_ctrl *ctrl);
> +	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8 size);
> +
> +	struct i3c_request *(*alloc_request)(struct i3c_target_ctrl *ctrl, gfp_t
> gfp_flags);
> +	void (*free_request)(struct i3c_request *req);
> +
> +	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
> +	int (*dequeue)(struct i3c_request *req);
> +
> +	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool tx);
> +
> +	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
> +	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
> +	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
> +	int (*set_status_format1)(struct i3c_target_ctrl *ctrl, u16 status);
> +	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
> +	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
> +	const struct i3c_target_ctrl_features *(*get_features)(struct
> i3c_target_ctrl *ctrl);
> +	struct module *owner;
> +};

This structure looks very different from the master controller ops 'i3c_master_controller_ops'.
I think you could probably combine 'set_config, enable' into 'bus_init', 'disable' to 'bus_cleanup'.
Also using the 'struct i3c_priv_xfer' rather redefining another 'struct i3c_request' would be much cleaner.

In the master i3c side they don't abbreviate i3c_master_controller to i3c_master_ctrl. I think we should
do the same here to standardize it. Thanks

Regards,
Joshua

> --
> 2.34.1
> 
> 
> --
> linux-i3c mailing list
> linux-i3c at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-i3c


From Frank.li at nxp.com  Mon May 13 08:56:08 2024
From: Frank.li at nxp.com (Frank Li)
Date: Mon, 13 May 2024 11:56:08 -0400
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
 <20240507204311.2898714-2-Frank.Li@nxp.com>
 <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
Message-ID: <ZkI4GO4czdgs5/Xy@lizhi-Precision-Tower-5810>

On Mon, May 13, 2024 at 03:16:05AM +0000, Joshua Yeong wrote:
> Joshua Yeong wrote:
> > 
> > Introduce a new target core layer in order to support target functions in linux
> > kernel. This comprises the controller library and function library.
> > Controller library implements functions specific to an target controller and
> > function library implements functions specific to an target function.
> > 
> > Introduce a new configfs entry to configure the target function configuring
> > and bind the target function with target controller.
> > 
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> > 
> > Notes:
> >     Change from v8 to v9
> >     -none
> > 
> >     Change from v7 to v8
> >     Added missed head files
> > 
> >     Change from v5 to v6
> >     - fixed build error when have not CONFIG_TARGET_CONFIGFS
> >     | Reported-by: kernel test robot <lkp at intel.com>
> >     | Closes: https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-
> > lkp at intel.com/
> > 
> >     Chagne from v4 to v5
> >     - add include <linux/slab.h> to fix build error
> >     | Reported-by: kernel test robot <lkp at intel.com>
> >     | Closes: https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > lkp at intel.com/
> > 
> >     Chagne from v4 to v8
> >     - add include <linux/slab.h> to fix build error
> >     | Reported-by: kernel test robot <lkp at intel.com>
> >     | Closes: https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > lkp at intel.com/
> > 
> >  drivers/i3c/Kconfig        |  28 +-
> >  drivers/i3c/Makefile       |   2 +
> >  drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
> >  drivers/i3c/target.c       | 453 ++++++++++++++++++++++++++++++
> >  include/linux/i3c/target.h | 548
> > +++++++++++++++++++++++++++++++++++++
> >
> > ...
> >
> > diff --git a/include/linux/i3c/target.h b/include/linux/i3c/target.h new file
> > mode 100644 index 0000000000000..b0bf06685834c
> > --- /dev/null
> > +++ b/include/linux/i3c/target.h
> >
> > ...
> >
> > +/**
> > + * struct i3c_target_ctrl_ops - set of function pointers for performing
> > +controller operations
> > + * @set_config: set I3C controller configuration
> > + * @enable: enable I3C controller
> > + * @disable: disable I3C controller
> > + * @raise_ibi: raise IBI interrupt to master
> > + * @alloc_request: allocate a i3c_request, optional, default use
> > +kmalloc
> > + * @free_request: free a i3c_request, default use kfree
> > + * @queue: queue an I3C transfer
> > + * @dequeue: dequeue an I3C transfer
> > + * @cancel_all_reqs: call all pending requests
> > + * @fifo_status: current FIFO status
> > + * @fifo_flush: flush hardware FIFO
> > + * @hotjoin: raise hotjoin request to master
> > + * @set_status_format1: set i3c status format1
> > + * @get_status_format1: get i3c status format1
> > + * @get_addr: get i3c dynmatic address
> > + * @get_features: ops to get the features supported by the I3C target
> > +controller
> > + * @owner: the module owner containing the ops  */ struct
> > +i3c_target_ctrl_ops {
> > +	int (*set_config)(struct i3c_target_ctrl *ctrl, struct i3c_target_func
> > *func);
> > +	int (*enable)(struct i3c_target_ctrl *ctrl);
> > +	int (*disable)(struct i3c_target_ctrl *ctrl);
> > +	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8 size);
> > +
> > +	struct i3c_request *(*alloc_request)(struct i3c_target_ctrl *ctrl, gfp_t
> > gfp_flags);
> > +	void (*free_request)(struct i3c_request *req);
> > +
> > +	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
> > +	int (*dequeue)(struct i3c_request *req);
> > +
> > +	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool tx);
> > +
> > +	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
> > +	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
> > +	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
> > +	int (*set_status_format1)(struct i3c_target_ctrl *ctrl, u16 status);
> > +	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
> > +	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
> > +	const struct i3c_target_ctrl_features *(*get_features)(struct
> > i3c_target_ctrl *ctrl);
> > +	struct module *owner;
> > +};
> 
> This structure looks very different from the master controller ops 'i3c_master_controller_ops'.
> I think you could probably combine 'set_config, enable' into 'bus_init', 'disable' to 'bus_cleanup'.
> Also using the 'struct i3c_priv_xfer' rather redefining another 'struct i3c_request' would be much cleaner.

Thanks, let me think this. i3c_priv_xter may include read and write, 
or write and read. I3C is quite fast. Most time software are not quite
enough to handle it in time.

If data len bigger than FIFO size, it have to use DMA to transfer it
because there are not flow controler at target side (write direction).
Read, hardware to use early terminate to tell host read too fast. but
for write, 9th bit is parit check bit. Data will be lost if software
have not read from FIFO in time. If use external DMA to do that, it needs
switch dma channel(generally, DMA rx and tx are two channel). switch
channel automatically are quite challenge at current dma engine
framework. 

Of course it will be possible if hardware implement simpilar I3C HCI
command queue. But I still not found any hardware that can do that yet.

If only limited data size to FIFO size, there are still limiation for 
xfer.  if write, read, write, read, two write may combined in one FIFO,
target driver may not split it which depend on hardware detect repeat
start and insert something into fifo.

It is hard to support all user case by I3C target software in linux.

write follow by read, generally used from get hardware register value
from target side, which almost impossible by software, it is not quick
enough to parse (write value) then send back data. 

> 
> In the master i3c side they don't abbreviate i3c_master_controller to i3c_master_ctrl. I think we should
> do the same here to standardize it. Thanks

The problem is in I3C spec: "master" already depericated

from i3c spec 1.1.1

"Controller: An I3C Device that is capable of controlling the I3C Bus."

master/slave => controller/target. 

It will become more confused if i3c_target_controller. "controller"
are ambiguity.

If "master" => "host" would be better situation, but not happen. A point, 
I use "ctrl"  here to avoid I3C spec defined "controller" term.

Anyway, I hope more people that involve target support discussion. 

> 
> Regards,
> Joshua
> 
> > --
> > 2.34.1
> > 
> > 
> > --
> > linux-i3c mailing list
> > linux-i3c at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-i3c


From joshua.yeong at starfivetech.com  Mon May 13 09:18:00 2024
From: joshua.yeong at starfivetech.com (Joshua Yeong)
Date: Mon, 13 May 2024 16:18:00 +0000
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <ZkI4GO4czdgs5/Xy@lizhi-Precision-Tower-5810>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
 <20240507204311.2898714-2-Frank.Li@nxp.com>
 <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkI4GO4czdgs5/Xy@lizhi-Precision-Tower-5810>
Message-ID: <SH0PR01MB084189445E4928BC5047495AF9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>

Frank Li wrote:
> On Mon, May 13, 2024 at 03:16:05AM +0000, Joshua Yeong wrote:
> > Joshua Yeong wrote:
> > >
> > > Introduce a new target core layer in order to support target
> > > functions in linux kernel. This comprises the controller library and function
> library.
> > > Controller library implements functions specific to an target
> > > controller and function library implements functions specific to an target
> function.
> > >
> > > Introduce a new configfs entry to configure the target function
> > > configuring and bind the target function with target controller.
> > >
> > > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > > ---
> > >
> > > Notes:
> > >     Change from v8 to v9
> > >     -none
> > >
> > >     Change from v7 to v8
> > >     Added missed head files
> > >
> > >     Change from v5 to v6
> > >     - fixed build error when have not CONFIG_TARGET_CONFIGFS
> > >     | Reported-by: kernel test robot <lkp at intel.com>
> > >     | Closes:
> > > https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-
> > > lkp at intel.com/
> > >
> > >     Chagne from v4 to v5
> > >     - add include <linux/slab.h> to fix build error
> > >     | Reported-by: kernel test robot <lkp at intel.com>
> > >     | Closes:
> > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > lkp at intel.com/
> > >
> > >     Chagne from v4 to v8
> > >     - add include <linux/slab.h> to fix build error
> > >     | Reported-by: kernel test robot <lkp at intel.com>
> > >     | Closes:
> > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > lkp at intel.com/
> > >
> > >  drivers/i3c/Kconfig        |  28 +-
> > >  drivers/i3c/Makefile       |   2 +
> > >  drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
> > >  drivers/i3c/target.c       | 453 ++++++++++++++++++++++++++++++
> > >  include/linux/i3c/target.h | 548
> > > +++++++++++++++++++++++++++++++++++++
> > >
> > > ...
> > >
> > > diff --git a/include/linux/i3c/target.h b/include/linux/i3c/target.h
> > > new file mode 100644 index 0000000000000..b0bf06685834c
> > > --- /dev/null
> > > +++ b/include/linux/i3c/target.h
> > >
> > > ...
> > >
> > > +/**
> > > + * struct i3c_target_ctrl_ops - set of function pointers for
> > > +performing controller operations
> > > + * @set_config: set I3C controller configuration
> > > + * @enable: enable I3C controller
> > > + * @disable: disable I3C controller
> > > + * @raise_ibi: raise IBI interrupt to master
> > > + * @alloc_request: allocate a i3c_request, optional, default use
> > > +kmalloc
> > > + * @free_request: free a i3c_request, default use kfree
> > > + * @queue: queue an I3C transfer
> > > + * @dequeue: dequeue an I3C transfer
> > > + * @cancel_all_reqs: call all pending requests
> > > + * @fifo_status: current FIFO status
> > > + * @fifo_flush: flush hardware FIFO
> > > + * @hotjoin: raise hotjoin request to master
> > > + * @set_status_format1: set i3c status format1
> > > + * @get_status_format1: get i3c status format1
> > > + * @get_addr: get i3c dynmatic address
> > > + * @get_features: ops to get the features supported by the I3C
> > > +target controller
> > > + * @owner: the module owner containing the ops  */ struct
> > > +i3c_target_ctrl_ops {
> > > +	int (*set_config)(struct i3c_target_ctrl *ctrl, struct
> > > +i3c_target_func
> > > *func);
> > > +	int (*enable)(struct i3c_target_ctrl *ctrl);
> > > +	int (*disable)(struct i3c_target_ctrl *ctrl);
> > > +	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8 size);
> > > +
> > > +	struct i3c_request *(*alloc_request)(struct i3c_target_ctrl *ctrl,
> > > +gfp_t
> > > gfp_flags);
> > > +	void (*free_request)(struct i3c_request *req);
> > > +
> > > +	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
> > > +	int (*dequeue)(struct i3c_request *req);
> > > +
> > > +	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool tx);
> > > +
> > > +	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
> > > +	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
> > > +	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
> > > +	int (*set_status_format1)(struct i3c_target_ctrl *ctrl, u16 status);
> > > +	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
> > > +	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
> > > +	const struct i3c_target_ctrl_features *(*get_features)(struct
> > > i3c_target_ctrl *ctrl);
> > > +	struct module *owner;
> > > +};
> >
> > This structure looks very different from the master controller ops
> 'i3c_master_controller_ops'.
> > I think you could probably combine 'set_config, enable' into 'bus_init',
> 'disable' to 'bus_cleanup'.
> > Also using the 'struct i3c_priv_xfer' rather redefining another 'struct
> i3c_request' would be much cleaner.
> 
> Thanks, let me think this. i3c_priv_xter may include read and write, or write
> and read. I3C is quite fast. Most time software are not quite enough to handle
> it in time.
> 
> If data len bigger than FIFO size, it have to use DMA to transfer it because
> there are not flow controler at target side (write direction).
> Read, hardware to use early terminate to tell host read too fast. but for write,
> 9th bit is parit check bit. Data will be lost if software have not read from FIFO
> in time. If use external DMA to do that, it needs switch dma channel(generally,
> DMA rx and tx are two channel). switch channel automatically are quite
> challenge at current dma engine framework.
> 

I guess when data is larger than FIFO, the i3c controller (master) should 
'aware' of this limitation when using such target device. So that appropriate
handle like make new read command, or do a write then another larger read
request. 

> Of course it will be possible if hardware implement simpilar I3C HCI command
> queue. But I still not found any hardware that can do that yet.
> 
> If only limited data size to FIFO size, there are still limiation for xfer.  if write,
> read, write, read, two write may combined in one FIFO, target driver may not
> split it which depend on hardware detect repeat start and insert something
> into fifo.
> 
> It is hard to support all user case by I3C target software in linux.

Can I know the speed limitation that you having issue while running on silicon? 

> 
> write follow by read, generally used from get hardware register value from
> target side, which almost impossible by software, it is not quick enough to
> parse (write value) then send back data.
> 
> >
> > In the master i3c side they don't abbreviate i3c_master_controller to
> > i3c_master_ctrl. I think we should do the same here to standardize it.
> > Thanks
> 
> The problem is in I3C spec: "master" already depericated
> 
> from i3c spec 1.1.1
> 
> "Controller: An I3C Device that is capable of controlling the I3C Bus."
> 
> master/slave => controller/target.
> 
> It will become more confused if i3c_target_controller. "controller"
> are ambiguity.
> 
> If "master" => "host" would be better situation, but not happen. A point, I use
> "ctrl"  here to avoid I3C spec defined "controller" term.
> 
> Anyway, I hope more people that involve target support discussion.
> 
> >
> > Regards,
> > Joshua
> >
> > > --
> > > 2.34.1
> > >
> > >
> > > --
> > > linux-i3c mailing list
> > > linux-i3c at lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/linux-i3c


From Frank.li at nxp.com  Mon May 13 11:48:31 2024
From: Frank.li at nxp.com (Frank Li)
Date: Mon, 13 May 2024 14:48:31 -0400
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <SH0PR01MB084189445E4928BC5047495AF9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
 <20240507204311.2898714-2-Frank.Li@nxp.com>
 <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkI4GO4czdgs5/Xy@lizhi-Precision-Tower-5810>
 <SH0PR01MB084189445E4928BC5047495AF9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
Message-ID: <ZkJgf/oRMMBswih1@lizhi-Precision-Tower-5810>

On Mon, May 13, 2024 at 04:18:00PM +0000, Joshua Yeong wrote:
> Frank Li wrote:
> > On Mon, May 13, 2024 at 03:16:05AM +0000, Joshua Yeong wrote:
> > > Joshua Yeong wrote:
> > > >
> > > > Introduce a new target core layer in order to support target
> > > > functions in linux kernel. This comprises the controller library and function
> > library.
> > > > Controller library implements functions specific to an target
> > > > controller and function library implements functions specific to an target
> > function.
> > > >
> > > > Introduce a new configfs entry to configure the target function
> > > > configuring and bind the target function with target controller.
> > > >
> > > > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > > > ---
> > > >
> > > > Notes:
> > > >     Change from v8 to v9
> > > >     -none
> > > >
> > > >     Change from v7 to v8
> > > >     Added missed head files
> > > >
> > > >     Change from v5 to v6
> > > >     - fixed build error when have not CONFIG_TARGET_CONFIGFS
> > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > >     | Closes:
> > > > https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-
> > > > lkp at intel.com/
> > > >
> > > >     Chagne from v4 to v5
> > > >     - add include <linux/slab.h> to fix build error
> > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > >     | Closes:
> > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > lkp at intel.com/
> > > >
> > > >     Chagne from v4 to v8
> > > >     - add include <linux/slab.h> to fix build error
> > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > >     | Closes:
> > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > lkp at intel.com/
> > > >
> > > >  drivers/i3c/Kconfig        |  28 +-
> > > >  drivers/i3c/Makefile       |   2 +
> > > >  drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
> > > >  drivers/i3c/target.c       | 453 ++++++++++++++++++++++++++++++
> > > >  include/linux/i3c/target.h | 548
> > > > +++++++++++++++++++++++++++++++++++++
> > > >
> > > > ...
> > > >
> > > > diff --git a/include/linux/i3c/target.h b/include/linux/i3c/target.h
> > > > new file mode 100644 index 0000000000000..b0bf06685834c
> > > > --- /dev/null
> > > > +++ b/include/linux/i3c/target.h
> > > >
> > > > ...
> > > >
> > > > +/**
> > > > + * struct i3c_target_ctrl_ops - set of function pointers for
> > > > +performing controller operations
> > > > + * @set_config: set I3C controller configuration
> > > > + * @enable: enable I3C controller
> > > > + * @disable: disable I3C controller
> > > > + * @raise_ibi: raise IBI interrupt to master
> > > > + * @alloc_request: allocate a i3c_request, optional, default use
> > > > +kmalloc
> > > > + * @free_request: free a i3c_request, default use kfree
> > > > + * @queue: queue an I3C transfer
> > > > + * @dequeue: dequeue an I3C transfer
> > > > + * @cancel_all_reqs: call all pending requests
> > > > + * @fifo_status: current FIFO status
> > > > + * @fifo_flush: flush hardware FIFO
> > > > + * @hotjoin: raise hotjoin request to master
> > > > + * @set_status_format1: set i3c status format1
> > > > + * @get_status_format1: get i3c status format1
> > > > + * @get_addr: get i3c dynmatic address
> > > > + * @get_features: ops to get the features supported by the I3C
> > > > +target controller
> > > > + * @owner: the module owner containing the ops  */ struct
> > > > +i3c_target_ctrl_ops {
> > > > +	int (*set_config)(struct i3c_target_ctrl *ctrl, struct
> > > > +i3c_target_func
> > > > *func);
> > > > +	int (*enable)(struct i3c_target_ctrl *ctrl);
> > > > +	int (*disable)(struct i3c_target_ctrl *ctrl);
> > > > +	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8 size);
> > > > +
> > > > +	struct i3c_request *(*alloc_request)(struct i3c_target_ctrl *ctrl,
> > > > +gfp_t
> > > > gfp_flags);
> > > > +	void (*free_request)(struct i3c_request *req);
> > > > +
> > > > +	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
> > > > +	int (*dequeue)(struct i3c_request *req);
> > > > +
> > > > +	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > +
> > > > +	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > +	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > +	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
> > > > +	int (*set_status_format1)(struct i3c_target_ctrl *ctrl, u16 status);
> > > > +	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
> > > > +	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
> > > > +	const struct i3c_target_ctrl_features *(*get_features)(struct
> > > > i3c_target_ctrl *ctrl);
> > > > +	struct module *owner;
> > > > +};
> > >
> > > This structure looks very different from the master controller ops
> > 'i3c_master_controller_ops'.
> > > I think you could probably combine 'set_config, enable' into 'bus_init',
> > 'disable' to 'bus_cleanup'.
> > > Also using the 'struct i3c_priv_xfer' rather redefining another 'struct
> > i3c_request' would be much cleaner.
> > 
> > Thanks, let me think this. i3c_priv_xter may include read and write, or write
> > and read. I3C is quite fast. Most time software are not quite enough to handle
> > it in time.
> > 
> > If data len bigger than FIFO size, it have to use DMA to transfer it because
> > there are not flow controler at target side (write direction).
> > Read, hardware to use early terminate to tell host read too fast. but for write,
> > 9th bit is parit check bit. Data will be lost if software have not read from FIFO
> > in time. If use external DMA to do that, it needs switch dma channel(generally,
> > DMA rx and tx are two channel). switch channel automatically are quite
> > challenge at current dma engine framework.
> > 
> 
> I guess when data is larger than FIFO, the i3c controller (master) should 
> 'aware' of this limitation when using such target device. So that appropriate
> handle like make new read command, or do a write then another larger read
> request. 

Between two commands, such as two write command, some data may lost because
first one have not chance to fetch it from FIFO.

Function driver can add some recovery mathod. But still better to use modem
async method to queue multi transfer and handle by dma automatically. One
transfer each time are just special case for queue.

If DMA support,  queue can work as big cycle buffer, underrun will never
happen unless system wrong, such as disable irq over 1 second. 

Actually, I worry about still i3c_priv_xfer, write followed read case.
Most likely, host driver do like

	write (cmd index/reg index) then read value or command result.

It is hard to decode cmd and send back data immedicately.

if there are not special DMA support,  more realistic usage mode is

cmd1, cmd2, cmd3,....

send back
	resp1, resp2, ....

instead of comebine cmd1 and resp1 at one transfer.

Host can do that just because all SCL are controller by host controllers.

> 
> > Of course it will be possible if hardware implement simpilar I3C HCI command
> > queue. But I still not found any hardware that can do that yet.
> > 
> > If only limited data size to FIFO size, there are still limiation for xfer.  if write,
> > read, write, read, two write may combined in one FIFO, target driver may not
> > split it which depend on hardware detect repeat start and insert something
> > into fifo.
> > 
> > It is hard to support all user case by I3C target software in linux.
> 
> Can I know the speed limitation that you having issue while running on silicon? 

I am not sure what "speed limitation" here? Do you means irq latency issue.

when I get "stop", there are new data already come into FIFO.  It is not
for problem for tty case now. 

But if it is well defined frame data, it may be problem.

I saw some data lost when target are busy without DMA support. driver can
query how many fifo space avaible before send data, but it reduced bus
utilities rate.

> 
> > 
> > write follow by read, generally used from get hardware register value from
> > target side, which almost impossible by software, it is not quick enough to
> > parse (write value) then send back data.
> > 
> > >
> > > In the master i3c side they don't abbreviate i3c_master_controller to
> > > i3c_master_ctrl. I think we should do the same here to standardize it.
> > > Thanks
> > 
> > The problem is in I3C spec: "master" already depericated
> > 
> > from i3c spec 1.1.1
> > 
> > "Controller: An I3C Device that is capable of controlling the I3C Bus."
> > 
> > master/slave => controller/target.
> > 
> > It will become more confused if i3c_target_controller. "controller"
> > are ambiguity.
> > 
> > If "master" => "host" would be better situation, but not happen. A point, I use
> > "ctrl"  here to avoid I3C spec defined "controller" term.
> > 
> > Anyway, I hope more people that involve target support discussion.
> > 
> > >
> > > Regards,
> > > Joshua
> > >
> > > > --
> > > > 2.34.1
> > > >
> > > >
> > > > --
> > > > linux-i3c mailing list
> > > > linux-i3c at lists.infradead.org
> > > > http://lists.infradead.org/mailman/listinfo/linux-i3c


From joshua.yeong at starfivetech.com  Mon May 13 20:07:25 2024
From: joshua.yeong at starfivetech.com (Joshua Yeong)
Date: Tue, 14 May 2024 03:07:25 +0000
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <ZkJgf/oRMMBswih1@lizhi-Precision-Tower-5810>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
 <20240507204311.2898714-2-Frank.Li@nxp.com>
 <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkI4GO4czdgs5/Xy@lizhi-Precision-Tower-5810>
 <SH0PR01MB084189445E4928BC5047495AF9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkJgf/oRMMBswih1@lizhi-Precision-Tower-5810>
Message-ID: <SH0PR01MB084183D40B4ECC0C4B3A9BF0F9E32@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>

Frank Li wrote:
> 
> On Mon, May 13, 2024 at 04:18:00PM +0000, Joshua Yeong wrote:
> > Frank Li wrote:
> > > On Mon, May 13, 2024 at 03:16:05AM +0000, Joshua Yeong wrote:
> > > > Joshua Yeong wrote:
> > > > >
> > > > > Introduce a new target core layer in order to support target
> > > > > functions in linux kernel. This comprises the controller library
> > > > > and function
> > > library.
> > > > > Controller library implements functions specific to an target
> > > > > controller and function library implements functions specific to
> > > > > an target
> > > function.
> > > > >
> > > > > Introduce a new configfs entry to configure the target function
> > > > > configuring and bind the target function with target controller.
> > > > >
> > > > > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > > > > ---
> > > > >
> > > > > Notes:
> > > > >     Change from v8 to v9
> > > > >     -none
> > > > >
> > > > >     Change from v7 to v8
> > > > >     Added missed head files
> > > > >
> > > > >     Change from v5 to v6
> > > > >     - fixed build error when have not CONFIG_TARGET_CONFIGFS
> > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > >     | Closes:
> > > > > https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-
> > > > > lkp at intel.com/
> > > > >
> > > > >     Chagne from v4 to v5
> > > > >     - add include <linux/slab.h> to fix build error
> > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > >     | Closes:
> > > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > > lkp at intel.com/
> > > > >
> > > > >     Chagne from v4 to v8
> > > > >     - add include <linux/slab.h> to fix build error
> > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > >     | Closes:
> > > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > > lkp at intel.com/
> > > > >
> > > > >  drivers/i3c/Kconfig        |  28 +-
> > > > >  drivers/i3c/Makefile       |   2 +
> > > > >  drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
> > > > >  drivers/i3c/target.c       | 453 ++++++++++++++++++++++++++++++
> > > > >  include/linux/i3c/target.h | 548
> > > > > +++++++++++++++++++++++++++++++++++++
> > > > >
> > > > > ...
> > > > >
> > > > > diff --git a/include/linux/i3c/target.h
> > > > > b/include/linux/i3c/target.h new file mode 100644 index
> > > > > 0000000000000..b0bf06685834c
> > > > > --- /dev/null
> > > > > +++ b/include/linux/i3c/target.h
> > > > >
> > > > > ...
> > > > >
> > > > > +/**
> > > > > + * struct i3c_target_ctrl_ops - set of function pointers for
> > > > > +performing controller operations
> > > > > + * @set_config: set I3C controller configuration
> > > > > + * @enable: enable I3C controller
> > > > > + * @disable: disable I3C controller
> > > > > + * @raise_ibi: raise IBI interrupt to master
> > > > > + * @alloc_request: allocate a i3c_request, optional, default
> > > > > +use kmalloc
> > > > > + * @free_request: free a i3c_request, default use kfree
> > > > > + * @queue: queue an I3C transfer
> > > > > + * @dequeue: dequeue an I3C transfer
> > > > > + * @cancel_all_reqs: call all pending requests
> > > > > + * @fifo_status: current FIFO status
> > > > > + * @fifo_flush: flush hardware FIFO
> > > > > + * @hotjoin: raise hotjoin request to master
> > > > > + * @set_status_format1: set i3c status format1
> > > > > + * @get_status_format1: get i3c status format1
> > > > > + * @get_addr: get i3c dynmatic address
> > > > > + * @get_features: ops to get the features supported by the I3C
> > > > > +target controller
> > > > > + * @owner: the module owner containing the ops  */ struct
> > > > > +i3c_target_ctrl_ops {
> > > > > +	int (*set_config)(struct i3c_target_ctrl *ctrl, struct
> > > > > +i3c_target_func
> > > > > *func);
> > > > > +	int (*enable)(struct i3c_target_ctrl *ctrl);
> > > > > +	int (*disable)(struct i3c_target_ctrl *ctrl);
> > > > > +	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8
> > > > > +size);
> > > > > +
> > > > > +	struct i3c_request *(*alloc_request)(struct i3c_target_ctrl
> > > > > +*ctrl, gfp_t
> > > > > gfp_flags);
> > > > > +	void (*free_request)(struct i3c_request *req);
> > > > > +
> > > > > +	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
> > > > > +	int (*dequeue)(struct i3c_request *req);
> > > > > +
> > > > > +	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool
> > > > > +tx);
> > > > > +
> > > > > +	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > > +	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > > +	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
> > > > > +	int (*set_status_format1)(struct i3c_target_ctrl *ctrl, u16
> status);
> > > > > +	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
> > > > > +	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
> > > > > +	const struct i3c_target_ctrl_features *(*get_features)(struct
> > > > > i3c_target_ctrl *ctrl);
> > > > > +	struct module *owner;
> > > > > +};
> > > >
> > > > This structure looks very different from the master controller ops
> > > 'i3c_master_controller_ops'.
> > > > I think you could probably combine 'set_config, enable' into
> > > > 'bus_init',
> > > 'disable' to 'bus_cleanup'.
> > > > Also using the 'struct i3c_priv_xfer' rather redefining another
> > > > 'struct
> > > i3c_request' would be much cleaner.
> > >
> > > Thanks, let me think this. i3c_priv_xter may include read and write,
> > > or write and read. I3C is quite fast. Most time software are not
> > > quite enough to handle it in time.
> > >
> > > If data len bigger than FIFO size, it have to use DMA to transfer it
> > > because there are not flow controler at target side (write direction).
> > > Read, hardware to use early terminate to tell host read too fast.
> > > but for write, 9th bit is parit check bit. Data will be lost if
> > > software have not read from FIFO in time. If use external DMA to do
> > > that, it needs switch dma channel(generally, DMA rx and tx are two
> > > channel). switch channel automatically are quite challenge at current dma
> engine framework.
> > >
> >
> > I guess when data is larger than FIFO, the i3c controller (master)
> > should 'aware' of this limitation when using such target device. So
> > that appropriate handle like make new read command, or do a write then
> > another larger read request.
> 
> Between two commands, such as two write command, some data may lost
> because first one have not chance to fetch it from FIFO.
> 
> Function driver can add some recovery mathod. But still better to use modem
> async method to queue multi transfer and handle by dma automatically. One
> transfer each time are just special case for queue.
> 
> If DMA support,  queue can work as big cycle buffer, underrun will never
> happen unless system wrong, such as disable irq over 1 second.
> 
> Actually, I worry about still i3c_priv_xfer, write followed read case.
> Most likely, host driver do like
> 
> 	write (cmd index/reg index) then read value or command result.
> 
> It is hard to decode cmd and send back data immedicately.
> 
> if there are not special DMA support,  more realistic usage mode is
> 
> cmd1, cmd2, cmd3,....
> 
> send back
> 	resp1, resp2, ....
> 
> instead of comebine cmd1 and resp1 at one transfer.
> 
> Host can do that just because all SCL are controller by host controllers.

The DMA queue mechanism should still be invisible to the framework. Target
could use info from SETMRL/SETMWL to 'prepare' whether internally DMA 
is needed.

We can also argue that the max read length should always not larger than 
the FIFO size. Slave data transfer interface should still match what i3c
spec expect to be, rather than the hardware limitation here.


> 
> >
> > > Of course it will be possible if hardware implement simpilar I3C HCI
> > > command queue. But I still not found any hardware that can do that yet.
> > >
> > > If only limited data size to FIFO size, there are still limiation
> > > for xfer.  if write, read, write, read, two write may combined in
> > > one FIFO, target driver may not split it which depend on hardware
> > > detect repeat start and insert something into fifo.
> > >
> > > It is hard to support all user case by I3C target software in linux.
> >
> > Can I know the speed limitation that you having issue while running on
> silicon?
> 
> I am not sure what "speed limitation" here? Do you means irq latency issue.
> 
> when I get "stop", there are new data already come into FIFO.  It is not for
> problem for tty case now.
> 
> But if it is well defined frame data, it may be problem.
> 
> I saw some data lost when target are busy without DMA support. driver can
> query how many fifo space avaible before send data, but it reduced bus
> utilities rate.
> 
> >
> > >
> > > write follow by read, generally used from get hardware register
> > > value from target side, which almost impossible by software, it is
> > > not quick enough to parse (write value) then send back data.
> > >
> > > >
> > > > In the master i3c side they don't abbreviate i3c_master_controller
> > > > to i3c_master_ctrl. I think we should do the same here to standardize it.
> > > > Thanks
> > >
> > > The problem is in I3C spec: "master" already depericated
> > >
> > > from i3c spec 1.1.1
> > >
> > > "Controller: An I3C Device that is capable of controlling the I3C Bus."
> > >
> > > master/slave => controller/target.
> > >
> > > It will become more confused if i3c_target_controller. "controller"
> > > are ambiguity.
> > >
> > > If "master" => "host" would be better situation, but not happen. A
> > > point, I use "ctrl"  here to avoid I3C spec defined "controller" term.
> > >
> > > Anyway, I hope more people that involve target support discussion.
> > >
> > > >
> > > > Regards,
> > > > Joshua
> > > >
> > > > > --
> > > > > 2.34.1
> > > > >
> > > > >
> > > > > --
> > > > > linux-i3c mailing list
> > > > > linux-i3c at lists.infradead.org
> > > > > http://lists.infradead.org/mailman/listinfo/linux-i3c


From Frank.li at nxp.com  Tue May 14 09:11:05 2024
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 14 May 2024 12:11:05 -0400
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <SH0PR01MB084183D40B4ECC0C4B3A9BF0F9E32@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
 <20240507204311.2898714-2-Frank.Li@nxp.com>
 <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkI4GO4czdgs5/Xy@lizhi-Precision-Tower-5810>
 <SH0PR01MB084189445E4928BC5047495AF9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkJgf/oRMMBswih1@lizhi-Precision-Tower-5810>
 <SH0PR01MB084183D40B4ECC0C4B3A9BF0F9E32@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
Message-ID: <ZkONGQPBeuR6oZ0Q@lizhi-Precision-Tower-5810>

On Tue, May 14, 2024 at 03:07:25AM +0000, Joshua Yeong wrote:
> Frank Li wrote:
> > 
> > On Mon, May 13, 2024 at 04:18:00PM +0000, Joshua Yeong wrote:
> > > Frank Li wrote:
> > > > On Mon, May 13, 2024 at 03:16:05AM +0000, Joshua Yeong wrote:
> > > > > Joshua Yeong wrote:
> > > > > >
> > > > > > Introduce a new target core layer in order to support target
> > > > > > functions in linux kernel. This comprises the controller library
> > > > > > and function
> > > > library.
> > > > > > Controller library implements functions specific to an target
> > > > > > controller and function library implements functions specific to
> > > > > > an target
> > > > function.
> > > > > >
> > > > > > Introduce a new configfs entry to configure the target function
> > > > > > configuring and bind the target function with target controller.
> > > > > >
> > > > > > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > > > > > ---
> > > > > >
> > > > > > Notes:
> > > > > >     Change from v8 to v9
> > > > > >     -none
> > > > > >
> > > > > >     Change from v7 to v8
> > > > > >     Added missed head files
> > > > > >
> > > > > >     Change from v5 to v6
> > > > > >     - fixed build error when have not CONFIG_TARGET_CONFIGFS
> > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > >     | Closes:
> > > > > > https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-
> > > > > > lkp at intel.com/
> > > > > >
> > > > > >     Chagne from v4 to v5
> > > > > >     - add include <linux/slab.h> to fix build error
> > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > >     | Closes:
> > > > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > > > lkp at intel.com/
> > > > > >
> > > > > >     Chagne from v4 to v8
> > > > > >     - add include <linux/slab.h> to fix build error
> > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > >     | Closes:
> > > > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > > > lkp at intel.com/
> > > > > >
> > > > > >  drivers/i3c/Kconfig        |  28 +-
> > > > > >  drivers/i3c/Makefile       |   2 +
> > > > > >  drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
> > > > > >  drivers/i3c/target.c       | 453 ++++++++++++++++++++++++++++++
> > > > > >  include/linux/i3c/target.h | 548
> > > > > > +++++++++++++++++++++++++++++++++++++
> > > > > >
> > > > > > ...
> > > > > >
> > > > > > diff --git a/include/linux/i3c/target.h
> > > > > > b/include/linux/i3c/target.h new file mode 100644 index
> > > > > > 0000000000000..b0bf06685834c
> > > > > > --- /dev/null
> > > > > > +++ b/include/linux/i3c/target.h
> > > > > >
> > > > > > ...
> > > > > >
> > > > > > +/**
> > > > > > + * struct i3c_target_ctrl_ops - set of function pointers for
> > > > > > +performing controller operations
> > > > > > + * @set_config: set I3C controller configuration
> > > > > > + * @enable: enable I3C controller
> > > > > > + * @disable: disable I3C controller
> > > > > > + * @raise_ibi: raise IBI interrupt to master
> > > > > > + * @alloc_request: allocate a i3c_request, optional, default
> > > > > > +use kmalloc
> > > > > > + * @free_request: free a i3c_request, default use kfree
> > > > > > + * @queue: queue an I3C transfer
> > > > > > + * @dequeue: dequeue an I3C transfer
> > > > > > + * @cancel_all_reqs: call all pending requests
> > > > > > + * @fifo_status: current FIFO status
> > > > > > + * @fifo_flush: flush hardware FIFO
> > > > > > + * @hotjoin: raise hotjoin request to master
> > > > > > + * @set_status_format1: set i3c status format1
> > > > > > + * @get_status_format1: get i3c status format1
> > > > > > + * @get_addr: get i3c dynmatic address
> > > > > > + * @get_features: ops to get the features supported by the I3C
> > > > > > +target controller
> > > > > > + * @owner: the module owner containing the ops  */ struct
> > > > > > +i3c_target_ctrl_ops {
> > > > > > +	int (*set_config)(struct i3c_target_ctrl *ctrl, struct
> > > > > > +i3c_target_func
> > > > > > *func);
> > > > > > +	int (*enable)(struct i3c_target_ctrl *ctrl);
> > > > > > +	int (*disable)(struct i3c_target_ctrl *ctrl);
> > > > > > +	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8
> > > > > > +size);
> > > > > > +
> > > > > > +	struct i3c_request *(*alloc_request)(struct i3c_target_ctrl
> > > > > > +*ctrl, gfp_t
> > > > > > gfp_flags);
> > > > > > +	void (*free_request)(struct i3c_request *req);
> > > > > > +
> > > > > > +	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
> > > > > > +	int (*dequeue)(struct i3c_request *req);
> > > > > > +
> > > > > > +	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool
> > > > > > +tx);
> > > > > > +
> > > > > > +	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > > > +	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > > > +	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
> > > > > > +	int (*set_status_format1)(struct i3c_target_ctrl *ctrl, u16
> > status);
> > > > > > +	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
> > > > > > +	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
> > > > > > +	const struct i3c_target_ctrl_features *(*get_features)(struct
> > > > > > i3c_target_ctrl *ctrl);
> > > > > > +	struct module *owner;
> > > > > > +};
> > > > >
> > > > > This structure looks very different from the master controller ops
> > > > 'i3c_master_controller_ops'.
> > > > > I think you could probably combine 'set_config, enable' into
> > > > > 'bus_init',
> > > > 'disable' to 'bus_cleanup'.
> > > > > Also using the 'struct i3c_priv_xfer' rather redefining another
> > > > > 'struct
> > > > i3c_request' would be much cleaner.
> > > >
> > > > Thanks, let me think this. i3c_priv_xter may include read and write,
> > > > or write and read. I3C is quite fast. Most time software are not
> > > > quite enough to handle it in time.
> > > >
> > > > If data len bigger than FIFO size, it have to use DMA to transfer it
> > > > because there are not flow controler at target side (write direction).
> > > > Read, hardware to use early terminate to tell host read too fast.
> > > > but for write, 9th bit is parit check bit. Data will be lost if
> > > > software have not read from FIFO in time. If use external DMA to do
> > > > that, it needs switch dma channel(generally, DMA rx and tx are two
> > > > channel). switch channel automatically are quite challenge at current dma
> > engine framework.
> > > >
> > >
> > > I guess when data is larger than FIFO, the i3c controller (master)
> > > should 'aware' of this limitation when using such target device. So
> > > that appropriate handle like make new read command, or do a write then
> > > another larger read request.
> > 
> > Between two commands, such as two write command, some data may lost
> > because first one have not chance to fetch it from FIFO.
> > 
> > Function driver can add some recovery mathod. But still better to use modem
> > async method to queue multi transfer and handle by dma automatically. One
> > transfer each time are just special case for queue.
> > 
> > If DMA support,  queue can work as big cycle buffer, underrun will never
> > happen unless system wrong, such as disable irq over 1 second.
> > 
> > Actually, I worry about still i3c_priv_xfer, write followed read case.
> > Most likely, host driver do like
> > 
> > 	write (cmd index/reg index) then read value or command result.
> > 
> > It is hard to decode cmd and send back data immedicately.
> > 
> > if there are not special DMA support,  more realistic usage mode is
> > 
> > cmd1, cmd2, cmd3,....
> > 
> > send back
> > 	resp1, resp2, ....
> > 
> > instead of comebine cmd1 and resp1 at one transfer.
> > 
> > Host can do that just because all SCL are controller by host controllers.
> 
> The DMA queue mechanism should still be invisible to the framework. Target
> could use info from SETMRL/SETMWL to 'prepare' whether internally DMA 
> is needed.

You are right. DMA should be invisiable to the framework, but framework
need provide API to prepare receiver buffer advance before transfer happen.
Only function driver know how much data need be prepared advance. For host
read/target write case, It also need cancel request function incase host
have not fetch data in certain time. Data need be updated. function driver
can't simple update data buffer, because it may already map to DMA. 
function driver need dequeue and queue request again.

framework should abstract common part of difference i3c target controller
and target function driver to avoid duplicate code.

And also need consider linux's irq latency limiation, try to make the
function driver can work on all target controller drivers.

I think priv_xfer, W, R, ...  are hard to implement. Maybe limit priv_xfer
's len to 1 or 2. But if limited to 1, it becames no big difference with
current implement.

> 
> We can also argue that the max read length should always not larger than 
> the FIFO size. Slave data transfer interface should still match what i3c
> spec expect to be, rather than the hardware limitation here.
> 

According to spec:
 	5.1.9.3.5 Maximum write length in a single command

I am not resure "singe command here". I think it should be between
"start" and "repeat start/stop". 

If my understand is correct,  host still send out W R W R ... as whole
I3C transaction, each one less than FIFO size. but totally size may exceed
FIFO size. 


> 
> > 
> > >
> > > > Of course it will be possible if hardware implement simpilar I3C HCI
> > > > command queue. But I still not found any hardware that can do that yet.
> > > >
> > > > If only limited data size to FIFO size, there are still limiation
> > > > for xfer.  if write, read, write, read, two write may combined in
> > > > one FIFO, target driver may not split it which depend on hardware
> > > > detect repeat start and insert something into fifo.
> > > >
> > > > It is hard to support all user case by I3C target software in linux.
> > >
> > > Can I know the speed limitation that you having issue while running on
> > silicon?
> > 
> > I am not sure what "speed limitation" here? Do you means irq latency issue.
> > 
> > when I get "stop", there are new data already come into FIFO.  It is not for
> > problem for tty case now.
> > 
> > But if it is well defined frame data, it may be problem.
> > 
> > I saw some data lost when target are busy without DMA support. driver can
> > query how many fifo space avaible before send data, but it reduced bus
> > utilities rate.
> > 
> > >
> > > >
> > > > write follow by read, generally used from get hardware register
> > > > value from target side, which almost impossible by software, it is
> > > > not quick enough to parse (write value) then send back data.
> > > >
> > > > >
> > > > > In the master i3c side they don't abbreviate i3c_master_controller
> > > > > to i3c_master_ctrl. I think we should do the same here to standardize it.
> > > > > Thanks
> > > >
> > > > The problem is in I3C spec: "master" already depericated
> > > >
> > > > from i3c spec 1.1.1
> > > >
> > > > "Controller: An I3C Device that is capable of controlling the I3C Bus."
> > > >
> > > > master/slave => controller/target.
> > > >
> > > > It will become more confused if i3c_target_controller. "controller"
> > > > are ambiguity.
> > > >
> > > > If "master" => "host" would be better situation, but not happen. A
> > > > point, I use "ctrl"  here to avoid I3C spec defined "controller" term.
> > > >
> > > > Anyway, I hope more people that involve target support discussion.
> > > >
> > > > >
> > > > > Regards,
> > > > > Joshua
> > > > >
> > > > > > --
> > > > > > 2.34.1
> > > > > >
> > > > > >
> > > > > > --
> > > > > > linux-i3c mailing list
> > > > > > linux-i3c at lists.infradead.org
> > > > > > http://lists.infradead.org/mailman/listinfo/linux-i3c


From joshua.yeong at starfivetech.com  Tue May 14 20:07:37 2024
From: joshua.yeong at starfivetech.com (Joshua Yeong)
Date: Wed, 15 May 2024 03:07:37 +0000
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <ZkONGQPBeuR6oZ0Q@lizhi-Precision-Tower-5810>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
 <20240507204311.2898714-2-Frank.Li@nxp.com>
 <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkI4GO4czdgs5/Xy@lizhi-Precision-Tower-5810>
 <SH0PR01MB084189445E4928BC5047495AF9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkJgf/oRMMBswih1@lizhi-Precision-Tower-5810>
 <SH0PR01MB084183D40B4ECC0C4B3A9BF0F9E32@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkONGQPBeuR6oZ0Q@lizhi-Precision-Tower-5810>
Message-ID: <SH0PR01MB084106A67DF4656C9B8CF686F9EC2@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>

Frank Li wrote:
> 
> On Tue, May 14, 2024 at 03:07:25AM +0000, Joshua Yeong wrote:
> > Frank Li wrote:
> > >
> > > On Mon, May 13, 2024 at 04:18:00PM +0000, Joshua Yeong wrote:
> > > > Frank Li wrote:
> > > > > On Mon, May 13, 2024 at 03:16:05AM +0000, Joshua Yeong wrote:
> > > > > > Joshua Yeong wrote:
> > > > > > >
> > > > > > > Introduce a new target core layer in order to support target
> > > > > > > functions in linux kernel. This comprises the controller
> > > > > > > library and function
> > > > > library.
> > > > > > > Controller library implements functions specific to an
> > > > > > > target controller and function library implements functions
> > > > > > > specific to an target
> > > > > function.
> > > > > > >
> > > > > > > Introduce a new configfs entry to configure the target
> > > > > > > function configuring and bind the target function with target
> controller.
> > > > > > >
> > > > > > > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > > > > > > ---
> > > > > > >
> > > > > > > Notes:
> > > > > > >     Change from v8 to v9
> > > > > > >     -none
> > > > > > >
> > > > > > >     Change from v7 to v8
> > > > > > >     Added missed head files
> > > > > > >
> > > > > > >     Change from v5 to v6
> > > > > > >     - fixed build error when have not CONFIG_TARGET_CONFIGFS
> > > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > > >     | Closes:
> > > > > > > https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-
> > > > > > > lkp at intel.com/
> > > > > > >
> > > > > > >     Chagne from v4 to v5
> > > > > > >     - add include <linux/slab.h> to fix build error
> > > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > > >     | Closes:
> > > > > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > > > > lkp at intel.com/
> > > > > > >
> > > > > > >     Chagne from v4 to v8
> > > > > > >     - add include <linux/slab.h> to fix build error
> > > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > > >     | Closes:
> > > > > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > > > > lkp at intel.com/
> > > > > > >
> > > > > > >  drivers/i3c/Kconfig        |  28 +-
> > > > > > >  drivers/i3c/Makefile       |   2 +
> > > > > > >  drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
> > > > > > >  drivers/i3c/target.c       | 453
> ++++++++++++++++++++++++++++++
> > > > > > >  include/linux/i3c/target.h | 548
> > > > > > > +++++++++++++++++++++++++++++++++++++
> > > > > > >
> > > > > > > ...
> > > > > > >
> > > > > > > diff --git a/include/linux/i3c/target.h
> > > > > > > b/include/linux/i3c/target.h new file mode 100644 index
> > > > > > > 0000000000000..b0bf06685834c
> > > > > > > --- /dev/null
> > > > > > > +++ b/include/linux/i3c/target.h
> > > > > > >
> > > > > > > ...
> > > > > > >
> > > > > > > +/**
> > > > > > > + * struct i3c_target_ctrl_ops - set of function pointers
> > > > > > > +for performing controller operations
> > > > > > > + * @set_config: set I3C controller configuration
> > > > > > > + * @enable: enable I3C controller
> > > > > > > + * @disable: disable I3C controller
> > > > > > > + * @raise_ibi: raise IBI interrupt to master
> > > > > > > + * @alloc_request: allocate a i3c_request, optional,
> > > > > > > +default use kmalloc
> > > > > > > + * @free_request: free a i3c_request, default use kfree
> > > > > > > + * @queue: queue an I3C transfer
> > > > > > > + * @dequeue: dequeue an I3C transfer
> > > > > > > + * @cancel_all_reqs: call all pending requests
> > > > > > > + * @fifo_status: current FIFO status
> > > > > > > + * @fifo_flush: flush hardware FIFO
> > > > > > > + * @hotjoin: raise hotjoin request to master
> > > > > > > + * @set_status_format1: set i3c status format1
> > > > > > > + * @get_status_format1: get i3c status format1
> > > > > > > + * @get_addr: get i3c dynmatic address
> > > > > > > + * @get_features: ops to get the features supported by the
> > > > > > > +I3C target controller
> > > > > > > + * @owner: the module owner containing the ops  */ struct
> > > > > > > +i3c_target_ctrl_ops {
> > > > > > > +	int (*set_config)(struct i3c_target_ctrl *ctrl, struct
> > > > > > > +i3c_target_func
> > > > > > > *func);
> > > > > > > +	int (*enable)(struct i3c_target_ctrl *ctrl);
> > > > > > > +	int (*disable)(struct i3c_target_ctrl *ctrl);
> > > > > > > +	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8
> > > > > > > +size);
> > > > > > > +
> > > > > > > +	struct i3c_request *(*alloc_request)(struct
> > > > > > > +i3c_target_ctrl *ctrl, gfp_t
> > > > > > > gfp_flags);
> > > > > > > +	void (*free_request)(struct i3c_request *req);
> > > > > > > +
> > > > > > > +	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
> > > > > > > +	int (*dequeue)(struct i3c_request *req);
> > > > > > > +
> > > > > > > +	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool
> > > > > > > +tx);
> > > > > > > +
> > > > > > > +	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > > > > +	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > > > > +	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
> > > > > > > +	int (*set_status_format1)(struct i3c_target_ctrl *ctrl,
> > > > > > > +u16
> > > status);
> > > > > > > +	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
> > > > > > > +	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
> > > > > > > +	const struct i3c_target_ctrl_features
> > > > > > > +*(*get_features)(struct
> > > > > > > i3c_target_ctrl *ctrl);
> > > > > > > +	struct module *owner;
> > > > > > > +};
> > > > > >
> > > > > > This structure looks very different from the master controller
> > > > > > ops
> > > > > 'i3c_master_controller_ops'.
> > > > > > I think you could probably combine 'set_config, enable' into
> > > > > > 'bus_init',
> > > > > 'disable' to 'bus_cleanup'.
> > > > > > Also using the 'struct i3c_priv_xfer' rather redefining
> > > > > > another 'struct
> > > > > i3c_request' would be much cleaner.
> > > > >
> > > > > Thanks, let me think this. i3c_priv_xter may include read and
> > > > > write, or write and read. I3C is quite fast. Most time software
> > > > > are not quite enough to handle it in time.
> > > > >
> > > > > If data len bigger than FIFO size, it have to use DMA to
> > > > > transfer it because there are not flow controler at target side (write
> direction).
> > > > > Read, hardware to use early terminate to tell host read too fast.
> > > > > but for write, 9th bit is parit check bit. Data will be lost if
> > > > > software have not read from FIFO in time. If use external DMA to
> > > > > do that, it needs switch dma channel(generally, DMA rx and tx
> > > > > are two channel). switch channel automatically are quite
> > > > > challenge at current dma
> > > engine framework.
> > > > >
> > > >
> > > > I guess when data is larger than FIFO, the i3c controller (master)
> > > > should 'aware' of this limitation when using such target device.
> > > > So that appropriate handle like make new read command, or do a
> > > > write then another larger read request.
> > >
> > > Between two commands, such as two write command, some data may lost
> > > because first one have not chance to fetch it from FIFO.
> > >
> > > Function driver can add some recovery mathod. But still better to
> > > use modem async method to queue multi transfer and handle by dma
> > > automatically. One transfer each time are just special case for queue.
> > >
> > > If DMA support,  queue can work as big cycle buffer, underrun will
> > > never happen unless system wrong, such as disable irq over 1 second.
> > >
> > > Actually, I worry about still i3c_priv_xfer, write followed read case.
> > > Most likely, host driver do like
> > >
> > > 	write (cmd index/reg index) then read value or command result.
> > >
> > > It is hard to decode cmd and send back data immedicately.
> > >
> > > if there are not special DMA support,  more realistic usage mode is
> > >
> > > cmd1, cmd2, cmd3,....
> > >
> > > send back
> > > 	resp1, resp2, ....
> > >
> > > instead of comebine cmd1 and resp1 at one transfer.
> > >
> > > Host can do that just because all SCL are controller by host controllers.
> >
> > The DMA queue mechanism should still be invisible to the framework.
> > Target could use info from SETMRL/SETMWL to 'prepare' whether
> > internally DMA is needed.
> 
> You are right. DMA should be invisiable to the framework, but framework
> need provide API to prepare receiver buffer advance before transfer happen.
> Only function driver know how much data need be prepared advance. For
> host read/target write case, It also need cancel request function incase host
> have not fetch data in certain time. Data need be updated. function driver
> can't simple update data buffer, because it may already map to DMA.
> function driver need dequeue and queue request again.
> 
> framework should abstract common part of difference i3c target controller
> and target function driver to avoid duplicate code.
> 
> And also need consider linux's irq latency limiation, try to make the function
> driver can work on all target controller drivers.
> 
> I think priv_xfer, W, R, ...  are hard to implement. Maybe limit priv_xfer 's len to
> 1 or 2. But if limited to 1, it becames no big difference with current implement.
> 

I think if the target unable to handle multiple transaction I think it is safe
to throw NACK. In the case small transaction is needed we can still use the 
same 'struc i3c_priv_xfer'. If it is large transaction and target unable to 
support multiple transaction it should be the target limitation rather than 
limiting every target private transfer here. 

> >
> > We can also argue that the max read length should always not larger
> > than the FIFO size. Slave data transfer interface should still match
> > what i3c spec expect to be, rather than the hardware limitation here.
> >
> 
> According to spec:
>  	5.1.9.3.5 Maximum write length in a single command
> 
> I am not resure "singe command here". I think it should be between "start"
> and "repeat start/stop".
> 
> If my understand is correct,  host still send out W R W R ... as whole I3C
> transaction, each one less than FIFO size. but totally size may exceed FIFO size.
> 
> 
> >
> > >
> > > >
> > > > > Of course it will be possible if hardware implement simpilar I3C
> > > > > HCI command queue. But I still not found any hardware that can do
> that yet.
> > > > >
> > > > > If only limited data size to FIFO size, there are still
> > > > > limiation for xfer.  if write, read, write, read, two write may
> > > > > combined in one FIFO, target driver may not split it which
> > > > > depend on hardware detect repeat start and insert something into fifo.
> > > > >
> > > > > It is hard to support all user case by I3C target software in linux.
> > > >
> > > > Can I know the speed limitation that you having issue while
> > > > running on
> > > silicon?
> > >
> > > I am not sure what "speed limitation" here? Do you means irq latency
> issue.
> > >
> > > when I get "stop", there are new data already come into FIFO.  It is
> > > not for problem for tty case now.
> > >
> > > But if it is well defined frame data, it may be problem.
> > >
> > > I saw some data lost when target are busy without DMA support.
> > > driver can query how many fifo space avaible before send data, but
> > > it reduced bus utilities rate.
> > >
> > > >
> > > > >
> > > > > write follow by read, generally used from get hardware register
> > > > > value from target side, which almost impossible by software, it
> > > > > is not quick enough to parse (write value) then send back data.
> > > > >
> > > > > >
> > > > > > In the master i3c side they don't abbreviate
> > > > > > i3c_master_controller to i3c_master_ctrl. I think we should do the
> same here to standardize it.
> > > > > > Thanks
> > > > >
> > > > > The problem is in I3C spec: "master" already depericated
> > > > >
> > > > > from i3c spec 1.1.1
> > > > >
> > > > > "Controller: An I3C Device that is capable of controlling the I3C Bus."
> > > > >
> > > > > master/slave => controller/target.
> > > > >
> > > > > It will become more confused if i3c_target_controller. "controller"
> > > > > are ambiguity.
> > > > >
> > > > > If "master" => "host" would be better situation, but not happen.
> > > > > A point, I use "ctrl"  here to avoid I3C spec defined "controller" term.
> > > > >
> > > > > Anyway, I hope more people that involve target support discussion.
> > > > >
> > > > > >
> > > > > > Regards,
> > > > > > Joshua
> > > > > >
> > > > > > > --
> > > > > > > 2.34.1
> > > > > > >
> > > > > > >
> > > > > > > --
> > > > > > > linux-i3c mailing list
> > > > > > > linux-i3c at lists.infradead.org
> > > > > > > http://lists.infradead.org/mailman/listinfo/linux-i3c


From Frank.li at nxp.com  Wed May 15 08:55:49 2024
From: Frank.li at nxp.com (Frank Li)
Date: Wed, 15 May 2024 11:55:49 -0400
Subject: [PATCH resend v9 1/8] i3c: add target mode support
In-Reply-To: <SH0PR01MB084106A67DF4656C9B8CF686F9EC2@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
References: <20240507204311.2898714-1-Frank.Li@nxp.com>
 <20240507204311.2898714-2-Frank.Li@nxp.com>
 <SH0PR01MB084187FBA1E4EE786A8996C9F9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkI4GO4czdgs5/Xy@lizhi-Precision-Tower-5810>
 <SH0PR01MB084189445E4928BC5047495AF9E22@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkJgf/oRMMBswih1@lizhi-Precision-Tower-5810>
 <SH0PR01MB084183D40B4ECC0C4B3A9BF0F9E32@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
 <ZkONGQPBeuR6oZ0Q@lizhi-Precision-Tower-5810>
 <SH0PR01MB084106A67DF4656C9B8CF686F9EC2@SH0PR01MB0841.CHNPR01.prod.partner.outlook.cn>
Message-ID: <ZkTbBRRtmGlTU142@lizhi-Precision-Tower-5810>

On Wed, May 15, 2024 at 03:07:37AM +0000, Joshua Yeong wrote:
> Frank Li wrote:
> > 
> > On Tue, May 14, 2024 at 03:07:25AM +0000, Joshua Yeong wrote:
> > > Frank Li wrote:
> > > >
> > > > On Mon, May 13, 2024 at 04:18:00PM +0000, Joshua Yeong wrote:
> > > > > Frank Li wrote:
> > > > > > On Mon, May 13, 2024 at 03:16:05AM +0000, Joshua Yeong wrote:
> > > > > > > Joshua Yeong wrote:
> > > > > > > >
> > > > > > > > Introduce a new target core layer in order to support target
> > > > > > > > functions in linux kernel. This comprises the controller
> > > > > > > > library and function
> > > > > > library.
> > > > > > > > Controller library implements functions specific to an
> > > > > > > > target controller and function library implements functions
> > > > > > > > specific to an target
> > > > > > function.
> > > > > > > >
> > > > > > > > Introduce a new configfs entry to configure the target
> > > > > > > > function configuring and bind the target function with target
> > controller.
> > > > > > > >
> > > > > > > > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > > > > > > > ---
> > > > > > > >
> > > > > > > > Notes:
> > > > > > > >     Change from v8 to v9
> > > > > > > >     -none
> > > > > > > >
> > > > > > > >     Change from v7 to v8
> > > > > > > >     Added missed head files
> > > > > > > >
> > > > > > > >     Change from v5 to v6
> > > > > > > >     - fixed build error when have not CONFIG_TARGET_CONFIGFS
> > > > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > > > >     | Closes:
> > > > > > > > https://lore.kernel.org/oe-kbuild-all/202402030437.GdGCrKeK-
> > > > > > > > lkp at intel.com/
> > > > > > > >
> > > > > > > >     Chagne from v4 to v5
> > > > > > > >     - add include <linux/slab.h> to fix build error
> > > > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > > > >     | Closes:
> > > > > > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > > > > > lkp at intel.com/
> > > > > > > >
> > > > > > > >     Chagne from v4 to v8
> > > > > > > >     - add include <linux/slab.h> to fix build error
> > > > > > > >     | Reported-by: kernel test robot <lkp at intel.com>
> > > > > > > >     | Closes:
> > > > > > > > https://lore.kernel.org/oe-kbuild-all/202401270838.wdxHPaAT-
> > > > > > > > lkp at intel.com/
> > > > > > > >
> > > > > > > >  drivers/i3c/Kconfig        |  28 +-
> > > > > > > >  drivers/i3c/Makefile       |   2 +
> > > > > > > >  drivers/i3c/i3c-cfs.c      | 389 ++++++++++++++++++++++++++
> > > > > > > >  drivers/i3c/target.c       | 453
> > ++++++++++++++++++++++++++++++
> > > > > > > >  include/linux/i3c/target.h | 548
> > > > > > > > +++++++++++++++++++++++++++++++++++++
> > > > > > > >
> > > > > > > > ...
> > > > > > > >
> > > > > > > > diff --git a/include/linux/i3c/target.h
> > > > > > > > b/include/linux/i3c/target.h new file mode 100644 index
> > > > > > > > 0000000000000..b0bf06685834c
> > > > > > > > --- /dev/null
> > > > > > > > +++ b/include/linux/i3c/target.h
> > > > > > > >
> > > > > > > > ...
> > > > > > > >
> > > > > > > > +/**
> > > > > > > > + * struct i3c_target_ctrl_ops - set of function pointers
> > > > > > > > +for performing controller operations
> > > > > > > > + * @set_config: set I3C controller configuration
> > > > > > > > + * @enable: enable I3C controller
> > > > > > > > + * @disable: disable I3C controller
> > > > > > > > + * @raise_ibi: raise IBI interrupt to master
> > > > > > > > + * @alloc_request: allocate a i3c_request, optional,
> > > > > > > > +default use kmalloc
> > > > > > > > + * @free_request: free a i3c_request, default use kfree
> > > > > > > > + * @queue: queue an I3C transfer
> > > > > > > > + * @dequeue: dequeue an I3C transfer
> > > > > > > > + * @cancel_all_reqs: call all pending requests
> > > > > > > > + * @fifo_status: current FIFO status
> > > > > > > > + * @fifo_flush: flush hardware FIFO
> > > > > > > > + * @hotjoin: raise hotjoin request to master
> > > > > > > > + * @set_status_format1: set i3c status format1
> > > > > > > > + * @get_status_format1: get i3c status format1
> > > > > > > > + * @get_addr: get i3c dynmatic address
> > > > > > > > + * @get_features: ops to get the features supported by the
> > > > > > > > +I3C target controller
> > > > > > > > + * @owner: the module owner containing the ops  */ struct
> > > > > > > > +i3c_target_ctrl_ops {
> > > > > > > > +	int (*set_config)(struct i3c_target_ctrl *ctrl, struct
> > > > > > > > +i3c_target_func
> > > > > > > > *func);
> > > > > > > > +	int (*enable)(struct i3c_target_ctrl *ctrl);
> > > > > > > > +	int (*disable)(struct i3c_target_ctrl *ctrl);
> > > > > > > > +	int (*raise_ibi)(struct i3c_target_ctrl *ctrl, void *p, u8
> > > > > > > > +size);
> > > > > > > > +
> > > > > > > > +	struct i3c_request *(*alloc_request)(struct
> > > > > > > > +i3c_target_ctrl *ctrl, gfp_t
> > > > > > > > gfp_flags);
> > > > > > > > +	void (*free_request)(struct i3c_request *req);
> > > > > > > > +
> > > > > > > > +	int (*queue)(struct i3c_request *req, gfp_t gfp_flags);
> > > > > > > > +	int (*dequeue)(struct i3c_request *req);
> > > > > > > > +
> > > > > > > > +	void (*cancel_all_reqs)(struct i3c_target_ctrl *ctrl, bool
> > > > > > > > +tx);
> > > > > > > > +
> > > > > > > > +	int (*fifo_status)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > > > > > +	void (*fifo_flush)(struct i3c_target_ctrl *ctrl, bool tx);
> > > > > > > > +	int (*hotjoin)(struct i3c_target_ctrl *ctrl);
> > > > > > > > +	int (*set_status_format1)(struct i3c_target_ctrl *ctrl,
> > > > > > > > +u16
> > > > status);
> > > > > > > > +	u16 (*get_status_format1)(struct i3c_target_ctrl *ctrl);
> > > > > > > > +	u8  (*get_addr)(struct i3c_target_ctrl *ctrl);
> > > > > > > > +	const struct i3c_target_ctrl_features
> > > > > > > > +*(*get_features)(struct
> > > > > > > > i3c_target_ctrl *ctrl);
> > > > > > > > +	struct module *owner;
> > > > > > > > +};
> > > > > > >
> > > > > > > This structure looks very different from the master controller
> > > > > > > ops
> > > > > > 'i3c_master_controller_ops'.
> > > > > > > I think you could probably combine 'set_config, enable' into
> > > > > > > 'bus_init',
> > > > > > 'disable' to 'bus_cleanup'.
> > > > > > > Also using the 'struct i3c_priv_xfer' rather redefining
> > > > > > > another 'struct
> > > > > > i3c_request' would be much cleaner.
> > > > > >
> > > > > > Thanks, let me think this. i3c_priv_xter may include read and
> > > > > > write, or write and read. I3C is quite fast. Most time software
> > > > > > are not quite enough to handle it in time.
> > > > > >
> > > > > > If data len bigger than FIFO size, it have to use DMA to
> > > > > > transfer it because there are not flow controler at target side (write
> > direction).
> > > > > > Read, hardware to use early terminate to tell host read too fast.
> > > > > > but for write, 9th bit is parit check bit. Data will be lost if
> > > > > > software have not read from FIFO in time. If use external DMA to
> > > > > > do that, it needs switch dma channel(generally, DMA rx and tx
> > > > > > are two channel). switch channel automatically are quite
> > > > > > challenge at current dma
> > > > engine framework.
> > > > > >
> > > > >
> > > > > I guess when data is larger than FIFO, the i3c controller (master)
> > > > > should 'aware' of this limitation when using such target device.
> > > > > So that appropriate handle like make new read command, or do a
> > > > > write then another larger read request.
> > > >
> > > > Between two commands, such as two write command, some data may lost
> > > > because first one have not chance to fetch it from FIFO.
> > > >
> > > > Function driver can add some recovery mathod. But still better to
> > > > use modem async method to queue multi transfer and handle by dma
> > > > automatically. One transfer each time are just special case for queue.
> > > >
> > > > If DMA support,  queue can work as big cycle buffer, underrun will
> > > > never happen unless system wrong, such as disable irq over 1 second.
> > > >
> > > > Actually, I worry about still i3c_priv_xfer, write followed read case.
> > > > Most likely, host driver do like
> > > >
> > > > 	write (cmd index/reg index) then read value or command result.
> > > >
> > > > It is hard to decode cmd and send back data immedicately.
> > > >
> > > > if there are not special DMA support,  more realistic usage mode is
> > > >
> > > > cmd1, cmd2, cmd3,....
> > > >
> > > > send back
> > > > 	resp1, resp2, ....
> > > >
> > > > instead of comebine cmd1 and resp1 at one transfer.
> > > >
> > > > Host can do that just because all SCL are controller by host controllers.
> > >
> > > The DMA queue mechanism should still be invisible to the framework.
> > > Target could use info from SETMRL/SETMWL to 'prepare' whether
> > > internally DMA is needed.
> > 
> > You are right. DMA should be invisiable to the framework, but framework
> > need provide API to prepare receiver buffer advance before transfer happen.
> > Only function driver know how much data need be prepared advance. For
> > host read/target write case, It also need cancel request function incase host
> > have not fetch data in certain time. Data need be updated. function driver
> > can't simple update data buffer, because it may already map to DMA.
> > function driver need dequeue and queue request again.
> > 
> > framework should abstract common part of difference i3c target controller
> > and target function driver to avoid duplicate code.
> > 
> > And also need consider linux's irq latency limiation, try to make the function
> > driver can work on all target controller drivers.
> > 
> > I think priv_xfer, W, R, ...  are hard to implement. Maybe limit priv_xfer 's len to
> > 1 or 2. But if limited to 1, it becames no big difference with current implement.
> > 
> 
> I think if the target unable to handle multiple transaction I think it is safe
> to throw NACK. In the case small transaction is needed we can still use the 
> same 'struc i3c_priv_xfer'. If it is large transaction and target unable to 
> support multiple transaction it should be the target limitation rather than 
> limiting every target private transfer here. 
> 

Do you have any actual user case for i3c target? So I will have better
overy all view?

Frank

> > >
> > > We can also argue that the max read length should always not larger
> > > than the FIFO size. Slave data transfer interface should still match
> > > what i3c spec expect to be, rather than the hardware limitation here.
> > >
> > 
> > According to spec:
> >  	5.1.9.3.5 Maximum write length in a single command
> > 
> > I am not resure "singe command here". I think it should be between "start"
> > and "repeat start/stop".
> > 
> > If my understand is correct,  host still send out W R W R ... as whole I3C
> > transaction, each one less than FIFO size. but totally size may exceed FIFO size.
> > 
> > 
> > >
> > > >
> > > > >
> > > > > > Of course it will be possible if hardware implement simpilar I3C
> > > > > > HCI command queue. But I still not found any hardware that can do
> > that yet.
> > > > > >
> > > > > > If only limited data size to FIFO size, there are still
> > > > > > limiation for xfer.  if write, read, write, read, two write may
> > > > > > combined in one FIFO, target driver may not split it which
> > > > > > depend on hardware detect repeat start and insert something into fifo.
> > > > > >
> > > > > > It is hard to support all user case by I3C target software in linux.
> > > > >
> > > > > Can I know the speed limitation that you having issue while
> > > > > running on
> > > > silicon?
> > > >
> > > > I am not sure what "speed limitation" here? Do you means irq latency
> > issue.
> > > >
> > > > when I get "stop", there are new data already come into FIFO.  It is
> > > > not for problem for tty case now.
> > > >
> > > > But if it is well defined frame data, it may be problem.
> > > >
> > > > I saw some data lost when target are busy without DMA support.
> > > > driver can query how many fifo space avaible before send data, but
> > > > it reduced bus utilities rate.
> > > >
> > > > >
> > > > > >
> > > > > > write follow by read, generally used from get hardware register
> > > > > > value from target side, which almost impossible by software, it
> > > > > > is not quick enough to parse (write value) then send back data.
> > > > > >
> > > > > > >
> > > > > > > In the master i3c side they don't abbreviate
> > > > > > > i3c_master_controller to i3c_master_ctrl. I think we should do the
> > same here to standardize it.
> > > > > > > Thanks
> > > > > >
> > > > > > The problem is in I3C spec: "master" already depericated
> > > > > >
> > > > > > from i3c spec 1.1.1
> > > > > >
> > > > > > "Controller: An I3C Device that is capable of controlling the I3C Bus."
> > > > > >
> > > > > > master/slave => controller/target.
> > > > > >
> > > > > > It will become more confused if i3c_target_controller. "controller"
> > > > > > are ambiguity.
> > > > > >
> > > > > > If "master" => "host" would be better situation, but not happen.
> > > > > > A point, I use "ctrl"  here to avoid I3C spec defined "controller" term.
> > > > > >
> > > > > > Anyway, I hope more people that involve target support discussion.
> > > > > >
> > > > > > >
> > > > > > > Regards,
> > > > > > > Joshua
> > > > > > >
> > > > > > > > --
> > > > > > > > 2.34.1
> > > > > > > >
> > > > > > > >
> > > > > > > > --
> > > > > > > > linux-i3c mailing list
> > > > > > > > linux-i3c at lists.infradead.org
> > > > > > > > http://lists.infradead.org/mailman/listinfo/linux-i3c


From alexandre.belloni at bootlin.com  Sat May 25 00:52:17 2024
From: alexandre.belloni at bootlin.com (Alexandre Belloni)
Date: Sat, 25 May 2024 09:52:17 +0200
Subject: [GIT PULL] I3C changes for 6.10
Message-ID: <20240525075217d90cc066@mail.local>

iHello Linus,

Here is the i3c subsytem pull request for 6.10. Runtime PM (power
management) is improved and hot-join support has been added to the dw
controller driver.

The following changes since commit 4cece764965020c22cff7665b18a012006359095:

  Linux 6.9-rc1 (2024-03-24 14:10:05 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux.git tags/i3c/for-6.10

for you to fetch changes up to 1d08326020fba690cbb7b8f1b38ab4eab6745969:

  i3c: dw: Add hot-join support. (2024-05-23 00:29:19 +0200)

----------------------------------------------------------------
I3C for 6.10

Core:
 - Allow device driver to trigger controller runtime PM

Drivers:
 - dw: hot-join support
 - svc: better IBI handling

----------------------------------------------------------------
Billy Tsai (1):
      i3c: dw: Add hot-join support.

Frank Li (3):
      i3c: Add comment for -EAGAIN in i3c_device_do_priv_xfers()
      i3c: master: svc: change ENXIO to EAGAIN when IBI occurs during start frame
      i3c: master: svc: fix invalidate IBI type and miss call client IBI handler

Mukesh Kumar Savaliya (1):
      i3c: master: Enable runtime PM for master controller

 drivers/i3c/device.c                |  4 +++
 drivers/i3c/master.c                |  6 ++++
 drivers/i3c/master/dw-i3c-master.c  | 65 ++++++++++++++++++++++++++++++-------
 drivers/i3c/master/dw-i3c-master.h  |  2 ++
 drivers/i3c/master/svc-i3c-master.c | 18 +++++++---
 5 files changed, 79 insertions(+), 16 deletions(-)

-- 
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


From pr-tracker-bot at kernel.org  Sat May 25 14:08:34 2024
From: pr-tracker-bot at kernel.org (pr-tracker-bot at kernel.org)
Date: Sat, 25 May 2024 21:08:34 +0000
Subject: [GIT PULL] I3C changes for 6.10
In-Reply-To: <20240525075217d90cc066@mail.local>
References: <20240525075217d90cc066@mail.local>
Message-ID: <171667131489.10959.15105780706399557618.pr-tracker-bot@kernel.org>

The pull request you sent on Sat, 25 May 2024 09:52:17 +0200:

> git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux.git tags/i3c/for-6.10

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/4286e1fceb8c99f25332dc7e85f9879408caa45a

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html


From Frank.Li at nxp.com  Fri May 31 08:43:08 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Fri, 31 May 2024 11:43:08 -0400
Subject: [PATCH 1/1] i3c: master: svc: fix IBIWON not set if IBI follow a hot join
Message-ID: <20240531154308.1246214-1-Frank.Li@nxp.com>

When an In-Band Interrupt(IBI) occurs after svc_i3c_master_do_daa_locked(),
typically triggered during a Hot Join (HJ) event, the IBIWON flag fails to
be set when issuing an auto IBI command.

The issue stems from the omission of emitting STOP upon successful
execution of svc_i3c_master_do_daa_locked(). Consequently, the controller
interprets it as a repeat start when emitting the auto IBI command. Per the
I3C specification, an IBI should never occur during a repeat start, thus
preventing the IBIWON flag from being set.

Emit STOP regardless of the success or failure of
svc_i3c_master_do_daa_locked() to match I3C spec requirement.

Cc: <stable at vger.kernel.org>
Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---

Notes:
    Change from v1 to v2
    - new patch

 drivers/i3c/master/svc-i3c-master.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index bb299ce02cccb..032fe032ec433 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -961,11 +961,11 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
 	spin_lock_irqsave(&master->xferqueue.lock, flags);
 	ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
 	spin_unlock_irqrestore(&master->xferqueue.lock, flags);
-	if (ret) {
-		svc_i3c_master_emit_stop(master);
-		svc_i3c_master_clear_merrwarn(master);
+
+	svc_i3c_master_emit_stop(master);
+	svc_i3c_master_clear_merrwarn(master);
+	if (ret)
 		goto rpm_out;
-	}
 
 	/* Register all devices who participated to the core */
 	for (i = 0; i < dev_nb; i++) {
-- 
2.34.1



From Frank.Li at nxp.com  Fri May 31 09:33:00 2024
From: Frank.Li at nxp.com (Frank Li)
Date: Fri, 31 May 2024 12:33:00 -0400
Subject: [PATCH 1/1] i3c: master: svc: resend target address when get NACK
Message-ID: <20240531163301.1401281-1-Frank.Li@nxp.com>

According to I3C Spec 1.1.1, 11-Jun-2021, section: 5.1.2.2.3:

If the Controller chooses to start an I3C Message with an I3C Dynamic
Address, then special provisions shall be made because that same I3C Target
may be initiating an IBI or a Controller Role Request. So, one of three
things may happen: (skip 1, 2)

3. The Addresses match and the RnW bits also match, and so neither
Controller nor Target will ACK since both are expecting the other side to
provide ACK. As a result, each side might think it had "won" arbitration,
but neither side would continue, as each would subsequently see that the
other did not provide ACK.
...
For either value of RnW: Due to the NACK, the Controller shall defer the
Private Write or Private Read, and should typically transmit the Target
						    ^^^^^^^^^^^^^^^^^^^
Address again after a Repeated START (i.e., the next one or any one prior
^^^^^^^^^^^^^
to a STOP in the Frame). Since the Address Header following a Repeated
START is not arbitrated, the Controller will always win (see Section
5.1.2.2.4).

Resend target address again if address is not 7E and controller get NACK.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 58 ++++++++++++++++++++++-------
 1 file changed, 44 insertions(+), 14 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 032fe032ec433..0b588ed5894ea 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1052,29 +1052,59 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 			       u8 *in, const u8 *out, unsigned int xfer_len,
 			       unsigned int *actual_len, bool continued)
 {
+	int retry = 2;
 	u32 reg;
 	int ret;
 
 	/* clean SVC_I3C_MINT_IBIWON w1c bits */
 	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
 
-	writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
-	       xfer_type |
-	       SVC_I3C_MCTRL_IBIRESP_NACK |
-	       SVC_I3C_MCTRL_DIR(rnw) |
-	       SVC_I3C_MCTRL_ADDR(addr) |
-	       SVC_I3C_MCTRL_RDTERM(*actual_len),
-	       master->regs + SVC_I3C_MCTRL);
 
-	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
+	while (retry--) {
+		writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
+		       xfer_type |
+		       SVC_I3C_MCTRL_IBIRESP_NACK |
+		       SVC_I3C_MCTRL_DIR(rnw) |
+		       SVC_I3C_MCTRL_ADDR(addr) |
+		       SVC_I3C_MCTRL_RDTERM(*actual_len),
+		       master->regs + SVC_I3C_MCTRL);
+
+		ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
 				 SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
-	if (ret)
-		goto emit_stop;
+		if (ret)
+			goto emit_stop;
 
-	if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
-		ret = -ENXIO;
-		*actual_len = 0;
-		goto emit_stop;
+		if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
+			/*
+			 * According to I3C Spec 1.1.1, 11-Jun-2021, section: 5.1.2.2.3.
+			 * If the Controller chooses to start an I3C Message with an I3C Dynamic
+			 * Address, then special provisions shall be made because that same I3C
+			 * Target may be initiating an IBI or a Controller Role Request. So, one of
+			 * three things may happen: (skip 1, 2)
+			 *
+			 * 3. The Addresses match and the RnW bits also match, and so neither
+			 * Controller nor Target will ACK since both are expecting the other side to
+			 * provide ACK. As a result, each side might think it had "won" arbitration,
+			 * but neither side would continue, as each would subsequently see that the
+			 * other did not provide ACK.
+			 * ...
+			 * For either value of RnW: Due to the NACK, the Controller shall defer the
+			 * Private Write or Private Read, and should typically transmit the Target
+			 * Address again after a Repeated START (i.e., the next one or any one prior
+			 * to a STOP in the Frame). Since the Address Header following a Repeated
+			 * START is not arbitrated, the Controller will always win (see Section
+			 * 5.1.2.2.4).
+			 */
+			if (retry && addr != 0x7e) {
+				writel(SVC_I3C_MERRWARN_NACK, master->regs + SVC_I3C_MERRWARN);
+			} else {
+				ret = -ENXIO;
+				*actual_len = 0;
+				goto emit_stop;
+			}
+		} else {
+			break;
+		}
 	}
 
 	/*
-- 
2.34.1



