This 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
Master side patch:
https://lore.kernel.org/imx/[email protected]/T/#u
Frank Li (5):
i3c: add slave mode support
dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave
i3c: slave: add svc slave controller support
i3c: slave: func: add tty driver
Documentation: i3c: Add I3C slave mode controller and function
.../bindings/i3c/silvaco,i3c-master.yaml | 8 +-
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 | 548 ++++++++++++
drivers/i3c/i3c-cfs.c | 389 +++++++++
drivers/i3c/slave.c | 453 ++++++++++
drivers/i3c/slave/Kconfig | 9 +
drivers/i3c/slave/Makefile | 4 +
drivers/i3c/slave/svc-i3c-slave.c | 795 ++++++++++++++++++
include/linux/i3c/slave.h | 503 +++++++++++
18 files changed, 3276 insertions(+), 3 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 include/linux/i3c/slave.h
--
2.34.1
Add compatible string 'silvaco,i3c-slave' for slave mode.
Signed-off-by: Frank Li <[email protected]>
---
.../devicetree/bindings/i3c/silvaco,i3c-master.yaml | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
index 133855f11b4f5..63db63f00a509 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 <[email protected]>
@@ -14,8 +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
Add tty over I3C slave function driver.
Signed-off-by: Frank Li <[email protected]>
---
drivers/i3c/Kconfig | 1 +
drivers/i3c/Makefile | 1 +
drivers/i3c/func/Kconfig | 9 +
drivers/i3c/func/Makefile | 3 +
drivers/i3c/func/tty.c | 548 ++++++++++++++++++++++++++++++++++++++
5 files changed, 562 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 802a9b3576f13..9b87cb4a84d85 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 # I3C_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..3ebf5bd2592a2
--- /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..ea48db49b2764
--- /dev/null
+++ b/drivers/i3c/func/tty.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <[email protected]>
+ */
+
+#include <linux/iopoll.h>
+#include <linux/i3c/slave.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty_flip.h>
+
+static DEFINE_IDR(i3c_tty_minors);
+static DEFINE_MUTEX(i3c_tty_minors_lock);
+
+static struct tty_driver *i3c_tty_driver;
+
+#define I3C_TTY_MINORS 256
+
+#define I3C_TX_NOEMPTY BIT(0)
+#define I3C_TTY_TRANS_SIZE 16
+#define I3C_TTY_IBI_TX BIT(0)
+
+struct ttyi3c_port {
+ struct tty_port port;
+ int minor;
+ struct i3c_slave_func *i3cdev;
+ struct circ_buf xmit;
+ int tail_in_queue;
+ struct completion txcomplete;
+ spinlock_t xlock;
+ void *buffer;
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ u16 status;
+};
+
+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++)
+ tty_insert_flip_char(&port->port, *(u8 *)(req->buf + i), 0);
+
+ tty_flip_buffer_push(&port->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 *sport = req->context;
+ struct circ_buf *xmit = &sport->xmit;
+ unsigned long flags;
+ int cnt;
+
+ if (req->status == I3C_REQUEST_CANCEL) {
+ i3c_slave_ctrl_free_request(req);
+ return;
+ }
+
+ spin_lock_irqsave(&sport->xlock, flags);
+ xmit->tail = (xmit->tail + req->actual) & (UART_XMIT_SIZE - 1);
+ cnt = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ if (cnt == 0)
+ complete(&sport->txcomplete);
+ else
+ queue_work(sport->workqueue, &sport->work);
+
+ spin_unlock_irqrestore(&sport->xlock, flags);
+
+ if (cnt < WAKEUP_CHARS)
+ tty_port_tty_wakeup(&sport->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->xmit;
+ int cnt = CIRC_CNT(xmit->head, sport->tail_in_queue, UART_XMIT_SIZE);
+ u8 ibi;
+
+ if (cnt == 0)
+ return;
+
+ while (cnt > 0) {
+ struct i3c_request *req = i3c_slave_ctrl_alloc_request(sport->i3cdev->ctrl,
+ GFP_KERNEL);
+ if (!req)
+ return;
+
+ req->length = CIRC_CNT_TO_END(xmit->head, sport->tail_in_queue, UART_XMIT_SIZE);
+
+ req->buf = xmit->buf + sport->tail_in_queue;
+ req->complete = i3c_slave_tty_tx_complete;
+ req->context = sport;
+ req->tx = true;
+
+ if (i3c_slave_ctrl_queue(req, GFP_KERNEL))
+ return;
+
+ sport->tail_in_queue += req->length;
+ sport->tail_in_queue &= UART_XMIT_SIZE - 1;
+
+ cnt = CIRC_CNT(xmit->head, sport->tail_in_queue, UART_XMIT_SIZE);
+ }
+
+ ibi = I3C_TTY_IBI_TX;
+ i3c_slave_ctrl_raise_ibi(sport->i3cdev->ctrl, &ibi, 1);
+}
+
+static int i3c_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+ const struct i3c_slave_ctrl_features *feature;
+ struct i3c_slave_func *func = sport->i3cdev;
+ struct i3c_request *req;
+ int rxfifo_size;
+ int offset = 0;
+
+ feature = i3c_slave_ctrl_get_features(func->ctrl);
+ if (!feature)
+ return -EINVAL;
+
+ rxfifo_size = feature->rx_fifo_sz;
+
+ if (!rxfifo_size)
+ rxfifo_size = I3C_TTY_TRANS_SIZE;
+
+ do {
+ req = i3c_slave_ctrl_alloc_request(func->ctrl, GFP_KERNEL);
+ if (!req)
+ goto err;
+
+ req->buf = (void *) (sport->buffer + offset);
+ req->length = rxfifo_size;
+ req->context = sport;
+ req->complete = i3c_slave_tty_rx_complete;
+ offset += rxfifo_size;
+
+ if (i3c_slave_ctrl_queue(req, GFP_KERNEL))
+ goto err;
+ } while (req && (offset + rxfifo_size) < UART_XMIT_SIZE);
+
+ reinit_completion(&sport->txcomplete);
+
+ return 0;
+err:
+ i3c_slave_ctrl_cancel_all_reqs(func->ctrl, false);
+ return -ENOMEM;
+}
+
+static void i3c_port_shutdown(struct tty_port *port)
+{
+ struct ttyi3c_port *sport =
+ container_of(port, struct ttyi3c_port, port);
+
+ cancel_work_sync(&sport->work);
+
+ i3c_slave_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, true);
+ i3c_slave_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, false);
+
+ sport->xmit.tail = sport->tail_in_queue = sport->xmit.head;
+
+ i3c_slave_ctrl_fifo_flush(sport->i3cdev->ctrl, true);
+ i3c_slave_ctrl_fifo_flush(sport->i3cdev->ctrl, false);
+}
+
+static void i3c_port_destruct(struct tty_port *port)
+{
+ struct ttyi3c_port *sport =
+ container_of(port, struct ttyi3c_port, port);
+
+ mutex_lock(&i3c_tty_minors_lock);
+ idr_remove(&i3c_tty_minors, sport->minor);
+ mutex_unlock(&i3c_tty_minors_lock);
+}
+
+static const struct tty_port_operations i3c_port_ops = {
+ .shutdown = i3c_port_shutdown,
+ .activate = i3c_port_activate,
+ .destruct = i3c_port_destruct,
+};
+
+static int i3c_slave_tty_bind(struct i3c_slave_func *func)
+{
+ struct ttyi3c_port *sport;
+ struct device *tty_dev;
+ int minor;
+ int ret;
+
+ sport = dev_get_drvdata(&func->dev);
+
+ if (i3c_slave_ctrl_set_config(func->ctrl, func)) {
+ dev_err(&func->dev, "failure set i3c config\n");
+ return -EINVAL;
+ }
+
+ sport->buffer = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!sport->buffer)
+ return -ENOMEM;
+
+ sport->xmit.buf = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!sport->xmit.buf)
+ goto err_alloc_xmit;
+
+
+ spin_lock_init(&sport->xlock);
+ init_completion(&sport->txcomplete);
+
+ mutex_lock(&i3c_tty_minors_lock);
+ ret = minor = idr_alloc(&i3c_tty_minors, sport, 0, I3C_TTY_MINORS, GFP_KERNEL);
+ mutex_unlock(&i3c_tty_minors_lock);
+
+ if (minor < 0)
+ goto err_idr_alloc;
+
+ tty_port_init(&sport->port);
+ sport->port.ops = &i3c_port_ops;
+
+ tty_dev = tty_port_register_device(&sport->port, i3c_tty_driver, minor,
+ &func->dev);
+ if (IS_ERR(tty_dev)) {
+ ret = PTR_ERR(tty_dev);
+ goto err_register_port;
+ }
+
+ sport->minor = minor;
+ ret = i3c_slave_ctrl_enable(func->ctrl);
+ if (ret)
+ goto err_ctrl_enable;
+
+ return 0;
+
+err_ctrl_enable:
+ tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
+err_register_port:
+ mutex_lock(&i3c_tty_minors_lock);
+ idr_remove(&i3c_tty_minors, sport->minor);
+ mutex_unlock(&i3c_tty_minors_lock);
+err_idr_alloc:
+ i3c_slave_ctrl_cancel_all_reqs(func->ctrl, false);
+ free_page((unsigned long)sport->xmit.buf);
+err_alloc_xmit:
+ free_page((unsigned long)sport->buffer);
+
+ dev_err(&func->dev, "bind failure\n");
+
+ return ret;
+}
+
+static void i3c_slave_tty_unbind(struct i3c_slave_func *func)
+{
+ struct ttyi3c_port *sport;
+
+ sport = dev_get_drvdata(&func->dev);
+
+ cancel_work_sync(&sport->work);
+
+ i3c_slave_ctrl_disable(func->ctrl);
+ i3c_slave_ctrl_cancel_all_reqs(func->ctrl, 0);
+ i3c_slave_ctrl_cancel_all_reqs(func->ctrl, 1);
+
+ tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
+
+ free_page((unsigned long)sport->buffer);
+}
+
+static struct i3c_slave_func_ops i3c_func_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;
+ 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);
+}
+
+static struct ttyi3c_port *i3c_get_by_minor(unsigned int minor)
+{
+ struct ttyi3c_port *sport;
+
+ mutex_lock(&i3c_tty_minors_lock);
+ sport = idr_find(&i3c_tty_minors, minor);
+ mutex_unlock(&i3c_tty_minors_lock);
+
+ return sport;
+}
+
+static int i3c_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct ttyi3c_port *sport;
+ int ret;
+
+ sport = i3c_get_by_minor(tty->index);
+ if (!sport)
+ return -ENODEV;
+
+ ret = tty_standard_install(driver, tty);
+ if (ret)
+ return ret;
+
+ tty->driver_data = sport;
+
+ return 0;
+}
+
+static ssize_t i3c_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+ struct circ_buf *circ = &sport->xmit;
+ unsigned long flags;
+ int c, ret = 0;
+
+ spin_lock_irqsave(&sport->xlock, flags);
+ while (1) {
+ c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+
+ memcpy(circ->buf + circ->head, buf, c);
+ circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ i3c_slave_ctrl_set_status_format1(sport->i3cdev->ctrl, sport->status | I3C_TX_NOEMPTY);
+ spin_unlock_irqrestore(&sport->xlock, flags);
+
+ if (circ->head != circ->tail)
+ queue_work(sport->workqueue, &sport->work);
+
+ return ret;
+}
+
+static int i3c_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+ struct circ_buf *circ = &sport->xmit;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&sport->xlock, flags);
+
+ if (sport && CIRC_SPACE(circ->head, circ->tail, UART_XMIT_SIZE) != 0) {
+ circ->buf[circ->head] = ch;
+ circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
+ ret = 1;
+ }
+
+ spin_unlock_irqrestore(&sport->xlock, flags);
+
+ return ret;
+}
+
+static void i3c_flush_chars(struct tty_struct *tty)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+ struct circ_buf *circ = &sport->xmit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->xlock, flags);
+ if (CIRC_SPACE(circ->head, circ->tail, UART_XMIT_SIZE))
+ queue_work(sport->workqueue, &sport->work);
+ spin_unlock_irqrestore(&sport->xlock, flags);
+}
+
+static unsigned int i3c_write_room(struct tty_struct *tty)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+ struct circ_buf *circ = &sport->xmit;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&sport->xlock, flags);
+ ret = CIRC_SPACE(circ->head, circ->tail, UART_XMIT_SIZE);
+ spin_unlock_irqrestore(&sport->xlock, flags);
+
+ return ret;
+}
+
+static void i3c_throttle(struct tty_struct *tty)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+
+ i3c_slave_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, false);
+}
+
+static void i3c_unthrottle(struct tty_struct *tty)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+
+ i3c_port_activate(&sport->port, tty);
+}
+
+static int i3c_open(struct tty_struct *tty, struct file *filp)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+ int ret;
+
+ if (!i3c_slave_ctrl_get_addr(sport->i3cdev->ctrl)) {
+ dev_info(&sport->i3cdev->dev, "No slave addr assigned, try hotjoin");
+ ret = i3c_slave_ctrl_hotjoin(sport->i3cdev->ctrl);
+ if (ret) {
+ dev_err(&sport->i3cdev->dev, "Hotjoin failure, check connection");
+ return ret;
+ }
+ }
+
+ return tty_port_open(&sport->port, tty, filp);
+}
+
+static void i3c_close(struct tty_struct *tty, struct file *filp)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+
+ if (!sport)
+ return;
+
+ tty_port_close(tty->port, tty, filp);
+}
+
+static void i3c_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct ttyi3c_port *sport = tty->driver_data;
+ struct circ_buf *circ = &sport->xmit;
+ int val;
+ int ret;
+ u8 ibi = I3C_TTY_IBI_TX;
+ int retry = 100;
+
+ if (circ->head != circ->tail) {
+
+ do {
+ ret = wait_for_completion_timeout(&sport->txcomplete, timeout / 100);
+ if (ret)
+ break;
+ i3c_slave_ctrl_raise_ibi(sport->i3cdev->ctrl, &ibi, 1);
+ } while (retry--);
+
+ reinit_completion(&sport->txcomplete);
+ }
+
+ read_poll_timeout(i3c_slave_ctrl_fifo_status, val, !val, 100, timeout, false,
+ sport->i3cdev->ctrl, true);
+
+ i3c_slave_ctrl_set_status_format1(sport->i3cdev->ctrl, sport->status & (~I3C_TX_NOEMPTY));
+}
+
+static const struct tty_operations i3c_tty_ops = {
+ .install = i3c_install,
+ .open = i3c_open,
+ .close = i3c_close,
+ .write = i3c_write,
+ .put_char = i3c_put_char,
+ .flush_chars = i3c_flush_chars,
+ .write_room = i3c_write_room,
+ .throttle = i3c_throttle,
+ .unthrottle = i3c_unthrottle,
+ .wait_until_sent = i3c_wait_until_sent,
+};
+
+DECLARE_I3C_SLAVE_FUNC(tty, i3c_tty_probe, i3c_tty_remove, &i3c_func_ops);
+
+static int __init i3c_tty_init(void)
+{
+ int ret;
+
+ i3c_tty_driver = tty_alloc_driver(
+ I3C_TTY_MINORS, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
+
+ if (IS_ERR(i3c_tty_driver))
+ return PTR_ERR(i3c_tty_driver);
+
+ i3c_tty_driver->driver_name = "ttySI3C", i3c_tty_driver->name = "ttySI3C",
+ i3c_tty_driver->minor_start = 0,
+ i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
+ i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
+ i3c_tty_driver->init_termios = tty_std_termios;
+ i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
+ CLOCAL;
+ i3c_tty_driver->init_termios.c_lflag = 0;
+
+ tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
+
+ ret = tty_register_driver(i3c_tty_driver);
+ if (ret) {
+ tty_driver_kref_put(i3c_tty_driver);
+ return ret;
+ }
+
+ ret = i3c_slave_func_register_driver(&ttyi3c_func);
+ if (ret) {
+ tty_unregister_driver(i3c_tty_driver);
+ tty_driver_kref_put(i3c_tty_driver);
+ }
+
+ return ret;
+}
+
+static void __exit i3c_tty_exit(void)
+{
+ i3c_slave_func_unregister_driver(&ttyi3c_func);
+ tty_unregister_driver(i3c_tty_driver);
+ tty_driver_kref_put(i3c_tty_driver);
+ idr_destroy(&i3c_tty_minors);
+}
+
+module_init(i3c_tty_init);
+module_exit(i3c_tty_exit);
+
+MODULE_LICENSE("GPL");
+
--
2.34.1
Add Silvaco I3C slave controller support
Signed-off-by: Frank Li <[email protected]>
---
drivers/i3c/Kconfig | 3 +
drivers/i3c/Makefile | 1 +
drivers/i3c/slave/Kconfig | 9 +
drivers/i3c/slave/Makefile | 4 +
drivers/i3c/slave/svc-i3c-slave.c | 795 ++++++++++++++++++++++++++++++
5 files changed, 812 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/Kconfig b/drivers/i3c/Kconfig
index d5f5ca7cd6a56..802a9b3576f13 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -48,3 +48,6 @@ config I3C_SLAVE_CONFIGFS
the slave function and used to bind the function with a slave
controller.
+if I3C_SLAVE
+source "drivers/i3c/slave/Kconfig"
+endif # I3C_SLAVE
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..2de741a9e0ff8
--- /dev/null
+++ b/drivers/i3c/slave/svc-i3c-slave.c
@@ -0,0 +1,795 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <[email protected]>
+ */
+
+#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; /* protect tx queue */
+ struct list_head rxq;
+ spinlock_t rxq_lock; /* protect rx queue */
+ struct list_head cq;
+ spinlock_t cq_lock; /* protect complete queue */
+
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+
+ struct completion dacomplete;
+ struct i3c_slave_ctrl_features features;
+
+ spinlock_t ctrl_lock; /* protext access SCTRL register */
+};
+
+#define I3C_SCONFIG 0x4
+#define I3C_SCONFIG_SLVENA_MASK BIT(0)
+#define I3C_SCONFIG_OFFLINE_MASK BIT(9)
+#define I3C_SCONFIG_SADDR_MASK GENMASK(31, 25)
+
+#define I3C_SSTATUS 0x8
+#define I3C_SSTATUS_STNOTSTOP_MASK BIT(0)
+#define I3C_SSTATUS_STOP_MASK BIT(10)
+#define I3C_SSTATUS_RX_PEND_MASK BIT(11)
+#define I3C_SSTATUS_TXNOTFULL_MASK BIT(12)
+#define I3C_SSTATUS_DACHG_MASK BIT(13)
+#define I3C_SSTATUS_EVDET_MASK GENMASK(21, 20)
+#define I3C_SSTATUS_EVDET_ACKED 0x3
+#define I3C_SSTATUS_IBIDIS_MASK BIT(24)
+#define I3C_SSTATUS_HJDIS_MASK BIT(27)
+
+#define I3C_SCTRL 0xc
+#define I3C_SCTRL_EVENT_MASK GENMASK(1, 0)
+#define I3C_SCTRL_EVENT_IBI 0x1
+#define I3C_SCTRL_EVENT_HOTJOIN 0x3
+#define I3C_SCTRL_EXTDATA_MASK BIT(3)
+#define I3C_SCTRL_IBIDATA_MASK GENMASK(15, 8)
+
+#define I3C_SINTSET 0x10
+#define I3C_SINTCLR 0x14
+#define I3C_SINT_START BIT(8)
+#define I3C_SINT_MATCHED BIT(9)
+#define I3C_SINT_STOP BIT(10)
+#define I3C_SINT_RXPEND BIT(11)
+#define I3C_SINT_TXSEND BIT(12)
+#define I3C_SINT_DACHG BIT(13)
+#define I3C_SINT_CCC BIT(14)
+#define I3C_SINT_ERRWARN BIT(15)
+#define I3C_SINT_DDRMAATCHED BIT(16)
+#define I3C_SINT_CHANDLED BIT(17)
+#define I3C_SINT_EVENT BIT(18)
+#define I3C_SINT_SLVRST BIT(19)
+
+#define I3C_SDATACTRL 0x2c
+#define I3C_SDATACTRL_RXEMPTY_MASK BIT(31)
+#define I3C_SDATACTRL_TXFULL_MASK BIT(30)
+#define I3C_SDATACTRL_RXCOUNT_MASK GENMASK(28, 24)
+#define I3C_SDATACTRL_TXCOUNT_MASK GENMASK(20, 16)
+#define I3C_SDATACTRL_FLUSHFB_MASK BIT(1)
+#define I3C_SDATACTRL_FLUSHTB_MASK BIT(0)
+
+#define I3C_SWDATAB 0x30
+#define I3C_SWDATAB_END_ALSO_MASK BIT(16)
+#define I3C_SWDATAB_END_MASK BIT(8)
+
+#define I3C_SWDATAE 0x34
+#define I3C_SRDATAB 0x40
+
+#define I3C_SCAPABILITIES 0x60
+#define I3C_SCAPABILITIES_FIFOTX_MASK GENMASK(27, 26)
+#define I3C_SCAPABILITIES_FIFORX_MASK GENMASK(29, 28)
+
+#define I3C_SMAXLIMITS 0x68
+#define I3C_SMAXLIMITS_MAXRD_MASK GENMASK(11, 0)
+#define I3C_SMAXLIMITS_MAXWR_MASK GENMASK(27, 16)
+
+#define I3C_SIDPARTNO 0x6c
+
+#define I3C_SIDEXT 0x70
+#define I3C_SIDEXT_BCR_MASK GENMASK(23, 16)
+#define I3C_SIDEXT_DCR_MASK GENMASK(15, 8)
+#define I3C_SVENDORID 0x74
+
+#define I3C_SMAPCTRL0 0x11c
+#define I3C_SMAPCTRL0_ENA_MASK BIT(0)
+#define I3C_SMAPCTRL0_DA_MASK GENMASK(7, 1)
+
+#define I3C_IBIEXT1 0x140
+#define I3C_IBIEXT1_CNT_MASK GEN_MASK(2, 0)
+#define I3C_IBIEXT1_MAX_MASK GEN_MASK(4, 6)
+#define I3C_IBIEXT1_EXT1_SHIFT 8
+#define I3C_IBIEXT1_EXT2_SHIFT 16
+#define I3C_IBIEXT1_EXT3_SHIFT 24
+
+#define I3C_IBIEXT2 0x144
+#define I3C_IBIEXT2_EXT4_SHIFT 0
+#define I3C_IBIEXT2_EXT5_SHIFT 8
+#define I3C_IBIEXT2_EXT6_SHIFT 16
+#define I3C_IBIEXT2_EXT7_SHIFT 24
+
+static int svc_i3c_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 void svc_i3c_queue_complete(struct svc_i3c_slave *svc, struct i3c_request *complete)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&svc->cq_lock, flags);
+ list_add_tail(&complete->list, &svc->cq);
+ spin_unlock_irqrestore(&svc->cq_lock, flags);
+ queue_work(svc->workqueue, &svc->work);
+}
+
+static void svc_i3c_fill_txfifo(struct svc_i3c_slave *svc)
+{
+ struct i3c_request *req, *complete = NULL;
+ unsigned long flags;
+ int val;
+
+ spin_lock_irqsave(&svc->txq_lock, flags);
+ while ((!!(req = list_first_entry_or_null(&svc->txq, struct i3c_request, list))) &&
+ !((readl_relaxed(svc->regs + I3C_SDATACTRL) & I3C_SDATACTRL_TXFULL_MASK))) {
+ while (!(readl_relaxed(svc->regs + I3C_SDATACTRL)
+ & I3C_SDATACTRL_TXFULL_MASK)) {
+ val = *(u8 *)(req->buf + req->actual);
+
+ if (req->actual + 1 == req->length)
+ writel_relaxed(val, svc->regs + I3C_SWDATAE);
+ else
+ writel_relaxed(val, svc->regs + I3C_SWDATAB);
+
+ req->actual++;
+
+ if (req->actual == req->length) {
+ list_del(&req->list);
+ complete = req;
+ spin_unlock_irqrestore(&svc->txq_lock, flags);
+
+ svc_i3c_queue_complete(svc, complete);
+
+ spin_lock_irqsave(&svc->txq_lock, flags);
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&svc->txq_lock, flags);
+}
+
+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)
+ svc_i3c_fill_txfifo(svc);
+
+ if (req->tx)
+ writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTSET);
+ else
+ writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTSET);
+
+ return 0;
+}
+
+static int svc_i3c_dequeue(struct i3c_request *req)
+{
+ struct svc_i3c_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;
+ unsigned long flags;
+ u8 *ibidata = p;
+ u32 ext1 = 0, ext2 = 0;
+ u32 val;
+ int ret;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ if (size && !p)
+ return -EINVAL;
+
+ if (size > 8)
+ return -EINVAL;
+
+ val = readl_relaxed(svc->regs + I3C_SSTATUS);
+ if (val & I3C_SSTATUS_IBIDIS_MASK)
+ return -EINVAL;
+
+ ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+ !(val & I3C_SCTRL_EVENT_MASK), 0, 10000);
+ if (ret) {
+ dev_err(&ctrl->dev, "Timeout when polling for NO event pending");
+ val &= ~I3C_SCTRL_EVENT_MASK;
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ return -ENAVAIL;
+ }
+
+ spin_lock_irqsave(&svc->ctrl_lock, flags);
+
+ val = readl_relaxed(svc->regs + I3C_SCTRL);
+
+ val &= ~I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK;
+ val |= FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_IBI);
+
+ if (size) {
+ val |= FIELD_PREP(I3C_SCTRL_IBIDATA_MASK, *ibidata);
+ ibidata++;
+
+ if (size > 1)
+ val |= I3C_SCTRL_EXTDATA_MASK;
+
+ size--;
+ if (size > 0) {
+ ext1 |= (size + 2);
+ ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT1_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT2_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT3_SHIFT;
+ size--;
+ }
+
+ writel_relaxed(ext1, svc->regs + I3C_IBIEXT1);
+
+ if (size > 0) {
+ ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT4_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT5_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT6_SHIFT;
+ size--;
+ }
+
+ if (size > 0) {
+ ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT7_SHIFT;
+ size--;
+ }
+
+ writeb_relaxed(ext2, svc->regs + I3C_IBIEXT2);
+ }
+
+ /* Issue IBI*/
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+ ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+ !(val & I3C_SCTRL_EVENT_MASK), 0, 1000000);
+ if (ret) {
+ dev_err(&ctrl->dev, "Timeout when polling for IBI finish\n");
+
+ //clear event to above hang bus
+ spin_lock_irqsave(&svc->ctrl_lock, flags);
+ val = readl_relaxed(svc->regs + I3C_SCTRL);
+ val &= ~I3C_SCTRL_EVENT_MASK;
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+ return -ENAVAIL;
+ }
+
+ return 0;
+}
+
+static void svc_i3c_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_DACHG_MASK)
+ complete_all(&svc->dacomplete);
+
+ if (statusFlags & I3C_SSTATUS_RX_PEND_MASK) {
+ spin_lock_irqsave(&svc->rxq_lock, flags);
+ req = list_first_entry_or_null(&svc->rxq, struct i3c_request, list);
+
+ if (!req) {
+ writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTCLR);
+ } else {
+ while (!(readl_relaxed(svc->regs + I3C_SDATACTRL) &
+ I3C_SDATACTRL_RXEMPTY_MASK)) {
+ *(u8 *)(req->buf + req->actual) =
+ readl_relaxed(svc->regs + I3C_SRDATAB);
+ req->actual++;
+
+ if (req->actual == req->length) {
+ complete = req;
+ list_del(&req->list);
+ break;
+ }
+ }
+
+ if (req->actual != req->length && (statusFlags & I3C_SSTATUS_STOP_MASK)) {
+ complete = req;
+ list_del(&req->list);
+ }
+ }
+ spin_unlock_irqrestore(&svc->rxq_lock, flags);
+
+ if (complete) {
+ spin_lock_irqsave(&svc->cq_lock, flags);
+ list_add_tail(&complete->list, &svc->cq);
+ spin_unlock_irqrestore(&svc->cq_lock, flags);
+ queue_work(svc->workqueue, &svc->work);
+ complete = NULL;
+ }
+ }
+
+ if (statusFlags & I3C_SSTATUS_TXNOTFULL_MASK) {
+ svc_i3c_fill_txfifo(svc);
+
+ spin_lock_irqsave(&svc->txq_lock, flags);
+ if (list_empty(&svc->txq))
+ writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTCLR);
+ spin_unlock_irqrestore(&svc->txq_lock, flags);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void svc_i3c_cancel_all_reqs(struct i3c_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 int svc_i3c_hotjoin(struct i3c_slave_ctrl *ctrl)
+{
+ struct svc_i3c_slave *svc;
+ int ret;
+ u32 val;
+ u32 cfg;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+ if (!svc)
+ return -EINVAL;
+
+ reinit_completion(&svc->dacomplete);
+
+ val = readl_relaxed(svc->regs + I3C_SSTATUS);
+ if (val & I3C_SSTATUS_HJDIS_MASK) {
+ dev_err(&ctrl->dev, "Hotjoin disabled by i3c master\n");
+ return -EINVAL;
+ }
+
+ ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+ !(val & I3C_SCTRL_EVENT_MASK), 0, 10000);
+ if (ret) {
+ dev_err(&ctrl->dev, "Timeout when polling for none event pending");
+ return -ENAVAIL;
+ }
+
+ cfg = readl_relaxed(svc->regs + I3C_SCONFIG);
+ cfg |= I3C_SCONFIG_OFFLINE_MASK;
+ writel_relaxed(cfg, svc->regs + I3C_SCONFIG);
+
+ val &= ~(I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK);
+ val |= FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_HOTJOIN);
+ /* Issue hotjoin*/
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+
+ ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val,
+ !(val & I3C_SCTRL_EVENT_MASK), 0, 100000);
+ if (ret) {
+ val &= ~FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_MASK);
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ dev_err(&ctrl->dev, "Timeout when polling for HOTJOIN finish\n");
+ return -EINVAL;
+ }
+
+ val = readl_relaxed(svc->regs + I3C_SSTATUS);
+ val = FIELD_GET(I3C_SSTATUS_EVDET_MASK, val);
+ if (val != I3C_SSTATUS_EVDET_ACKED) {
+ dev_err(&ctrl->dev, "Master NACKED hotjoin request\n");
+ return -EINVAL;
+ }
+
+ writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTSET);
+ ret = wait_for_completion_timeout(&svc->dacomplete, msecs_to_jiffies(100));
+ writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTCLR);
+ if (!ret) {
+ dev_err(&ctrl->dev, "wait for da assignment timeout\n");
+ return -EIO;
+ }
+
+ val = readl_relaxed(svc->regs + I3C_SMAPCTRL0);
+ val = FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val);
+ dev_info(&ctrl->dev, "Get dynamtic address 0x%x\n", val);
+ return 0;
+}
+
+static int svc_i3c_set_status_format1(struct i3c_slave_ctrl *ctrl, u16 status)
+{
+ struct svc_i3c_slave *svc;
+ unsigned long flags;
+ u32 val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ spin_lock_irqsave(&svc->ctrl_lock, flags);
+ val = readl_relaxed(svc->regs + I3C_SCTRL);
+ val &= 0xFFFF;
+ val |= status << 16;
+ writel_relaxed(val, svc->regs + I3C_SCTRL);
+ spin_unlock_irqrestore(&svc->ctrl_lock, flags);
+
+ return 0;
+}
+
+static u16 svc_i3c_get_status_format1(struct i3c_slave_ctrl *ctrl)
+{
+ struct svc_i3c_slave *svc;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ return readl_relaxed(svc->regs + I3C_SCTRL) >> 16;
+}
+
+static u8 svc_i3c_get_addr(struct i3c_slave_ctrl *ctrl)
+{
+ struct svc_i3c_slave *svc;
+ int val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ val = readl_relaxed(svc->regs + I3C_SMAPCTRL0);
+
+ if (val & I3C_SMAPCTRL0_ENA_MASK)
+ return FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val);
+
+ return 0;
+}
+
+int svc_i3c_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx)
+{
+ struct svc_i3c_slave *svc;
+ int val;
+
+ svc = dev_get_drvdata(&ctrl->dev);
+
+ val = readl_relaxed(svc->regs + I3C_SDATACTRL);
+
+ if (tx)
+ return FIELD_GET(I3C_SDATACTRL_TXCOUNT_MASK, val);
+ else
+ return FIELD_GET(I3C_SDATACTRL_RXCOUNT_MASK, val);
+}
+
+static struct i3c_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,
+ .hotjoin = svc_i3c_hotjoin,
+ .fifo_status = svc_i3c_fifo_status,
+ .set_status_format1 = svc_i3c_set_status_format1,
+ .get_status_format1 = svc_i3c_get_status_format1,
+ .get_addr = svc_i3c_get_addr,
+};
+
+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);
+ spin_lock_init(&svc->ctrl_lock);
+
+ init_completion(&svc->dacomplete);
+
+ 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-v1" },
+ { /* 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
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 <[email protected]>
---
drivers/i3c/Kconfig | 26 ++
drivers/i3c/Makefile | 2 +
drivers/i3c/i3c-cfs.c | 389 +++++++++++++++++++++++++++++
drivers/i3c/slave.c | 453 ++++++++++++++++++++++++++++++++++
include/linux/i3c/slave.h | 503 ++++++++++++++++++++++++++++++++++++++
5 files changed, 1373 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..d5f5ca7cd6a56 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -22,3 +22,29 @@ 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.
+
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..35e07da50ebb0
--- /dev/null
+++ b/drivers/i3c/i3c-cfs.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Configfs to configure the I3C Slave
+ *
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <[email protected]>
+ */
+
+#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 ssize_t i3c_slave_ctrl_hotjoin_store(struct config_item *item, const char *page, size_t len)
+{
+ struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(item);
+ struct i3c_slave_ctrl *ctrl;
+ int ret;
+
+ ctrl = ctrl_group->ctrl;
+
+ ret = i3c_slave_ctrl_hotjoin(ctrl);
+ if (ret) {
+ dev_err(&ctrl->dev, "failed to hotjoin i3c slave controller\n");
+ return -EINVAL;
+ }
+
+ return len;
+}
+
+static ssize_t i3c_slave_ctrl_hotjoin_show(struct config_item *item, char *page)
+{
+ return sysfs_emit(page, "%d\n", 0);
+}
+
+CONFIGFS_ATTR(i3c_slave_ctrl_, hotjoin);
+
+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 struct configfs_attribute *i3c_slave_ctrl_attrs[] = {
+ &i3c_slave_ctrl_attr_hotjoin,
+ NULL,
+};
+
+static const struct config_item_type i3c_slave_ctrl_type = {
+ .ct_item_ops = &i3c_slave_ctrl_item_ops,
+ .ct_attrs = i3c_slave_ctrl_attrs,
+ .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 <[email protected]>");
diff --git a/drivers/i3c/slave.c b/drivers/i3c/slave.c
new file mode 100644
index 0000000000000..4d2cea1d09299
--- /dev/null
+++ b/drivers/i3c/slave.c
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * configfs to configure the I3C Slave
+ *
+ * Copyright (C) 2023 NXP
+ * Author: Frank Li <[email protected]>
+ */
+
+#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
+ * @ctrl: slave controller 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_ctrl_hotjoin() - trigger device hotjoin
+ * @ctrl: slave controller
+ *
+ * return: 0: success, others failure
+ */
+int i3c_slave_ctrl_hotjoin(struct i3c_slave_ctrl *ctrl)
+{
+ if (!ctrl || IS_ERR(ctrl))
+ return -EINVAL;
+
+ if (!ctrl->ops->hotjoin)
+ return -EINVAL;
+
+ return ctrl->ops->hotjoin(ctrl);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_hotjoin);
+
+/**
+ * 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..19eb71cfa81f2
--- /dev/null
+++ b/include/linux/i3c/slave.h
@@ -0,0 +1,503 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <[email protected]>
+ */
+
+#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: maximum write length
+ * @max_read_len: maximum 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);
+ int (*hotjoin)(struct i3c_slave_ctrl *ctrl);
+ int (*set_status_format1)(struct i3c_slave_ctrl *ctrl, u16 status);
+ u16 (*get_status_format1)(struct i3c_slave_ctrl *ctrl);
+ u8 (*get_addr)(struct i3c_slave_ctrl *ctrl);
+ 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;
+}
+
+/**
+ * i3c_slave_set_status_format1() - Set controller supported features
+ * &ctrl: I3C slave controller device
+ * &status: I3C GETSTATUS format1
+ *
+ * Returns: Zero for success, or an error code in case of failure
+ */
+static inline int
+i3c_slave_ctrl_set_status_format1(struct i3c_slave_ctrl *ctrl, u16 status)
+{
+ if (ctrl && ctrl->ops && ctrl->ops->set_status_format1)
+ return ctrl->ops->set_status_format1(ctrl, status);
+
+ return -EINVAL;
+}
+
+/**
+ * i3c_slave_get_status_format1() - Get controller supported features
+ * &ctrl: I3C slave controller device
+ *
+ * Return: I3C GETSTATUS format1
+ */
+static inline u16
+i3c_slave_ctrl_get_status_format1(struct i3c_slave_ctrl *ctrl)
+{
+ if (ctrl && ctrl->ops && ctrl->ops->get_status_format1)
+ return ctrl->ops->get_status_format1(ctrl);
+
+ return 0;
+}
+
+/**
+ * i3c_slave_ctrl_get_addr() - Get controller address
+ * &ctrl: I3C slave controller device
+ *
+ * Return: address
+ */
+static inline u8 i3c_slave_ctrl_get_addr(struct i3c_slave_ctrl *ctrl)
+{
+ if (ctrl && ctrl->ops && ctrl->ops->get_addr)
+ return ctrl->ops->get_addr(ctrl);
+
+ return 0;
+}
+
+#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);
+int i3c_slave_ctrl_hotjoin(struct i3c_slave_ctrl *ctrl);
+
+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
Add I3C slave mode and tty over i3c func driver document.
Signed-off-by: Frank Li <[email protected]>
---
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 <[email protected]>
+
+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 <[email protected]>
+
+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 <[email protected]>
+
+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 <[email protected]>
+
+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
On Wed, Oct 18, 2023 at 05:58:09PM -0400, Frank Li wrote:
> Add I3C slave mode and tty over i3c func driver document.
>
> Signed-off-by: Frank Li <[email protected]>
> ---
> 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
Please omit i3c-tty-function.rst. This one accidently add to here.
I will remove it at next version.
> 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 <[email protected]>
> +
> +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 <[email protected]>
> +
> +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 <[email protected]>
> +
> +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 <[email protected]>
> +
> +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
>
On 18/10/2023 23:58, 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 <[email protected]>
> ---
> drivers/i3c/Kconfig | 26 ++
> drivers/i3c/Makefile | 2 +
> drivers/i3c/i3c-cfs.c | 389 +++++++++++++++++++++++++++++
> drivers/i3c/slave.c | 453 ++++++++++++++++++++++++++++++++++
> include/linux/i3c/slave.h | 503 ++++++++++++++++++++++++++++++++++++++
> 5 files changed, 1373 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..d5f5ca7cd6a56 100644
> --- a/drivers/i3c/Kconfig
> +++ b/drivers/i3c/Kconfig
> @@ -22,3 +22,29 @@ menuconfig I3C
> if I3C
> source "drivers/i3c/master/Kconfig"
> endif # I3C
> +
> +config I3C_SLAVE
It doesn't look like you follow Kernel naming convention (see coding style).
> + bool "I3C Slave Support"
> + help
> + Support I3C Slave Mode.
> +
Best regards,
Krzysztof
On 18/10/2023 23:58, Frank Li wrote:
> Add compatible string 'silvaco,i3c-slave' for slave mode.
>
> Signed-off-by: Frank Li <[email protected]>
> ---
> .../devicetree/bindings/i3c/silvaco,i3c-master.yaml | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
> index 133855f11b4f5..63db63f00a509 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 <[email protected]>
> @@ -14,8 +14,10 @@ allOf:
>
> properties:
> compatible:
> - const: silvaco,i3c-master-v1
> -
> + const:
> + enum:
> + - silvaco,i3c-master-v1
> + - silvaco,i3c-slave-v1
Missing blank line.
Rather choose name from Coding style.
Best regards,
Krzysztof
On 18. 10. 23, 23:58, Frank Li wrote:
> Add tty over I3C slave function driver.
Many of the master review comments apply also here. Please fix here too.
More below.
> --- /dev/null
> +++ b/drivers/i3c/func/tty.c
> @@ -0,0 +1,548 @@
...
> +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++)
> + tty_insert_flip_char(&port->port, *(u8 *)(req->buf + i), 0);
Maybe I miss something obvious, but req->buf is void *. So why not
simple tty_insert_flip_string()?
> + sport->buffer = (void *)get_zeroed_page(GFP_KERNEL);
> + if (!sport->buffer)
> + return -ENOMEM;
> +
> + sport->xmit.buf = (void *)get_zeroed_page(GFP_KERNEL);
tty_port_alloc_xmit_buf()
> +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;
> + dev_set_drvdata(&func->dev, port);
> +
> + port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&func->dev));
Another wq? You'd have to have a strong reason for these. Drop that.
> + 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);
That can be on one line.
> +
> + destroy_workqueue(port->workqueue);
> +}
> +static int i3c_open(struct tty_struct *tty, struct file *filp)
> +{
> + struct ttyi3c_port *sport = tty->driver_data;
> + int ret;
> +
> + if (!i3c_slave_ctrl_get_addr(sport->i3cdev->ctrl)) {
> + dev_info(&sport->i3cdev->dev, "No slave addr assigned, try hotjoin");
Should this be a debug print instead?
> + ret = i3c_slave_ctrl_hotjoin(sport->i3cdev->ctrl);
> + if (ret) {
> + dev_err(&sport->i3cdev->dev, "Hotjoin failure, check connection");
> + return ret;
> + }
> + }
> +
> + return tty_port_open(&sport->port, tty, filp);
regards,
--
js
suse labs
On Wed, 18 Oct 2023 17:58:06 -0400, Frank Li wrote:
> Add compatible string 'silvaco,i3c-slave' for slave mode.
>
> Signed-off-by: Frank Li <[email protected]>
> ---
> .../devicetree/bindings/i3c/silvaco,i3c-master.yaml | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml: properties:compatible:const: {'enum': ['silvaco,i3c-master-v1', 'silvaco,i3c-slave-v1']} is not of type 'integer', 'string'
from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml: properties:compatible:const: {'enum': ['silvaco,i3c-master-v1', 'silvaco,i3c-slave-v1']} is not of type 'string'
from schema $id: http://devicetree.org/meta-schemas/string-array.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.example.dtb: i3c-master@a0000000: compatible: {'enum': ['silvaco,i3c-master-v1', 'silvaco,i3c-slave-v1']} was expected
from schema $id: http://devicetree.org/schemas/i3c/silvaco,i3c-master.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.example.dtb: i3c-master@a0000000: Unevaluated properties are not allowed ('compatible' was unexpected)
from schema $id: http://devicetree.org/schemas/i3c/silvaco,i3c-master.yaml#
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
On Thu, Oct 19, 2023 at 09:00:05AM +0200, Krzysztof Kozlowski wrote:
> On 18/10/2023 23:58, 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 <[email protected]>
> > ---
> > drivers/i3c/Kconfig | 26 ++
> > drivers/i3c/Makefile | 2 +
> > drivers/i3c/i3c-cfs.c | 389 +++++++++++++++++++++++++++++
> > drivers/i3c/slave.c | 453 ++++++++++++++++++++++++++++++++++
> > include/linux/i3c/slave.h | 503 ++++++++++++++++++++++++++++++++++++++
> > 5 files changed, 1373 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..d5f5ca7cd6a56 100644
> > --- a/drivers/i3c/Kconfig
> > +++ b/drivers/i3c/Kconfig
> > @@ -22,3 +22,29 @@ menuconfig I3C
> > if I3C
> > source "drivers/i3c/master/Kconfig"
> > endif # I3C
> > +
> > +config I3C_SLAVE
>
> It doesn't look like you follow Kernel naming convention (see coding style).
I checked I3C spec. It use words 'target'.
Is it okay using I3C_TARGET?
>
> > + bool "I3C Slave Support"
> > + help
> > + Support I3C Slave Mode.
> > +
>
>
>
> Best regards,
> Krzysztof
>
On Thu, Oct 19, 2023 at 11:02:10AM -0400, Frank Li wrote:
> On Thu, Oct 19, 2023 at 09:00:05AM +0200, Krzysztof Kozlowski wrote:
> > On 18/10/2023 23:58, 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 <[email protected]>
> > > ---
> > > drivers/i3c/Kconfig | 26 ++
> > > drivers/i3c/Makefile | 2 +
> > > drivers/i3c/i3c-cfs.c | 389 +++++++++++++++++++++++++++++
> > > drivers/i3c/slave.c | 453 ++++++++++++++++++++++++++++++++++
> > > include/linux/i3c/slave.h | 503 ++++++++++++++++++++++++++++++++++++++
> > > 5 files changed, 1373 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..d5f5ca7cd6a56 100644
> > > --- a/drivers/i3c/Kconfig
> > > +++ b/drivers/i3c/Kconfig
> > > @@ -22,3 +22,29 @@ menuconfig I3C
> > > if I3C
> > > source "drivers/i3c/master/Kconfig"
> > > endif # I3C
> > > +
> > > +config I3C_SLAVE
> >
> > It doesn't look like you follow Kernel naming convention (see coding style).
>
> I checked I3C spec. It use words 'target'.
> Is it okay using I3C_TARGET?
Why wouldn't it be?
On Thu, Oct 19, 2023 at 05:46:42PM +0200, Greg KH wrote:
> On Thu, Oct 19, 2023 at 11:02:10AM -0400, Frank Li wrote:
> > On Thu, Oct 19, 2023 at 09:00:05AM +0200, Krzysztof Kozlowski wrote:
> > > On 18/10/2023 23:58, 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 <[email protected]>
> > > > ---
> > > > drivers/i3c/Kconfig | 26 ++
> > > > drivers/i3c/Makefile | 2 +
> > > > drivers/i3c/i3c-cfs.c | 389 +++++++++++++++++++++++++++++
> > > > drivers/i3c/slave.c | 453 ++++++++++++++++++++++++++++++++++
> > > > include/linux/i3c/slave.h | 503 ++++++++++++++++++++++++++++++++++++++
> > > > 5 files changed, 1373 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..d5f5ca7cd6a56 100644
> > > > --- a/drivers/i3c/Kconfig
> > > > +++ b/drivers/i3c/Kconfig
> > > > @@ -22,3 +22,29 @@ menuconfig I3C
> > > > if I3C
> > > > source "drivers/i3c/master/Kconfig"
> > > > endif # I3C
> > > > +
> > > > +config I3C_SLAVE
> > >
> > > It doesn't look like you follow Kernel naming convention (see coding style).
> >
> > I checked I3C spec. It use words 'target'.
> > Is it okay using I3C_TARGET?
>
> Why wouldn't it be?
Sorry, that was snotty of me, long day of driving, my apologies. Yes,
please use "target" and the other terminology to reflect this, and not
the ones you used in your driver as it is against the current kernel
coding style rules.
thanks,
greg k-h
Hi Frank,
kernel test robot noticed the following build warnings:
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v6.6-rc6 next-20231020]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Frank-Li/i3c-add-slave-mode-support/20231019-055940
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20231018215809.3477437-2-Frank.Li%40nxp.com
patch subject: [PATCH 1/5] i3c: add slave mode support
config: sparc-allyesconfig (https://download.01.org/0day-ci/archive/20231020/[email protected]/config)
compiler: sparc64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231020/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/i3c/slave.c:166: warning: Function parameter or member 'ctrl' not described in 'devm_i3c_slave_ctrl_destroy'
>> drivers/i3c/slave.c:166: warning: Excess function parameter 'ops' description in 'devm_i3c_slave_ctrl_destroy'
>> drivers/i3c/slave.c:166: warning: Excess function parameter 'owner' description in 'devm_i3c_slave_ctrl_destroy'
>> drivers/i3c/slave.c:228: warning: expecting prototype for i3c_slave_ctrl(). Prototype was for i3c_slave_ctrl_get() instead
>> drivers/i3c/slave.c:420: warning: Function parameter or member 'fd' not described in 'i3c_slave_func_unregister_driver'
>> drivers/i3c/slave.c:420: warning: Excess function parameter 'driver' description in 'i3c_slave_func_unregister_driver'
vim +166 drivers/i3c/slave.c
a63b2858bd837d Frank Li 2023-10-18 154
a63b2858bd837d Frank Li 2023-10-18 155 /**
a63b2858bd837d Frank Li 2023-10-18 156 * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device
a63b2858bd837d Frank Li 2023-10-18 157 * @dev: device that is creating the new slave controller device
a63b2858bd837d Frank Li 2023-10-18 158 * @ops: function pointers for performing slave controller operations
a63b2858bd837d Frank Li 2023-10-18 159 * @owner: the owner of the module that creates the slave controller device
a63b2858bd837d Frank Li 2023-10-18 160 *
a63b2858bd837d Frank Li 2023-10-18 161 * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it
a63b2858bd837d Frank Li 2023-10-18 162 * also associates the device with the i3c_slave using devres. On driver detach, release function is
a63b2858bd837d Frank Li 2023-10-18 163 * invoked on the devres data, then devres data is freed.
a63b2858bd837d Frank Li 2023-10-18 164 */
a63b2858bd837d Frank Li 2023-10-18 165 void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl)
a63b2858bd837d Frank Li 2023-10-18 @166 {
a63b2858bd837d Frank Li 2023-10-18 167 int r;
a63b2858bd837d Frank Li 2023-10-18 168
a63b2858bd837d Frank Li 2023-10-18 169 r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match,
a63b2858bd837d Frank Li 2023-10-18 170 ctrl);
a63b2858bd837d Frank Li 2023-10-18 171 dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
a63b2858bd837d Frank Li 2023-10-18 172 }
a63b2858bd837d Frank Li 2023-10-18 173 EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy);
a63b2858bd837d Frank Li 2023-10-18 174
a63b2858bd837d Frank Li 2023-10-18 175 /**
a63b2858bd837d Frank Li 2023-10-18 176 * i3c_slave_ctrl_destroy() - destroy the slave controller device
a63b2858bd837d Frank Li 2023-10-18 177 * @ctrl: the slave controller device that has to be destroyed
a63b2858bd837d Frank Li 2023-10-18 178 *
a63b2858bd837d Frank Li 2023-10-18 179 * Invoke to destroy the I3C slave device
a63b2858bd837d Frank Li 2023-10-18 180 */
a63b2858bd837d Frank Li 2023-10-18 181 void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl)
a63b2858bd837d Frank Li 2023-10-18 182 {
a63b2858bd837d Frank Li 2023-10-18 183 i3c_slave_cfs_remove_ctrl_group(ctrl->group);
a63b2858bd837d Frank Li 2023-10-18 184 device_unregister(&ctrl->dev);
a63b2858bd837d Frank Li 2023-10-18 185 }
a63b2858bd837d Frank Li 2023-10-18 186 EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy);
a63b2858bd837d Frank Li 2023-10-18 187
a63b2858bd837d Frank Li 2023-10-18 188 /**
a63b2858bd837d Frank Li 2023-10-18 189 * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller
a63b2858bd837d Frank Li 2023-10-18 190 * @ctrl: the controller device to which the slave function should be added
a63b2858bd837d Frank Li 2023-10-18 191 * @func: the slave function to be added
a63b2858bd837d Frank Li 2023-10-18 192 *
a63b2858bd837d Frank Li 2023-10-18 193 * An I3C slave device can have only one functions.
a63b2858bd837d Frank Li 2023-10-18 194 */
a63b2858bd837d Frank Li 2023-10-18 195 int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
a63b2858bd837d Frank Li 2023-10-18 196 {
a63b2858bd837d Frank Li 2023-10-18 197 if (ctrl->func)
a63b2858bd837d Frank Li 2023-10-18 198 return -EBUSY;
a63b2858bd837d Frank Li 2023-10-18 199
a63b2858bd837d Frank Li 2023-10-18 200 ctrl->func = func;
a63b2858bd837d Frank Li 2023-10-18 201 func->ctrl = ctrl;
a63b2858bd837d Frank Li 2023-10-18 202
a63b2858bd837d Frank Li 2023-10-18 203 return 0;
a63b2858bd837d Frank Li 2023-10-18 204 }
a63b2858bd837d Frank Li 2023-10-18 205 EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func);
a63b2858bd837d Frank Li 2023-10-18 206
a63b2858bd837d Frank Li 2023-10-18 207 /**
a63b2858bd837d Frank Li 2023-10-18 208 * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller
a63b2858bd837d Frank Li 2023-10-18 209 * @ctrl: the controller device to which the slave function should be removed
a63b2858bd837d Frank Li 2023-10-18 210 * @func: the slave function to be removed
a63b2858bd837d Frank Li 2023-10-18 211 *
a63b2858bd837d Frank Li 2023-10-18 212 * An I3C slave device can have only one functions.
a63b2858bd837d Frank Li 2023-10-18 213 */
a63b2858bd837d Frank Li 2023-10-18 214 void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
a63b2858bd837d Frank Li 2023-10-18 215 {
a63b2858bd837d Frank Li 2023-10-18 216 ctrl->func = NULL;
a63b2858bd837d Frank Li 2023-10-18 217 }
a63b2858bd837d Frank Li 2023-10-18 218 EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func);
a63b2858bd837d Frank Li 2023-10-18 219
a63b2858bd837d Frank Li 2023-10-18 220 /**
a63b2858bd837d Frank Li 2023-10-18 221 * i3c_slave_ctrl() - get the I3C slave controller
a63b2858bd837d Frank Li 2023-10-18 222 * @name: device name of the slave controller
a63b2858bd837d Frank Li 2023-10-18 223 *
a63b2858bd837d Frank Li 2023-10-18 224 * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the
a63b2858bd837d Frank Li 2023-10-18 225 * slave controller
a63b2858bd837d Frank Li 2023-10-18 226 */
a63b2858bd837d Frank Li 2023-10-18 227 struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name)
a63b2858bd837d Frank Li 2023-10-18 @228 {
a63b2858bd837d Frank Li 2023-10-18 229 int ret = -EINVAL;
a63b2858bd837d Frank Li 2023-10-18 230 struct i3c_slave_ctrl *ctrl;
a63b2858bd837d Frank Li 2023-10-18 231 struct device *dev;
a63b2858bd837d Frank Li 2023-10-18 232 struct class_dev_iter iter;
a63b2858bd837d Frank Li 2023-10-18 233
a63b2858bd837d Frank Li 2023-10-18 234 class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL);
a63b2858bd837d Frank Li 2023-10-18 235 while ((dev = class_dev_iter_next(&iter))) {
a63b2858bd837d Frank Li 2023-10-18 236 if (strcmp(name, dev_name(dev)))
a63b2858bd837d Frank Li 2023-10-18 237 continue;
a63b2858bd837d Frank Li 2023-10-18 238
a63b2858bd837d Frank Li 2023-10-18 239 ctrl = to_i3c_slave_ctrl(dev);
a63b2858bd837d Frank Li 2023-10-18 240 if (!try_module_get(ctrl->ops->owner)) {
a63b2858bd837d Frank Li 2023-10-18 241 ret = -EINVAL;
a63b2858bd837d Frank Li 2023-10-18 242 goto err;
a63b2858bd837d Frank Li 2023-10-18 243 }
a63b2858bd837d Frank Li 2023-10-18 244
a63b2858bd837d Frank Li 2023-10-18 245 class_dev_iter_exit(&iter);
a63b2858bd837d Frank Li 2023-10-18 246 get_device(&ctrl->dev);
a63b2858bd837d Frank Li 2023-10-18 247 return ctrl;
a63b2858bd837d Frank Li 2023-10-18 248 }
a63b2858bd837d Frank Li 2023-10-18 249
a63b2858bd837d Frank Li 2023-10-18 250 err:
a63b2858bd837d Frank Li 2023-10-18 251 class_dev_iter_exit(&iter);
a63b2858bd837d Frank Li 2023-10-18 252 return ERR_PTR(ret);
a63b2858bd837d Frank Li 2023-10-18 253 }
a63b2858bd837d Frank Li 2023-10-18 254 EXPORT_SYMBOL_GPL(i3c_slave_ctrl_get);
a63b2858bd837d Frank Li 2023-10-18 255
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Frank,
kernel test robot noticed the following build warnings:
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v6.6-rc6 next-20231020]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Frank-Li/i3c-add-slave-mode-support/20231019-055940
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20231018215809.3477437-2-Frank.Li%40nxp.com
patch subject: [PATCH 1/5] i3c: add slave mode support
config: microblaze-allyesconfig (https://download.01.org/0day-ci/archive/20231021/[email protected]/config)
compiler: microblaze-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231021/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/
All warnings (new ones prefixed by >>):
In file included from drivers/i3c/slave.c:11:
include/linux/i3c/slave.h: In function 'i3c_slave_ctrl_alloc_request':
include/linux/i3c/slave.h:272:23: error: implicit declaration of function 'kzalloc' [-Werror=implicit-function-declaration]
272 | req = kzalloc(sizeof(*req), gfp_flags);
| ^~~~~~~
>> include/linux/i3c/slave.h:272:21: warning: assignment to 'struct i3c_request *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
272 | req = kzalloc(sizeof(*req), gfp_flags);
| ^
include/linux/i3c/slave.h: In function 'i3c_slave_ctrl_free_request':
include/linux/i3c/slave.h:298:17: error: implicit declaration of function 'kfree'; did you mean 'kvfree'? [-Werror=implicit-function-declaration]
298 | kfree(req);
| ^~~~~
| kvfree
In file included from drivers/i3c/slave.c:13:
include/linux/slab.h: At top level:
>> include/linux/slab.h:227:6: warning: conflicting types for 'kfree'; have 'void(const void *)'
227 | void kfree(const void *objp);
| ^~~~~
include/linux/i3c/slave.h:298:17: note: previous implicit declaration of 'kfree' with type 'void(const void *)'
298 | kfree(req);
| ^~~~~
include/linux/slab.h:718:37: error: conflicting types for 'kzalloc'; have 'void *(size_t, gfp_t)' {aka 'void *(unsigned int, unsigned int)'}
718 | static inline __alloc_size(1) void *kzalloc(size_t size, gfp_t flags)
| ^~~~~~~
include/linux/i3c/slave.h:272:23: note: previous implicit declaration of 'kzalloc' with type 'int()'
272 | req = kzalloc(sizeof(*req), gfp_flags);
| ^~~~~~~
cc1: some warnings being treated as errors
vim +272 include/linux/i3c/slave.h
a63b2858bd837d Frank Li 2023-10-18 256
a63b2858bd837d Frank Li 2023-10-18 257 /**
a63b2858bd837d Frank Li 2023-10-18 258 * i3c_slave_ctrl_alloc_request() - Alloc an I3C transfer
a63b2858bd837d Frank Li 2023-10-18 259 * @ctrl: I3C slave controller device
a63b2858bd837d Frank Li 2023-10-18 260 * gfp_flags: additional gfp flags used when allocating the buffers
a63b2858bd837d Frank Li 2023-10-18 261 *
a63b2858bd837d Frank Li 2023-10-18 262 * Returns: Zero for success, or an error code in case of failure
a63b2858bd837d Frank Li 2023-10-18 263 */
a63b2858bd837d Frank Li 2023-10-18 264 static inline struct i3c_request *
a63b2858bd837d Frank Li 2023-10-18 265 i3c_slave_ctrl_alloc_request(struct i3c_slave_ctrl *ctrl, gfp_t gfp_flags)
a63b2858bd837d Frank Li 2023-10-18 266 {
a63b2858bd837d Frank Li 2023-10-18 267 struct i3c_request *req = NULL;
a63b2858bd837d Frank Li 2023-10-18 268
a63b2858bd837d Frank Li 2023-10-18 269 if (ctrl && ctrl->ops && ctrl->ops->alloc_request)
a63b2858bd837d Frank Li 2023-10-18 270 req = ctrl->ops->alloc_request(ctrl, gfp_flags);
a63b2858bd837d Frank Li 2023-10-18 271 else
a63b2858bd837d Frank Li 2023-10-18 @272 req = kzalloc(sizeof(*req), gfp_flags);
a63b2858bd837d Frank Li 2023-10-18 273
a63b2858bd837d Frank Li 2023-10-18 274 if (req)
a63b2858bd837d Frank Li 2023-10-18 275 req->ctrl = ctrl;
a63b2858bd837d Frank Li 2023-10-18 276
a63b2858bd837d Frank Li 2023-10-18 277 return req;
a63b2858bd837d Frank Li 2023-10-18 278 }
a63b2858bd837d Frank Li 2023-10-18 279
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Frank,
kernel test robot noticed the following build warnings:
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v6.6-rc6 next-20231020]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Frank-Li/i3c-add-slave-mode-support/20231019-055940
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link: https://lore.kernel.org/r/20231018215809.3477437-4-Frank.Li%40nxp.com
patch subject: [PATCH 3/5] i3c: slave: add svc slave controller support
config: sparc-allyesconfig (https://download.01.org/0day-ci/archive/20231020/[email protected]/config)
compiler: sparc64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231020/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/r/[email protected]/
All warnings (new ones prefixed by >>):
>> drivers/i3c/slave/svc-i3c-slave.c:211:39: warning: no previous prototype for 'svc_i3c_get_features' [-Wmissing-prototypes]
211 | const struct i3c_slave_ctrl_features *svc_i3c_get_features(struct i3c_slave_ctrl *ctrl)
| ^~~~~~~~~~~~~~~~~~~~
>> drivers/i3c/slave/svc-i3c-slave.c:666:5: warning: no previous prototype for 'svc_i3c_fifo_status' [-Wmissing-prototypes]
666 | int svc_i3c_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx)
| ^~~~~~~~~~~~~~~~~~~
vim +/svc_i3c_get_features +211 drivers/i3c/slave/svc-i3c-slave.c
10d1fc84a07d32 Frank Li 2023-10-18 210
10d1fc84a07d32 Frank Li 2023-10-18 @211 const struct i3c_slave_ctrl_features *svc_i3c_get_features(struct i3c_slave_ctrl *ctrl)
10d1fc84a07d32 Frank Li 2023-10-18 212 {
10d1fc84a07d32 Frank Li 2023-10-18 213 struct svc_i3c_slave *svc;
10d1fc84a07d32 Frank Li 2023-10-18 214
10d1fc84a07d32 Frank Li 2023-10-18 215 svc = dev_get_drvdata(&ctrl->dev);
10d1fc84a07d32 Frank Li 2023-10-18 216
10d1fc84a07d32 Frank Li 2023-10-18 217 if (!svc)
10d1fc84a07d32 Frank Li 2023-10-18 218 return NULL;
10d1fc84a07d32 Frank Li 2023-10-18 219
10d1fc84a07d32 Frank Li 2023-10-18 220 return &svc->features;
10d1fc84a07d32 Frank Li 2023-10-18 221 }
10d1fc84a07d32 Frank Li 2023-10-18 222
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki