All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/9] I3C Slave Mode support
@ 2023-09-05 21:38 ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

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

Typic usage as

The user can configure the i3c-slave-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-slave-tty Device to slave Controller
------------------------------------------------

In order for the slave function device to be useful, it has to be bound to
a I3C slave 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-slave/

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

These patches have not full implemented, especially at tty driver side.

But it does not impact to review i3c slave part, especially data transfer
and configfs.

Frank Li (9):
  i3c: add actual in i3c_priv_xfer
  i3c: svc: rename read_len as actual_len
  i3c: master: svc return actual transfer data len
  dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
  i3c: add slave mode support
  i3c: slave: add svc slave controller support
  i3c: slave: func: add tty driver
  tty: serial: add tty over I3C master side driver
  Documentation: i3c: Add I3C slave mode controller and function

 .../bindings/i3c/silvaco,i3c-master.yaml      |   7 +-
 Documentation/driver-api/i3c/index.rst        |   1 +
 .../driver-api/i3c/slave/i3c-slave-cfs.rst    | 109 ++++
 .../driver-api/i3c/slave/i3c-slave.rst        | 189 ++++++
 .../driver-api/i3c/slave/i3c-tty-function.rst | 103 +++
 .../driver-api/i3c/slave/i3c-tty-howto.rst    | 109 ++++
 Documentation/driver-api/i3c/slave/index.rst  |  13 +
 drivers/i3c/Kconfig                           |  30 +
 drivers/i3c/Makefile                          |   4 +
 drivers/i3c/func/Kconfig                      |   9 +
 drivers/i3c/func/Makefile                     |   3 +
 drivers/i3c/func/tty.c                        | 345 ++++++++++
 drivers/i3c/i3c-cfs.c                         | 361 +++++++++++
 drivers/i3c/master/svc-i3c-master.c           |  52 +-
 drivers/i3c/slave.c                           | 441 +++++++++++++
 drivers/i3c/slave/Kconfig                     |   9 +
 drivers/i3c/slave/Makefile                    |   4 +
 drivers/i3c/slave/svc-i3c-slave.c             | 600 ++++++++++++++++++
 drivers/tty/serial/Kconfig                    |   6 +
 drivers/tty/serial/Makefile                   |   1 +
 drivers/tty/serial/ttyi3c.c                   | 276 ++++++++
 include/linux/i3c/device.h                    |   1 +
 include/linux/i3c/slave.h                     | 458 +++++++++++++
 23 files changed, 3109 insertions(+), 22 deletions(-)
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-function.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
 create mode 100644 Documentation/driver-api/i3c/slave/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/slave.c
 create mode 100644 drivers/i3c/slave/Kconfig
 create mode 100644 drivers/i3c/slave/Makefile
 create mode 100644 drivers/i3c/slave/svc-i3c-slave.c
 create mode 100644 drivers/tty/serial/ttyi3c.c
 create mode 100644 include/linux/i3c/slave.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [RFC PATCH 0/9] I3C Slave Mode support
@ 2023-09-05 21:38 ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

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

Typic usage as

The user can configure the i3c-slave-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-slave-tty Device to slave Controller
------------------------------------------------

In order for the slave function device to be useful, it has to be bound to
a I3C slave 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-slave/

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

These patches have not full implemented, especially at tty driver side.

But it does not impact to review i3c slave part, especially data transfer
and configfs.

Frank Li (9):
  i3c: add actual in i3c_priv_xfer
  i3c: svc: rename read_len as actual_len
  i3c: master: svc return actual transfer data len
  dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
  i3c: add slave mode support
  i3c: slave: add svc slave controller support
  i3c: slave: func: add tty driver
  tty: serial: add tty over I3C master side driver
  Documentation: i3c: Add I3C slave mode controller and function

 .../bindings/i3c/silvaco,i3c-master.yaml      |   7 +-
 Documentation/driver-api/i3c/index.rst        |   1 +
 .../driver-api/i3c/slave/i3c-slave-cfs.rst    | 109 ++++
 .../driver-api/i3c/slave/i3c-slave.rst        | 189 ++++++
 .../driver-api/i3c/slave/i3c-tty-function.rst | 103 +++
 .../driver-api/i3c/slave/i3c-tty-howto.rst    | 109 ++++
 Documentation/driver-api/i3c/slave/index.rst  |  13 +
 drivers/i3c/Kconfig                           |  30 +
 drivers/i3c/Makefile                          |   4 +
 drivers/i3c/func/Kconfig                      |   9 +
 drivers/i3c/func/Makefile                     |   3 +
 drivers/i3c/func/tty.c                        | 345 ++++++++++
 drivers/i3c/i3c-cfs.c                         | 361 +++++++++++
 drivers/i3c/master/svc-i3c-master.c           |  52 +-
 drivers/i3c/slave.c                           | 441 +++++++++++++
 drivers/i3c/slave/Kconfig                     |   9 +
 drivers/i3c/slave/Makefile                    |   4 +
 drivers/i3c/slave/svc-i3c-slave.c             | 600 ++++++++++++++++++
 drivers/tty/serial/Kconfig                    |   6 +
 drivers/tty/serial/Makefile                   |   1 +
 drivers/tty/serial/ttyi3c.c                   | 276 ++++++++
 include/linux/i3c/device.h                    |   1 +
 include/linux/i3c/slave.h                     | 458 +++++++++++++
 23 files changed, 3109 insertions(+), 22 deletions(-)
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-function.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
 create mode 100644 Documentation/driver-api/i3c/slave/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/slave.c
 create mode 100644 drivers/i3c/slave/Kconfig
 create mode 100644 drivers/i3c/slave/Makefile
 create mode 100644 drivers/i3c/slave/svc-i3c-slave.c
 create mode 100644 drivers/tty/serial/ttyi3c.c
 create mode 100644 include/linux/i3c/slave.h

-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [RFC PATCH 1/9] i3c: add actual in i3c_priv_xfer
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

In MIPI I3C Specification:

"Ninth Bit of SDR Target Returned (Read) Data as End-of-Data: In I2C, the
ninth Data bit from Target to Controller is an ACK by the Controller. By
contrast, in I3C this bit allows the Target to end a Read, and allows the
Controller to Abort a Read. In SDR terms, the ninth bit of Read data is
referred to as the T-Bit (for ‘Transition’)"

I3C allow devices early terminate data transfer. So need "actual" field to
indicate how much get by i3c_priv_xfer.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 include/linux/i3c/device.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 90fa83464f003..f2fa7ee5d96d9 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -66,6 +66,7 @@ struct i3c_priv_xfer {
 		void *in;
 		const void *out;
 	} data;
+	u16 actual;
 	enum i3c_error_code err;
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 1/9] i3c: add actual in i3c_priv_xfer
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

In MIPI I3C Specification:

"Ninth Bit of SDR Target Returned (Read) Data as End-of-Data: In I2C, the
ninth Data bit from Target to Controller is an ACK by the Controller. By
contrast, in I3C this bit allows the Target to end a Read, and allows the
Controller to Abort a Read. In SDR terms, the ninth bit of Read data is
referred to as the T-Bit (for ‘Transition’)"

I3C allow devices early terminate data transfer. So need "actual" field to
indicate how much get by i3c_priv_xfer.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 include/linux/i3c/device.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 90fa83464f003..f2fa7ee5d96d9 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -66,6 +66,7 @@ struct i3c_priv_xfer {
 		void *in;
 		const void *out;
 	} data;
+	u16 actual;
 	enum i3c_error_code err;
 };
 
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 2/9] i3c: svc: rename read_len as actual_len
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

Device may NACK data transfer because FIFO full. Master side need know how
much already transferred. Driver can reuse variable 'read_len', so rename
it to void confuse.

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index c308e22f0ac5e..14185ee7dd19b 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -133,7 +133,7 @@ struct svc_i3c_cmd {
 	u8 *in;
 	const void *out;
 	unsigned int len;
-	unsigned int read_len;
+	unsigned int actual_len;
 	bool continued;
 };
 
@@ -1001,7 +1001,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
 static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 			       bool rnw, unsigned int xfer_type, u8 addr,
 			       u8 *in, const u8 *out, unsigned int xfer_len,
-			       unsigned int *read_len, bool continued)
+			       unsigned int *actual_len, bool continued)
 {
 	u32 reg;
 	int ret;
@@ -1011,7 +1011,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
 	       SVC_I3C_MCTRL_DIR(rnw) |
 	       SVC_I3C_MCTRL_ADDR(addr) |
-	       SVC_I3C_MCTRL_RDTERM(*read_len),
+	       SVC_I3C_MCTRL_RDTERM(*actual_len),
 	       master->regs + SVC_I3C_MCTRL);
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
@@ -1032,7 +1032,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 
 	if (rnw)
-		*read_len = ret;
+		*actual_len = ret;
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
 				 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
@@ -1114,7 +1114,7 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 
 		ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
 					  cmd->addr, cmd->in, cmd->out,
-					  cmd->len, &cmd->read_len,
+					  cmd->len, &cmd->actual_len,
 					  cmd->continued);
 		if (ret)
 			break;
@@ -1200,7 +1200,7 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = NULL;
 	cmd->out = buf;
 	cmd->len = xfer_len;
-	cmd->read_len = 0;
+	cmd->actual_len = 0;
 	cmd->continued = false;
 
 	svc_i3c_master_enqueue_xfer(master, xfer);
@@ -1218,7 +1218,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 					      struct i3c_ccc_cmd *ccc)
 {
 	unsigned int xfer_len = ccc->dests[0].payload.len;
-	unsigned int read_len = ccc->rnw ? xfer_len : 0;
+	unsigned int actual_len = ccc->rnw ? xfer_len : 0;
 	struct svc_i3c_xfer *xfer;
 	struct svc_i3c_cmd *cmd;
 	int ret;
@@ -1236,7 +1236,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = NULL;
 	cmd->out = &ccc->id;
 	cmd->len = 1;
-	cmd->read_len = 0;
+	cmd->actual_len = 0;
 	cmd->continued = true;
 
 	/* Directed message */
@@ -1246,15 +1246,15 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL;
 	cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data,
 	cmd->len = xfer_len;
-	cmd->read_len = read_len;
+	cmd->actual_len = actual_len;
 	cmd->continued = false;
 
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
 
-	if (cmd->read_len != xfer_len)
-		ccc->dests[0].payload.len = cmd->read_len;
+	if (cmd->actual_len != xfer_len)
+		ccc->dests[0].payload.len = cmd->actual_len;
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1304,7 +1304,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
 		cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
 		cmd->len = xfers[i].len;
-		cmd->read_len = xfers[i].rnw ? xfers[i].len : 0;
+		cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0;
 		cmd->continued = (i + 1) < nxfers;
 	}
 
@@ -1342,7 +1342,7 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->in = cmd->rnw ? xfers[i].buf : NULL;
 		cmd->out = cmd->rnw ? NULL : xfers[i].buf;
 		cmd->len = xfers[i].len;
-		cmd->read_len = cmd->rnw ? xfers[i].len : 0;
+		cmd->actual_len = cmd->rnw ? xfers[i].len : 0;
 		cmd->continued = (i + 1 < nxfers);
 	}
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 2/9] i3c: svc: rename read_len as actual_len
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

Device may NACK data transfer because FIFO full. Master side need know how
much already transferred. Driver can reuse variable 'read_len', so rename
it to void confuse.

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index c308e22f0ac5e..14185ee7dd19b 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -133,7 +133,7 @@ struct svc_i3c_cmd {
 	u8 *in;
 	const void *out;
 	unsigned int len;
-	unsigned int read_len;
+	unsigned int actual_len;
 	bool continued;
 };
 
@@ -1001,7 +1001,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
 static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 			       bool rnw, unsigned int xfer_type, u8 addr,
 			       u8 *in, const u8 *out, unsigned int xfer_len,
-			       unsigned int *read_len, bool continued)
+			       unsigned int *actual_len, bool continued)
 {
 	u32 reg;
 	int ret;
@@ -1011,7 +1011,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
 	       SVC_I3C_MCTRL_DIR(rnw) |
 	       SVC_I3C_MCTRL_ADDR(addr) |
-	       SVC_I3C_MCTRL_RDTERM(*read_len),
+	       SVC_I3C_MCTRL_RDTERM(*actual_len),
 	       master->regs + SVC_I3C_MCTRL);
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
@@ -1032,7 +1032,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 
 	if (rnw)
-		*read_len = ret;
+		*actual_len = ret;
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
 				 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
@@ -1114,7 +1114,7 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 
 		ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
 					  cmd->addr, cmd->in, cmd->out,
-					  cmd->len, &cmd->read_len,
+					  cmd->len, &cmd->actual_len,
 					  cmd->continued);
 		if (ret)
 			break;
@@ -1200,7 +1200,7 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = NULL;
 	cmd->out = buf;
 	cmd->len = xfer_len;
-	cmd->read_len = 0;
+	cmd->actual_len = 0;
 	cmd->continued = false;
 
 	svc_i3c_master_enqueue_xfer(master, xfer);
@@ -1218,7 +1218,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 					      struct i3c_ccc_cmd *ccc)
 {
 	unsigned int xfer_len = ccc->dests[0].payload.len;
-	unsigned int read_len = ccc->rnw ? xfer_len : 0;
+	unsigned int actual_len = ccc->rnw ? xfer_len : 0;
 	struct svc_i3c_xfer *xfer;
 	struct svc_i3c_cmd *cmd;
 	int ret;
@@ -1236,7 +1236,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = NULL;
 	cmd->out = &ccc->id;
 	cmd->len = 1;
-	cmd->read_len = 0;
+	cmd->actual_len = 0;
 	cmd->continued = true;
 
 	/* Directed message */
@@ -1246,15 +1246,15 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL;
 	cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data,
 	cmd->len = xfer_len;
-	cmd->read_len = read_len;
+	cmd->actual_len = actual_len;
 	cmd->continued = false;
 
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
 
-	if (cmd->read_len != xfer_len)
-		ccc->dests[0].payload.len = cmd->read_len;
+	if (cmd->actual_len != xfer_len)
+		ccc->dests[0].payload.len = cmd->actual_len;
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1304,7 +1304,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
 		cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
 		cmd->len = xfers[i].len;
-		cmd->read_len = xfers[i].rnw ? xfers[i].len : 0;
+		cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0;
 		cmd->continued = (i + 1) < nxfers;
 	}
 
@@ -1342,7 +1342,7 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->in = cmd->rnw ? xfers[i].buf : NULL;
 		cmd->out = cmd->rnw ? NULL : xfers[i].buf;
 		cmd->len = xfers[i].len;
-		cmd->read_len = cmd->rnw ? xfers[i].len : 0;
+		cmd->actual_len = cmd->rnw ? xfers[i].len : 0;
 		cmd->continued = (i + 1 < nxfers);
 	}
 
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 3/9] i3c: master: svc return actual transfer data len
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

I3C allow devices early terminate data transfer. So set "actual" to
indicate how much data get by i3c_priv_xfer.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 14185ee7dd19b..74c4ce8789daf 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -134,6 +134,7 @@ struct svc_i3c_cmd {
 	const void *out;
 	unsigned int len;
 	unsigned int actual_len;
+	struct i3c_priv_xfer *xfer;
 	bool continued;
 };
 
@@ -972,12 +973,14 @@ static int svc_i3c_master_read(struct svc_i3c_master *master,
 }
 
 static int svc_i3c_master_write(struct svc_i3c_master *master,
-				const u8 *out, unsigned int len)
+				const u8 *out, unsigned int len, unsigned int *actual)
 {
-	int offset = 0, ret;
+	int ret;
 	u32 mdctrl;
 
-	while (offset < len) {
+	*actual = 0;
+
+	while (*actual < len) {
 		ret = readl_poll_timeout(master->regs + SVC_I3C_MDATACTRL,
 					 mdctrl,
 					 !(mdctrl & SVC_I3C_MDATACTRL_TXFULL),
@@ -989,10 +992,10 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
 		 * The last byte to be sent over the bus must either have the
 		 * "end" bit set or be written in MWDATABE.
 		 */
-		if (likely(offset < (len - 1)))
-			writel(out[offset++], master->regs + SVC_I3C_MWDATAB);
+		if (likely(*actual < (len - 1)))
+			writel(out[(*actual)++], master->regs + SVC_I3C_MWDATAB);
 		else
-			writel(out[offset++], master->regs + SVC_I3C_MWDATABE);
+			writel(out[(*actual)++], master->regs + SVC_I3C_MWDATABE);
 	}
 
 	return 0;
@@ -1027,7 +1030,8 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	if (rnw)
 		ret = svc_i3c_master_read(master, in, xfer_len);
 	else
-		ret = svc_i3c_master_write(master, out, xfer_len);
+		ret = svc_i3c_master_write(master, out, xfer_len, actual_len);
+
 	if (ret < 0)
 		goto emit_stop;
 
@@ -1047,6 +1051,10 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		/* Wait idle if stop is sent. */
 		readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
 				   SVC_I3C_MSTATUS_STATE_IDLE(reg), 0, 1000);
+
+		if (!rnw)
+			*actual_len -= SVC_I3C_MDATACTRL_RXCOUNT(
+							readl(master->regs + SVC_I3C_MDATACTRL));
 	}
 
 	return 0;
@@ -1116,6 +1124,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 					  cmd->addr, cmd->in, cmd->out,
 					  cmd->len, &cmd->actual_len,
 					  cmd->continued);
+		if (cmd->xfer)
+			cmd->xfer->actual = cmd->actual_len;
+
 		if (ret)
 			break;
 	}
@@ -1299,6 +1310,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 	for (i = 0; i < nxfers; i++) {
 		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
 
+		cmd->xfer = xfers + i;
 		cmd->addr = master->addrs[data->index];
 		cmd->rnw = xfers[i].rnw;
 		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 3/9] i3c: master: svc return actual transfer data len
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

I3C allow devices early terminate data transfer. So set "actual" to
indicate how much data get by i3c_priv_xfer.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 14185ee7dd19b..74c4ce8789daf 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -134,6 +134,7 @@ struct svc_i3c_cmd {
 	const void *out;
 	unsigned int len;
 	unsigned int actual_len;
+	struct i3c_priv_xfer *xfer;
 	bool continued;
 };
 
@@ -972,12 +973,14 @@ static int svc_i3c_master_read(struct svc_i3c_master *master,
 }
 
 static int svc_i3c_master_write(struct svc_i3c_master *master,
-				const u8 *out, unsigned int len)
+				const u8 *out, unsigned int len, unsigned int *actual)
 {
-	int offset = 0, ret;
+	int ret;
 	u32 mdctrl;
 
-	while (offset < len) {
+	*actual = 0;
+
+	while (*actual < len) {
 		ret = readl_poll_timeout(master->regs + SVC_I3C_MDATACTRL,
 					 mdctrl,
 					 !(mdctrl & SVC_I3C_MDATACTRL_TXFULL),
@@ -989,10 +992,10 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
 		 * The last byte to be sent over the bus must either have the
 		 * "end" bit set or be written in MWDATABE.
 		 */
-		if (likely(offset < (len - 1)))
-			writel(out[offset++], master->regs + SVC_I3C_MWDATAB);
+		if (likely(*actual < (len - 1)))
+			writel(out[(*actual)++], master->regs + SVC_I3C_MWDATAB);
 		else
-			writel(out[offset++], master->regs + SVC_I3C_MWDATABE);
+			writel(out[(*actual)++], master->regs + SVC_I3C_MWDATABE);
 	}
 
 	return 0;
@@ -1027,7 +1030,8 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	if (rnw)
 		ret = svc_i3c_master_read(master, in, xfer_len);
 	else
-		ret = svc_i3c_master_write(master, out, xfer_len);
+		ret = svc_i3c_master_write(master, out, xfer_len, actual_len);
+
 	if (ret < 0)
 		goto emit_stop;
 
@@ -1047,6 +1051,10 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		/* Wait idle if stop is sent. */
 		readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
 				   SVC_I3C_MSTATUS_STATE_IDLE(reg), 0, 1000);
+
+		if (!rnw)
+			*actual_len -= SVC_I3C_MDATACTRL_RXCOUNT(
+							readl(master->regs + SVC_I3C_MDATACTRL));
 	}
 
 	return 0;
@@ -1116,6 +1124,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 					  cmd->addr, cmd->in, cmd->out,
 					  cmd->len, &cmd->actual_len,
 					  cmd->continued);
+		if (cmd->xfer)
+			cmd->xfer->actual = cmd->actual_len;
+
 		if (ret)
 			break;
 	}
@@ -1299,6 +1310,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 	for (i = 0; i < nxfers; i++) {
 		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
 
+		cmd->xfer = xfers + i;
 		cmd->addr = master->addrs[data->index];
 		cmd->rnw = xfers[i].rnw;
 		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

Add compatible string 'silvaco,i3c-slave-v1' for slave mode.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 .../devicetree/bindings/i3c/silvaco,i3c-master.yaml        | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
index 133855f11b4f5..da8a2f358505a 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/slave
 
 maintainers:
   - Conor Culhane <conor.culhane@silvaco.com>
@@ -14,7 +14,10 @@ allOf:
 
 properties:
   compatible:
-    const: silvaco,i3c-master-v1
+    const:
+      enum:
+        - silvaco,i3c-master-v1
+        - silvaco,i3c-slave-v1
 
   reg:
     maxItems: 1
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

Add compatible string 'silvaco,i3c-slave-v1' for slave mode.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 .../devicetree/bindings/i3c/silvaco,i3c-master.yaml        | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
index 133855f11b4f5..da8a2f358505a 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/slave
 
 maintainers:
   - Conor Culhane <conor.culhane@silvaco.com>
@@ -14,7 +14,10 @@ allOf:
 
 properties:
   compatible:
-    const: silvaco,i3c-master-v1
+    const:
+      enum:
+        - silvaco,i3c-master-v1
+        - silvaco,i3c-slave-v1
 
   reg:
     maxItems: 1
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 5/9] i3c: add slave mode support
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

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

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

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/i3c/Kconfig       |  29 +++
 drivers/i3c/Makefile      |   2 +
 drivers/i3c/i3c-cfs.c     | 361 ++++++++++++++++++++++++++++++
 drivers/i3c/slave.c       | 441 ++++++++++++++++++++++++++++++++++++
 include/linux/i3c/slave.h | 458 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1291 insertions(+)
 create mode 100644 drivers/i3c/i3c-cfs.c
 create mode 100644 drivers/i3c/slave.c
 create mode 100644 include/linux/i3c/slave.h

diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index 30a441506f61c..bdc173bc0da12 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -22,3 +22,32 @@ menuconfig I3C
 if I3C
 source "drivers/i3c/master/Kconfig"
 endif # I3C
+
+config I3C_SLAVE
+	bool "I3C Slave Support"
+	help
+	  Support I3C Slave Mode.
+
+	  Enable this configuration option to support configurable I3C slave.
+	  This should be enabled if the platform has a I3C controller that can
+	  operate in slave mode.
+
+	  Enabling this option will build the I3C slave library, which includes
+	  slave controller library and slave function library.
+
+	  If in doubt, say "N" to disable slave support.
+
+config I3C_SLAVE_CONFIGFS
+	bool "I3C Slave Configfs Support"
+	depends on I3C_SLAVE
+	select CONFIGFS_FS
+	help
+	  Configfs entry for slave function and controller.
+
+	  This will enable the configfs entry that can be used to configure
+	  the slave function and used to bind the function with a slave
+	  controller.
+
+if I3C_SLAVE
+source "drivers/i3c/slave/Kconfig"
+endif #I#C_SLAVE
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 11982efbc6d91..6407ddec3a4a9 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -2,3 +2,5 @@
 i3c-y				:= device.o master.o
 obj-$(CONFIG_I3C)		+= i3c.o
 obj-$(CONFIG_I3C)		+= master/
+obj-$(CONFIG_I3C_SLAVE)		+= slave.o
+obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
diff --git a/drivers/i3c/i3c-cfs.c b/drivers/i3c/i3c-cfs.c
new file mode 100644
index 0000000000000..1f53fada43645
--- /dev/null
+++ b/drivers/i3c/i3c-cfs.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Configfs to configure the I3C Slave
+ *
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/i3c/slave.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(functions_mutex);
+static struct config_group *functions_group;
+static struct config_group *controllers_group;
+
+struct i3c_slave_func_group {
+	struct config_group group;
+	struct i3c_slave_func *func;
+};
+
+struct i3c_slave_ctrl_group {
+	struct config_group group;
+	struct i3c_slave_ctrl *ctrl;
+};
+
+static inline struct i3c_slave_func_group *to_i3c_slave_func_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct i3c_slave_func_group, group);
+}
+
+static inline struct i3c_slave_ctrl_group *to_i3c_slave_ctrl_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct i3c_slave_ctrl_group, group);
+}
+
+static int i3c_slave_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg)
+{
+	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg);
+	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
+	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
+	struct i3c_slave_func *func = func_group->func;
+	int ret;
+
+	ret = i3c_slave_ctrl_add_func(ctrl, func);
+	if (ret)
+		return ret;
+
+	ret = i3c_slave_func_bind(func);
+	if (ret) {
+		i3c_slave_ctrl_remove_func(ctrl, func);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void i3c_slave_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg)
+{
+	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg->ci_parent);
+	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
+	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
+	struct i3c_slave_func *func = func_group->func;
+
+	i3c_slave_func_unbind(func);
+	i3c_slave_ctrl_remove_func(ctrl, func);
+}
+
+static struct configfs_item_operations i3c_slave_ctrl_item_ops = {
+	.allow_link     = i3c_slave_ctrl_func_link,
+	.drop_link      = i3c_slave_ctrl_func_unlink,
+};
+
+static const struct config_item_type i3c_slave_ctrl_type = {
+	.ct_item_ops    = &i3c_slave_ctrl_item_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+/**
+ * i3c_slave_cfs_add_ctrl_group() - add I3C slave controller group
+ * @ctrl: I3C slave controller device
+ *
+ * Return: Pointer to struct config_group
+ */
+struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl)
+{
+	struct i3c_slave_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_slave_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_slave_cfs_add_ctrl_group);
+
+/**
+ * i3c_slave_cfs_remove_ctrl_group() - remove I3C slave controller group
+ * @group: the group to be removed
+ */
+void i3c_slave_cfs_remove_ctrl_group(struct config_group *group)
+{
+	struct i3c_slave_ctrl_group *ctrl_group;
+
+	if (!group)
+		return;
+
+	ctrl_group = container_of(group, struct i3c_slave_ctrl_group, group);
+	i3c_slave_ctrl_put(ctrl_group->ctrl);
+	configfs_unregister_group(&ctrl_group->group);
+	kfree(ctrl_group);
+}
+EXPORT_SYMBOL(i3c_slave_cfs_remove_ctrl_group);
+
+#define I3C_SLAVE_ATTR_R(_name)                                                \
+static ssize_t i3c_slave_func_##_name##_show(struct config_item *item, char *page)    \
+{                                                                              \
+	struct i3c_slave_func *func = to_i3c_slave_func_group(item)->func;                     \
+	return sysfs_emit(page, "0x%04x\n", func->_name);               \
+}
+
+#define I3C_SLAVE_ATTR_W(_name, _u)                                            \
+static ssize_t i3c_slave_func_##_name##_store(struct config_item *item,               \
+				       const char *page, size_t len)           \
+{                                                                              \
+	_u val;                                                               \
+	struct i3c_slave_func *func = to_i3c_slave_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_slave_func_, vendor_id);
+
+I3C_SLAVE_ATTR_R(vendor_info);
+I3C_SLAVE_ATTR_W(vendor_info, u16);
+CONFIGFS_ATTR(i3c_slave_func_, vendor_info);
+
+I3C_SLAVE_ATTR_R(part_id);
+I3C_SLAVE_ATTR_W(part_id, u16);
+CONFIGFS_ATTR(i3c_slave_func_, part_id);
+
+I3C_SLAVE_ATTR_R(instance_id);
+I3C_SLAVE_ATTR_W(instance_id, u8);
+CONFIGFS_ATTR(i3c_slave_func_, instance_id);
+
+I3C_SLAVE_ATTR_R(ext_id);
+I3C_SLAVE_ATTR_W(ext_id, u16);
+CONFIGFS_ATTR(i3c_slave_func_, ext_id);
+
+I3C_SLAVE_ATTR_R(max_write_len);
+I3C_SLAVE_ATTR_W(max_write_len, u16);
+CONFIGFS_ATTR(i3c_slave_func_, max_write_len);
+
+I3C_SLAVE_ATTR_R(max_read_len);
+I3C_SLAVE_ATTR_W(max_read_len, u16);
+CONFIGFS_ATTR(i3c_slave_func_, max_read_len);
+
+I3C_SLAVE_ATTR_R(bcr);
+I3C_SLAVE_ATTR_W(bcr, u8);
+CONFIGFS_ATTR(i3c_slave_func_, bcr);
+
+I3C_SLAVE_ATTR_R(dcr);
+I3C_SLAVE_ATTR_W(dcr, u8);
+CONFIGFS_ATTR(i3c_slave_func_, dcr);
+
+static struct configfs_attribute *i3c_slave_func_attrs[] = {
+	&i3c_slave_func_attr_vendor_id,
+	&i3c_slave_func_attr_vendor_info,
+	&i3c_slave_func_attr_part_id,
+	&i3c_slave_func_attr_instance_id,
+	&i3c_slave_func_attr_ext_id,
+	&i3c_slave_func_attr_max_write_len,
+	&i3c_slave_func_attr_max_read_len,
+	&i3c_slave_func_attr_bcr,
+	&i3c_slave_func_attr_dcr,
+	NULL,
+};
+
+static const struct config_item_type i3c_slave_func_type = {
+	.ct_attrs       = i3c_slave_func_attrs,
+	.ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *i3c_slave_func_make(struct config_group *group, const char *name)
+{
+	struct i3c_slave_func_group *func_group;
+	struct i3c_slave_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_slave_func_type);
+
+	func = i3c_slave_func_create(group->cg_item.ci_name, name);
+	if (IS_ERR(func)) {
+		pr_err("failed to create i3c slave 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_slave_func_drop(struct config_group *group, struct config_item *item)
+{
+	config_item_put(item);
+}
+
+static struct configfs_group_operations i3c_slave_func_group_ops = {
+	.make_group     = &i3c_slave_func_make,
+	.drop_item      = &i3c_slave_func_drop,
+};
+
+static const struct config_item_type i3c_slave_func_group_type = {
+	.ct_group_ops   = &i3c_slave_func_group_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+/**
+ * i3c_slave_cfs_add_func_group() - add I3C slave function group
+ * @name: group name
+ *
+ * Return: Pointer to struct config_group
+ */
+struct config_group *i3c_slave_cfs_add_func_group(const char *name)
+{
+	struct config_group *group;
+
+	group = configfs_register_default_group(functions_group, name,
+						&i3c_slave_func_group_type);
+	if (IS_ERR(group))
+		pr_err("failed to register configfs group for %s function\n",
+		       name);
+
+	return group;
+}
+EXPORT_SYMBOL(i3c_slave_cfs_add_func_group);
+
+/**
+ * i3c_slave_cfs_remove_func_group() - add I3C slave function group
+ * @group: group to be removed
+ */
+void i3c_slave_cfs_remove_func_group(struct config_group *group)
+{
+	if (IS_ERR_OR_NULL(group))
+		return;
+
+	configfs_unregister_default_group(group);
+}
+EXPORT_SYMBOL(i3c_slave_cfs_remove_func_group);
+
+static const struct config_item_type i3c_slave_controllers_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static const struct config_item_type i3c_slave_functions_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static const struct config_item_type i3c_slave_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static struct configfs_subsystem i3c_slave_cfs_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "i3c_slave",
+			.ci_type = &i3c_slave_type,
+		},
+	},
+	.su_mutex = __MUTEX_INITIALIZER(i3c_slave_cfs_subsys.su_mutex),
+};
+
+static int __init i3c_slave_cfs_init(void)
+{
+	int ret;
+	struct config_group *root = &i3c_slave_cfs_subsys.su_group;
+
+	config_group_init(root);
+
+	ret = configfs_register_subsystem(&i3c_slave_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_slave_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_slave_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_slave_cfs_subsys);
+
+err:
+	return ret;
+}
+module_init(i3c_slave_cfs_init);
+
+MODULE_DESCRIPTION("I3C FUNC CONFIGFS");
+MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
+
+
diff --git a/drivers/i3c/slave.c b/drivers/i3c/slave.c
new file mode 100644
index 0000000000000..edd7a2888271b
--- /dev/null
+++ b/drivers/i3c/slave.c
@@ -0,0 +1,441 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * configfs to configure the I3C Slave
+ *
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/i3c/slave.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(func_lock);
+static struct class *i3c_slave_ctrl_class;
+
+static void i3c_slave_func_dev_release(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+
+	kfree(func->name);
+	kfree(func);
+}
+
+static const struct device_type i3c_slave_func_type = {
+	.release        = i3c_slave_func_dev_release,
+};
+
+static int i3c_slave_func_match_driver(struct device *dev, struct device_driver *drv)
+{
+	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
+}
+
+static int i3c_slave_func_device_probe(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
+
+	if (!driver->probe)
+		return -ENODEV;
+
+	func->driver = driver;
+
+	return driver->probe(func);
+}
+
+static void i3c_slave_func_device_remove(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
+
+	if (driver->remove)
+		driver->remove(func);
+	func->driver = NULL;
+}
+
+static const struct bus_type i3c_slave_func_bus_type = {
+	.name = "i3c_slave_func",
+	.probe = i3c_slave_func_device_probe,
+	.remove = i3c_slave_func_device_remove,
+	.match = i3c_slave_func_match_driver,
+};
+
+static void i3c_slave_ctrl_release(struct device *dev)
+{
+	kfree(to_i3c_slave_ctrl(dev));
+}
+
+static void devm_i3c_slave_ctrl_release(struct device *dev, void *res)
+{
+	struct i3c_slave_ctrl *ctrl = *(struct i3c_slave_ctrl **)res;
+
+	i3c_slave_ctrl_destroy(ctrl);
+}
+
+struct i3c_slave_ctrl *
+__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+		      struct module *owner)
+{
+	struct i3c_slave_ctrl **ptr, *ctrl;
+
+	ptr = devres_alloc(devm_i3c_slave_ctrl_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ctrl = __i3c_slave_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_slave_ctrl_match(struct device *dev, void *res, void *match_data)
+{
+	struct i3c_slave_ctrl **ptr = res;
+
+	return *ptr == match_data;
+}
+
+/**
+ * __i3c_slave_ctrl_create() - create a new slave controller device
+ * @dev: device that is creating the new slave controller
+ * @ops: function pointers for performing slave controller  operations
+ * @owner: the owner of the module that creates the slave controller device
+ *
+ * Return: Pointer to struct i3c_slave_ctrl
+ */
+struct i3c_slave_ctrl *
+__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+			struct module *owner)
+{
+	struct i3c_slave_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_slave_ctrl_class;
+	ctrl->dev.parent = dev;
+	ctrl->dev.release = i3c_slave_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_slave_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_slave_ctrl_create);
+
+/**
+ * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device
+ * @dev: device that is creating the new slave controller device
+ * @ops: function pointers for performing slave controller operations
+ * @owner: the owner of the module that creates the slave controller device
+ *
+ * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it
+ * also associates the device with the i3c_slave using devres. On driver detach, release function is
+ * invoked on the devres data, then devres data is freed.
+ */
+void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl)
+{
+	int r;
+
+	r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match,
+			   ctrl);
+	dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy);
+
+
+
+/**
+ * i3c_slave_ctrl_destroy() - destroy the slave controller device
+ * @ctrl: the slave controller device that has to be destroyed
+ *
+ * Invoke to destroy the I3C slave device
+ */
+void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl)
+{
+	i3c_slave_cfs_remove_ctrl_group(ctrl->group);
+	device_unregister(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy);
+
+/**
+ * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller
+ * @ctrl: the controller device to which the slave function should be added
+ * @func: the slave function to be added
+ *
+ * An I3C slave device can have only one functions.
+ */
+int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	if (ctrl->func)
+		return -EBUSY;
+
+	ctrl->func = func;
+	func->ctrl = ctrl;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func);
+
+/**
+ * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller
+ * @ctrl: the controller device to which the slave function should be removed
+ * @func: the slave function to be removed
+ *
+ * An I3C slave device can have only one functions.
+ */
+void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	ctrl->func = NULL;
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func);
+
+
+/**
+ * i3c_slave_ctrl() - get the I3C slave controller
+ * @name: device name of the slave controller
+ *
+ * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the
+ * slave controller
+ */
+struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name)
+{
+	int ret = -EINVAL;
+	struct i3c_slave_ctrl *ctrl;
+	struct device *dev;
+	struct class_dev_iter iter;
+
+	class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL);
+	while ((dev = class_dev_iter_next(&iter))) {
+		if (strcmp(name, dev_name(dev)))
+			continue;
+
+		ctrl = to_i3c_slave_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_slave_ctrl_get);
+
+/**
+ * i3c_slave_ctrl_put() - release the I3C endpoint controller
+ * @slave: slave returned by pci_slave_get()
+ *
+ * release the refcount the caller obtained by invoking i3c_slave_ctrl_get()
+ */
+void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl)
+{
+	if (!ctrl || IS_ERR(ctrl))
+		return;
+
+	module_put(ctrl->ops->owner);
+	put_device(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_put);
+
+/**
+ * i3c_slave_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_slave_func_bind(struct i3c_slave_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_slave_func_bind);
+
+/**
+ * i3c_slave_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_slave_func_unbind(struct i3c_slave_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_slave_func_unbind);
+
+/**
+ * i3c_slave_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_slave_func *i3c_slave_func_create(const char *drv_name, const char *name)
+{
+	struct i3c_slave_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_slave_func_bus_type;
+	dev->type = &i3c_slave_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_slave_func_create);
+
+/**
+ * __i3c_slave_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_slave_func_register_driver(struct i3c_slave_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_slave_func_bus_type;
+	driver->driver.owner = owner;
+
+	ret = driver_register(&driver->driver);
+	if (ret)
+		return ret;
+
+	i3c_slave_cfs_add_func_group(driver->driver.name);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__i3c_slave_func_register_driver);
+
+/**
+ * i3c_slave_func_unregister_driver() - unregister the I3C function driver
+ * @driver: the I3C function driver that has to be unregistered
+ *
+ * Invoke to unregister the I3C function driver.
+ */
+void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *fd)
+{
+	mutex_lock(&func_lock);
+	mutex_unlock(&func_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_func_unregister_driver);
+
+static int __init i3c_slave_init(void)
+{
+	int ret;
+
+	i3c_slave_ctrl_class = class_create("i3c_slave");
+	if (IS_ERR(i3c_slave_ctrl_class)) {
+		pr_err("failed to create i3c slave class --> %ld\n",
+			PTR_ERR(i3c_slave_ctrl_class));
+		return PTR_ERR(i3c_slave_ctrl_class);
+	}
+
+	ret = bus_register(&i3c_slave_func_bus_type);
+	if (ret) {
+		class_destroy(i3c_slave_ctrl_class);
+		pr_err("failed to register i3c slave func bus --> %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+module_init(i3c_slave_init);
+
+static void __exit i3c_slave_exit(void)
+{
+	class_destroy(i3c_slave_ctrl_class);
+	bus_unregister(&i3c_slave_func_bus_type);
+
+}
+module_exit(i3c_slave_exit);
+
diff --git a/include/linux/i3c/slave.h b/include/linux/i3c/slave.h
new file mode 100644
index 0000000000000..a4cbbfc6d6ea9
--- /dev/null
+++ b/include/linux/i3c/slave.h
@@ -0,0 +1,458 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#ifndef I3C_SLAVE_H
+#define I3C_SLAVE_H
+
+#include <linux/device.h>
+
+struct i3c_slave_func;
+struct i3c_slave_ctrl;
+
+/**
+ * struct i3c_slave_func_ops - set of function pointers for performing i3c slave 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_slave_func_ops {
+	int (*bind)(struct i3c_slave_func *func);
+	void (*unbind)(struct i3c_slave_func *func);
+};
+
+/**
+ * struct i3c_slave_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
+ * @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_slave_func_driver {
+	int (*probe)(struct i3c_slave_func *func);
+	void (*remove)(struct i3c_slave_func *func);
+
+	char *name;
+	struct device_driver driver;
+	struct i3c_slave_func_ops *ops;
+	struct module *owner;
+};
+
+/**
+ * struct i3c_slave_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_slave_func_ops
+ * @ctrl: binded I3C controller device
+ * @is_bound: indicates if bind notification to function driver has been invoked
+ * @vednor_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: maxium write length
+ * @max_read_len: maxium read length
+ * @bcr: bus characteristics register (BCR)
+ * @dcr: device characteristics register (DCR)
+ */
+struct i3c_slave_func {
+	struct device dev;
+	char *name;
+	struct i3c_slave_func_driver *driver;
+	struct config_group *group;
+	/* mutex to protect against concurrent access of i3c_slave_func_ops */
+	struct mutex lock;
+	struct i3c_slave_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
+ * @status: transfer status
+ * @actual: how much actually transferred
+ * @ctrl: I3C slave controller associate with this request
+ * @tx: transfer direction, 1: slave to master, 0: master to slave
+ */
+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_slave_ctrl *ctrl;
+	bool tx;
+};
+
+/**
+ * struct i3c_slave_ctrl_features - represents I3C slave controller features.
+ * @tx_fifo_sz: tx hardware fifo size
+ * @rx_fifo_sz: rx hardware fifo size
+ */
+struct i3c_slave_ctrl_features {
+	u32 tx_fifo_sz;
+	u32 rx_fifo_sz;
+};
+
+/**
+ * struct i3c_slave_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: rasie IBI interrupt to master
+ * @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
+ * @get_features: ops to get the features supported by the I3C slave controller
+ * @owner: the module owner containing the ops
+ */
+struct i3c_slave_ctrl_ops {
+	int (*set_config)(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
+	int (*enable)(struct i3c_slave_ctrl *ctrl);
+	int (*disable)(struct i3c_slave_ctrl *ctrl);
+	int (*raise_ibi)(struct i3c_slave_ctrl *ctrl, void *p, u8 size);
+
+	struct i3c_request *(*alloc_request)(struct i3c_slave_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_slave_ctrl *ctrl, bool tx);
+
+	int (*fifo_status)(struct i3c_slave_ctrl *ctrl, bool tx);
+	void (*fifo_flush)(struct i3c_slave_ctrl *ctrl, bool tx);
+
+	const struct i3c_slave_ctrl_features *(*get_features)(struct i3c_slave_ctrl *ctrl);
+	struct module *owner;
+};
+
+/**
+ * struct i3c_slave_ctrl - represents the I3C slave device
+ * @dev: I3C slave device
+ * @ops: function pointers for performing endpoint operations
+ * @func: slave functions present in this controller device
+ * @group: configfs group representing the I3C controller device
+ */
+struct i3c_slave_ctrl {
+	struct device dev;
+	const struct i3c_slave_ctrl_ops *ops;
+	struct i3c_slave_func *func;
+	struct config_group *group;
+};
+
+/**
+ * i3c_slave_ctrl_raise_ibi() - Raise IBI to master
+ * @ctrl: I3C slave 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_slave_ctrl_raise_ibi(struct i3c_slave_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_slave_ctrl_cancel_all_reqs() - Cancel all pending request
+ * @ctrl: I3C slave controller
+ * @tx: Transfer diretion queue
+ * @size: size of optional data
+ */
+static inline void i3c_slave_ctrl_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->cancel_all_reqs)
+		ctrl->ops->cancel_all_reqs(ctrl, tx);
+}
+
+/**
+ * i3c_slave_ctrl_set_config() - Set controller configuration
+ * @ctrl: I3C slave controller device
+ * @func: Function device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->set_config)
+		return ctrl->ops->set_config(ctrl, func);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_slave_ctrl_enable() - Enable I3C controller
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_enable(struct i3c_slave_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->enable)
+		return ctrl->ops->enable(ctrl);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_slave_ctrl_disable() - Disable I3C controller
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_disable(struct i3c_slave_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->disable)
+		return ctrl->ops->disable(ctrl);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_slave_ctrl_alloc_request() - Alloc an I3C transfer
+ * @ctrl: I3C slave 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_slave_ctrl_alloc_request(struct i3c_slave_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_slave_ctrl_free_request() - Free an I3C transfer
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline void
+i3c_slave_ctrl_free_request(struct i3c_request *req)
+{
+	struct i3c_slave_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_slave_ctrl_queue() - Queue an I3C transfer
+ * @ctrl: I3C slave 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 int
+i3c_slave_ctrl_queue(struct i3c_request *req, gfp_t gfp_flags)
+{
+	struct i3c_slave_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_slave_ctrl_dequeue() - Dequeue an I3C transfer
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_dequeue(struct i3c_request *req)
+{
+	struct i3c_slave_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_slave_ctrl_fifo_status() - Get controller FIFO status
+ * @ctrl: I3C slave controller device
+ * @tx: 1: Slave to master, 0: master to slave
+ *
+ * Returns: How much data in FIFO
+ */
+static inline int
+i3c_slave_ctrl_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->fifo_status)
+		return ctrl->ops->fifo_status(ctrl, tx);
+
+	return 0;
+}
+
+/**
+ * i3c_slave_ctrl_fifo_flush() - Flush controller FIFO
+ * @ctrl: I3C slave controller device
+ * @tx: 1: Slave to master, 0: master to slave
+ *
+ */
+static inline void
+i3c_slave_ctrl_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->fifo_flush)
+		return ctrl->ops->fifo_flush(ctrl, tx);
+}
+
+/**
+ * i3c_slave_ctrl_get_features() - Get controller supported features
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: The pointer to struct i3c_slave_ctrl_features
+ */
+static inline const struct i3c_slave_ctrl_features*
+i3c_slave_ctrl_get_features(struct i3c_slave_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->get_features)
+		return ctrl->ops->get_features(ctrl);
+
+	return NULL;
+}
+
+#define to_i3c_slave_ctrl(device) container_of((device), struct i3c_slave_ctrl, dev)
+
+#define to_i3c_slave_func(func_dev) container_of((func_dev), struct i3c_slave_func, dev)
+#define to_i3c_slave_func_driver(drv) (container_of((drv), struct i3c_slave_func_driver, driver))
+
+#define i3c_slave_ctrl_create(dev, ops) \
+		__i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
+#define devm_i3c_slave_ctrl_create(dev, ops) \
+		__devm_i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
+
+struct i3c_slave_ctrl *
+__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+		      struct module *owner);
+struct i3c_slave_ctrl *
+__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+		 struct module *owner);
+
+void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *epc);
+void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *epc);
+
+
+int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
+void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
+
+struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl);
+
+
+void i3c_slave_cfs_remove_ctrl_group(struct config_group *group);
+struct config_group *i3c_slave_cfs_add_func_group(const char *name);
+void i3c_slave_cfs_remove_func_group(struct config_group *group);
+struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name);
+void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl);
+
+int i3c_slave_func_bind(struct i3c_slave_func *func);
+void i3c_slave_func_unbind(struct i3c_slave_func *func);
+struct i3c_slave_func *i3c_slave_func_create(const char *drv_name, const char *name);
+
+#define i3c_slave_func_register_driver(drv) \
+	__i3c_slave_func_register_driver(drv, THIS_MODULE)
+
+int __i3c_slave_func_register_driver(struct i3c_slave_func_driver *drv, struct module *owner);
+void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *drv);
+
+#define DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops)			\
+	static struct i3c_slave_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_SLAVE_INIT(_name, _probe, _remove, _ops)			\
+	DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops);			\
+	static int __init _name ## mod_init(void)				\
+	{									\
+		return i3c_slave_func_register_driver(&_name ## i3c_func);	\
+	}									\
+	static void __exit _name ## mod_exit(void)				\
+	{									\
+		i3c_slave_func_unregister_driver(&_name ## i3c_func);		\
+	}									\
+	module_init(_name ## mod_init);						\
+	module_exit(_name ## mod_exit)
+
+#endif
+
+
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 5/9] i3c: add slave mode support
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

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

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

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/i3c/Kconfig       |  29 +++
 drivers/i3c/Makefile      |   2 +
 drivers/i3c/i3c-cfs.c     | 361 ++++++++++++++++++++++++++++++
 drivers/i3c/slave.c       | 441 ++++++++++++++++++++++++++++++++++++
 include/linux/i3c/slave.h | 458 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 1291 insertions(+)
 create mode 100644 drivers/i3c/i3c-cfs.c
 create mode 100644 drivers/i3c/slave.c
 create mode 100644 include/linux/i3c/slave.h

diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index 30a441506f61c..bdc173bc0da12 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -22,3 +22,32 @@ menuconfig I3C
 if I3C
 source "drivers/i3c/master/Kconfig"
 endif # I3C
+
+config I3C_SLAVE
+	bool "I3C Slave Support"
+	help
+	  Support I3C Slave Mode.
+
+	  Enable this configuration option to support configurable I3C slave.
+	  This should be enabled if the platform has a I3C controller that can
+	  operate in slave mode.
+
+	  Enabling this option will build the I3C slave library, which includes
+	  slave controller library and slave function library.
+
+	  If in doubt, say "N" to disable slave support.
+
+config I3C_SLAVE_CONFIGFS
+	bool "I3C Slave Configfs Support"
+	depends on I3C_SLAVE
+	select CONFIGFS_FS
+	help
+	  Configfs entry for slave function and controller.
+
+	  This will enable the configfs entry that can be used to configure
+	  the slave function and used to bind the function with a slave
+	  controller.
+
+if I3C_SLAVE
+source "drivers/i3c/slave/Kconfig"
+endif #I#C_SLAVE
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 11982efbc6d91..6407ddec3a4a9 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -2,3 +2,5 @@
 i3c-y				:= device.o master.o
 obj-$(CONFIG_I3C)		+= i3c.o
 obj-$(CONFIG_I3C)		+= master/
+obj-$(CONFIG_I3C_SLAVE)		+= slave.o
+obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
diff --git a/drivers/i3c/i3c-cfs.c b/drivers/i3c/i3c-cfs.c
new file mode 100644
index 0000000000000..1f53fada43645
--- /dev/null
+++ b/drivers/i3c/i3c-cfs.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Configfs to configure the I3C Slave
+ *
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/i3c/slave.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(functions_mutex);
+static struct config_group *functions_group;
+static struct config_group *controllers_group;
+
+struct i3c_slave_func_group {
+	struct config_group group;
+	struct i3c_slave_func *func;
+};
+
+struct i3c_slave_ctrl_group {
+	struct config_group group;
+	struct i3c_slave_ctrl *ctrl;
+};
+
+static inline struct i3c_slave_func_group *to_i3c_slave_func_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct i3c_slave_func_group, group);
+}
+
+static inline struct i3c_slave_ctrl_group *to_i3c_slave_ctrl_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct i3c_slave_ctrl_group, group);
+}
+
+static int i3c_slave_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg)
+{
+	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg);
+	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
+	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
+	struct i3c_slave_func *func = func_group->func;
+	int ret;
+
+	ret = i3c_slave_ctrl_add_func(ctrl, func);
+	if (ret)
+		return ret;
+
+	ret = i3c_slave_func_bind(func);
+	if (ret) {
+		i3c_slave_ctrl_remove_func(ctrl, func);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void i3c_slave_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg)
+{
+	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg->ci_parent);
+	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
+	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
+	struct i3c_slave_func *func = func_group->func;
+
+	i3c_slave_func_unbind(func);
+	i3c_slave_ctrl_remove_func(ctrl, func);
+}
+
+static struct configfs_item_operations i3c_slave_ctrl_item_ops = {
+	.allow_link     = i3c_slave_ctrl_func_link,
+	.drop_link      = i3c_slave_ctrl_func_unlink,
+};
+
+static const struct config_item_type i3c_slave_ctrl_type = {
+	.ct_item_ops    = &i3c_slave_ctrl_item_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+/**
+ * i3c_slave_cfs_add_ctrl_group() - add I3C slave controller group
+ * @ctrl: I3C slave controller device
+ *
+ * Return: Pointer to struct config_group
+ */
+struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl)
+{
+	struct i3c_slave_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_slave_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_slave_cfs_add_ctrl_group);
+
+/**
+ * i3c_slave_cfs_remove_ctrl_group() - remove I3C slave controller group
+ * @group: the group to be removed
+ */
+void i3c_slave_cfs_remove_ctrl_group(struct config_group *group)
+{
+	struct i3c_slave_ctrl_group *ctrl_group;
+
+	if (!group)
+		return;
+
+	ctrl_group = container_of(group, struct i3c_slave_ctrl_group, group);
+	i3c_slave_ctrl_put(ctrl_group->ctrl);
+	configfs_unregister_group(&ctrl_group->group);
+	kfree(ctrl_group);
+}
+EXPORT_SYMBOL(i3c_slave_cfs_remove_ctrl_group);
+
+#define I3C_SLAVE_ATTR_R(_name)                                                \
+static ssize_t i3c_slave_func_##_name##_show(struct config_item *item, char *page)    \
+{                                                                              \
+	struct i3c_slave_func *func = to_i3c_slave_func_group(item)->func;                     \
+	return sysfs_emit(page, "0x%04x\n", func->_name);               \
+}
+
+#define I3C_SLAVE_ATTR_W(_name, _u)                                            \
+static ssize_t i3c_slave_func_##_name##_store(struct config_item *item,               \
+				       const char *page, size_t len)           \
+{                                                                              \
+	_u val;                                                               \
+	struct i3c_slave_func *func = to_i3c_slave_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_slave_func_, vendor_id);
+
+I3C_SLAVE_ATTR_R(vendor_info);
+I3C_SLAVE_ATTR_W(vendor_info, u16);
+CONFIGFS_ATTR(i3c_slave_func_, vendor_info);
+
+I3C_SLAVE_ATTR_R(part_id);
+I3C_SLAVE_ATTR_W(part_id, u16);
+CONFIGFS_ATTR(i3c_slave_func_, part_id);
+
+I3C_SLAVE_ATTR_R(instance_id);
+I3C_SLAVE_ATTR_W(instance_id, u8);
+CONFIGFS_ATTR(i3c_slave_func_, instance_id);
+
+I3C_SLAVE_ATTR_R(ext_id);
+I3C_SLAVE_ATTR_W(ext_id, u16);
+CONFIGFS_ATTR(i3c_slave_func_, ext_id);
+
+I3C_SLAVE_ATTR_R(max_write_len);
+I3C_SLAVE_ATTR_W(max_write_len, u16);
+CONFIGFS_ATTR(i3c_slave_func_, max_write_len);
+
+I3C_SLAVE_ATTR_R(max_read_len);
+I3C_SLAVE_ATTR_W(max_read_len, u16);
+CONFIGFS_ATTR(i3c_slave_func_, max_read_len);
+
+I3C_SLAVE_ATTR_R(bcr);
+I3C_SLAVE_ATTR_W(bcr, u8);
+CONFIGFS_ATTR(i3c_slave_func_, bcr);
+
+I3C_SLAVE_ATTR_R(dcr);
+I3C_SLAVE_ATTR_W(dcr, u8);
+CONFIGFS_ATTR(i3c_slave_func_, dcr);
+
+static struct configfs_attribute *i3c_slave_func_attrs[] = {
+	&i3c_slave_func_attr_vendor_id,
+	&i3c_slave_func_attr_vendor_info,
+	&i3c_slave_func_attr_part_id,
+	&i3c_slave_func_attr_instance_id,
+	&i3c_slave_func_attr_ext_id,
+	&i3c_slave_func_attr_max_write_len,
+	&i3c_slave_func_attr_max_read_len,
+	&i3c_slave_func_attr_bcr,
+	&i3c_slave_func_attr_dcr,
+	NULL,
+};
+
+static const struct config_item_type i3c_slave_func_type = {
+	.ct_attrs       = i3c_slave_func_attrs,
+	.ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *i3c_slave_func_make(struct config_group *group, const char *name)
+{
+	struct i3c_slave_func_group *func_group;
+	struct i3c_slave_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_slave_func_type);
+
+	func = i3c_slave_func_create(group->cg_item.ci_name, name);
+	if (IS_ERR(func)) {
+		pr_err("failed to create i3c slave 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_slave_func_drop(struct config_group *group, struct config_item *item)
+{
+	config_item_put(item);
+}
+
+static struct configfs_group_operations i3c_slave_func_group_ops = {
+	.make_group     = &i3c_slave_func_make,
+	.drop_item      = &i3c_slave_func_drop,
+};
+
+static const struct config_item_type i3c_slave_func_group_type = {
+	.ct_group_ops   = &i3c_slave_func_group_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+/**
+ * i3c_slave_cfs_add_func_group() - add I3C slave function group
+ * @name: group name
+ *
+ * Return: Pointer to struct config_group
+ */
+struct config_group *i3c_slave_cfs_add_func_group(const char *name)
+{
+	struct config_group *group;
+
+	group = configfs_register_default_group(functions_group, name,
+						&i3c_slave_func_group_type);
+	if (IS_ERR(group))
+		pr_err("failed to register configfs group for %s function\n",
+		       name);
+
+	return group;
+}
+EXPORT_SYMBOL(i3c_slave_cfs_add_func_group);
+
+/**
+ * i3c_slave_cfs_remove_func_group() - add I3C slave function group
+ * @group: group to be removed
+ */
+void i3c_slave_cfs_remove_func_group(struct config_group *group)
+{
+	if (IS_ERR_OR_NULL(group))
+		return;
+
+	configfs_unregister_default_group(group);
+}
+EXPORT_SYMBOL(i3c_slave_cfs_remove_func_group);
+
+static const struct config_item_type i3c_slave_controllers_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static const struct config_item_type i3c_slave_functions_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static const struct config_item_type i3c_slave_type = {
+	.ct_owner       = THIS_MODULE,
+};
+
+static struct configfs_subsystem i3c_slave_cfs_subsys = {
+	.su_group = {
+		.cg_item = {
+			.ci_namebuf = "i3c_slave",
+			.ci_type = &i3c_slave_type,
+		},
+	},
+	.su_mutex = __MUTEX_INITIALIZER(i3c_slave_cfs_subsys.su_mutex),
+};
+
+static int __init i3c_slave_cfs_init(void)
+{
+	int ret;
+	struct config_group *root = &i3c_slave_cfs_subsys.su_group;
+
+	config_group_init(root);
+
+	ret = configfs_register_subsystem(&i3c_slave_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_slave_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_slave_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_slave_cfs_subsys);
+
+err:
+	return ret;
+}
+module_init(i3c_slave_cfs_init);
+
+MODULE_DESCRIPTION("I3C FUNC CONFIGFS");
+MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
+
+
diff --git a/drivers/i3c/slave.c b/drivers/i3c/slave.c
new file mode 100644
index 0000000000000..edd7a2888271b
--- /dev/null
+++ b/drivers/i3c/slave.c
@@ -0,0 +1,441 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * configfs to configure the I3C Slave
+ *
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/i3c/slave.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(func_lock);
+static struct class *i3c_slave_ctrl_class;
+
+static void i3c_slave_func_dev_release(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+
+	kfree(func->name);
+	kfree(func);
+}
+
+static const struct device_type i3c_slave_func_type = {
+	.release        = i3c_slave_func_dev_release,
+};
+
+static int i3c_slave_func_match_driver(struct device *dev, struct device_driver *drv)
+{
+	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
+}
+
+static int i3c_slave_func_device_probe(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
+
+	if (!driver->probe)
+		return -ENODEV;
+
+	func->driver = driver;
+
+	return driver->probe(func);
+}
+
+static void i3c_slave_func_device_remove(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
+
+	if (driver->remove)
+		driver->remove(func);
+	func->driver = NULL;
+}
+
+static const struct bus_type i3c_slave_func_bus_type = {
+	.name = "i3c_slave_func",
+	.probe = i3c_slave_func_device_probe,
+	.remove = i3c_slave_func_device_remove,
+	.match = i3c_slave_func_match_driver,
+};
+
+static void i3c_slave_ctrl_release(struct device *dev)
+{
+	kfree(to_i3c_slave_ctrl(dev));
+}
+
+static void devm_i3c_slave_ctrl_release(struct device *dev, void *res)
+{
+	struct i3c_slave_ctrl *ctrl = *(struct i3c_slave_ctrl **)res;
+
+	i3c_slave_ctrl_destroy(ctrl);
+}
+
+struct i3c_slave_ctrl *
+__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+		      struct module *owner)
+{
+	struct i3c_slave_ctrl **ptr, *ctrl;
+
+	ptr = devres_alloc(devm_i3c_slave_ctrl_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ctrl = __i3c_slave_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_slave_ctrl_match(struct device *dev, void *res, void *match_data)
+{
+	struct i3c_slave_ctrl **ptr = res;
+
+	return *ptr == match_data;
+}
+
+/**
+ * __i3c_slave_ctrl_create() - create a new slave controller device
+ * @dev: device that is creating the new slave controller
+ * @ops: function pointers for performing slave controller  operations
+ * @owner: the owner of the module that creates the slave controller device
+ *
+ * Return: Pointer to struct i3c_slave_ctrl
+ */
+struct i3c_slave_ctrl *
+__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+			struct module *owner)
+{
+	struct i3c_slave_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_slave_ctrl_class;
+	ctrl->dev.parent = dev;
+	ctrl->dev.release = i3c_slave_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_slave_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_slave_ctrl_create);
+
+/**
+ * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device
+ * @dev: device that is creating the new slave controller device
+ * @ops: function pointers for performing slave controller operations
+ * @owner: the owner of the module that creates the slave controller device
+ *
+ * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it
+ * also associates the device with the i3c_slave using devres. On driver detach, release function is
+ * invoked on the devres data, then devres data is freed.
+ */
+void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl)
+{
+	int r;
+
+	r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match,
+			   ctrl);
+	dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy);
+
+
+
+/**
+ * i3c_slave_ctrl_destroy() - destroy the slave controller device
+ * @ctrl: the slave controller device that has to be destroyed
+ *
+ * Invoke to destroy the I3C slave device
+ */
+void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl)
+{
+	i3c_slave_cfs_remove_ctrl_group(ctrl->group);
+	device_unregister(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy);
+
+/**
+ * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller
+ * @ctrl: the controller device to which the slave function should be added
+ * @func: the slave function to be added
+ *
+ * An I3C slave device can have only one functions.
+ */
+int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	if (ctrl->func)
+		return -EBUSY;
+
+	ctrl->func = func;
+	func->ctrl = ctrl;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func);
+
+/**
+ * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller
+ * @ctrl: the controller device to which the slave function should be removed
+ * @func: the slave function to be removed
+ *
+ * An I3C slave device can have only one functions.
+ */
+void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	ctrl->func = NULL;
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func);
+
+
+/**
+ * i3c_slave_ctrl() - get the I3C slave controller
+ * @name: device name of the slave controller
+ *
+ * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the
+ * slave controller
+ */
+struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name)
+{
+	int ret = -EINVAL;
+	struct i3c_slave_ctrl *ctrl;
+	struct device *dev;
+	struct class_dev_iter iter;
+
+	class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL);
+	while ((dev = class_dev_iter_next(&iter))) {
+		if (strcmp(name, dev_name(dev)))
+			continue;
+
+		ctrl = to_i3c_slave_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_slave_ctrl_get);
+
+/**
+ * i3c_slave_ctrl_put() - release the I3C endpoint controller
+ * @slave: slave returned by pci_slave_get()
+ *
+ * release the refcount the caller obtained by invoking i3c_slave_ctrl_get()
+ */
+void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl)
+{
+	if (!ctrl || IS_ERR(ctrl))
+		return;
+
+	module_put(ctrl->ops->owner);
+	put_device(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_put);
+
+/**
+ * i3c_slave_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_slave_func_bind(struct i3c_slave_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_slave_func_bind);
+
+/**
+ * i3c_slave_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_slave_func_unbind(struct i3c_slave_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_slave_func_unbind);
+
+/**
+ * i3c_slave_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_slave_func *i3c_slave_func_create(const char *drv_name, const char *name)
+{
+	struct i3c_slave_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_slave_func_bus_type;
+	dev->type = &i3c_slave_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_slave_func_create);
+
+/**
+ * __i3c_slave_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_slave_func_register_driver(struct i3c_slave_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_slave_func_bus_type;
+	driver->driver.owner = owner;
+
+	ret = driver_register(&driver->driver);
+	if (ret)
+		return ret;
+
+	i3c_slave_cfs_add_func_group(driver->driver.name);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__i3c_slave_func_register_driver);
+
+/**
+ * i3c_slave_func_unregister_driver() - unregister the I3C function driver
+ * @driver: the I3C function driver that has to be unregistered
+ *
+ * Invoke to unregister the I3C function driver.
+ */
+void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *fd)
+{
+	mutex_lock(&func_lock);
+	mutex_unlock(&func_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_func_unregister_driver);
+
+static int __init i3c_slave_init(void)
+{
+	int ret;
+
+	i3c_slave_ctrl_class = class_create("i3c_slave");
+	if (IS_ERR(i3c_slave_ctrl_class)) {
+		pr_err("failed to create i3c slave class --> %ld\n",
+			PTR_ERR(i3c_slave_ctrl_class));
+		return PTR_ERR(i3c_slave_ctrl_class);
+	}
+
+	ret = bus_register(&i3c_slave_func_bus_type);
+	if (ret) {
+		class_destroy(i3c_slave_ctrl_class);
+		pr_err("failed to register i3c slave func bus --> %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+module_init(i3c_slave_init);
+
+static void __exit i3c_slave_exit(void)
+{
+	class_destroy(i3c_slave_ctrl_class);
+	bus_unregister(&i3c_slave_func_bus_type);
+
+}
+module_exit(i3c_slave_exit);
+
diff --git a/include/linux/i3c/slave.h b/include/linux/i3c/slave.h
new file mode 100644
index 0000000000000..a4cbbfc6d6ea9
--- /dev/null
+++ b/include/linux/i3c/slave.h
@@ -0,0 +1,458 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#ifndef I3C_SLAVE_H
+#define I3C_SLAVE_H
+
+#include <linux/device.h>
+
+struct i3c_slave_func;
+struct i3c_slave_ctrl;
+
+/**
+ * struct i3c_slave_func_ops - set of function pointers for performing i3c slave 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_slave_func_ops {
+	int (*bind)(struct i3c_slave_func *func);
+	void (*unbind)(struct i3c_slave_func *func);
+};
+
+/**
+ * struct i3c_slave_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
+ * @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_slave_func_driver {
+	int (*probe)(struct i3c_slave_func *func);
+	void (*remove)(struct i3c_slave_func *func);
+
+	char *name;
+	struct device_driver driver;
+	struct i3c_slave_func_ops *ops;
+	struct module *owner;
+};
+
+/**
+ * struct i3c_slave_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_slave_func_ops
+ * @ctrl: binded I3C controller device
+ * @is_bound: indicates if bind notification to function driver has been invoked
+ * @vednor_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: maxium write length
+ * @max_read_len: maxium read length
+ * @bcr: bus characteristics register (BCR)
+ * @dcr: device characteristics register (DCR)
+ */
+struct i3c_slave_func {
+	struct device dev;
+	char *name;
+	struct i3c_slave_func_driver *driver;
+	struct config_group *group;
+	/* mutex to protect against concurrent access of i3c_slave_func_ops */
+	struct mutex lock;
+	struct i3c_slave_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
+ * @status: transfer status
+ * @actual: how much actually transferred
+ * @ctrl: I3C slave controller associate with this request
+ * @tx: transfer direction, 1: slave to master, 0: master to slave
+ */
+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_slave_ctrl *ctrl;
+	bool tx;
+};
+
+/**
+ * struct i3c_slave_ctrl_features - represents I3C slave controller features.
+ * @tx_fifo_sz: tx hardware fifo size
+ * @rx_fifo_sz: rx hardware fifo size
+ */
+struct i3c_slave_ctrl_features {
+	u32 tx_fifo_sz;
+	u32 rx_fifo_sz;
+};
+
+/**
+ * struct i3c_slave_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: rasie IBI interrupt to master
+ * @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
+ * @get_features: ops to get the features supported by the I3C slave controller
+ * @owner: the module owner containing the ops
+ */
+struct i3c_slave_ctrl_ops {
+	int (*set_config)(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
+	int (*enable)(struct i3c_slave_ctrl *ctrl);
+	int (*disable)(struct i3c_slave_ctrl *ctrl);
+	int (*raise_ibi)(struct i3c_slave_ctrl *ctrl, void *p, u8 size);
+
+	struct i3c_request *(*alloc_request)(struct i3c_slave_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_slave_ctrl *ctrl, bool tx);
+
+	int (*fifo_status)(struct i3c_slave_ctrl *ctrl, bool tx);
+	void (*fifo_flush)(struct i3c_slave_ctrl *ctrl, bool tx);
+
+	const struct i3c_slave_ctrl_features *(*get_features)(struct i3c_slave_ctrl *ctrl);
+	struct module *owner;
+};
+
+/**
+ * struct i3c_slave_ctrl - represents the I3C slave device
+ * @dev: I3C slave device
+ * @ops: function pointers for performing endpoint operations
+ * @func: slave functions present in this controller device
+ * @group: configfs group representing the I3C controller device
+ */
+struct i3c_slave_ctrl {
+	struct device dev;
+	const struct i3c_slave_ctrl_ops *ops;
+	struct i3c_slave_func *func;
+	struct config_group *group;
+};
+
+/**
+ * i3c_slave_ctrl_raise_ibi() - Raise IBI to master
+ * @ctrl: I3C slave 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_slave_ctrl_raise_ibi(struct i3c_slave_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_slave_ctrl_cancel_all_reqs() - Cancel all pending request
+ * @ctrl: I3C slave controller
+ * @tx: Transfer diretion queue
+ * @size: size of optional data
+ */
+static inline void i3c_slave_ctrl_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->cancel_all_reqs)
+		ctrl->ops->cancel_all_reqs(ctrl, tx);
+}
+
+/**
+ * i3c_slave_ctrl_set_config() - Set controller configuration
+ * @ctrl: I3C slave controller device
+ * @func: Function device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->set_config)
+		return ctrl->ops->set_config(ctrl, func);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_slave_ctrl_enable() - Enable I3C controller
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_enable(struct i3c_slave_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->enable)
+		return ctrl->ops->enable(ctrl);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_slave_ctrl_disable() - Disable I3C controller
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_disable(struct i3c_slave_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->disable)
+		return ctrl->ops->disable(ctrl);
+
+	return -EINVAL;
+}
+
+/**
+ * i3c_slave_ctrl_alloc_request() - Alloc an I3C transfer
+ * @ctrl: I3C slave 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_slave_ctrl_alloc_request(struct i3c_slave_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_slave_ctrl_free_request() - Free an I3C transfer
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline void
+i3c_slave_ctrl_free_request(struct i3c_request *req)
+{
+	struct i3c_slave_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_slave_ctrl_queue() - Queue an I3C transfer
+ * @ctrl: I3C slave 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 int
+i3c_slave_ctrl_queue(struct i3c_request *req, gfp_t gfp_flags)
+{
+	struct i3c_slave_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_slave_ctrl_dequeue() - Dequeue an I3C transfer
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_dequeue(struct i3c_request *req)
+{
+	struct i3c_slave_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_slave_ctrl_fifo_status() - Get controller FIFO status
+ * @ctrl: I3C slave controller device
+ * @tx: 1: Slave to master, 0: master to slave
+ *
+ * Returns: How much data in FIFO
+ */
+static inline int
+i3c_slave_ctrl_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->fifo_status)
+		return ctrl->ops->fifo_status(ctrl, tx);
+
+	return 0;
+}
+
+/**
+ * i3c_slave_ctrl_fifo_flush() - Flush controller FIFO
+ * @ctrl: I3C slave controller device
+ * @tx: 1: Slave to master, 0: master to slave
+ *
+ */
+static inline void
+i3c_slave_ctrl_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->fifo_flush)
+		return ctrl->ops->fifo_flush(ctrl, tx);
+}
+
+/**
+ * i3c_slave_ctrl_get_features() - Get controller supported features
+ * @ctrl: I3C slave controller device
+ *
+ * Returns: The pointer to struct i3c_slave_ctrl_features
+ */
+static inline const struct i3c_slave_ctrl_features*
+i3c_slave_ctrl_get_features(struct i3c_slave_ctrl *ctrl)
+{
+	if (ctrl && ctrl->ops && ctrl->ops->get_features)
+		return ctrl->ops->get_features(ctrl);
+
+	return NULL;
+}
+
+#define to_i3c_slave_ctrl(device) container_of((device), struct i3c_slave_ctrl, dev)
+
+#define to_i3c_slave_func(func_dev) container_of((func_dev), struct i3c_slave_func, dev)
+#define to_i3c_slave_func_driver(drv) (container_of((drv), struct i3c_slave_func_driver, driver))
+
+#define i3c_slave_ctrl_create(dev, ops) \
+		__i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
+#define devm_i3c_slave_ctrl_create(dev, ops) \
+		__devm_i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
+
+struct i3c_slave_ctrl *
+__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+		      struct module *owner);
+struct i3c_slave_ctrl *
+__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+		 struct module *owner);
+
+void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *epc);
+void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *epc);
+
+
+int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
+void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
+
+struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl);
+
+
+void i3c_slave_cfs_remove_ctrl_group(struct config_group *group);
+struct config_group *i3c_slave_cfs_add_func_group(const char *name);
+void i3c_slave_cfs_remove_func_group(struct config_group *group);
+struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name);
+void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl);
+
+int i3c_slave_func_bind(struct i3c_slave_func *func);
+void i3c_slave_func_unbind(struct i3c_slave_func *func);
+struct i3c_slave_func *i3c_slave_func_create(const char *drv_name, const char *name);
+
+#define i3c_slave_func_register_driver(drv) \
+	__i3c_slave_func_register_driver(drv, THIS_MODULE)
+
+int __i3c_slave_func_register_driver(struct i3c_slave_func_driver *drv, struct module *owner);
+void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *drv);
+
+#define DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops)			\
+	static struct i3c_slave_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_SLAVE_INIT(_name, _probe, _remove, _ops)			\
+	DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops);			\
+	static int __init _name ## mod_init(void)				\
+	{									\
+		return i3c_slave_func_register_driver(&_name ## i3c_func);	\
+	}									\
+	static void __exit _name ## mod_exit(void)				\
+	{									\
+		i3c_slave_func_unregister_driver(&_name ## i3c_func);		\
+	}									\
+	module_init(_name ## mod_init);						\
+	module_exit(_name ## mod_exit)
+
+#endif
+
+
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 6/9] i3c: slave: add svc slave controller support
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

Add Silvaco I3C slave controller support

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/i3c/Makefile              |   1 +
 drivers/i3c/slave/Kconfig         |   9 +
 drivers/i3c/slave/Makefile        |   4 +
 drivers/i3c/slave/svc-i3c-slave.c | 600 ++++++++++++++++++++++++++++++
 4 files changed, 614 insertions(+)
 create mode 100644 drivers/i3c/slave/Kconfig
 create mode 100644 drivers/i3c/slave/Makefile
 create mode 100644 drivers/i3c/slave/svc-i3c-slave.c

diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 6407ddec3a4a9..ef1acbe13fe60 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_I3C)		+= i3c.o
 obj-$(CONFIG_I3C)		+= master/
 obj-$(CONFIG_I3C_SLAVE)		+= slave.o
 obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
+obj-$(CONFIG_I3C_SLAVE)		+= slave/
diff --git a/drivers/i3c/slave/Kconfig b/drivers/i3c/slave/Kconfig
new file mode 100644
index 0000000000000..e385dbdea193b
--- /dev/null
+++ b/drivers/i3c/slave/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config I3C_SLAVE_CTRL_SVC
+	tristate "Silvaco I3C Dual-Role Slave driver"
+	depends on I3C
+	depends on HAS_IOMEM
+	depends on !(ALPHA || PARISC)
+	help
+	  Support for Silvaco I3C Dual-Role Slave Controller.
diff --git a/drivers/i3c/slave/Makefile b/drivers/i3c/slave/Makefile
new file mode 100644
index 0000000000000..612be24536311
--- /dev/null
+++ b/drivers/i3c/slave/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-${CONFIG_I3C_SLAVE_CTRL_SVC} += svc-i3c-slave.o
+
diff --git a/drivers/i3c/slave/svc-i3c-slave.c b/drivers/i3c/slave/svc-i3c-slave.c
new file mode 100644
index 0000000000000..01824913bfbc4
--- /dev/null
+++ b/drivers/i3c/slave/svc-i3c-slave.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/i3c/slave.h>
+#include <linux/i3c/slave.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/i3c/device.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+
+enum i3c_clks {
+	PCLK,
+	FCLK,
+	SCLK,
+	MAXCLK,
+};
+
+struct svc_i3c_slave {
+	struct device *dev;
+	void __iomem *regs;
+	int irq;
+	struct clk_bulk_data clks[MAXCLK];
+
+	struct list_head txq;
+	spinlock_t txq_lock;
+	struct list_head rxq;
+	spinlock_t rxq_lock;
+	struct list_head cq;
+	spinlock_t cq_lock;
+
+	struct work_struct work;
+	struct workqueue_struct *workqueue;
+
+	struct i3c_slave_ctrl_features features;
+};
+
+#define I3C_SCONFIG	0x4
+#define   I3C_SCONFIG_SLVENA_MASK	BIT(0)
+#define   I3C_SCONFIG_SADDR_MASK	GENMASK(31, 25)
+
+#define I3C_SSTATUS	0x8
+#define	  I3C_SSTATUS_STOP_MASK		BIT(10)
+#define	  I3C_SSTATUS_RX_PEND_MASK	BIT(11)
+#define   I3C_SSTATUS_TXNOTFULL_MASK	BIT(12)
+
+#define I3C_SCTRL	0xc
+#define   I3C_SCTRL_EVENT_MASK		GENMASK(1, 0)
+#define	  I3C_SCTRL_EVENT_IBI		0x1
+#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_FLUSHFB_MASK	BIT(1)
+#define   I3C_SDATACTRL_FLUSHTB_MASK	BIT(0)
+
+#define I3C_SWDATAB	0x30
+#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_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_slave_enable(struct i3c_slave_ctrl *ctrl)
+{
+	struct svc_i3c_slave *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_slave_disable(struct i3c_slave_ctrl *ctrl)
+{
+	struct svc_i3c_slave *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_slave_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	struct svc_i3c_slave *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;
+}
+
+const struct i3c_slave_ctrl_features *svc_i3c_get_features(struct i3c_slave_ctrl *ctrl)
+{
+	struct svc_i3c_slave *svc;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	if (!svc)
+		return NULL;
+
+	return &svc->features;
+}
+
+static int svc_i3c_slave_queue(struct i3c_request *req, gfp_t)
+{
+	struct svc_i3c_slave *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)
+		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_slave *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_slave_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	struct svc_i3c_slave *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_slave_raise_ibi(struct i3c_slave_ctrl *ctrl, void *p, u8 size)
+{
+	struct svc_i3c_slave *svc;
+	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;
+
+	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");
+		return -ENAVAIL;
+	}
+
+	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);
+
+	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 IBI finish\n");
+		return -ENAVAIL;
+	}
+
+	return 0;
+}
+
+static void svc_i3c_slave_complete(struct work_struct *work)
+{
+	struct svc_i3c_slave *svc = container_of(work, struct svc_i3c_slave, 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_slave_irq_handler(int irq, void *dev_id)
+{
+	struct i3c_request *req, *complete = NULL;
+	struct svc_i3c_slave *svc = dev_id;
+	unsigned long flags;
+	u32 statusFlags;
+
+	statusFlags = readl(svc->regs + I3C_SSTATUS);
+	writel(statusFlags, svc->regs + I3C_SSTATUS);
+
+	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) {
+
+		complete = NULL;
+		spin_lock_irqsave(&svc->txq_lock, flags);
+		req = list_first_entry_or_null(&svc->txq, struct i3c_request, list);
+		if (!req) {
+			writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTCLR);
+		} else {
+			while (!(readl_relaxed(svc->regs + I3C_SDATACTRL)
+					& I3C_SDATACTRL_TXFULL_MASK)) {
+
+				if (req->actual + 1 == req->length)
+					writel_relaxed(*(u8 *)(req->buf + req->actual),
+						       svc->regs + I3C_SWDATAE);
+				else
+					writel_relaxed(*(u8 *)(req->buf + req->actual),
+						       svc->regs + I3C_SWDATAB);
+
+				req->actual++;
+
+				if (req->actual == req->length) {
+					list_del(&req->list);
+					complete = req;
+					break;
+				}
+			}
+		}
+		spin_unlock_irqrestore(&svc->txq_lock, flags);
+		if (complete)
+			complete->complete(complete);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void svc_i3c_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	struct svc_i3c_slave *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 struct i3c_slave_ctrl_ops svc_i3c_slave_ops = {
+	.set_config = svc_i3c_slave_set_config,
+	.enable = svc_i3c_slave_enable,
+	.disable = svc_i3c_slave_disable,
+	.queue = svc_i3c_slave_queue,
+	.dequeue = svc_i3c_dequeue,
+	.raise_ibi = svc_i3c_slave_raise_ibi,
+	.fifo_flush = svc_i3c_slave_fifo_flush,
+	.cancel_all_reqs = svc_i3c_cancel_all_reqs,
+	.get_features = svc_i3c_get_features,
+};
+
+static int svc_i3c_slave_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct i3c_slave_ctrl *slave;
+	struct svc_i3c_slave *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);
+
+	INIT_WORK(&svc->work, svc_i3c_slave_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_slave_irq_handler, 0, "svc-i3c-irq", svc);
+	if (ret)
+		return -ENOENT;
+
+	slave = devm_i3c_slave_ctrl_create(dev, &svc_i3c_slave_ops);
+	if (!slave)
+		return -ENOMEM;
+
+	dev_set_drvdata(&slave->dev, svc);
+
+	return 0;
+}
+
+static int svc_i3c_slave_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id svc_i3c_slave_of_match_tbl[] = {
+	{ .compatible = "silvaco,i3c-slave" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, svc_i3c_slave_of_match_tbl);
+
+static struct platform_driver svc_i3c_slave = {
+	.probe = svc_i3c_slave_probe,
+	.remove = svc_i3c_slave_remove,
+	.driver = {
+		.name = "silvaco-i3c-slave",
+		.of_match_table = svc_i3c_slave_of_match_tbl,
+		//.pm = &svc_i3c_pm_ops,
+	},
+};
+module_platform_driver(svc_i3c_slave);
+
+MODULE_DESCRIPTION("Silvaco dual-role I3C slave driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 6/9] i3c: slave: add svc slave controller support
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

Add Silvaco I3C slave controller support

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/i3c/Makefile              |   1 +
 drivers/i3c/slave/Kconfig         |   9 +
 drivers/i3c/slave/Makefile        |   4 +
 drivers/i3c/slave/svc-i3c-slave.c | 600 ++++++++++++++++++++++++++++++
 4 files changed, 614 insertions(+)
 create mode 100644 drivers/i3c/slave/Kconfig
 create mode 100644 drivers/i3c/slave/Makefile
 create mode 100644 drivers/i3c/slave/svc-i3c-slave.c

diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 6407ddec3a4a9..ef1acbe13fe60 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_I3C)		+= i3c.o
 obj-$(CONFIG_I3C)		+= master/
 obj-$(CONFIG_I3C_SLAVE)		+= slave.o
 obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
+obj-$(CONFIG_I3C_SLAVE)		+= slave/
diff --git a/drivers/i3c/slave/Kconfig b/drivers/i3c/slave/Kconfig
new file mode 100644
index 0000000000000..e385dbdea193b
--- /dev/null
+++ b/drivers/i3c/slave/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config I3C_SLAVE_CTRL_SVC
+	tristate "Silvaco I3C Dual-Role Slave driver"
+	depends on I3C
+	depends on HAS_IOMEM
+	depends on !(ALPHA || PARISC)
+	help
+	  Support for Silvaco I3C Dual-Role Slave Controller.
diff --git a/drivers/i3c/slave/Makefile b/drivers/i3c/slave/Makefile
new file mode 100644
index 0000000000000..612be24536311
--- /dev/null
+++ b/drivers/i3c/slave/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-${CONFIG_I3C_SLAVE_CTRL_SVC} += svc-i3c-slave.o
+
diff --git a/drivers/i3c/slave/svc-i3c-slave.c b/drivers/i3c/slave/svc-i3c-slave.c
new file mode 100644
index 0000000000000..01824913bfbc4
--- /dev/null
+++ b/drivers/i3c/slave/svc-i3c-slave.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/i3c/slave.h>
+#include <linux/i3c/slave.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/i3c/device.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+
+enum i3c_clks {
+	PCLK,
+	FCLK,
+	SCLK,
+	MAXCLK,
+};
+
+struct svc_i3c_slave {
+	struct device *dev;
+	void __iomem *regs;
+	int irq;
+	struct clk_bulk_data clks[MAXCLK];
+
+	struct list_head txq;
+	spinlock_t txq_lock;
+	struct list_head rxq;
+	spinlock_t rxq_lock;
+	struct list_head cq;
+	spinlock_t cq_lock;
+
+	struct work_struct work;
+	struct workqueue_struct *workqueue;
+
+	struct i3c_slave_ctrl_features features;
+};
+
+#define I3C_SCONFIG	0x4
+#define   I3C_SCONFIG_SLVENA_MASK	BIT(0)
+#define   I3C_SCONFIG_SADDR_MASK	GENMASK(31, 25)
+
+#define I3C_SSTATUS	0x8
+#define	  I3C_SSTATUS_STOP_MASK		BIT(10)
+#define	  I3C_SSTATUS_RX_PEND_MASK	BIT(11)
+#define   I3C_SSTATUS_TXNOTFULL_MASK	BIT(12)
+
+#define I3C_SCTRL	0xc
+#define   I3C_SCTRL_EVENT_MASK		GENMASK(1, 0)
+#define	  I3C_SCTRL_EVENT_IBI		0x1
+#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_FLUSHFB_MASK	BIT(1)
+#define   I3C_SDATACTRL_FLUSHTB_MASK	BIT(0)
+
+#define I3C_SWDATAB	0x30
+#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_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_slave_enable(struct i3c_slave_ctrl *ctrl)
+{
+	struct svc_i3c_slave *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_slave_disable(struct i3c_slave_ctrl *ctrl)
+{
+	struct svc_i3c_slave *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_slave_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	struct svc_i3c_slave *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;
+}
+
+const struct i3c_slave_ctrl_features *svc_i3c_get_features(struct i3c_slave_ctrl *ctrl)
+{
+	struct svc_i3c_slave *svc;
+
+	svc = dev_get_drvdata(&ctrl->dev);
+
+	if (!svc)
+		return NULL;
+
+	return &svc->features;
+}
+
+static int svc_i3c_slave_queue(struct i3c_request *req, gfp_t)
+{
+	struct svc_i3c_slave *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)
+		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_slave *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_slave_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	struct svc_i3c_slave *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_slave_raise_ibi(struct i3c_slave_ctrl *ctrl, void *p, u8 size)
+{
+	struct svc_i3c_slave *svc;
+	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;
+
+	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");
+		return -ENAVAIL;
+	}
+
+	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);
+
+	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 IBI finish\n");
+		return -ENAVAIL;
+	}
+
+	return 0;
+}
+
+static void svc_i3c_slave_complete(struct work_struct *work)
+{
+	struct svc_i3c_slave *svc = container_of(work, struct svc_i3c_slave, 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_slave_irq_handler(int irq, void *dev_id)
+{
+	struct i3c_request *req, *complete = NULL;
+	struct svc_i3c_slave *svc = dev_id;
+	unsigned long flags;
+	u32 statusFlags;
+
+	statusFlags = readl(svc->regs + I3C_SSTATUS);
+	writel(statusFlags, svc->regs + I3C_SSTATUS);
+
+	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) {
+
+		complete = NULL;
+		spin_lock_irqsave(&svc->txq_lock, flags);
+		req = list_first_entry_or_null(&svc->txq, struct i3c_request, list);
+		if (!req) {
+			writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTCLR);
+		} else {
+			while (!(readl_relaxed(svc->regs + I3C_SDATACTRL)
+					& I3C_SDATACTRL_TXFULL_MASK)) {
+
+				if (req->actual + 1 == req->length)
+					writel_relaxed(*(u8 *)(req->buf + req->actual),
+						       svc->regs + I3C_SWDATAE);
+				else
+					writel_relaxed(*(u8 *)(req->buf + req->actual),
+						       svc->regs + I3C_SWDATAB);
+
+				req->actual++;
+
+				if (req->actual == req->length) {
+					list_del(&req->list);
+					complete = req;
+					break;
+				}
+			}
+		}
+		spin_unlock_irqrestore(&svc->txq_lock, flags);
+		if (complete)
+			complete->complete(complete);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void svc_i3c_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+	struct svc_i3c_slave *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 struct i3c_slave_ctrl_ops svc_i3c_slave_ops = {
+	.set_config = svc_i3c_slave_set_config,
+	.enable = svc_i3c_slave_enable,
+	.disable = svc_i3c_slave_disable,
+	.queue = svc_i3c_slave_queue,
+	.dequeue = svc_i3c_dequeue,
+	.raise_ibi = svc_i3c_slave_raise_ibi,
+	.fifo_flush = svc_i3c_slave_fifo_flush,
+	.cancel_all_reqs = svc_i3c_cancel_all_reqs,
+	.get_features = svc_i3c_get_features,
+};
+
+static int svc_i3c_slave_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct i3c_slave_ctrl *slave;
+	struct svc_i3c_slave *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);
+
+	INIT_WORK(&svc->work, svc_i3c_slave_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_slave_irq_handler, 0, "svc-i3c-irq", svc);
+	if (ret)
+		return -ENOENT;
+
+	slave = devm_i3c_slave_ctrl_create(dev, &svc_i3c_slave_ops);
+	if (!slave)
+		return -ENOMEM;
+
+	dev_set_drvdata(&slave->dev, svc);
+
+	return 0;
+}
+
+static int svc_i3c_slave_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id svc_i3c_slave_of_match_tbl[] = {
+	{ .compatible = "silvaco,i3c-slave" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, svc_i3c_slave_of_match_tbl);
+
+static struct platform_driver svc_i3c_slave = {
+	.probe = svc_i3c_slave_probe,
+	.remove = svc_i3c_slave_remove,
+	.driver = {
+		.name = "silvaco-i3c-slave",
+		.of_match_table = svc_i3c_slave_of_match_tbl,
+		//.pm = &svc_i3c_pm_ops,
+	},
+};
+module_platform_driver(svc_i3c_slave);
+
+MODULE_DESCRIPTION("Silvaco dual-role I3C slave driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 7/9] i3c: slave: func: add tty driver
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

Add tty over I3C slave function driver.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/i3c/Kconfig       |   1 +
 drivers/i3c/Makefile      |   1 +
 drivers/i3c/func/Kconfig  |   9 +
 drivers/i3c/func/Makefile |   3 +
 drivers/i3c/func/tty.c    | 345 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 359 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 bdc173bc0da12..fa0f63e0e3e6e 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -50,4 +50,5 @@ config I3C_SLAVE_CONFIGFS
 
 if I3C_SLAVE
 source "drivers/i3c/slave/Kconfig"
+source "drivers/i3c/func/Kconfig"
 endif #I#C_SLAVE
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index ef1acbe13fe60..7814bf2dd9b40 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_I3C)		+= master/
 obj-$(CONFIG_I3C_SLAVE)		+= slave.o
 obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
 obj-$(CONFIG_I3C_SLAVE)		+= slave/
+obj-$(CONFIG_I3C_SLAVE)		+= func/
diff --git a/drivers/i3c/func/Kconfig b/drivers/i3c/func/Kconfig
new file mode 100644
index 0000000000000..f122e2cc32de8
--- /dev/null
+++ b/drivers/i3c/func/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config I3C_SLAVE_FUNC_TTY
+	tristate "PCI Endpoint Test driver"
+	depends on I3C_SLAVE
+	help
+	  I3C Slave TTY Function Driver
+
+	  General TTY over I3C slave controller function drivers.
diff --git a/drivers/i3c/func/Makefile b/drivers/i3c/func/Makefile
new file mode 100644
index 0000000000000..db3262e402edd
--- /dev/null
+++ b/drivers/i3c/func/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_I3C_SLAVE_FUNC_TTY)              += tty.o
diff --git a/drivers/i3c/func/tty.c b/drivers/i3c/func/tty.c
new file mode 100644
index 0000000000000..be43878913452
--- /dev/null
+++ b/drivers/i3c/func/tty.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/i3c/slave.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty_flip.h>
+
+#define PORT_I3C 124
+
+struct ttyi3c_port {
+	struct uart_port	port;
+	struct i3c_slave_func	*i3cdev;
+	unsigned long		buffer;
+	struct work_struct	work;
+	struct workqueue_struct	*workqueue;
+};
+
+static struct uart_driver ttyi3c_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "ttySI3C",
+	.dev_name	= "ttySI3C",
+	.nr		= 1,
+};
+
+static unsigned int ttyi3c_tx_empty(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+
+}
+
+static unsigned int ttyi3c_get_mctrl(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_stop_tx(struct uart_port *port)
+{
+}
+
+static void ttyi3c_stop_rx(struct uart_port *port)
+{
+}
+
+static void ttyi3c_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static int ttyi3c_startup(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_shutdown(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+
+}
+
+static void
+ttyi3c_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old)
+{
+
+}
+
+static const char *ttyi3c_type(struct uart_port *port)
+{
+	return "I3CTTY";
+}
+
+static void ttyi3c_release_port(struct uart_port *port)
+{
+
+}
+
+static int ttyi3c_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_config_port(struct uart_port *port, int flags)
+{
+
+}
+
+static int ttyi3c_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return 0;
+}
+
+static void ttyi3c_flush_buffer(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_start_tx(struct uart_port *port)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	queue_work(sport->workqueue, &sport->work);
+}
+
+static void i3c_slave_tty_rx_complete(struct i3c_request *req)
+{
+	struct ttyi3c_port *port = req->context;
+
+	if (req->status == I3C_REQUEST_CANCEL) {
+		i3c_slave_ctrl_free_request(req);
+		return;
+	}
+
+	for (int i = 0; i < req->actual; i++) {
+		if (tty_insert_flip_char(&port->port.state->port, *(u8 *)(req->buf + i), 0) == 0)
+			port->port.icount.buf_overrun++;
+	}
+
+	tty_flip_buffer_push(&port->port.state->port);
+	req->actual = 0;
+	req->status = 0;
+	i3c_slave_ctrl_queue(req, GFP_KERNEL);
+}
+
+
+static void i3c_slave_tty_tx_complete(struct i3c_request *req)
+{
+	struct ttyi3c_port *port = req->context;
+	struct circ_buf *xmit = &port->port.state->xmit;
+
+	if (req->status == I3C_REQUEST_CANCEL) {
+		i3c_slave_ctrl_free_request(req);
+		return;
+	}
+
+	uart_xmit_advance(&port->port, req->actual);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&port->port);
+
+
+	i3c_slave_ctrl_free_request(req);
+}
+
+static void i3c_slave_tty_i3c_work(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, work);
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	int cnt = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	int actual;
+	int ret;
+
+	if (cnt == 0)
+		return;
+
+	if (cnt > 0) {
+		struct i3c_request *req = i3c_slave_ctrl_alloc_request(sport->i3cdev->ctrl,
+								       GFP_KERNEL);
+		if (!req)
+			return;
+
+		req->length = ((xmit->tail + cnt) > UART_XMIT_SIZE) ? UART_XMIT_SIZE - xmit->tail :
+								    cnt;
+		req->buf =  xmit->buf + xmit->tail;
+		req->complete = i3c_slave_tty_tx_complete;
+		req->context = sport;
+		req->tx = true;
+
+		if (i3c_slave_ctrl_queue(req, GFP_KERNEL))
+			return;
+
+		if ((xmit->tail + cnt) > UART_XMIT_SIZE) {
+			req = i3c_slave_ctrl_alloc_request(sport->i3cdev->ctrl, GFP_KERNEL);
+			if (!req)
+				return;
+			req->buf = xmit->buf;
+			req->length = xmit->tail + cnt - UART_XMIT_SIZE;
+			req->complete = i3c_slave_tty_tx_complete;
+
+			if (i3c_slave_ctrl_queue(req, GFP_KERNEL))
+				return;
+		}
+	}
+
+	i3c_slave_ctrl_raise_ibi(sport->i3cdev->ctrl, NULL, 0);
+}
+
+static const struct uart_ops ttyi3c_pops = {
+	.tx_empty       = ttyi3c_tx_empty,
+	.set_mctrl      = ttyi3c_set_mctrl,
+	.get_mctrl      = ttyi3c_get_mctrl,
+	.stop_tx        = ttyi3c_stop_tx,
+	.start_tx       = ttyi3c_start_tx,
+	.stop_rx        = ttyi3c_stop_rx,
+	.break_ctl      = ttyi3c_break_ctl,
+	.startup        = ttyi3c_startup,
+	.shutdown       = ttyi3c_shutdown,
+	.pm             = ttyi3c_uart_pm,
+	.set_termios    = ttyi3c_set_termios,
+	.type           = ttyi3c_type,
+	.request_port   = ttyi3c_request_port,
+	.release_port   = ttyi3c_release_port,
+	.config_port    = ttyi3c_config_port,
+	.verify_port    = ttyi3c_verify_port,
+	.flush_buffer   = ttyi3c_flush_buffer,
+#if defined(CONFIG_CONSOLE_POLL)
+	.poll_init      = ttyi3c_poll_init,
+	.poll_get_char  = ttyi3c_poll_get_char,
+	.poll_put_char  = ttyi3c_poll_put_char,
+#endif
+};
+
+static int i3c_slave_tty_bind(struct i3c_slave_func *func)
+{
+	const struct i3c_slave_ctrl_features *feature;
+	unsigned int rxfifo_size;
+	struct ttyi3c_port *port;
+	struct i3c_request *req;
+	int offset = 0;
+	int ret;
+
+	feature = i3c_slave_ctrl_get_features(func->ctrl);
+	if (!feature)
+		return -EINVAL;
+
+	rxfifo_size = feature->rx_fifo_sz;
+
+	if (!rxfifo_size)
+		rxfifo_size = 16;
+
+	port = dev_get_drvdata(&func->dev);
+
+	port->buffer = get_zeroed_page(GFP_KERNEL);
+	if (!port->buffer)
+		return -ENOMEM;
+
+	if (i3c_slave_ctrl_set_config(func->ctrl, func)) {
+		dev_err(&func->dev, "failure set i3c config\n");
+		return -EINVAL;
+	}
+
+	req = i3c_slave_ctrl_alloc_request(func->ctrl, GFP_KERNEL);
+	do {
+		req->buf = (void *) (port->buffer + offset);
+		req->length = rxfifo_size;
+		req->context = port;
+		req->complete = i3c_slave_tty_rx_complete;
+		offset += rxfifo_size;
+
+		if (i3c_slave_ctrl_queue(req, GFP_KERNEL))
+			break;
+	} while (req == NULL || offset >= PAGE_SIZE);
+
+	if (i3c_slave_ctrl_set_config(func->ctrl, func)) {
+		dev_err(&func->dev, "failure set i3c config\n");
+		return -EINVAL;
+	}
+
+	ret = uart_register_driver(&ttyi3c_reg);
+	if (ret)
+		return ret;
+
+	ret = uart_add_one_port(&ttyi3c_reg, &port->port);
+	if (ret)
+		goto err_one_port;
+
+	ret = i3c_slave_ctrl_enable(func->ctrl);
+	if (ret)
+		goto err_ctrl_enable;
+
+	return 0;
+
+err_ctrl_enable:
+	uart_remove_one_port(&ttyi3c_reg, &port->port);
+err_one_port:
+	uart_unregister_driver(&ttyi3c_reg);
+	dev_err(&func->dev, "bind failure\n");
+
+	return ret;
+}
+
+static void i3c_slave_tty_unbind(struct i3c_slave_func *func)
+{
+	struct ttyi3c_port *port;
+
+	port = dev_get_drvdata(&func->dev);
+
+	i3c_slave_ctrl_disable(func->ctrl);
+	i3c_slave_ctrl_cancel_all_reqs(func->ctrl, 0);
+	i3c_slave_ctrl_cancel_all_reqs(func->ctrl, 1);
+	uart_remove_one_port(&ttyi3c_reg, &port->port);
+	uart_unregister_driver(&ttyi3c_reg);
+
+	free_page(port->buffer);
+}
+
+static struct i3c_slave_func_ops i3c_tty_ops = {
+	.bind   = i3c_slave_tty_bind,
+	.unbind = i3c_slave_tty_unbind,
+};
+
+static int i3c_tty_probe(struct i3c_slave_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;
+	port->port.dev = &func->dev;
+	port->port.ops = &ttyi3c_pops;
+	port->port.type = PORT_I3C;
+
+	dev_set_drvdata(&func->dev, port);
+
+	port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&func->dev));
+	if (!port->workqueue)
+		return -ENOMEM;
+
+	INIT_WORK(&port->work, i3c_slave_tty_i3c_work);
+
+	return 0;
+}
+
+static void  i3c_tty_remove(struct i3c_slave_func *func)
+{
+	struct ttyi3c_port *port;
+
+	port = dev_get_drvdata(&func->dev);
+
+	destroy_workqueue(port->workqueue);
+}
+
+DECLARE_I3C_SLAVE_INIT(tty, i3c_tty_probe, i3c_tty_remove, &i3c_tty_ops);
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 7/9] i3c: slave: func: add tty driver
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

Add tty over I3C slave function driver.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/i3c/Kconfig       |   1 +
 drivers/i3c/Makefile      |   1 +
 drivers/i3c/func/Kconfig  |   9 +
 drivers/i3c/func/Makefile |   3 +
 drivers/i3c/func/tty.c    | 345 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 359 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 bdc173bc0da12..fa0f63e0e3e6e 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -50,4 +50,5 @@ config I3C_SLAVE_CONFIGFS
 
 if I3C_SLAVE
 source "drivers/i3c/slave/Kconfig"
+source "drivers/i3c/func/Kconfig"
 endif #I#C_SLAVE
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index ef1acbe13fe60..7814bf2dd9b40 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_I3C)		+= master/
 obj-$(CONFIG_I3C_SLAVE)		+= slave.o
 obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
 obj-$(CONFIG_I3C_SLAVE)		+= slave/
+obj-$(CONFIG_I3C_SLAVE)		+= func/
diff --git a/drivers/i3c/func/Kconfig b/drivers/i3c/func/Kconfig
new file mode 100644
index 0000000000000..f122e2cc32de8
--- /dev/null
+++ b/drivers/i3c/func/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config I3C_SLAVE_FUNC_TTY
+	tristate "PCI Endpoint Test driver"
+	depends on I3C_SLAVE
+	help
+	  I3C Slave TTY Function Driver
+
+	  General TTY over I3C slave controller function drivers.
diff --git a/drivers/i3c/func/Makefile b/drivers/i3c/func/Makefile
new file mode 100644
index 0000000000000..db3262e402edd
--- /dev/null
+++ b/drivers/i3c/func/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_I3C_SLAVE_FUNC_TTY)              += tty.o
diff --git a/drivers/i3c/func/tty.c b/drivers/i3c/func/tty.c
new file mode 100644
index 0000000000000..be43878913452
--- /dev/null
+++ b/drivers/i3c/func/tty.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/i3c/slave.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty_flip.h>
+
+#define PORT_I3C 124
+
+struct ttyi3c_port {
+	struct uart_port	port;
+	struct i3c_slave_func	*i3cdev;
+	unsigned long		buffer;
+	struct work_struct	work;
+	struct workqueue_struct	*workqueue;
+};
+
+static struct uart_driver ttyi3c_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "ttySI3C",
+	.dev_name	= "ttySI3C",
+	.nr		= 1,
+};
+
+static unsigned int ttyi3c_tx_empty(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+
+}
+
+static unsigned int ttyi3c_get_mctrl(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_stop_tx(struct uart_port *port)
+{
+}
+
+static void ttyi3c_stop_rx(struct uart_port *port)
+{
+}
+
+static void ttyi3c_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static int ttyi3c_startup(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_shutdown(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+
+}
+
+static void
+ttyi3c_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old)
+{
+
+}
+
+static const char *ttyi3c_type(struct uart_port *port)
+{
+	return "I3CTTY";
+}
+
+static void ttyi3c_release_port(struct uart_port *port)
+{
+
+}
+
+static int ttyi3c_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_config_port(struct uart_port *port, int flags)
+{
+
+}
+
+static int ttyi3c_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return 0;
+}
+
+static void ttyi3c_flush_buffer(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_start_tx(struct uart_port *port)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	queue_work(sport->workqueue, &sport->work);
+}
+
+static void i3c_slave_tty_rx_complete(struct i3c_request *req)
+{
+	struct ttyi3c_port *port = req->context;
+
+	if (req->status == I3C_REQUEST_CANCEL) {
+		i3c_slave_ctrl_free_request(req);
+		return;
+	}
+
+	for (int i = 0; i < req->actual; i++) {
+		if (tty_insert_flip_char(&port->port.state->port, *(u8 *)(req->buf + i), 0) == 0)
+			port->port.icount.buf_overrun++;
+	}
+
+	tty_flip_buffer_push(&port->port.state->port);
+	req->actual = 0;
+	req->status = 0;
+	i3c_slave_ctrl_queue(req, GFP_KERNEL);
+}
+
+
+static void i3c_slave_tty_tx_complete(struct i3c_request *req)
+{
+	struct ttyi3c_port *port = req->context;
+	struct circ_buf *xmit = &port->port.state->xmit;
+
+	if (req->status == I3C_REQUEST_CANCEL) {
+		i3c_slave_ctrl_free_request(req);
+		return;
+	}
+
+	uart_xmit_advance(&port->port, req->actual);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&port->port);
+
+
+	i3c_slave_ctrl_free_request(req);
+}
+
+static void i3c_slave_tty_i3c_work(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, work);
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	int cnt = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	int actual;
+	int ret;
+
+	if (cnt == 0)
+		return;
+
+	if (cnt > 0) {
+		struct i3c_request *req = i3c_slave_ctrl_alloc_request(sport->i3cdev->ctrl,
+								       GFP_KERNEL);
+		if (!req)
+			return;
+
+		req->length = ((xmit->tail + cnt) > UART_XMIT_SIZE) ? UART_XMIT_SIZE - xmit->tail :
+								    cnt;
+		req->buf =  xmit->buf + xmit->tail;
+		req->complete = i3c_slave_tty_tx_complete;
+		req->context = sport;
+		req->tx = true;
+
+		if (i3c_slave_ctrl_queue(req, GFP_KERNEL))
+			return;
+
+		if ((xmit->tail + cnt) > UART_XMIT_SIZE) {
+			req = i3c_slave_ctrl_alloc_request(sport->i3cdev->ctrl, GFP_KERNEL);
+			if (!req)
+				return;
+			req->buf = xmit->buf;
+			req->length = xmit->tail + cnt - UART_XMIT_SIZE;
+			req->complete = i3c_slave_tty_tx_complete;
+
+			if (i3c_slave_ctrl_queue(req, GFP_KERNEL))
+				return;
+		}
+	}
+
+	i3c_slave_ctrl_raise_ibi(sport->i3cdev->ctrl, NULL, 0);
+}
+
+static const struct uart_ops ttyi3c_pops = {
+	.tx_empty       = ttyi3c_tx_empty,
+	.set_mctrl      = ttyi3c_set_mctrl,
+	.get_mctrl      = ttyi3c_get_mctrl,
+	.stop_tx        = ttyi3c_stop_tx,
+	.start_tx       = ttyi3c_start_tx,
+	.stop_rx        = ttyi3c_stop_rx,
+	.break_ctl      = ttyi3c_break_ctl,
+	.startup        = ttyi3c_startup,
+	.shutdown       = ttyi3c_shutdown,
+	.pm             = ttyi3c_uart_pm,
+	.set_termios    = ttyi3c_set_termios,
+	.type           = ttyi3c_type,
+	.request_port   = ttyi3c_request_port,
+	.release_port   = ttyi3c_release_port,
+	.config_port    = ttyi3c_config_port,
+	.verify_port    = ttyi3c_verify_port,
+	.flush_buffer   = ttyi3c_flush_buffer,
+#if defined(CONFIG_CONSOLE_POLL)
+	.poll_init      = ttyi3c_poll_init,
+	.poll_get_char  = ttyi3c_poll_get_char,
+	.poll_put_char  = ttyi3c_poll_put_char,
+#endif
+};
+
+static int i3c_slave_tty_bind(struct i3c_slave_func *func)
+{
+	const struct i3c_slave_ctrl_features *feature;
+	unsigned int rxfifo_size;
+	struct ttyi3c_port *port;
+	struct i3c_request *req;
+	int offset = 0;
+	int ret;
+
+	feature = i3c_slave_ctrl_get_features(func->ctrl);
+	if (!feature)
+		return -EINVAL;
+
+	rxfifo_size = feature->rx_fifo_sz;
+
+	if (!rxfifo_size)
+		rxfifo_size = 16;
+
+	port = dev_get_drvdata(&func->dev);
+
+	port->buffer = get_zeroed_page(GFP_KERNEL);
+	if (!port->buffer)
+		return -ENOMEM;
+
+	if (i3c_slave_ctrl_set_config(func->ctrl, func)) {
+		dev_err(&func->dev, "failure set i3c config\n");
+		return -EINVAL;
+	}
+
+	req = i3c_slave_ctrl_alloc_request(func->ctrl, GFP_KERNEL);
+	do {
+		req->buf = (void *) (port->buffer + offset);
+		req->length = rxfifo_size;
+		req->context = port;
+		req->complete = i3c_slave_tty_rx_complete;
+		offset += rxfifo_size;
+
+		if (i3c_slave_ctrl_queue(req, GFP_KERNEL))
+			break;
+	} while (req == NULL || offset >= PAGE_SIZE);
+
+	if (i3c_slave_ctrl_set_config(func->ctrl, func)) {
+		dev_err(&func->dev, "failure set i3c config\n");
+		return -EINVAL;
+	}
+
+	ret = uart_register_driver(&ttyi3c_reg);
+	if (ret)
+		return ret;
+
+	ret = uart_add_one_port(&ttyi3c_reg, &port->port);
+	if (ret)
+		goto err_one_port;
+
+	ret = i3c_slave_ctrl_enable(func->ctrl);
+	if (ret)
+		goto err_ctrl_enable;
+
+	return 0;
+
+err_ctrl_enable:
+	uart_remove_one_port(&ttyi3c_reg, &port->port);
+err_one_port:
+	uart_unregister_driver(&ttyi3c_reg);
+	dev_err(&func->dev, "bind failure\n");
+
+	return ret;
+}
+
+static void i3c_slave_tty_unbind(struct i3c_slave_func *func)
+{
+	struct ttyi3c_port *port;
+
+	port = dev_get_drvdata(&func->dev);
+
+	i3c_slave_ctrl_disable(func->ctrl);
+	i3c_slave_ctrl_cancel_all_reqs(func->ctrl, 0);
+	i3c_slave_ctrl_cancel_all_reqs(func->ctrl, 1);
+	uart_remove_one_port(&ttyi3c_reg, &port->port);
+	uart_unregister_driver(&ttyi3c_reg);
+
+	free_page(port->buffer);
+}
+
+static struct i3c_slave_func_ops i3c_tty_ops = {
+	.bind   = i3c_slave_tty_bind,
+	.unbind = i3c_slave_tty_unbind,
+};
+
+static int i3c_tty_probe(struct i3c_slave_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;
+	port->port.dev = &func->dev;
+	port->port.ops = &ttyi3c_pops;
+	port->port.type = PORT_I3C;
+
+	dev_set_drvdata(&func->dev, port);
+
+	port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&func->dev));
+	if (!port->workqueue)
+		return -ENOMEM;
+
+	INIT_WORK(&port->work, i3c_slave_tty_i3c_work);
+
+	return 0;
+}
+
+static void  i3c_tty_remove(struct i3c_slave_func *func)
+{
+	struct ttyi3c_port *port;
+
+	port = dev_get_drvdata(&func->dev);
+
+	destroy_workqueue(port->workqueue);
+}
+
+DECLARE_I3C_SLAVE_INIT(tty, i3c_tty_probe, i3c_tty_remove, &i3c_tty_ops);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 8/9] tty: serial: add tty over I3C master side driver
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

This is master side tty driver for tty over i3c.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/tty/serial/Kconfig  |   6 +
 drivers/tty/serial/Makefile |   1 +
 drivers/tty/serial/ttyi3c.c | 276 ++++++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+)
 create mode 100644 drivers/tty/serial/ttyi3c.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index bdc568a4ab669..2ceafe94cd1dc 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1578,6 +1578,12 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
 	  but you can alter that using a kernel command line option such as
 	  "console=ttyNVTx".
 
+config SERIAL_I3C
+	tristate "tty over i3c"
+	depends on I3C
+	help
+	  Select this options if you'd like use UART over I3C master controller
+
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 138abbc897381..60c101809cdcf 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -88,6 +88,7 @@ obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o
 obj-$(CONFIG_SERIAL_SIFIVE)	+= sifive.o
 obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o
 obj-$(CONFIG_SERIAL_SUNPLUS)	+= sunplus-uart.o
+obj-$(CONFIG_SERIAL_I3C)	+= ttyi3c.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/ttyi3c.c b/drivers/tty/serial/ttyi3c.c
new file mode 100644
index 0000000000000..e2912c8bac87d
--- /dev/null
+++ b/drivers/tty/serial/ttyi3c.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/tty_flip.h>
+
+#define DEVICE_NAME "ttyI3C"
+#define PORT_I3C 124
+
+struct ttyi3c_port {
+	struct uart_port	port;
+	unsigned int		txfifo_size;
+	unsigned int		rxfifo_size;
+	void			*buffer;
+	struct i3c_device	*i3cdev;
+	struct work_struct	work;
+	struct workqueue_struct *workqueue;
+};
+
+static struct uart_driver ttyi3c_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "fsl_ttyi3c",
+	.dev_name	= DEVICE_NAME,
+	.nr		= 1,
+};
+
+static const struct i3c_device_id fsl_tty_i3c_ids[] = {
+	I3C_DEVICE(0x011B, 0x1000, NULL),
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i3c, fsl_tty_i3c_ids);
+
+static unsigned int ttyi3c_tx_empty(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+
+}
+
+static unsigned int ttyi3c_get_mctrl(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_stop_tx(struct uart_port *port)
+{
+}
+
+static void ttyi3c_stop_rx(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_break_ctl(struct uart_port *port, int break_state)
+{
+
+}
+
+static int ttyi3c_startup(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_shutdown(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+
+}
+
+static void
+ttyi3c_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old)
+{
+
+}
+
+static const char *ttyi3c_type(struct uart_port *port)
+{
+	return "I3CTTY";
+}
+
+static void ttyi3c_release_port(struct uart_port *port)
+{
+
+}
+
+static int ttyi3c_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_config_port(struct uart_port *port, int flags)
+{
+
+}
+
+static int ttyi3c_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return 0;
+}
+
+static void ttyi3c_flush_buffer(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_start_tx(struct uart_port *port)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	queue_work(sport->workqueue, &sport->work);
+}
+
+static const struct uart_ops ttyi3c_pops = {
+	.tx_empty	= ttyi3c_tx_empty,
+	.set_mctrl	= ttyi3c_set_mctrl,
+	.get_mctrl	= ttyi3c_get_mctrl,
+	.stop_tx	= ttyi3c_stop_tx,
+	.start_tx	= ttyi3c_start_tx,
+	.stop_rx	= ttyi3c_stop_rx,
+	.break_ctl	= ttyi3c_break_ctl,
+	.startup	= ttyi3c_startup,
+	.shutdown	= ttyi3c_shutdown,
+	.pm		= ttyi3c_uart_pm,
+	.set_termios	= ttyi3c_set_termios,
+	.type		= ttyi3c_type,
+	.request_port	= ttyi3c_request_port,
+	.release_port	= ttyi3c_release_port,
+	.config_port	= ttyi3c_config_port,
+	.verify_port	= ttyi3c_verify_port,
+	.flush_buffer	= ttyi3c_flush_buffer,
+};
+
+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);
+	struct i3c_priv_xfer xfers;
+	int i = 0;
+
+	memset(&xfers, 0, sizeof(xfers));
+
+	xfers.data.in = sport->buffer;
+	xfers.len = 16;
+	xfers.rnw = 1;
+
+	i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
+
+	for (i = 0; i < xfers.actual; i++)
+		if (tty_insert_flip_char(&sport->port.state->port, *(u8 *)(sport->buffer + i), 0)
+		    == 0)
+			sport->port.icount.buf_overrun++;
+
+	tty_flip_buffer_push(&sport->port.state->port);
+}
+
+static void fsl_tty_i3c_work(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, work);
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	int cnt = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	struct i3c_priv_xfer xfers;
+	int actual;
+	int ret;
+
+	if (cnt == 0)
+		return;
+
+	if (cnt > 0) {
+		xfers.rnw = 0;
+		xfers.len = ((xmit->tail + cnt) > UART_XMIT_SIZE) ? UART_XMIT_SIZE - xmit->tail :
+								    cnt;
+		xfers.data.out = xmit->buf + xmit->tail;
+
+		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
+
+		actual = ret ? xfers.actual : xfers.len;
+
+		uart_xmit_advance(&sport->port, actual);
+
+		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+			uart_write_wakeup(&sport->port);
+
+		cnt -= actual;
+	}
+
+	if (cnt)
+		queue_work(sport->workqueue, &sport->work);
+}
+
+static int fsl_tty_i3c_probe(struct i3c_device *i3cdev)
+{
+	struct ttyi3c_port *port;
+	struct i3c_ibi_setup req;
+	int ret;
+
+	port = devm_kzalloc(&i3cdev->dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->i3cdev = i3cdev;
+	port->port.ops = &ttyi3c_pops;
+	port->port.type = PORT_I3C;
+	port->port.dev = &i3cdev->dev;
+	port->buffer = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
+
+	req.max_payload_len = 8;
+	req.num_slots = 4;
+	req.handler = &i3c_controller_irq_handler;
+
+	dev_set_drvdata(&i3cdev->dev, port);
+
+	INIT_WORK(&port->work, fsl_tty_i3c_work);
+	port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&i3cdev->dev));
+	if (!port->workqueue)
+		return -ENOMEM;
+
+	ret = uart_register_driver(&ttyi3c_reg);
+	if (ret)
+		return ret;
+
+	ret = uart_add_one_port(&ttyi3c_reg, &port->port);
+	if (ret)
+		goto err_add_one_port;
+
+	ret = i3c_device_request_ibi(i3cdev, &req);
+	if (ret)
+		goto err_request_ibi;
+
+	ret = i3c_device_enable_ibi(i3cdev);
+	if (ret)
+		goto  err_enable_ibi;
+
+	return 0;
+
+err_enable_ibi:
+	i3c_device_free_ibi(i3cdev);
+err_request_ibi:
+	uart_remove_one_port(&ttyi3c_reg, &port->port);
+err_add_one_port:
+	uart_unregister_driver(&ttyi3c_reg);
+
+	return ret;
+}
+
+static struct i3c_driver fsl_tty_i3c_driver = {
+	.driver = {
+		.name = "ttyi3c",
+	},
+	.probe = fsl_tty_i3c_probe,
+	.id_table = fsl_tty_i3c_ids,
+};
+
+module_i3c_driver(fsl_tty_i3c_driver);
+MODULE_LICENSE("GPL");
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 8/9] tty: serial: add tty over I3C master side driver
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

This is master side tty driver for tty over i3c.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/tty/serial/Kconfig  |   6 +
 drivers/tty/serial/Makefile |   1 +
 drivers/tty/serial/ttyi3c.c | 276 ++++++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+)
 create mode 100644 drivers/tty/serial/ttyi3c.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index bdc568a4ab669..2ceafe94cd1dc 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1578,6 +1578,12 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
 	  but you can alter that using a kernel command line option such as
 	  "console=ttyNVTx".
 
+config SERIAL_I3C
+	tristate "tty over i3c"
+	depends on I3C
+	help
+	  Select this options if you'd like use UART over I3C master controller
+
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 138abbc897381..60c101809cdcf 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -88,6 +88,7 @@ obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o
 obj-$(CONFIG_SERIAL_SIFIVE)	+= sifive.o
 obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o
 obj-$(CONFIG_SERIAL_SUNPLUS)	+= sunplus-uart.o
+obj-$(CONFIG_SERIAL_I3C)	+= ttyi3c.o
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/ttyi3c.c b/drivers/tty/serial/ttyi3c.c
new file mode 100644
index 0000000000000..e2912c8bac87d
--- /dev/null
+++ b/drivers/tty/serial/ttyi3c.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li@nxp.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/tty_flip.h>
+
+#define DEVICE_NAME "ttyI3C"
+#define PORT_I3C 124
+
+struct ttyi3c_port {
+	struct uart_port	port;
+	unsigned int		txfifo_size;
+	unsigned int		rxfifo_size;
+	void			*buffer;
+	struct i3c_device	*i3cdev;
+	struct work_struct	work;
+	struct workqueue_struct *workqueue;
+};
+
+static struct uart_driver ttyi3c_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "fsl_ttyi3c",
+	.dev_name	= DEVICE_NAME,
+	.nr		= 1,
+};
+
+static const struct i3c_device_id fsl_tty_i3c_ids[] = {
+	I3C_DEVICE(0x011B, 0x1000, NULL),
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i3c, fsl_tty_i3c_ids);
+
+static unsigned int ttyi3c_tx_empty(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+
+}
+
+static unsigned int ttyi3c_get_mctrl(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_stop_tx(struct uart_port *port)
+{
+}
+
+static void ttyi3c_stop_rx(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_break_ctl(struct uart_port *port, int break_state)
+{
+
+}
+
+static int ttyi3c_startup(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_shutdown(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+
+}
+
+static void
+ttyi3c_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old)
+{
+
+}
+
+static const char *ttyi3c_type(struct uart_port *port)
+{
+	return "I3CTTY";
+}
+
+static void ttyi3c_release_port(struct uart_port *port)
+{
+
+}
+
+static int ttyi3c_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ttyi3c_config_port(struct uart_port *port, int flags)
+{
+
+}
+
+static int ttyi3c_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return 0;
+}
+
+static void ttyi3c_flush_buffer(struct uart_port *port)
+{
+
+}
+
+static void ttyi3c_start_tx(struct uart_port *port)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	queue_work(sport->workqueue, &sport->work);
+}
+
+static const struct uart_ops ttyi3c_pops = {
+	.tx_empty	= ttyi3c_tx_empty,
+	.set_mctrl	= ttyi3c_set_mctrl,
+	.get_mctrl	= ttyi3c_get_mctrl,
+	.stop_tx	= ttyi3c_stop_tx,
+	.start_tx	= ttyi3c_start_tx,
+	.stop_rx	= ttyi3c_stop_rx,
+	.break_ctl	= ttyi3c_break_ctl,
+	.startup	= ttyi3c_startup,
+	.shutdown	= ttyi3c_shutdown,
+	.pm		= ttyi3c_uart_pm,
+	.set_termios	= ttyi3c_set_termios,
+	.type		= ttyi3c_type,
+	.request_port	= ttyi3c_request_port,
+	.release_port	= ttyi3c_release_port,
+	.config_port	= ttyi3c_config_port,
+	.verify_port	= ttyi3c_verify_port,
+	.flush_buffer	= ttyi3c_flush_buffer,
+};
+
+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);
+	struct i3c_priv_xfer xfers;
+	int i = 0;
+
+	memset(&xfers, 0, sizeof(xfers));
+
+	xfers.data.in = sport->buffer;
+	xfers.len = 16;
+	xfers.rnw = 1;
+
+	i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
+
+	for (i = 0; i < xfers.actual; i++)
+		if (tty_insert_flip_char(&sport->port.state->port, *(u8 *)(sport->buffer + i), 0)
+		    == 0)
+			sport->port.icount.buf_overrun++;
+
+	tty_flip_buffer_push(&sport->port.state->port);
+}
+
+static void fsl_tty_i3c_work(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, work);
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	int cnt = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	struct i3c_priv_xfer xfers;
+	int actual;
+	int ret;
+
+	if (cnt == 0)
+		return;
+
+	if (cnt > 0) {
+		xfers.rnw = 0;
+		xfers.len = ((xmit->tail + cnt) > UART_XMIT_SIZE) ? UART_XMIT_SIZE - xmit->tail :
+								    cnt;
+		xfers.data.out = xmit->buf + xmit->tail;
+
+		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
+
+		actual = ret ? xfers.actual : xfers.len;
+
+		uart_xmit_advance(&sport->port, actual);
+
+		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+			uart_write_wakeup(&sport->port);
+
+		cnt -= actual;
+	}
+
+	if (cnt)
+		queue_work(sport->workqueue, &sport->work);
+}
+
+static int fsl_tty_i3c_probe(struct i3c_device *i3cdev)
+{
+	struct ttyi3c_port *port;
+	struct i3c_ibi_setup req;
+	int ret;
+
+	port = devm_kzalloc(&i3cdev->dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->i3cdev = i3cdev;
+	port->port.ops = &ttyi3c_pops;
+	port->port.type = PORT_I3C;
+	port->port.dev = &i3cdev->dev;
+	port->buffer = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
+
+	req.max_payload_len = 8;
+	req.num_slots = 4;
+	req.handler = &i3c_controller_irq_handler;
+
+	dev_set_drvdata(&i3cdev->dev, port);
+
+	INIT_WORK(&port->work, fsl_tty_i3c_work);
+	port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&i3cdev->dev));
+	if (!port->workqueue)
+		return -ENOMEM;
+
+	ret = uart_register_driver(&ttyi3c_reg);
+	if (ret)
+		return ret;
+
+	ret = uart_add_one_port(&ttyi3c_reg, &port->port);
+	if (ret)
+		goto err_add_one_port;
+
+	ret = i3c_device_request_ibi(i3cdev, &req);
+	if (ret)
+		goto err_request_ibi;
+
+	ret = i3c_device_enable_ibi(i3cdev);
+	if (ret)
+		goto  err_enable_ibi;
+
+	return 0;
+
+err_enable_ibi:
+	i3c_device_free_ibi(i3cdev);
+err_request_ibi:
+	uart_remove_one_port(&ttyi3c_reg, &port->port);
+err_add_one_port:
+	uart_unregister_driver(&ttyi3c_reg);
+
+	return ret;
+}
+
+static struct i3c_driver fsl_tty_i3c_driver = {
+	.driver = {
+		.name = "ttyi3c",
+	},
+	.probe = fsl_tty_i3c_probe,
+	.id_table = fsl_tty_i3c_ids,
+};
+
+module_i3c_driver(fsl_tty_i3c_driver);
+MODULE_LICENSE("GPL");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 9/9] Documentation: i3c: Add I3C slave mode controller and function
  2023-09-05 21:38 ` Frank Li
@ 2023-09-05 21:38   ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

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

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 Documentation/driver-api/i3c/index.rst        |   1 +
 .../driver-api/i3c/slave/i3c-slave-cfs.rst    | 109 ++++++++++
 .../driver-api/i3c/slave/i3c-slave.rst        | 189 ++++++++++++++++++
 .../driver-api/i3c/slave/i3c-tty-function.rst | 103 ++++++++++
 .../driver-api/i3c/slave/i3c-tty-howto.rst    | 109 ++++++++++
 Documentation/driver-api/i3c/slave/index.rst  |  13 ++
 6 files changed, 524 insertions(+)
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-function.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
 create mode 100644 Documentation/driver-api/i3c/slave/index.rst

diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
index 783d6dad054b6..63fc51fc8bd58 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
+   slave/index
diff --git a/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst b/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
new file mode 100644
index 0000000000000..d78fcbc4e5587
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================
+Configuring I3C Slave Using CONFIGFS
+=======================================
+
+:Author: Frank Li <Frank.Li@nxp.com>
+
+The I3C Slave Core exposes configfs entry (i3c_slave) to configure the I3C
+slave function and to bind the slave function
+with the slave controller. (For introducing other mechanisms to
+configure the I3C Slave Function refer to [1]).
+
+Mounting configfs
+=================
+
+The I3C Slave Core layer creates i3c_slave 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_slave 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_slave/
+		.. 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_slave/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 slave 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_slave/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 slave 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/slave/pci-slave.rst
diff --git a/Documentation/driver-api/i3c/slave/i3c-slave.rst b/Documentation/driver-api/i3c/slave/i3c-slave.rst
new file mode 100644
index 0000000000000..363421241b594
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-slave.rst
@@ -0,0 +1,189 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Author: Frank Li <Frank.Li@nxp.com>
+
+This document is a guide to use the I3C Slave Framework in order to create
+slave controller driver, slave 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 Slave mode. I3C Slave Framework will add slave mode
+support in Linux. This will help to run Linux in an slave system which can
+have a wide variety of use cases from testing or validation, co-processor
+accelerator, etc.
+
+I3C Slave Core
+=================
+
+The I3C Slave Core layer comprises 3 components: the Slave Controller
+library, the Slave Function library, and the configfs layer to bind the
+slave function with the slave controller.
+
+I3C Slave Controller Library
+------------------------------------
+
+The Controller library provides APIs to be used by the controller that can
+operate in slave mode. It also provides APIs to be used by function
+driver/library in order to implement a particular slave function.
+
+APIs for the I3C Slave controller Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C controller driver.
+
+* devm_i3c_slave_ctrl_create()/i3c_slave_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_slave_ctrl_create()/i3c_slave_ctrl_create().
+
+* devm_i3c_slave_ctrl_destroy()/i3c_slave_ctrl_destroy()
+
+   The I3C controller driver can destroy the Controller device created by
+   either devm_i3c_slave_ctrl_create() or i3c_slave_ctrl_create() using
+   devm_i3c_slave_ctrl_destroy() or i3c_slave_ctrl_destroy().
+
+I3C Slave Controller APIs for the I3C Slave Function Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave function driver.
+
+* i3c_slave_ctrl_set_config()
+
+   The I3C slave function driver should use i3c_slave_ctrl_set_config() to
+   write i3c configuration to the slave controller.
+
+* i3c_slave_ctrl_enable()/i3c_slave_ctrl_disable()
+
+   The I3C slave function driver should use i3c_slave_ctrl_enable()/
+   i3c_slave_ctrl_disable() to enable/disable i3c slave controller.
+
+* i3c_slave_ctrl_alloc_request()/i3c_slave_ctrl_free_request()
+
+   The I3C slave function driver should usei3c_slave_ctrl_alloc_request() /
+   i3c_slave_ctrl_free_request() to alloc/free a i3c request.
+
+* i3c_slave_ctrl_raise_ibi()
+
+   The I3C slave function driver should use i3c_slave_ctrl_raise_ibi() to
+   raise IBI.
+
+* i3c_slave_ctrl_queue()/i3c_slave_ctrl_dequeue()
+
+   The I3C slave function driver should use i3c_slave_ctrl_queue()/
+   i3c_slave_ctrl_dequeue(), to queue/dequeue I3C transfer to/from transfer
+   queue.
+
+* i3c_slave_ctrl_get_features()
+
+   The I3C slave function driver should use i3c_slave_ctrl_get_features()
+   to get I3C slave controller supported features.
+
+Other I3C Slave Controller APIs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are other APIs provided by the Controller library. These are used for
+binding the I3C Slave Function device with Controlller device. i3c-cfs.c can
+be used as reference for using these APIs.
+
+* i3c_slave_ctrl_get()
+
+   Get a reference to the I3C slave controller based on the device name of
+   the controller.
+
+* i3c_slave_ctrl_put()
+
+   Release the reference to the I3C slave controller obtained using
+   i3c_slave_ctrl_get()
+
+* i3c_slave_ctrl_add_func()
+
+   Add a I3C slave function to a I3C slave controller.
+
+* i3c_slave_ctrl_remove_func()
+
+   Remove the I3C slave function from I3C slave controller.
+
+I3C Slave Function Library
+----------------------------------
+
+The I3C Slave Function library provides APIs to be used by the function driver
+and the Controller library to provide slave mode functionality.
+
+I3C Slave Function APIs for the I3C Slave Function Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave function driver.
+
+* i3c_slave_func_register_driver()
+
+   The I3C Slave 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_slave_func_register_driver().
+
+* i3c_slave_func_unregister_driver()
+
+  The I3C Function driver can unregister the I3C Function driver by using
+  i3c_epf_unregister_driver().
+
+APIs for the I3C Slave Controller Library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave controller library.
+
+Other I3C Slave 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_slave_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_slave_func_destroy()
+
+   Destroy the created I3C Function device.
+
+* i3c_slave_func_bind()
+
+   i3c_slave_func_bind() should be invoked when the EPF device has been bound
+   to a Controller device.
+
+* i3c_slave_func_unbind()
+
+   i3c_slave_func_unbind() should be invoked when the binding between EPC
+   device and function device is lost.
diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-function.rst b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst
new file mode 100644
index 0000000000000..3c8521d7aa31a
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst
@@ -0,0 +1,103 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+PCI Test Function
+=================
+
+:Author: Kishon Vijay Abraham I <kishon@ti.com>
+
+Traditionally PCI RC has always been validated by using standard
+PCI cards like ethernet PCI cards or USB PCI cards or SATA PCI cards.
+However with the addition of EP-core in linux kernel, it is possible
+to configure a PCI controller that can operate in EP mode to work as
+a test device.
+
+The PCI endpoint test device is a virtual device (defined in software)
+used to test the endpoint functionality and serve as a sample driver
+for other PCI endpoint devices (to use the EP framework).
+
+The PCI endpoint test device has the following registers:
+
+	1) PCI_ENDPOINT_TEST_MAGIC
+	2) PCI_ENDPOINT_TEST_COMMAND
+	3) PCI_ENDPOINT_TEST_STATUS
+	4) PCI_ENDPOINT_TEST_SRC_ADDR
+	5) PCI_ENDPOINT_TEST_DST_ADDR
+	6) PCI_ENDPOINT_TEST_SIZE
+	7) PCI_ENDPOINT_TEST_CHECKSUM
+	8) PCI_ENDPOINT_TEST_IRQ_TYPE
+	9) PCI_ENDPOINT_TEST_IRQ_NUMBER
+
+* PCI_ENDPOINT_TEST_MAGIC
+
+This register will be used to test BAR0. A known pattern will be written
+and read back from MAGIC register to verify BAR0.
+
+* PCI_ENDPOINT_TEST_COMMAND
+
+This register will be used by the host driver to indicate the function
+that the endpoint device must perform.
+
+========	================================================================
+Bitfield	Description
+========	================================================================
+Bit 0		raise legacy IRQ
+Bit 1		raise MSI IRQ
+Bit 2		raise MSI-X IRQ
+Bit 3		read command (read data from RC buffer)
+Bit 4		write command (write data to RC buffer)
+Bit 5		copy command (copy data from one RC buffer to another RC buffer)
+========	================================================================
+
+* PCI_ENDPOINT_TEST_STATUS
+
+This register reflects the status of the PCI endpoint device.
+
+========	==============================
+Bitfield	Description
+========	==============================
+Bit 0		read success
+Bit 1		read fail
+Bit 2		write success
+Bit 3		write fail
+Bit 4		copy success
+Bit 5		copy fail
+Bit 6		IRQ raised
+Bit 7		source address is invalid
+Bit 8		destination address is invalid
+========	==============================
+
+* PCI_ENDPOINT_TEST_SRC_ADDR
+
+This register contains the source address (RC buffer address) for the
+COPY/READ command.
+
+* PCI_ENDPOINT_TEST_DST_ADDR
+
+This register contains the destination address (RC buffer address) for
+the COPY/WRITE command.
+
+* PCI_ENDPOINT_TEST_IRQ_TYPE
+
+This register contains the interrupt type (Legacy/MSI) triggered
+for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands.
+
+Possible types:
+
+======	==
+Legacy	0
+MSI	1
+MSI-X	2
+======	==
+
+* PCI_ENDPOINT_TEST_IRQ_NUMBER
+
+This register contains the triggered ID interrupt.
+
+Admissible values:
+
+======	===========
+Legacy	0
+MSI	[1 .. 32]
+MSI-X	[1 .. 2048]
+======	===========
diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
new file mode 100644
index 0000000000000..11c8900fd16f3
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+I3C TTY User Guide
+===================
+
+:Author: Frank Li <Frank.Li@nxp.com>
+
+This document is a guide to help users use i3c-slave-tty function driver
+and i3ctty master driver for testing I3C. The list of steps to be followed in the
+master side and slave side is given below.
+
+Endpoint Device
+===============
+
+Endpoint Controller Devices
+---------------------------
+
+To find the list of slave controller devices in the system::
+
+	# ls  /sys/class/i3c_slave/
+	  44330000.i3c-slave
+
+If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
+
+	# ls /sys/kernel/config/i3c_slave/controllers/
+	  44330000.i3c-slave
+
+
+Endpoint Function Drivers
+-------------------------
+
+To find the list of slave function drivers in the system::
+
+	# ls /sys/bus/i3c_slave_func/drivers
+	  tty
+
+If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
+
+	# ls /sys/kernel/config/i3c_slave/functions
+	  tty
+
+
+Creating i3c-slave-tty Device
+----------------------------
+
+I3C slave function device can be created using the configfs. To create
+i3c-slave-tty device, the following commands can be used::
+
+	# mount -t configfs none /sys/kernel/config
+	# cd /sys/kernel/config/i3c_slave/
+	# mkdir functions/tty/func1
+
+The "mkdir func1" above creates the i3c-slave-tty function device that will
+be probed by i3c tty driver.
+
+The I3C slave 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 slave function driver populates these entries with default values
+when the device is bound to the driver. The i3c-slave-tty driver populates
+vendorid with 0xffff and interrupt_pin with 0x0001::
+
+	# cat functions/tty/func1/vendor_id
+	  0x0
+
+Configuring i3c-slave-tty Device
+-------------------------------
+
+The user can configure the i3c-slave-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-slave-tty Device to slave Controller
+------------------------------------------------
+
+In order for the slave function device to be useful, it has to be bound to
+a I3C slave 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-slave/
+
+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 Slave TTY function Device
+-----------------------------------
+
+Host side:
+	cat /dev/ttyI3C0
+Slave side
+	echo abc >/dev/ttyI3C0
+
+You will see "abc" show at console.
+
+You can use other tty tool to test I3C slave tty device.
diff --git a/Documentation/driver-api/i3c/slave/index.rst b/Documentation/driver-api/i3c/slave/index.rst
new file mode 100644
index 0000000000000..69727ccf985db
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/index.rst
@@ -0,0 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+I3C Slave Framework
+======================
+
+.. toctree::
+   :maxdepth: 2
+
+   i3c-slave
+   i3c-slave-cfs
+   i3c-tty-howto
+
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [RFC PATCH 9/9] Documentation: i3c: Add I3C slave mode controller and function
@ 2023-09-05 21:38   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-05 21:38 UTC (permalink / raw)
  To: miquel.raynal
  Cc: Frank.Li, alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

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

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 Documentation/driver-api/i3c/index.rst        |   1 +
 .../driver-api/i3c/slave/i3c-slave-cfs.rst    | 109 ++++++++++
 .../driver-api/i3c/slave/i3c-slave.rst        | 189 ++++++++++++++++++
 .../driver-api/i3c/slave/i3c-tty-function.rst | 103 ++++++++++
 .../driver-api/i3c/slave/i3c-tty-howto.rst    | 109 ++++++++++
 Documentation/driver-api/i3c/slave/index.rst  |  13 ++
 6 files changed, 524 insertions(+)
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-function.rst
 create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
 create mode 100644 Documentation/driver-api/i3c/slave/index.rst

diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
index 783d6dad054b6..63fc51fc8bd58 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
+   slave/index
diff --git a/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst b/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
new file mode 100644
index 0000000000000..d78fcbc4e5587
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================
+Configuring I3C Slave Using CONFIGFS
+=======================================
+
+:Author: Frank Li <Frank.Li@nxp.com>
+
+The I3C Slave Core exposes configfs entry (i3c_slave) to configure the I3C
+slave function and to bind the slave function
+with the slave controller. (For introducing other mechanisms to
+configure the I3C Slave Function refer to [1]).
+
+Mounting configfs
+=================
+
+The I3C Slave Core layer creates i3c_slave 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_slave 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_slave/
+		.. 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_slave/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 slave 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_slave/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 slave 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/slave/pci-slave.rst
diff --git a/Documentation/driver-api/i3c/slave/i3c-slave.rst b/Documentation/driver-api/i3c/slave/i3c-slave.rst
new file mode 100644
index 0000000000000..363421241b594
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-slave.rst
@@ -0,0 +1,189 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Author: Frank Li <Frank.Li@nxp.com>
+
+This document is a guide to use the I3C Slave Framework in order to create
+slave controller driver, slave 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 Slave mode. I3C Slave Framework will add slave mode
+support in Linux. This will help to run Linux in an slave system which can
+have a wide variety of use cases from testing or validation, co-processor
+accelerator, etc.
+
+I3C Slave Core
+=================
+
+The I3C Slave Core layer comprises 3 components: the Slave Controller
+library, the Slave Function library, and the configfs layer to bind the
+slave function with the slave controller.
+
+I3C Slave Controller Library
+------------------------------------
+
+The Controller library provides APIs to be used by the controller that can
+operate in slave mode. It also provides APIs to be used by function
+driver/library in order to implement a particular slave function.
+
+APIs for the I3C Slave controller Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C controller driver.
+
+* devm_i3c_slave_ctrl_create()/i3c_slave_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_slave_ctrl_create()/i3c_slave_ctrl_create().
+
+* devm_i3c_slave_ctrl_destroy()/i3c_slave_ctrl_destroy()
+
+   The I3C controller driver can destroy the Controller device created by
+   either devm_i3c_slave_ctrl_create() or i3c_slave_ctrl_create() using
+   devm_i3c_slave_ctrl_destroy() or i3c_slave_ctrl_destroy().
+
+I3C Slave Controller APIs for the I3C Slave Function Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave function driver.
+
+* i3c_slave_ctrl_set_config()
+
+   The I3C slave function driver should use i3c_slave_ctrl_set_config() to
+   write i3c configuration to the slave controller.
+
+* i3c_slave_ctrl_enable()/i3c_slave_ctrl_disable()
+
+   The I3C slave function driver should use i3c_slave_ctrl_enable()/
+   i3c_slave_ctrl_disable() to enable/disable i3c slave controller.
+
+* i3c_slave_ctrl_alloc_request()/i3c_slave_ctrl_free_request()
+
+   The I3C slave function driver should usei3c_slave_ctrl_alloc_request() /
+   i3c_slave_ctrl_free_request() to alloc/free a i3c request.
+
+* i3c_slave_ctrl_raise_ibi()
+
+   The I3C slave function driver should use i3c_slave_ctrl_raise_ibi() to
+   raise IBI.
+
+* i3c_slave_ctrl_queue()/i3c_slave_ctrl_dequeue()
+
+   The I3C slave function driver should use i3c_slave_ctrl_queue()/
+   i3c_slave_ctrl_dequeue(), to queue/dequeue I3C transfer to/from transfer
+   queue.
+
+* i3c_slave_ctrl_get_features()
+
+   The I3C slave function driver should use i3c_slave_ctrl_get_features()
+   to get I3C slave controller supported features.
+
+Other I3C Slave Controller APIs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are other APIs provided by the Controller library. These are used for
+binding the I3C Slave Function device with Controlller device. i3c-cfs.c can
+be used as reference for using these APIs.
+
+* i3c_slave_ctrl_get()
+
+   Get a reference to the I3C slave controller based on the device name of
+   the controller.
+
+* i3c_slave_ctrl_put()
+
+   Release the reference to the I3C slave controller obtained using
+   i3c_slave_ctrl_get()
+
+* i3c_slave_ctrl_add_func()
+
+   Add a I3C slave function to a I3C slave controller.
+
+* i3c_slave_ctrl_remove_func()
+
+   Remove the I3C slave function from I3C slave controller.
+
+I3C Slave Function Library
+----------------------------------
+
+The I3C Slave Function library provides APIs to be used by the function driver
+and the Controller library to provide slave mode functionality.
+
+I3C Slave Function APIs for the I3C Slave Function Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave function driver.
+
+* i3c_slave_func_register_driver()
+
+   The I3C Slave 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_slave_func_register_driver().
+
+* i3c_slave_func_unregister_driver()
+
+  The I3C Function driver can unregister the I3C Function driver by using
+  i3c_epf_unregister_driver().
+
+APIs for the I3C Slave Controller Library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave controller library.
+
+Other I3C Slave 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_slave_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_slave_func_destroy()
+
+   Destroy the created I3C Function device.
+
+* i3c_slave_func_bind()
+
+   i3c_slave_func_bind() should be invoked when the EPF device has been bound
+   to a Controller device.
+
+* i3c_slave_func_unbind()
+
+   i3c_slave_func_unbind() should be invoked when the binding between EPC
+   device and function device is lost.
diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-function.rst b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst
new file mode 100644
index 0000000000000..3c8521d7aa31a
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst
@@ -0,0 +1,103 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+PCI Test Function
+=================
+
+:Author: Kishon Vijay Abraham I <kishon@ti.com>
+
+Traditionally PCI RC has always been validated by using standard
+PCI cards like ethernet PCI cards or USB PCI cards or SATA PCI cards.
+However with the addition of EP-core in linux kernel, it is possible
+to configure a PCI controller that can operate in EP mode to work as
+a test device.
+
+The PCI endpoint test device is a virtual device (defined in software)
+used to test the endpoint functionality and serve as a sample driver
+for other PCI endpoint devices (to use the EP framework).
+
+The PCI endpoint test device has the following registers:
+
+	1) PCI_ENDPOINT_TEST_MAGIC
+	2) PCI_ENDPOINT_TEST_COMMAND
+	3) PCI_ENDPOINT_TEST_STATUS
+	4) PCI_ENDPOINT_TEST_SRC_ADDR
+	5) PCI_ENDPOINT_TEST_DST_ADDR
+	6) PCI_ENDPOINT_TEST_SIZE
+	7) PCI_ENDPOINT_TEST_CHECKSUM
+	8) PCI_ENDPOINT_TEST_IRQ_TYPE
+	9) PCI_ENDPOINT_TEST_IRQ_NUMBER
+
+* PCI_ENDPOINT_TEST_MAGIC
+
+This register will be used to test BAR0. A known pattern will be written
+and read back from MAGIC register to verify BAR0.
+
+* PCI_ENDPOINT_TEST_COMMAND
+
+This register will be used by the host driver to indicate the function
+that the endpoint device must perform.
+
+========	================================================================
+Bitfield	Description
+========	================================================================
+Bit 0		raise legacy IRQ
+Bit 1		raise MSI IRQ
+Bit 2		raise MSI-X IRQ
+Bit 3		read command (read data from RC buffer)
+Bit 4		write command (write data to RC buffer)
+Bit 5		copy command (copy data from one RC buffer to another RC buffer)
+========	================================================================
+
+* PCI_ENDPOINT_TEST_STATUS
+
+This register reflects the status of the PCI endpoint device.
+
+========	==============================
+Bitfield	Description
+========	==============================
+Bit 0		read success
+Bit 1		read fail
+Bit 2		write success
+Bit 3		write fail
+Bit 4		copy success
+Bit 5		copy fail
+Bit 6		IRQ raised
+Bit 7		source address is invalid
+Bit 8		destination address is invalid
+========	==============================
+
+* PCI_ENDPOINT_TEST_SRC_ADDR
+
+This register contains the source address (RC buffer address) for the
+COPY/READ command.
+
+* PCI_ENDPOINT_TEST_DST_ADDR
+
+This register contains the destination address (RC buffer address) for
+the COPY/WRITE command.
+
+* PCI_ENDPOINT_TEST_IRQ_TYPE
+
+This register contains the interrupt type (Legacy/MSI) triggered
+for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands.
+
+Possible types:
+
+======	==
+Legacy	0
+MSI	1
+MSI-X	2
+======	==
+
+* PCI_ENDPOINT_TEST_IRQ_NUMBER
+
+This register contains the triggered ID interrupt.
+
+Admissible values:
+
+======	===========
+Legacy	0
+MSI	[1 .. 32]
+MSI-X	[1 .. 2048]
+======	===========
diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
new file mode 100644
index 0000000000000..11c8900fd16f3
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+I3C TTY User Guide
+===================
+
+:Author: Frank Li <Frank.Li@nxp.com>
+
+This document is a guide to help users use i3c-slave-tty function driver
+and i3ctty master driver for testing I3C. The list of steps to be followed in the
+master side and slave side is given below.
+
+Endpoint Device
+===============
+
+Endpoint Controller Devices
+---------------------------
+
+To find the list of slave controller devices in the system::
+
+	# ls  /sys/class/i3c_slave/
+	  44330000.i3c-slave
+
+If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
+
+	# ls /sys/kernel/config/i3c_slave/controllers/
+	  44330000.i3c-slave
+
+
+Endpoint Function Drivers
+-------------------------
+
+To find the list of slave function drivers in the system::
+
+	# ls /sys/bus/i3c_slave_func/drivers
+	  tty
+
+If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
+
+	# ls /sys/kernel/config/i3c_slave/functions
+	  tty
+
+
+Creating i3c-slave-tty Device
+----------------------------
+
+I3C slave function device can be created using the configfs. To create
+i3c-slave-tty device, the following commands can be used::
+
+	# mount -t configfs none /sys/kernel/config
+	# cd /sys/kernel/config/i3c_slave/
+	# mkdir functions/tty/func1
+
+The "mkdir func1" above creates the i3c-slave-tty function device that will
+be probed by i3c tty driver.
+
+The I3C slave 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 slave function driver populates these entries with default values
+when the device is bound to the driver. The i3c-slave-tty driver populates
+vendorid with 0xffff and interrupt_pin with 0x0001::
+
+	# cat functions/tty/func1/vendor_id
+	  0x0
+
+Configuring i3c-slave-tty Device
+-------------------------------
+
+The user can configure the i3c-slave-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-slave-tty Device to slave Controller
+------------------------------------------------
+
+In order for the slave function device to be useful, it has to be bound to
+a I3C slave 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-slave/
+
+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 Slave TTY function Device
+-----------------------------------
+
+Host side:
+	cat /dev/ttyI3C0
+Slave side
+	echo abc >/dev/ttyI3C0
+
+You will see "abc" show at console.
+
+You can use other tty tool to test I3C slave tty device.
diff --git a/Documentation/driver-api/i3c/slave/index.rst b/Documentation/driver-api/i3c/slave/index.rst
new file mode 100644
index 0000000000000..69727ccf985db
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/index.rst
@@ -0,0 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+I3C Slave Framework
+======================
+
+.. toctree::
+   :maxdepth: 2
+
+   i3c-slave
+   i3c-slave-cfs
+   i3c-tty-howto
+
-- 
2.34.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
  2023-09-05 21:38   ` Frank Li
@ 2023-09-06  8:01     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 32+ messages in thread
From: Krzysztof Kozlowski @ 2023-09-06  8:01 UTC (permalink / raw)
  To: Frank Li, miquel.raynal
  Cc: alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

On 05/09/2023 23:38, Frank Li wrote:
> Add compatible string 'silvaco,i3c-slave-v1' for slave mode.
> 

No, it's the same device.

Anyway, this was not tested.

Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC. It might happen, that command when run on an older
kernel, gives you outdated entries. Therefore please be sure you base
your patches on recent Linux kernel.

You missed at least devicetree list (maybe more), so this won't be
tested by automated tooling. Performing review on untested code might be
a waste of time, thus I will skip this patch entirely till you follow
the process allowing the patch to be tested.

Please kindly resend and include all necessary To/Cc entries.


Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
@ 2023-09-06  8:01     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 32+ messages in thread
From: Krzysztof Kozlowski @ 2023-09-06  8:01 UTC (permalink / raw)
  To: Frank Li, miquel.raynal
  Cc: alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

On 05/09/2023 23:38, Frank Li wrote:
> Add compatible string 'silvaco,i3c-slave-v1' for slave mode.
> 

No, it's the same device.

Anyway, this was not tested.

Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC. It might happen, that command when run on an older
kernel, gives you outdated entries. Therefore please be sure you base
your patches on recent Linux kernel.

You missed at least devicetree list (maybe more), so this won't be
tested by automated tooling. Performing review on untested code might be
a waste of time, thus I will skip this patch entirely till you follow
the process allowing the patch to be tested.

Please kindly resend and include all necessary To/Cc entries.


Best regards,
Krzysztof


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 8/9] tty: serial: add tty over I3C master side driver
  2023-09-05 21:38   ` Frank Li
@ 2023-09-06  8:02     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 32+ messages in thread
From: Krzysztof Kozlowski @ 2023-09-06  8:02 UTC (permalink / raw)
  To: Frank Li, miquel.raynal
  Cc: alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

On 05/09/2023 23:38, Frank Li wrote:
> This is master side tty driver for tty over i3c.
> 
> Signed-off-by: Frank Li <Frank.Li@nxp.com>

Why do you skip Cc-ing maintainers?

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 8/9] tty: serial: add tty over I3C master side driver
@ 2023-09-06  8:02     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 32+ messages in thread
From: Krzysztof Kozlowski @ 2023-09-06  8:02 UTC (permalink / raw)
  To: Frank Li, miquel.raynal
  Cc: alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel

On 05/09/2023 23:38, Frank Li wrote:
> This is master side tty driver for tty over i3c.
> 
> Signed-off-by: Frank Li <Frank.Li@nxp.com>

Why do you skip Cc-ing maintainers?

Best regards,
Krzysztof


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
  2023-09-06  8:01     ` Krzysztof Kozlowski
@ 2023-09-07 14:28       ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-07 14:28 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: miquel.raynal, alexandre.belloni, conor.culhane, imx, linux-i3c,
	linux-kernel

On Wed, Sep 06, 2023 at 10:01:40AM +0200, Krzysztof Kozlowski wrote:
> On 05/09/2023 23:38, Frank Li wrote:
> > Add compatible string 'silvaco,i3c-slave-v1' for slave mode.
> > 
> 
> No, it's the same device.
> 
> Anyway, this was not tested.
> 
> Please use scripts/get_maintainers.pl to get a list of necessary people
> and lists to CC. It might happen, that command when run on an older
> kernel, gives you outdated entries. Therefore please be sure you base
> your patches on recent Linux kernel.
> 
> You missed at least devicetree list (maybe more), so this won't be
> tested by automated tooling. Performing review on untested code might be
> a waste of time, thus I will skip this patch entirely till you follow
> the process allowing the patch to be tested.
> 
> Please kindly resend and include all necessary To/Cc entries.

Thank you for your comments. I write notes at RFC cover letter, this
patches is not totally completed yet. I want to get more feedback about
i3c slave mode API and configfs design, which will impact the overall
design.

So I have not included tty mail list and you in review list.
I send out all patches just because let i3c reviewer know how to use these
API. 

Frank

> 
> 
> Best regards,
> Krzysztof
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
@ 2023-09-07 14:28       ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-07 14:28 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: miquel.raynal, alexandre.belloni, conor.culhane, imx, linux-i3c,
	linux-kernel

On Wed, Sep 06, 2023 at 10:01:40AM +0200, Krzysztof Kozlowski wrote:
> On 05/09/2023 23:38, Frank Li wrote:
> > Add compatible string 'silvaco,i3c-slave-v1' for slave mode.
> > 
> 
> No, it's the same device.
> 
> Anyway, this was not tested.
> 
> Please use scripts/get_maintainers.pl to get a list of necessary people
> and lists to CC. It might happen, that command when run on an older
> kernel, gives you outdated entries. Therefore please be sure you base
> your patches on recent Linux kernel.
> 
> You missed at least devicetree list (maybe more), so this won't be
> tested by automated tooling. Performing review on untested code might be
> a waste of time, thus I will skip this patch entirely till you follow
> the process allowing the patch to be tested.
> 
> Please kindly resend and include all necessary To/Cc entries.

Thank you for your comments. I write notes at RFC cover letter, this
patches is not totally completed yet. I want to get more feedback about
i3c slave mode API and configfs design, which will impact the overall
design.

So I have not included tty mail list and you in review list.
I send out all patches just because let i3c reviewer know how to use these
API. 

Frank

> 
> 
> Best regards,
> Krzysztof
> 

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 5/9] i3c: add slave mode support
  2023-09-05 21:38   ` Frank Li
@ 2023-09-11 11:14     ` Joshua Yeong
  -1 siblings, 0 replies; 32+ messages in thread
From: Joshua Yeong @ 2023-09-11 11:14 UTC (permalink / raw)
  To: Frank Li, miquel.raynal
  Cc: alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel


 >--- a/drivers/i3c/Makefile
 >+++ b/drivers/i3c/Makefile
 >@@ -2,3 +2,5 @@
  >i3c-y                := device.o master.o
 > obj-$(CONFIG_I3C)        += i3c.o
 > obj-$(CONFIG_I3C)        += master/
 >+obj-$(CONFIG_I3C_SLAVE)        += slave.o
 >+obj-$(CONFIG_I3C_SLAVE_CONFIGFS)    += i3c-cfs.o

Shall we consider to include the content of i3c-cfs.c part of slave.c ?


On 06-Sep-23 5:38 AM, Frank Li wrote:
> Introduce a new slave core layer in order to support slave functions in
> linux kernel. This comprises the controller library and function library.
> Controller library implements functions specific to an slave controller
> and function library implements functions specific to an slave function.
>
> Introduce a new configfs entry to configure the slave function configuring
> and bind the slave function with slave controller.
>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
> ---
>   drivers/i3c/Kconfig       |  29 +++
>   drivers/i3c/Makefile      |   2 +
>   drivers/i3c/i3c-cfs.c     | 361 ++++++++++++++++++++++++++++++
>   drivers/i3c/slave.c       | 441 ++++++++++++++++++++++++++++++++++++
>   include/linux/i3c/slave.h | 458 ++++++++++++++++++++++++++++++++++++++
>   5 files changed, 1291 insertions(+)
>   create mode 100644 drivers/i3c/i3c-cfs.c
>   create mode 100644 drivers/i3c/slave.c
>   create mode 100644 include/linux/i3c/slave.h
>
> diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
> index 30a441506f61c..bdc173bc0da12 100644
> --- a/drivers/i3c/Kconfig
> +++ b/drivers/i3c/Kconfig
> @@ -22,3 +22,32 @@ menuconfig I3C
>   if I3C
>   source "drivers/i3c/master/Kconfig"
>   endif # I3C
> +
> +config I3C_SLAVE
> +	bool "I3C Slave Support"
> +	help
> +	  Support I3C Slave Mode.
> +
> +	  Enable this configuration option to support configurable I3C slave.
> +	  This should be enabled if the platform has a I3C controller that can
> +	  operate in slave mode.
> +
> +	  Enabling this option will build the I3C slave library, which includes
> +	  slave controller library and slave function library.
> +
> +	  If in doubt, say "N" to disable slave support.
> +
> +config I3C_SLAVE_CONFIGFS
> +	bool "I3C Slave Configfs Support"
> +	depends on I3C_SLAVE
> +	select CONFIGFS_FS
> +	help
> +	  Configfs entry for slave function and controller.
> +
> +	  This will enable the configfs entry that can be used to configure
> +	  the slave function and used to bind the function with a slave
> +	  controller.
> +
> +if I3C_SLAVE
> +source "drivers/i3c/slave/Kconfig"
> +endif #I#C_SLAVE
> diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
> index 11982efbc6d91..6407ddec3a4a9 100644
> --- a/drivers/i3c/Makefile
> +++ b/drivers/i3c/Makefile
> @@ -2,3 +2,5 @@
>   i3c-y				:= device.o master.o
>   obj-$(CONFIG_I3C)		+= i3c.o
>   obj-$(CONFIG_I3C)		+= master/
> +obj-$(CONFIG_I3C_SLAVE)		+= slave.o
> +obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
> diff --git a/drivers/i3c/i3c-cfs.c b/drivers/i3c/i3c-cfs.c
> new file mode 100644
> index 0000000000000..1f53fada43645
> --- /dev/null
> +++ b/drivers/i3c/i3c-cfs.c
> @@ -0,0 +1,361 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Configfs to configure the I3C Slave
> + *
> + * Copyright (C) 2023 NXP
> + * Author: Frank Li <Frank.Li@nxp.com>
> + */
> +
> +#include <linux/configfs.h>
> +#include <linux/module.h>
> +#include <linux/i3c/slave.h>
> +#include <linux/idr.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_MUTEX(functions_mutex);
> +static struct config_group *functions_group;
> +static struct config_group *controllers_group;
> +
> +struct i3c_slave_func_group {
> +	struct config_group group;
> +	struct i3c_slave_func *func;
> +};
> +
> +struct i3c_slave_ctrl_group {
> +	struct config_group group;
> +	struct i3c_slave_ctrl *ctrl;
> +};
> +
> +static inline struct i3c_slave_func_group *to_i3c_slave_func_group(struct config_item *item)
> +{
> +	return container_of(to_config_group(item), struct i3c_slave_func_group, group);
> +}
> +
> +static inline struct i3c_slave_ctrl_group *to_i3c_slave_ctrl_group(struct config_item *item)
> +{
> +	return container_of(to_config_group(item), struct i3c_slave_ctrl_group, group);
> +}
> +
> +static int i3c_slave_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg)
> +{
> +	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg);
> +	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
> +	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
> +	struct i3c_slave_func *func = func_group->func;
> +	int ret;
> +
> +	ret = i3c_slave_ctrl_add_func(ctrl, func);
> +	if (ret)
> +		return ret;
> +
> +	ret = i3c_slave_func_bind(func);
> +	if (ret) {
> +		i3c_slave_ctrl_remove_func(ctrl, func);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void i3c_slave_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg)
> +{
> +	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg->ci_parent);
> +	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
> +	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
> +	struct i3c_slave_func *func = func_group->func;
> +
> +	i3c_slave_func_unbind(func);
> +	i3c_slave_ctrl_remove_func(ctrl, func);
> +}
> +
> +static struct configfs_item_operations i3c_slave_ctrl_item_ops = {
> +	.allow_link     = i3c_slave_ctrl_func_link,
> +	.drop_link      = i3c_slave_ctrl_func_unlink,
> +};
> +
> +static const struct config_item_type i3c_slave_ctrl_type = {
> +	.ct_item_ops    = &i3c_slave_ctrl_item_ops,
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +/**
> + * i3c_slave_cfs_add_ctrl_group() - add I3C slave controller group
> + * @ctrl: I3C slave controller device
> + *
> + * Return: Pointer to struct config_group
> + */
> +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl)
> +{
> +	struct i3c_slave_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_slave_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_slave_cfs_add_ctrl_group);
> +
> +/**
> + * i3c_slave_cfs_remove_ctrl_group() - remove I3C slave controller group
> + * @group: the group to be removed
> + */
> +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group)
> +{
> +	struct i3c_slave_ctrl_group *ctrl_group;
> +
> +	if (!group)
> +		return;
> +
> +	ctrl_group = container_of(group, struct i3c_slave_ctrl_group, group);
> +	i3c_slave_ctrl_put(ctrl_group->ctrl);
> +	configfs_unregister_group(&ctrl_group->group);
> +	kfree(ctrl_group);
> +}
> +EXPORT_SYMBOL(i3c_slave_cfs_remove_ctrl_group);
> +
> +#define I3C_SLAVE_ATTR_R(_name)                                                \
> +static ssize_t i3c_slave_func_##_name##_show(struct config_item *item, char *page)    \
> +{                                                                              \
> +	struct i3c_slave_func *func = to_i3c_slave_func_group(item)->func;                     \
> +	return sysfs_emit(page, "0x%04x\n", func->_name);               \
> +}
> +
> +#define I3C_SLAVE_ATTR_W(_name, _u)                                            \
> +static ssize_t i3c_slave_func_##_name##_store(struct config_item *item,               \
> +				       const char *page, size_t len)           \
> +{                                                                              \
> +	_u val;                                                               \
> +	struct i3c_slave_func *func = to_i3c_slave_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_slave_func_, vendor_id);
> +
> +I3C_SLAVE_ATTR_R(vendor_info);
> +I3C_SLAVE_ATTR_W(vendor_info, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, vendor_info);
> +
> +I3C_SLAVE_ATTR_R(part_id);
> +I3C_SLAVE_ATTR_W(part_id, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, part_id);
> +
> +I3C_SLAVE_ATTR_R(instance_id);
> +I3C_SLAVE_ATTR_W(instance_id, u8);
> +CONFIGFS_ATTR(i3c_slave_func_, instance_id);
> +
> +I3C_SLAVE_ATTR_R(ext_id);
> +I3C_SLAVE_ATTR_W(ext_id, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, ext_id);
> +
> +I3C_SLAVE_ATTR_R(max_write_len);
> +I3C_SLAVE_ATTR_W(max_write_len, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, max_write_len);
> +
> +I3C_SLAVE_ATTR_R(max_read_len);
> +I3C_SLAVE_ATTR_W(max_read_len, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, max_read_len);
> +
> +I3C_SLAVE_ATTR_R(bcr);
> +I3C_SLAVE_ATTR_W(bcr, u8);
> +CONFIGFS_ATTR(i3c_slave_func_, bcr);
> +
> +I3C_SLAVE_ATTR_R(dcr);
> +I3C_SLAVE_ATTR_W(dcr, u8);
> +CONFIGFS_ATTR(i3c_slave_func_, dcr);
> +
> +static struct configfs_attribute *i3c_slave_func_attrs[] = {
> +	&i3c_slave_func_attr_vendor_id,
> +	&i3c_slave_func_attr_vendor_info,
> +	&i3c_slave_func_attr_part_id,
> +	&i3c_slave_func_attr_instance_id,
> +	&i3c_slave_func_attr_ext_id,
> +	&i3c_slave_func_attr_max_write_len,
> +	&i3c_slave_func_attr_max_read_len,
> +	&i3c_slave_func_attr_bcr,
> +	&i3c_slave_func_attr_dcr,
> +	NULL,
> +};
> +
> +static const struct config_item_type i3c_slave_func_type = {
> +	.ct_attrs       = i3c_slave_func_attrs,
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +static struct config_group *i3c_slave_func_make(struct config_group *group, const char *name)
> +{
> +	struct i3c_slave_func_group *func_group;
> +	struct i3c_slave_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_slave_func_type);
> +
> +	func = i3c_slave_func_create(group->cg_item.ci_name, name);
> +	if (IS_ERR(func)) {
> +		pr_err("failed to create i3c slave 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_slave_func_drop(struct config_group *group, struct config_item *item)
> +{
> +	config_item_put(item);
> +}
> +
> +static struct configfs_group_operations i3c_slave_func_group_ops = {
> +	.make_group     = &i3c_slave_func_make,
> +	.drop_item      = &i3c_slave_func_drop,
> +};
> +
> +static const struct config_item_type i3c_slave_func_group_type = {
> +	.ct_group_ops   = &i3c_slave_func_group_ops,
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +/**
> + * i3c_slave_cfs_add_func_group() - add I3C slave function group
> + * @name: group name
> + *
> + * Return: Pointer to struct config_group
> + */
> +struct config_group *i3c_slave_cfs_add_func_group(const char *name)
> +{
> +	struct config_group *group;
> +
> +	group = configfs_register_default_group(functions_group, name,
> +						&i3c_slave_func_group_type);
> +	if (IS_ERR(group))
> +		pr_err("failed to register configfs group for %s function\n",
> +		       name);
> +
> +	return group;
> +}
> +EXPORT_SYMBOL(i3c_slave_cfs_add_func_group);
> +
> +/**
> + * i3c_slave_cfs_remove_func_group() - add I3C slave function group
> + * @group: group to be removed
> + */
> +void i3c_slave_cfs_remove_func_group(struct config_group *group)
> +{
> +	if (IS_ERR_OR_NULL(group))
> +		return;
> +
> +	configfs_unregister_default_group(group);
> +}
> +EXPORT_SYMBOL(i3c_slave_cfs_remove_func_group);
> +
> +static const struct config_item_type i3c_slave_controllers_type = {
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +static const struct config_item_type i3c_slave_functions_type = {
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +static const struct config_item_type i3c_slave_type = {
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +static struct configfs_subsystem i3c_slave_cfs_subsys = {
> +	.su_group = {
> +		.cg_item = {
> +			.ci_namebuf = "i3c_slave",
> +			.ci_type = &i3c_slave_type,
> +		},
> +	},
> +	.su_mutex = __MUTEX_INITIALIZER(i3c_slave_cfs_subsys.su_mutex),
> +};
> +
> +static int __init i3c_slave_cfs_init(void)
> +{
> +	int ret;
> +	struct config_group *root = &i3c_slave_cfs_subsys.su_group;
> +
> +	config_group_init(root);
> +
> +	ret = configfs_register_subsystem(&i3c_slave_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_slave_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_slave_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_slave_cfs_subsys);
> +
> +err:
> +	return ret;
> +}
> +module_init(i3c_slave_cfs_init);
> +
> +MODULE_DESCRIPTION("I3C FUNC CONFIGFS");
> +MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
> +
> +
> diff --git a/drivers/i3c/slave.c b/drivers/i3c/slave.c
> new file mode 100644
> index 0000000000000..edd7a2888271b
> --- /dev/null
> +++ b/drivers/i3c/slave.c
> @@ -0,0 +1,441 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * configfs to configure the I3C Slave
> + *
> + * Copyright (C) 2023 NXP
> + * Author: Frank Li <Frank.Li@nxp.com>
> + */
> +
> +#include <linux/configfs.h>
> +#include <linux/module.h>
> +#include <linux/i3c/slave.h>
> +#include <linux/idr.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_MUTEX(func_lock);
> +static struct class *i3c_slave_ctrl_class;
> +
> +static void i3c_slave_func_dev_release(struct device *dev)
> +{
> +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> +
> +	kfree(func->name);
> +	kfree(func);
> +}
> +
> +static const struct device_type i3c_slave_func_type = {
> +	.release        = i3c_slave_func_dev_release,
> +};
> +
> +static int i3c_slave_func_match_driver(struct device *dev, struct device_driver *drv)
> +{
> +	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
> +}
> +
> +static int i3c_slave_func_device_probe(struct device *dev)
> +{
> +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> +	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
> +
> +	if (!driver->probe)
> +		return -ENODEV;
> +
> +	func->driver = driver;
> +
> +	return driver->probe(func);
> +}
> +
> +static void i3c_slave_func_device_remove(struct device *dev)
> +{
> +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> +	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
> +
> +	if (driver->remove)
> +		driver->remove(func);
> +	func->driver = NULL;
> +}
> +
> +static const struct bus_type i3c_slave_func_bus_type = {
> +	.name = "i3c_slave_func",
> +	.probe = i3c_slave_func_device_probe,
> +	.remove = i3c_slave_func_device_remove,
> +	.match = i3c_slave_func_match_driver,
> +};
> +
> +static void i3c_slave_ctrl_release(struct device *dev)
> +{
> +	kfree(to_i3c_slave_ctrl(dev));
> +}
> +
> +static void devm_i3c_slave_ctrl_release(struct device *dev, void *res)
> +{
> +	struct i3c_slave_ctrl *ctrl = *(struct i3c_slave_ctrl **)res;
> +
> +	i3c_slave_ctrl_destroy(ctrl);
> +}
> +
> +struct i3c_slave_ctrl *
> +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> +		      struct module *owner)
> +{
> +	struct i3c_slave_ctrl **ptr, *ctrl;
> +
> +	ptr = devres_alloc(devm_i3c_slave_ctrl_release, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ctrl = __i3c_slave_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_slave_ctrl_match(struct device *dev, void *res, void *match_data)
> +{
> +	struct i3c_slave_ctrl **ptr = res;
> +
> +	return *ptr == match_data;
> +}
> +
> +/**
> + * __i3c_slave_ctrl_create() - create a new slave controller device
> + * @dev: device that is creating the new slave controller
> + * @ops: function pointers for performing slave controller  operations
> + * @owner: the owner of the module that creates the slave controller device
> + *
> + * Return: Pointer to struct i3c_slave_ctrl
> + */
> +struct i3c_slave_ctrl *
> +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> +			struct module *owner)
> +{
> +	struct i3c_slave_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_slave_ctrl_class;
> +	ctrl->dev.parent = dev;
> +	ctrl->dev.release = i3c_slave_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_slave_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_slave_ctrl_create);
> +
> +/**
> + * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device
> + * @dev: device that is creating the new slave controller device
> + * @ops: function pointers for performing slave controller operations
> + * @owner: the owner of the module that creates the slave controller device
> + *
> + * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it
> + * also associates the device with the i3c_slave using devres. On driver detach, release function is
> + * invoked on the devres data, then devres data is freed.
> + */
> +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl)
> +{
> +	int r;
> +
> +	r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match,
> +			   ctrl);
> +	dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
> +}
> +EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy);
> +
> +
> +
> +/**
> + * i3c_slave_ctrl_destroy() - destroy the slave controller device
> + * @ctrl: the slave controller device that has to be destroyed
> + *
> + * Invoke to destroy the I3C slave device
> + */
> +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl)
> +{
> +	i3c_slave_cfs_remove_ctrl_group(ctrl->group);
> +	device_unregister(&ctrl->dev);
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy);
> +
> +/**
> + * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller
> + * @ctrl: the controller device to which the slave function should be added
> + * @func: the slave function to be added
> + *
> + * An I3C slave device can have only one functions.
> + */
> +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> +{
> +	if (ctrl->func)
> +		return -EBUSY;
> +
> +	ctrl->func = func;
> +	func->ctrl = ctrl;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func);
> +
> +/**
> + * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller
> + * @ctrl: the controller device to which the slave function should be removed
> + * @func: the slave function to be removed
> + *
> + * An I3C slave device can have only one functions.
> + */
> +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> +{
> +	ctrl->func = NULL;
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func);
> +
> +
> +/**
> + * i3c_slave_ctrl() - get the I3C slave controller
> + * @name: device name of the slave controller
> + *
> + * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the
> + * slave controller
> + */
> +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name)
> +{
> +	int ret = -EINVAL;
> +	struct i3c_slave_ctrl *ctrl;
> +	struct device *dev;
> +	struct class_dev_iter iter;
> +
> +	class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL);
> +	while ((dev = class_dev_iter_next(&iter))) {
> +		if (strcmp(name, dev_name(dev)))
> +			continue;
> +
> +		ctrl = to_i3c_slave_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_slave_ctrl_get);
> +
> +/**
> + * i3c_slave_ctrl_put() - release the I3C endpoint controller
> + * @slave: slave returned by pci_slave_get()
> + *
> + * release the refcount the caller obtained by invoking i3c_slave_ctrl_get()
> + */
> +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl)
> +{
> +	if (!ctrl || IS_ERR(ctrl))
> +		return;
> +
> +	module_put(ctrl->ops->owner);
> +	put_device(&ctrl->dev);
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_put);
> +
> +/**
> + * i3c_slave_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_slave_func_bind(struct i3c_slave_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_slave_func_bind);
> +
> +/**
> + * i3c_slave_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_slave_func_unbind(struct i3c_slave_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_slave_func_unbind);
> +
> +/**
> + * i3c_slave_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_slave_func *i3c_slave_func_create(const char *drv_name, const char *name)
> +{
> +	struct i3c_slave_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_slave_func_bus_type;
> +	dev->type = &i3c_slave_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_slave_func_create);
> +
> +/**
> + * __i3c_slave_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_slave_func_register_driver(struct i3c_slave_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_slave_func_bus_type;
> +	driver->driver.owner = owner;
> +
> +	ret = driver_register(&driver->driver);
> +	if (ret)
> +		return ret;
> +
> +	i3c_slave_cfs_add_func_group(driver->driver.name);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(__i3c_slave_func_register_driver);
> +
> +/**
> + * i3c_slave_func_unregister_driver() - unregister the I3C function driver
> + * @driver: the I3C function driver that has to be unregistered
> + *
> + * Invoke to unregister the I3C function driver.
> + */
> +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *fd)
> +{
> +	mutex_lock(&func_lock);
> +	mutex_unlock(&func_lock);
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_func_unregister_driver);
> +
> +static int __init i3c_slave_init(void)
> +{
> +	int ret;
> +
> +	i3c_slave_ctrl_class = class_create("i3c_slave");
> +	if (IS_ERR(i3c_slave_ctrl_class)) {
> +		pr_err("failed to create i3c slave class --> %ld\n",
> +			PTR_ERR(i3c_slave_ctrl_class));
> +		return PTR_ERR(i3c_slave_ctrl_class);
> +	}
> +
> +	ret = bus_register(&i3c_slave_func_bus_type);
> +	if (ret) {
> +		class_destroy(i3c_slave_ctrl_class);
> +		pr_err("failed to register i3c slave func bus --> %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +module_init(i3c_slave_init);
> +
> +static void __exit i3c_slave_exit(void)
> +{
> +	class_destroy(i3c_slave_ctrl_class);
> +	bus_unregister(&i3c_slave_func_bus_type);
> +
> +}
> +module_exit(i3c_slave_exit);
> +
> diff --git a/include/linux/i3c/slave.h b/include/linux/i3c/slave.h
> new file mode 100644
> index 0000000000000..a4cbbfc6d6ea9
> --- /dev/null
> +++ b/include/linux/i3c/slave.h
> @@ -0,0 +1,458 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2023 NXP.
> + *
> + * Author: Frank Li <Frank.Li@nxp.com>
> + */
> +
> +#ifndef I3C_SLAVE_H
> +#define I3C_SLAVE_H
> +
> +#include <linux/device.h>
> +
> +struct i3c_slave_func;
> +struct i3c_slave_ctrl;
> +
> +/**
> + * struct i3c_slave_func_ops - set of function pointers for performing i3c slave 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_slave_func_ops {
> +	int (*bind)(struct i3c_slave_func *func);
> +	void (*unbind)(struct i3c_slave_func *func);
> +};
> +
> +/**
> + * struct i3c_slave_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
> + * @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_slave_func_driver {
> +	int (*probe)(struct i3c_slave_func *func);
> +	void (*remove)(struct i3c_slave_func *func);
> +
> +	char *name;
> +	struct device_driver driver;
> +	struct i3c_slave_func_ops *ops;
> +	struct module *owner;
> +};
> +
> +/**
> + * struct i3c_slave_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_slave_func_ops
> + * @ctrl: binded I3C controller device
> + * @is_bound: indicates if bind notification to function driver has been invoked
> + * @vednor_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: maxium write length
> + * @max_read_len: maxium read length
> + * @bcr: bus characteristics register (BCR)
> + * @dcr: device characteristics register (DCR)
> + */
> +struct i3c_slave_func {
> +	struct device dev;
> +	char *name;
> +	struct i3c_slave_func_driver *driver;
> +	struct config_group *group;
> +	/* mutex to protect against concurrent access of i3c_slave_func_ops */
> +	struct mutex lock;
> +	struct i3c_slave_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
> + * @status: transfer status
> + * @actual: how much actually transferred
> + * @ctrl: I3C slave controller associate with this request
> + * @tx: transfer direction, 1: slave to master, 0: master to slave
> + */
> +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_slave_ctrl *ctrl;
> +	bool tx;
> +};
> +
> +/**
> + * struct i3c_slave_ctrl_features - represents I3C slave controller features.
> + * @tx_fifo_sz: tx hardware fifo size
> + * @rx_fifo_sz: rx hardware fifo size
> + */
> +struct i3c_slave_ctrl_features {
> +	u32 tx_fifo_sz;
> +	u32 rx_fifo_sz;
> +};
> +
> +/**
> + * struct i3c_slave_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: rasie IBI interrupt to master
> + * @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
> + * @get_features: ops to get the features supported by the I3C slave controller
> + * @owner: the module owner containing the ops
> + */
> +struct i3c_slave_ctrl_ops {
> +	int (*set_config)(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> +	int (*enable)(struct i3c_slave_ctrl *ctrl);
> +	int (*disable)(struct i3c_slave_ctrl *ctrl);
> +	int (*raise_ibi)(struct i3c_slave_ctrl *ctrl, void *p, u8 size);
> +
> +	struct i3c_request *(*alloc_request)(struct i3c_slave_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_slave_ctrl *ctrl, bool tx);
> +
> +	int (*fifo_status)(struct i3c_slave_ctrl *ctrl, bool tx);
> +	void (*fifo_flush)(struct i3c_slave_ctrl *ctrl, bool tx);
> +
> +	const struct i3c_slave_ctrl_features *(*get_features)(struct i3c_slave_ctrl *ctrl);
> +	struct module *owner;
> +};
> +
> +/**
> + * struct i3c_slave_ctrl - represents the I3C slave device
> + * @dev: I3C slave device
> + * @ops: function pointers for performing endpoint operations
> + * @func: slave functions present in this controller device
> + * @group: configfs group representing the I3C controller device
> + */
> +struct i3c_slave_ctrl {
> +	struct device dev;
> +	const struct i3c_slave_ctrl_ops *ops;
> +	struct i3c_slave_func *func;
> +	struct config_group *group;
> +};
> +
> +/**
> + * i3c_slave_ctrl_raise_ibi() - Raise IBI to master
> + * @ctrl: I3C slave 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_slave_ctrl_raise_ibi(struct i3c_slave_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_slave_ctrl_cancel_all_reqs() - Cancel all pending request
> + * @ctrl: I3C slave controller
> + * @tx: Transfer diretion queue
> + * @size: size of optional data
> + */
> +static inline void i3c_slave_ctrl_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->cancel_all_reqs)
> +		ctrl->ops->cancel_all_reqs(ctrl, tx);
> +}
> +
> +/**
> + * i3c_slave_ctrl_set_config() - Set controller configuration
> + * @ctrl: I3C slave controller device
> + * @func: Function device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline int
> +i3c_slave_ctrl_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->set_config)
> +		return ctrl->ops->set_config(ctrl, func);
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * i3c_slave_ctrl_enable() - Enable I3C controller
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline int
> +i3c_slave_ctrl_enable(struct i3c_slave_ctrl *ctrl)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->enable)
> +		return ctrl->ops->enable(ctrl);
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * i3c_slave_ctrl_disable() - Disable I3C controller
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline int
> +i3c_slave_ctrl_disable(struct i3c_slave_ctrl *ctrl)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->disable)
> +		return ctrl->ops->disable(ctrl);
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * i3c_slave_ctrl_alloc_request() - Alloc an I3C transfer
> + * @ctrl: I3C slave 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_slave_ctrl_alloc_request(struct i3c_slave_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_slave_ctrl_free_request() - Free an I3C transfer
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline void
> +i3c_slave_ctrl_free_request(struct i3c_request *req)
> +{
> +	struct i3c_slave_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_slave_ctrl_queue() - Queue an I3C transfer
> + * @ctrl: I3C slave 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 int
> +i3c_slave_ctrl_queue(struct i3c_request *req, gfp_t gfp_flags)
> +{
> +	struct i3c_slave_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_slave_ctrl_dequeue() - Dequeue an I3C transfer
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline int
> +i3c_slave_ctrl_dequeue(struct i3c_request *req)
> +{
> +	struct i3c_slave_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_slave_ctrl_fifo_status() - Get controller FIFO status
> + * @ctrl: I3C slave controller device
> + * @tx: 1: Slave to master, 0: master to slave
> + *
> + * Returns: How much data in FIFO
> + */
> +static inline int
> +i3c_slave_ctrl_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->fifo_status)
> +		return ctrl->ops->fifo_status(ctrl, tx);
> +
> +	return 0;
> +}
> +
> +/**
> + * i3c_slave_ctrl_fifo_flush() - Flush controller FIFO
> + * @ctrl: I3C slave controller device
> + * @tx: 1: Slave to master, 0: master to slave
> + *
> + */
> +static inline void
> +i3c_slave_ctrl_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->fifo_flush)
> +		return ctrl->ops->fifo_flush(ctrl, tx);
> +}
> +
> +/**
> + * i3c_slave_ctrl_get_features() - Get controller supported features
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: The pointer to struct i3c_slave_ctrl_features
> + */
> +static inline const struct i3c_slave_ctrl_features*
> +i3c_slave_ctrl_get_features(struct i3c_slave_ctrl *ctrl)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->get_features)
> +		return ctrl->ops->get_features(ctrl);
> +
> +	return NULL;
> +}
> +
> +#define to_i3c_slave_ctrl(device) container_of((device), struct i3c_slave_ctrl, dev)
> +
> +#define to_i3c_slave_func(func_dev) container_of((func_dev), struct i3c_slave_func, dev)
> +#define to_i3c_slave_func_driver(drv) (container_of((drv), struct i3c_slave_func_driver, driver))
> +
> +#define i3c_slave_ctrl_create(dev, ops) \
> +		__i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
> +#define devm_i3c_slave_ctrl_create(dev, ops) \
> +		__devm_i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
> +
> +struct i3c_slave_ctrl *
> +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> +		      struct module *owner);
> +struct i3c_slave_ctrl *
> +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> +		 struct module *owner);
> +
> +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *epc);
> +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *epc);
> +
> +
> +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> +
> +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl);
> +
> +
> +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group);
> +struct config_group *i3c_slave_cfs_add_func_group(const char *name);
> +void i3c_slave_cfs_remove_func_group(struct config_group *group);
> +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name);
> +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl);
> +
> +int i3c_slave_func_bind(struct i3c_slave_func *func);
> +void i3c_slave_func_unbind(struct i3c_slave_func *func);
> +struct i3c_slave_func *i3c_slave_func_create(const char *drv_name, const char *name);
> +
> +#define i3c_slave_func_register_driver(drv) \
> +	__i3c_slave_func_register_driver(drv, THIS_MODULE)
> +
> +int __i3c_slave_func_register_driver(struct i3c_slave_func_driver *drv, struct module *owner);
> +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *drv);
> +
> +#define DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops)			\
> +	static struct i3c_slave_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_SLAVE_INIT(_name, _probe, _remove, _ops)			\
> +	DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops);			\
> +	static int __init _name ## mod_init(void)				\
> +	{									\
> +		return i3c_slave_func_register_driver(&_name ## i3c_func);	\
> +	}									\
> +	static void __exit _name ## mod_exit(void)				\
> +	{									\
> +		i3c_slave_func_unregister_driver(&_name ## i3c_func);		\
> +	}									\
> +	module_init(_name ## mod_init);						\
> +	module_exit(_name ## mod_exit)
> +
> +#endif
> +
> +

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 5/9] i3c: add slave mode support
@ 2023-09-11 11:14     ` Joshua Yeong
  0 siblings, 0 replies; 32+ messages in thread
From: Joshua Yeong @ 2023-09-11 11:14 UTC (permalink / raw)
  To: Frank Li, miquel.raynal
  Cc: alexandre.belloni, conor.culhane, imx, linux-i3c, linux-kernel


 >--- a/drivers/i3c/Makefile
 >+++ b/drivers/i3c/Makefile
 >@@ -2,3 +2,5 @@
  >i3c-y                := device.o master.o
 > obj-$(CONFIG_I3C)        += i3c.o
 > obj-$(CONFIG_I3C)        += master/
 >+obj-$(CONFIG_I3C_SLAVE)        += slave.o
 >+obj-$(CONFIG_I3C_SLAVE_CONFIGFS)    += i3c-cfs.o

Shall we consider to include the content of i3c-cfs.c part of slave.c ?


On 06-Sep-23 5:38 AM, Frank Li wrote:
> Introduce a new slave core layer in order to support slave functions in
> linux kernel. This comprises the controller library and function library.
> Controller library implements functions specific to an slave controller
> and function library implements functions specific to an slave function.
>
> Introduce a new configfs entry to configure the slave function configuring
> and bind the slave function with slave controller.
>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
> ---
>   drivers/i3c/Kconfig       |  29 +++
>   drivers/i3c/Makefile      |   2 +
>   drivers/i3c/i3c-cfs.c     | 361 ++++++++++++++++++++++++++++++
>   drivers/i3c/slave.c       | 441 ++++++++++++++++++++++++++++++++++++
>   include/linux/i3c/slave.h | 458 ++++++++++++++++++++++++++++++++++++++
>   5 files changed, 1291 insertions(+)
>   create mode 100644 drivers/i3c/i3c-cfs.c
>   create mode 100644 drivers/i3c/slave.c
>   create mode 100644 include/linux/i3c/slave.h
>
> diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
> index 30a441506f61c..bdc173bc0da12 100644
> --- a/drivers/i3c/Kconfig
> +++ b/drivers/i3c/Kconfig
> @@ -22,3 +22,32 @@ menuconfig I3C
>   if I3C
>   source "drivers/i3c/master/Kconfig"
>   endif # I3C
> +
> +config I3C_SLAVE
> +	bool "I3C Slave Support"
> +	help
> +	  Support I3C Slave Mode.
> +
> +	  Enable this configuration option to support configurable I3C slave.
> +	  This should be enabled if the platform has a I3C controller that can
> +	  operate in slave mode.
> +
> +	  Enabling this option will build the I3C slave library, which includes
> +	  slave controller library and slave function library.
> +
> +	  If in doubt, say "N" to disable slave support.
> +
> +config I3C_SLAVE_CONFIGFS
> +	bool "I3C Slave Configfs Support"
> +	depends on I3C_SLAVE
> +	select CONFIGFS_FS
> +	help
> +	  Configfs entry for slave function and controller.
> +
> +	  This will enable the configfs entry that can be used to configure
> +	  the slave function and used to bind the function with a slave
> +	  controller.
> +
> +if I3C_SLAVE
> +source "drivers/i3c/slave/Kconfig"
> +endif #I#C_SLAVE
> diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
> index 11982efbc6d91..6407ddec3a4a9 100644
> --- a/drivers/i3c/Makefile
> +++ b/drivers/i3c/Makefile
> @@ -2,3 +2,5 @@
>   i3c-y				:= device.o master.o
>   obj-$(CONFIG_I3C)		+= i3c.o
>   obj-$(CONFIG_I3C)		+= master/
> +obj-$(CONFIG_I3C_SLAVE)		+= slave.o
> +obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
> diff --git a/drivers/i3c/i3c-cfs.c b/drivers/i3c/i3c-cfs.c
> new file mode 100644
> index 0000000000000..1f53fada43645
> --- /dev/null
> +++ b/drivers/i3c/i3c-cfs.c
> @@ -0,0 +1,361 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Configfs to configure the I3C Slave
> + *
> + * Copyright (C) 2023 NXP
> + * Author: Frank Li <Frank.Li@nxp.com>
> + */
> +
> +#include <linux/configfs.h>
> +#include <linux/module.h>
> +#include <linux/i3c/slave.h>
> +#include <linux/idr.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_MUTEX(functions_mutex);
> +static struct config_group *functions_group;
> +static struct config_group *controllers_group;
> +
> +struct i3c_slave_func_group {
> +	struct config_group group;
> +	struct i3c_slave_func *func;
> +};
> +
> +struct i3c_slave_ctrl_group {
> +	struct config_group group;
> +	struct i3c_slave_ctrl *ctrl;
> +};
> +
> +static inline struct i3c_slave_func_group *to_i3c_slave_func_group(struct config_item *item)
> +{
> +	return container_of(to_config_group(item), struct i3c_slave_func_group, group);
> +}
> +
> +static inline struct i3c_slave_ctrl_group *to_i3c_slave_ctrl_group(struct config_item *item)
> +{
> +	return container_of(to_config_group(item), struct i3c_slave_ctrl_group, group);
> +}
> +
> +static int i3c_slave_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg)
> +{
> +	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg);
> +	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
> +	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
> +	struct i3c_slave_func *func = func_group->func;
> +	int ret;
> +
> +	ret = i3c_slave_ctrl_add_func(ctrl, func);
> +	if (ret)
> +		return ret;
> +
> +	ret = i3c_slave_func_bind(func);
> +	if (ret) {
> +		i3c_slave_ctrl_remove_func(ctrl, func);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void i3c_slave_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg)
> +{
> +	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg->ci_parent);
> +	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
> +	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
> +	struct i3c_slave_func *func = func_group->func;
> +
> +	i3c_slave_func_unbind(func);
> +	i3c_slave_ctrl_remove_func(ctrl, func);
> +}
> +
> +static struct configfs_item_operations i3c_slave_ctrl_item_ops = {
> +	.allow_link     = i3c_slave_ctrl_func_link,
> +	.drop_link      = i3c_slave_ctrl_func_unlink,
> +};
> +
> +static const struct config_item_type i3c_slave_ctrl_type = {
> +	.ct_item_ops    = &i3c_slave_ctrl_item_ops,
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +/**
> + * i3c_slave_cfs_add_ctrl_group() - add I3C slave controller group
> + * @ctrl: I3C slave controller device
> + *
> + * Return: Pointer to struct config_group
> + */
> +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl)
> +{
> +	struct i3c_slave_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_slave_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_slave_cfs_add_ctrl_group);
> +
> +/**
> + * i3c_slave_cfs_remove_ctrl_group() - remove I3C slave controller group
> + * @group: the group to be removed
> + */
> +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group)
> +{
> +	struct i3c_slave_ctrl_group *ctrl_group;
> +
> +	if (!group)
> +		return;
> +
> +	ctrl_group = container_of(group, struct i3c_slave_ctrl_group, group);
> +	i3c_slave_ctrl_put(ctrl_group->ctrl);
> +	configfs_unregister_group(&ctrl_group->group);
> +	kfree(ctrl_group);
> +}
> +EXPORT_SYMBOL(i3c_slave_cfs_remove_ctrl_group);
> +
> +#define I3C_SLAVE_ATTR_R(_name)                                                \
> +static ssize_t i3c_slave_func_##_name##_show(struct config_item *item, char *page)    \
> +{                                                                              \
> +	struct i3c_slave_func *func = to_i3c_slave_func_group(item)->func;                     \
> +	return sysfs_emit(page, "0x%04x\n", func->_name);               \
> +}
> +
> +#define I3C_SLAVE_ATTR_W(_name, _u)                                            \
> +static ssize_t i3c_slave_func_##_name##_store(struct config_item *item,               \
> +				       const char *page, size_t len)           \
> +{                                                                              \
> +	_u val;                                                               \
> +	struct i3c_slave_func *func = to_i3c_slave_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_slave_func_, vendor_id);
> +
> +I3C_SLAVE_ATTR_R(vendor_info);
> +I3C_SLAVE_ATTR_W(vendor_info, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, vendor_info);
> +
> +I3C_SLAVE_ATTR_R(part_id);
> +I3C_SLAVE_ATTR_W(part_id, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, part_id);
> +
> +I3C_SLAVE_ATTR_R(instance_id);
> +I3C_SLAVE_ATTR_W(instance_id, u8);
> +CONFIGFS_ATTR(i3c_slave_func_, instance_id);
> +
> +I3C_SLAVE_ATTR_R(ext_id);
> +I3C_SLAVE_ATTR_W(ext_id, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, ext_id);
> +
> +I3C_SLAVE_ATTR_R(max_write_len);
> +I3C_SLAVE_ATTR_W(max_write_len, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, max_write_len);
> +
> +I3C_SLAVE_ATTR_R(max_read_len);
> +I3C_SLAVE_ATTR_W(max_read_len, u16);
> +CONFIGFS_ATTR(i3c_slave_func_, max_read_len);
> +
> +I3C_SLAVE_ATTR_R(bcr);
> +I3C_SLAVE_ATTR_W(bcr, u8);
> +CONFIGFS_ATTR(i3c_slave_func_, bcr);
> +
> +I3C_SLAVE_ATTR_R(dcr);
> +I3C_SLAVE_ATTR_W(dcr, u8);
> +CONFIGFS_ATTR(i3c_slave_func_, dcr);
> +
> +static struct configfs_attribute *i3c_slave_func_attrs[] = {
> +	&i3c_slave_func_attr_vendor_id,
> +	&i3c_slave_func_attr_vendor_info,
> +	&i3c_slave_func_attr_part_id,
> +	&i3c_slave_func_attr_instance_id,
> +	&i3c_slave_func_attr_ext_id,
> +	&i3c_slave_func_attr_max_write_len,
> +	&i3c_slave_func_attr_max_read_len,
> +	&i3c_slave_func_attr_bcr,
> +	&i3c_slave_func_attr_dcr,
> +	NULL,
> +};
> +
> +static const struct config_item_type i3c_slave_func_type = {
> +	.ct_attrs       = i3c_slave_func_attrs,
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +static struct config_group *i3c_slave_func_make(struct config_group *group, const char *name)
> +{
> +	struct i3c_slave_func_group *func_group;
> +	struct i3c_slave_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_slave_func_type);
> +
> +	func = i3c_slave_func_create(group->cg_item.ci_name, name);
> +	if (IS_ERR(func)) {
> +		pr_err("failed to create i3c slave 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_slave_func_drop(struct config_group *group, struct config_item *item)
> +{
> +	config_item_put(item);
> +}
> +
> +static struct configfs_group_operations i3c_slave_func_group_ops = {
> +	.make_group     = &i3c_slave_func_make,
> +	.drop_item      = &i3c_slave_func_drop,
> +};
> +
> +static const struct config_item_type i3c_slave_func_group_type = {
> +	.ct_group_ops   = &i3c_slave_func_group_ops,
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +/**
> + * i3c_slave_cfs_add_func_group() - add I3C slave function group
> + * @name: group name
> + *
> + * Return: Pointer to struct config_group
> + */
> +struct config_group *i3c_slave_cfs_add_func_group(const char *name)
> +{
> +	struct config_group *group;
> +
> +	group = configfs_register_default_group(functions_group, name,
> +						&i3c_slave_func_group_type);
> +	if (IS_ERR(group))
> +		pr_err("failed to register configfs group for %s function\n",
> +		       name);
> +
> +	return group;
> +}
> +EXPORT_SYMBOL(i3c_slave_cfs_add_func_group);
> +
> +/**
> + * i3c_slave_cfs_remove_func_group() - add I3C slave function group
> + * @group: group to be removed
> + */
> +void i3c_slave_cfs_remove_func_group(struct config_group *group)
> +{
> +	if (IS_ERR_OR_NULL(group))
> +		return;
> +
> +	configfs_unregister_default_group(group);
> +}
> +EXPORT_SYMBOL(i3c_slave_cfs_remove_func_group);
> +
> +static const struct config_item_type i3c_slave_controllers_type = {
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +static const struct config_item_type i3c_slave_functions_type = {
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +static const struct config_item_type i3c_slave_type = {
> +	.ct_owner       = THIS_MODULE,
> +};
> +
> +static struct configfs_subsystem i3c_slave_cfs_subsys = {
> +	.su_group = {
> +		.cg_item = {
> +			.ci_namebuf = "i3c_slave",
> +			.ci_type = &i3c_slave_type,
> +		},
> +	},
> +	.su_mutex = __MUTEX_INITIALIZER(i3c_slave_cfs_subsys.su_mutex),
> +};
> +
> +static int __init i3c_slave_cfs_init(void)
> +{
> +	int ret;
> +	struct config_group *root = &i3c_slave_cfs_subsys.su_group;
> +
> +	config_group_init(root);
> +
> +	ret = configfs_register_subsystem(&i3c_slave_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_slave_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_slave_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_slave_cfs_subsys);
> +
> +err:
> +	return ret;
> +}
> +module_init(i3c_slave_cfs_init);
> +
> +MODULE_DESCRIPTION("I3C FUNC CONFIGFS");
> +MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
> +
> +
> diff --git a/drivers/i3c/slave.c b/drivers/i3c/slave.c
> new file mode 100644
> index 0000000000000..edd7a2888271b
> --- /dev/null
> +++ b/drivers/i3c/slave.c
> @@ -0,0 +1,441 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * configfs to configure the I3C Slave
> + *
> + * Copyright (C) 2023 NXP
> + * Author: Frank Li <Frank.Li@nxp.com>
> + */
> +
> +#include <linux/configfs.h>
> +#include <linux/module.h>
> +#include <linux/i3c/slave.h>
> +#include <linux/idr.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_MUTEX(func_lock);
> +static struct class *i3c_slave_ctrl_class;
> +
> +static void i3c_slave_func_dev_release(struct device *dev)
> +{
> +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> +
> +	kfree(func->name);
> +	kfree(func);
> +}
> +
> +static const struct device_type i3c_slave_func_type = {
> +	.release        = i3c_slave_func_dev_release,
> +};
> +
> +static int i3c_slave_func_match_driver(struct device *dev, struct device_driver *drv)
> +{
> +	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
> +}
> +
> +static int i3c_slave_func_device_probe(struct device *dev)
> +{
> +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> +	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
> +
> +	if (!driver->probe)
> +		return -ENODEV;
> +
> +	func->driver = driver;
> +
> +	return driver->probe(func);
> +}
> +
> +static void i3c_slave_func_device_remove(struct device *dev)
> +{
> +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> +	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
> +
> +	if (driver->remove)
> +		driver->remove(func);
> +	func->driver = NULL;
> +}
> +
> +static const struct bus_type i3c_slave_func_bus_type = {
> +	.name = "i3c_slave_func",
> +	.probe = i3c_slave_func_device_probe,
> +	.remove = i3c_slave_func_device_remove,
> +	.match = i3c_slave_func_match_driver,
> +};
> +
> +static void i3c_slave_ctrl_release(struct device *dev)
> +{
> +	kfree(to_i3c_slave_ctrl(dev));
> +}
> +
> +static void devm_i3c_slave_ctrl_release(struct device *dev, void *res)
> +{
> +	struct i3c_slave_ctrl *ctrl = *(struct i3c_slave_ctrl **)res;
> +
> +	i3c_slave_ctrl_destroy(ctrl);
> +}
> +
> +struct i3c_slave_ctrl *
> +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> +		      struct module *owner)
> +{
> +	struct i3c_slave_ctrl **ptr, *ctrl;
> +
> +	ptr = devres_alloc(devm_i3c_slave_ctrl_release, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ctrl = __i3c_slave_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_slave_ctrl_match(struct device *dev, void *res, void *match_data)
> +{
> +	struct i3c_slave_ctrl **ptr = res;
> +
> +	return *ptr == match_data;
> +}
> +
> +/**
> + * __i3c_slave_ctrl_create() - create a new slave controller device
> + * @dev: device that is creating the new slave controller
> + * @ops: function pointers for performing slave controller  operations
> + * @owner: the owner of the module that creates the slave controller device
> + *
> + * Return: Pointer to struct i3c_slave_ctrl
> + */
> +struct i3c_slave_ctrl *
> +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> +			struct module *owner)
> +{
> +	struct i3c_slave_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_slave_ctrl_class;
> +	ctrl->dev.parent = dev;
> +	ctrl->dev.release = i3c_slave_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_slave_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_slave_ctrl_create);
> +
> +/**
> + * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device
> + * @dev: device that is creating the new slave controller device
> + * @ops: function pointers for performing slave controller operations
> + * @owner: the owner of the module that creates the slave controller device
> + *
> + * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it
> + * also associates the device with the i3c_slave using devres. On driver detach, release function is
> + * invoked on the devres data, then devres data is freed.
> + */
> +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl)
> +{
> +	int r;
> +
> +	r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match,
> +			   ctrl);
> +	dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
> +}
> +EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy);
> +
> +
> +
> +/**
> + * i3c_slave_ctrl_destroy() - destroy the slave controller device
> + * @ctrl: the slave controller device that has to be destroyed
> + *
> + * Invoke to destroy the I3C slave device
> + */
> +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl)
> +{
> +	i3c_slave_cfs_remove_ctrl_group(ctrl->group);
> +	device_unregister(&ctrl->dev);
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy);
> +
> +/**
> + * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller
> + * @ctrl: the controller device to which the slave function should be added
> + * @func: the slave function to be added
> + *
> + * An I3C slave device can have only one functions.
> + */
> +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> +{
> +	if (ctrl->func)
> +		return -EBUSY;
> +
> +	ctrl->func = func;
> +	func->ctrl = ctrl;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func);
> +
> +/**
> + * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller
> + * @ctrl: the controller device to which the slave function should be removed
> + * @func: the slave function to be removed
> + *
> + * An I3C slave device can have only one functions.
> + */
> +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> +{
> +	ctrl->func = NULL;
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func);
> +
> +
> +/**
> + * i3c_slave_ctrl() - get the I3C slave controller
> + * @name: device name of the slave controller
> + *
> + * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the
> + * slave controller
> + */
> +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name)
> +{
> +	int ret = -EINVAL;
> +	struct i3c_slave_ctrl *ctrl;
> +	struct device *dev;
> +	struct class_dev_iter iter;
> +
> +	class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL);
> +	while ((dev = class_dev_iter_next(&iter))) {
> +		if (strcmp(name, dev_name(dev)))
> +			continue;
> +
> +		ctrl = to_i3c_slave_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_slave_ctrl_get);
> +
> +/**
> + * i3c_slave_ctrl_put() - release the I3C endpoint controller
> + * @slave: slave returned by pci_slave_get()
> + *
> + * release the refcount the caller obtained by invoking i3c_slave_ctrl_get()
> + */
> +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl)
> +{
> +	if (!ctrl || IS_ERR(ctrl))
> +		return;
> +
> +	module_put(ctrl->ops->owner);
> +	put_device(&ctrl->dev);
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_put);
> +
> +/**
> + * i3c_slave_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_slave_func_bind(struct i3c_slave_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_slave_func_bind);
> +
> +/**
> + * i3c_slave_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_slave_func_unbind(struct i3c_slave_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_slave_func_unbind);
> +
> +/**
> + * i3c_slave_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_slave_func *i3c_slave_func_create(const char *drv_name, const char *name)
> +{
> +	struct i3c_slave_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_slave_func_bus_type;
> +	dev->type = &i3c_slave_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_slave_func_create);
> +
> +/**
> + * __i3c_slave_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_slave_func_register_driver(struct i3c_slave_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_slave_func_bus_type;
> +	driver->driver.owner = owner;
> +
> +	ret = driver_register(&driver->driver);
> +	if (ret)
> +		return ret;
> +
> +	i3c_slave_cfs_add_func_group(driver->driver.name);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(__i3c_slave_func_register_driver);
> +
> +/**
> + * i3c_slave_func_unregister_driver() - unregister the I3C function driver
> + * @driver: the I3C function driver that has to be unregistered
> + *
> + * Invoke to unregister the I3C function driver.
> + */
> +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *fd)
> +{
> +	mutex_lock(&func_lock);
> +	mutex_unlock(&func_lock);
> +}
> +EXPORT_SYMBOL_GPL(i3c_slave_func_unregister_driver);
> +
> +static int __init i3c_slave_init(void)
> +{
> +	int ret;
> +
> +	i3c_slave_ctrl_class = class_create("i3c_slave");
> +	if (IS_ERR(i3c_slave_ctrl_class)) {
> +		pr_err("failed to create i3c slave class --> %ld\n",
> +			PTR_ERR(i3c_slave_ctrl_class));
> +		return PTR_ERR(i3c_slave_ctrl_class);
> +	}
> +
> +	ret = bus_register(&i3c_slave_func_bus_type);
> +	if (ret) {
> +		class_destroy(i3c_slave_ctrl_class);
> +		pr_err("failed to register i3c slave func bus --> %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +module_init(i3c_slave_init);
> +
> +static void __exit i3c_slave_exit(void)
> +{
> +	class_destroy(i3c_slave_ctrl_class);
> +	bus_unregister(&i3c_slave_func_bus_type);
> +
> +}
> +module_exit(i3c_slave_exit);
> +
> diff --git a/include/linux/i3c/slave.h b/include/linux/i3c/slave.h
> new file mode 100644
> index 0000000000000..a4cbbfc6d6ea9
> --- /dev/null
> +++ b/include/linux/i3c/slave.h
> @@ -0,0 +1,458 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright 2023 NXP.
> + *
> + * Author: Frank Li <Frank.Li@nxp.com>
> + */
> +
> +#ifndef I3C_SLAVE_H
> +#define I3C_SLAVE_H
> +
> +#include <linux/device.h>
> +
> +struct i3c_slave_func;
> +struct i3c_slave_ctrl;
> +
> +/**
> + * struct i3c_slave_func_ops - set of function pointers for performing i3c slave 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_slave_func_ops {
> +	int (*bind)(struct i3c_slave_func *func);
> +	void (*unbind)(struct i3c_slave_func *func);
> +};
> +
> +/**
> + * struct i3c_slave_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
> + * @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_slave_func_driver {
> +	int (*probe)(struct i3c_slave_func *func);
> +	void (*remove)(struct i3c_slave_func *func);
> +
> +	char *name;
> +	struct device_driver driver;
> +	struct i3c_slave_func_ops *ops;
> +	struct module *owner;
> +};
> +
> +/**
> + * struct i3c_slave_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_slave_func_ops
> + * @ctrl: binded I3C controller device
> + * @is_bound: indicates if bind notification to function driver has been invoked
> + * @vednor_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: maxium write length
> + * @max_read_len: maxium read length
> + * @bcr: bus characteristics register (BCR)
> + * @dcr: device characteristics register (DCR)
> + */
> +struct i3c_slave_func {
> +	struct device dev;
> +	char *name;
> +	struct i3c_slave_func_driver *driver;
> +	struct config_group *group;
> +	/* mutex to protect against concurrent access of i3c_slave_func_ops */
> +	struct mutex lock;
> +	struct i3c_slave_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
> + * @status: transfer status
> + * @actual: how much actually transferred
> + * @ctrl: I3C slave controller associate with this request
> + * @tx: transfer direction, 1: slave to master, 0: master to slave
> + */
> +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_slave_ctrl *ctrl;
> +	bool tx;
> +};
> +
> +/**
> + * struct i3c_slave_ctrl_features - represents I3C slave controller features.
> + * @tx_fifo_sz: tx hardware fifo size
> + * @rx_fifo_sz: rx hardware fifo size
> + */
> +struct i3c_slave_ctrl_features {
> +	u32 tx_fifo_sz;
> +	u32 rx_fifo_sz;
> +};
> +
> +/**
> + * struct i3c_slave_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: rasie IBI interrupt to master
> + * @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
> + * @get_features: ops to get the features supported by the I3C slave controller
> + * @owner: the module owner containing the ops
> + */
> +struct i3c_slave_ctrl_ops {
> +	int (*set_config)(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> +	int (*enable)(struct i3c_slave_ctrl *ctrl);
> +	int (*disable)(struct i3c_slave_ctrl *ctrl);
> +	int (*raise_ibi)(struct i3c_slave_ctrl *ctrl, void *p, u8 size);
> +
> +	struct i3c_request *(*alloc_request)(struct i3c_slave_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_slave_ctrl *ctrl, bool tx);
> +
> +	int (*fifo_status)(struct i3c_slave_ctrl *ctrl, bool tx);
> +	void (*fifo_flush)(struct i3c_slave_ctrl *ctrl, bool tx);
> +
> +	const struct i3c_slave_ctrl_features *(*get_features)(struct i3c_slave_ctrl *ctrl);
> +	struct module *owner;
> +};
> +
> +/**
> + * struct i3c_slave_ctrl - represents the I3C slave device
> + * @dev: I3C slave device
> + * @ops: function pointers for performing endpoint operations
> + * @func: slave functions present in this controller device
> + * @group: configfs group representing the I3C controller device
> + */
> +struct i3c_slave_ctrl {
> +	struct device dev;
> +	const struct i3c_slave_ctrl_ops *ops;
> +	struct i3c_slave_func *func;
> +	struct config_group *group;
> +};
> +
> +/**
> + * i3c_slave_ctrl_raise_ibi() - Raise IBI to master
> + * @ctrl: I3C slave 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_slave_ctrl_raise_ibi(struct i3c_slave_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_slave_ctrl_cancel_all_reqs() - Cancel all pending request
> + * @ctrl: I3C slave controller
> + * @tx: Transfer diretion queue
> + * @size: size of optional data
> + */
> +static inline void i3c_slave_ctrl_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->cancel_all_reqs)
> +		ctrl->ops->cancel_all_reqs(ctrl, tx);
> +}
> +
> +/**
> + * i3c_slave_ctrl_set_config() - Set controller configuration
> + * @ctrl: I3C slave controller device
> + * @func: Function device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline int
> +i3c_slave_ctrl_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->set_config)
> +		return ctrl->ops->set_config(ctrl, func);
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * i3c_slave_ctrl_enable() - Enable I3C controller
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline int
> +i3c_slave_ctrl_enable(struct i3c_slave_ctrl *ctrl)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->enable)
> +		return ctrl->ops->enable(ctrl);
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * i3c_slave_ctrl_disable() - Disable I3C controller
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline int
> +i3c_slave_ctrl_disable(struct i3c_slave_ctrl *ctrl)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->disable)
> +		return ctrl->ops->disable(ctrl);
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * i3c_slave_ctrl_alloc_request() - Alloc an I3C transfer
> + * @ctrl: I3C slave 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_slave_ctrl_alloc_request(struct i3c_slave_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_slave_ctrl_free_request() - Free an I3C transfer
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline void
> +i3c_slave_ctrl_free_request(struct i3c_request *req)
> +{
> +	struct i3c_slave_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_slave_ctrl_queue() - Queue an I3C transfer
> + * @ctrl: I3C slave 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 int
> +i3c_slave_ctrl_queue(struct i3c_request *req, gfp_t gfp_flags)
> +{
> +	struct i3c_slave_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_slave_ctrl_dequeue() - Dequeue an I3C transfer
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: Zero for success, or an error code in case of failure
> + */
> +static inline int
> +i3c_slave_ctrl_dequeue(struct i3c_request *req)
> +{
> +	struct i3c_slave_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_slave_ctrl_fifo_status() - Get controller FIFO status
> + * @ctrl: I3C slave controller device
> + * @tx: 1: Slave to master, 0: master to slave
> + *
> + * Returns: How much data in FIFO
> + */
> +static inline int
> +i3c_slave_ctrl_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->fifo_status)
> +		return ctrl->ops->fifo_status(ctrl, tx);
> +
> +	return 0;
> +}
> +
> +/**
> + * i3c_slave_ctrl_fifo_flush() - Flush controller FIFO
> + * @ctrl: I3C slave controller device
> + * @tx: 1: Slave to master, 0: master to slave
> + *
> + */
> +static inline void
> +i3c_slave_ctrl_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->fifo_flush)
> +		return ctrl->ops->fifo_flush(ctrl, tx);
> +}
> +
> +/**
> + * i3c_slave_ctrl_get_features() - Get controller supported features
> + * @ctrl: I3C slave controller device
> + *
> + * Returns: The pointer to struct i3c_slave_ctrl_features
> + */
> +static inline const struct i3c_slave_ctrl_features*
> +i3c_slave_ctrl_get_features(struct i3c_slave_ctrl *ctrl)
> +{
> +	if (ctrl && ctrl->ops && ctrl->ops->get_features)
> +		return ctrl->ops->get_features(ctrl);
> +
> +	return NULL;
> +}
> +
> +#define to_i3c_slave_ctrl(device) container_of((device), struct i3c_slave_ctrl, dev)
> +
> +#define to_i3c_slave_func(func_dev) container_of((func_dev), struct i3c_slave_func, dev)
> +#define to_i3c_slave_func_driver(drv) (container_of((drv), struct i3c_slave_func_driver, driver))
> +
> +#define i3c_slave_ctrl_create(dev, ops) \
> +		__i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
> +#define devm_i3c_slave_ctrl_create(dev, ops) \
> +		__devm_i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
> +
> +struct i3c_slave_ctrl *
> +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> +		      struct module *owner);
> +struct i3c_slave_ctrl *
> +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> +		 struct module *owner);
> +
> +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *epc);
> +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *epc);
> +
> +
> +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> +
> +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl);
> +
> +
> +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group);
> +struct config_group *i3c_slave_cfs_add_func_group(const char *name);
> +void i3c_slave_cfs_remove_func_group(struct config_group *group);
> +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name);
> +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl);
> +
> +int i3c_slave_func_bind(struct i3c_slave_func *func);
> +void i3c_slave_func_unbind(struct i3c_slave_func *func);
> +struct i3c_slave_func *i3c_slave_func_create(const char *drv_name, const char *name);
> +
> +#define i3c_slave_func_register_driver(drv) \
> +	__i3c_slave_func_register_driver(drv, THIS_MODULE)
> +
> +int __i3c_slave_func_register_driver(struct i3c_slave_func_driver *drv, struct module *owner);
> +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *drv);
> +
> +#define DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops)			\
> +	static struct i3c_slave_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_SLAVE_INIT(_name, _probe, _remove, _ops)			\
> +	DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops);			\
> +	static int __init _name ## mod_init(void)				\
> +	{									\
> +		return i3c_slave_func_register_driver(&_name ## i3c_func);	\
> +	}									\
> +	static void __exit _name ## mod_exit(void)				\
> +	{									\
> +		i3c_slave_func_unregister_driver(&_name ## i3c_func);		\
> +	}									\
> +	module_init(_name ## mod_init);						\
> +	module_exit(_name ## mod_exit)
> +
> +#endif
> +
> +

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 5/9] i3c: add slave mode support
  2023-09-11 11:14     ` Joshua Yeong
@ 2023-09-11 22:17       ` Frank Li
  -1 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-11 22:17 UTC (permalink / raw)
  To: Joshua Yeong
  Cc: miquel.raynal, alexandre.belloni, conor.culhane, imx, linux-i3c,
	linux-kernel

On Mon, Sep 11, 2023 at 07:14:45PM +0800, Joshua Yeong wrote:
> 
> >--- a/drivers/i3c/Makefile
> >+++ b/drivers/i3c/Makefile
> >@@ -2,3 +2,5 @@
>  >i3c-y                := device.o master.o
> > obj-$(CONFIG_I3C)        += i3c.o
> > obj-$(CONFIG_I3C)        += master/
> >+obj-$(CONFIG_I3C_SLAVE)        += slave.o
> >+obj-$(CONFIG_I3C_SLAVE_CONFIGFS)    += i3c-cfs.o
> 
> Shall we consider to include the content of i3c-cfs.c part of slave.c ?
> 

PCI and USB's cfs is indepent file. It is logically seperated with slave.o
I still think seperate file are better.

Frank

> 
> On 06-Sep-23 5:38 AM, Frank Li wrote:
> > Introduce a new slave core layer in order to support slave functions in
> > linux kernel. This comprises the controller library and function library.
> > Controller library implements functions specific to an slave controller
> > and function library implements functions specific to an slave function.
> > 
> > Introduce a new configfs entry to configure the slave function configuring
> > and bind the slave function with slave controller.
> > 
> > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > ---
> >   drivers/i3c/Kconfig       |  29 +++
> >   drivers/i3c/Makefile      |   2 +
> >   drivers/i3c/i3c-cfs.c     | 361 ++++++++++++++++++++++++++++++
> >   drivers/i3c/slave.c       | 441 ++++++++++++++++++++++++++++++++++++
> >   include/linux/i3c/slave.h | 458 ++++++++++++++++++++++++++++++++++++++
> >   5 files changed, 1291 insertions(+)
> >   create mode 100644 drivers/i3c/i3c-cfs.c
> >   create mode 100644 drivers/i3c/slave.c
> >   create mode 100644 include/linux/i3c/slave.h
> > 
> > diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
> > index 30a441506f61c..bdc173bc0da12 100644
> > --- a/drivers/i3c/Kconfig
> > +++ b/drivers/i3c/Kconfig
> > @@ -22,3 +22,32 @@ menuconfig I3C
> >   if I3C
> >   source "drivers/i3c/master/Kconfig"
> >   endif # I3C
> > +
> > +config I3C_SLAVE
> > +	bool "I3C Slave Support"
> > +	help
> > +	  Support I3C Slave Mode.
> > +
> > +	  Enable this configuration option to support configurable I3C slave.
> > +	  This should be enabled if the platform has a I3C controller that can
> > +	  operate in slave mode.
> > +
> > +	  Enabling this option will build the I3C slave library, which includes
> > +	  slave controller library and slave function library.
> > +
> > +	  If in doubt, say "N" to disable slave support.
> > +
> > +config I3C_SLAVE_CONFIGFS
> > +	bool "I3C Slave Configfs Support"
> > +	depends on I3C_SLAVE
> > +	select CONFIGFS_FS
> > +	help
> > +	  Configfs entry for slave function and controller.
> > +
> > +	  This will enable the configfs entry that can be used to configure
> > +	  the slave function and used to bind the function with a slave
> > +	  controller.
> > +
> > +if I3C_SLAVE
> > +source "drivers/i3c/slave/Kconfig"
> > +endif #I#C_SLAVE
> > diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
> > index 11982efbc6d91..6407ddec3a4a9 100644
> > --- a/drivers/i3c/Makefile
> > +++ b/drivers/i3c/Makefile
> > @@ -2,3 +2,5 @@
> >   i3c-y				:= device.o master.o
> >   obj-$(CONFIG_I3C)		+= i3c.o
> >   obj-$(CONFIG_I3C)		+= master/
> > +obj-$(CONFIG_I3C_SLAVE)		+= slave.o
> > +obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
> > diff --git a/drivers/i3c/i3c-cfs.c b/drivers/i3c/i3c-cfs.c
> > new file mode 100644
> > index 0000000000000..1f53fada43645
> > --- /dev/null
> > +++ b/drivers/i3c/i3c-cfs.c
> > @@ -0,0 +1,361 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Configfs to configure the I3C Slave
> > + *
> > + * Copyright (C) 2023 NXP
> > + * Author: Frank Li <Frank.Li@nxp.com>
> > + */
> > +
> > +#include <linux/configfs.h>
> > +#include <linux/module.h>
> > +#include <linux/i3c/slave.h>
> > +#include <linux/idr.h>
> > +#include <linux/slab.h>
> > +
> > +static DEFINE_MUTEX(functions_mutex);
> > +static struct config_group *functions_group;
> > +static struct config_group *controllers_group;
> > +
> > +struct i3c_slave_func_group {
> > +	struct config_group group;
> > +	struct i3c_slave_func *func;
> > +};
> > +
> > +struct i3c_slave_ctrl_group {
> > +	struct config_group group;
> > +	struct i3c_slave_ctrl *ctrl;
> > +};
> > +
> > +static inline struct i3c_slave_func_group *to_i3c_slave_func_group(struct config_item *item)
> > +{
> > +	return container_of(to_config_group(item), struct i3c_slave_func_group, group);
> > +}
> > +
> > +static inline struct i3c_slave_ctrl_group *to_i3c_slave_ctrl_group(struct config_item *item)
> > +{
> > +	return container_of(to_config_group(item), struct i3c_slave_ctrl_group, group);
> > +}
> > +
> > +static int i3c_slave_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg)
> > +{
> > +	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg);
> > +	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
> > +	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
> > +	struct i3c_slave_func *func = func_group->func;
> > +	int ret;
> > +
> > +	ret = i3c_slave_ctrl_add_func(ctrl, func);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = i3c_slave_func_bind(func);
> > +	if (ret) {
> > +		i3c_slave_ctrl_remove_func(ctrl, func);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void i3c_slave_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg)
> > +{
> > +	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg->ci_parent);
> > +	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
> > +	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
> > +	struct i3c_slave_func *func = func_group->func;
> > +
> > +	i3c_slave_func_unbind(func);
> > +	i3c_slave_ctrl_remove_func(ctrl, func);
> > +}
> > +
> > +static struct configfs_item_operations i3c_slave_ctrl_item_ops = {
> > +	.allow_link     = i3c_slave_ctrl_func_link,
> > +	.drop_link      = i3c_slave_ctrl_func_unlink,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_ctrl_type = {
> > +	.ct_item_ops    = &i3c_slave_ctrl_item_ops,
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +/**
> > + * i3c_slave_cfs_add_ctrl_group() - add I3C slave controller group
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Return: Pointer to struct config_group
> > + */
> > +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	struct i3c_slave_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_slave_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_slave_cfs_add_ctrl_group);
> > +
> > +/**
> > + * i3c_slave_cfs_remove_ctrl_group() - remove I3C slave controller group
> > + * @group: the group to be removed
> > + */
> > +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group)
> > +{
> > +	struct i3c_slave_ctrl_group *ctrl_group;
> > +
> > +	if (!group)
> > +		return;
> > +
> > +	ctrl_group = container_of(group, struct i3c_slave_ctrl_group, group);
> > +	i3c_slave_ctrl_put(ctrl_group->ctrl);
> > +	configfs_unregister_group(&ctrl_group->group);
> > +	kfree(ctrl_group);
> > +}
> > +EXPORT_SYMBOL(i3c_slave_cfs_remove_ctrl_group);
> > +
> > +#define I3C_SLAVE_ATTR_R(_name)                                                \
> > +static ssize_t i3c_slave_func_##_name##_show(struct config_item *item, char *page)    \
> > +{                                                                              \
> > +	struct i3c_slave_func *func = to_i3c_slave_func_group(item)->func;                     \
> > +	return sysfs_emit(page, "0x%04x\n", func->_name);               \
> > +}
> > +
> > +#define I3C_SLAVE_ATTR_W(_name, _u)                                            \
> > +static ssize_t i3c_slave_func_##_name##_store(struct config_item *item,               \
> > +				       const char *page, size_t len)           \
> > +{                                                                              \
> > +	_u val;                                                               \
> > +	struct i3c_slave_func *func = to_i3c_slave_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_slave_func_, vendor_id);
> > +
> > +I3C_SLAVE_ATTR_R(vendor_info);
> > +I3C_SLAVE_ATTR_W(vendor_info, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, vendor_info);
> > +
> > +I3C_SLAVE_ATTR_R(part_id);
> > +I3C_SLAVE_ATTR_W(part_id, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, part_id);
> > +
> > +I3C_SLAVE_ATTR_R(instance_id);
> > +I3C_SLAVE_ATTR_W(instance_id, u8);
> > +CONFIGFS_ATTR(i3c_slave_func_, instance_id);
> > +
> > +I3C_SLAVE_ATTR_R(ext_id);
> > +I3C_SLAVE_ATTR_W(ext_id, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, ext_id);
> > +
> > +I3C_SLAVE_ATTR_R(max_write_len);
> > +I3C_SLAVE_ATTR_W(max_write_len, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, max_write_len);
> > +
> > +I3C_SLAVE_ATTR_R(max_read_len);
> > +I3C_SLAVE_ATTR_W(max_read_len, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, max_read_len);
> > +
> > +I3C_SLAVE_ATTR_R(bcr);
> > +I3C_SLAVE_ATTR_W(bcr, u8);
> > +CONFIGFS_ATTR(i3c_slave_func_, bcr);
> > +
> > +I3C_SLAVE_ATTR_R(dcr);
> > +I3C_SLAVE_ATTR_W(dcr, u8);
> > +CONFIGFS_ATTR(i3c_slave_func_, dcr);
> > +
> > +static struct configfs_attribute *i3c_slave_func_attrs[] = {
> > +	&i3c_slave_func_attr_vendor_id,
> > +	&i3c_slave_func_attr_vendor_info,
> > +	&i3c_slave_func_attr_part_id,
> > +	&i3c_slave_func_attr_instance_id,
> > +	&i3c_slave_func_attr_ext_id,
> > +	&i3c_slave_func_attr_max_write_len,
> > +	&i3c_slave_func_attr_max_read_len,
> > +	&i3c_slave_func_attr_bcr,
> > +	&i3c_slave_func_attr_dcr,
> > +	NULL,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_func_type = {
> > +	.ct_attrs       = i3c_slave_func_attrs,
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +static struct config_group *i3c_slave_func_make(struct config_group *group, const char *name)
> > +{
> > +	struct i3c_slave_func_group *func_group;
> > +	struct i3c_slave_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_slave_func_type);
> > +
> > +	func = i3c_slave_func_create(group->cg_item.ci_name, name);
> > +	if (IS_ERR(func)) {
> > +		pr_err("failed to create i3c slave 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_slave_func_drop(struct config_group *group, struct config_item *item)
> > +{
> > +	config_item_put(item);
> > +}
> > +
> > +static struct configfs_group_operations i3c_slave_func_group_ops = {
> > +	.make_group     = &i3c_slave_func_make,
> > +	.drop_item      = &i3c_slave_func_drop,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_func_group_type = {
> > +	.ct_group_ops   = &i3c_slave_func_group_ops,
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +/**
> > + * i3c_slave_cfs_add_func_group() - add I3C slave function group
> > + * @name: group name
> > + *
> > + * Return: Pointer to struct config_group
> > + */
> > +struct config_group *i3c_slave_cfs_add_func_group(const char *name)
> > +{
> > +	struct config_group *group;
> > +
> > +	group = configfs_register_default_group(functions_group, name,
> > +						&i3c_slave_func_group_type);
> > +	if (IS_ERR(group))
> > +		pr_err("failed to register configfs group for %s function\n",
> > +		       name);
> > +
> > +	return group;
> > +}
> > +EXPORT_SYMBOL(i3c_slave_cfs_add_func_group);
> > +
> > +/**
> > + * i3c_slave_cfs_remove_func_group() - add I3C slave function group
> > + * @group: group to be removed
> > + */
> > +void i3c_slave_cfs_remove_func_group(struct config_group *group)
> > +{
> > +	if (IS_ERR_OR_NULL(group))
> > +		return;
> > +
> > +	configfs_unregister_default_group(group);
> > +}
> > +EXPORT_SYMBOL(i3c_slave_cfs_remove_func_group);
> > +
> > +static const struct config_item_type i3c_slave_controllers_type = {
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_functions_type = {
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_type = {
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +static struct configfs_subsystem i3c_slave_cfs_subsys = {
> > +	.su_group = {
> > +		.cg_item = {
> > +			.ci_namebuf = "i3c_slave",
> > +			.ci_type = &i3c_slave_type,
> > +		},
> > +	},
> > +	.su_mutex = __MUTEX_INITIALIZER(i3c_slave_cfs_subsys.su_mutex),
> > +};
> > +
> > +static int __init i3c_slave_cfs_init(void)
> > +{
> > +	int ret;
> > +	struct config_group *root = &i3c_slave_cfs_subsys.su_group;
> > +
> > +	config_group_init(root);
> > +
> > +	ret = configfs_register_subsystem(&i3c_slave_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_slave_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_slave_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_slave_cfs_subsys);
> > +
> > +err:
> > +	return ret;
> > +}
> > +module_init(i3c_slave_cfs_init);
> > +
> > +MODULE_DESCRIPTION("I3C FUNC CONFIGFS");
> > +MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
> > +
> > +
> > diff --git a/drivers/i3c/slave.c b/drivers/i3c/slave.c
> > new file mode 100644
> > index 0000000000000..edd7a2888271b
> > --- /dev/null
> > +++ b/drivers/i3c/slave.c
> > @@ -0,0 +1,441 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * configfs to configure the I3C Slave
> > + *
> > + * Copyright (C) 2023 NXP
> > + * Author: Frank Li <Frank.Li@nxp.com>
> > + */
> > +
> > +#include <linux/configfs.h>
> > +#include <linux/module.h>
> > +#include <linux/i3c/slave.h>
> > +#include <linux/idr.h>
> > +#include <linux/slab.h>
> > +
> > +static DEFINE_MUTEX(func_lock);
> > +static struct class *i3c_slave_ctrl_class;
> > +
> > +static void i3c_slave_func_dev_release(struct device *dev)
> > +{
> > +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> > +
> > +	kfree(func->name);
> > +	kfree(func);
> > +}
> > +
> > +static const struct device_type i3c_slave_func_type = {
> > +	.release        = i3c_slave_func_dev_release,
> > +};
> > +
> > +static int i3c_slave_func_match_driver(struct device *dev, struct device_driver *drv)
> > +{
> > +	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
> > +}
> > +
> > +static int i3c_slave_func_device_probe(struct device *dev)
> > +{
> > +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> > +	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
> > +
> > +	if (!driver->probe)
> > +		return -ENODEV;
> > +
> > +	func->driver = driver;
> > +
> > +	return driver->probe(func);
> > +}
> > +
> > +static void i3c_slave_func_device_remove(struct device *dev)
> > +{
> > +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> > +	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
> > +
> > +	if (driver->remove)
> > +		driver->remove(func);
> > +	func->driver = NULL;
> > +}
> > +
> > +static const struct bus_type i3c_slave_func_bus_type = {
> > +	.name = "i3c_slave_func",
> > +	.probe = i3c_slave_func_device_probe,
> > +	.remove = i3c_slave_func_device_remove,
> > +	.match = i3c_slave_func_match_driver,
> > +};
> > +
> > +static void i3c_slave_ctrl_release(struct device *dev)
> > +{
> > +	kfree(to_i3c_slave_ctrl(dev));
> > +}
> > +
> > +static void devm_i3c_slave_ctrl_release(struct device *dev, void *res)
> > +{
> > +	struct i3c_slave_ctrl *ctrl = *(struct i3c_slave_ctrl **)res;
> > +
> > +	i3c_slave_ctrl_destroy(ctrl);
> > +}
> > +
> > +struct i3c_slave_ctrl *
> > +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> > +		      struct module *owner)
> > +{
> > +	struct i3c_slave_ctrl **ptr, *ctrl;
> > +
> > +	ptr = devres_alloc(devm_i3c_slave_ctrl_release, sizeof(*ptr), GFP_KERNEL);
> > +	if (!ptr)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	ctrl = __i3c_slave_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_slave_ctrl_match(struct device *dev, void *res, void *match_data)
> > +{
> > +	struct i3c_slave_ctrl **ptr = res;
> > +
> > +	return *ptr == match_data;
> > +}
> > +
> > +/**
> > + * __i3c_slave_ctrl_create() - create a new slave controller device
> > + * @dev: device that is creating the new slave controller
> > + * @ops: function pointers for performing slave controller  operations
> > + * @owner: the owner of the module that creates the slave controller device
> > + *
> > + * Return: Pointer to struct i3c_slave_ctrl
> > + */
> > +struct i3c_slave_ctrl *
> > +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> > +			struct module *owner)
> > +{
> > +	struct i3c_slave_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_slave_ctrl_class;
> > +	ctrl->dev.parent = dev;
> > +	ctrl->dev.release = i3c_slave_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_slave_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_slave_ctrl_create);
> > +
> > +/**
> > + * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device
> > + * @dev: device that is creating the new slave controller device
> > + * @ops: function pointers for performing slave controller operations
> > + * @owner: the owner of the module that creates the slave controller device
> > + *
> > + * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it
> > + * also associates the device with the i3c_slave using devres. On driver detach, release function is
> > + * invoked on the devres data, then devres data is freed.
> > + */
> > +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl)
> > +{
> > +	int r;
> > +
> > +	r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match,
> > +			   ctrl);
> > +	dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
> > +}
> > +EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy);
> > +
> > +
> > +
> > +/**
> > + * i3c_slave_ctrl_destroy() - destroy the slave controller device
> > + * @ctrl: the slave controller device that has to be destroyed
> > + *
> > + * Invoke to destroy the I3C slave device
> > + */
> > +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	i3c_slave_cfs_remove_ctrl_group(ctrl->group);
> > +	device_unregister(&ctrl->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy);
> > +
> > +/**
> > + * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller
> > + * @ctrl: the controller device to which the slave function should be added
> > + * @func: the slave function to be added
> > + *
> > + * An I3C slave device can have only one functions.
> > + */
> > +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> > +{
> > +	if (ctrl->func)
> > +		return -EBUSY;
> > +
> > +	ctrl->func = func;
> > +	func->ctrl = ctrl;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func);
> > +
> > +/**
> > + * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller
> > + * @ctrl: the controller device to which the slave function should be removed
> > + * @func: the slave function to be removed
> > + *
> > + * An I3C slave device can have only one functions.
> > + */
> > +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> > +{
> > +	ctrl->func = NULL;
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func);
> > +
> > +
> > +/**
> > + * i3c_slave_ctrl() - get the I3C slave controller
> > + * @name: device name of the slave controller
> > + *
> > + * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the
> > + * slave controller
> > + */
> > +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name)
> > +{
> > +	int ret = -EINVAL;
> > +	struct i3c_slave_ctrl *ctrl;
> > +	struct device *dev;
> > +	struct class_dev_iter iter;
> > +
> > +	class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL);
> > +	while ((dev = class_dev_iter_next(&iter))) {
> > +		if (strcmp(name, dev_name(dev)))
> > +			continue;
> > +
> > +		ctrl = to_i3c_slave_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_slave_ctrl_get);
> > +
> > +/**
> > + * i3c_slave_ctrl_put() - release the I3C endpoint controller
> > + * @slave: slave returned by pci_slave_get()
> > + *
> > + * release the refcount the caller obtained by invoking i3c_slave_ctrl_get()
> > + */
> > +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	if (!ctrl || IS_ERR(ctrl))
> > +		return;
> > +
> > +	module_put(ctrl->ops->owner);
> > +	put_device(&ctrl->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_put);
> > +
> > +/**
> > + * i3c_slave_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_slave_func_bind(struct i3c_slave_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_slave_func_bind);
> > +
> > +/**
> > + * i3c_slave_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_slave_func_unbind(struct i3c_slave_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_slave_func_unbind);
> > +
> > +/**
> > + * i3c_slave_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_slave_func *i3c_slave_func_create(const char *drv_name, const char *name)
> > +{
> > +	struct i3c_slave_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_slave_func_bus_type;
> > +	dev->type = &i3c_slave_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_slave_func_create);
> > +
> > +/**
> > + * __i3c_slave_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_slave_func_register_driver(struct i3c_slave_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_slave_func_bus_type;
> > +	driver->driver.owner = owner;
> > +
> > +	ret = driver_register(&driver->driver);
> > +	if (ret)
> > +		return ret;
> > +
> > +	i3c_slave_cfs_add_func_group(driver->driver.name);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(__i3c_slave_func_register_driver);
> > +
> > +/**
> > + * i3c_slave_func_unregister_driver() - unregister the I3C function driver
> > + * @driver: the I3C function driver that has to be unregistered
> > + *
> > + * Invoke to unregister the I3C function driver.
> > + */
> > +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *fd)
> > +{
> > +	mutex_lock(&func_lock);
> > +	mutex_unlock(&func_lock);
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_func_unregister_driver);
> > +
> > +static int __init i3c_slave_init(void)
> > +{
> > +	int ret;
> > +
> > +	i3c_slave_ctrl_class = class_create("i3c_slave");
> > +	if (IS_ERR(i3c_slave_ctrl_class)) {
> > +		pr_err("failed to create i3c slave class --> %ld\n",
> > +			PTR_ERR(i3c_slave_ctrl_class));
> > +		return PTR_ERR(i3c_slave_ctrl_class);
> > +	}
> > +
> > +	ret = bus_register(&i3c_slave_func_bus_type);
> > +	if (ret) {
> > +		class_destroy(i3c_slave_ctrl_class);
> > +		pr_err("failed to register i3c slave func bus --> %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +module_init(i3c_slave_init);
> > +
> > +static void __exit i3c_slave_exit(void)
> > +{
> > +	class_destroy(i3c_slave_ctrl_class);
> > +	bus_unregister(&i3c_slave_func_bus_type);
> > +
> > +}
> > +module_exit(i3c_slave_exit);
> > +
> > diff --git a/include/linux/i3c/slave.h b/include/linux/i3c/slave.h
> > new file mode 100644
> > index 0000000000000..a4cbbfc6d6ea9
> > --- /dev/null
> > +++ b/include/linux/i3c/slave.h
> > @@ -0,0 +1,458 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright 2023 NXP.
> > + *
> > + * Author: Frank Li <Frank.Li@nxp.com>
> > + */
> > +
> > +#ifndef I3C_SLAVE_H
> > +#define I3C_SLAVE_H
> > +
> > +#include <linux/device.h>
> > +
> > +struct i3c_slave_func;
> > +struct i3c_slave_ctrl;
> > +
> > +/**
> > + * struct i3c_slave_func_ops - set of function pointers for performing i3c slave 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_slave_func_ops {
> > +	int (*bind)(struct i3c_slave_func *func);
> > +	void (*unbind)(struct i3c_slave_func *func);
> > +};
> > +
> > +/**
> > + * struct i3c_slave_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
> > + * @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_slave_func_driver {
> > +	int (*probe)(struct i3c_slave_func *func);
> > +	void (*remove)(struct i3c_slave_func *func);
> > +
> > +	char *name;
> > +	struct device_driver driver;
> > +	struct i3c_slave_func_ops *ops;
> > +	struct module *owner;
> > +};
> > +
> > +/**
> > + * struct i3c_slave_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_slave_func_ops
> > + * @ctrl: binded I3C controller device
> > + * @is_bound: indicates if bind notification to function driver has been invoked
> > + * @vednor_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: maxium write length
> > + * @max_read_len: maxium read length
> > + * @bcr: bus characteristics register (BCR)
> > + * @dcr: device characteristics register (DCR)
> > + */
> > +struct i3c_slave_func {
> > +	struct device dev;
> > +	char *name;
> > +	struct i3c_slave_func_driver *driver;
> > +	struct config_group *group;
> > +	/* mutex to protect against concurrent access of i3c_slave_func_ops */
> > +	struct mutex lock;
> > +	struct i3c_slave_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
> > + * @status: transfer status
> > + * @actual: how much actually transferred
> > + * @ctrl: I3C slave controller associate with this request
> > + * @tx: transfer direction, 1: slave to master, 0: master to slave
> > + */
> > +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_slave_ctrl *ctrl;
> > +	bool tx;
> > +};
> > +
> > +/**
> > + * struct i3c_slave_ctrl_features - represents I3C slave controller features.
> > + * @tx_fifo_sz: tx hardware fifo size
> > + * @rx_fifo_sz: rx hardware fifo size
> > + */
> > +struct i3c_slave_ctrl_features {
> > +	u32 tx_fifo_sz;
> > +	u32 rx_fifo_sz;
> > +};
> > +
> > +/**
> > + * struct i3c_slave_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: rasie IBI interrupt to master
> > + * @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
> > + * @get_features: ops to get the features supported by the I3C slave controller
> > + * @owner: the module owner containing the ops
> > + */
> > +struct i3c_slave_ctrl_ops {
> > +	int (*set_config)(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> > +	int (*enable)(struct i3c_slave_ctrl *ctrl);
> > +	int (*disable)(struct i3c_slave_ctrl *ctrl);
> > +	int (*raise_ibi)(struct i3c_slave_ctrl *ctrl, void *p, u8 size);
> > +
> > +	struct i3c_request *(*alloc_request)(struct i3c_slave_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_slave_ctrl *ctrl, bool tx);
> > +
> > +	int (*fifo_status)(struct i3c_slave_ctrl *ctrl, bool tx);
> > +	void (*fifo_flush)(struct i3c_slave_ctrl *ctrl, bool tx);
> > +
> > +	const struct i3c_slave_ctrl_features *(*get_features)(struct i3c_slave_ctrl *ctrl);
> > +	struct module *owner;
> > +};
> > +
> > +/**
> > + * struct i3c_slave_ctrl - represents the I3C slave device
> > + * @dev: I3C slave device
> > + * @ops: function pointers for performing endpoint operations
> > + * @func: slave functions present in this controller device
> > + * @group: configfs group representing the I3C controller device
> > + */
> > +struct i3c_slave_ctrl {
> > +	struct device dev;
> > +	const struct i3c_slave_ctrl_ops *ops;
> > +	struct i3c_slave_func *func;
> > +	struct config_group *group;
> > +};
> > +
> > +/**
> > + * i3c_slave_ctrl_raise_ibi() - Raise IBI to master
> > + * @ctrl: I3C slave 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_slave_ctrl_raise_ibi(struct i3c_slave_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_slave_ctrl_cancel_all_reqs() - Cancel all pending request
> > + * @ctrl: I3C slave controller
> > + * @tx: Transfer diretion queue
> > + * @size: size of optional data
> > + */
> > +static inline void i3c_slave_ctrl_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->cancel_all_reqs)
> > +		ctrl->ops->cancel_all_reqs(ctrl, tx);
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_set_config() - Set controller configuration
> > + * @ctrl: I3C slave controller device
> > + * @func: Function device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline int
> > +i3c_slave_ctrl_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->set_config)
> > +		return ctrl->ops->set_config(ctrl, func);
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_enable() - Enable I3C controller
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline int
> > +i3c_slave_ctrl_enable(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->enable)
> > +		return ctrl->ops->enable(ctrl);
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_disable() - Disable I3C controller
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline int
> > +i3c_slave_ctrl_disable(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->disable)
> > +		return ctrl->ops->disable(ctrl);
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_alloc_request() - Alloc an I3C transfer
> > + * @ctrl: I3C slave 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_slave_ctrl_alloc_request(struct i3c_slave_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_slave_ctrl_free_request() - Free an I3C transfer
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline void
> > +i3c_slave_ctrl_free_request(struct i3c_request *req)
> > +{
> > +	struct i3c_slave_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_slave_ctrl_queue() - Queue an I3C transfer
> > + * @ctrl: I3C slave 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 int
> > +i3c_slave_ctrl_queue(struct i3c_request *req, gfp_t gfp_flags)
> > +{
> > +	struct i3c_slave_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_slave_ctrl_dequeue() - Dequeue an I3C transfer
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline int
> > +i3c_slave_ctrl_dequeue(struct i3c_request *req)
> > +{
> > +	struct i3c_slave_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_slave_ctrl_fifo_status() - Get controller FIFO status
> > + * @ctrl: I3C slave controller device
> > + * @tx: 1: Slave to master, 0: master to slave
> > + *
> > + * Returns: How much data in FIFO
> > + */
> > +static inline int
> > +i3c_slave_ctrl_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->fifo_status)
> > +		return ctrl->ops->fifo_status(ctrl, tx);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_fifo_flush() - Flush controller FIFO
> > + * @ctrl: I3C slave controller device
> > + * @tx: 1: Slave to master, 0: master to slave
> > + *
> > + */
> > +static inline void
> > +i3c_slave_ctrl_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->fifo_flush)
> > +		return ctrl->ops->fifo_flush(ctrl, tx);
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_get_features() - Get controller supported features
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: The pointer to struct i3c_slave_ctrl_features
> > + */
> > +static inline const struct i3c_slave_ctrl_features*
> > +i3c_slave_ctrl_get_features(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->get_features)
> > +		return ctrl->ops->get_features(ctrl);
> > +
> > +	return NULL;
> > +}
> > +
> > +#define to_i3c_slave_ctrl(device) container_of((device), struct i3c_slave_ctrl, dev)
> > +
> > +#define to_i3c_slave_func(func_dev) container_of((func_dev), struct i3c_slave_func, dev)
> > +#define to_i3c_slave_func_driver(drv) (container_of((drv), struct i3c_slave_func_driver, driver))
> > +
> > +#define i3c_slave_ctrl_create(dev, ops) \
> > +		__i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
> > +#define devm_i3c_slave_ctrl_create(dev, ops) \
> > +		__devm_i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
> > +
> > +struct i3c_slave_ctrl *
> > +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> > +		      struct module *owner);
> > +struct i3c_slave_ctrl *
> > +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> > +		 struct module *owner);
> > +
> > +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *epc);
> > +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *epc);
> > +
> > +
> > +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> > +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> > +
> > +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl);
> > +
> > +
> > +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group);
> > +struct config_group *i3c_slave_cfs_add_func_group(const char *name);
> > +void i3c_slave_cfs_remove_func_group(struct config_group *group);
> > +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name);
> > +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl);
> > +
> > +int i3c_slave_func_bind(struct i3c_slave_func *func);
> > +void i3c_slave_func_unbind(struct i3c_slave_func *func);
> > +struct i3c_slave_func *i3c_slave_func_create(const char *drv_name, const char *name);
> > +
> > +#define i3c_slave_func_register_driver(drv) \
> > +	__i3c_slave_func_register_driver(drv, THIS_MODULE)
> > +
> > +int __i3c_slave_func_register_driver(struct i3c_slave_func_driver *drv, struct module *owner);
> > +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *drv);
> > +
> > +#define DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops)			\
> > +	static struct i3c_slave_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_SLAVE_INIT(_name, _probe, _remove, _ops)			\
> > +	DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops);			\
> > +	static int __init _name ## mod_init(void)				\
> > +	{									\
> > +		return i3c_slave_func_register_driver(&_name ## i3c_func);	\
> > +	}									\
> > +	static void __exit _name ## mod_exit(void)				\
> > +	{									\
> > +		i3c_slave_func_unregister_driver(&_name ## i3c_func);		\
> > +	}									\
> > +	module_init(_name ## mod_init);						\
> > +	module_exit(_name ## mod_exit)
> > +
> > +#endif
> > +
> > +

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 5/9] i3c: add slave mode support
@ 2023-09-11 22:17       ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2023-09-11 22:17 UTC (permalink / raw)
  To: Joshua Yeong
  Cc: miquel.raynal, alexandre.belloni, conor.culhane, imx, linux-i3c,
	linux-kernel

On Mon, Sep 11, 2023 at 07:14:45PM +0800, Joshua Yeong wrote:
> 
> >--- a/drivers/i3c/Makefile
> >+++ b/drivers/i3c/Makefile
> >@@ -2,3 +2,5 @@
>  >i3c-y                := device.o master.o
> > obj-$(CONFIG_I3C)        += i3c.o
> > obj-$(CONFIG_I3C)        += master/
> >+obj-$(CONFIG_I3C_SLAVE)        += slave.o
> >+obj-$(CONFIG_I3C_SLAVE_CONFIGFS)    += i3c-cfs.o
> 
> Shall we consider to include the content of i3c-cfs.c part of slave.c ?
> 

PCI and USB's cfs is indepent file. It is logically seperated with slave.o
I still think seperate file are better.

Frank

> 
> On 06-Sep-23 5:38 AM, Frank Li wrote:
> > Introduce a new slave core layer in order to support slave functions in
> > linux kernel. This comprises the controller library and function library.
> > Controller library implements functions specific to an slave controller
> > and function library implements functions specific to an slave function.
> > 
> > Introduce a new configfs entry to configure the slave function configuring
> > and bind the slave function with slave controller.
> > 
> > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > ---
> >   drivers/i3c/Kconfig       |  29 +++
> >   drivers/i3c/Makefile      |   2 +
> >   drivers/i3c/i3c-cfs.c     | 361 ++++++++++++++++++++++++++++++
> >   drivers/i3c/slave.c       | 441 ++++++++++++++++++++++++++++++++++++
> >   include/linux/i3c/slave.h | 458 ++++++++++++++++++++++++++++++++++++++
> >   5 files changed, 1291 insertions(+)
> >   create mode 100644 drivers/i3c/i3c-cfs.c
> >   create mode 100644 drivers/i3c/slave.c
> >   create mode 100644 include/linux/i3c/slave.h
> > 
> > diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
> > index 30a441506f61c..bdc173bc0da12 100644
> > --- a/drivers/i3c/Kconfig
> > +++ b/drivers/i3c/Kconfig
> > @@ -22,3 +22,32 @@ menuconfig I3C
> >   if I3C
> >   source "drivers/i3c/master/Kconfig"
> >   endif # I3C
> > +
> > +config I3C_SLAVE
> > +	bool "I3C Slave Support"
> > +	help
> > +	  Support I3C Slave Mode.
> > +
> > +	  Enable this configuration option to support configurable I3C slave.
> > +	  This should be enabled if the platform has a I3C controller that can
> > +	  operate in slave mode.
> > +
> > +	  Enabling this option will build the I3C slave library, which includes
> > +	  slave controller library and slave function library.
> > +
> > +	  If in doubt, say "N" to disable slave support.
> > +
> > +config I3C_SLAVE_CONFIGFS
> > +	bool "I3C Slave Configfs Support"
> > +	depends on I3C_SLAVE
> > +	select CONFIGFS_FS
> > +	help
> > +	  Configfs entry for slave function and controller.
> > +
> > +	  This will enable the configfs entry that can be used to configure
> > +	  the slave function and used to bind the function with a slave
> > +	  controller.
> > +
> > +if I3C_SLAVE
> > +source "drivers/i3c/slave/Kconfig"
> > +endif #I#C_SLAVE
> > diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
> > index 11982efbc6d91..6407ddec3a4a9 100644
> > --- a/drivers/i3c/Makefile
> > +++ b/drivers/i3c/Makefile
> > @@ -2,3 +2,5 @@
> >   i3c-y				:= device.o master.o
> >   obj-$(CONFIG_I3C)		+= i3c.o
> >   obj-$(CONFIG_I3C)		+= master/
> > +obj-$(CONFIG_I3C_SLAVE)		+= slave.o
> > +obj-$(CONFIG_I3C_SLAVE_CONFIGFS)	+= i3c-cfs.o
> > diff --git a/drivers/i3c/i3c-cfs.c b/drivers/i3c/i3c-cfs.c
> > new file mode 100644
> > index 0000000000000..1f53fada43645
> > --- /dev/null
> > +++ b/drivers/i3c/i3c-cfs.c
> > @@ -0,0 +1,361 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Configfs to configure the I3C Slave
> > + *
> > + * Copyright (C) 2023 NXP
> > + * Author: Frank Li <Frank.Li@nxp.com>
> > + */
> > +
> > +#include <linux/configfs.h>
> > +#include <linux/module.h>
> > +#include <linux/i3c/slave.h>
> > +#include <linux/idr.h>
> > +#include <linux/slab.h>
> > +
> > +static DEFINE_MUTEX(functions_mutex);
> > +static struct config_group *functions_group;
> > +static struct config_group *controllers_group;
> > +
> > +struct i3c_slave_func_group {
> > +	struct config_group group;
> > +	struct i3c_slave_func *func;
> > +};
> > +
> > +struct i3c_slave_ctrl_group {
> > +	struct config_group group;
> > +	struct i3c_slave_ctrl *ctrl;
> > +};
> > +
> > +static inline struct i3c_slave_func_group *to_i3c_slave_func_group(struct config_item *item)
> > +{
> > +	return container_of(to_config_group(item), struct i3c_slave_func_group, group);
> > +}
> > +
> > +static inline struct i3c_slave_ctrl_group *to_i3c_slave_ctrl_group(struct config_item *item)
> > +{
> > +	return container_of(to_config_group(item), struct i3c_slave_ctrl_group, group);
> > +}
> > +
> > +static int i3c_slave_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg)
> > +{
> > +	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg);
> > +	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
> > +	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
> > +	struct i3c_slave_func *func = func_group->func;
> > +	int ret;
> > +
> > +	ret = i3c_slave_ctrl_add_func(ctrl, func);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = i3c_slave_func_bind(func);
> > +	if (ret) {
> > +		i3c_slave_ctrl_remove_func(ctrl, func);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void i3c_slave_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg)
> > +{
> > +	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg->ci_parent);
> > +	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
> > +	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
> > +	struct i3c_slave_func *func = func_group->func;
> > +
> > +	i3c_slave_func_unbind(func);
> > +	i3c_slave_ctrl_remove_func(ctrl, func);
> > +}
> > +
> > +static struct configfs_item_operations i3c_slave_ctrl_item_ops = {
> > +	.allow_link     = i3c_slave_ctrl_func_link,
> > +	.drop_link      = i3c_slave_ctrl_func_unlink,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_ctrl_type = {
> > +	.ct_item_ops    = &i3c_slave_ctrl_item_ops,
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +/**
> > + * i3c_slave_cfs_add_ctrl_group() - add I3C slave controller group
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Return: Pointer to struct config_group
> > + */
> > +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	struct i3c_slave_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_slave_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_slave_cfs_add_ctrl_group);
> > +
> > +/**
> > + * i3c_slave_cfs_remove_ctrl_group() - remove I3C slave controller group
> > + * @group: the group to be removed
> > + */
> > +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group)
> > +{
> > +	struct i3c_slave_ctrl_group *ctrl_group;
> > +
> > +	if (!group)
> > +		return;
> > +
> > +	ctrl_group = container_of(group, struct i3c_slave_ctrl_group, group);
> > +	i3c_slave_ctrl_put(ctrl_group->ctrl);
> > +	configfs_unregister_group(&ctrl_group->group);
> > +	kfree(ctrl_group);
> > +}
> > +EXPORT_SYMBOL(i3c_slave_cfs_remove_ctrl_group);
> > +
> > +#define I3C_SLAVE_ATTR_R(_name)                                                \
> > +static ssize_t i3c_slave_func_##_name##_show(struct config_item *item, char *page)    \
> > +{                                                                              \
> > +	struct i3c_slave_func *func = to_i3c_slave_func_group(item)->func;                     \
> > +	return sysfs_emit(page, "0x%04x\n", func->_name);               \
> > +}
> > +
> > +#define I3C_SLAVE_ATTR_W(_name, _u)                                            \
> > +static ssize_t i3c_slave_func_##_name##_store(struct config_item *item,               \
> > +				       const char *page, size_t len)           \
> > +{                                                                              \
> > +	_u val;                                                               \
> > +	struct i3c_slave_func *func = to_i3c_slave_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_slave_func_, vendor_id);
> > +
> > +I3C_SLAVE_ATTR_R(vendor_info);
> > +I3C_SLAVE_ATTR_W(vendor_info, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, vendor_info);
> > +
> > +I3C_SLAVE_ATTR_R(part_id);
> > +I3C_SLAVE_ATTR_W(part_id, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, part_id);
> > +
> > +I3C_SLAVE_ATTR_R(instance_id);
> > +I3C_SLAVE_ATTR_W(instance_id, u8);
> > +CONFIGFS_ATTR(i3c_slave_func_, instance_id);
> > +
> > +I3C_SLAVE_ATTR_R(ext_id);
> > +I3C_SLAVE_ATTR_W(ext_id, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, ext_id);
> > +
> > +I3C_SLAVE_ATTR_R(max_write_len);
> > +I3C_SLAVE_ATTR_W(max_write_len, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, max_write_len);
> > +
> > +I3C_SLAVE_ATTR_R(max_read_len);
> > +I3C_SLAVE_ATTR_W(max_read_len, u16);
> > +CONFIGFS_ATTR(i3c_slave_func_, max_read_len);
> > +
> > +I3C_SLAVE_ATTR_R(bcr);
> > +I3C_SLAVE_ATTR_W(bcr, u8);
> > +CONFIGFS_ATTR(i3c_slave_func_, bcr);
> > +
> > +I3C_SLAVE_ATTR_R(dcr);
> > +I3C_SLAVE_ATTR_W(dcr, u8);
> > +CONFIGFS_ATTR(i3c_slave_func_, dcr);
> > +
> > +static struct configfs_attribute *i3c_slave_func_attrs[] = {
> > +	&i3c_slave_func_attr_vendor_id,
> > +	&i3c_slave_func_attr_vendor_info,
> > +	&i3c_slave_func_attr_part_id,
> > +	&i3c_slave_func_attr_instance_id,
> > +	&i3c_slave_func_attr_ext_id,
> > +	&i3c_slave_func_attr_max_write_len,
> > +	&i3c_slave_func_attr_max_read_len,
> > +	&i3c_slave_func_attr_bcr,
> > +	&i3c_slave_func_attr_dcr,
> > +	NULL,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_func_type = {
> > +	.ct_attrs       = i3c_slave_func_attrs,
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +static struct config_group *i3c_slave_func_make(struct config_group *group, const char *name)
> > +{
> > +	struct i3c_slave_func_group *func_group;
> > +	struct i3c_slave_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_slave_func_type);
> > +
> > +	func = i3c_slave_func_create(group->cg_item.ci_name, name);
> > +	if (IS_ERR(func)) {
> > +		pr_err("failed to create i3c slave 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_slave_func_drop(struct config_group *group, struct config_item *item)
> > +{
> > +	config_item_put(item);
> > +}
> > +
> > +static struct configfs_group_operations i3c_slave_func_group_ops = {
> > +	.make_group     = &i3c_slave_func_make,
> > +	.drop_item      = &i3c_slave_func_drop,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_func_group_type = {
> > +	.ct_group_ops   = &i3c_slave_func_group_ops,
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +/**
> > + * i3c_slave_cfs_add_func_group() - add I3C slave function group
> > + * @name: group name
> > + *
> > + * Return: Pointer to struct config_group
> > + */
> > +struct config_group *i3c_slave_cfs_add_func_group(const char *name)
> > +{
> > +	struct config_group *group;
> > +
> > +	group = configfs_register_default_group(functions_group, name,
> > +						&i3c_slave_func_group_type);
> > +	if (IS_ERR(group))
> > +		pr_err("failed to register configfs group for %s function\n",
> > +		       name);
> > +
> > +	return group;
> > +}
> > +EXPORT_SYMBOL(i3c_slave_cfs_add_func_group);
> > +
> > +/**
> > + * i3c_slave_cfs_remove_func_group() - add I3C slave function group
> > + * @group: group to be removed
> > + */
> > +void i3c_slave_cfs_remove_func_group(struct config_group *group)
> > +{
> > +	if (IS_ERR_OR_NULL(group))
> > +		return;
> > +
> > +	configfs_unregister_default_group(group);
> > +}
> > +EXPORT_SYMBOL(i3c_slave_cfs_remove_func_group);
> > +
> > +static const struct config_item_type i3c_slave_controllers_type = {
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_functions_type = {
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +static const struct config_item_type i3c_slave_type = {
> > +	.ct_owner       = THIS_MODULE,
> > +};
> > +
> > +static struct configfs_subsystem i3c_slave_cfs_subsys = {
> > +	.su_group = {
> > +		.cg_item = {
> > +			.ci_namebuf = "i3c_slave",
> > +			.ci_type = &i3c_slave_type,
> > +		},
> > +	},
> > +	.su_mutex = __MUTEX_INITIALIZER(i3c_slave_cfs_subsys.su_mutex),
> > +};
> > +
> > +static int __init i3c_slave_cfs_init(void)
> > +{
> > +	int ret;
> > +	struct config_group *root = &i3c_slave_cfs_subsys.su_group;
> > +
> > +	config_group_init(root);
> > +
> > +	ret = configfs_register_subsystem(&i3c_slave_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_slave_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_slave_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_slave_cfs_subsys);
> > +
> > +err:
> > +	return ret;
> > +}
> > +module_init(i3c_slave_cfs_init);
> > +
> > +MODULE_DESCRIPTION("I3C FUNC CONFIGFS");
> > +MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
> > +
> > +
> > diff --git a/drivers/i3c/slave.c b/drivers/i3c/slave.c
> > new file mode 100644
> > index 0000000000000..edd7a2888271b
> > --- /dev/null
> > +++ b/drivers/i3c/slave.c
> > @@ -0,0 +1,441 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * configfs to configure the I3C Slave
> > + *
> > + * Copyright (C) 2023 NXP
> > + * Author: Frank Li <Frank.Li@nxp.com>
> > + */
> > +
> > +#include <linux/configfs.h>
> > +#include <linux/module.h>
> > +#include <linux/i3c/slave.h>
> > +#include <linux/idr.h>
> > +#include <linux/slab.h>
> > +
> > +static DEFINE_MUTEX(func_lock);
> > +static struct class *i3c_slave_ctrl_class;
> > +
> > +static void i3c_slave_func_dev_release(struct device *dev)
> > +{
> > +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> > +
> > +	kfree(func->name);
> > +	kfree(func);
> > +}
> > +
> > +static const struct device_type i3c_slave_func_type = {
> > +	.release        = i3c_slave_func_dev_release,
> > +};
> > +
> > +static int i3c_slave_func_match_driver(struct device *dev, struct device_driver *drv)
> > +{
> > +	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
> > +}
> > +
> > +static int i3c_slave_func_device_probe(struct device *dev)
> > +{
> > +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> > +	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
> > +
> > +	if (!driver->probe)
> > +		return -ENODEV;
> > +
> > +	func->driver = driver;
> > +
> > +	return driver->probe(func);
> > +}
> > +
> > +static void i3c_slave_func_device_remove(struct device *dev)
> > +{
> > +	struct i3c_slave_func *func = to_i3c_slave_func(dev);
> > +	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
> > +
> > +	if (driver->remove)
> > +		driver->remove(func);
> > +	func->driver = NULL;
> > +}
> > +
> > +static const struct bus_type i3c_slave_func_bus_type = {
> > +	.name = "i3c_slave_func",
> > +	.probe = i3c_slave_func_device_probe,
> > +	.remove = i3c_slave_func_device_remove,
> > +	.match = i3c_slave_func_match_driver,
> > +};
> > +
> > +static void i3c_slave_ctrl_release(struct device *dev)
> > +{
> > +	kfree(to_i3c_slave_ctrl(dev));
> > +}
> > +
> > +static void devm_i3c_slave_ctrl_release(struct device *dev, void *res)
> > +{
> > +	struct i3c_slave_ctrl *ctrl = *(struct i3c_slave_ctrl **)res;
> > +
> > +	i3c_slave_ctrl_destroy(ctrl);
> > +}
> > +
> > +struct i3c_slave_ctrl *
> > +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> > +		      struct module *owner)
> > +{
> > +	struct i3c_slave_ctrl **ptr, *ctrl;
> > +
> > +	ptr = devres_alloc(devm_i3c_slave_ctrl_release, sizeof(*ptr), GFP_KERNEL);
> > +	if (!ptr)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	ctrl = __i3c_slave_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_slave_ctrl_match(struct device *dev, void *res, void *match_data)
> > +{
> > +	struct i3c_slave_ctrl **ptr = res;
> > +
> > +	return *ptr == match_data;
> > +}
> > +
> > +/**
> > + * __i3c_slave_ctrl_create() - create a new slave controller device
> > + * @dev: device that is creating the new slave controller
> > + * @ops: function pointers for performing slave controller  operations
> > + * @owner: the owner of the module that creates the slave controller device
> > + *
> > + * Return: Pointer to struct i3c_slave_ctrl
> > + */
> > +struct i3c_slave_ctrl *
> > +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> > +			struct module *owner)
> > +{
> > +	struct i3c_slave_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_slave_ctrl_class;
> > +	ctrl->dev.parent = dev;
> > +	ctrl->dev.release = i3c_slave_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_slave_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_slave_ctrl_create);
> > +
> > +/**
> > + * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device
> > + * @dev: device that is creating the new slave controller device
> > + * @ops: function pointers for performing slave controller operations
> > + * @owner: the owner of the module that creates the slave controller device
> > + *
> > + * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it
> > + * also associates the device with the i3c_slave using devres. On driver detach, release function is
> > + * invoked on the devres data, then devres data is freed.
> > + */
> > +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl)
> > +{
> > +	int r;
> > +
> > +	r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match,
> > +			   ctrl);
> > +	dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
> > +}
> > +EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy);
> > +
> > +
> > +
> > +/**
> > + * i3c_slave_ctrl_destroy() - destroy the slave controller device
> > + * @ctrl: the slave controller device that has to be destroyed
> > + *
> > + * Invoke to destroy the I3C slave device
> > + */
> > +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	i3c_slave_cfs_remove_ctrl_group(ctrl->group);
> > +	device_unregister(&ctrl->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy);
> > +
> > +/**
> > + * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller
> > + * @ctrl: the controller device to which the slave function should be added
> > + * @func: the slave function to be added
> > + *
> > + * An I3C slave device can have only one functions.
> > + */
> > +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> > +{
> > +	if (ctrl->func)
> > +		return -EBUSY;
> > +
> > +	ctrl->func = func;
> > +	func->ctrl = ctrl;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func);
> > +
> > +/**
> > + * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller
> > + * @ctrl: the controller device to which the slave function should be removed
> > + * @func: the slave function to be removed
> > + *
> > + * An I3C slave device can have only one functions.
> > + */
> > +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> > +{
> > +	ctrl->func = NULL;
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func);
> > +
> > +
> > +/**
> > + * i3c_slave_ctrl() - get the I3C slave controller
> > + * @name: device name of the slave controller
> > + *
> > + * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the
> > + * slave controller
> > + */
> > +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name)
> > +{
> > +	int ret = -EINVAL;
> > +	struct i3c_slave_ctrl *ctrl;
> > +	struct device *dev;
> > +	struct class_dev_iter iter;
> > +
> > +	class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL);
> > +	while ((dev = class_dev_iter_next(&iter))) {
> > +		if (strcmp(name, dev_name(dev)))
> > +			continue;
> > +
> > +		ctrl = to_i3c_slave_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_slave_ctrl_get);
> > +
> > +/**
> > + * i3c_slave_ctrl_put() - release the I3C endpoint controller
> > + * @slave: slave returned by pci_slave_get()
> > + *
> > + * release the refcount the caller obtained by invoking i3c_slave_ctrl_get()
> > + */
> > +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	if (!ctrl || IS_ERR(ctrl))
> > +		return;
> > +
> > +	module_put(ctrl->ops->owner);
> > +	put_device(&ctrl->dev);
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_put);
> > +
> > +/**
> > + * i3c_slave_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_slave_func_bind(struct i3c_slave_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_slave_func_bind);
> > +
> > +/**
> > + * i3c_slave_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_slave_func_unbind(struct i3c_slave_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_slave_func_unbind);
> > +
> > +/**
> > + * i3c_slave_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_slave_func *i3c_slave_func_create(const char *drv_name, const char *name)
> > +{
> > +	struct i3c_slave_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_slave_func_bus_type;
> > +	dev->type = &i3c_slave_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_slave_func_create);
> > +
> > +/**
> > + * __i3c_slave_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_slave_func_register_driver(struct i3c_slave_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_slave_func_bus_type;
> > +	driver->driver.owner = owner;
> > +
> > +	ret = driver_register(&driver->driver);
> > +	if (ret)
> > +		return ret;
> > +
> > +	i3c_slave_cfs_add_func_group(driver->driver.name);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(__i3c_slave_func_register_driver);
> > +
> > +/**
> > + * i3c_slave_func_unregister_driver() - unregister the I3C function driver
> > + * @driver: the I3C function driver that has to be unregistered
> > + *
> > + * Invoke to unregister the I3C function driver.
> > + */
> > +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *fd)
> > +{
> > +	mutex_lock(&func_lock);
> > +	mutex_unlock(&func_lock);
> > +}
> > +EXPORT_SYMBOL_GPL(i3c_slave_func_unregister_driver);
> > +
> > +static int __init i3c_slave_init(void)
> > +{
> > +	int ret;
> > +
> > +	i3c_slave_ctrl_class = class_create("i3c_slave");
> > +	if (IS_ERR(i3c_slave_ctrl_class)) {
> > +		pr_err("failed to create i3c slave class --> %ld\n",
> > +			PTR_ERR(i3c_slave_ctrl_class));
> > +		return PTR_ERR(i3c_slave_ctrl_class);
> > +	}
> > +
> > +	ret = bus_register(&i3c_slave_func_bus_type);
> > +	if (ret) {
> > +		class_destroy(i3c_slave_ctrl_class);
> > +		pr_err("failed to register i3c slave func bus --> %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +module_init(i3c_slave_init);
> > +
> > +static void __exit i3c_slave_exit(void)
> > +{
> > +	class_destroy(i3c_slave_ctrl_class);
> > +	bus_unregister(&i3c_slave_func_bus_type);
> > +
> > +}
> > +module_exit(i3c_slave_exit);
> > +
> > diff --git a/include/linux/i3c/slave.h b/include/linux/i3c/slave.h
> > new file mode 100644
> > index 0000000000000..a4cbbfc6d6ea9
> > --- /dev/null
> > +++ b/include/linux/i3c/slave.h
> > @@ -0,0 +1,458 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright 2023 NXP.
> > + *
> > + * Author: Frank Li <Frank.Li@nxp.com>
> > + */
> > +
> > +#ifndef I3C_SLAVE_H
> > +#define I3C_SLAVE_H
> > +
> > +#include <linux/device.h>
> > +
> > +struct i3c_slave_func;
> > +struct i3c_slave_ctrl;
> > +
> > +/**
> > + * struct i3c_slave_func_ops - set of function pointers for performing i3c slave 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_slave_func_ops {
> > +	int (*bind)(struct i3c_slave_func *func);
> > +	void (*unbind)(struct i3c_slave_func *func);
> > +};
> > +
> > +/**
> > + * struct i3c_slave_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
> > + * @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_slave_func_driver {
> > +	int (*probe)(struct i3c_slave_func *func);
> > +	void (*remove)(struct i3c_slave_func *func);
> > +
> > +	char *name;
> > +	struct device_driver driver;
> > +	struct i3c_slave_func_ops *ops;
> > +	struct module *owner;
> > +};
> > +
> > +/**
> > + * struct i3c_slave_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_slave_func_ops
> > + * @ctrl: binded I3C controller device
> > + * @is_bound: indicates if bind notification to function driver has been invoked
> > + * @vednor_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: maxium write length
> > + * @max_read_len: maxium read length
> > + * @bcr: bus characteristics register (BCR)
> > + * @dcr: device characteristics register (DCR)
> > + */
> > +struct i3c_slave_func {
> > +	struct device dev;
> > +	char *name;
> > +	struct i3c_slave_func_driver *driver;
> > +	struct config_group *group;
> > +	/* mutex to protect against concurrent access of i3c_slave_func_ops */
> > +	struct mutex lock;
> > +	struct i3c_slave_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
> > + * @status: transfer status
> > + * @actual: how much actually transferred
> > + * @ctrl: I3C slave controller associate with this request
> > + * @tx: transfer direction, 1: slave to master, 0: master to slave
> > + */
> > +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_slave_ctrl *ctrl;
> > +	bool tx;
> > +};
> > +
> > +/**
> > + * struct i3c_slave_ctrl_features - represents I3C slave controller features.
> > + * @tx_fifo_sz: tx hardware fifo size
> > + * @rx_fifo_sz: rx hardware fifo size
> > + */
> > +struct i3c_slave_ctrl_features {
> > +	u32 tx_fifo_sz;
> > +	u32 rx_fifo_sz;
> > +};
> > +
> > +/**
> > + * struct i3c_slave_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: rasie IBI interrupt to master
> > + * @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
> > + * @get_features: ops to get the features supported by the I3C slave controller
> > + * @owner: the module owner containing the ops
> > + */
> > +struct i3c_slave_ctrl_ops {
> > +	int (*set_config)(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> > +	int (*enable)(struct i3c_slave_ctrl *ctrl);
> > +	int (*disable)(struct i3c_slave_ctrl *ctrl);
> > +	int (*raise_ibi)(struct i3c_slave_ctrl *ctrl, void *p, u8 size);
> > +
> > +	struct i3c_request *(*alloc_request)(struct i3c_slave_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_slave_ctrl *ctrl, bool tx);
> > +
> > +	int (*fifo_status)(struct i3c_slave_ctrl *ctrl, bool tx);
> > +	void (*fifo_flush)(struct i3c_slave_ctrl *ctrl, bool tx);
> > +
> > +	const struct i3c_slave_ctrl_features *(*get_features)(struct i3c_slave_ctrl *ctrl);
> > +	struct module *owner;
> > +};
> > +
> > +/**
> > + * struct i3c_slave_ctrl - represents the I3C slave device
> > + * @dev: I3C slave device
> > + * @ops: function pointers for performing endpoint operations
> > + * @func: slave functions present in this controller device
> > + * @group: configfs group representing the I3C controller device
> > + */
> > +struct i3c_slave_ctrl {
> > +	struct device dev;
> > +	const struct i3c_slave_ctrl_ops *ops;
> > +	struct i3c_slave_func *func;
> > +	struct config_group *group;
> > +};
> > +
> > +/**
> > + * i3c_slave_ctrl_raise_ibi() - Raise IBI to master
> > + * @ctrl: I3C slave 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_slave_ctrl_raise_ibi(struct i3c_slave_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_slave_ctrl_cancel_all_reqs() - Cancel all pending request
> > + * @ctrl: I3C slave controller
> > + * @tx: Transfer diretion queue
> > + * @size: size of optional data
> > + */
> > +static inline void i3c_slave_ctrl_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->cancel_all_reqs)
> > +		ctrl->ops->cancel_all_reqs(ctrl, tx);
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_set_config() - Set controller configuration
> > + * @ctrl: I3C slave controller device
> > + * @func: Function device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline int
> > +i3c_slave_ctrl_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->set_config)
> > +		return ctrl->ops->set_config(ctrl, func);
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_enable() - Enable I3C controller
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline int
> > +i3c_slave_ctrl_enable(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->enable)
> > +		return ctrl->ops->enable(ctrl);
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_disable() - Disable I3C controller
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline int
> > +i3c_slave_ctrl_disable(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->disable)
> > +		return ctrl->ops->disable(ctrl);
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_alloc_request() - Alloc an I3C transfer
> > + * @ctrl: I3C slave 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_slave_ctrl_alloc_request(struct i3c_slave_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_slave_ctrl_free_request() - Free an I3C transfer
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline void
> > +i3c_slave_ctrl_free_request(struct i3c_request *req)
> > +{
> > +	struct i3c_slave_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_slave_ctrl_queue() - Queue an I3C transfer
> > + * @ctrl: I3C slave 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 int
> > +i3c_slave_ctrl_queue(struct i3c_request *req, gfp_t gfp_flags)
> > +{
> > +	struct i3c_slave_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_slave_ctrl_dequeue() - Dequeue an I3C transfer
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: Zero for success, or an error code in case of failure
> > + */
> > +static inline int
> > +i3c_slave_ctrl_dequeue(struct i3c_request *req)
> > +{
> > +	struct i3c_slave_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_slave_ctrl_fifo_status() - Get controller FIFO status
> > + * @ctrl: I3C slave controller device
> > + * @tx: 1: Slave to master, 0: master to slave
> > + *
> > + * Returns: How much data in FIFO
> > + */
> > +static inline int
> > +i3c_slave_ctrl_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->fifo_status)
> > +		return ctrl->ops->fifo_status(ctrl, tx);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_fifo_flush() - Flush controller FIFO
> > + * @ctrl: I3C slave controller device
> > + * @tx: 1: Slave to master, 0: master to slave
> > + *
> > + */
> > +static inline void
> > +i3c_slave_ctrl_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->fifo_flush)
> > +		return ctrl->ops->fifo_flush(ctrl, tx);
> > +}
> > +
> > +/**
> > + * i3c_slave_ctrl_get_features() - Get controller supported features
> > + * @ctrl: I3C slave controller device
> > + *
> > + * Returns: The pointer to struct i3c_slave_ctrl_features
> > + */
> > +static inline const struct i3c_slave_ctrl_features*
> > +i3c_slave_ctrl_get_features(struct i3c_slave_ctrl *ctrl)
> > +{
> > +	if (ctrl && ctrl->ops && ctrl->ops->get_features)
> > +		return ctrl->ops->get_features(ctrl);
> > +
> > +	return NULL;
> > +}
> > +
> > +#define to_i3c_slave_ctrl(device) container_of((device), struct i3c_slave_ctrl, dev)
> > +
> > +#define to_i3c_slave_func(func_dev) container_of((func_dev), struct i3c_slave_func, dev)
> > +#define to_i3c_slave_func_driver(drv) (container_of((drv), struct i3c_slave_func_driver, driver))
> > +
> > +#define i3c_slave_ctrl_create(dev, ops) \
> > +		__i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
> > +#define devm_i3c_slave_ctrl_create(dev, ops) \
> > +		__devm_i3c_slave_ctrl_create((dev), (ops), THIS_MODULE)
> > +
> > +struct i3c_slave_ctrl *
> > +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> > +		      struct module *owner);
> > +struct i3c_slave_ctrl *
> > +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
> > +		 struct module *owner);
> > +
> > +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *epc);
> > +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *epc);
> > +
> > +
> > +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> > +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func);
> > +
> > +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl);
> > +
> > +
> > +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group);
> > +struct config_group *i3c_slave_cfs_add_func_group(const char *name);
> > +void i3c_slave_cfs_remove_func_group(struct config_group *group);
> > +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name);
> > +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl);
> > +
> > +int i3c_slave_func_bind(struct i3c_slave_func *func);
> > +void i3c_slave_func_unbind(struct i3c_slave_func *func);
> > +struct i3c_slave_func *i3c_slave_func_create(const char *drv_name, const char *name);
> > +
> > +#define i3c_slave_func_register_driver(drv) \
> > +	__i3c_slave_func_register_driver(drv, THIS_MODULE)
> > +
> > +int __i3c_slave_func_register_driver(struct i3c_slave_func_driver *drv, struct module *owner);
> > +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *drv);
> > +
> > +#define DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops)			\
> > +	static struct i3c_slave_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_SLAVE_INIT(_name, _probe, _remove, _ops)			\
> > +	DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops);			\
> > +	static int __init _name ## mod_init(void)				\
> > +	{									\
> > +		return i3c_slave_func_register_driver(&_name ## i3c_func);	\
> > +	}									\
> > +	static void __exit _name ## mod_exit(void)				\
> > +	{									\
> > +		i3c_slave_func_unregister_driver(&_name ## i3c_func);		\
> > +	}									\
> > +	module_init(_name ## mod_init);						\
> > +	module_exit(_name ## mod_exit)
> > +
> > +#endif
> > +
> > +

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
  2023-09-07 14:28       ` Frank Li
@ 2023-09-13 15:49         ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 32+ messages in thread
From: Krzysztof Kozlowski @ 2023-09-13 15:49 UTC (permalink / raw)
  To: Frank Li
  Cc: miquel.raynal, alexandre.belloni, conor.culhane, imx, linux-i3c,
	linux-kernel

On 07/09/2023 16:28, Frank Li wrote:
> On Wed, Sep 06, 2023 at 10:01:40AM +0200, Krzysztof Kozlowski wrote:
>> On 05/09/2023 23:38, Frank Li wrote:
>>> Add compatible string 'silvaco,i3c-slave-v1' for slave mode.
>>>
>>
>> No, it's the same device.
>>
>> Anyway, this was not tested.
>>
>> Please use scripts/get_maintainers.pl to get a list of necessary people
>> and lists to CC. It might happen, that command when run on an older
>> kernel, gives you outdated entries. Therefore please be sure you base
>> your patches on recent Linux kernel.
>>
>> You missed at least devicetree list (maybe more), so this won't be
>> tested by automated tooling. Performing review on untested code might be
>> a waste of time, thus I will skip this patch entirely till you follow
>> the process allowing the patch to be tested.
>>
>> Please kindly resend and include all necessary To/Cc entries.
> 
> Thank you for your comments. I write notes at RFC cover letter, this
> patches is not totally completed yet. I want to get more feedback about
> i3c slave mode API and configfs design, which will impact the overall
> design.
> 
> So I have not included tty mail list and you in review list.
> I send out all patches just because let i3c reviewer know how to use these
> API. 

Other people send RFC and they, surprise, expect comments. Probably
because RFC means Requests For Comments. If you do not expect comments,
review, call it somehow else, like "BROKEN", so we will know to ignore it.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1
@ 2023-09-13 15:49         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 32+ messages in thread
From: Krzysztof Kozlowski @ 2023-09-13 15:49 UTC (permalink / raw)
  To: Frank Li
  Cc: miquel.raynal, alexandre.belloni, conor.culhane, imx, linux-i3c,
	linux-kernel

On 07/09/2023 16:28, Frank Li wrote:
> On Wed, Sep 06, 2023 at 10:01:40AM +0200, Krzysztof Kozlowski wrote:
>> On 05/09/2023 23:38, Frank Li wrote:
>>> Add compatible string 'silvaco,i3c-slave-v1' for slave mode.
>>>
>>
>> No, it's the same device.
>>
>> Anyway, this was not tested.
>>
>> Please use scripts/get_maintainers.pl to get a list of necessary people
>> and lists to CC. It might happen, that command when run on an older
>> kernel, gives you outdated entries. Therefore please be sure you base
>> your patches on recent Linux kernel.
>>
>> You missed at least devicetree list (maybe more), so this won't be
>> tested by automated tooling. Performing review on untested code might be
>> a waste of time, thus I will skip this patch entirely till you follow
>> the process allowing the patch to be tested.
>>
>> Please kindly resend and include all necessary To/Cc entries.
> 
> Thank you for your comments. I write notes at RFC cover letter, this
> patches is not totally completed yet. I want to get more feedback about
> i3c slave mode API and configfs design, which will impact the overall
> design.
> 
> So I have not included tty mail list and you in review list.
> I send out all patches just because let i3c reviewer know how to use these
> API. 

Other people send RFC and they, surprise, expect comments. Probably
because RFC means Requests For Comments. If you do not expect comments,
review, call it somehow else, like "BROKEN", so we will know to ignore it.

Best regards,
Krzysztof


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2023-09-13 15:50 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-09-05 21:38 [RFC PATCH 0/9] I3C Slave Mode support Frank Li
2023-09-05 21:38 ` Frank Li
2023-09-05 21:38 ` [RFC PATCH 1/9] i3c: add actual in i3c_priv_xfer Frank Li
2023-09-05 21:38   ` Frank Li
2023-09-05 21:38 ` [RFC PATCH 2/9] i3c: svc: rename read_len as actual_len Frank Li
2023-09-05 21:38   ` Frank Li
2023-09-05 21:38 ` [RFC PATCH 3/9] i3c: master: svc return actual transfer data len Frank Li
2023-09-05 21:38   ` Frank Li
2023-09-05 21:38 ` [RFC PATCH 4/9] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave-v1 Frank Li
2023-09-05 21:38   ` Frank Li
2023-09-06  8:01   ` Krzysztof Kozlowski
2023-09-06  8:01     ` Krzysztof Kozlowski
2023-09-07 14:28     ` Frank Li
2023-09-07 14:28       ` Frank Li
2023-09-13 15:49       ` Krzysztof Kozlowski
2023-09-13 15:49         ` Krzysztof Kozlowski
2023-09-05 21:38 ` [RFC PATCH 5/9] i3c: add slave mode support Frank Li
2023-09-05 21:38   ` Frank Li
2023-09-11 11:14   ` Joshua Yeong
2023-09-11 11:14     ` Joshua Yeong
2023-09-11 22:17     ` Frank Li
2023-09-11 22:17       ` Frank Li
2023-09-05 21:38 ` [RFC PATCH 6/9] i3c: slave: add svc slave controller support Frank Li
2023-09-05 21:38   ` Frank Li
2023-09-05 21:38 ` [RFC PATCH 7/9] i3c: slave: func: add tty driver Frank Li
2023-09-05 21:38   ` Frank Li
2023-09-05 21:38 ` [RFC PATCH 8/9] tty: serial: add tty over I3C master side driver Frank Li
2023-09-05 21:38   ` Frank Li
2023-09-06  8:02   ` Krzysztof Kozlowski
2023-09-06  8:02     ` Krzysztof Kozlowski
2023-09-05 21:38 ` [RFC PATCH 9/9] Documentation: i3c: Add I3C slave mode controller and function Frank Li
2023-09-05 21:38   ` Frank Li

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.