From matt at codeconstruct.com.au  Mon Oct  2 23:36:22 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Tue,  3 Oct 2023 14:36:22 +0800
Subject: [PATCH net-next v3 1/3] dt-bindings: i3c: Add mctp-controller property
In-Reply-To: <20231003063624.126723-1-matt@codeconstruct.com.au>
References: <20231003063624.126723-1-matt@codeconstruct.com.au>
Message-ID: <20231003063624.126723-2-matt@codeconstruct.com.au>

This property is used to describe a I3C bus with attached MCTP I3C
target devices.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski at linaro.org>
Acked-by: Alexandre Belloni <alexandre.belloni at bootlin.com>
---

v2:

- Reworded DT property description to match I2C

 Documentation/devicetree/bindings/i3c/i3c.yaml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml
index ab69f4115de4..d9483fbd2454 100644
--- a/Documentation/devicetree/bindings/i3c/i3c.yaml
+++ b/Documentation/devicetree/bindings/i3c/i3c.yaml
@@ -55,6 +55,12 @@ properties:
 
       May not be supported by all controllers.
 
+  mctp-controller:
+    type: boolean
+    description: |
+      Indicates that the system is accessible via this bus as an endpoint for
+      MCTP over I3C transport.
+
 required:
   - "#address-cells"
   - "#size-cells"
-- 
2.39.2



From matt at codeconstruct.com.au  Mon Oct  2 23:36:21 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Tue,  3 Oct 2023 14:36:21 +0800
Subject: [PATCH net-next v3 0/3] I3C MCTP net driver
Message-ID: <20231003063624.126723-1-matt@codeconstruct.com.au>

This series adds an I3C transport for the kernel's MCTP network
protocol. MCTP is a communication protocol between system components
(BMCs, drives, NICs etc), with higher level protocols such as NVMe-MI or
PLDM built on top of it (in userspace). It runs over various transports
such as I2C, PCIe, or I3C.

The mctp-i3c driver follows a similar approach to the kernel's existing
mctp-i2c driver, creating a "mctpi3cX" network interface for each
numbered I3C bus. Busses opt in to support by adding a "mctp-controller"
property to the devicetree:

&i3c0 {
        mctp-controller;
}

The driver will bind to MCTP class devices (DCR 0xCC) that are on a
supported I3C bus. Each bus is represented by a `struct mctp_i3c_bus`
that keeps state for the network device. An individual I3C device
(struct mctp_i3c_device) performs operations using the "parent"
mctp_i3c_bus object. The I3C notify/enumeration patch is needed so that
the mctp-i3c driver can handle creating/removing mctp_i3c_bus objects as
required.

The mctp-i3c driver is using the Provisioned ID as an identifier for
target I3C devices (the neighbour address), as that will be more stable
than the I3C dynamic address. The driver internally translates that to a
dynamic address for bus operations.

The driver has been tested using an AST2600 platform. A remote endpoint 
has been tested against QEMU, as well as using the target mode support 
in Aspeed's vendor tree.

I3C maintainers have acked merging this through net-next tree.

Thanks,
Matt

---

v3:

- Use get_unaligned_be48()
- Use kthread_run()
- Don't set net namespace

v2:

- Rebased to net-next
- Removed unnecessary pr_ printing
- Fixed reverse christmas tree ordering
- Reworded DT property description to match I2C

Jeremy Kerr (1):
  i3c: Add support for bus enumeration & notification

Matt Johnston (2):
  dt-bindings: i3c: Add mctp-controller property
  mctp i3c: MCTP I3C driver

 .../devicetree/bindings/i3c/i3c.yaml          |   6 +
 drivers/i3c/master.c                          |  35 +
 drivers/net/mctp/Kconfig                      |   9 +
 drivers/net/mctp/Makefile                     |   1 +
 drivers/net/mctp/mctp-i3c.c                   | 760 ++++++++++++++++++
 include/linux/i3c/master.h                    |  11 +
 6 files changed, 822 insertions(+)
 create mode 100644 drivers/net/mctp/mctp-i3c.c

-- 
2.39.2



From matt at codeconstruct.com.au  Mon Oct  2 23:36:23 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Tue,  3 Oct 2023 14:36:23 +0800
Subject: [PATCH net-next v3 2/3] i3c: Add support for bus enumeration & notification
In-Reply-To: <20231003063624.126723-1-matt@codeconstruct.com.au>
References: <20231003063624.126723-1-matt@codeconstruct.com.au>
Message-ID: <20231003063624.126723-3-matt@codeconstruct.com.au>

From: Jeremy Kerr <jk at codeconstruct.com.au>

This allows other drivers to be notified when new i3c busses are
attached, referring to a whole i3c bus as opposed to individual
devices.

Signed-off-by: Jeremy Kerr <jk at codeconstruct.com.au>
Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
Acked-by: Alexandre Belloni <alexandre.belloni at bootlin.com>
---
 drivers/i3c/master.c       | 35 +++++++++++++++++++++++++++++++++++
 include/linux/i3c/master.h | 11 +++++++++++
 2 files changed, 46 insertions(+)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 87283e4a4607..959ec5269376 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -22,6 +22,7 @@
 static DEFINE_IDR(i3c_bus_idr);
 static DEFINE_MUTEX(i3c_core_lock);
 static int __i3c_first_dynamic_bus_num;
+static BLOCKING_NOTIFIER_HEAD(i3c_bus_notifier);
 
 /**
  * i3c_bus_maintenance_lock - Lock the bus for a maintenance operation
@@ -453,6 +454,36 @@ static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np)
 	return 0;
 }
 
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+			     void *data)
+{
+	struct i3c_bus *bus;
+	int id;
+
+	mutex_lock(&i3c_core_lock);
+	idr_for_each_entry(&i3c_bus_idr, bus, id)
+		fn(bus, data);
+	mutex_unlock(&i3c_core_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_for_each_bus_locked);
+
+int i3c_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_register_notifier);
+
+int i3c_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_unregister_notifier);
+
+static void i3c_bus_notify(struct i3c_bus *bus, unsigned int action)
+{
+	blocking_notifier_call_chain(&i3c_bus_notifier, action, bus);
+}
+
 static const char * const i3c_bus_mode_strings[] = {
 	[I3C_BUS_MODE_PURE] = "pure",
 	[I3C_BUS_MODE_MIXED_FAST] = "mixed-fast",
@@ -2682,6 +2713,8 @@ int i3c_master_register(struct i3c_master_controller *master,
 	if (ret)
 		goto err_del_dev;
 
+	i3c_bus_notify(i3cbus, I3C_NOTIFY_BUS_ADD);
+
 	/*
 	 * We're done initializing the bus and the controller, we can now
 	 * register I3C devices discovered during the initial DAA.
@@ -2714,6 +2747,8 @@ EXPORT_SYMBOL_GPL(i3c_master_register);
  */
 void i3c_master_unregister(struct i3c_master_controller *master)
 {
+	i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
+
 	i3c_master_i2c_adapter_cleanup(master);
 	i3c_master_unregister_i3c_devs(master);
 	i3c_master_bus_cleanup(master);
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 0b52da4f2346..db909ef79be4 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -24,6 +24,12 @@
 
 struct i2c_client;
 
+/* notifier actions. notifier call data is the struct i3c_bus */
+enum {
+	I3C_NOTIFY_BUS_ADD,
+	I3C_NOTIFY_BUS_REMOVE,
+};
+
 struct i3c_master_controller;
 struct i3c_bus;
 struct i3c_device;
@@ -652,4 +658,9 @@ void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot);
 
 struct i3c_ibi_slot *i3c_master_get_free_ibi_slot(struct i3c_dev_desc *dev);
 
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+			     void *data);
+int i3c_register_notifier(struct notifier_block *nb);
+int i3c_unregister_notifier(struct notifier_block *nb);
+
 #endif /* I3C_MASTER_H */
-- 
2.39.2



From matt at codeconstruct.com.au  Mon Oct  2 23:36:24 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Tue,  3 Oct 2023 14:36:24 +0800
Subject: [PATCH net-next v3 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231003063624.126723-1-matt@codeconstruct.com.au>
References: <20231003063624.126723-1-matt@codeconstruct.com.au>
Message-ID: <20231003063624.126723-4-matt@codeconstruct.com.au>

Provides MCTP network transport over an I3C bus, as specified in
DMTF DSP0233.

Each I3C bus (with "mctp-controller" devicetree property) gets an
"mctpi3cX" net device created. I3C devices are reachable as remote
endpoints through that net device. Link layer addressing uses the
I3C PID as a fixed hardware address for neighbour table entries.

The driver matches I3C devices that have the MIPI assigned DCR 0xCC for
MCTP.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
---

v3:

- Use get_unaligned_be48()
- Use kthread_run()
- Don't set net namespace

v2:

- Removed unnecessary pr_ printing
- Fixed reverse christmas tree ordering

 drivers/net/mctp/Kconfig    |   9 +
 drivers/net/mctp/Makefile   |   1 +
 drivers/net/mctp/mctp-i3c.c | 760 ++++++++++++++++++++++++++++++++++++
 3 files changed, 770 insertions(+)
 create mode 100644 drivers/net/mctp/mctp-i3c.c

diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig
index dc71657d9184..ce9d2d2ccf3b 100644
--- a/drivers/net/mctp/Kconfig
+++ b/drivers/net/mctp/Kconfig
@@ -33,6 +33,15 @@ config MCTP_TRANSPORT_I2C
 	  from DMTF specification DSP0237. A MCTP protocol network device is
 	  created for each I2C bus that has been assigned a mctp-i2c device.
 
+config MCTP_TRANSPORT_I3C
+	tristate "MCTP I3C transport"
+	depends on I3C
+	help
+	  Provides a driver to access MCTP devices over I3C transport,
+	  from DMTF specification DSP0233.
+	  A MCTP protocol network device is created for each I3C bus
+	  having a "mctp-controller" devicetree property.
+
 endmenu
 
 endif
diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile
index 1ca3e6028f77..e1cb99ced54a 100644
--- a/drivers/net/mctp/Makefile
+++ b/drivers/net/mctp/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o
 obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o
+obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o
diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c
new file mode 100644
index 000000000000..6d111b7f583c
--- /dev/null
+++ b/drivers/net/mctp/mctp-i3c.c
@@ -0,0 +1,760 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implements DMTF specification
+ * "DSP0233 Management Component Transport Protocol (MCTP) I3C Transport
+ * Binding"
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0233_1.0.0.pdf
+ *
+ * Copyright (c) 2023 Code Construct
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/if_arp.h>
+#include <net/mctp.h>
+#include <net/mctpdevice.h>
+
+#define MCTP_I3C_MAXBUF 65536
+/* 48 bit Provisioned Id */
+#define PID_SIZE 6
+
+/* 64 byte payload, 4 byte MCTP header */
+static const int MCTP_I3C_MINMTU = 64 + 4;
+/* One byte less to allow for the PEC */
+static const int MCTP_I3C_MAXMTU = MCTP_I3C_MAXBUF - 1;
+/* 4 byte MCTP header, no data, 1 byte PEC */
+static const int MCTP_I3C_MINLEN = 4 + 1;
+
+/* Sufficient for 64kB at min mtu */
+static const int MCTP_I3C_TX_QUEUE_LEN = 1100;
+
+/* Somewhat arbitrary */
+static const int MCTP_I3C_IBI_SLOTS = 8;
+
+/* Mandatory Data Byte in an IBI, from DSP0233 */
+static const u8 I3C_MDB_MCTP = 0xAE;
+/* From MIPI Device Characteristics Register (DCR) Assignments */
+static const u8 I3C_DCR_MCTP = 0xCC;
+
+static const char *MCTP_I3C_OF_PROP = "mctp-controller";
+
+/* List of mctp_i3c_busdev */
+static LIST_HEAD(busdevs);
+/* Protects busdevs, as well as mctp_i3c_bus.devs lists */
+static DEFINE_MUTEX(busdevs_lock);
+
+struct mctp_i3c_bus {
+	struct net_device *ndev;
+
+	struct task_struct *tx_thread;
+	wait_queue_head_t tx_wq;
+	/* tx_lock protects tx_skb and devs */
+	spinlock_t tx_lock;
+	/* Next skb to transmit */
+	struct sk_buff *tx_skb;
+	/* Scratch buffer for xmit */
+	u8 tx_scratch[MCTP_I3C_MAXBUF];
+
+	/* Element of busdevs */
+	struct list_head list;
+
+	/* Provisioned ID of our controller */
+	u64 pid;
+
+	struct i3c_bus *bus;
+	/* Head of mctp_i3c_device.list. Protected by busdevs_lock */
+	struct list_head devs;
+};
+
+struct mctp_i3c_device {
+	struct i3c_device *i3c;
+	struct mctp_i3c_bus *mbus;
+	struct list_head list; /* Element of mctp_i3c_bus.devs */
+
+	/* Held while tx_thread is using this device */
+	struct mutex lock;
+
+	/* Whether BCR indicates MDB is present in IBI */
+	bool have_mdb;
+	/* I3C dynamic address */
+	u8 addr;
+	/* Maximum read length */
+	u16 mrl;
+	/* Maximum write length */
+	u16 mwl;
+	/* Provisioned ID */
+	u64 pid;
+};
+
+/* We synthesise a mac header using the Provisioned ID.
+ * Used to pass dest to mctp_i3c_start_xmit.
+ */
+struct mctp_i3c_internal_hdr {
+	u8 dest[PID_SIZE];
+	u8 source[PID_SIZE];
+} __packed;
+
+static int mctp_i3c_read(struct mctp_i3c_device *mi)
+{
+	struct i3c_priv_xfer xfer = { .rnw = 1, .len = mi->mrl };
+	struct net_device_stats *stats = &mi->mbus->ndev->stats;
+	struct mctp_i3c_internal_hdr *ihdr = NULL;
+	struct sk_buff *skb = NULL;
+	struct mctp_skb_cb *cb;
+	int net_status, rc;
+	u8 pec, addr;
+
+	skb = netdev_alloc_skb(mi->mbus->ndev,
+			       mi->mrl + sizeof(struct mctp_i3c_internal_hdr));
+	if (!skb) {
+		stats->rx_dropped++;
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	skb->protocol = htons(ETH_P_MCTP);
+	/* Create a header for internal use */
+	skb_reset_mac_header(skb);
+	ihdr = skb_put(skb, sizeof(struct mctp_i3c_internal_hdr));
+	put_unaligned_be48(mi->pid, ihdr->source);
+	put_unaligned_be48(mi->mbus->pid, ihdr->dest);
+	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
+
+	xfer.data.in = skb_put(skb, mi->mrl);
+
+	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
+	if (rc < 0)
+		goto err;
+
+	if (WARN_ON_ONCE(xfer.len > mi->mrl)) {
+		/* Bad i3c bus driver */
+		rc = -EIO;
+		goto err;
+	}
+	if (xfer.len < MCTP_I3C_MINLEN) {
+		stats->rx_length_errors++;
+		rc = -EIO;
+		goto err;
+	}
+
+	/* check PEC, including address byte */
+	addr = mi->addr << 1 | 1;
+	pec = i2c_smbus_pec(0, &addr, 1);
+	pec = i2c_smbus_pec(pec, xfer.data.in, xfer.len - 1);
+	if (pec != ((u8 *)xfer.data.in)[xfer.len - 1]) {
+		stats->rx_crc_errors++;
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* Remove PEC */
+	skb_trim(skb, xfer.len - 1);
+
+	cb = __mctp_cb(skb);
+	cb->halen = PID_SIZE;
+	put_unaligned_be48(mi->pid, cb->haddr);
+
+	net_status = netif_rx(skb);
+
+	if (net_status == NET_RX_SUCCESS) {
+		stats->rx_packets++;
+		stats->rx_bytes += xfer.len - 1;
+	} else {
+		stats->rx_dropped++;
+	}
+
+	return 0;
+err:
+	kfree_skb(skb);
+	return rc;
+}
+
+static void mctp_i3c_ibi_handler(struct i3c_device *i3c,
+				 const struct i3c_ibi_payload *payload)
+{
+	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
+
+	if (WARN_ON_ONCE(!mi))
+		return;
+
+	if (mi->have_mdb) {
+		if (payload->len > 0) {
+			if (((u8 *)payload->data)[0] != I3C_MDB_MCTP) {
+				/* Not a mctp-i3c interrupt, ignore it */
+				return;
+			}
+		} else {
+			/* The BCR advertised a Mandatory Data Byte but the
+			 * device didn't send one.
+			 */
+			dev_warn_once(i3cdev_to_dev(i3c), "IBI with missing MDB");
+		}
+	}
+
+	mctp_i3c_read(mi);
+}
+
+static int mctp_i3c_setup(struct mctp_i3c_device *mi)
+{
+	bool ibi_set = false, ibi_enabled = false;
+	const struct i3c_ibi_setup ibi = {
+		.max_payload_len = 1,
+		.num_slots = MCTP_I3C_IBI_SLOTS,
+		.handler = mctp_i3c_ibi_handler,
+	};
+	struct i3c_device_info info;
+	int rc;
+
+	i3c_device_get_info(mi->i3c, &info);
+	mi->have_mdb = info.bcr & BIT(2);
+	mi->addr = info.dyn_addr;
+	mi->mwl = info.max_write_len;
+	mi->mrl = info.max_read_len;
+	mi->pid = info.pid;
+
+	rc = i3c_device_request_ibi(mi->i3c, &ibi);
+	if (rc == 0) {
+		ibi_set = true;
+	} else if (rc == -ENOTSUPP) {
+		/* This driver only supports In-Band Interrupt mode. Support for Polling Mode
+		 * could be added if required.
+		 */
+		dev_warn(i3cdev_to_dev(mi->i3c), "Failed, bus driver doesn't support In-Band Interrupts");
+		goto err;
+	} else {
+		dev_err(i3cdev_to_dev(mi->i3c), "Failed requesting IBI (%d)\n", rc);
+		goto err;
+	}
+
+	if (ibi_set) {
+		/* Device setup must be complete when IBI is enabled */
+		rc = i3c_device_enable_ibi(mi->i3c);
+		if (rc < 0) {
+			/* Assume a driver supporting request_ibi also supports enable_ibi */
+			dev_err(i3cdev_to_dev(mi->i3c), "Failed enabling IBI (%d)\n", rc);
+			goto err;
+		}
+		ibi_enabled = true;
+	}
+
+	return 0;
+err:
+	if (ibi_enabled)
+		i3c_device_disable_ibi(mi->i3c);
+	if (ibi_set)
+		i3c_device_free_ibi(mi->i3c);
+	return rc;
+}
+
+/* Adds a new MCTP i3c_device to a bus */
+static int mctp_i3c_add_device(struct mctp_i3c_bus *mbus, struct i3c_device *i3c)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_device *mi = NULL;
+	int rc;
+
+	mi = kzalloc(sizeof(*mi), GFP_KERNEL);
+	if (!mi) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	mi->mbus = mbus;
+	mi->i3c = i3c;
+	mutex_init(&mi->lock);
+	list_add(&mi->list, &mbus->devs);
+
+	i3cdev_set_drvdata(i3c, mi);
+	rc = mctp_i3c_setup(mi);
+	if (rc < 0)
+		goto err;
+
+	return 0;
+err:
+	dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc);
+	if (mi) {
+		list_del(&mi->list);
+		kfree(mi);
+	}
+	return rc;
+}
+
+static int mctp_i3c_probe(struct i3c_device *i3c)
+{
+	struct mctp_i3c_bus *b = NULL, *mbus = NULL;
+	int rc;
+
+	/* Look for a known bus */
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry(b, &busdevs, list)
+		if (b->bus == i3c->bus) {
+			mbus = b;
+			break;
+		}
+	mutex_unlock(&busdevs_lock);
+
+	if (!mbus) {
+		/* probably no "mctp-controller" property on the i3c bus */
+		return -ENODEV;
+	}
+
+	rc = mctp_i3c_add_device(mbus, i3c);
+	if (!rc)
+		goto err;
+
+	return 0;
+
+err:
+	return rc;
+}
+
+static void mctp_i3c_remove_device(struct mctp_i3c_device *mi)
+__must_hold(&busdevs_lock)
+{
+	/* Ensure the tx thread isn't using the device */
+	mutex_lock(&mi->lock);
+
+	/* Counterpart of mctp_i3c_setup */
+	i3c_device_disable_ibi(mi->i3c);
+	i3c_device_free_ibi(mi->i3c);
+
+	/* Counterpart of mctp_i3c_add_device */
+	i3cdev_set_drvdata(mi->i3c, NULL);
+	list_del(&mi->list);
+
+	/* Safe to unlock after removing from the list */
+	mutex_unlock(&mi->lock);
+	kfree(mi);
+}
+
+static void mctp_i3c_remove(struct i3c_device *i3c)
+{
+	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
+
+	/* We my have received a Bus Remove notify prior to device remove,
+	 * so mi will already be removed.
+	 */
+	if (!mi)
+		return;
+
+	mutex_lock(&busdevs_lock);
+	mctp_i3c_remove_device(mi);
+	mutex_unlock(&busdevs_lock);
+}
+
+/* Returns the device for an address, with mi->lock held */
+static struct mctp_i3c_device *
+mctp_i3c_lookup(struct mctp_i3c_bus *mbus, u64 pid)
+{
+	struct mctp_i3c_device *mi = NULL, *ret = NULL;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry(mi, &mbus->devs, list)
+		if (mi->pid == pid) {
+			ret = mi;
+			mutex_lock(&mi->lock);
+			break;
+		}
+	mutex_unlock(&busdevs_lock);
+	return ret;
+}
+
+static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
+{
+	struct net_device_stats *stats = &mbus->ndev->stats;
+	struct i3c_priv_xfer xfer = { .rnw = false };
+	struct mctp_i3c_internal_hdr *ihdr = NULL;
+	struct mctp_i3c_device *mi = NULL;
+	unsigned int data_len;
+	u8 *data = NULL;
+	u8 addr, pec;
+	int rc = 0;
+	u64 pid;
+
+	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
+	data_len = skb->len;
+
+	ihdr = (void *)skb_mac_header(skb);
+
+	pid = get_unaligned_be48(ihdr->dest);
+	mi = mctp_i3c_lookup(mbus, pid);
+	if (!mi) {
+		/* I3C endpoint went away after the packet was enqueued? */
+		stats->tx_dropped++;
+		goto out;
+	}
+
+	if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF))
+		goto out;
+
+	if (data_len + 1 > (unsigned int)mi->mwl) {
+		/* Route MTU was larger than supported by the endpoint */
+		stats->tx_dropped++;
+		goto out;
+	}
+
+	/* Need a linear buffer with space for the PEC */
+	xfer.len = data_len + 1;
+	if (skb_tailroom(skb) >= 1) {
+		skb_put(skb, 1);
+		data = skb->data;
+	} else {
+		// TODO: test this
+		/* Otherwise need to copy the buffer */
+		skb_copy_bits(skb, 0, mbus->tx_scratch, skb->len);
+		data = mbus->tx_scratch;
+	}
+
+	/* PEC calculation */
+	addr = mi->addr << 1;
+	pec = i2c_smbus_pec(0, &addr, 1);
+	pec = i2c_smbus_pec(pec, data, data_len);
+	data[data_len] = pec;
+
+	xfer.data.out = data;
+	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
+	if (rc == 0) {
+		stats->tx_bytes += data_len;
+		stats->tx_packets++;
+	} else {
+		stats->tx_errors++;
+	}
+
+out:
+	if (mi)
+		mutex_unlock(&mi->lock);
+}
+
+static int mctp_i3c_tx_thread(void *data)
+{
+	struct mctp_i3c_bus *mbus = data;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	for (;;) {
+		if (kthread_should_stop())
+			break;
+
+		spin_lock_irqsave(&mbus->tx_lock, flags);
+		skb = mbus->tx_skb;
+		mbus->tx_skb = NULL;
+		spin_unlock_irqrestore(&mbus->tx_lock, flags);
+
+		if (netif_queue_stopped(mbus->ndev))
+			netif_wake_queue(mbus->ndev);
+
+		if (skb) {
+			mctp_i3c_xmit(mbus, skb);
+			kfree_skb(skb);
+		} else {
+			wait_event_idle(mbus->tx_wq,
+					mbus->tx_skb || kthread_should_stop());
+		}
+	}
+
+	return 0;
+}
+
+static netdev_tx_t mctp_i3c_start_xmit(struct sk_buff *skb,
+				       struct net_device *ndev)
+{
+	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
+	unsigned long flags;
+	netdev_tx_t ret;
+
+	spin_lock_irqsave(&mbus->tx_lock, flags);
+	netif_stop_queue(ndev);
+	if (mbus->tx_skb) {
+		dev_warn_ratelimited(&ndev->dev, "TX with queue stopped");
+		ret = NETDEV_TX_BUSY;
+	} else {
+		mbus->tx_skb = skb;
+		ret = NETDEV_TX_OK;
+	}
+	spin_unlock_irqrestore(&mbus->tx_lock, flags);
+
+	if (ret == NETDEV_TX_OK)
+		wake_up(&mbus->tx_wq);
+
+	return ret;
+}
+
+static void mctp_i3c_bus_free(struct mctp_i3c_bus *mbus)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_device *mi = NULL, *tmp = NULL;
+
+	if (mbus->tx_thread) {
+		kthread_stop(mbus->tx_thread);
+		mbus->tx_thread = NULL;
+	}
+
+	/* Remove any child devices */
+	list_for_each_entry_safe(mi, tmp, &mbus->devs, list) {
+		mctp_i3c_remove_device(mi);
+	}
+
+	kfree_skb(mbus->tx_skb);
+	list_del(&mbus->list);
+}
+
+static void mctp_i3c_ndo_uninit(struct net_device *ndev)
+{
+	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
+
+	/* Perform cleanup here to ensure there are no remaining references */
+	mctp_i3c_bus_free(mbus);
+}
+
+static int mctp_i3c_header_create(struct sk_buff *skb, struct net_device *dev,
+				  unsigned short type, const void *daddr,
+	   const void *saddr, unsigned int len)
+{
+	struct mctp_i3c_internal_hdr *ihdr;
+
+	skb_push(skb, sizeof(struct mctp_i3c_internal_hdr));
+	skb_reset_mac_header(skb);
+	ihdr = (void *)skb_mac_header(skb);
+	memcpy(ihdr->dest, daddr, PID_SIZE);
+	memcpy(ihdr->source, saddr, PID_SIZE);
+	return 0;
+}
+
+static const struct net_device_ops mctp_i3c_ops = {
+	.ndo_start_xmit = mctp_i3c_start_xmit,
+	.ndo_uninit = mctp_i3c_ndo_uninit,
+};
+
+static const struct header_ops mctp_i3c_headops = {
+	.create = mctp_i3c_header_create,
+};
+
+static void mctp_i3c_net_setup(struct net_device *dev)
+{
+	dev->type = ARPHRD_MCTP;
+
+	dev->mtu = MCTP_I3C_MAXMTU;
+	dev->min_mtu = MCTP_I3C_MINMTU;
+	dev->max_mtu = MCTP_I3C_MAXMTU;
+	dev->tx_queue_len = MCTP_I3C_TX_QUEUE_LEN;
+
+	dev->hard_header_len = sizeof(struct mctp_i3c_internal_hdr);
+	dev->addr_len = PID_SIZE;
+
+	dev->netdev_ops	= &mctp_i3c_ops;
+	dev->header_ops	= &mctp_i3c_headops;
+}
+
+static bool mctp_i3c_is_mctp_controller(struct i3c_bus *bus)
+{
+	struct i3c_dev_desc *master = bus->cur_master;
+
+	if (!master)
+		return false;
+
+	return of_property_read_bool(master->common.master->dev.of_node, MCTP_I3C_OF_PROP);
+}
+
+/* Returns the Provisioned Id of a local bus master */
+static int mctp_i3c_bus_local_pid(struct i3c_bus *bus, u64 *ret_pid)
+{
+	struct i3c_dev_desc *master;
+
+	master = bus->cur_master;
+	if (WARN_ON_ONCE(!master))
+		return -ENOENT;
+	*ret_pid = master->info.pid;
+
+	return 0;
+}
+
+/* Returns an ERR_PTR on failure */
+static struct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_bus *mbus = NULL;
+	struct net_device *ndev = NULL;
+	char namebuf[IFNAMSIZ];
+	u8 addr[PID_SIZE];
+	int rc;
+
+	if (!mctp_i3c_is_mctp_controller(bus))
+		return ERR_PTR(-ENOENT);
+
+	snprintf(namebuf, sizeof(namebuf), "mctpi3c%d", bus->id);
+	ndev = alloc_netdev(sizeof(*mbus), namebuf, NET_NAME_ENUM, mctp_i3c_net_setup);
+	if (!ndev) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	mbus = netdev_priv(ndev);
+	mbus->ndev = ndev;
+	mbus->bus = bus;
+	INIT_LIST_HEAD(&mbus->devs);
+	list_add(&mbus->list, &busdevs);
+
+	rc = mctp_i3c_bus_local_pid(bus, &mbus->pid);
+	if (rc < 0) {
+		dev_err(&ndev->dev, "No I3C PID available\n");
+		goto err;
+	}
+	put_unaligned_be48(mbus->pid, addr);
+	dev_addr_set(ndev, addr);
+
+	init_waitqueue_head(&mbus->tx_wq);
+	spin_lock_init(&mbus->tx_lock);
+	mbus->tx_thread = kthread_run(mctp_i3c_tx_thread, mbus,
+					 "%s/tx", ndev->name);
+	if (IS_ERR(mbus->tx_thread)) {
+		dev_warn(&ndev->dev, "Error creating thread: %pe\n",
+			mbus->tx_thread);
+		rc = PTR_ERR(mbus->tx_thread);
+		mbus->tx_thread = NULL;
+		goto err;
+	}
+
+	rc = mctp_register_netdev(ndev, NULL);
+	if (rc < 0) {
+		dev_warn(&ndev->dev, "netdev register failed: %d\n", rc);
+		goto err;
+	}
+	return mbus;
+err:
+	/* uninit will not get called if a netdev has not been registered,
+	 * so we perform the same mbus cleanup manually.
+	 */
+	if (mbus)
+		mctp_i3c_bus_free(mbus);
+	if (ndev)
+		free_netdev(ndev);
+	return ERR_PTR(rc);
+}
+
+static void mctp_i3c_bus_remove(struct mctp_i3c_bus *mbus)
+__must_hold(&busdevs_lock)
+{
+	/* Unregister calls through to ndo_uninit -> mctp_i3c_bus_free() */
+	mctp_unregister_netdev(mbus->ndev);
+
+	free_netdev(mbus->ndev);
+	/* mbus is deallocated */
+}
+
+/* Removes all mctp-i3c busses */
+static void mctp_i3c_bus_remove_all(void)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list) {
+		mctp_i3c_bus_remove(mbus);
+	}
+	mutex_unlock(&busdevs_lock);
+}
+
+/* Adds a i3c_bus if it isn't already in the busdevs list.
+ * Suitable as an i3c_for_each_bus_locked callback.
+ */
+static int mctp_i3c_bus_add_new(struct i3c_bus *bus, void *data)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
+	bool exists = false;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
+		if (mbus->bus == bus)
+			exists = true;
+
+	/* It is OK for a bus to already exist. That can occur due to
+	 * the race in mod_init between notifier and for_each_bus
+	 */
+	if (!exists)
+		mctp_i3c_bus_add(bus);
+	mutex_unlock(&busdevs_lock);
+	return 0;
+}
+
+static void mctp_i3c_notify_bus_remove(struct i3c_bus *bus)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
+		if (mbus->bus == bus)
+			mctp_i3c_bus_remove(mbus);
+	mutex_unlock(&busdevs_lock);
+}
+
+static int mctp_i3c_notifier_call(struct notifier_block *nb,
+				  unsigned long action, void *data)
+{
+	switch (action) {
+	case I3C_NOTIFY_BUS_ADD:
+		mctp_i3c_bus_add_new((struct i3c_bus *)data, NULL);
+		break;
+	case I3C_NOTIFY_BUS_REMOVE:
+		mctp_i3c_notify_bus_remove((struct i3c_bus *)data);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block mctp_i3c_notifier = {
+	.notifier_call = mctp_i3c_notifier_call,
+};
+
+static const struct i3c_device_id mctp_i3c_ids[] = {
+	I3C_CLASS(I3C_DCR_MCTP, NULL),
+	{ 0 },
+};
+
+static struct i3c_driver mctp_i3c_driver = {
+	.driver = {
+		.name = "mctp-i3c",
+	},
+	.probe = mctp_i3c_probe,
+	.remove = mctp_i3c_remove,
+	.id_table = mctp_i3c_ids,
+};
+
+static __init int mctp_i3c_mod_init(void)
+{
+	int rc;
+
+	rc = i3c_register_notifier(&mctp_i3c_notifier);
+	if (rc < 0) {
+		i3c_driver_unregister(&mctp_i3c_driver);
+		return rc;
+	}
+
+	i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL);
+
+	rc = i3c_driver_register(&mctp_i3c_driver);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static __exit void mctp_i3c_mod_exit(void)
+{
+	int rc;
+
+	i3c_driver_unregister(&mctp_i3c_driver);
+
+	rc = i3c_unregister_notifier(&mctp_i3c_notifier);
+	if (rc < 0)
+		pr_warn("MCTP I3C could not unregister notifier, %d\n", rc);
+
+	mctp_i3c_bus_remove_all();
+}
+
+module_init(mctp_i3c_mod_init);
+module_exit(mctp_i3c_mod_exit);
+
+MODULE_DEVICE_TABLE(i3c, mctp_i3c_ids);
+MODULE_DESCRIPTION("MCTP I3C device");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt Johnston <matt at codeconstruct.com.au>");
-- 
2.39.2



From matt at codeconstruct.com.au  Tue Oct  3 00:53:39 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Tue,  3 Oct 2023 15:53:39 +0800
Subject: [PATCH] i3c: Fix typo "Provisional ID" to "Provisioned ID"
Message-ID: <20231003075339.197099-1-matt@codeconstruct.com.au>

The MIPI I3C spec refers to a Provisioned ID, since it is (sometimes)
provisioned at device manufacturing.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
---
 Documentation/ABI/testing/sysfs-bus-i3c        | 4 ++--
 Documentation/devicetree/bindings/i3c/i3c.yaml | 4 ++--
 Documentation/driver-api/i3c/protocol.rst      | 4 ++--
 drivers/i3c/master/svc-i3c-master.c            | 2 +-
 include/linux/i3c/device.h                     | 2 +-
 include/linux/i3c/master.h                     | 2 +-
 6 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-i3c b/Documentation/ABI/testing/sysfs-bus-i3c
index 1f4a2662335b..e5248fd67a56 100644
--- a/Documentation/ABI/testing/sysfs-bus-i3c
+++ b/Documentation/ABI/testing/sysfs-bus-i3c
@@ -67,7 +67,7 @@ What:		/sys/bus/i3c/devices/i3c-<bus-id>/pid
 KernelVersion:  5.0
 Contact:	linux-i3c at vger.kernel.org
 Description:
-		PID stands for Provisional ID and is used to uniquely identify
+		PID stands for Provisioned ID and is used to uniquely identify
 		a device on a bus. This PID contains information about the
 		vendor, the part and an instance ID so that several devices of
 		the same type can be connected on the same bus.
@@ -123,7 +123,7 @@ What:		/sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>/pid
 KernelVersion:  5.0
 Contact:	linux-i3c at vger.kernel.org
 Description:
-		PID stands for Provisional ID and is used to uniquely identify
+		PID stands for Provisioned ID and is used to uniquely identify
 		a device on a bus. This PID contains information about the
 		vendor, the part and an instance ID so that several devices of
 		the same type can be connected on the same bus.
diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml
index ab69f4115de4..f8ac7a3e3123 100644
--- a/Documentation/devicetree/bindings/i3c/i3c.yaml
+++ b/Documentation/devicetree/bindings/i3c/i3c.yaml
@@ -119,12 +119,12 @@ patternProperties:
                 minimum: 0
                 maximum: 0x7f
               - description: |
-                  First half of the Provisional ID (following the PID
+                  First half of the Provisioned ID (following the PID
                   definition provided by the I3C specification).
 
                   Contains the manufacturer ID left-shifted by 1.
               - description: |
-                  Second half of the Provisional ID (following the PID
+                  Second half of the Provisioned ID (following the PID
                   definition provided by the I3C specification).
 
                   Contains the ORing of the part ID left-shifted by 16,
diff --git a/Documentation/driver-api/i3c/protocol.rst b/Documentation/driver-api/i3c/protocol.rst
index 02653defa011..23a0b93c62b1 100644
--- a/Documentation/driver-api/i3c/protocol.rst
+++ b/Documentation/driver-api/i3c/protocol.rst
@@ -71,8 +71,8 @@ During DAA, each I3C device reports 3 important things:
   related capabilities
 * DCR: Device Characteristic Register. This 8-bit register describes the
   functionalities provided by the device
-* Provisional ID: A 48-bit unique identifier. On a given bus there should be no
-  Provisional ID collision, otherwise the discovery mechanism may fail.
+* Provisioned ID: A 48-bit unique identifier. On a given bus there should be no
+  Provisioned ID collision, otherwise the discovery mechanism may fail.
 
 I3C slave events
 ================
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 32eca2d6caf0..e23d7900c5a1 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -765,7 +765,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
 			u8 data[6];
 
 			/*
-			 * We only care about the 48-bit provisional ID yet to
+			 * We only care about the 48-bit provisioned ID yet to
 			 * be sure a device does not nack an address twice.
 			 * Otherwise, we would just need to flush the RX FIFO.
 			 */
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 90fa83464f00..84ed77c04940 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -96,7 +96,7 @@ enum i3c_dcr {
 
 /**
  * struct i3c_device_info - I3C device information
- * @pid: Provisional ID
+ * @pid: Provisioned ID
  * @bcr: Bus Characteristic Register
  * @dcr: Device Characteristic Register
  * @static_addr: static/I2C address
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 0b52da4f2346..4fd6a777150f 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -166,7 +166,7 @@ struct i3c_device_ibi_info {
  *		 assigned a dynamic address by the master. Will be used during
  *		 bus initialization to assign it a specific dynamic address
  *		 before starting DAA (Dynamic Address Assignment)
- * @pid: I3C Provisional ID exposed by the device. This is a unique identifier
+ * @pid: I3C Provisioned ID exposed by the device. This is a unique identifier
  *	 that may be used to attach boardinfo to i3c_dev_desc when the device
  *	 does not have a static address
  * @of_node: optional DT node in case the device has been described in the DT
-- 
2.39.2



From miquel.raynal at bootlin.com  Tue Oct  3 01:23:19 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 3 Oct 2023 10:23:19 +0200
Subject: [PATCH] i3c: Fix typo "Provisional ID" to "Provisioned ID"
In-Reply-To: <20231003075339.197099-1-matt@codeconstruct.com.au>
References: <20231003075339.197099-1-matt@codeconstruct.com.au>
Message-ID: <20231003102319.77fdf512@xps-13>

Hi Matt,

matt at codeconstruct.com.au wrote on Tue,  3 Oct 2023 15:53:39 +0800:

> The MIPI I3C spec refers to a Provisioned ID, since it is (sometimes)
> provisioned at device manufacturing.
> 
> Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>

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

Thanks,
Miqu?l


From lkp at intel.com  Tue Oct  3 06:59:32 2023
From: lkp at intel.com (kernel test robot)
Date: Tue, 3 Oct 2023 21:59:32 +0800
Subject: [PATCH net-next v3 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231003063624.126723-4-matt@codeconstruct.com.au>
References: <20231003063624.126723-4-matt@codeconstruct.com.au>
Message-ID: <202310032142.NaWYwlZc-lkp@intel.com>

Hi Matt,

kernel test robot noticed the following build errors:

[auto build test ERROR on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Matt-Johnston/dt-bindings-i3c-Add-mctp-controller-property/20231003-144037
base:   net-next/main
patch link:    https://lore.kernel.org/r/20231003063624.126723-4-matt%40codeconstruct.com.au
patch subject: [PATCH net-next v3 3/3] mctp i3c: MCTP I3C driver
config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20231003/202310032142.NaWYwlZc-lkp at intel.com/config)
compiler: sh4-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231003/202310032142.NaWYwlZc-lkp at intel.com/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 <lkp at intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202310032142.NaWYwlZc-lkp at intel.com/

All errors (new ones prefixed by >>):

   drivers/net/mctp/mctp-i3c.c: In function 'mctp_i3c_read':
>> drivers/net/mctp/mctp-i3c.c:121:9: error: implicit declaration of function 'put_unaligned_be48' [-Werror=implicit-function-declaration]
     121 |         put_unaligned_be48(mi->pid, ihdr->source);
         |         ^~~~~~~~~~~~~~~~~~
   drivers/net/mctp/mctp-i3c.c: In function 'mctp_i3c_xmit':
>> drivers/net/mctp/mctp-i3c.c:380:15: error: implicit declaration of function 'get_unaligned_be48'; did you mean 'get_unalign_ctl'? [-Werror=implicit-function-declaration]
     380 |         pid = get_unaligned_be48(ihdr->dest);
         |               ^~~~~~~~~~~~~~~~~~
         |               get_unalign_ctl
   cc1: some warnings being treated as errors


vim +/put_unaligned_be48 +121 drivers/net/mctp/mctp-i3c.c

    98	
    99	static int mctp_i3c_read(struct mctp_i3c_device *mi)
   100	{
   101		struct i3c_priv_xfer xfer = { .rnw = 1, .len = mi->mrl };
   102		struct net_device_stats *stats = &mi->mbus->ndev->stats;
   103		struct mctp_i3c_internal_hdr *ihdr = NULL;
   104		struct sk_buff *skb = NULL;
   105		struct mctp_skb_cb *cb;
   106		int net_status, rc;
   107		u8 pec, addr;
   108	
   109		skb = netdev_alloc_skb(mi->mbus->ndev,
   110				       mi->mrl + sizeof(struct mctp_i3c_internal_hdr));
   111		if (!skb) {
   112			stats->rx_dropped++;
   113			rc = -ENOMEM;
   114			goto err;
   115		}
   116	
   117		skb->protocol = htons(ETH_P_MCTP);
   118		/* Create a header for internal use */
   119		skb_reset_mac_header(skb);
   120		ihdr = skb_put(skb, sizeof(struct mctp_i3c_internal_hdr));
 > 121		put_unaligned_be48(mi->pid, ihdr->source);
   122		put_unaligned_be48(mi->mbus->pid, ihdr->dest);
   123		skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
   124	
   125		xfer.data.in = skb_put(skb, mi->mrl);
   126	
   127		rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
   128		if (rc < 0)
   129			goto err;
   130	
   131		if (WARN_ON_ONCE(xfer.len > mi->mrl)) {
   132			/* Bad i3c bus driver */
   133			rc = -EIO;
   134			goto err;
   135		}
   136		if (xfer.len < MCTP_I3C_MINLEN) {
   137			stats->rx_length_errors++;
   138			rc = -EIO;
   139			goto err;
   140		}
   141	
   142		/* check PEC, including address byte */
   143		addr = mi->addr << 1 | 1;
   144		pec = i2c_smbus_pec(0, &addr, 1);
   145		pec = i2c_smbus_pec(pec, xfer.data.in, xfer.len - 1);
   146		if (pec != ((u8 *)xfer.data.in)[xfer.len - 1]) {
   147			stats->rx_crc_errors++;
   148			rc = -EINVAL;
   149			goto err;
   150		}
   151	
   152		/* Remove PEC */
   153		skb_trim(skb, xfer.len - 1);
   154	
   155		cb = __mctp_cb(skb);
   156		cb->halen = PID_SIZE;
   157		put_unaligned_be48(mi->pid, cb->haddr);
   158	
   159		net_status = netif_rx(skb);
   160	
   161		if (net_status == NET_RX_SUCCESS) {
   162			stats->rx_packets++;
   163			stats->rx_bytes += xfer.len - 1;
   164		} else {
   165			stats->rx_dropped++;
   166		}
   167	
   168		return 0;
   169	err:
   170		kfree_skb(skb);
   171		return rc;
   172	}
   173	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


From matt at codeconstruct.com.au  Tue Oct  3 20:13:14 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Wed,  4 Oct 2023 11:13:14 +0800
Subject: [PATCH net-next v4 1/3] dt-bindings: i3c: Add mctp-controller property
In-Reply-To: <20231004031316.725107-1-matt@codeconstruct.com.au>
References: <20231004031316.725107-1-matt@codeconstruct.com.au>
Message-ID: <20231004031316.725107-2-matt@codeconstruct.com.au>

This property is used to describe a I3C bus with attached MCTP I3C
target devices.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski at linaro.org>
Acked-by: Alexandre Belloni <alexandre.belloni at bootlin.com>
---

v2:

- Reworded DT property description to match I2C

 Documentation/devicetree/bindings/i3c/i3c.yaml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml
index ab69f4115de4..d9483fbd2454 100644
--- a/Documentation/devicetree/bindings/i3c/i3c.yaml
+++ b/Documentation/devicetree/bindings/i3c/i3c.yaml
@@ -55,6 +55,12 @@ properties:
 
       May not be supported by all controllers.
 
+  mctp-controller:
+    type: boolean
+    description: |
+      Indicates that the system is accessible via this bus as an endpoint for
+      MCTP over I3C transport.
+
 required:
   - "#address-cells"
   - "#size-cells"
-- 
2.39.2



From matt at codeconstruct.com.au  Tue Oct  3 20:13:13 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Wed,  4 Oct 2023 11:13:13 +0800
Subject: [PATCH net-next v4 0/3] I3C MCTP net driver
Message-ID: <20231004031316.725107-1-matt@codeconstruct.com.au>

This series adds an I3C transport for the kernel's MCTP network
protocol. MCTP is a communication protocol between system components
(BMCs, drives, NICs etc), with higher level protocols such as NVMe-MI or
PLDM built on top of it (in userspace). It runs over various transports
such as I2C, PCIe, or I3C.

The mctp-i3c driver follows a similar approach to the kernel's existing
mctp-i2c driver, creating a "mctpi3cX" network interface for each
numbered I3C bus. Busses opt in to support by adding a "mctp-controller"
property to the devicetree:

&i3c0 {
        mctp-controller;
}

The driver will bind to MCTP class devices (DCR 0xCC) that are on a
supported I3C bus. Each bus is represented by a `struct mctp_i3c_bus`
that keeps state for the network device. An individual I3C device
(struct mctp_i3c_device) performs operations using the "parent"
mctp_i3c_bus object. The I3C notify/enumeration patch is needed so that
the mctp-i3c driver can handle creating/removing mctp_i3c_bus objects as
required.

The mctp-i3c driver is using the Provisioned ID as an identifier for
target I3C devices (the neighbour address), as that will be more stable
than the I3C dynamic address. The driver internally translates that to a
dynamic address for bus operations.

The driver has been tested using an AST2600 platform. A remote endpoint 
has been tested against QEMU, as well as using the target mode support 
in Aspeed's vendor tree.

I3C maintainers have acked merging this through net-next tree.

Thanks,
Matt

---

v4:

- Add asm/unaligned.h include

v3:

- Use get_unaligned_be48()
- Use kthread_run()
- Don't set net namespace

v2:

- Rebased to net-next
- Removed unnecessary pr_ printing
- Fixed reverse christmas tree ordering
- Reworded DT property description to match I2C

Jeremy Kerr (1):
  i3c: Add support for bus enumeration & notification

Matt Johnston (2):
  dt-bindings: i3c: Add mctp-controller property
  mctp i3c: MCTP I3C driver

 .../devicetree/bindings/i3c/i3c.yaml          |   6 +
 drivers/i3c/master.c                          |  35 +
 drivers/net/mctp/Kconfig                      |   9 +
 drivers/net/mctp/Makefile                     |   1 +
 drivers/net/mctp/mctp-i3c.c                   | 761 ++++++++++++++++++
 include/linux/i3c/master.h                    |  11 +
 6 files changed, 823 insertions(+)
 create mode 100644 drivers/net/mctp/mctp-i3c.c

-- 
2.39.2



From matt at codeconstruct.com.au  Tue Oct  3 20:13:15 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Wed,  4 Oct 2023 11:13:15 +0800
Subject: [PATCH net-next v4 2/3] i3c: Add support for bus enumeration & notification
In-Reply-To: <20231004031316.725107-1-matt@codeconstruct.com.au>
References: <20231004031316.725107-1-matt@codeconstruct.com.au>
Message-ID: <20231004031316.725107-3-matt@codeconstruct.com.au>

From: Jeremy Kerr <jk at codeconstruct.com.au>

This allows other drivers to be notified when new i3c busses are
attached, referring to a whole i3c bus as opposed to individual
devices.

Signed-off-by: Jeremy Kerr <jk at codeconstruct.com.au>
Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
Acked-by: Alexandre Belloni <alexandre.belloni at bootlin.com>
---
 drivers/i3c/master.c       | 35 +++++++++++++++++++++++++++++++++++
 include/linux/i3c/master.h | 11 +++++++++++
 2 files changed, 46 insertions(+)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 87283e4a4607..959ec5269376 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -22,6 +22,7 @@
 static DEFINE_IDR(i3c_bus_idr);
 static DEFINE_MUTEX(i3c_core_lock);
 static int __i3c_first_dynamic_bus_num;
+static BLOCKING_NOTIFIER_HEAD(i3c_bus_notifier);
 
 /**
  * i3c_bus_maintenance_lock - Lock the bus for a maintenance operation
@@ -453,6 +454,36 @@ static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np)
 	return 0;
 }
 
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+			     void *data)
+{
+	struct i3c_bus *bus;
+	int id;
+
+	mutex_lock(&i3c_core_lock);
+	idr_for_each_entry(&i3c_bus_idr, bus, id)
+		fn(bus, data);
+	mutex_unlock(&i3c_core_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_for_each_bus_locked);
+
+int i3c_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_register_notifier);
+
+int i3c_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_unregister_notifier);
+
+static void i3c_bus_notify(struct i3c_bus *bus, unsigned int action)
+{
+	blocking_notifier_call_chain(&i3c_bus_notifier, action, bus);
+}
+
 static const char * const i3c_bus_mode_strings[] = {
 	[I3C_BUS_MODE_PURE] = "pure",
 	[I3C_BUS_MODE_MIXED_FAST] = "mixed-fast",
@@ -2682,6 +2713,8 @@ int i3c_master_register(struct i3c_master_controller *master,
 	if (ret)
 		goto err_del_dev;
 
+	i3c_bus_notify(i3cbus, I3C_NOTIFY_BUS_ADD);
+
 	/*
 	 * We're done initializing the bus and the controller, we can now
 	 * register I3C devices discovered during the initial DAA.
@@ -2714,6 +2747,8 @@ EXPORT_SYMBOL_GPL(i3c_master_register);
  */
 void i3c_master_unregister(struct i3c_master_controller *master)
 {
+	i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
+
 	i3c_master_i2c_adapter_cleanup(master);
 	i3c_master_unregister_i3c_devs(master);
 	i3c_master_bus_cleanup(master);
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 0b52da4f2346..db909ef79be4 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -24,6 +24,12 @@
 
 struct i2c_client;
 
+/* notifier actions. notifier call data is the struct i3c_bus */
+enum {
+	I3C_NOTIFY_BUS_ADD,
+	I3C_NOTIFY_BUS_REMOVE,
+};
+
 struct i3c_master_controller;
 struct i3c_bus;
 struct i3c_device;
@@ -652,4 +658,9 @@ void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot);
 
 struct i3c_ibi_slot *i3c_master_get_free_ibi_slot(struct i3c_dev_desc *dev);
 
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+			     void *data);
+int i3c_register_notifier(struct notifier_block *nb);
+int i3c_unregister_notifier(struct notifier_block *nb);
+
 #endif /* I3C_MASTER_H */
-- 
2.39.2



From matt at codeconstruct.com.au  Tue Oct  3 20:13:16 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Wed,  4 Oct 2023 11:13:16 +0800
Subject: [PATCH net-next v4 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231004031316.725107-1-matt@codeconstruct.com.au>
References: <20231004031316.725107-1-matt@codeconstruct.com.au>
Message-ID: <20231004031316.725107-4-matt@codeconstruct.com.au>

Provides MCTP network transport over an I3C bus, as specified in
DMTF DSP0233.

Each I3C bus (with "mctp-controller" devicetree property) gets an
"mctpi3cX" net device created. I3C devices are reachable as remote
endpoints through that net device. Link layer addressing uses the
I3C PID as a fixed hardware address for neighbour table entries.

The driver matches I3C devices that have the MIPI assigned DCR 0xCC for
MCTP.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
---

v4:

- Add asm/unaligned.h include

v3:

- Use get_unaligned_be48()
- Use kthread_run()
- Don't set net namespace

v2:

- Removed unnecessary pr_ printing
- Fixed reverse christmas tree ordering

 drivers/net/mctp/Kconfig    |   9 +
 drivers/net/mctp/Makefile   |   1 +
 drivers/net/mctp/mctp-i3c.c | 761 ++++++++++++++++++++++++++++++++++++
 3 files changed, 771 insertions(+)
 create mode 100644 drivers/net/mctp/mctp-i3c.c

diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig
index dc71657d9184..ce9d2d2ccf3b 100644
--- a/drivers/net/mctp/Kconfig
+++ b/drivers/net/mctp/Kconfig
@@ -33,6 +33,15 @@ config MCTP_TRANSPORT_I2C
 	  from DMTF specification DSP0237. A MCTP protocol network device is
 	  created for each I2C bus that has been assigned a mctp-i2c device.
 
+config MCTP_TRANSPORT_I3C
+	tristate "MCTP I3C transport"
+	depends on I3C
+	help
+	  Provides a driver to access MCTP devices over I3C transport,
+	  from DMTF specification DSP0233.
+	  A MCTP protocol network device is created for each I3C bus
+	  having a "mctp-controller" devicetree property.
+
 endmenu
 
 endif
diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile
index 1ca3e6028f77..e1cb99ced54a 100644
--- a/drivers/net/mctp/Makefile
+++ b/drivers/net/mctp/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o
 obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o
+obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o
diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c
new file mode 100644
index 000000000000..d67314a84630
--- /dev/null
+++ b/drivers/net/mctp/mctp-i3c.c
@@ -0,0 +1,761 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implements DMTF specification
+ * "DSP0233 Management Component Transport Protocol (MCTP) I3C Transport
+ * Binding"
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0233_1.0.0.pdf
+ *
+ * Copyright (c) 2023 Code Construct
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/if_arp.h>
+#include <asm/unaligned.h>
+#include <net/mctp.h>
+#include <net/mctpdevice.h>
+
+#define MCTP_I3C_MAXBUF 65536
+/* 48 bit Provisioned Id */
+#define PID_SIZE 6
+
+/* 64 byte payload, 4 byte MCTP header */
+static const int MCTP_I3C_MINMTU = 64 + 4;
+/* One byte less to allow for the PEC */
+static const int MCTP_I3C_MAXMTU = MCTP_I3C_MAXBUF - 1;
+/* 4 byte MCTP header, no data, 1 byte PEC */
+static const int MCTP_I3C_MINLEN = 4 + 1;
+
+/* Sufficient for 64kB at min mtu */
+static const int MCTP_I3C_TX_QUEUE_LEN = 1100;
+
+/* Somewhat arbitrary */
+static const int MCTP_I3C_IBI_SLOTS = 8;
+
+/* Mandatory Data Byte in an IBI, from DSP0233 */
+static const u8 I3C_MDB_MCTP = 0xAE;
+/* From MIPI Device Characteristics Register (DCR) Assignments */
+static const u8 I3C_DCR_MCTP = 0xCC;
+
+static const char *MCTP_I3C_OF_PROP = "mctp-controller";
+
+/* List of mctp_i3c_busdev */
+static LIST_HEAD(busdevs);
+/* Protects busdevs, as well as mctp_i3c_bus.devs lists */
+static DEFINE_MUTEX(busdevs_lock);
+
+struct mctp_i3c_bus {
+	struct net_device *ndev;
+
+	struct task_struct *tx_thread;
+	wait_queue_head_t tx_wq;
+	/* tx_lock protects tx_skb and devs */
+	spinlock_t tx_lock;
+	/* Next skb to transmit */
+	struct sk_buff *tx_skb;
+	/* Scratch buffer for xmit */
+	u8 tx_scratch[MCTP_I3C_MAXBUF];
+
+	/* Element of busdevs */
+	struct list_head list;
+
+	/* Provisioned ID of our controller */
+	u64 pid;
+
+	struct i3c_bus *bus;
+	/* Head of mctp_i3c_device.list. Protected by busdevs_lock */
+	struct list_head devs;
+};
+
+struct mctp_i3c_device {
+	struct i3c_device *i3c;
+	struct mctp_i3c_bus *mbus;
+	struct list_head list; /* Element of mctp_i3c_bus.devs */
+
+	/* Held while tx_thread is using this device */
+	struct mutex lock;
+
+	/* Whether BCR indicates MDB is present in IBI */
+	bool have_mdb;
+	/* I3C dynamic address */
+	u8 addr;
+	/* Maximum read length */
+	u16 mrl;
+	/* Maximum write length */
+	u16 mwl;
+	/* Provisioned ID */
+	u64 pid;
+};
+
+/* We synthesise a mac header using the Provisioned ID.
+ * Used to pass dest to mctp_i3c_start_xmit.
+ */
+struct mctp_i3c_internal_hdr {
+	u8 dest[PID_SIZE];
+	u8 source[PID_SIZE];
+} __packed;
+
+static int mctp_i3c_read(struct mctp_i3c_device *mi)
+{
+	struct i3c_priv_xfer xfer = { .rnw = 1, .len = mi->mrl };
+	struct net_device_stats *stats = &mi->mbus->ndev->stats;
+	struct mctp_i3c_internal_hdr *ihdr = NULL;
+	struct sk_buff *skb = NULL;
+	struct mctp_skb_cb *cb;
+	int net_status, rc;
+	u8 pec, addr;
+
+	skb = netdev_alloc_skb(mi->mbus->ndev,
+			       mi->mrl + sizeof(struct mctp_i3c_internal_hdr));
+	if (!skb) {
+		stats->rx_dropped++;
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	skb->protocol = htons(ETH_P_MCTP);
+	/* Create a header for internal use */
+	skb_reset_mac_header(skb);
+	ihdr = skb_put(skb, sizeof(struct mctp_i3c_internal_hdr));
+	put_unaligned_be48(mi->pid, ihdr->source);
+	put_unaligned_be48(mi->mbus->pid, ihdr->dest);
+	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
+
+	xfer.data.in = skb_put(skb, mi->mrl);
+
+	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
+	if (rc < 0)
+		goto err;
+
+	if (WARN_ON_ONCE(xfer.len > mi->mrl)) {
+		/* Bad i3c bus driver */
+		rc = -EIO;
+		goto err;
+	}
+	if (xfer.len < MCTP_I3C_MINLEN) {
+		stats->rx_length_errors++;
+		rc = -EIO;
+		goto err;
+	}
+
+	/* check PEC, including address byte */
+	addr = mi->addr << 1 | 1;
+	pec = i2c_smbus_pec(0, &addr, 1);
+	pec = i2c_smbus_pec(pec, xfer.data.in, xfer.len - 1);
+	if (pec != ((u8 *)xfer.data.in)[xfer.len - 1]) {
+		stats->rx_crc_errors++;
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* Remove PEC */
+	skb_trim(skb, xfer.len - 1);
+
+	cb = __mctp_cb(skb);
+	cb->halen = PID_SIZE;
+	put_unaligned_be48(mi->pid, cb->haddr);
+
+	net_status = netif_rx(skb);
+
+	if (net_status == NET_RX_SUCCESS) {
+		stats->rx_packets++;
+		stats->rx_bytes += xfer.len - 1;
+	} else {
+		stats->rx_dropped++;
+	}
+
+	return 0;
+err:
+	kfree_skb(skb);
+	return rc;
+}
+
+static void mctp_i3c_ibi_handler(struct i3c_device *i3c,
+				 const struct i3c_ibi_payload *payload)
+{
+	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
+
+	if (WARN_ON_ONCE(!mi))
+		return;
+
+	if (mi->have_mdb) {
+		if (payload->len > 0) {
+			if (((u8 *)payload->data)[0] != I3C_MDB_MCTP) {
+				/* Not a mctp-i3c interrupt, ignore it */
+				return;
+			}
+		} else {
+			/* The BCR advertised a Mandatory Data Byte but the
+			 * device didn't send one.
+			 */
+			dev_warn_once(i3cdev_to_dev(i3c), "IBI with missing MDB");
+		}
+	}
+
+	mctp_i3c_read(mi);
+}
+
+static int mctp_i3c_setup(struct mctp_i3c_device *mi)
+{
+	bool ibi_set = false, ibi_enabled = false;
+	const struct i3c_ibi_setup ibi = {
+		.max_payload_len = 1,
+		.num_slots = MCTP_I3C_IBI_SLOTS,
+		.handler = mctp_i3c_ibi_handler,
+	};
+	struct i3c_device_info info;
+	int rc;
+
+	i3c_device_get_info(mi->i3c, &info);
+	mi->have_mdb = info.bcr & BIT(2);
+	mi->addr = info.dyn_addr;
+	mi->mwl = info.max_write_len;
+	mi->mrl = info.max_read_len;
+	mi->pid = info.pid;
+
+	rc = i3c_device_request_ibi(mi->i3c, &ibi);
+	if (rc == 0) {
+		ibi_set = true;
+	} else if (rc == -ENOTSUPP) {
+		/* This driver only supports In-Band Interrupt mode. Support for Polling Mode
+		 * could be added if required.
+		 */
+		dev_warn(i3cdev_to_dev(mi->i3c), "Failed, bus driver doesn't support In-Band Interrupts");
+		goto err;
+	} else {
+		dev_err(i3cdev_to_dev(mi->i3c), "Failed requesting IBI (%d)\n", rc);
+		goto err;
+	}
+
+	if (ibi_set) {
+		/* Device setup must be complete when IBI is enabled */
+		rc = i3c_device_enable_ibi(mi->i3c);
+		if (rc < 0) {
+			/* Assume a driver supporting request_ibi also supports enable_ibi */
+			dev_err(i3cdev_to_dev(mi->i3c), "Failed enabling IBI (%d)\n", rc);
+			goto err;
+		}
+		ibi_enabled = true;
+	}
+
+	return 0;
+err:
+	if (ibi_enabled)
+		i3c_device_disable_ibi(mi->i3c);
+	if (ibi_set)
+		i3c_device_free_ibi(mi->i3c);
+	return rc;
+}
+
+/* Adds a new MCTP i3c_device to a bus */
+static int mctp_i3c_add_device(struct mctp_i3c_bus *mbus, struct i3c_device *i3c)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_device *mi = NULL;
+	int rc;
+
+	mi = kzalloc(sizeof(*mi), GFP_KERNEL);
+	if (!mi) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	mi->mbus = mbus;
+	mi->i3c = i3c;
+	mutex_init(&mi->lock);
+	list_add(&mi->list, &mbus->devs);
+
+	i3cdev_set_drvdata(i3c, mi);
+	rc = mctp_i3c_setup(mi);
+	if (rc < 0)
+		goto err;
+
+	return 0;
+err:
+	dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc);
+	if (mi) {
+		list_del(&mi->list);
+		kfree(mi);
+	}
+	return rc;
+}
+
+static int mctp_i3c_probe(struct i3c_device *i3c)
+{
+	struct mctp_i3c_bus *b = NULL, *mbus = NULL;
+	int rc;
+
+	/* Look for a known bus */
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry(b, &busdevs, list)
+		if (b->bus == i3c->bus) {
+			mbus = b;
+			break;
+		}
+	mutex_unlock(&busdevs_lock);
+
+	if (!mbus) {
+		/* probably no "mctp-controller" property on the i3c bus */
+		return -ENODEV;
+	}
+
+	rc = mctp_i3c_add_device(mbus, i3c);
+	if (!rc)
+		goto err;
+
+	return 0;
+
+err:
+	return rc;
+}
+
+static void mctp_i3c_remove_device(struct mctp_i3c_device *mi)
+__must_hold(&busdevs_lock)
+{
+	/* Ensure the tx thread isn't using the device */
+	mutex_lock(&mi->lock);
+
+	/* Counterpart of mctp_i3c_setup */
+	i3c_device_disable_ibi(mi->i3c);
+	i3c_device_free_ibi(mi->i3c);
+
+	/* Counterpart of mctp_i3c_add_device */
+	i3cdev_set_drvdata(mi->i3c, NULL);
+	list_del(&mi->list);
+
+	/* Safe to unlock after removing from the list */
+	mutex_unlock(&mi->lock);
+	kfree(mi);
+}
+
+static void mctp_i3c_remove(struct i3c_device *i3c)
+{
+	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
+
+	/* We my have received a Bus Remove notify prior to device remove,
+	 * so mi will already be removed.
+	 */
+	if (!mi)
+		return;
+
+	mutex_lock(&busdevs_lock);
+	mctp_i3c_remove_device(mi);
+	mutex_unlock(&busdevs_lock);
+}
+
+/* Returns the device for an address, with mi->lock held */
+static struct mctp_i3c_device *
+mctp_i3c_lookup(struct mctp_i3c_bus *mbus, u64 pid)
+{
+	struct mctp_i3c_device *mi = NULL, *ret = NULL;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry(mi, &mbus->devs, list)
+		if (mi->pid == pid) {
+			ret = mi;
+			mutex_lock(&mi->lock);
+			break;
+		}
+	mutex_unlock(&busdevs_lock);
+	return ret;
+}
+
+static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
+{
+	struct net_device_stats *stats = &mbus->ndev->stats;
+	struct i3c_priv_xfer xfer = { .rnw = false };
+	struct mctp_i3c_internal_hdr *ihdr = NULL;
+	struct mctp_i3c_device *mi = NULL;
+	unsigned int data_len;
+	u8 *data = NULL;
+	u8 addr, pec;
+	int rc = 0;
+	u64 pid;
+
+	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
+	data_len = skb->len;
+
+	ihdr = (void *)skb_mac_header(skb);
+
+	pid = get_unaligned_be48(ihdr->dest);
+	mi = mctp_i3c_lookup(mbus, pid);
+	if (!mi) {
+		/* I3C endpoint went away after the packet was enqueued? */
+		stats->tx_dropped++;
+		goto out;
+	}
+
+	if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF))
+		goto out;
+
+	if (data_len + 1 > (unsigned int)mi->mwl) {
+		/* Route MTU was larger than supported by the endpoint */
+		stats->tx_dropped++;
+		goto out;
+	}
+
+	/* Need a linear buffer with space for the PEC */
+	xfer.len = data_len + 1;
+	if (skb_tailroom(skb) >= 1) {
+		skb_put(skb, 1);
+		data = skb->data;
+	} else {
+		// TODO: test this
+		/* Otherwise need to copy the buffer */
+		skb_copy_bits(skb, 0, mbus->tx_scratch, skb->len);
+		data = mbus->tx_scratch;
+	}
+
+	/* PEC calculation */
+	addr = mi->addr << 1;
+	pec = i2c_smbus_pec(0, &addr, 1);
+	pec = i2c_smbus_pec(pec, data, data_len);
+	data[data_len] = pec;
+
+	xfer.data.out = data;
+	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
+	if (rc == 0) {
+		stats->tx_bytes += data_len;
+		stats->tx_packets++;
+	} else {
+		stats->tx_errors++;
+	}
+
+out:
+	if (mi)
+		mutex_unlock(&mi->lock);
+}
+
+static int mctp_i3c_tx_thread(void *data)
+{
+	struct mctp_i3c_bus *mbus = data;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	for (;;) {
+		if (kthread_should_stop())
+			break;
+
+		spin_lock_irqsave(&mbus->tx_lock, flags);
+		skb = mbus->tx_skb;
+		mbus->tx_skb = NULL;
+		spin_unlock_irqrestore(&mbus->tx_lock, flags);
+
+		if (netif_queue_stopped(mbus->ndev))
+			netif_wake_queue(mbus->ndev);
+
+		if (skb) {
+			mctp_i3c_xmit(mbus, skb);
+			kfree_skb(skb);
+		} else {
+			wait_event_idle(mbus->tx_wq,
+					mbus->tx_skb || kthread_should_stop());
+		}
+	}
+
+	return 0;
+}
+
+static netdev_tx_t mctp_i3c_start_xmit(struct sk_buff *skb,
+				       struct net_device *ndev)
+{
+	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
+	unsigned long flags;
+	netdev_tx_t ret;
+
+	spin_lock_irqsave(&mbus->tx_lock, flags);
+	netif_stop_queue(ndev);
+	if (mbus->tx_skb) {
+		dev_warn_ratelimited(&ndev->dev, "TX with queue stopped");
+		ret = NETDEV_TX_BUSY;
+	} else {
+		mbus->tx_skb = skb;
+		ret = NETDEV_TX_OK;
+	}
+	spin_unlock_irqrestore(&mbus->tx_lock, flags);
+
+	if (ret == NETDEV_TX_OK)
+		wake_up(&mbus->tx_wq);
+
+	return ret;
+}
+
+static void mctp_i3c_bus_free(struct mctp_i3c_bus *mbus)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_device *mi = NULL, *tmp = NULL;
+
+	if (mbus->tx_thread) {
+		kthread_stop(mbus->tx_thread);
+		mbus->tx_thread = NULL;
+	}
+
+	/* Remove any child devices */
+	list_for_each_entry_safe(mi, tmp, &mbus->devs, list) {
+		mctp_i3c_remove_device(mi);
+	}
+
+	kfree_skb(mbus->tx_skb);
+	list_del(&mbus->list);
+}
+
+static void mctp_i3c_ndo_uninit(struct net_device *ndev)
+{
+	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
+
+	/* Perform cleanup here to ensure there are no remaining references */
+	mctp_i3c_bus_free(mbus);
+}
+
+static int mctp_i3c_header_create(struct sk_buff *skb, struct net_device *dev,
+				  unsigned short type, const void *daddr,
+	   const void *saddr, unsigned int len)
+{
+	struct mctp_i3c_internal_hdr *ihdr;
+
+	skb_push(skb, sizeof(struct mctp_i3c_internal_hdr));
+	skb_reset_mac_header(skb);
+	ihdr = (void *)skb_mac_header(skb);
+	memcpy(ihdr->dest, daddr, PID_SIZE);
+	memcpy(ihdr->source, saddr, PID_SIZE);
+	return 0;
+}
+
+static const struct net_device_ops mctp_i3c_ops = {
+	.ndo_start_xmit = mctp_i3c_start_xmit,
+	.ndo_uninit = mctp_i3c_ndo_uninit,
+};
+
+static const struct header_ops mctp_i3c_headops = {
+	.create = mctp_i3c_header_create,
+};
+
+static void mctp_i3c_net_setup(struct net_device *dev)
+{
+	dev->type = ARPHRD_MCTP;
+
+	dev->mtu = MCTP_I3C_MAXMTU;
+	dev->min_mtu = MCTP_I3C_MINMTU;
+	dev->max_mtu = MCTP_I3C_MAXMTU;
+	dev->tx_queue_len = MCTP_I3C_TX_QUEUE_LEN;
+
+	dev->hard_header_len = sizeof(struct mctp_i3c_internal_hdr);
+	dev->addr_len = PID_SIZE;
+
+	dev->netdev_ops	= &mctp_i3c_ops;
+	dev->header_ops	= &mctp_i3c_headops;
+}
+
+static bool mctp_i3c_is_mctp_controller(struct i3c_bus *bus)
+{
+	struct i3c_dev_desc *master = bus->cur_master;
+
+	if (!master)
+		return false;
+
+	return of_property_read_bool(master->common.master->dev.of_node, MCTP_I3C_OF_PROP);
+}
+
+/* Returns the Provisioned Id of a local bus master */
+static int mctp_i3c_bus_local_pid(struct i3c_bus *bus, u64 *ret_pid)
+{
+	struct i3c_dev_desc *master;
+
+	master = bus->cur_master;
+	if (WARN_ON_ONCE(!master))
+		return -ENOENT;
+	*ret_pid = master->info.pid;
+
+	return 0;
+}
+
+/* Returns an ERR_PTR on failure */
+static struct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_bus *mbus = NULL;
+	struct net_device *ndev = NULL;
+	char namebuf[IFNAMSIZ];
+	u8 addr[PID_SIZE];
+	int rc;
+
+	if (!mctp_i3c_is_mctp_controller(bus))
+		return ERR_PTR(-ENOENT);
+
+	snprintf(namebuf, sizeof(namebuf), "mctpi3c%d", bus->id);
+	ndev = alloc_netdev(sizeof(*mbus), namebuf, NET_NAME_ENUM, mctp_i3c_net_setup);
+	if (!ndev) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	mbus = netdev_priv(ndev);
+	mbus->ndev = ndev;
+	mbus->bus = bus;
+	INIT_LIST_HEAD(&mbus->devs);
+	list_add(&mbus->list, &busdevs);
+
+	rc = mctp_i3c_bus_local_pid(bus, &mbus->pid);
+	if (rc < 0) {
+		dev_err(&ndev->dev, "No I3C PID available\n");
+		goto err;
+	}
+	put_unaligned_be48(mbus->pid, addr);
+	dev_addr_set(ndev, addr);
+
+	init_waitqueue_head(&mbus->tx_wq);
+	spin_lock_init(&mbus->tx_lock);
+	mbus->tx_thread = kthread_run(mctp_i3c_tx_thread, mbus,
+					 "%s/tx", ndev->name);
+	if (IS_ERR(mbus->tx_thread)) {
+		dev_warn(&ndev->dev, "Error creating thread: %pe\n",
+			mbus->tx_thread);
+		rc = PTR_ERR(mbus->tx_thread);
+		mbus->tx_thread = NULL;
+		goto err;
+	}
+
+	rc = mctp_register_netdev(ndev, NULL);
+	if (rc < 0) {
+		dev_warn(&ndev->dev, "netdev register failed: %d\n", rc);
+		goto err;
+	}
+	return mbus;
+err:
+	/* uninit will not get called if a netdev has not been registered,
+	 * so we perform the same mbus cleanup manually.
+	 */
+	if (mbus)
+		mctp_i3c_bus_free(mbus);
+	if (ndev)
+		free_netdev(ndev);
+	return ERR_PTR(rc);
+}
+
+static void mctp_i3c_bus_remove(struct mctp_i3c_bus *mbus)
+__must_hold(&busdevs_lock)
+{
+	/* Unregister calls through to ndo_uninit -> mctp_i3c_bus_free() */
+	mctp_unregister_netdev(mbus->ndev);
+
+	free_netdev(mbus->ndev);
+	/* mbus is deallocated */
+}
+
+/* Removes all mctp-i3c busses */
+static void mctp_i3c_bus_remove_all(void)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list) {
+		mctp_i3c_bus_remove(mbus);
+	}
+	mutex_unlock(&busdevs_lock);
+}
+
+/* Adds a i3c_bus if it isn't already in the busdevs list.
+ * Suitable as an i3c_for_each_bus_locked callback.
+ */
+static int mctp_i3c_bus_add_new(struct i3c_bus *bus, void *data)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
+	bool exists = false;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
+		if (mbus->bus == bus)
+			exists = true;
+
+	/* It is OK for a bus to already exist. That can occur due to
+	 * the race in mod_init between notifier and for_each_bus
+	 */
+	if (!exists)
+		mctp_i3c_bus_add(bus);
+	mutex_unlock(&busdevs_lock);
+	return 0;
+}
+
+static void mctp_i3c_notify_bus_remove(struct i3c_bus *bus)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
+		if (mbus->bus == bus)
+			mctp_i3c_bus_remove(mbus);
+	mutex_unlock(&busdevs_lock);
+}
+
+static int mctp_i3c_notifier_call(struct notifier_block *nb,
+				  unsigned long action, void *data)
+{
+	switch (action) {
+	case I3C_NOTIFY_BUS_ADD:
+		mctp_i3c_bus_add_new((struct i3c_bus *)data, NULL);
+		break;
+	case I3C_NOTIFY_BUS_REMOVE:
+		mctp_i3c_notify_bus_remove((struct i3c_bus *)data);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block mctp_i3c_notifier = {
+	.notifier_call = mctp_i3c_notifier_call,
+};
+
+static const struct i3c_device_id mctp_i3c_ids[] = {
+	I3C_CLASS(I3C_DCR_MCTP, NULL),
+	{ 0 },
+};
+
+static struct i3c_driver mctp_i3c_driver = {
+	.driver = {
+		.name = "mctp-i3c",
+	},
+	.probe = mctp_i3c_probe,
+	.remove = mctp_i3c_remove,
+	.id_table = mctp_i3c_ids,
+};
+
+static __init int mctp_i3c_mod_init(void)
+{
+	int rc;
+
+	rc = i3c_register_notifier(&mctp_i3c_notifier);
+	if (rc < 0) {
+		i3c_driver_unregister(&mctp_i3c_driver);
+		return rc;
+	}
+
+	i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL);
+
+	rc = i3c_driver_register(&mctp_i3c_driver);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static __exit void mctp_i3c_mod_exit(void)
+{
+	int rc;
+
+	i3c_driver_unregister(&mctp_i3c_driver);
+
+	rc = i3c_unregister_notifier(&mctp_i3c_notifier);
+	if (rc < 0)
+		pr_warn("MCTP I3C could not unregister notifier, %d\n", rc);
+
+	mctp_i3c_bus_remove_all();
+}
+
+module_init(mctp_i3c_mod_init);
+module_exit(mctp_i3c_mod_exit);
+
+MODULE_DEVICE_TABLE(i3c, mctp_i3c_ids);
+MODULE_DESCRIPTION("MCTP I3C device");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt Johnston <matt at codeconstruct.com.au>");
-- 
2.39.2



From robh at kernel.org  Wed Oct  4 07:17:05 2023
From: robh at kernel.org (Rob Herring)
Date: Wed, 4 Oct 2023 09:17:05 -0500
Subject: [PATCH] i3c: Fix typo "Provisional ID" to "Provisioned ID"
In-Reply-To: <20231003075339.197099-1-matt@codeconstruct.com.au>
References: <20231003075339.197099-1-matt@codeconstruct.com.au>
Message-ID: <20231004141705.GA2947549-robh@kernel.org>

On Tue, Oct 03, 2023 at 03:53:39PM +0800, Matt Johnston wrote:
> The MIPI I3C spec refers to a Provisioned ID, since it is (sometimes)
> provisioned at device manufacturing.
> 
> Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
> ---
>  Documentation/ABI/testing/sysfs-bus-i3c        | 4 ++--
>  Documentation/devicetree/bindings/i3c/i3c.yaml | 4 ++--

Acked-by: Rob Herring <robh at kernel.org>

>  Documentation/driver-api/i3c/protocol.rst      | 4 ++--
>  drivers/i3c/master/svc-i3c-master.c            | 2 +-
>  include/linux/i3c/device.h                     | 2 +-
>  include/linux/i3c/master.h                     | 2 +-
>  6 files changed, 9 insertions(+), 9 deletions(-)


From lkp at intel.com  Fri Oct  6 22:50:44 2023
From: lkp at intel.com (kernel test robot)
Date: Sat, 7 Oct 2023 13:50:44 +0800
Subject: [PATCH net-next v4 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231004031316.725107-4-matt@codeconstruct.com.au>
References: <20231004031316.725107-4-matt@codeconstruct.com.au>
Message-ID: <202310071339.iWeOdKQk-lkp@intel.com>

Hi Matt,

kernel test robot noticed the following build errors:

[auto build test ERROR on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Matt-Johnston/dt-bindings-i3c-Add-mctp-controller-property/20231004-111533
base:   net-next/main
patch link:    https://lore.kernel.org/r/20231004031316.725107-4-matt%40codeconstruct.com.au
patch subject: [PATCH net-next v4 3/3] mctp i3c: MCTP I3C driver
config: x86_64-buildonly-randconfig-005-20231007 (https://download.01.org/0day-ci/archive/20231007/202310071339.iWeOdKQk-lkp at intel.com/config)
compiler: gcc-7 (Ubuntu 7.5.0-6ubuntu2) 7.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231007/202310071339.iWeOdKQk-lkp at intel.com/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 <lkp at intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202310071339.iWeOdKQk-lkp at intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/net/mctp/mctp-i3c.c:13:0:
>> drivers/net/mctp/mctp-i3c.c:710:12: error: initializer element is not constant
     I3C_CLASS(I3C_DCR_MCTP, NULL),
               ^
   include/linux/i3c/device.h:166:10: note: in definition of macro 'I3C_CLASS'
      .dcr = _dcr,      \
             ^~~~
   drivers/net/mctp/mctp-i3c.c:710:12: note: (near initialization for 'mctp_i3c_ids[0].dcr')
     I3C_CLASS(I3C_DCR_MCTP, NULL),
               ^
   include/linux/i3c/device.h:166:10: note: in definition of macro 'I3C_CLASS'
      .dcr = _dcr,      \
             ^~~~


vim +710 drivers/net/mctp/mctp-i3c.c

   708	
   709	static const struct i3c_device_id mctp_i3c_ids[] = {
 > 710		I3C_CLASS(I3C_DCR_MCTP, NULL),
   711		{ 0 },
   712	};
   713	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


From matt at codeconstruct.com.au  Sun Oct  8 19:54:46 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Mon,  9 Oct 2023 10:54:46 +0800
Subject: [PATCH net-next v5 0/3] I3C MCTP net driver
Message-ID: <20231009025451.490374-1-matt@codeconstruct.com.au>

This series adds an I3C transport for the kernel's MCTP network
protocol. MCTP is a communication protocol between system components
(BMCs, drives, NICs etc), with higher level protocols such as NVMe-MI or
PLDM built on top of it (in userspace). It runs over various transports
such as I2C, PCIe, or I3C.

The mctp-i3c driver follows a similar approach to the kernel's existing
mctp-i2c driver, creating a "mctpi3cX" network interface for each
numbered I3C bus. Busses opt in to support by adding a "mctp-controller"
property to the devicetree:

&i3c0 {
        mctp-controller;
}

The driver will bind to MCTP class devices (DCR 0xCC) that are on a
supported I3C bus. Each bus is represented by a `struct mctp_i3c_bus`
that keeps state for the network device. An individual I3C device
(struct mctp_i3c_device) performs operations using the "parent"
mctp_i3c_bus object. The I3C notify/enumeration patch is needed so that
the mctp-i3c driver can handle creating/removing mctp_i3c_bus objects as
required.

The mctp-i3c driver is using the Provisioned ID as an identifier for
target I3C devices (the neighbour address), as that will be more stable
than the I3C dynamic address. The driver internally translates that to a
dynamic address for bus operations.

The driver has been tested using an AST2600 platform. A remote endpoint 
has been tested against QEMU, as well as using the target mode support 
in Aspeed's vendor tree.

I3C maintainers have acked merging this through net-next tree.

Thanks,
Matt

---

v5:

- Use #define for constant initializer, fixes older gcc
- Wrap lines at 80 characters, fix parenthesis alignment 

v4:

- Add asm/unaligned.h include

v3:

- Use get_unaligned_be48()
- Use kthread_run()
- Don't set net namespace

v2:

- Rebased to net-next
- Removed unnecessary pr_ printing
- Fixed reverse christmas tree ordering
- Reworded DT property description to match I2C

Jeremy Kerr (1):
  i3c: Add support for bus enumeration & notification

Matt Johnston (2):
  dt-bindings: i3c: Add mctp-controller property
  mctp i3c: MCTP I3C driver

 .../devicetree/bindings/i3c/i3c.yaml          |   6 +
 drivers/i3c/master.c                          |  35 +
 drivers/net/mctp/Kconfig                      |   9 +
 drivers/net/mctp/Makefile                     |   1 +
 drivers/net/mctp/mctp-i3c.c                   | 769 ++++++++++++++++++
 include/linux/i3c/master.h                    |  11 +
 6 files changed, 831 insertions(+)
 create mode 100644 drivers/net/mctp/mctp-i3c.c

-- 
2.42.0



From matt at codeconstruct.com.au  Sun Oct  8 19:54:47 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Mon,  9 Oct 2023 10:54:47 +0800
Subject: [PATCH net-next v5 1/3] dt-bindings: i3c: Add mctp-controller property
In-Reply-To: <20231009025451.490374-1-matt@codeconstruct.com.au>
References: <20231009025451.490374-1-matt@codeconstruct.com.au>
Message-ID: <20231009025451.490374-2-matt@codeconstruct.com.au>

This property is used to describe a I3C bus with attached MCTP I3C
target devices.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski at linaro.org>
Acked-by: Alexandre Belloni <alexandre.belloni at bootlin.com>
---

v2:

- Reworded DT property description to match I2C

 Documentation/devicetree/bindings/i3c/i3c.yaml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml
index ab69f4115de4..d9483fbd2454 100644
--- a/Documentation/devicetree/bindings/i3c/i3c.yaml
+++ b/Documentation/devicetree/bindings/i3c/i3c.yaml
@@ -55,6 +55,12 @@ properties:
 
       May not be supported by all controllers.
 
+  mctp-controller:
+    type: boolean
+    description: |
+      Indicates that the system is accessible via this bus as an endpoint for
+      MCTP over I3C transport.
+
 required:
   - "#address-cells"
   - "#size-cells"
-- 
2.42.0



From matt at codeconstruct.com.au  Sun Oct  8 19:54:48 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Mon,  9 Oct 2023 10:54:48 +0800
Subject: [PATCH net-next v5 2/3] i3c: Add support for bus enumeration & notification
In-Reply-To: <20231009025451.490374-1-matt@codeconstruct.com.au>
References: <20231009025451.490374-1-matt@codeconstruct.com.au>
Message-ID: <20231009025451.490374-3-matt@codeconstruct.com.au>

From: Jeremy Kerr <jk at codeconstruct.com.au>

This allows other drivers to be notified when new i3c busses are
attached, referring to a whole i3c bus as opposed to individual
devices.

Signed-off-by: Jeremy Kerr <jk at codeconstruct.com.au>
Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
Acked-by: Alexandre Belloni <alexandre.belloni at bootlin.com>
---
 drivers/i3c/master.c       | 35 +++++++++++++++++++++++++++++++++++
 include/linux/i3c/master.h | 11 +++++++++++
 2 files changed, 46 insertions(+)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 87283e4a4607..959ec5269376 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -22,6 +22,7 @@
 static DEFINE_IDR(i3c_bus_idr);
 static DEFINE_MUTEX(i3c_core_lock);
 static int __i3c_first_dynamic_bus_num;
+static BLOCKING_NOTIFIER_HEAD(i3c_bus_notifier);
 
 /**
  * i3c_bus_maintenance_lock - Lock the bus for a maintenance operation
@@ -453,6 +454,36 @@ static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np)
 	return 0;
 }
 
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+			     void *data)
+{
+	struct i3c_bus *bus;
+	int id;
+
+	mutex_lock(&i3c_core_lock);
+	idr_for_each_entry(&i3c_bus_idr, bus, id)
+		fn(bus, data);
+	mutex_unlock(&i3c_core_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_for_each_bus_locked);
+
+int i3c_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_register_notifier);
+
+int i3c_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_unregister_notifier);
+
+static void i3c_bus_notify(struct i3c_bus *bus, unsigned int action)
+{
+	blocking_notifier_call_chain(&i3c_bus_notifier, action, bus);
+}
+
 static const char * const i3c_bus_mode_strings[] = {
 	[I3C_BUS_MODE_PURE] = "pure",
 	[I3C_BUS_MODE_MIXED_FAST] = "mixed-fast",
@@ -2682,6 +2713,8 @@ int i3c_master_register(struct i3c_master_controller *master,
 	if (ret)
 		goto err_del_dev;
 
+	i3c_bus_notify(i3cbus, I3C_NOTIFY_BUS_ADD);
+
 	/*
 	 * We're done initializing the bus and the controller, we can now
 	 * register I3C devices discovered during the initial DAA.
@@ -2714,6 +2747,8 @@ EXPORT_SYMBOL_GPL(i3c_master_register);
  */
 void i3c_master_unregister(struct i3c_master_controller *master)
 {
+	i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
+
 	i3c_master_i2c_adapter_cleanup(master);
 	i3c_master_unregister_i3c_devs(master);
 	i3c_master_bus_cleanup(master);
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 0b52da4f2346..db909ef79be4 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -24,6 +24,12 @@
 
 struct i2c_client;
 
+/* notifier actions. notifier call data is the struct i3c_bus */
+enum {
+	I3C_NOTIFY_BUS_ADD,
+	I3C_NOTIFY_BUS_REMOVE,
+};
+
 struct i3c_master_controller;
 struct i3c_bus;
 struct i3c_device;
@@ -652,4 +658,9 @@ void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot);
 
 struct i3c_ibi_slot *i3c_master_get_free_ibi_slot(struct i3c_dev_desc *dev);
 
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+			     void *data);
+int i3c_register_notifier(struct notifier_block *nb);
+int i3c_unregister_notifier(struct notifier_block *nb);
+
 #endif /* I3C_MASTER_H */
-- 
2.42.0



From matt at codeconstruct.com.au  Sun Oct  8 19:54:49 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Mon,  9 Oct 2023 10:54:49 +0800
Subject: [PATCH net-next v5 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231009025451.490374-1-matt@codeconstruct.com.au>
References: <20231009025451.490374-1-matt@codeconstruct.com.au>
Message-ID: <20231009025451.490374-4-matt@codeconstruct.com.au>

Provides MCTP network transport over an I3C bus, as specified in
DMTF DSP0233.

Each I3C bus (with "mctp-controller" devicetree property) gets an
"mctpi3cX" net device created. I3C devices are reachable as remote
endpoints through that net device. Link layer addressing uses the
I3C PID as a fixed hardware address for neighbour table entries.

The driver matches I3C devices that have the MIPI assigned DCR 0xCC for
MCTP.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
---

v5:

- Use #define for constant initializer, fixes older gcc
- Wrap lines at 80 characters, fix parenthesis alignment 

v4:

- Add asm/unaligned.h include

v3:

- Use get_unaligned_be48()
- Use kthread_run()
- Don't set net namespace

v2:

- Removed unnecessary pr_ printing
- Fixed reverse christmas tree ordering

 drivers/net/mctp/Kconfig    |   9 +
 drivers/net/mctp/Makefile   |   1 +
 drivers/net/mctp/mctp-i3c.c | 769 ++++++++++++++++++++++++++++++++++++
 3 files changed, 779 insertions(+)
 create mode 100644 drivers/net/mctp/mctp-i3c.c

diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig
index dc71657d9184..ce9d2d2ccf3b 100644
--- a/drivers/net/mctp/Kconfig
+++ b/drivers/net/mctp/Kconfig
@@ -33,6 +33,15 @@ config MCTP_TRANSPORT_I2C
 	  from DMTF specification DSP0237. A MCTP protocol network device is
 	  created for each I2C bus that has been assigned a mctp-i2c device.
 
+config MCTP_TRANSPORT_I3C
+	tristate "MCTP I3C transport"
+	depends on I3C
+	help
+	  Provides a driver to access MCTP devices over I3C transport,
+	  from DMTF specification DSP0233.
+	  A MCTP protocol network device is created for each I3C bus
+	  having a "mctp-controller" devicetree property.
+
 endmenu
 
 endif
diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile
index 1ca3e6028f77..e1cb99ced54a 100644
--- a/drivers/net/mctp/Makefile
+++ b/drivers/net/mctp/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o
 obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o
+obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o
diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c
new file mode 100644
index 000000000000..91cb6d454fbb
--- /dev/null
+++ b/drivers/net/mctp/mctp-i3c.c
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implements DMTF specification
+ * "DSP0233 Management Component Transport Protocol (MCTP) I3C Transport
+ * Binding"
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0233_1.0.0.pdf
+ *
+ * Copyright (c) 2023 Code Construct
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/if_arp.h>
+#include <asm/unaligned.h>
+#include <net/mctp.h>
+#include <net/mctpdevice.h>
+
+#define MCTP_I3C_MAXBUF 65536
+/* 48 bit Provisioned Id */
+#define PID_SIZE 6
+
+/* 64 byte payload, 4 byte MCTP header */
+static const int MCTP_I3C_MINMTU = 64 + 4;
+/* One byte less to allow for the PEC */
+static const int MCTP_I3C_MAXMTU = MCTP_I3C_MAXBUF - 1;
+/* 4 byte MCTP header, no data, 1 byte PEC */
+static const int MCTP_I3C_MINLEN = 4 + 1;
+
+/* Sufficient for 64kB at min mtu */
+static const int MCTP_I3C_TX_QUEUE_LEN = 1100;
+
+/* Somewhat arbitrary */
+static const int MCTP_I3C_IBI_SLOTS = 8;
+
+/* Mandatory Data Byte in an IBI, from DSP0233 */
+#define I3C_MDB_MCTP 0xAE
+/* From MIPI Device Characteristics Register (DCR) Assignments */
+#define I3C_DCR_MCTP 0xCC
+
+static const char *MCTP_I3C_OF_PROP = "mctp-controller";
+
+/* List of mctp_i3c_busdev */
+static LIST_HEAD(busdevs);
+/* Protects busdevs, as well as mctp_i3c_bus.devs lists */
+static DEFINE_MUTEX(busdevs_lock);
+
+struct mctp_i3c_bus {
+	struct net_device *ndev;
+
+	struct task_struct *tx_thread;
+	wait_queue_head_t tx_wq;
+	/* tx_lock protects tx_skb and devs */
+	spinlock_t tx_lock;
+	/* Next skb to transmit */
+	struct sk_buff *tx_skb;
+	/* Scratch buffer for xmit */
+	u8 tx_scratch[MCTP_I3C_MAXBUF];
+
+	/* Element of busdevs */
+	struct list_head list;
+
+	/* Provisioned ID of our controller */
+	u64 pid;
+
+	struct i3c_bus *bus;
+	/* Head of mctp_i3c_device.list. Protected by busdevs_lock */
+	struct list_head devs;
+};
+
+struct mctp_i3c_device {
+	struct i3c_device *i3c;
+	struct mctp_i3c_bus *mbus;
+	struct list_head list; /* Element of mctp_i3c_bus.devs */
+
+	/* Held while tx_thread is using this device */
+	struct mutex lock;
+
+	/* Whether BCR indicates MDB is present in IBI */
+	bool have_mdb;
+	/* I3C dynamic address */
+	u8 addr;
+	/* Maximum read length */
+	u16 mrl;
+	/* Maximum write length */
+	u16 mwl;
+	/* Provisioned ID */
+	u64 pid;
+};
+
+/* We synthesise a mac header using the Provisioned ID.
+ * Used to pass dest to mctp_i3c_start_xmit.
+ */
+struct mctp_i3c_internal_hdr {
+	u8 dest[PID_SIZE];
+	u8 source[PID_SIZE];
+} __packed;
+
+static int mctp_i3c_read(struct mctp_i3c_device *mi)
+{
+	struct i3c_priv_xfer xfer = { .rnw = 1, .len = mi->mrl };
+	struct net_device_stats *stats = &mi->mbus->ndev->stats;
+	struct mctp_i3c_internal_hdr *ihdr = NULL;
+	struct sk_buff *skb = NULL;
+	struct mctp_skb_cb *cb;
+	int net_status, rc;
+	u8 pec, addr;
+
+	skb = netdev_alloc_skb(mi->mbus->ndev,
+			       mi->mrl + sizeof(struct mctp_i3c_internal_hdr));
+	if (!skb) {
+		stats->rx_dropped++;
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	skb->protocol = htons(ETH_P_MCTP);
+	/* Create a header for internal use */
+	skb_reset_mac_header(skb);
+	ihdr = skb_put(skb, sizeof(struct mctp_i3c_internal_hdr));
+	put_unaligned_be48(mi->pid, ihdr->source);
+	put_unaligned_be48(mi->mbus->pid, ihdr->dest);
+	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
+
+	xfer.data.in = skb_put(skb, mi->mrl);
+
+	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
+	if (rc < 0)
+		goto err;
+
+	if (WARN_ON_ONCE(xfer.len > mi->mrl)) {
+		/* Bad i3c bus driver */
+		rc = -EIO;
+		goto err;
+	}
+	if (xfer.len < MCTP_I3C_MINLEN) {
+		stats->rx_length_errors++;
+		rc = -EIO;
+		goto err;
+	}
+
+	/* check PEC, including address byte */
+	addr = mi->addr << 1 | 1;
+	pec = i2c_smbus_pec(0, &addr, 1);
+	pec = i2c_smbus_pec(pec, xfer.data.in, xfer.len - 1);
+	if (pec != ((u8 *)xfer.data.in)[xfer.len - 1]) {
+		stats->rx_crc_errors++;
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* Remove PEC */
+	skb_trim(skb, xfer.len - 1);
+
+	cb = __mctp_cb(skb);
+	cb->halen = PID_SIZE;
+	put_unaligned_be48(mi->pid, cb->haddr);
+
+	net_status = netif_rx(skb);
+
+	if (net_status == NET_RX_SUCCESS) {
+		stats->rx_packets++;
+		stats->rx_bytes += xfer.len - 1;
+	} else {
+		stats->rx_dropped++;
+	}
+
+	return 0;
+err:
+	kfree_skb(skb);
+	return rc;
+}
+
+static void mctp_i3c_ibi_handler(struct i3c_device *i3c,
+				 const struct i3c_ibi_payload *payload)
+{
+	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
+
+	if (WARN_ON_ONCE(!mi))
+		return;
+
+	if (mi->have_mdb) {
+		if (payload->len > 0) {
+			if (((u8 *)payload->data)[0] != I3C_MDB_MCTP) {
+				/* Not a mctp-i3c interrupt, ignore it */
+				return;
+			}
+		} else {
+			/* The BCR advertised a Mandatory Data Byte but the
+			 * device didn't send one.
+			 */
+			dev_warn_once(i3cdev_to_dev(i3c), "IBI with missing MDB");
+		}
+	}
+
+	mctp_i3c_read(mi);
+}
+
+static int mctp_i3c_setup(struct mctp_i3c_device *mi)
+{
+	bool ibi_set = false, ibi_enabled = false;
+	const struct i3c_ibi_setup ibi = {
+		.max_payload_len = 1,
+		.num_slots = MCTP_I3C_IBI_SLOTS,
+		.handler = mctp_i3c_ibi_handler,
+	};
+	struct i3c_device_info info;
+	int rc;
+
+	i3c_device_get_info(mi->i3c, &info);
+	mi->have_mdb = info.bcr & BIT(2);
+	mi->addr = info.dyn_addr;
+	mi->mwl = info.max_write_len;
+	mi->mrl = info.max_read_len;
+	mi->pid = info.pid;
+
+	rc = i3c_device_request_ibi(mi->i3c, &ibi);
+	if (rc == 0) {
+		ibi_set = true;
+	} else if (rc == -ENOTSUPP) {
+		/* This driver only supports In-Band Interrupt mode
+		 * (ENOTSUPP is from the i3c layer, not EOPNOTSUPP).
+		 * Support for Polling Mode could be added if required.
+		 */
+		dev_warn(i3cdev_to_dev(mi->i3c), "Failed, bus driver doesn't support In-Band Interrupts");
+		goto err;
+	} else {
+		dev_err(i3cdev_to_dev(mi->i3c),
+			"Failed requesting IBI (%d)\n", rc);
+		goto err;
+	}
+
+	if (ibi_set) {
+		/* Device setup must be complete when IBI is enabled */
+		rc = i3c_device_enable_ibi(mi->i3c);
+		if (rc < 0) {
+			/* Assume a driver supporting request_ibi also
+			 * supports enable_ibi.
+			 */
+			dev_err(i3cdev_to_dev(mi->i3c),
+				"Failed enabling IBI (%d)\n", rc);
+			goto err;
+		}
+		ibi_enabled = true;
+	}
+
+	return 0;
+err:
+	if (ibi_enabled)
+		i3c_device_disable_ibi(mi->i3c);
+	if (ibi_set)
+		i3c_device_free_ibi(mi->i3c);
+	return rc;
+}
+
+/* Adds a new MCTP i3c_device to a bus */
+static int mctp_i3c_add_device(struct mctp_i3c_bus *mbus,
+			       struct i3c_device *i3c)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_device *mi = NULL;
+	int rc;
+
+	mi = kzalloc(sizeof(*mi), GFP_KERNEL);
+	if (!mi) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	mi->mbus = mbus;
+	mi->i3c = i3c;
+	mutex_init(&mi->lock);
+	list_add(&mi->list, &mbus->devs);
+
+	i3cdev_set_drvdata(i3c, mi);
+	rc = mctp_i3c_setup(mi);
+	if (rc < 0)
+		goto err;
+
+	return 0;
+err:
+	dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc);
+	if (mi) {
+		list_del(&mi->list);
+		kfree(mi);
+	}
+	return rc;
+}
+
+static int mctp_i3c_probe(struct i3c_device *i3c)
+{
+	struct mctp_i3c_bus *b = NULL, *mbus = NULL;
+	int rc;
+
+	/* Look for a known bus */
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry(b, &busdevs, list)
+		if (b->bus == i3c->bus) {
+			mbus = b;
+			break;
+		}
+	mutex_unlock(&busdevs_lock);
+
+	if (!mbus) {
+		/* probably no "mctp-controller" property on the i3c bus */
+		return -ENODEV;
+	}
+
+	rc = mctp_i3c_add_device(mbus, i3c);
+	if (!rc)
+		goto err;
+
+	return 0;
+
+err:
+	return rc;
+}
+
+static void mctp_i3c_remove_device(struct mctp_i3c_device *mi)
+__must_hold(&busdevs_lock)
+{
+	/* Ensure the tx thread isn't using the device */
+	mutex_lock(&mi->lock);
+
+	/* Counterpart of mctp_i3c_setup */
+	i3c_device_disable_ibi(mi->i3c);
+	i3c_device_free_ibi(mi->i3c);
+
+	/* Counterpart of mctp_i3c_add_device */
+	i3cdev_set_drvdata(mi->i3c, NULL);
+	list_del(&mi->list);
+
+	/* Safe to unlock after removing from the list */
+	mutex_unlock(&mi->lock);
+	kfree(mi);
+}
+
+static void mctp_i3c_remove(struct i3c_device *i3c)
+{
+	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
+
+	/* We my have received a Bus Remove notify prior to device remove,
+	 * so mi will already be removed.
+	 */
+	if (!mi)
+		return;
+
+	mutex_lock(&busdevs_lock);
+	mctp_i3c_remove_device(mi);
+	mutex_unlock(&busdevs_lock);
+}
+
+/* Returns the device for an address, with mi->lock held */
+static struct mctp_i3c_device *
+mctp_i3c_lookup(struct mctp_i3c_bus *mbus, u64 pid)
+{
+	struct mctp_i3c_device *mi = NULL, *ret = NULL;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry(mi, &mbus->devs, list)
+		if (mi->pid == pid) {
+			ret = mi;
+			mutex_lock(&mi->lock);
+			break;
+		}
+	mutex_unlock(&busdevs_lock);
+	return ret;
+}
+
+static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
+{
+	struct net_device_stats *stats = &mbus->ndev->stats;
+	struct i3c_priv_xfer xfer = { .rnw = false };
+	struct mctp_i3c_internal_hdr *ihdr = NULL;
+	struct mctp_i3c_device *mi = NULL;
+	unsigned int data_len;
+	u8 *data = NULL;
+	u8 addr, pec;
+	int rc = 0;
+	u64 pid;
+
+	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
+	data_len = skb->len;
+
+	ihdr = (void *)skb_mac_header(skb);
+
+	pid = get_unaligned_be48(ihdr->dest);
+	mi = mctp_i3c_lookup(mbus, pid);
+	if (!mi) {
+		/* I3C endpoint went away after the packet was enqueued? */
+		stats->tx_dropped++;
+		goto out;
+	}
+
+	if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF))
+		goto out;
+
+	if (data_len + 1 > (unsigned int)mi->mwl) {
+		/* Route MTU was larger than supported by the endpoint */
+		stats->tx_dropped++;
+		goto out;
+	}
+
+	/* Need a linear buffer with space for the PEC */
+	xfer.len = data_len + 1;
+	if (skb_tailroom(skb) >= 1) {
+		skb_put(skb, 1);
+		data = skb->data;
+	} else {
+		// TODO: test this
+		/* Otherwise need to copy the buffer */
+		skb_copy_bits(skb, 0, mbus->tx_scratch, skb->len);
+		data = mbus->tx_scratch;
+	}
+
+	/* PEC calculation */
+	addr = mi->addr << 1;
+	pec = i2c_smbus_pec(0, &addr, 1);
+	pec = i2c_smbus_pec(pec, data, data_len);
+	data[data_len] = pec;
+
+	xfer.data.out = data;
+	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
+	if (rc == 0) {
+		stats->tx_bytes += data_len;
+		stats->tx_packets++;
+	} else {
+		stats->tx_errors++;
+	}
+
+out:
+	if (mi)
+		mutex_unlock(&mi->lock);
+}
+
+static int mctp_i3c_tx_thread(void *data)
+{
+	struct mctp_i3c_bus *mbus = data;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	for (;;) {
+		if (kthread_should_stop())
+			break;
+
+		spin_lock_irqsave(&mbus->tx_lock, flags);
+		skb = mbus->tx_skb;
+		mbus->tx_skb = NULL;
+		spin_unlock_irqrestore(&mbus->tx_lock, flags);
+
+		if (netif_queue_stopped(mbus->ndev))
+			netif_wake_queue(mbus->ndev);
+
+		if (skb) {
+			mctp_i3c_xmit(mbus, skb);
+			kfree_skb(skb);
+		} else {
+			wait_event_idle(mbus->tx_wq,
+					mbus->tx_skb || kthread_should_stop());
+		}
+	}
+
+	return 0;
+}
+
+static netdev_tx_t mctp_i3c_start_xmit(struct sk_buff *skb,
+				       struct net_device *ndev)
+{
+	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
+	unsigned long flags;
+	netdev_tx_t ret;
+
+	spin_lock_irqsave(&mbus->tx_lock, flags);
+	netif_stop_queue(ndev);
+	if (mbus->tx_skb) {
+		dev_warn_ratelimited(&ndev->dev, "TX with queue stopped");
+		ret = NETDEV_TX_BUSY;
+	} else {
+		mbus->tx_skb = skb;
+		ret = NETDEV_TX_OK;
+	}
+	spin_unlock_irqrestore(&mbus->tx_lock, flags);
+
+	if (ret == NETDEV_TX_OK)
+		wake_up(&mbus->tx_wq);
+
+	return ret;
+}
+
+static void mctp_i3c_bus_free(struct mctp_i3c_bus *mbus)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_device *mi = NULL, *tmp = NULL;
+
+	if (mbus->tx_thread) {
+		kthread_stop(mbus->tx_thread);
+		mbus->tx_thread = NULL;
+	}
+
+	/* Remove any child devices */
+	list_for_each_entry_safe(mi, tmp, &mbus->devs, list) {
+		mctp_i3c_remove_device(mi);
+	}
+
+	kfree_skb(mbus->tx_skb);
+	list_del(&mbus->list);
+}
+
+static void mctp_i3c_ndo_uninit(struct net_device *ndev)
+{
+	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
+
+	/* Perform cleanup here to ensure there are no remaining references */
+	mctp_i3c_bus_free(mbus);
+}
+
+static int mctp_i3c_header_create(struct sk_buff *skb, struct net_device *dev,
+				  unsigned short type, const void *daddr,
+	   const void *saddr, unsigned int len)
+{
+	struct mctp_i3c_internal_hdr *ihdr;
+
+	skb_push(skb, sizeof(struct mctp_i3c_internal_hdr));
+	skb_reset_mac_header(skb);
+	ihdr = (void *)skb_mac_header(skb);
+	memcpy(ihdr->dest, daddr, PID_SIZE);
+	memcpy(ihdr->source, saddr, PID_SIZE);
+	return 0;
+}
+
+static const struct net_device_ops mctp_i3c_ops = {
+	.ndo_start_xmit = mctp_i3c_start_xmit,
+	.ndo_uninit = mctp_i3c_ndo_uninit,
+};
+
+static const struct header_ops mctp_i3c_headops = {
+	.create = mctp_i3c_header_create,
+};
+
+static void mctp_i3c_net_setup(struct net_device *dev)
+{
+	dev->type = ARPHRD_MCTP;
+
+	dev->mtu = MCTP_I3C_MAXMTU;
+	dev->min_mtu = MCTP_I3C_MINMTU;
+	dev->max_mtu = MCTP_I3C_MAXMTU;
+	dev->tx_queue_len = MCTP_I3C_TX_QUEUE_LEN;
+
+	dev->hard_header_len = sizeof(struct mctp_i3c_internal_hdr);
+	dev->addr_len = PID_SIZE;
+
+	dev->netdev_ops	= &mctp_i3c_ops;
+	dev->header_ops	= &mctp_i3c_headops;
+}
+
+static bool mctp_i3c_is_mctp_controller(struct i3c_bus *bus)
+{
+	struct i3c_dev_desc *master = bus->cur_master;
+
+	if (!master)
+		return false;
+
+	return of_property_read_bool(master->common.master->dev.of_node,
+				     MCTP_I3C_OF_PROP);
+}
+
+/* Returns the Provisioned Id of a local bus master */
+static int mctp_i3c_bus_local_pid(struct i3c_bus *bus, u64 *ret_pid)
+{
+	struct i3c_dev_desc *master;
+
+	master = bus->cur_master;
+	if (WARN_ON_ONCE(!master))
+		return -ENOENT;
+	*ret_pid = master->info.pid;
+
+	return 0;
+}
+
+/* Returns an ERR_PTR on failure */
+static struct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_bus *mbus = NULL;
+	struct net_device *ndev = NULL;
+	char namebuf[IFNAMSIZ];
+	u8 addr[PID_SIZE];
+	int rc;
+
+	if (!mctp_i3c_is_mctp_controller(bus))
+		return ERR_PTR(-ENOENT);
+
+	snprintf(namebuf, sizeof(namebuf), "mctpi3c%d", bus->id);
+	ndev = alloc_netdev(sizeof(*mbus), namebuf, NET_NAME_ENUM,
+			    mctp_i3c_net_setup);
+	if (!ndev) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	mbus = netdev_priv(ndev);
+	mbus->ndev = ndev;
+	mbus->bus = bus;
+	INIT_LIST_HEAD(&mbus->devs);
+	list_add(&mbus->list, &busdevs);
+
+	rc = mctp_i3c_bus_local_pid(bus, &mbus->pid);
+	if (rc < 0) {
+		dev_err(&ndev->dev, "No I3C PID available\n");
+		goto err;
+	}
+	put_unaligned_be48(mbus->pid, addr);
+	dev_addr_set(ndev, addr);
+
+	init_waitqueue_head(&mbus->tx_wq);
+	spin_lock_init(&mbus->tx_lock);
+	mbus->tx_thread = kthread_run(mctp_i3c_tx_thread, mbus,
+				      "%s/tx", ndev->name);
+	if (IS_ERR(mbus->tx_thread)) {
+		dev_warn(&ndev->dev, "Error creating thread: %pe\n",
+			 mbus->tx_thread);
+		rc = PTR_ERR(mbus->tx_thread);
+		mbus->tx_thread = NULL;
+		goto err;
+	}
+
+	rc = mctp_register_netdev(ndev, NULL);
+	if (rc < 0) {
+		dev_warn(&ndev->dev, "netdev register failed: %d\n", rc);
+		goto err;
+	}
+	return mbus;
+err:
+	/* uninit will not get called if a netdev has not been registered,
+	 * so we perform the same mbus cleanup manually.
+	 */
+	if (mbus)
+		mctp_i3c_bus_free(mbus);
+	if (ndev)
+		free_netdev(ndev);
+	return ERR_PTR(rc);
+}
+
+static void mctp_i3c_bus_remove(struct mctp_i3c_bus *mbus)
+__must_hold(&busdevs_lock)
+{
+	/* Unregister calls through to ndo_uninit -> mctp_i3c_bus_free() */
+	mctp_unregister_netdev(mbus->ndev);
+
+	free_netdev(mbus->ndev);
+	/* mbus is deallocated */
+}
+
+/* Removes all mctp-i3c busses */
+static void mctp_i3c_bus_remove_all(void)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list) {
+		mctp_i3c_bus_remove(mbus);
+	}
+	mutex_unlock(&busdevs_lock);
+}
+
+/* Adds a i3c_bus if it isn't already in the busdevs list.
+ * Suitable as an i3c_for_each_bus_locked callback.
+ */
+static int mctp_i3c_bus_add_new(struct i3c_bus *bus, void *data)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
+	bool exists = false;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
+		if (mbus->bus == bus)
+			exists = true;
+
+	/* It is OK for a bus to already exist. That can occur due to
+	 * the race in mod_init between notifier and for_each_bus
+	 */
+	if (!exists)
+		mctp_i3c_bus_add(bus);
+	mutex_unlock(&busdevs_lock);
+	return 0;
+}
+
+static void mctp_i3c_notify_bus_remove(struct i3c_bus *bus)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
+		if (mbus->bus == bus)
+			mctp_i3c_bus_remove(mbus);
+	mutex_unlock(&busdevs_lock);
+}
+
+static int mctp_i3c_notifier_call(struct notifier_block *nb,
+				  unsigned long action, void *data)
+{
+	switch (action) {
+	case I3C_NOTIFY_BUS_ADD:
+		mctp_i3c_bus_add_new((struct i3c_bus *)data, NULL);
+		break;
+	case I3C_NOTIFY_BUS_REMOVE:
+		mctp_i3c_notify_bus_remove((struct i3c_bus *)data);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block mctp_i3c_notifier = {
+	.notifier_call = mctp_i3c_notifier_call,
+};
+
+static const struct i3c_device_id mctp_i3c_ids[] = {
+	I3C_CLASS(I3C_DCR_MCTP, NULL),
+	{ 0 },
+};
+
+static struct i3c_driver mctp_i3c_driver = {
+	.driver = {
+		.name = "mctp-i3c",
+	},
+	.probe = mctp_i3c_probe,
+	.remove = mctp_i3c_remove,
+	.id_table = mctp_i3c_ids,
+};
+
+static __init int mctp_i3c_mod_init(void)
+{
+	int rc;
+
+	rc = i3c_register_notifier(&mctp_i3c_notifier);
+	if (rc < 0) {
+		i3c_driver_unregister(&mctp_i3c_driver);
+		return rc;
+	}
+
+	i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL);
+
+	rc = i3c_driver_register(&mctp_i3c_driver);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static __exit void mctp_i3c_mod_exit(void)
+{
+	int rc;
+
+	i3c_driver_unregister(&mctp_i3c_driver);
+
+	rc = i3c_unregister_notifier(&mctp_i3c_notifier);
+	if (rc < 0)
+		pr_warn("MCTP I3C could not unregister notifier, %d\n", rc);
+
+	mctp_i3c_bus_remove_all();
+}
+
+module_init(mctp_i3c_mod_init);
+module_exit(mctp_i3c_mod_exit);
+
+MODULE_DEVICE_TABLE(i3c, mctp_i3c_ids);
+MODULE_DESCRIPTION("MCTP I3C device");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt Johnston <matt at codeconstruct.com.au>");
-- 
2.42.0



From alexandre.belloni at bootlin.com  Tue Oct 10 03:13:01 2023
From: alexandre.belloni at bootlin.com (Alexandre Belloni)
Date: Tue, 10 Oct 2023 12:13:01 +0200
Subject: [PATCH] i3c: Fix typo "Provisional ID" to "Provisioned ID"
In-Reply-To: <20231003075339.197099-1-matt@codeconstruct.com.au>
References: <20231003075339.197099-1-matt@codeconstruct.com.au>
Message-ID: <169693275141.1756460.10774272832837669579.b4-ty@bootlin.com>


On Tue, 03 Oct 2023 15:53:39 +0800, Matt Johnston wrote:
> The MIPI I3C spec refers to a Provisioned ID, since it is (sometimes)
> provisioned at device manufacturing.
> 
> 

Applied, thanks!

[1/1] i3c: Fix typo "Provisional ID" to "Provisioned ID"
      commit: 57ec42b9a1b7e4db4a1c2aa4fcc4eefe6d31bcb8

Best regards,

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


From matt at codeconstruct.com.au  Thu Oct 12 21:06:24 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Fri, 13 Oct 2023 12:06:24 +0800
Subject: [PATCH net-next v6 2/3] i3c: Add support for bus enumeration & notification
In-Reply-To: <20231013040628.354323-1-matt@codeconstruct.com.au>
References: <20231013040628.354323-1-matt@codeconstruct.com.au>
Message-ID: <20231013040628.354323-3-matt@codeconstruct.com.au>

From: Jeremy Kerr <jk at codeconstruct.com.au>

This allows other drivers to be notified when new i3c busses are
attached, referring to a whole i3c bus as opposed to individual
devices.

Signed-off-by: Jeremy Kerr <jk at codeconstruct.com.au>
Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
Acked-by: Alexandre Belloni <alexandre.belloni at bootlin.com>
---
 drivers/i3c/master.c       | 35 +++++++++++++++++++++++++++++++++++
 include/linux/i3c/master.h | 11 +++++++++++
 2 files changed, 46 insertions(+)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 87283e4a4607..959ec5269376 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -22,6 +22,7 @@
 static DEFINE_IDR(i3c_bus_idr);
 static DEFINE_MUTEX(i3c_core_lock);
 static int __i3c_first_dynamic_bus_num;
+static BLOCKING_NOTIFIER_HEAD(i3c_bus_notifier);
 
 /**
  * i3c_bus_maintenance_lock - Lock the bus for a maintenance operation
@@ -453,6 +454,36 @@ static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np)
 	return 0;
 }
 
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+			     void *data)
+{
+	struct i3c_bus *bus;
+	int id;
+
+	mutex_lock(&i3c_core_lock);
+	idr_for_each_entry(&i3c_bus_idr, bus, id)
+		fn(bus, data);
+	mutex_unlock(&i3c_core_lock);
+}
+EXPORT_SYMBOL_GPL(i3c_for_each_bus_locked);
+
+int i3c_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_register_notifier);
+
+int i3c_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&i3c_bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(i3c_unregister_notifier);
+
+static void i3c_bus_notify(struct i3c_bus *bus, unsigned int action)
+{
+	blocking_notifier_call_chain(&i3c_bus_notifier, action, bus);
+}
+
 static const char * const i3c_bus_mode_strings[] = {
 	[I3C_BUS_MODE_PURE] = "pure",
 	[I3C_BUS_MODE_MIXED_FAST] = "mixed-fast",
@@ -2682,6 +2713,8 @@ int i3c_master_register(struct i3c_master_controller *master,
 	if (ret)
 		goto err_del_dev;
 
+	i3c_bus_notify(i3cbus, I3C_NOTIFY_BUS_ADD);
+
 	/*
 	 * We're done initializing the bus and the controller, we can now
 	 * register I3C devices discovered during the initial DAA.
@@ -2714,6 +2747,8 @@ EXPORT_SYMBOL_GPL(i3c_master_register);
  */
 void i3c_master_unregister(struct i3c_master_controller *master)
 {
+	i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
+
 	i3c_master_i2c_adapter_cleanup(master);
 	i3c_master_unregister_i3c_devs(master);
 	i3c_master_bus_cleanup(master);
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 0b52da4f2346..db909ef79be4 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -24,6 +24,12 @@
 
 struct i2c_client;
 
+/* notifier actions. notifier call data is the struct i3c_bus */
+enum {
+	I3C_NOTIFY_BUS_ADD,
+	I3C_NOTIFY_BUS_REMOVE,
+};
+
 struct i3c_master_controller;
 struct i3c_bus;
 struct i3c_device;
@@ -652,4 +658,9 @@ void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot);
 
 struct i3c_ibi_slot *i3c_master_get_free_ibi_slot(struct i3c_dev_desc *dev);
 
+void i3c_for_each_bus_locked(int (*fn)(struct i3c_bus *bus, void *data),
+			     void *data);
+int i3c_register_notifier(struct notifier_block *nb);
+int i3c_unregister_notifier(struct notifier_block *nb);
+
 #endif /* I3C_MASTER_H */
-- 
2.42.0



From matt at codeconstruct.com.au  Thu Oct 12 21:06:23 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Fri, 13 Oct 2023 12:06:23 +0800
Subject: [PATCH net-next v6 1/3] dt-bindings: i3c: Add mctp-controller property
In-Reply-To: <20231013040628.354323-1-matt@codeconstruct.com.au>
References: <20231013040628.354323-1-matt@codeconstruct.com.au>
Message-ID: <20231013040628.354323-2-matt@codeconstruct.com.au>

This property is used to describe a I3C bus with attached MCTP I3C
target devices.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski at linaro.org>
Acked-by: Alexandre Belloni <alexandre.belloni at bootlin.com>
---

v2:

- Reworded DT property description to match I2C

 Documentation/devicetree/bindings/i3c/i3c.yaml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml
index ab69f4115de4..d9483fbd2454 100644
--- a/Documentation/devicetree/bindings/i3c/i3c.yaml
+++ b/Documentation/devicetree/bindings/i3c/i3c.yaml
@@ -55,6 +55,12 @@ properties:
 
       May not be supported by all controllers.
 
+  mctp-controller:
+    type: boolean
+    description: |
+      Indicates that the system is accessible via this bus as an endpoint for
+      MCTP over I3C transport.
+
 required:
   - "#address-cells"
   - "#size-cells"
-- 
2.42.0



From matt at codeconstruct.com.au  Thu Oct 12 21:06:22 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Fri, 13 Oct 2023 12:06:22 +0800
Subject: [PATCH net-next v6 0/3] I3C MCTP net driver
Message-ID: <20231013040628.354323-1-matt@codeconstruct.com.au>

This series adds an I3C transport for the kernel's MCTP network
protocol. MCTP is a communication protocol between system components
(BMCs, drives, NICs etc), with higher level protocols such as NVMe-MI or
PLDM built on top of it (in userspace). It runs over various transports
such as I2C, PCIe, or I3C.

The mctp-i3c driver follows a similar approach to the kernel's existing
mctp-i2c driver, creating a "mctpi3cX" network interface for each
numbered I3C bus. Busses opt in to support by adding a "mctp-controller"
property to the devicetree:

&i3c0 {
        mctp-controller;
}

The driver will bind to MCTP class devices (DCR 0xCC) that are on a
supported I3C bus. Each bus is represented by a `struct mctp_i3c_bus`
that keeps state for the network device. An individual I3C device
(struct mctp_i3c_device) performs operations using the "parent"
mctp_i3c_bus object. The I3C notify/enumeration patch is needed so that
the mctp-i3c driver can handle creating/removing mctp_i3c_bus objects as
required.

The mctp-i3c driver is using the Provisioned ID as an identifier for
target I3C devices (the neighbour address), as that will be more stable
than the I3C dynamic address. The driver internally translates that to a
dynamic address for bus operations.

The driver has been tested using an AST2600 platform. A remote endpoint 
has been tested against QEMU, as well as using the target mode support 
in Aspeed's vendor tree.

I3C maintainers have acked merging this through net-next tree.

Thanks,
Matt

---

Thank you for the review Paolo, I've made those changes.

v6:

- Use multiple labels for error path handling
- Fix error handling from mctp_i3c_probe()
- Use spinlock_bh() instead for tx_lock
- Remove stale TODO comment (it had been tested)

v5:

- Use #define for constant initializer, fixes older gcc
- Wrap lines at 80 characters, fix parenthesis alignment 

v4:

- Add asm/unaligned.h include

v3:

- Use get_unaligned_be48()
- Use kthread_run()
- Don't set net namespace

v2:

- Rebased to net-next
- Removed unnecessary pr_ printing
- Fixed reverse christmas tree ordering
- Reworded DT property description to match I2C

Jeremy Kerr (1):
  i3c: Add support for bus enumeration & notification

Matt Johnston (2):
  dt-bindings: i3c: Add mctp-controller property
  mctp i3c: MCTP I3C driver

 .../devicetree/bindings/i3c/i3c.yaml          |   6 +
 drivers/i3c/master.c                          |  35 +
 drivers/net/mctp/Kconfig                      |   9 +
 drivers/net/mctp/Makefile                     |   1 +
 drivers/net/mctp/mctp-i3c.c                   | 755 ++++++++++++++++++
 include/linux/i3c/master.h                    |  11 +
 6 files changed, 817 insertions(+)
 create mode 100644 drivers/net/mctp/mctp-i3c.c

-- 
2.42.0



From matt at codeconstruct.com.au  Thu Oct 12 21:06:25 2023
From: matt at codeconstruct.com.au (Matt Johnston)
Date: Fri, 13 Oct 2023 12:06:25 +0800
Subject: [PATCH net-next v6 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231013040628.354323-1-matt@codeconstruct.com.au>
References: <20231013040628.354323-1-matt@codeconstruct.com.au>
Message-ID: <20231013040628.354323-4-matt@codeconstruct.com.au>

Provides MCTP network transport over an I3C bus, as specified in
DMTF DSP0233.

Each I3C bus (with "mctp-controller" devicetree property) gets an
"mctpi3cX" net device created. I3C devices are reachable as remote
endpoints through that net device. Link layer addressing uses the
I3C PID as a fixed hardware address for neighbour table entries.

The driver matches I3C devices that have the MIPI assigned DCR 0xCC for
MCTP.

Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
---

v6:

- Use multiple labels for error path handling
- Fix error handling from mctp_i3c_probe()
- Use spinlock_bh() instead for tx_lock
- Remove stale TODO comment (it had been tested)

v5:

- Use #define for constant initializer, fixes older gcc
- Wrap lines at 80 characters, fix parenthesis alignment 

v4:

- Add asm/unaligned.h include

v3:

- Use get_unaligned_be48()
- Use kthread_run()
- Don't set net namespace

v2:

- Removed unnecessary pr_ printing
- Fixed reverse christmas tree ordering

 drivers/net/mctp/Kconfig    |   9 +
 drivers/net/mctp/Makefile   |   1 +
 drivers/net/mctp/mctp-i3c.c | 755 ++++++++++++++++++++++++++++++++++++
 3 files changed, 765 insertions(+)
 create mode 100644 drivers/net/mctp/mctp-i3c.c

diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig
index dc71657d9184..ce9d2d2ccf3b 100644
--- a/drivers/net/mctp/Kconfig
+++ b/drivers/net/mctp/Kconfig
@@ -33,6 +33,15 @@ config MCTP_TRANSPORT_I2C
 	  from DMTF specification DSP0237. A MCTP protocol network device is
 	  created for each I2C bus that has been assigned a mctp-i2c device.
 
+config MCTP_TRANSPORT_I3C
+	tristate "MCTP I3C transport"
+	depends on I3C
+	help
+	  Provides a driver to access MCTP devices over I3C transport,
+	  from DMTF specification DSP0233.
+	  A MCTP protocol network device is created for each I3C bus
+	  having a "mctp-controller" devicetree property.
+
 endmenu
 
 endif
diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile
index 1ca3e6028f77..e1cb99ced54a 100644
--- a/drivers/net/mctp/Makefile
+++ b/drivers/net/mctp/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o
 obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o
+obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o
diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c
new file mode 100644
index 000000000000..8e989c157caa
--- /dev/null
+++ b/drivers/net/mctp/mctp-i3c.c
@@ -0,0 +1,755 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implements DMTF specification
+ * "DSP0233 Management Component Transport Protocol (MCTP) I3C Transport
+ * Binding"
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0233_1.0.0.pdf
+ *
+ * Copyright (c) 2023 Code Construct
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/if_arp.h>
+#include <asm/unaligned.h>
+#include <net/mctp.h>
+#include <net/mctpdevice.h>
+
+#define MCTP_I3C_MAXBUF 65536
+/* 48 bit Provisioned Id */
+#define PID_SIZE 6
+
+/* 64 byte payload, 4 byte MCTP header */
+static const int MCTP_I3C_MINMTU = 64 + 4;
+/* One byte less to allow for the PEC */
+static const int MCTP_I3C_MAXMTU = MCTP_I3C_MAXBUF - 1;
+/* 4 byte MCTP header, no data, 1 byte PEC */
+static const int MCTP_I3C_MINLEN = 4 + 1;
+
+/* Sufficient for 64kB at min mtu */
+static const int MCTP_I3C_TX_QUEUE_LEN = 1100;
+
+/* Somewhat arbitrary */
+static const int MCTP_I3C_IBI_SLOTS = 8;
+
+/* Mandatory Data Byte in an IBI, from DSP0233 */
+#define I3C_MDB_MCTP 0xAE
+/* From MIPI Device Characteristics Register (DCR) Assignments */
+#define I3C_DCR_MCTP 0xCC
+
+static const char *MCTP_I3C_OF_PROP = "mctp-controller";
+
+/* List of mctp_i3c_busdev */
+static LIST_HEAD(busdevs);
+/* Protects busdevs, as well as mctp_i3c_bus.devs lists */
+static DEFINE_MUTEX(busdevs_lock);
+
+struct mctp_i3c_bus {
+	struct net_device *ndev;
+
+	struct task_struct *tx_thread;
+	wait_queue_head_t tx_wq;
+	/* tx_lock protects tx_skb and devs */
+	spinlock_t tx_lock;
+	/* Next skb to transmit */
+	struct sk_buff *tx_skb;
+	/* Scratch buffer for xmit */
+	u8 tx_scratch[MCTP_I3C_MAXBUF];
+
+	/* Element of busdevs */
+	struct list_head list;
+
+	/* Provisioned ID of our controller */
+	u64 pid;
+
+	struct i3c_bus *bus;
+	/* Head of mctp_i3c_device.list. Protected by busdevs_lock */
+	struct list_head devs;
+};
+
+struct mctp_i3c_device {
+	struct i3c_device *i3c;
+	struct mctp_i3c_bus *mbus;
+	struct list_head list; /* Element of mctp_i3c_bus.devs */
+
+	/* Held while tx_thread is using this device */
+	struct mutex lock;
+
+	/* Whether BCR indicates MDB is present in IBI */
+	bool have_mdb;
+	/* I3C dynamic address */
+	u8 addr;
+	/* Maximum read length */
+	u16 mrl;
+	/* Maximum write length */
+	u16 mwl;
+	/* Provisioned ID */
+	u64 pid;
+};
+
+/* We synthesise a mac header using the Provisioned ID.
+ * Used to pass dest to mctp_i3c_start_xmit.
+ */
+struct mctp_i3c_internal_hdr {
+	u8 dest[PID_SIZE];
+	u8 source[PID_SIZE];
+} __packed;
+
+static int mctp_i3c_read(struct mctp_i3c_device *mi)
+{
+	struct i3c_priv_xfer xfer = { .rnw = 1, .len = mi->mrl };
+	struct net_device_stats *stats = &mi->mbus->ndev->stats;
+	struct mctp_i3c_internal_hdr *ihdr = NULL;
+	struct sk_buff *skb = NULL;
+	struct mctp_skb_cb *cb;
+	int net_status, rc;
+	u8 pec, addr;
+
+	skb = netdev_alloc_skb(mi->mbus->ndev,
+			       mi->mrl + sizeof(struct mctp_i3c_internal_hdr));
+	if (!skb) {
+		stats->rx_dropped++;
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	skb->protocol = htons(ETH_P_MCTP);
+	/* Create a header for internal use */
+	skb_reset_mac_header(skb);
+	ihdr = skb_put(skb, sizeof(struct mctp_i3c_internal_hdr));
+	put_unaligned_be48(mi->pid, ihdr->source);
+	put_unaligned_be48(mi->mbus->pid, ihdr->dest);
+	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
+
+	xfer.data.in = skb_put(skb, mi->mrl);
+
+	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
+	if (rc < 0)
+		goto err;
+
+	if (WARN_ON_ONCE(xfer.len > mi->mrl)) {
+		/* Bad i3c bus driver */
+		rc = -EIO;
+		goto err;
+	}
+	if (xfer.len < MCTP_I3C_MINLEN) {
+		stats->rx_length_errors++;
+		rc = -EIO;
+		goto err;
+	}
+
+	/* check PEC, including address byte */
+	addr = mi->addr << 1 | 1;
+	pec = i2c_smbus_pec(0, &addr, 1);
+	pec = i2c_smbus_pec(pec, xfer.data.in, xfer.len - 1);
+	if (pec != ((u8 *)xfer.data.in)[xfer.len - 1]) {
+		stats->rx_crc_errors++;
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* Remove PEC */
+	skb_trim(skb, xfer.len - 1);
+
+	cb = __mctp_cb(skb);
+	cb->halen = PID_SIZE;
+	put_unaligned_be48(mi->pid, cb->haddr);
+
+	net_status = netif_rx(skb);
+
+	if (net_status == NET_RX_SUCCESS) {
+		stats->rx_packets++;
+		stats->rx_bytes += xfer.len - 1;
+	} else {
+		stats->rx_dropped++;
+	}
+
+	return 0;
+err:
+	kfree_skb(skb);
+	return rc;
+}
+
+static void mctp_i3c_ibi_handler(struct i3c_device *i3c,
+				 const struct i3c_ibi_payload *payload)
+{
+	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
+
+	if (WARN_ON_ONCE(!mi))
+		return;
+
+	if (mi->have_mdb) {
+		if (payload->len > 0) {
+			if (((u8 *)payload->data)[0] != I3C_MDB_MCTP) {
+				/* Not a mctp-i3c interrupt, ignore it */
+				return;
+			}
+		} else {
+			/* The BCR advertised a Mandatory Data Byte but the
+			 * device didn't send one.
+			 */
+			dev_warn_once(i3cdev_to_dev(i3c), "IBI with missing MDB");
+		}
+	}
+
+	mctp_i3c_read(mi);
+}
+
+static int mctp_i3c_setup(struct mctp_i3c_device *mi)
+{
+	const struct i3c_ibi_setup ibi = {
+		.max_payload_len = 1,
+		.num_slots = MCTP_I3C_IBI_SLOTS,
+		.handler = mctp_i3c_ibi_handler,
+	};
+	struct i3c_device_info info;
+	int rc;
+
+	i3c_device_get_info(mi->i3c, &info);
+	mi->have_mdb = info.bcr & BIT(2);
+	mi->addr = info.dyn_addr;
+	mi->mwl = info.max_write_len;
+	mi->mrl = info.max_read_len;
+	mi->pid = info.pid;
+
+	rc = i3c_device_request_ibi(mi->i3c, &ibi);
+	if (rc == -ENOTSUPP) {
+		/* This driver only supports In-Band Interrupt mode.
+		 * Support for Polling Mode could be added if required.
+		 * (ENOTSUPP is from the i3c layer, not EOPNOTSUPP).
+		 */
+		dev_warn(i3cdev_to_dev(mi->i3c),
+			 "Failed, bus driver doesn't support In-Band Interrupts");
+		goto err;
+	} else if (rc < 0) {
+		dev_err(i3cdev_to_dev(mi->i3c),
+			"Failed requesting IBI (%d)\n", rc);
+		goto err;
+	}
+
+	rc = i3c_device_enable_ibi(mi->i3c);
+	if (rc < 0) {
+		/* Assume a driver supporting request_ibi also
+		 * supports enable_ibi.
+		 */
+		dev_err(i3cdev_to_dev(mi->i3c), "Failed enabling IBI (%d)\n", rc);
+		goto err_free_ibi;
+	}
+
+	return 0;
+
+err_free_ibi:
+	i3c_device_free_ibi(mi->i3c);
+
+err:
+	return rc;
+}
+
+/* Adds a new MCTP i3c_device to a bus */
+static int mctp_i3c_add_device(struct mctp_i3c_bus *mbus,
+			       struct i3c_device *i3c)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_device *mi = NULL;
+	int rc;
+
+	mi = kzalloc(sizeof(*mi), GFP_KERNEL);
+	if (!mi) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	mi->mbus = mbus;
+	mi->i3c = i3c;
+	mutex_init(&mi->lock);
+	list_add(&mi->list, &mbus->devs);
+
+	i3cdev_set_drvdata(i3c, mi);
+	rc = mctp_i3c_setup(mi);
+	if (rc < 0)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	list_del(&mi->list);
+	kfree(mi);
+
+err:
+	dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc);
+	return rc;
+}
+
+static int mctp_i3c_probe(struct i3c_device *i3c)
+{
+	struct mctp_i3c_bus *b = NULL, *mbus = NULL;
+
+	/* Look for a known bus */
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry(b, &busdevs, list)
+		if (b->bus == i3c->bus) {
+			mbus = b;
+			break;
+		}
+	mutex_unlock(&busdevs_lock);
+
+	if (!mbus) {
+		/* probably no "mctp-controller" property on the i3c bus */
+		return -ENODEV;
+	}
+
+	return mctp_i3c_add_device(mbus, i3c);
+}
+
+static void mctp_i3c_remove_device(struct mctp_i3c_device *mi)
+__must_hold(&busdevs_lock)
+{
+	/* Ensure the tx thread isn't using the device */
+	mutex_lock(&mi->lock);
+
+	/* Counterpart of mctp_i3c_setup */
+	i3c_device_disable_ibi(mi->i3c);
+	i3c_device_free_ibi(mi->i3c);
+
+	/* Counterpart of mctp_i3c_add_device */
+	i3cdev_set_drvdata(mi->i3c, NULL);
+	list_del(&mi->list);
+
+	/* Safe to unlock after removing from the list */
+	mutex_unlock(&mi->lock);
+	kfree(mi);
+}
+
+static void mctp_i3c_remove(struct i3c_device *i3c)
+{
+	struct mctp_i3c_device *mi = i3cdev_get_drvdata(i3c);
+
+	/* We my have received a Bus Remove notify prior to device remove,
+	 * so mi will already be removed.
+	 */
+	if (!mi)
+		return;
+
+	mutex_lock(&busdevs_lock);
+	mctp_i3c_remove_device(mi);
+	mutex_unlock(&busdevs_lock);
+}
+
+/* Returns the device for an address, with mi->lock held */
+static struct mctp_i3c_device *
+mctp_i3c_lookup(struct mctp_i3c_bus *mbus, u64 pid)
+{
+	struct mctp_i3c_device *mi = NULL, *ret = NULL;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry(mi, &mbus->devs, list)
+		if (mi->pid == pid) {
+			ret = mi;
+			mutex_lock(&mi->lock);
+			break;
+		}
+	mutex_unlock(&busdevs_lock);
+	return ret;
+}
+
+static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
+{
+	struct net_device_stats *stats = &mbus->ndev->stats;
+	struct i3c_priv_xfer xfer = { .rnw = false };
+	struct mctp_i3c_internal_hdr *ihdr = NULL;
+	struct mctp_i3c_device *mi = NULL;
+	unsigned int data_len;
+	u8 *data = NULL;
+	u8 addr, pec;
+	int rc = 0;
+	u64 pid;
+
+	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
+	data_len = skb->len;
+
+	ihdr = (void *)skb_mac_header(skb);
+
+	pid = get_unaligned_be48(ihdr->dest);
+	mi = mctp_i3c_lookup(mbus, pid);
+	if (!mi) {
+		/* I3C endpoint went away after the packet was enqueued? */
+		stats->tx_dropped++;
+		goto out;
+	}
+
+	if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF))
+		goto out;
+
+	if (data_len + 1 > (unsigned int)mi->mwl) {
+		/* Route MTU was larger than supported by the endpoint */
+		stats->tx_dropped++;
+		goto out;
+	}
+
+	/* Need a linear buffer with space for the PEC */
+	xfer.len = data_len + 1;
+	if (skb_tailroom(skb) >= 1) {
+		skb_put(skb, 1);
+		data = skb->data;
+	} else {
+		/* Otherwise need to copy the buffer */
+		skb_copy_bits(skb, 0, mbus->tx_scratch, skb->len);
+		data = mbus->tx_scratch;
+	}
+
+	/* PEC calculation */
+	addr = mi->addr << 1;
+	pec = i2c_smbus_pec(0, &addr, 1);
+	pec = i2c_smbus_pec(pec, data, data_len);
+	data[data_len] = pec;
+
+	xfer.data.out = data;
+	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
+	if (rc == 0) {
+		stats->tx_bytes += data_len;
+		stats->tx_packets++;
+	} else {
+		stats->tx_errors++;
+	}
+
+out:
+	if (mi)
+		mutex_unlock(&mi->lock);
+}
+
+static int mctp_i3c_tx_thread(void *data)
+{
+	struct mctp_i3c_bus *mbus = data;
+	struct sk_buff *skb;
+
+	for (;;) {
+		if (kthread_should_stop())
+			break;
+
+		spin_lock_bh(&mbus->tx_lock);
+		skb = mbus->tx_skb;
+		mbus->tx_skb = NULL;
+		spin_unlock_bh(&mbus->tx_lock);
+
+		if (netif_queue_stopped(mbus->ndev))
+			netif_wake_queue(mbus->ndev);
+
+		if (skb) {
+			mctp_i3c_xmit(mbus, skb);
+			kfree_skb(skb);
+		} else {
+			wait_event_idle(mbus->tx_wq,
+					mbus->tx_skb || kthread_should_stop());
+		}
+	}
+
+	return 0;
+}
+
+static netdev_tx_t mctp_i3c_start_xmit(struct sk_buff *skb,
+				       struct net_device *ndev)
+{
+	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
+	netdev_tx_t ret;
+
+	spin_lock(&mbus->tx_lock);
+	netif_stop_queue(ndev);
+	if (mbus->tx_skb) {
+		dev_warn_ratelimited(&ndev->dev, "TX with queue stopped");
+		ret = NETDEV_TX_BUSY;
+	} else {
+		mbus->tx_skb = skb;
+		ret = NETDEV_TX_OK;
+	}
+	spin_unlock(&mbus->tx_lock);
+
+	if (ret == NETDEV_TX_OK)
+		wake_up(&mbus->tx_wq);
+
+	return ret;
+}
+
+static void mctp_i3c_bus_free(struct mctp_i3c_bus *mbus)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_device *mi = NULL, *tmp = NULL;
+
+	if (mbus->tx_thread) {
+		kthread_stop(mbus->tx_thread);
+		mbus->tx_thread = NULL;
+	}
+
+	/* Remove any child devices */
+	list_for_each_entry_safe(mi, tmp, &mbus->devs, list) {
+		mctp_i3c_remove_device(mi);
+	}
+
+	kfree_skb(mbus->tx_skb);
+	list_del(&mbus->list);
+}
+
+static void mctp_i3c_ndo_uninit(struct net_device *ndev)
+{
+	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
+
+	/* Perform cleanup here to ensure there are no remaining references */
+	mctp_i3c_bus_free(mbus);
+}
+
+static int mctp_i3c_header_create(struct sk_buff *skb, struct net_device *dev,
+				  unsigned short type, const void *daddr,
+	   const void *saddr, unsigned int len)
+{
+	struct mctp_i3c_internal_hdr *ihdr;
+
+	skb_push(skb, sizeof(struct mctp_i3c_internal_hdr));
+	skb_reset_mac_header(skb);
+	ihdr = (void *)skb_mac_header(skb);
+	memcpy(ihdr->dest, daddr, PID_SIZE);
+	memcpy(ihdr->source, saddr, PID_SIZE);
+	return 0;
+}
+
+static const struct net_device_ops mctp_i3c_ops = {
+	.ndo_start_xmit = mctp_i3c_start_xmit,
+	.ndo_uninit = mctp_i3c_ndo_uninit,
+};
+
+static const struct header_ops mctp_i3c_headops = {
+	.create = mctp_i3c_header_create,
+};
+
+static void mctp_i3c_net_setup(struct net_device *dev)
+{
+	dev->type = ARPHRD_MCTP;
+
+	dev->mtu = MCTP_I3C_MAXMTU;
+	dev->min_mtu = MCTP_I3C_MINMTU;
+	dev->max_mtu = MCTP_I3C_MAXMTU;
+	dev->tx_queue_len = MCTP_I3C_TX_QUEUE_LEN;
+
+	dev->hard_header_len = sizeof(struct mctp_i3c_internal_hdr);
+	dev->addr_len = PID_SIZE;
+
+	dev->netdev_ops	= &mctp_i3c_ops;
+	dev->header_ops	= &mctp_i3c_headops;
+}
+
+static bool mctp_i3c_is_mctp_controller(struct i3c_bus *bus)
+{
+	struct i3c_dev_desc *master = bus->cur_master;
+
+	if (!master)
+		return false;
+
+	return of_property_read_bool(master->common.master->dev.of_node,
+				     MCTP_I3C_OF_PROP);
+}
+
+/* Returns the Provisioned Id of a local bus master */
+static int mctp_i3c_bus_local_pid(struct i3c_bus *bus, u64 *ret_pid)
+{
+	struct i3c_dev_desc *master;
+
+	master = bus->cur_master;
+	if (WARN_ON_ONCE(!master))
+		return -ENOENT;
+	*ret_pid = master->info.pid;
+
+	return 0;
+}
+
+/* Returns an ERR_PTR on failure */
+static struct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus)
+__must_hold(&busdevs_lock)
+{
+	struct mctp_i3c_bus *mbus = NULL;
+	struct net_device *ndev = NULL;
+	char namebuf[IFNAMSIZ];
+	u8 addr[PID_SIZE];
+	int rc;
+
+	if (!mctp_i3c_is_mctp_controller(bus))
+		return ERR_PTR(-ENOENT);
+
+	snprintf(namebuf, sizeof(namebuf), "mctpi3c%d", bus->id);
+	ndev = alloc_netdev(sizeof(*mbus), namebuf, NET_NAME_ENUM,
+			    mctp_i3c_net_setup);
+	if (!ndev) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	mbus = netdev_priv(ndev);
+	mbus->ndev = ndev;
+	mbus->bus = bus;
+	INIT_LIST_HEAD(&mbus->devs);
+	list_add(&mbus->list, &busdevs);
+
+	rc = mctp_i3c_bus_local_pid(bus, &mbus->pid);
+	if (rc < 0) {
+		dev_err(&ndev->dev, "No I3C PID available\n");
+		goto err_free_uninit;
+	}
+	put_unaligned_be48(mbus->pid, addr);
+	dev_addr_set(ndev, addr);
+
+	init_waitqueue_head(&mbus->tx_wq);
+	spin_lock_init(&mbus->tx_lock);
+	mbus->tx_thread = kthread_run(mctp_i3c_tx_thread, mbus,
+				      "%s/tx", ndev->name);
+	if (IS_ERR(mbus->tx_thread)) {
+		dev_warn(&ndev->dev, "Error creating thread: %pe\n",
+			 mbus->tx_thread);
+		rc = PTR_ERR(mbus->tx_thread);
+		mbus->tx_thread = NULL;
+		goto err_free_uninit;
+	}
+
+	rc = mctp_register_netdev(ndev, NULL);
+	if (rc < 0) {
+		dev_warn(&ndev->dev, "netdev register failed: %d\n", rc);
+		goto err_free_netdev;
+	}
+	return mbus;
+
+err_free_uninit:
+	/* uninit will not get called if a netdev has not been registered,
+	 * so we perform the same mbus cleanup manually.
+	 */
+	mctp_i3c_bus_free(mbus);
+
+err_free_netdev:
+	free_netdev(ndev);
+
+err:
+	return ERR_PTR(rc);
+}
+
+static void mctp_i3c_bus_remove(struct mctp_i3c_bus *mbus)
+__must_hold(&busdevs_lock)
+{
+	/* Unregister calls through to ndo_uninit -> mctp_i3c_bus_free() */
+	mctp_unregister_netdev(mbus->ndev);
+
+	free_netdev(mbus->ndev);
+	/* mbus is deallocated */
+}
+
+/* Removes all mctp-i3c busses */
+static void mctp_i3c_bus_remove_all(void)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list) {
+		mctp_i3c_bus_remove(mbus);
+	}
+	mutex_unlock(&busdevs_lock);
+}
+
+/* Adds a i3c_bus if it isn't already in the busdevs list.
+ * Suitable as an i3c_for_each_bus_locked callback.
+ */
+static int mctp_i3c_bus_add_new(struct i3c_bus *bus, void *data)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp = NULL;
+	bool exists = false;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
+		if (mbus->bus == bus)
+			exists = true;
+
+	/* It is OK for a bus to already exist. That can occur due to
+	 * the race in mod_init between notifier and for_each_bus
+	 */
+	if (!exists)
+		mctp_i3c_bus_add(bus);
+	mutex_unlock(&busdevs_lock);
+	return 0;
+}
+
+static void mctp_i3c_notify_bus_remove(struct i3c_bus *bus)
+{
+	struct mctp_i3c_bus *mbus = NULL, *tmp;
+
+	mutex_lock(&busdevs_lock);
+	list_for_each_entry_safe(mbus, tmp, &busdevs, list)
+		if (mbus->bus == bus)
+			mctp_i3c_bus_remove(mbus);
+	mutex_unlock(&busdevs_lock);
+}
+
+static int mctp_i3c_notifier_call(struct notifier_block *nb,
+				  unsigned long action, void *data)
+{
+	switch (action) {
+	case I3C_NOTIFY_BUS_ADD:
+		mctp_i3c_bus_add_new((struct i3c_bus *)data, NULL);
+		break;
+	case I3C_NOTIFY_BUS_REMOVE:
+		mctp_i3c_notify_bus_remove((struct i3c_bus *)data);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block mctp_i3c_notifier = {
+	.notifier_call = mctp_i3c_notifier_call,
+};
+
+static const struct i3c_device_id mctp_i3c_ids[] = {
+	I3C_CLASS(I3C_DCR_MCTP, NULL),
+	{ 0 },
+};
+
+static struct i3c_driver mctp_i3c_driver = {
+	.driver = {
+		.name = "mctp-i3c",
+	},
+	.probe = mctp_i3c_probe,
+	.remove = mctp_i3c_remove,
+	.id_table = mctp_i3c_ids,
+};
+
+static __init int mctp_i3c_mod_init(void)
+{
+	int rc;
+
+	rc = i3c_register_notifier(&mctp_i3c_notifier);
+	if (rc < 0) {
+		i3c_driver_unregister(&mctp_i3c_driver);
+		return rc;
+	}
+
+	i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL);
+
+	rc = i3c_driver_register(&mctp_i3c_driver);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static __exit void mctp_i3c_mod_exit(void)
+{
+	int rc;
+
+	i3c_driver_unregister(&mctp_i3c_driver);
+
+	rc = i3c_unregister_notifier(&mctp_i3c_notifier);
+	if (rc < 0)
+		pr_warn("MCTP I3C could not unregister notifier, %d\n", rc);
+
+	mctp_i3c_bus_remove_all();
+}
+
+module_init(mctp_i3c_mod_init);
+module_exit(mctp_i3c_mod_exit);
+
+MODULE_DEVICE_TABLE(i3c, mctp_i3c_ids);
+MODULE_DESCRIPTION("MCTP I3C device");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt Johnston <matt at codeconstruct.com.au>");
-- 
2.42.0



From pabeni at redhat.com  Thu Oct 12 04:50:39 2023
From: pabeni at redhat.com (Paolo Abeni)
Date: Thu, 12 Oct 2023 13:50:39 +0200
Subject: [PATCH net-next v5 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231009025451.490374-4-matt@codeconstruct.com.au>
References: <20231009025451.490374-1-matt@codeconstruct.com.au>
	 <20231009025451.490374-4-matt@codeconstruct.com.au>
Message-ID: <e704c45bc3c81d45541b82bded0618380d81634f.camel@redhat.com>

On Mon, 2023-10-09 at 10:54 +0800, Matt Johnston wrote:

> +static int mctp_i3c_setup(struct mctp_i3c_device *mi)
> +{
> +	bool ibi_set = false, ibi_enabled = false;
> +	const struct i3c_ibi_setup ibi = {
> +		.max_payload_len = 1,
> +		.num_slots = MCTP_I3C_IBI_SLOTS,
> +		.handler = mctp_i3c_ibi_handler,
> +	};
> +	struct i3c_device_info info;
> +	int rc;
> +
> +	i3c_device_get_info(mi->i3c, &info);
> +	mi->have_mdb = info.bcr & BIT(2);
> +	mi->addr = info.dyn_addr;
> +	mi->mwl = info.max_write_len;
> +	mi->mrl = info.max_read_len;
> +	mi->pid = info.pid;
> +
> +	rc = i3c_device_request_ibi(mi->i3c, &ibi);
> +	if (rc == 0) {
> +		ibi_set = true;
> +	} else if (rc == -ENOTSUPP) {
> +		/* This driver only supports In-Band Interrupt mode
> +		 * (ENOTSUPP is from the i3c layer, not EOPNOTSUPP).
> +		 * Support for Polling Mode could be added if required.
> +		 */
> +		dev_warn(i3cdev_to_dev(mi->i3c), "Failed, bus driver doesn't support In-Band Interrupts");
> +		goto err;
> +	} else {
> +		dev_err(i3cdev_to_dev(mi->i3c),
> +			"Failed requesting IBI (%d)\n", rc);
> +		goto err;
> +	}
> +
> +	if (ibi_set) {
> +		/* Device setup must be complete when IBI is enabled */
> +		rc = i3c_device_enable_ibi(mi->i3c);
> +		if (rc < 0) {
> +			/* Assume a driver supporting request_ibi also
> +			 * supports enable_ibi.
> +			 */
> +			dev_err(i3cdev_to_dev(mi->i3c),
> +				"Failed enabling IBI (%d)\n", rc);
> +			goto err;
> +		}
> +		ibi_enabled = true;
> +	}
> +
> +	return 0;
> +err:
> +	if (ibi_enabled)
> +		i3c_device_disable_ibi(mi->i3c);

Apparently no error code path can reach here with 'ibi_enabled ==
true', if so please drop the above lines.

> +	if (ibi_set)
> +		i3c_device_free_ibi(mi->i3c);
> +	return rc;
> +}
> +
> +/* Adds a new MCTP i3c_device to a bus */
> +static int mctp_i3c_add_device(struct mctp_i3c_bus *mbus,
> +			       struct i3c_device *i3c)
> +__must_hold(&busdevs_lock)
> +{
> +	struct mctp_i3c_device *mi = NULL;
> +	int rc;
> +
> +	mi = kzalloc(sizeof(*mi), GFP_KERNEL);
> +	if (!mi) {
> +		rc = -ENOMEM;
> +		goto err;
> +	}
> +	mi->mbus = mbus;
> +	mi->i3c = i3c;
> +	mutex_init(&mi->lock);
> +	list_add(&mi->list, &mbus->devs);
> +
> +	i3cdev_set_drvdata(i3c, mi);
> +	rc = mctp_i3c_setup(mi);
> +	if (rc < 0)
> +		goto err;

You can make the code simpler with:

		goto free;
> +
> +	return 0;

and here:

free:
	list_del(&mi->list);
	kfree(mi);

err:
	dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc);
	return rc;

> +err:
> +	dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc);
> +	if (mi) {
> +		list_del(&mi->list);
> +		kfree(mi);
> +	}
> +	return rc;
> +}
> +
> +static int mctp_i3c_probe(struct i3c_device *i3c)
> +{
> +	struct mctp_i3c_bus *b = NULL, *mbus = NULL;
> +	int rc;
> +
> +	/* Look for a known bus */
> +	mutex_lock(&busdevs_lock);
> +	list_for_each_entry(b, &busdevs, list)
> +		if (b->bus == i3c->bus) {
> +			mbus = b;
> +			break;
> +		}
> +	mutex_unlock(&busdevs_lock);
> +
> +	if (!mbus) {
> +		/* probably no "mctp-controller" property on the i3c bus */
> +		return -ENODEV;
> +	}
> +
> +	rc = mctp_i3c_add_device(mbus, i3c);
> +	if (!rc)
> +		goto err;

This is confusing: if 'rc' is zero (!rc) the function will return 0
('return rc' later), and otherwise it will return zero again.

Either ignore the return code, or more likely the error path need some
change.

> +static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb)
> +{
> +	struct net_device_stats *stats = &mbus->ndev->stats;
> +	struct i3c_priv_xfer xfer = { .rnw = false };
> +	struct mctp_i3c_internal_hdr *ihdr = NULL;
> +	struct mctp_i3c_device *mi = NULL;
> +	unsigned int data_len;
> +	u8 *data = NULL;
> +	u8 addr, pec;
> +	int rc = 0;
> +	u64 pid;
> +
> +	skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr));
> +	data_len = skb->len;
> +
> +	ihdr = (void *)skb_mac_header(skb);
> +
> +	pid = get_unaligned_be48(ihdr->dest);
> +	mi = mctp_i3c_lookup(mbus, pid);
> +	if (!mi) {
> +		/* I3C endpoint went away after the packet was enqueued? */
> +		stats->tx_dropped++;
> +		goto out;
> +	}
> +
> +	if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF))
> +		goto out;
> +
> +	if (data_len + 1 > (unsigned int)mi->mwl) {
> +		/* Route MTU was larger than supported by the endpoint */
> +		stats->tx_dropped++;
> +		goto out;
> +	}
> +
> +	/* Need a linear buffer with space for the PEC */
> +	xfer.len = data_len + 1;
> +	if (skb_tailroom(skb) >= 1) {
> +		skb_put(skb, 1);
> +		data = skb->data;
> +	} else {
> +		// TODO: test this

I hope this comment is a left over? In any case please avoid c++ style
comments.

> +		/* Otherwise need to copy the buffer */
> +		skb_copy_bits(skb, 0, mbus->tx_scratch, skb->len);
> +		data = mbus->tx_scratch;
> +	}
> +
> +	/* PEC calculation */
> +	addr = mi->addr << 1;
> +	pec = i2c_smbus_pec(0, &addr, 1);
> +	pec = i2c_smbus_pec(pec, data, data_len);
> +	data[data_len] = pec;
> +
> +	xfer.data.out = data;
> +	rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1);
> +	if (rc == 0) {
> +		stats->tx_bytes += data_len;
> +		stats->tx_packets++;
> +	} else {
> +		stats->tx_errors++;
> +	}
> +
> +out:
> +	if (mi)
> +		mutex_unlock(&mi->lock);
> +}
> +
> +static int mctp_i3c_tx_thread(void *data)
> +{
> +	struct mctp_i3c_bus *mbus = data;
> +	struct sk_buff *skb;
> +	unsigned long flags;
> +
> +	for (;;) {
> +		if (kthread_should_stop())
> +			break;
> +
> +		spin_lock_irqsave(&mbus->tx_lock, flags);
> +		skb = mbus->tx_skb;
> +		mbus->tx_skb = NULL;
> +		spin_unlock_irqrestore(&mbus->tx_lock, flags);
> +
> +		if (netif_queue_stopped(mbus->ndev))
> +			netif_wake_queue(mbus->ndev);
> +
> +		if (skb) {
> +			mctp_i3c_xmit(mbus, skb);
> +			kfree_skb(skb);
> +		} else {
> +			wait_event_idle(mbus->tx_wq,
> +					mbus->tx_skb || kthread_should_stop());
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static netdev_tx_t mctp_i3c_start_xmit(struct sk_buff *skb,
> +				       struct net_device *ndev)
> +{
> +	struct mctp_i3c_bus *mbus = netdev_priv(ndev);
> +	unsigned long flags;
> +	netdev_tx_t ret;
> +
> +	spin_lock_irqsave(&mbus->tx_lock, flags);

Why are you using the _irqsave variant? The only other path acquiring
this lock is mctp_i3c_tx_thread(), in process context, while here we
have BH disabled. Plain spin_lock should suffice here, paired with _BH
variant in mctp_i3c_tx_thread.

> +	netif_stop_queue(ndev);
> +	if (mbus->tx_skb) {
> +		dev_warn_ratelimited(&ndev->dev, "TX with queue stopped");
> +		ret = NETDEV_TX_BUSY;
> +	} else {
> +		mbus->tx_skb = skb;
> +		ret = NETDEV_TX_OK;
> +	}
> +	spin_unlock_irqrestore(&mbus->tx_lock, flags);
> +
> +	if (ret == NETDEV_TX_OK)
> +		wake_up(&mbus->tx_wq);
> +
> +	return ret;
> +}

[...]

> +/* Returns an ERR_PTR on failure */
> +static struct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus)
> +__must_hold(&busdevs_lock)
> +{
> +	struct mctp_i3c_bus *mbus = NULL;
> +	struct net_device *ndev = NULL;
> +	char namebuf[IFNAMSIZ];
> +	u8 addr[PID_SIZE];
> +	int rc;
> +
> +	if (!mctp_i3c_is_mctp_controller(bus))
> +		return ERR_PTR(-ENOENT);
> +
> +	snprintf(namebuf, sizeof(namebuf), "mctpi3c%d", bus->id);
> +	ndev = alloc_netdev(sizeof(*mbus), namebuf, NET_NAME_ENUM,
> +			    mctp_i3c_net_setup);
> +	if (!ndev) {
> +		rc = -ENOMEM;
> +		goto err;
> +	}
> +
> +	mbus = netdev_priv(ndev);
> +	mbus->ndev = ndev;
> +	mbus->bus = bus;
> +	INIT_LIST_HEAD(&mbus->devs);
> +	list_add(&mbus->list, &busdevs);
> +
> +	rc = mctp_i3c_bus_local_pid(bus, &mbus->pid);
> +	if (rc < 0) {
> +		dev_err(&ndev->dev, "No I3C PID available\n");
> +		goto err;
> +	}
> +	put_unaligned_be48(mbus->pid, addr);
> +	dev_addr_set(ndev, addr);
> +
> +	init_waitqueue_head(&mbus->tx_wq);
> +	spin_lock_init(&mbus->tx_lock);
> +	mbus->tx_thread = kthread_run(mctp_i3c_tx_thread, mbus,
> +				      "%s/tx", ndev->name);
> +	if (IS_ERR(mbus->tx_thread)) {
> +		dev_warn(&ndev->dev, "Error creating thread: %pe\n",
> +			 mbus->tx_thread);
> +		rc = PTR_ERR(mbus->tx_thread);
> +		mbus->tx_thread = NULL;
> +		goto err;
> +	}
> +
> +	rc = mctp_register_netdev(ndev, NULL);
> +	if (rc < 0) {
> +		dev_warn(&ndev->dev, "netdev register failed: %d\n", rc);
> +		goto err;
> +	}
> +	return mbus;
> +err:
> +	/* uninit will not get called if a netdev has not been registered,
> +	 * so we perform the same mbus cleanup manually.
> +	 */
> +	if (mbus)
> +		mctp_i3c_bus_free(mbus);
> +	if (ndev)
> +		free_netdev(ndev);

A more conventional way of handling the error paths would be using
multiple labels, e.g.:

err_free_bus:
	mctp_i3c_bus_free(mbus);

err_free_ndev:
	free_netdev(ndev);

err:

> +	return ERR_PTR(rc);
> +}

Cheers,

Paolo



From zbigniew.lukwinski at linux.intel.com  Sun Oct 15 15:23:33 2023
From: zbigniew.lukwinski at linux.intel.com (Zbigniew Lukwinski)
Date: Mon, 16 Oct 2023 00:23:33 +0200
Subject: [PATCH v1 0/1] Fix for IBI handling order
Message-ID: <20231015222334.1652401-1-zbigniew.lukwinski@linux.intel.com>

I would like to introduce here patch to cover IBI handling order broken.

IBI shall be handled in order they appear on the bus. Otherwise could hit
case when order of handling them in device driver will be different. It may
lead to invalid assembling fragmented packets or events order broken.

There are two use cases when this problem can happen.

1. MCTP over I3C (aligned with DMTF specification)
Here problem can happen when target device (CPU) wants to send
MCTP request fragment into a few MCTP packets. In such case flow will look
like (having three MCTP packets):
a. CPU wants to send first MCTP packet
b. BMC gets IBI for the first MCTP packet and task is created to handle
that IBI
d. created task handles IBI and runs read transaction to get MCTP packet
and waits for read transaction completion
e. when read transaction happens, CPU wants to send second MCTP packet
right away
f. BMC gets IBI for the second MCTP packet and task is created to handle
that IBI
h. created task (second one) handles IBI and runs read transaction to get
MCTP packet and waits for read transaction completion
e. when read transaction happens, CPU wants to send third MCTP packet right
away
f. BMC gets IBI for the third MCTP packet and task is created to handle
that IBI

At this point having three IBI task without explicitly configured order of
execution which could result with the second one will go first and second
MCTP packet will reach MCTP recipient first.

2. Debug for I3C (aligned with MIPI specification)
Here problem can happen when target device (CPU) sends multiple IBIs one
after another. This could result with the wrong event order will reach
recipient/consumer.

Added separate workqueue with option WQ_MEM_RECLAIM for each device driver.
This ensures IBI handling order and improves IBI handling performance: IBI
handlers for device B are not blocked by IBI handlers for device A.

Zbigniew Lukwinski (1):
  i3c: master: handle IBIs in order they came

 drivers/i3c/master.c       | 14 +++++++++++++-
 include/linux/i3c/master.h |  4 +++-
 2 files changed, 16 insertions(+), 2 deletions(-)

-- 
2.34.1



From zbigniew.lukwinski at linux.intel.com  Sun Oct 15 15:23:34 2023
From: zbigniew.lukwinski at linux.intel.com (Zbigniew Lukwinski)
Date: Mon, 16 Oct 2023 00:23:34 +0200
Subject: [PATCH v1 1/1] i3c: master: handle IBIs in order they came
In-Reply-To: <20231015222334.1652401-1-zbigniew.lukwinski@linux.intel.com>
References: <20231015222334.1652401-1-zbigniew.lukwinski@linux.intel.com>
Message-ID: <20231015222334.1652401-2-zbigniew.lukwinski@linux.intel.com>

IBI shall be handled in order they appear on the bus. Otherwise could hit
case when order of handling them in device driver will be different. It may
lead to invalid assembling fragmented packets or events order broken.

Added separate workqueue with option WQ_MEM_RECLAIM for each device driver.
This ensures IBI handling order and improves IBI handling performance: IBI
handlers for device B are not blocked by IBI handlers for device A.

Original solution (single workqueue in main driver) was able to handle also
general IBI (not related to specific device) like HJ or MR. So leaving this
for such purposes.

Signed-off-by: Zbigniew Lukwinski <zbigniew.lukwinski at linux.intel.com>
---
 drivers/i3c/master.c       | 14 +++++++++++++-
 include/linux/i3c/master.h |  4 +++-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 87283e4a4607..7d3785d11336 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2372,7 +2372,7 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
 void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
 {
 	atomic_inc(&dev->ibi->pending_ibis);
-	queue_work(dev->common.master->wq, &slot->work);
+	queue_work(dev->ibi->wq, &slot->work);
 }
 EXPORT_SYMBOL_GPL(i3c_master_queue_ibi);
 
@@ -2813,6 +2813,12 @@ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
 	if (!ibi)
 		return -ENOMEM;
 
+	ibi->wq = alloc_ordered_workqueue(dev_name(i3cdev_to_dev(dev->dev)), WQ_MEM_RECLAIM);
+	if (!ibi->wq) {
+		kfree(ibi);
+		return -ENOMEM;
+	}
+
 	atomic_set(&ibi->pending_ibis, 0);
 	init_completion(&ibi->all_ibis_handled);
 	ibi->handler = req->handler;
@@ -2840,6 +2846,12 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
 		WARN_ON(i3c_dev_disable_ibi_locked(dev));
 
 	master->ops->free_ibi(dev);
+
+	if (dev->ibi->wq) {
+		destroy_workqueue(dev->ibi->wq);
+		dev->ibi->wq = NULL;
+	}
+
 	kfree(dev->ibi);
 	dev->ibi = NULL;
 }
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 0b52da4f2346..0a2a33a0f3af 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -129,6 +129,7 @@ struct i3c_ibi_slot {
  *		     rejected by the master
  * @num_slots: number of IBI slots reserved for this device
  * @enabled: reflect the IBI status
+ * @wq: workqueue used to execute IBI handlers.
  * @handler: IBI handler specified at i3c_device_request_ibi() call time. This
  *	     handler will be called from the controller workqueue, and as such
  *	     is allowed to sleep (though it is recommended to process the IBI
@@ -151,6 +152,7 @@ struct i3c_device_ibi_info {
 	unsigned int max_payload_len;
 	unsigned int num_slots;
 	unsigned int enabled;
+	struct workqueue_struct *wq;
 	void (*handler)(struct i3c_device *dev,
 			const struct i3c_ibi_payload *payload);
 };
@@ -469,7 +471,7 @@ struct i3c_master_controller_ops {
  * @boardinfo.i2c: list of I2C boardinfo objects
  * @boardinfo: board-level information attached to devices connected on the bus
  * @bus: I3C bus exposed by this master
- * @wq: workqueue used to execute IBI handlers. Can also be used by master
+ * @wq: workqueue which can be used by master
  *	drivers if they need to postpone operations that need to take place
  *	in a thread context. Typical examples are Hot Join processing which
  *	requires taking the bus lock in maintenance, which in turn, can only
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:24:49 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:24:49 -0400
Subject: [PATCH 1/2] dt-bindings: i3c: Fix silvaco,i3c-master compatible string
Message-ID: <20231016152450.2850498-1-Frank.Li@nxp.com>

In driver, compatible string is silvaco,i3c-master instead of
silvaco,i3c-master-v1.

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

diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
index 133855f11b4f..19e3f75eb45c 100644
--- a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
+++ b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
@@ -14,7 +14,7 @@ allOf:
 
 properties:
   compatible:
-    const: silvaco,i3c-master-v1
+    const: silvaco,i3c-master
 
   reg:
     maxItems: 1
@@ -49,7 +49,7 @@ unevaluatedProperties: false
 examples:
   - |
     i3c-master at a0000000 {
-        compatible = "silvaco,i3c-master-v1";
+        compatible = "silvaco,i3c-master";
         clocks = <&zynqmp_clk 71>, <&fclk>, <&sclk>;
         clock-names = "pclk", "fast_clk", "slow_clk";
         interrupt-parent = <&gic>;
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:24:50 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:24:50 -0400
Subject: [PATCH 2/2] arm64: dts: freescale: imx93: add i3c1 and i3c2
In-Reply-To: <20231016152450.2850498-1-Frank.Li@nxp.com>
References: <20231016152450.2850498-1-Frank.Li@nxp.com>
Message-ID: <20231016152450.2850498-2-Frank.Li@nxp.com>

Add I3C1 and I3C2.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 arch/arm64/boot/dts/freescale/imx93.dtsi | 26 ++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi
index 6f85a05ee7e1..4d9ed0b32853 100644
--- a/arch/arm64/boot/dts/freescale/imx93.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx93.dtsi
@@ -242,6 +242,19 @@ tpm2: pwm at 44320000 {
 				status = "disabled";
 			};
 
+			i3c1: i3c-master at 44330000 {
+				compatible = "silvaco,i3c-master";
+				reg = <0x44330000 0x10000>;
+				interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <3>;
+				#size-cells = <0>;
+				clocks = <&clk IMX93_CLK_BUS_AON>,
+					 <&clk IMX93_CLK_I3C1_GATE>,
+					 <&clk IMX93_CLK_I3C1_SLOW>;
+				clock-names = "pclk", "fast_clk", "slow_clk";
+				status = "disabled";
+			};
+
 			lpi2c1: i2c at 44340000 {
 				compatible = "fsl,imx93-lpi2c", "fsl,imx7ulp-lpi2c";
 				reg = <0x44340000 0x10000>;
@@ -496,6 +509,19 @@ tpm6: pwm at 42510000 {
 				status = "disabled";
 			};
 
+			i3c2: i3c-master at 42520000 {
+				compatible = "silvaco,i3c-master";
+				reg = <0x42520000 0x10000>;
+				interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <3>;
+				#size-cells = <0>;
+				clocks = <&clk IMX93_CLK_BUS_WAKEUP>,
+					 <&clk IMX93_CLK_I3C2_GATE>,
+					 <&clk IMX93_CLK_I3C2_SLOW>;
+				clock-names = "pclk", "fast_clk", "slow_clk";
+				status = "disabled";
+			};
+
 			lpi2c3: i2c at 42530000 {
 				compatible = "fsl,imx93-lpi2c", "fsl,imx7ulp-lpi2c";
 				reg = <0x42530000 0x10000>;
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:32:26 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:32:26 -0400
Subject: [PATCH 0/6] i3c: master: svc: collection of bugs fixes
Message-ID: <20231016153232.2851095-1-Frank.Li@nxp.com>

Each patch is indepedents. See commit message for detail.

Frank Li (6):
  i3c: master: svc: fix race condition in ibi work thread
  i3c: master: svc: fix wrong data return when IBI happen during start
    frame
  i3c: master: svc: fix ibi may not return mandatory data byte
  i3c: master: svc: fix check wrong status register in irq handler
  i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
  i3c: master: svc: fix random hot join failure since timeout error

 drivers/i3c/master/svc-i3c-master.c | 50 ++++++++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)

-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:32:27 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:32:27 -0400
Subject: [PATCH 1/6] i3c: master: svc: fix race condition in ibi work thread
In-Reply-To: <20231016153232.2851095-1-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
Message-ID: <20231016153232.2851095-2-Frank.Li@nxp.com>

The ibi work thread operates asynchronously with other transfers, such as
svc_i3c_master_priv_xfers(). Introduces mutex protection to ensure the
completion of the entire i3c/i2c transaction.

Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
Cc: stable at vger.kernel.org
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index c308e22f0ac5..ebdb3ea1af9d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -202,6 +202,7 @@ struct svc_i3c_master {
 		/* Prevent races within IBI handlers */
 		spinlock_t lock;
 	} ibi;
+	struct mutex lock;
 };
 
 /**
@@ -383,6 +384,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 	u32 status, val;
 	int ret;
 
+	mutex_lock(&master->lock);
 	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
 	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
 	       SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -459,6 +461,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 
 reenable_ibis:
 	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+	mutex_unlock(&master->lock);
 }
 
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
@@ -1203,9 +1206,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = 0;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	kfree(buf);
@@ -1249,9 +1254,11 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = read_len;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	if (cmd->read_len != xfer_len)
 		ccc->dests[0].payload.len = cmd->read_len;
@@ -1308,9 +1315,11 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->continued = (i + 1) < nxfers;
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1346,9 +1355,11 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->continued = (i + 1 < nxfers);
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1539,6 +1550,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
 	INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
 	INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
+	mutex_init(&master->lock);
+
 	ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
 			       IRQF_NO_SUSPEND, "svc-i3c-irq", master);
 	if (ret)
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:32:28 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:32:28 -0400
Subject: [PATCH 2/6] i3c: master: svc: fix wrong data return when IBI happen during start frame
In-Reply-To: <20231016153232.2851095-1-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
Message-ID: <20231016153232.2851095-3-Frank.Li@nxp.com>

     ???????     ????  ????  ????  ????  ????  ????  ????  ????  ??????
SCL: ?     ???????  ????  ????  ????  ????  ????  ????  ????  ????
     ????                       ???????     ???????     ?????????????
SDA:    ?????????????????????????     ???????     ???????           ??????
     xxx?    ??                                        ??    ??    ??    ?
   : xxx?IBI ??               Addr(0x0a)               ?? RW ??NACK?? S  ?

In-Band Interrupt (IBI) occurred and IBI work thread may not to be
scheduled. When svc_i3c_master_priv_xfers() initiates the I3C transfer
frame and attempts to send address 0x7e, the target interprets it as an
IBI handler and returns the target address 0x0a.

However, svc_i3c_master_priv_xfers() does not handle this case and proceeds
with other transfers, resulting in incorrect data being returned.

IBIWON check has been added in svc_i3c_master_xfer(). In case this
situation occurs, a failure is now returned to the driver.

Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
Cc: stable at vger.kernel.org
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index ebdb3ea1af9d..0f57a5f75e39 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1009,6 +1009,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	u32 reg;
 	int ret;
 
+	/* clean SVC_I3C_MINT_IBIWON w1c bits */
+	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
 	writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
 	       xfer_type |
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
@@ -1027,6 +1030,23 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 	}
 
+	/*
+	 * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
+	 * with I3C Target Address.
+	 *
+	 * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
+	 * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
+	 * Role Request (i.e., Secondary Controller requests to become the Active Controller), or
+	 * a Hot-Join Request has been made.
+	 *
+	 * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return falure
+	 * and yeild the above events handler.
+	 */
+	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
+		ret = -ENXIO;
+		goto emit_stop;
+	}
+
 	if (rnw)
 		ret = svc_i3c_master_read(master, in, xfer_len);
 	else
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:32:29 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:32:29 -0400
Subject: [PATCH 3/6] i3c: master: svc: fix ibi may not return mandatory data byte
In-Reply-To: <20231016153232.2851095-1-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
Message-ID: <20231016153232.2851095-4-Frank.Li@nxp.com>

MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
creates an issue when the I3C clock is slow, and the CPU is running fast
enough that MSTATUS[RXPEND] may not be updated when the code reach checking
point. As a result, mandatory data are being missed.

Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data already
in FIFO.

Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
Cc: stable at vger.kernel.org
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 0f57a5f75e39..c252446b2bc5 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -331,6 +331,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	struct i3c_ibi_slot *slot;
 	unsigned int count;
 	u32 mdatactrl;
+	int ret, val;
 	u8 *buf;
 
 	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
@@ -340,6 +341,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	slot->len = 0;
 	buf = slot->data;
 
+	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
+						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
+	if (ret) {
+		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
+		return ret;
+	}
+
 	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
 	       slot->len < SVC_I3C_FIFO_SIZE) {
 		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:32:30 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:32:30 -0400
Subject: [PATCH 4/6] i3c: master: svc: fix check wrong status register in irq handler
In-Reply-To: <20231016153232.2851095-1-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
Message-ID: <20231016153232.2851095-5-Frank.Li@nxp.com>

svc_i3c_master_irq_handler() wrong check register SVC_I3C_MINTMASKED. It
should be SVC_I3C_MSTATUS.

Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
Cc: stable at vger.kernel.org
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index c252446b2bc5..5ab68d6e439d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -475,7 +475,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
 {
 	struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
-	u32 active = readl(master->regs + SVC_I3C_MINTMASKED);
+	u32 active = readl(master->regs + SVC_I3C_MSTATUS);
 
 	if (!SVC_I3C_MSTATUS_SLVSTART(active))
 		return IRQ_NONE;
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:32:31 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:32:31 -0400
Subject: [PATCH 5/6] i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
In-Reply-To: <20231016153232.2851095-1-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
Message-ID: <20231016153232.2851095-6-Frank.Li@nxp.com>

Need call svc_i3c_master_emit_stop() release bus.

Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
Cc: stable at vger.kernel.org
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 5ab68d6e439d..5bca369d6912 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -403,6 +403,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
 	if (ret) {
 		dev_err(master->dev, "Timeout when polling for IBIWON\n");
+		svc_i3c_master_emit_stop(master);
 		goto reenable_ibis;
 	}
 
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:32:32 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:32:32 -0400
Subject: [PATCH 6/6] i3c: master: svc: fix random hot join failure since timeout error
In-Reply-To: <20231016153232.2851095-1-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
Message-ID: <20231016153232.2851095-7-Frank.Li@nxp.com>

master side report:
  silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000

BIT 20: TIMEOUT error
  The module has stalled too long in a frame. This happens when:
  - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
middle of a message,
  - No STOP was issued and between messages,
  - IBI manual is used and no decision was made.
  The maximum stall period is 10 KHz or 100 ?s.

This is a just warning. System irq thread schedule latency is possible
bigger than 100us. Just omit this waring.

Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
Cc: stable at vger.kernel.org
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 5bca369d6912..18bc277edc8a 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -93,6 +93,7 @@
 #define SVC_I3C_MINTMASKED   0x098
 #define SVC_I3C_MERRWARN     0x09C
 #define   SVC_I3C_MERRWARN_NACK BIT(2)
+#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
 #define SVC_I3C_MDMACTRL     0x0A0
 #define SVC_I3C_MDATACTRL    0x0AC
 #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
@@ -225,6 +226,11 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
 	if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
 		merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
 		writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
+
+		/* ignore timeout error */
+		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
+			return false;
+
 		dev_err(master->dev,
 			"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
 			mstatus, merrwarn);
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:46:27 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:46:27 -0400
Subject: [PATCH 0/5] i3c: master: some improvment for i3c master
Message-ID: <20231016154632.2851957-1-Frank.Li@nxp.com>

These are dependent on bugs fixed serial: https://lore.kernel.org/imx/20231016153232.2851095-1-Frank.Li at nxp.com/T/#t
There are two major improment

1. Add actual size in i3c_transfer because i3c allow target early termiate
transfer.
2. Add API for i3c_dev_gettstatus_format1 for i3c comand GET_STATUS.
3. svc master enable hotjoin default

Frank Li (5):
  i3c: master: svc: enable hotjoin default
  i3c: add actual in i3c_priv_xfer
  i3c: svc: rename read_len as actual_len
  i3c: master: svc return actual transfer data len
  i3c: add API i3c_dev_gettstatus_format1() to get target device status

 drivers/i3c/device.c                | 24 ++++++++++++++++
 drivers/i3c/internals.h             |  1 +
 drivers/i3c/master.c                | 27 ++++++++++++++++++
 drivers/i3c/master/svc-i3c-master.c | 44 +++++++++++++++++++----------
 include/linux/i3c/device.h          |  2 ++
 5 files changed, 83 insertions(+), 15 deletions(-)

-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:46:28 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:46:28 -0400
Subject: [PATCH 1/5] i3c: master: svc: enable hotjoin default
In-Reply-To: <20231016154632.2851957-1-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
Message-ID: <20231016154632.2851957-2-Frank.Li@nxp.com>

Hotjoin require clock running and enable SLVSTART irq.
Add module parameter 'hotjoin' to disable hotjoin and enable runtime_pm to
save power.

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 18bc277edc8a..f0d99b029e5f 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -128,6 +128,9 @@
 /* This parameter depends on the implementation and may be tuned */
 #define SVC_I3C_FIFO_SIZE 16
 
+static bool hotjoin = true;
+module_param(hotjoin, bool, S_IRUGO | S_IWUSR);
+
 struct svc_i3c_cmd {
 	u8 addr;
 	bool rnw;
@@ -1623,8 +1626,12 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 	if (ret)
 		goto rpm_disable;
 
-	pm_runtime_mark_last_busy(&pdev->dev);
-	pm_runtime_put_autosuspend(&pdev->dev);
+	if (hotjoin) {
+		svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+	} else {
+		pm_runtime_mark_last_busy(&pdev->dev);
+		pm_runtime_put_autosuspend(&pdev->dev);
+	}
 
 	return 0;
 
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:46:29 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:46:29 -0400
Subject: [PATCH 2/5] i3c: add actual in i3c_priv_xfer
In-Reply-To: <20231016154632.2851957-1-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
Message-ID: <20231016154632.2851957-3-Frank.Li@nxp.com>

In MIPI I3C Specification:

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

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

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

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



From Frank.Li at nxp.com  Mon Oct 16 08:46:30 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:46:30 -0400
Subject: [PATCH 3/5] i3c: svc: rename read_len as actual_len
In-Reply-To: <20231016154632.2851957-1-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
Message-ID: <20231016154632.2851957-4-Frank.Li@nxp.com>

I3C transfer, target can early terminate transfer.
I2C transfer, target can NACK write transfer.

'actual_len' is better name than 'read_len'.

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index f0d99b029e5f..3570b709cf60 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -137,7 +137,7 @@ struct svc_i3c_cmd {
 	u8 *in;
 	const void *out;
 	unsigned int len;
-	unsigned int read_len;
+	unsigned int actual_len;
 	bool continued;
 };
 
@@ -1022,7 +1022,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
 static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 			       bool rnw, unsigned int xfer_type, u8 addr,
 			       u8 *in, const u8 *out, unsigned int xfer_len,
-			       unsigned int *read_len, bool continued)
+			       unsigned int *actual_len, bool continued)
 {
 	u32 reg;
 	int ret;
@@ -1035,7 +1035,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
 	       SVC_I3C_MCTRL_DIR(rnw) |
 	       SVC_I3C_MCTRL_ADDR(addr) |
-	       SVC_I3C_MCTRL_RDTERM(*read_len),
+	       SVC_I3C_MCTRL_RDTERM(*actual_len),
 	       master->regs + SVC_I3C_MCTRL);
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
@@ -1073,7 +1073,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 
 	if (rnw)
-		*read_len = ret;
+		*actual_len = ret;
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
 				 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
@@ -1155,7 +1155,7 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 
 		ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
 					  cmd->addr, cmd->in, cmd->out,
-					  cmd->len, &cmd->read_len,
+					  cmd->len, &cmd->actual_len,
 					  cmd->continued);
 		if (ret)
 			break;
@@ -1241,7 +1241,7 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = NULL;
 	cmd->out = buf;
 	cmd->len = xfer_len;
-	cmd->read_len = 0;
+	cmd->actual_len = 0;
 	cmd->continued = false;
 
 	mutex_lock(&master->lock);
@@ -1261,7 +1261,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 					      struct i3c_ccc_cmd *ccc)
 {
 	unsigned int xfer_len = ccc->dests[0].payload.len;
-	unsigned int read_len = ccc->rnw ? xfer_len : 0;
+	unsigned int actual_len = ccc->rnw ? xfer_len : 0;
 	struct svc_i3c_xfer *xfer;
 	struct svc_i3c_cmd *cmd;
 	int ret;
@@ -1279,7 +1279,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = NULL;
 	cmd->out = &ccc->id;
 	cmd->len = 1;
-	cmd->read_len = 0;
+	cmd->actual_len = 0;
 	cmd->continued = true;
 
 	/* Directed message */
@@ -1289,7 +1289,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL;
 	cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data,
 	cmd->len = xfer_len;
-	cmd->read_len = read_len;
+	cmd->actual_len = actual_len;
 	cmd->continued = false;
 
 	mutex_lock(&master->lock);
@@ -1298,8 +1298,8 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 		svc_i3c_master_dequeue_xfer(master, xfer);
 	mutex_unlock(&master->lock);
 
-	if (cmd->read_len != xfer_len)
-		ccc->dests[0].payload.len = cmd->read_len;
+	if (cmd->actual_len != xfer_len)
+		ccc->dests[0].payload.len = cmd->actual_len;
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1349,7 +1349,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
 		cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
 		cmd->len = xfers[i].len;
-		cmd->read_len = xfers[i].rnw ? xfers[i].len : 0;
+		cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0;
 		cmd->continued = (i + 1) < nxfers;
 	}
 
@@ -1389,7 +1389,7 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->in = cmd->rnw ? xfers[i].buf : NULL;
 		cmd->out = cmd->rnw ? NULL : xfers[i].buf;
 		cmd->len = xfers[i].len;
-		cmd->read_len = cmd->rnw ? xfers[i].len : 0;
+		cmd->actual_len = cmd->rnw ? xfers[i].len : 0;
 		cmd->continued = (i + 1 < nxfers);
 	}
 
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:46:31 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:46:31 -0400
Subject: [PATCH 4/5] i3c: master: svc return actual transfer data len
In-Reply-To: <20231016154632.2851957-1-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
Message-ID: <20231016154632.2851957-5-Frank.Li@nxp.com>

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

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 3570b709cf60..444825aafa6f 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -138,6 +138,7 @@ struct svc_i3c_cmd {
 	const void *out;
 	unsigned int len;
 	unsigned int actual_len;
+	struct i3c_priv_xfer *xfer;
 	bool continued;
 };
 
@@ -1045,6 +1046,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 
 	if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
 		ret = -ENXIO;
+		*actual_len = 0;
 		goto emit_stop;
 	}
 
@@ -1062,6 +1064,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	 */
 	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
 		ret = -ENXIO;
+		*actual_len = 0;
 		goto emit_stop;
 	}
 
@@ -1157,6 +1160,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 					  cmd->addr, cmd->in, cmd->out,
 					  cmd->len, &cmd->actual_len,
 					  cmd->continued);
+		if (cmd->xfer)
+			cmd->xfer->actual = cmd->actual_len;
+
 		if (ret)
 			break;
 	}
@@ -1344,6 +1350,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 	for (i = 0; i < nxfers; i++) {
 		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
 
+		cmd->xfer = xfers + i;
 		cmd->addr = master->addrs[data->index];
 		cmd->rnw = xfers[i].rnw;
 		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 08:46:32 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 11:46:32 -0400
Subject: [PATCH 5/5] i3c: add API i3c_dev_gettstatus_format1() to get target device status
In-Reply-To: <20231016154632.2851957-1-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
Message-ID: <20231016154632.2851957-6-Frank.Li@nxp.com>

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

Add API to fetch it with format1.

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

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



From Frank.li at nxp.com  Mon Oct 16 12:32:31 2023
From: Frank.li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 15:32:31 -0400
Subject: [PATCH 1/2] dt-bindings: i3c: Fix silvaco,i3c-master compatible
 string
In-Reply-To: <20231016-chatroom-regulator-a75f1d8be809@spud>
References: <20231016152450.2850498-1-Frank.Li@nxp.com>
 <20231016-chatroom-regulator-a75f1d8be809@spud>
Message-ID: <ZS2Pz1HxUgrx4+0R@lizhi-Precision-Tower-5810>

On Mon, Oct 16, 2023 at 06:30:11PM +0100, Conor Dooley wrote:
> On Mon, Oct 16, 2023 at 11:24:49AM -0400, Frank Li wrote:
> > In driver, compatible string is silvaco,i3c-master instead of
> > silvaco,i3c-master-v1.
> 
> And what makes the driver right & the binding wrong? AFAICT, this is an
> IP sold by silvaco & the -v1 suffix was explicitly requested during
> review of the binding.

The driver existed for the long time. DTS files already use
silvaco,i3c-master.

There are two options, one change doc, the other change dts and drivers.
I think change doc is easiest ways.

If there are v2 in future, we can added silvaco,i3c-master-v2 in future.

If everyone prefer change drivers and dts, I can work on new patch. Just
break back compatiblity.

Frank

> 
> Thanks,
> Conor.
> 
> > 
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> >  Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml | 4 ++--
> >  1 file changed, 2 insertions(+), 2 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
> > index 133855f11b4f..19e3f75eb45c 100644
> > --- a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
> > +++ b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
> > @@ -14,7 +14,7 @@ allOf:
> >  
> >  properties:
> >    compatible:
> > -    const: silvaco,i3c-master-v1
> > +    const: silvaco,i3c-master
> >  
> >    reg:
> >      maxItems: 1
> > @@ -49,7 +49,7 @@ unevaluatedProperties: false
> >  examples:
> >    - |
> >      i3c-master at a0000000 {
> > -        compatible = "silvaco,i3c-master-v1";
> > +        compatible = "silvaco,i3c-master";
> >          clocks = <&zynqmp_clk 71>, <&fclk>, <&sclk>;
> >          clock-names = "pclk", "fast_clk", "slow_clk";
> >          interrupt-parent = <&gic>;
> > -- 
> > 2.34.1
> > 




From krzysztof.kozlowski at linaro.org  Mon Oct 16 12:57:18 2023
From: krzysztof.kozlowski at linaro.org (Krzysztof Kozlowski)
Date: Mon, 16 Oct 2023 21:57:18 +0200
Subject: [PATCH 1/2] dt-bindings: i3c: Fix silvaco,i3c-master compatible
 string
In-Reply-To: <ZS2Pz1HxUgrx4+0R@lizhi-Precision-Tower-5810>
References: <20231016152450.2850498-1-Frank.Li@nxp.com>
 <20231016-chatroom-regulator-a75f1d8be809@spud>
 <ZS2Pz1HxUgrx4+0R@lizhi-Precision-Tower-5810>
Message-ID: <6110c58a-8003-4889-9a4b-6a7d1821c00e@linaro.org>

On 16/10/2023 21:32, Frank Li wrote:
> On Mon, Oct 16, 2023 at 06:30:11PM +0100, Conor Dooley wrote:
>> On Mon, Oct 16, 2023 at 11:24:49AM -0400, Frank Li wrote:
>>> In driver, compatible string is silvaco,i3c-master instead of
>>> silvaco,i3c-master-v1.
>>
>> And what makes the driver right & the binding wrong? AFAICT, this is an
>> IP sold by silvaco & the -v1 suffix was explicitly requested during
>> review of the binding.
> 
> The driver existed for the long time. DTS files already use
> silvaco,i3c-master.
> 
> There are two options, one change doc, the other change dts and drivers.
> I think change doc is easiest ways.
> 
> If there are v2 in future, we can added silvaco,i3c-master-v2 in future.
> 
> If everyone prefer change drivers and dts, I can work on new patch. Just
> break back compatiblity.

No, because Rob explicitly pointed out too generic name.
https://lore.kernel.org/all/20210111200821.GA2978830 at robh.at.kernel.org/

Is it some way to avoid implementing feedback or how does it work?

Best regards,
Krzysztof



From Frank.li at nxp.com  Mon Oct 16 13:10:57 2023
From: Frank.li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 16:10:57 -0400
Subject: [PATCH 1/2] dt-bindings: i3c: Fix silvaco,i3c-master compatible
 string
In-Reply-To: <6110c58a-8003-4889-9a4b-6a7d1821c00e@linaro.org>
References: <20231016152450.2850498-1-Frank.Li@nxp.com>
 <20231016-chatroom-regulator-a75f1d8be809@spud>
 <ZS2Pz1HxUgrx4+0R@lizhi-Precision-Tower-5810>
 <6110c58a-8003-4889-9a4b-6a7d1821c00e@linaro.org>
Message-ID: <ZS2Y0YRPItYhUBXE@lizhi-Precision-Tower-5810>

On Mon, Oct 16, 2023 at 09:57:18PM +0200, Krzysztof Kozlowski wrote:
> On 16/10/2023 21:32, Frank Li wrote:
> > On Mon, Oct 16, 2023 at 06:30:11PM +0100, Conor Dooley wrote:
> >> On Mon, Oct 16, 2023 at 11:24:49AM -0400, Frank Li wrote:
> >>> In driver, compatible string is silvaco,i3c-master instead of
> >>> silvaco,i3c-master-v1.
> >>
> >> And what makes the driver right & the binding wrong? AFAICT, this is an
> >> IP sold by silvaco & the -v1 suffix was explicitly requested during
> >> review of the binding.
> > 
> > The driver existed for the long time. DTS files already use
> > silvaco,i3c-master.
> > 
> > There are two options, one change doc, the other change dts and drivers.
> > I think change doc is easiest ways.
> > 
> > If there are v2 in future, we can added silvaco,i3c-master-v2 in future.
> > 
> > If everyone prefer change drivers and dts, I can work on new patch. Just
> > break back compatiblity.
> 
> No, because Rob explicitly pointed out too generic name.
> https://lore.kernel.org/all/20210111200821.GA2978830 at robh.at.kernel.org/
> 
> Is it some way to avoid implementing feedback or how does it work?

Downstream use silvaco,i3c-master to match driver. Anyways, miss match
happened.

option 1: fix doc
option 2: fix driver and dts

what do you perfer? 

> 
> Best regards,
> Krzysztof
> 


From krzysztof.kozlowski at linaro.org  Mon Oct 16 13:43:28 2023
From: krzysztof.kozlowski at linaro.org (Krzysztof Kozlowski)
Date: Mon, 16 Oct 2023 22:43:28 +0200
Subject: [PATCH 1/2] dt-bindings: i3c: Fix silvaco,i3c-master compatible
 string
In-Reply-To: <ZS2Y0YRPItYhUBXE@lizhi-Precision-Tower-5810>
References: <20231016152450.2850498-1-Frank.Li@nxp.com>
 <20231016-chatroom-regulator-a75f1d8be809@spud>
 <ZS2Pz1HxUgrx4+0R@lizhi-Precision-Tower-5810>
 <6110c58a-8003-4889-9a4b-6a7d1821c00e@linaro.org>
 <ZS2Y0YRPItYhUBXE@lizhi-Precision-Tower-5810>
Message-ID: <b5d3b52f-f551-483c-a4d4-763130b48fe3@linaro.org>

On 16/10/2023 22:10, Frank Li wrote:
> On Mon, Oct 16, 2023 at 09:57:18PM +0200, Krzysztof Kozlowski wrote:
>> On 16/10/2023 21:32, Frank Li wrote:
>>> On Mon, Oct 16, 2023 at 06:30:11PM +0100, Conor Dooley wrote:
>>>> On Mon, Oct 16, 2023 at 11:24:49AM -0400, Frank Li wrote:
>>>>> In driver, compatible string is silvaco,i3c-master instead of
>>>>> silvaco,i3c-master-v1.
>>>>
>>>> And what makes the driver right & the binding wrong? AFAICT, this is an
>>>> IP sold by silvaco & the -v1 suffix was explicitly requested during
>>>> review of the binding.
>>>
>>> The driver existed for the long time. DTS files already use
>>> silvaco,i3c-master.
>>>
>>> There are two options, one change doc, the other change dts and drivers.
>>> I think change doc is easiest ways.
>>>
>>> If there are v2 in future, we can added silvaco,i3c-master-v2 in future.
>>>
>>> If everyone prefer change drivers and dts, I can work on new patch. Just
>>> break back compatiblity.
>>
>> No, because Rob explicitly pointed out too generic name.
>> https://lore.kernel.org/all/20210111200821.GA2978830 at robh.at.kernel.org/
>>
>> Is it some way to avoid implementing feedback or how does it work?
> 
> Downstream use silvaco,i3c-master to match driver. Anyways, miss match

Too bad for downstream.

> happened.
> 
> option 1: fix doc
> option 2: fix driver and dts
> 
> what do you perfer? 

Fix driver and dts, keep existing compatible as deprecated.

Best regards,
Krzysztof



From Frank.Li at nxp.com  Mon Oct 16 14:18:53 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 17:18:53 -0400
Subject: [PATCH 1/2] i3c: master: svc: fix compatibility string mismatch with binding doc
Message-ID: <20231016211854.2901427-1-Frank.Li@nxp.com>

In the binding documentation, the compatible string is specified as
'silvaco,i3c-master-v1', but in the driver, it is defined as
'silvaco,i3c-master'.

Add 'silvaco,i3c-master-v1' to the driver and deprecates
'silvaco,i3c-master' to ensure compatibility with the documentation.

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

Notes:
    Change from v1 to v2
    - update driver by using compatible string silvaco,i3c-master-v1

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index cf932ee056ef..4c74b11b13c6 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1650,7 +1650,8 @@ static const struct dev_pm_ops svc_i3c_pm_ops = {
 };
 
 static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
-	{ .compatible = "silvaco,i3c-master" },
+	{ .compatible = "silvaco,i3c-master" }, /* deprecated */
+	{ .compatible = "silvaco,i3c-master-v1"},
 	{ /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 16 14:18:54 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 16 Oct 2023 17:18:54 -0400
Subject: [PATCH 2/2] arm64: dts: freescale: imx93: add i3c1 and i3c2
In-Reply-To: <20231016211854.2901427-1-Frank.Li@nxp.com>
References: <20231016211854.2901427-1-Frank.Li@nxp.com>
Message-ID: <20231016211854.2901427-2-Frank.Li@nxp.com>

Add I3C1 and I3C2.

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

Notes:
    Change from v1 to v2
    - using compatible string silvaco,i3c-master-v1

 arch/arm64/boot/dts/freescale/imx93.dtsi | 26 ++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi
index 6f85a05ee7e1..c6ad9ba9eb9a 100644
--- a/arch/arm64/boot/dts/freescale/imx93.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx93.dtsi
@@ -242,6 +242,19 @@ tpm2: pwm at 44320000 {
 				status = "disabled";
 			};
 
+			i3c1: i3c-master at 44330000 {
+				compatible = "silvaco,i3c-master-v1";
+				reg = <0x44330000 0x10000>;
+				interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <3>;
+				#size-cells = <0>;
+				clocks = <&clk IMX93_CLK_BUS_AON>,
+					 <&clk IMX93_CLK_I3C1_GATE>,
+					 <&clk IMX93_CLK_I3C1_SLOW>;
+				clock-names = "pclk", "fast_clk", "slow_clk";
+				status = "disabled";
+			};
+
 			lpi2c1: i2c at 44340000 {
 				compatible = "fsl,imx93-lpi2c", "fsl,imx7ulp-lpi2c";
 				reg = <0x44340000 0x10000>;
@@ -496,6 +509,19 @@ tpm6: pwm at 42510000 {
 				status = "disabled";
 			};
 
+			i3c2: i3c-master at 42520000 {
+				compatible = "silvaco,i3c-master-v1";
+				reg = <0x42520000 0x10000>;
+				interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <3>;
+				#size-cells = <0>;
+				clocks = <&clk IMX93_CLK_BUS_WAKEUP>,
+					 <&clk IMX93_CLK_I3C2_GATE>,
+					 <&clk IMX93_CLK_I3C2_SLOW>;
+				clock-names = "pclk", "fast_clk", "slow_clk";
+				status = "disabled";
+			};
+
 			lpi2c3: i2c at 42530000 {
 				compatible = "fsl,imx93-lpi2c", "fsl,imx7ulp-lpi2c";
 				reg = <0x42530000 0x10000>;
-- 
2.34.1



From conor at kernel.org  Mon Oct 16 10:30:11 2023
From: conor at kernel.org (Conor Dooley)
Date: Mon, 16 Oct 2023 18:30:11 +0100
Subject: [PATCH 1/2] dt-bindings: i3c: Fix silvaco,i3c-master compatible
 string
In-Reply-To: <20231016152450.2850498-1-Frank.Li@nxp.com>
References: <20231016152450.2850498-1-Frank.Li@nxp.com>
Message-ID: <20231016-chatroom-regulator-a75f1d8be809@spud>

On Mon, Oct 16, 2023 at 11:24:49AM -0400, Frank Li wrote:
> In driver, compatible string is silvaco,i3c-master instead of
> silvaco,i3c-master-v1.

And what makes the driver right & the binding wrong? AFAICT, this is an
IP sold by silvaco & the -v1 suffix was explicitly requested during
review of the binding.

Thanks,
Conor.

> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
> index 133855f11b4f..19e3f75eb45c 100644
> --- a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
> +++ b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
> @@ -14,7 +14,7 @@ allOf:
>  
>  properties:
>    compatible:
> -    const: silvaco,i3c-master-v1
> +    const: silvaco,i3c-master
>  
>    reg:
>      maxItems: 1
> @@ -49,7 +49,7 @@ unevaluatedProperties: false
>  examples:
>    - |
>      i3c-master at a0000000 {
> -        compatible = "silvaco,i3c-master-v1";
> +        compatible = "silvaco,i3c-master";
>          clocks = <&zynqmp_clk 71>, <&fclk>, <&sclk>;
>          clock-names = "pclk", "fast_clk", "slow_clk";
>          interrupt-parent = <&gic>;
> -- 
> 2.34.1
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-i3c/attachments/20231016/65a6ecf5/attachment.sig>

From jarkko.nikula at linux.intel.com  Mon Oct 16 23:10:31 2023
From: jarkko.nikula at linux.intel.com (Jarkko Nikula)
Date: Tue, 17 Oct 2023 09:10:31 +0300
Subject: [PATCH 2/5] i3c: add actual in i3c_priv_xfer
In-Reply-To: <20231016154632.2851957-3-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
 <20231016154632.2851957-3-Frank.Li@nxp.com>
Message-ID: <696dbfdc-1b08-446d-8983-dda09d861f59@linux.intel.com>

Hi

On 10/16/23 18:46, Frank Li wrote:
> In MIPI I3C Specification:
> 
> "Ninth Bit of SDR Target Returned (Read) Data as End-of-Data: In I2C, the
> ninth Data bit from Target to Controller is an ACK by the Controller. By
> contrast, in I3C this bit allows the Target to end a Read, and allows the
> Controller to Abort a Read. In SDR terms, the ninth bit of Read data is
> referred to as the T-Bit (for ?Transition?)"
> 
> I3C allow devices early terminate data transfer. So need "actual" field to
> indicate how much get by i3c_priv_xfer.
> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>   include/linux/i3c/device.h | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
> index 90fa83464f00..f2fa7ee5d96d 100644
> --- a/include/linux/i3c/device.h
> +++ b/include/linux/i3c/device.h
> @@ -66,6 +66,7 @@ struct i3c_priv_xfer {
>   		void *in;
>   		const void *out;
>   	} data;
> +	u16 actual;
>   	enum i3c_error_code err;
>   };
>   
Would this be more clear if named as "actual_len" and put next after 
"len" field in this structure? Also kerneldoc comment is missing.


From joe at perches.com  Mon Oct 16 23:48:03 2023
From: joe at perches.com (Joe Perches)
Date: Mon, 16 Oct 2023 23:48:03 -0700
Subject: [PATCH 1/5] i3c: master: svc: enable hotjoin default
In-Reply-To: <20231016154632.2851957-2-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
	 <20231016154632.2851957-2-Frank.Li@nxp.com>
Message-ID: <fe2148dd4e1547a937624ebb63653ab0ccb04383.camel@perches.com>

On Mon, 2023-10-16 at 11:46 -0400, Frank Li wrote:
> Hotjoin require clock running and enable SLVSTART irq.
> Add module parameter 'hotjoin' to disable hotjoin and enable runtime_pm to
> save power.
[]
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
[]
> @@ -128,6 +128,9 @@
>  /* This parameter depends on the implementation and may be tuned */
>  #define SVC_I3C_FIFO_SIZE 16
>  
> +static bool hotjoin = true;
> +module_param(hotjoin, bool, S_IRUGO | S_IWUSR);

Might be nicer using 0644

Consider using checkpatch --strict on the series.



From jarkko.nikula at linux.intel.com  Tue Oct 17 01:33:34 2023
From: jarkko.nikula at linux.intel.com (Jarkko Nikula)
Date: Tue, 17 Oct 2023 11:33:34 +0300
Subject: [PATCH 4/5] i3c: master: svc return actual transfer data len
In-Reply-To: <20231016154632.2851957-5-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
 <20231016154632.2851957-5-Frank.Li@nxp.com>
Message-ID: <af35a533-2126-4a61-a6cd-31ed1e615f2b@linux.intel.com>

Hi

On 10/16/23 18:46, Frank Li wrote:
> I3C allow devices early terminate data transfer. So set "actual" to
> indicate how much data get by i3c_priv_xfer.
> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>   drivers/i3c/master/svc-i3c-master.c | 7 +++++++
>   1 file changed, 7 insertions(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 3570b709cf60..444825aafa6f 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -138,6 +138,7 @@ struct svc_i3c_cmd {
>   	const void *out;
>   	unsigned int len;
>   	unsigned int actual_len;
> +	struct i3c_priv_xfer *xfer;
>   	bool continued;
>   };
>   
I'm thinking would it make sense to combine this and previous patch by 
removing the read_len/actual_len variable from this structure and use 
the added one (by the patch 2/5) from "struct i3c_priv_xfer" directly?


From horms at kernel.org  Tue Oct 17 01:24:27 2023
From: horms at kernel.org (Simon Horman)
Date: Tue, 17 Oct 2023 10:24:27 +0200
Subject: [PATCH net-next v6 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231013040628.354323-4-matt@codeconstruct.com.au>
References: <20231013040628.354323-1-matt@codeconstruct.com.au>
 <20231013040628.354323-4-matt@codeconstruct.com.au>
Message-ID: <20231017082427.GH1751252@kernel.org>

On Fri, Oct 13, 2023 at 12:06:25PM +0800, Matt Johnston wrote:
> Provides MCTP network transport over an I3C bus, as specified in
> DMTF DSP0233.
> 
> Each I3C bus (with "mctp-controller" devicetree property) gets an
> "mctpi3cX" net device created. I3C devices are reachable as remote
> endpoints through that net device. Link layer addressing uses the
> I3C PID as a fixed hardware address for neighbour table entries.
> 
> The driver matches I3C devices that have the MIPI assigned DCR 0xCC for
> MCTP.
> 
> Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>

Hi Matt,

one minor nit below, which you can take, leave, or leave for later
as far as I am concerned.

Overall the patch looks good to me and I see that Paolo's review of v5 has
has been addressed.

Reviewed-by: Simon Horman <horms at kernel.org>

> +/* List of mctp_i3c_busdev */
> +static LIST_HEAD(busdevs);
> +/* Protects busdevs, as well as mctp_i3c_bus.devs lists */
> +static DEFINE_MUTEX(busdevs_lock);
> +
> +struct mctp_i3c_bus {
> +	struct net_device *ndev;
> +
> +	struct task_struct *tx_thread;
> +	wait_queue_head_t tx_wq;
> +	/* tx_lock protects tx_skb and devs */
> +	spinlock_t tx_lock;
> +	/* Next skb to transmit */
> +	struct sk_buff *tx_skb;
> +	/* Scratch buffer for xmit */
> +	u8 tx_scratch[MCTP_I3C_MAXBUF];
> +
> +	/* Element of busdevs */
> +	struct list_head list;

I am unsure if it is important, but I observe that on x86_64
list spans a cacheline.

> +
> +	/* Provisioned ID of our controller */
> +	u64 pid;
> +
> +	struct i3c_bus *bus;
> +	/* Head of mctp_i3c_device.list. Protected by busdevs_lock */
> +	struct list_head devs;
> +};

...


From pabeni at redhat.com  Tue Oct 17 03:46:07 2023
From: pabeni at redhat.com (Paolo Abeni)
Date: Tue, 17 Oct 2023 12:46:07 +0200
Subject: [PATCH net-next v6 3/3] mctp i3c: MCTP I3C driver
In-Reply-To: <20231017082427.GH1751252@kernel.org>
References: <20231013040628.354323-1-matt@codeconstruct.com.au>
	 <20231013040628.354323-4-matt@codeconstruct.com.au>
	 <20231017082427.GH1751252@kernel.org>
Message-ID: <caa89a15568b2d92592476995bfcf362475be11f.camel@redhat.com>

On Tue, 2023-10-17 at 10:24 +0200, Simon Horman wrote:
> On Fri, Oct 13, 2023 at 12:06:25PM +0800, Matt Johnston wrote:
> > Provides MCTP network transport over an I3C bus, as specified in
> > DMTF DSP0233.
> > 
> > Each I3C bus (with "mctp-controller" devicetree property) gets an
> > "mctpi3cX" net device created. I3C devices are reachable as remote
> > endpoints through that net device. Link layer addressing uses the
> > I3C PID as a fixed hardware address for neighbour table entries.
> > 
> > The driver matches I3C devices that have the MIPI assigned DCR 0xCC for
> > MCTP.
> > 
> > Signed-off-by: Matt Johnston <matt at codeconstruct.com.au>
> 
> Hi Matt,
> 
> one minor nit below, which you can take, leave, or leave for later
> as far as I am concerned.
> 
> Overall the patch looks good to me and I see that Paolo's review of v5 has
> has been addressed.
> 
> Reviewed-by: Simon Horman <horms at kernel.org>
> 
> > +/* List of mctp_i3c_busdev */
> > +static LIST_HEAD(busdevs);
> > +/* Protects busdevs, as well as mctp_i3c_bus.devs lists */
> > +static DEFINE_MUTEX(busdevs_lock);
> > +
> > +struct mctp_i3c_bus {
> > +	struct net_device *ndev;
> > +
> > +	struct task_struct *tx_thread;
> > +	wait_queue_head_t tx_wq;
> > +	/* tx_lock protects tx_skb and devs */
> > +	spinlock_t tx_lock;
> > +	/* Next skb to transmit */
> > +	struct sk_buff *tx_skb;
> > +	/* Scratch buffer for xmit */
> > +	u8 tx_scratch[MCTP_I3C_MAXBUF];
> > +
> > +	/* Element of busdevs */
> > +	struct list_head list;
> 
> I am unsure if it is important, but I observe that on x86_64
> list spans a cacheline.

It looks like 'list' is only touched on control path, so it's should
not critical.

Cheers,

Paolo



From patchwork-bot+netdevbpf at kernel.org  Tue Oct 17 04:00:24 2023
From: patchwork-bot+netdevbpf at kernel.org (patchwork-bot+netdevbpf at kernel.org)
Date: Tue, 17 Oct 2023 11:00:24 +0000
Subject: [PATCH net-next v6 0/3] I3C MCTP net driver
In-Reply-To: <20231013040628.354323-1-matt@codeconstruct.com.au>
References: <20231013040628.354323-1-matt@codeconstruct.com.au>
Message-ID: <169754042417.23145.5070726957751164458.git-patchwork-notify@kernel.org>

Hello:

This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni at redhat.com>:

On Fri, 13 Oct 2023 12:06:22 +0800 you wrote:
> This series adds an I3C transport for the kernel's MCTP network
> protocol. MCTP is a communication protocol between system components
> (BMCs, drives, NICs etc), with higher level protocols such as NVMe-MI or
> PLDM built on top of it (in userspace). It runs over various transports
> such as I2C, PCIe, or I3C.
> 
> The mctp-i3c driver follows a similar approach to the kernel's existing
> mctp-i2c driver, creating a "mctpi3cX" network interface for each
> numbered I3C bus. Busses opt in to support by adding a "mctp-controller"
> property to the devicetree:
> 
> [...]

Here is the summary with links:
  - [net-next,v6,1/3] dt-bindings: i3c: Add mctp-controller property
    https://git.kernel.org/netdev/net-next/c/ee71d6d5f18b
  - [net-next,v6,2/3] i3c: Add support for bus enumeration & notification
    https://git.kernel.org/netdev/net-next/c/0ac6486e5cbd
  - [net-next,v6,3/3] mctp i3c: MCTP I3C driver
    https://git.kernel.org/netdev/net-next/c/c8755b29b58e

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html




From lkp at intel.com  Tue Oct 17 06:33:35 2023
From: lkp at intel.com (kernel test robot)
Date: Tue, 17 Oct 2023 21:33:35 +0800
Subject: [PATCH 1/6] i3c: master: svc: fix race condition in ibi work
 thread
In-Reply-To: <20231016153232.2851095-2-Frank.Li@nxp.com>
References: <20231016153232.2851095-2-Frank.Li@nxp.com>
Message-ID: <202310172150.4GVdV44X-lkp@intel.com>

Hi Frank,

kernel test robot noticed the following build warnings:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.6-rc6 next-20231017]
[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-master-svc-fix-race-condition-in-ibi-work-thread/20231017-151123
base:   linus/master
patch link:    https://lore.kernel.org/r/20231016153232.2851095-2-Frank.Li%40nxp.com
patch subject: [PATCH 1/6] i3c: master: svc: fix race condition in ibi work thread
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20231017/202310172150.4GVdV44X-lkp at intel.com/config)
compiler: m68k-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231017/202310172150.4GVdV44X-lkp at intel.com/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 <lkp at intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202310172150.4GVdV44X-lkp at intel.com/

All warnings (new ones prefixed by >>):

>> drivers/i3c/master/svc-i3c-master.c:207: warning: Function parameter or member 'lock' not described in 'svc_i3c_master'


vim +207 drivers/i3c/master/svc-i3c-master.c

1c5ee2a77b1bacd Clark Wang    2023-05-17  153  
dd3c52846d5954a Miquel Raynal 2021-01-21  154  /**
dd3c52846d5954a Miquel Raynal 2021-01-21  155   * struct svc_i3c_master - Silvaco I3C Master structure
dd3c52846d5954a Miquel Raynal 2021-01-21  156   * @base: I3C master controller
dd3c52846d5954a Miquel Raynal 2021-01-21  157   * @dev: Corresponding device
dd3c52846d5954a Miquel Raynal 2021-01-21  158   * @regs: Memory mapping
5496eac6ad7428f Miquel Raynal 2023-08-17  159   * @saved_regs: Volatile values for PM operations
dd3c52846d5954a Miquel Raynal 2021-01-21  160   * @free_slots: Bit array of available slots
dd3c52846d5954a Miquel Raynal 2021-01-21  161   * @addrs: Array containing the dynamic addresses of each attached device
dd3c52846d5954a Miquel Raynal 2021-01-21  162   * @descs: Array of descriptors, one per attached device
dd3c52846d5954a Miquel Raynal 2021-01-21  163   * @hj_work: Hot-join work
dd3c52846d5954a Miquel Raynal 2021-01-21  164   * @ibi_work: IBI work
dd3c52846d5954a Miquel Raynal 2021-01-21  165   * @irq: Main interrupt
dd3c52846d5954a Miquel Raynal 2021-01-21  166   * @pclk: System clock
dd3c52846d5954a Miquel Raynal 2021-01-21  167   * @fclk: Fast clock (bus)
dd3c52846d5954a Miquel Raynal 2021-01-21  168   * @sclk: Slow clock (other events)
dd3c52846d5954a Miquel Raynal 2021-01-21  169   * @xferqueue: Transfer queue structure
dd3c52846d5954a Miquel Raynal 2021-01-21  170   * @xferqueue.list: List member
dd3c52846d5954a Miquel Raynal 2021-01-21  171   * @xferqueue.cur: Current ongoing transfer
dd3c52846d5954a Miquel Raynal 2021-01-21  172   * @xferqueue.lock: Queue lock
dd3c52846d5954a Miquel Raynal 2021-01-21  173   * @ibi: IBI structure
dd3c52846d5954a Miquel Raynal 2021-01-21  174   * @ibi.num_slots: Number of slots available in @ibi.slots
dd3c52846d5954a Miquel Raynal 2021-01-21  175   * @ibi.slots: Available IBI slots
dd3c52846d5954a Miquel Raynal 2021-01-21  176   * @ibi.tbq_slot: To be queued IBI slot
dd3c52846d5954a Miquel Raynal 2021-01-21  177   * @ibi.lock: IBI lock
dd3c52846d5954a Miquel Raynal 2021-01-21  178   */
dd3c52846d5954a Miquel Raynal 2021-01-21  179  struct svc_i3c_master {
dd3c52846d5954a Miquel Raynal 2021-01-21  180  	struct i3c_master_controller base;
dd3c52846d5954a Miquel Raynal 2021-01-21  181  	struct device *dev;
dd3c52846d5954a Miquel Raynal 2021-01-21  182  	void __iomem *regs;
1c5ee2a77b1bacd Clark Wang    2023-05-17  183  	struct svc_i3c_regs_save saved_regs;
dd3c52846d5954a Miquel Raynal 2021-01-21  184  	u32 free_slots;
dd3c52846d5954a Miquel Raynal 2021-01-21  185  	u8 addrs[SVC_I3C_MAX_DEVS];
dd3c52846d5954a Miquel Raynal 2021-01-21  186  	struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS];
dd3c52846d5954a Miquel Raynal 2021-01-21  187  	struct work_struct hj_work;
dd3c52846d5954a Miquel Raynal 2021-01-21  188  	struct work_struct ibi_work;
dd3c52846d5954a Miquel Raynal 2021-01-21  189  	int irq;
dd3c52846d5954a Miquel Raynal 2021-01-21  190  	struct clk *pclk;
dd3c52846d5954a Miquel Raynal 2021-01-21  191  	struct clk *fclk;
dd3c52846d5954a Miquel Raynal 2021-01-21  192  	struct clk *sclk;
dd3c52846d5954a Miquel Raynal 2021-01-21  193  	struct {
dd3c52846d5954a Miquel Raynal 2021-01-21  194  		struct list_head list;
dd3c52846d5954a Miquel Raynal 2021-01-21  195  		struct svc_i3c_xfer *cur;
dd3c52846d5954a Miquel Raynal 2021-01-21  196  		/* Prevent races between transfers */
dd3c52846d5954a Miquel Raynal 2021-01-21  197  		spinlock_t lock;
dd3c52846d5954a Miquel Raynal 2021-01-21  198  	} xferqueue;
dd3c52846d5954a Miquel Raynal 2021-01-21  199  	struct {
dd3c52846d5954a Miquel Raynal 2021-01-21  200  		unsigned int num_slots;
dd3c52846d5954a Miquel Raynal 2021-01-21  201  		struct i3c_dev_desc **slots;
dd3c52846d5954a Miquel Raynal 2021-01-21  202  		struct i3c_ibi_slot *tbq_slot;
dd3c52846d5954a Miquel Raynal 2021-01-21  203  		/* Prevent races within IBI handlers */
dd3c52846d5954a Miquel Raynal 2021-01-21  204  		spinlock_t lock;
dd3c52846d5954a Miquel Raynal 2021-01-21  205  	} ibi;
f32ae0219a47f74 Frank Li      2023-10-16  206  	struct mutex lock;
dd3c52846d5954a Miquel Raynal 2021-01-21 @207  };
dd3c52846d5954a Miquel Raynal 2021-01-21  208  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


From miquel.raynal at bootlin.com  Tue Oct 17 07:03:27 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:03:27 +0200
Subject: [PATCH 1/2] i3c: master: svc: fix compatibility string mismatch
 with binding doc
In-Reply-To: <20231016211854.2901427-1-Frank.Li@nxp.com>
References: <20231016211854.2901427-1-Frank.Li@nxp.com>
Message-ID: <20231017160327.569043e0@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 17:18:53 -0400:

> In the binding documentation, the compatible string is specified as
> 'silvaco,i3c-master-v1', but in the driver, it is defined as
> 'silvaco,i3c-master'.
> 
> Add 'silvaco,i3c-master-v1' to the driver and deprecates
> 'silvaco,i3c-master' to ensure compatibility with the documentation.
> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>

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

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:04:57 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:04:57 +0200
Subject: [PATCH 1/5] i3c: master: svc: enable hotjoin default
In-Reply-To: <20231016154632.2851957-2-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
	<20231016154632.2851957-2-Frank.Li@nxp.com>
Message-ID: <20231017160457.4f0c2490@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:46:28 -0400:

> Hotjoin require clock running and enable SLVSTART irq.
> Add module parameter 'hotjoin' to disable hotjoin and enable runtime_pm to
> save power.

I am really not a big fan of the use of modules parameters. Maybe it
makes sense here. Alex, a better idea?

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:07:29 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:07:29 +0200
Subject: [PATCH 3/5] i3c: svc: rename read_len as actual_len
In-Reply-To: <20231016154632.2851957-4-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
	<20231016154632.2851957-4-Frank.Li@nxp.com>
Message-ID: <20231017160729.58984467@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:46:30 -0400:

> I3C transfer, target can early terminate transfer.
> I2C transfer, target can NACK write transfer.
> 
> 'actual_len' is better name than 'read_len'.

I don't find read_len confusing because it may mean "how much bytes
were actually read" rather than "how much we need to read". But if
actual_len sounds better, I'm fine.

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

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

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:10:07 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:10:07 +0200
Subject: [PATCH 4/5] i3c: master: svc return actual transfer data len
In-Reply-To: <20231016154632.2851957-5-Frank.Li@nxp.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
	<20231016154632.2851957-5-Frank.Li@nxp.com>
Message-ID: <20231017161007.146a5199@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:46:31 -0400:

> I3C allow devices early terminate data transfer. So set "actual" to
> indicate how much data get by i3c_priv_xfer.
> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/i3c/master/svc-i3c-master.c | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 3570b709cf60..444825aafa6f 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -138,6 +138,7 @@ struct svc_i3c_cmd {
>  	const void *out;
>  	unsigned int len;
>  	unsigned int actual_len;
> +	struct i3c_priv_xfer *xfer;
>  	bool continued;
>  };
>  
> @@ -1045,6 +1046,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
>  
>  	if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
>  		ret = -ENXIO;
> +		*actual_len = 0;
>  		goto emit_stop;
>  	}
>  
> @@ -1062,6 +1064,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
>  	 */
>  	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
>  		ret = -ENXIO;
> +		*actual_len = 0;
>  		goto emit_stop;
>  	}
>  
> @@ -1157,6 +1160,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
>  					  cmd->addr, cmd->in, cmd->out,
>  					  cmd->len, &cmd->actual_len,
>  					  cmd->continued);
> +		if (cmd->xfer)
> +			cmd->xfer->actual = cmd->actual_len;

Just to be sure, wouldn't it be more natural to always fill cmd->xfer
rather than checking it here?

> +
>  		if (ret)
>  			break;
>  	}
> @@ -1344,6 +1350,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
>  	for (i = 0; i < nxfers; i++) {
>  		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
>  
> +		cmd->xfer = xfers + i;

Please follow the same pattern as below: = &xfers[i]

>  		cmd->addr = master->addrs[data->index];
>  		cmd->rnw = xfers[i].rnw;
>  		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:15:04 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:15:04 +0200
Subject: [PATCH 1/2] dt-bindings: i3c: Fix silvaco,i3c-master compatible
 string
In-Reply-To: <b5d3b52f-f551-483c-a4d4-763130b48fe3@linaro.org>
References: <20231016152450.2850498-1-Frank.Li@nxp.com>
	<20231016-chatroom-regulator-a75f1d8be809@spud>
	<ZS2Pz1HxUgrx4+0R@lizhi-Precision-Tower-5810>
	<6110c58a-8003-4889-9a4b-6a7d1821c00e@linaro.org>
	<ZS2Y0YRPItYhUBXE@lizhi-Precision-Tower-5810>
	<b5d3b52f-f551-483c-a4d4-763130b48fe3@linaro.org>
Message-ID: <20231017161504.396150aa@xps-13>

Hi Krzysztof,

krzysztof.kozlowski at linaro.org wrote on Mon, 16 Oct 2023 22:43:28 +0200:

> On 16/10/2023 22:10, Frank Li wrote:
> > On Mon, Oct 16, 2023 at 09:57:18PM +0200, Krzysztof Kozlowski wrote:  
> >> On 16/10/2023 21:32, Frank Li wrote:  
> >>> On Mon, Oct 16, 2023 at 06:30:11PM +0100, Conor Dooley wrote:  
> >>>> On Mon, Oct 16, 2023 at 11:24:49AM -0400, Frank Li wrote:  
> >>>>> In driver, compatible string is silvaco,i3c-master instead of
> >>>>> silvaco,i3c-master-v1.  
> >>>>
> >>>> And what makes the driver right & the binding wrong? AFAICT, this is an
> >>>> IP sold by silvaco & the -v1 suffix was explicitly requested during
> >>>> review of the binding.  
> >>>
> >>> The driver existed for the long time. DTS files already use
> >>> silvaco,i3c-master.
> >>>
> >>> There are two options, one change doc, the other change dts and drivers.
> >>> I think change doc is easiest ways.
> >>>
> >>> If there are v2 in future, we can added silvaco,i3c-master-v2 in future.
> >>>
> >>> If everyone prefer change drivers and dts, I can work on new patch. Just
> >>> break back compatiblity.  
> >>
> >> No, because Rob explicitly pointed out too generic name.
> >> https://lore.kernel.org/all/20210111200821.GA2978830 at robh.at.kernel.org/
> >>
> >> Is it some way to avoid implementing feedback or how does it work?  

Not at all, it's my (faithful) fault, I did update the bindings upon
Rob request and apparently kept the previous string in the driver.

After thinking again I would drop silvaco,i3c-master entirely from the
driver, it was never intended to keep it.

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:16:58 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:16:58 +0200
Subject: [PATCH 1/6] i3c: master: svc: fix race condition in ibi work
 thread
In-Reply-To: <20231016153232.2851095-2-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-2-Frank.Li@nxp.com>
Message-ID: <20231017161658.2de6d9f3@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:27 -0400:

> The ibi work thread operates asynchronously with other transfers, such as
> svc_i3c_master_priv_xfers(). Introduces mutex protection to ensure the

Introduce

> completion of the entire i3c/i2c transaction.

Did you experience faulty conditions or was it reported thanks to
static analysis?

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

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

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:21:32 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:21:32 +0200
Subject: [PATCH 2/6] i3c: master: svc: fix wrong data return when IBI
 happen during start frame
In-Reply-To: <20231016153232.2851095-3-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-3-Frank.Li@nxp.com>
Message-ID: <20231017162132.361be2c3@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:28 -0400:

>      ???????     ????  ????  ????  ????  ????  ????  ????  ????  ??????
> SCL: ?     ???????  ????  ????  ????  ????  ????  ????  ????  ????
>      ????                       ???????     ???????     ?????????????
> SDA:    ?????????????????????????     ???????     ???????           ??????
>      xxx?    ??                                        ??    ??    ??    ?
>    : xxx?IBI ??               Addr(0x0a)               ?? RW ??NACK?? S  ?
> 
> In-Band Interrupt (IBI) occurred and IBI work thread may not to be

If an In-Band...	occurs and the IBI work thread is not
immediately scheduled, when svc... initiates an I3C transfer and
attempts...

> scheduled. When svc_i3c_master_priv_xfers() initiates the I3C transfer
> frame and attempts to send address 0x7e, the target interprets it as an
> IBI handler and returns the target address 0x0a.
> 
> However, svc_i3c_master_priv_xfers() does not handle this case and proceeds
> with other transfers, resulting in incorrect data being returned.
> 
> IBIWON check has been added in svc_i3c_master_xfer(). In case this

Add IBIWON check in svc_...

> situation occurs, a failure is now returned to the driver.

			return a failure...

> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/i3c/master/svc-i3c-master.c | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index ebdb3ea1af9d..0f57a5f75e39 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -1009,6 +1009,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
>  	u32 reg;
>  	int ret;
>  
> +	/* clean SVC_I3C_MINT_IBIWON w1c bits */
> +	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
> +
>  	writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
>  	       xfer_type |
>  	       SVC_I3C_MCTRL_IBIRESP_NACK |
> @@ -1027,6 +1030,23 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
>  		goto emit_stop;
>  	}
>  
> +	/*
> +	 * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
> +	 * with I3C Target Address.
> +	 *
> +	 * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
> +	 * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
> +	 * Role Request (i.e., Secondary Controller requests to become the Active Controller), or
> +	 * a Hot-Join Request has been made.
> +	 *
> +	 * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return falure
> +	 * and yeild the above events handler.

Typos: yeild and falure

> +	 */
> +	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
> +		ret = -ENXIO;
> +		goto emit_stop;
> +	}
> +
>  	if (rnw)
>  		ret = svc_i3c_master_read(master, in, xfer_len);
>  	else

With all the typos fixed:

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

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:27:07 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:27:07 +0200
Subject: [PATCH 3/6] i3c: master: svc: fix ibi may not return mandatory
 data byte
In-Reply-To: <20231016153232.2851095-4-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-4-Frank.Li@nxp.com>
Message-ID: <20231017162707.187bebcf@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:29 -0400:

> MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
> creates an issue when the I3C clock is slow, and the CPU is running fast
> enough that MSTATUS[RXPEND] may not be updated when the code reach checking
> point. As a result, mandatory data are being missed.

					can be missed.

> Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data already
> in FIFO.

is already in the FIFO

> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/i3c/master/svc-i3c-master.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 0f57a5f75e39..c252446b2bc5 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -331,6 +331,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
>  	struct i3c_ibi_slot *slot;
>  	unsigned int count;
>  	u32 mdatactrl;
> +	int ret, val;
>  	u8 *buf;
>  
>  	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
> @@ -340,6 +341,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
>  	slot->len = 0;
>  	buf = slot->data;
>  
> +	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
> +						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);

Are you sure !MSTATUS_COMPLETE(val) is never a valid condition?
Especially with non-mandatory bytes?

Also, are you sure of the indentation here?

> +	if (ret) {
> +		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
> +		return ret;
> +	}
> +
>  	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
>  	       slot->len < SVC_I3C_FIFO_SIZE) {
>  		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:28:46 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:28:46 +0200
Subject: [PATCH 4/6] i3c: master: svc: fix check wrong status register
 in irq handler
In-Reply-To: <20231016153232.2851095-5-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-5-Frank.Li@nxp.com>
Message-ID: <20231017162846.188d7a78@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:30 -0400:

> svc_i3c_master_irq_handler() wrong check register SVC_I3C_MINTMASKED. It

				wrongly checks
> should be SVC_I3C_MSTATUS.

Ah right, good catch.

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

> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/i3c/master/svc-i3c-master.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index c252446b2bc5..5ab68d6e439d 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -475,7 +475,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
>  static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
>  {
>  	struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
> -	u32 active = readl(master->regs + SVC_I3C_MINTMASKED);
> +	u32 active = readl(master->regs + SVC_I3C_MSTATUS);
>  
>  	if (!SVC_I3C_MSTATUS_SLVSTART(active))
>  		return IRQ_NONE;


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:29:24 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:29:24 +0200
Subject: [PATCH 5/6] i3c: master: svc: fix SDA keep low when polling
 IBIWON timeout happen
In-Reply-To: <20231016153232.2851095-6-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-6-Frank.Li@nxp.com>
Message-ID: <20231017162924.3a4b4200@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:31 -0400:

> Need call svc_i3c_master_emit_stop() release bus.

Can you please elaborate this commit message please?

> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/i3c/master/svc-i3c-master.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 5ab68d6e439d..5bca369d6912 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -403,6 +403,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
>  					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
>  	if (ret) {
>  		dev_err(master->dev, "Timeout when polling for IBIWON\n");
> +		svc_i3c_master_emit_stop(master);
>  		goto reenable_ibis;
>  	}
>  


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:33:35 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:33:35 +0200
Subject: [PATCH 6/6] i3c: master: svc: fix random hot join failure since
 timeout error
In-Reply-To: <20231016153232.2851095-7-Frank.Li@nxp.com>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-7-Frank.Li@nxp.com>
Message-ID: <20231017163335.16770af2@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:32 -0400:

> master side report:
>   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> 
> BIT 20: TIMEOUT error
>   The module has stalled too long in a frame. This happens when:
>   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> middle of a message,
>   - No STOP was issued and between messages,
>   - IBI manual is used and no decision was made.
>   The maximum stall period is 10 KHz or 100 ?s.
> 
> This is a just warning. System irq thread schedule latency is possible

							can be bigger
> bigger than 100us. Just omit this waring.

I'm not sure this is the correct approach. It's a real issue but there
is not much we can do about it. Perhaps dev_err is too high, but I
would not entirely drop this message. Maybe a comment and turning the
message into a dbg printk would be more appropriate?

> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  drivers/i3c/master/svc-i3c-master.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 5bca369d6912..18bc277edc8a 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -93,6 +93,7 @@
>  #define SVC_I3C_MINTMASKED   0x098
>  #define SVC_I3C_MERRWARN     0x09C
>  #define   SVC_I3C_MERRWARN_NACK BIT(2)
> +#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
>  #define SVC_I3C_MDMACTRL     0x0A0
>  #define SVC_I3C_MDATACTRL    0x0AC
>  #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
> @@ -225,6 +226,11 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
>  	if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
>  		merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
>  		writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
> +
> +		/* ignore timeout error */
> +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> +			return false;
> +
>  		dev_err(master->dev,
>  			"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
>  			mstatus, merrwarn);


Thanks,
Miqu?l


From Frank.li at nxp.com  Tue Oct 17 07:37:57 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 10:37:57 -0400
Subject: [PATCH 1/6] i3c: master: svc: fix race condition in ibi work
 thread
In-Reply-To: <20231017161658.2de6d9f3@xps-13>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
 <20231016153232.2851095-2-Frank.Li@nxp.com>
 <20231017161658.2de6d9f3@xps-13>
Message-ID: <ZS6cRULPrwWjCL02@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 04:16:58PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:27 -0400:
> 
> > The ibi work thread operates asynchronously with other transfers, such as
> > svc_i3c_master_priv_xfers(). Introduces mutex protection to ensure the
> 
> Introduce
> 
> > completion of the entire i3c/i2c transaction.
> 
> Did you experience faulty conditions or was it reported thanks to
> static analysis?

Yes, I met. But it needs my slave part patches, which will be ready sent
out review soon.

Frank

> 
> Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>
> 
> > 
> > Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> > Cc: stable at vger.kernel.org
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> 
> Thanks,
> Miqu?l


From Frank.li at nxp.com  Tue Oct 17 07:45:14 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 10:45:14 -0400
Subject: [PATCH 6/6] i3c: master: svc: fix random hot join failure since
 timeout error
In-Reply-To: <20231017163335.16770af2@xps-13>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
 <20231016153232.2851095-7-Frank.Li@nxp.com>
 <20231017163335.16770af2@xps-13>
Message-ID: <ZS6d+mANtuhoAcuX@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 04:33:35PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:32 -0400:
> 
> > master side report:
> >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > 
> > BIT 20: TIMEOUT error
> >   The module has stalled too long in a frame. This happens when:
> >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > middle of a message,
> >   - No STOP was issued and between messages,
> >   - IBI manual is used and no decision was made.
> >   The maximum stall period is 10 KHz or 100 ?s.
> > 
> > This is a just warning. System irq thread schedule latency is possible
> 
> 							can be bigger
> > bigger than 100us. Just omit this waring.
> 
> I'm not sure this is the correct approach. It's a real issue but there
> is not much we can do about it. Perhaps dev_err is too high, but I
> would not entirely drop this message. Maybe a comment and turning the
> message into a dbg printk would be more appropriate?

The key is not message. It return true, means IBI/HJ thread will not run.

Frank

> 
> > Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> > Cc: stable at vger.kernel.org
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> >  drivers/i3c/master/svc-i3c-master.c | 6 ++++++
> >  1 file changed, 6 insertions(+)
> > 
> > diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> > index 5bca369d6912..18bc277edc8a 100644
> > --- a/drivers/i3c/master/svc-i3c-master.c
> > +++ b/drivers/i3c/master/svc-i3c-master.c
> > @@ -93,6 +93,7 @@
> >  #define SVC_I3C_MINTMASKED   0x098
> >  #define SVC_I3C_MERRWARN     0x09C
> >  #define   SVC_I3C_MERRWARN_NACK BIT(2)
> > +#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
> >  #define SVC_I3C_MDMACTRL     0x0A0
> >  #define SVC_I3C_MDATACTRL    0x0AC
> >  #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
> > @@ -225,6 +226,11 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
> >  	if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
> >  		merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
> >  		writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
> > +
> > +		/* ignore timeout error */
> > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > +			return false;
> > +
> >  		dev_err(master->dev,
> >  			"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> >  			mstatus, merrwarn);
> 
> 
> Thanks,
> Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 07:49:46 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 16:49:46 +0200
Subject: [PATCH 1/6] i3c: master: svc: fix race condition in ibi work
 thread
In-Reply-To: <ZS6cRULPrwWjCL02@lizhi-Precision-Tower-5810>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-2-Frank.Li@nxp.com>
	<20231017161658.2de6d9f3@xps-13>
	<ZS6cRULPrwWjCL02@lizhi-Precision-Tower-5810>
Message-ID: <20231017164946.623abd0d@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Tue, 17 Oct 2023 10:37:57 -0400:

> On Tue, Oct 17, 2023 at 04:16:58PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:27 -0400:
> >   
> > > The ibi work thread operates asynchronously with other transfers, such as
> > > svc_i3c_master_priv_xfers(). Introduces mutex protection to ensure the  
> > 
> > Introduce
> >   
> > > completion of the entire i3c/i2c transaction.  
> > 
> > Did you experience faulty conditions or was it reported thanks to
> > static analysis?  
> 
> Yes, I met. But it needs my slave part patches, which will be ready sent
> out review soon.

I believe several of the "fixes" in this series are related to newer
uses (typically your i3c slave support) which were not in the scope of
the original submission. As these new features are not supposed to be
backported in stable kernels and because these are new corner cases,you
may drop the CC:/Fixes tags to avoid useless backports.

Some of them however are real fixes for situations we may happen with
the current level of support, please keep the tags in these, and move
them all to the beginning of your series.

Thanks,
Miqu?l


From Frank.li at nxp.com  Tue Oct 17 07:51:25 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 10:51:25 -0400
Subject: [PATCH 1/5] i3c: master: svc: enable hotjoin default
In-Reply-To: <20231017160457.4f0c2490@xps-13>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
 <20231016154632.2851957-2-Frank.Li@nxp.com>
 <20231017160457.4f0c2490@xps-13>
Message-ID: <ZS6fbVtbuBH32aRU@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 04:04:57PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:46:28 -0400:
> 
> > Hotjoin require clock running and enable SLVSTART irq.
> > Add module parameter 'hotjoin' to disable hotjoin and enable runtime_pm to
> > save power.
> 
> I am really not a big fan of the use of modules parameters. Maybe it
> makes sense here. Alex, a better idea?

Maybe we can create sys entry to enable/disable hotjoin. I think i3c
should default support hotjoin, but it exist user case that needn't hj and
want more aggressive power saving.

If create /sys/ entry, it need change driver/i3c/master.c.

Frank

> 
> Thanks,
> Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 08:06:03 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 17:06:03 +0200
Subject: [PATCH 6/6] i3c: master: svc: fix random hot join failure since
 timeout error
In-Reply-To: <ZS6d+mANtuhoAcuX@lizhi-Precision-Tower-5810>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-7-Frank.Li@nxp.com>
	<20231017163335.16770af2@xps-13>
	<ZS6d+mANtuhoAcuX@lizhi-Precision-Tower-5810>
Message-ID: <20231017170603.5ef996c2@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Tue, 17 Oct 2023 10:45:14 -0400:

> On Tue, Oct 17, 2023 at 04:33:35PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:32 -0400:
> >   
> > > master side report:
> > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > 
> > > BIT 20: TIMEOUT error
> > >   The module has stalled too long in a frame. This happens when:
> > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > middle of a message,
> > >   - No STOP was issued and between messages,
> > >   - IBI manual is used and no decision was made.
> > >   The maximum stall period is 10 KHz or 100 ?s.
> > > 
> > > This is a just warning. System irq thread schedule latency is possible  
> > 
> > 							can be bigger  
> > > bigger than 100us. Just omit this waring.  
> > 
> > I'm not sure this is the correct approach. It's a real issue but there
> > is not much we can do about it. Perhaps dev_err is too high, but I
> > would not entirely drop this message. Maybe a comment and turning the
> > message into a dbg printk would be more appropriate?  
> 
> The key is not message. It return true, means IBI/HJ thread will not run.

But why should the workers run if it's too late?

Thanks,
Miqu?l


From Frank.li at nxp.com  Tue Oct 17 08:10:54 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 11:10:54 -0400
Subject: [PATCH 1/6] i3c: master: svc: fix race condition in ibi work
 thread
In-Reply-To: <20231017164946.623abd0d@xps-13>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
 <20231016153232.2851095-2-Frank.Li@nxp.com>
 <20231017161658.2de6d9f3@xps-13>
 <ZS6cRULPrwWjCL02@lizhi-Precision-Tower-5810>
 <20231017164946.623abd0d@xps-13>
Message-ID: <ZS6j/tpqEppFADk1@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 04:49:46PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.li at nxp.com wrote on Tue, 17 Oct 2023 10:37:57 -0400:
> 
> > On Tue, Oct 17, 2023 at 04:16:58PM +0200, Miquel Raynal wrote:
> > > Hi Frank,
> > > 
> > > Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:27 -0400:
> > >   
> > > > The ibi work thread operates asynchronously with other transfers, such as
> > > > svc_i3c_master_priv_xfers(). Introduces mutex protection to ensure the  
> > > 
> > > Introduce
> > >   
> > > > completion of the entire i3c/i2c transaction.  
> > > 
> > > Did you experience faulty conditions or was it reported thanks to
> > > static analysis?  
> > 
> > Yes, I met. But it needs my slave part patches, which will be ready sent
> > out review soon.
> 
> I believe several of the "fixes" in this series are related to newer
> uses (typically your i3c slave support) which were not in the scope of
> the original submission. As these new features are not supposed to be
> backported in stable kernels and because these are new corner cases,you
> may drop the CC:/Fixes tags to avoid useless backports.

I don't think so. The issue exists. Just difficult to find it. If there are
i3c device that use IBI frequently. The problem should be trigger. My case
just make it easy to trigger the problem.

In previous existed code, IBI is supported.

I think it is typical case, which need CC/Fixes.

Frank

> 
> Some of them however are real fixes for situations we may happen with
> the current level of support, please keep the tags in these, and move
> them all to the beginning of your series.
> 
> Thanks,
> Miqu?l


From miquel.raynal at bootlin.com  Tue Oct 17 08:23:09 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Tue, 17 Oct 2023 17:23:09 +0200
Subject: [PATCH 1/6] i3c: master: svc: fix race condition in ibi work
 thread
In-Reply-To: <ZS6j/tpqEppFADk1@lizhi-Precision-Tower-5810>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
	<20231016153232.2851095-2-Frank.Li@nxp.com>
	<20231017161658.2de6d9f3@xps-13>
	<ZS6cRULPrwWjCL02@lizhi-Precision-Tower-5810>
	<20231017164946.623abd0d@xps-13>
	<ZS6j/tpqEppFADk1@lizhi-Precision-Tower-5810>
Message-ID: <20231017172309.2a251393@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Tue, 17 Oct 2023 11:10:54 -0400:

> On Tue, Oct 17, 2023 at 04:49:46PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.li at nxp.com wrote on Tue, 17 Oct 2023 10:37:57 -0400:
> >   
> > > On Tue, Oct 17, 2023 at 04:16:58PM +0200, Miquel Raynal wrote:  
> > > > Hi Frank,
> > > > 
> > > > Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:27 -0400:
> > > >     
> > > > > The ibi work thread operates asynchronously with other transfers, such as
> > > > > svc_i3c_master_priv_xfers(). Introduces mutex protection to ensure the    
> > > > 
> > > > Introduce
> > > >     
> > > > > completion of the entire i3c/i2c transaction.    
> > > > 
> > > > Did you experience faulty conditions or was it reported thanks to
> > > > static analysis?    
> > > 
> > > Yes, I met. But it needs my slave part patches, which will be ready sent
> > > out review soon.  
> > 
> > I believe several of the "fixes" in this series are related to newer
> > uses (typically your i3c slave support) which were not in the scope of
> > the original submission. As these new features are not supposed to be
> > backported in stable kernels and because these are new corner cases,you
> > may drop the CC:/Fixes tags to avoid useless backports.  
> 
> I don't think so. The issue exists. Just difficult to find it. If there are
> i3c device that use IBI frequently. The problem should be trigger. My case
> just make it easy to trigger the problem.
> 
> In previous existed code, IBI is supported.
> 
> I think it is typical case, which need CC/Fixes.

I am not talking about this patch in particular.

> 
> Frank
> 
> > 
> > Some of them however are real fixes for situations we may happen with
> > the current level of support, please keep the tags in these, and move
> > them all to the beginning of your series.
> > 
> > Thanks,
> > Miqu?l  


Thanks,
Miqu?l


From Frank.li at nxp.com  Tue Oct 17 08:25:37 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 11:25:37 -0400
Subject: [PATCH 6/6] i3c: master: svc: fix random hot join failure since
 timeout error
In-Reply-To: <20231017170603.5ef996c2@xps-13>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
 <20231016153232.2851095-7-Frank.Li@nxp.com>
 <20231017163335.16770af2@xps-13>
 <ZS6d+mANtuhoAcuX@lizhi-Precision-Tower-5810>
 <20231017170603.5ef996c2@xps-13>
Message-ID: <ZS6ncZeNaen13s1b@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 05:06:03PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.li at nxp.com wrote on Tue, 17 Oct 2023 10:45:14 -0400:
> 
> > On Tue, Oct 17, 2023 at 04:33:35PM +0200, Miquel Raynal wrote:
> > > Hi Frank,
> > > 
> > > Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:32 -0400:
> > >   
> > > > master side report:
> > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > 
> > > > BIT 20: TIMEOUT error
> > > >   The module has stalled too long in a frame. This happens when:
> > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > middle of a message,
> > > >   - No STOP was issued and between messages,
> > > >   - IBI manual is used and no decision was made.
> > > >   The maximum stall period is 10 KHz or 100 ?s.
> > > > 
> > > > This is a just warning. System irq thread schedule latency is possible  
> > > 
> > > 							can be bigger  
> > > > bigger than 100us. Just omit this waring.  
> > > 
> > > I'm not sure this is the correct approach. It's a real issue but there
> > > is not much we can do about it. Perhaps dev_err is too high, but I
> > > would not entirely drop this message. Maybe a comment and turning the
> > > message into a dbg printk would be more appropriate?  
> > 
> > The key is not message. It return true, means IBI/HJ thread will not run.
> 
> But why should the workers run if it's too late?

IBI ACK already sent, target think master already accepted IBI. then master
driver check TIMEOUT, 

If without run IBI thread, target's driver will wait target sent IBI. And
target wait for driver handle IBI. 

So the whole system may lock or wait for long time out.

Hardware check TIMEOUT and software send ACK is totally async.

> 
> Thanks,
> Miqu?l


From Frank.Li at nxp.com  Tue Oct 17 12:46:56 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 15:46:56 -0400
Subject: [PATCH v2 1/2] i3c: master: svc: fix compatibility string mismatch with binding doc
Message-ID: <20231017194657.3199749-1-Frank.Li@nxp.com>

In the binding documentation, the compatible string is specified as
'silvaco,i3c-master-v1', but in the driver, it is defined as
'silvaco,i3c-master'.

Rename 'silvaco,i3c-master' to 'silvaco,i3c-master-v1' to ensure
compatibility with the documentation.

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

Notes:
    Change from v2 to v3
    - Miqu?l think drop silvaco,i3c-master totally. I grep dts tree. No
    upstream dts use silvaco,i3c-master yet.
      I have not add "Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>",
    it is different solution with previous one even just one line change.
      I think v2/v3 both is good, feel free to pick each one.
    
    Change from v1 to v2
    - update driver by using compatible string silvaco,i3c-master-v1
    
    Change from v1 to v2
    - update driver by using compatible string silvaco,i3c-master-v1

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index cf932ee056ef9..e307ba2b965e6 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1650,7 +1650,7 @@ static const struct dev_pm_ops svc_i3c_pm_ops = {
 };
 
 static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
-	{ .compatible = "silvaco,i3c-master" },
+	{ .compatible = "silvaco,i3c-master-v1"},
 	{ /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
-- 
2.34.1



From Frank.Li at nxp.com  Tue Oct 17 12:46:57 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 15:46:57 -0400
Subject: [PATCH v2 2/2] arm64: dts: freescale: imx93: add i3c1 and i3c2
In-Reply-To: <20231017194657.3199749-1-Frank.Li@nxp.com>
References: <20231017194657.3199749-1-Frank.Li@nxp.com>
Message-ID: <20231017194657.3199749-2-Frank.Li@nxp.com>

Add I3C1 and I3C2.

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

Notes:
    Change from v1 to v2
    - using compatible string silvaco,i3c-master-v1

 arch/arm64/boot/dts/freescale/imx93.dtsi | 26 ++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi
index 6f85a05ee7e1a..c6ad9ba9eb9af 100644
--- a/arch/arm64/boot/dts/freescale/imx93.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx93.dtsi
@@ -242,6 +242,19 @@ tpm2: pwm at 44320000 {
 				status = "disabled";
 			};
 
+			i3c1: i3c-master at 44330000 {
+				compatible = "silvaco,i3c-master-v1";
+				reg = <0x44330000 0x10000>;
+				interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <3>;
+				#size-cells = <0>;
+				clocks = <&clk IMX93_CLK_BUS_AON>,
+					 <&clk IMX93_CLK_I3C1_GATE>,
+					 <&clk IMX93_CLK_I3C1_SLOW>;
+				clock-names = "pclk", "fast_clk", "slow_clk";
+				status = "disabled";
+			};
+
 			lpi2c1: i2c at 44340000 {
 				compatible = "fsl,imx93-lpi2c", "fsl,imx7ulp-lpi2c";
 				reg = <0x44340000 0x10000>;
@@ -496,6 +509,19 @@ tpm6: pwm at 42510000 {
 				status = "disabled";
 			};
 
+			i3c2: i3c-master at 42520000 {
+				compatible = "silvaco,i3c-master-v1";
+				reg = <0x42520000 0x10000>;
+				interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+				#address-cells = <3>;
+				#size-cells = <0>;
+				clocks = <&clk IMX93_CLK_BUS_WAKEUP>,
+					 <&clk IMX93_CLK_I3C2_GATE>,
+					 <&clk IMX93_CLK_I3C2_SLOW>;
+				clock-names = "pclk", "fast_clk", "slow_clk";
+				status = "disabled";
+			};
+
 			lpi2c3: i2c at 42530000 {
 				compatible = "fsl,imx93-lpi2c", "fsl,imx7ulp-lpi2c";
 				reg = <0x42530000 0x10000>;
-- 
2.34.1



From robh at kernel.org  Tue Oct 17 13:14:04 2023
From: robh at kernel.org (Rob Herring)
Date: Tue, 17 Oct 2023 15:14:04 -0500
Subject: [PATCH 2/2] arm64: dts: freescale: imx93: add i3c1 and i3c2
In-Reply-To: <20231016152450.2850498-2-Frank.Li@nxp.com>
References: <20231016152450.2850498-1-Frank.Li@nxp.com>
 <20231016152450.2850498-2-Frank.Li@nxp.com>
Message-ID: <20231017201404.GA2570433-robh@kernel.org>

On Mon, Oct 16, 2023 at 11:24:50AM -0400, Frank Li wrote:
> Add I3C1 and I3C2.
> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  arch/arm64/boot/dts/freescale/imx93.dtsi | 26 ++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi
> index 6f85a05ee7e1..4d9ed0b32853 100644
> --- a/arch/arm64/boot/dts/freescale/imx93.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx93.dtsi
> @@ -242,6 +242,19 @@ tpm2: pwm at 44320000 {
>  				status = "disabled";
>  			};
>  
> +			i3c1: i3c-master at 44330000 {
> +				compatible = "silvaco,i3c-master";

The real problem here is not whether we have "v1" or not, but you need 
an SoC specific compatible. Unless there's a public spec where we can 
know exactly how many resets, clocks, interrupts, etc.

Rob


From Frank.li at nxp.com  Tue Oct 17 13:55:41 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 16:55:41 -0400
Subject: [PATCH 3/6] i3c: master: svc: fix ibi may not return mandatory
 data byte
In-Reply-To: <20231017162707.187bebcf@xps-13>
References: <20231016153232.2851095-1-Frank.Li@nxp.com>
 <20231016153232.2851095-4-Frank.Li@nxp.com>
 <20231017162707.187bebcf@xps-13>
Message-ID: <ZS70zTwcGOjxB6MS@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 04:27:07PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:32:29 -0400:
> 
> > MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
> > creates an issue when the I3C clock is slow, and the CPU is running fast
> > enough that MSTATUS[RXPEND] may not be updated when the code reach checking
> > point. As a result, mandatory data are being missed.
> 
> 					can be missed.
> 
> > Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data already
> > in FIFO.
> 
> is already in the FIFO
> 
> > 
> > Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> > Cc: stable at vger.kernel.org
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> >  drivers/i3c/master/svc-i3c-master.c | 8 ++++++++
> >  1 file changed, 8 insertions(+)
> > 
> > diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> > index 0f57a5f75e39..c252446b2bc5 100644
> > --- a/drivers/i3c/master/svc-i3c-master.c
> > +++ b/drivers/i3c/master/svc-i3c-master.c
> > @@ -331,6 +331,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
> >  	struct i3c_ibi_slot *slot;
> >  	unsigned int count;
> >  	u32 mdatactrl;
> > +	int ret, val;
> >  	u8 *buf;
> >  
> >  	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
> > @@ -340,6 +341,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
> >  	slot->len = 0;
> >  	buf = slot->data;
> >  
> > +	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
> > +						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
> 
> Are you sure !MSTATUS_COMPLETE(val) is never a valid condition?
> Especially with non-mandatory bytes?
> 
> Also, are you sure of the indentation here?

If no-mandatory data, it is equal RDTERAM is 0. It takes some times to
change my slave code to create such test case.

Frank Li

> 
> > +	if (ret) {
> > +		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
> > +		return ret;
> > +	}
> > +
> >  	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
> >  	       slot->len < SVC_I3C_FIFO_SIZE) {
> >  		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
> 
> 
> Thanks,
> Miqu?l


From Frank.li at nxp.com  Tue Oct 17 13:43:45 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 17 Oct 2023 16:43:45 -0400
Subject: [PATCH 3/5] i3c: svc: rename read_len as actual_len
In-Reply-To: <20231017160729.58984467@xps-13>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
 <20231016154632.2851957-4-Frank.Li@nxp.com>
 <20231017160729.58984467@xps-13>
Message-ID: <ZS7yATy4RmLVFohO@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 04:07:29PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:46:30 -0400:
> 
> > I3C transfer, target can early terminate transfer.
> > I2C transfer, target can NACK write transfer.
> > 
> > 'actual_len' is better name than 'read_len'.
> 
> I don't find read_len confusing because it may mean "how much bytes
> were actually read" rather than "how much we need to read". But if
> actual_len sounds better, I'm fine.

Another reason: According to i3c spec. 5.2.2.3.6 line 6090.
Target ends or continues the write transfer.

in HDR mode, target can end write transfer. So use 'actual_len'

Do you think I need add it to commit message

Frank

> 
> Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>
> 
> > 
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> 
> Thanks,
> Miqu?l


From alexandre.belloni at bootlin.com  Tue Oct 17 15:22:30 2023
From: alexandre.belloni at bootlin.com (Alexandre Belloni)
Date: Wed, 18 Oct 2023 00:22:30 +0200
Subject: [PATCH 1/5] i3c: master: svc: enable hotjoin default
In-Reply-To: <ZS6fbVtbuBH32aRU@lizhi-Precision-Tower-5810>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
 <20231016154632.2851957-2-Frank.Li@nxp.com>
 <20231017160457.4f0c2490@xps-13>
 <ZS6fbVtbuBH32aRU@lizhi-Precision-Tower-5810>
Message-ID: <20231017222230636965c5@mail.local>

Hello,

On 17/10/2023 10:51:25-0400, Frank Li wrote:
> On Tue, Oct 17, 2023 at 04:04:57PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:46:28 -0400:
> > 
> > > Hotjoin require clock running and enable SLVSTART irq.
> > > Add module parameter 'hotjoin' to disable hotjoin and enable runtime_pm to
> > > save power.
> > 
> > I am really not a big fan of the use of modules parameters. Maybe it
> > makes sense here. Alex, a better idea?
> 
> Maybe we can create sys entry to enable/disable hotjoin. I think i3c
> should default support hotjoin, but it exist user case that needn't hj and
> want more aggressive power saving.
> 
> If create /sys/ entry, it need change driver/i3c/master.c.
> 

If this can be changed dynamically, I guess ideally, we should be able
to set it independently per controller so it could be disabled on a bus
but kept enabled on others.


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


From Frank.Li at nxp.com  Wed Oct 18 08:48:03 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:48:03 -0400
Subject: [PATCH v2 0/6] i3c: master: svc: collection of bugs fixes
Message-ID: <20231018154809.3304558-1-Frank.Li@nxp.com>

Each patch is indepedents. See commit message for detail.

Change from v1 to v2.
See each patch notes

Frank Li (6):
  master reduce clock
  i3c: master: svc: fix race condition in ibi work thread
  i3c: master: svc: fix wrong data return when IBI happen during start
    frame
  i3c: master: svc: fix ibi may not return mandatory data byte
  i3c: master: svc: fix check wrong status register in irq handler
  i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen

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

-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:48:04 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:48:04 -0400
Subject: [PATCH v2 1/6] master reduce clock
In-Reply-To: <20231018154809.3304558-1-Frank.Li@nxp.com>
References: <20231018154809.3304558-1-Frank.Li@nxp.com>
Message-ID: <20231018154809.3304558-2-Frank.Li@nxp.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index e307ba2b965e6..d29de5fe533e6 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -511,7 +511,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 	 * Using I3C Push-Pull mode, target is 12.5MHz/80ns period.
 	 * Simplest configuration is using a 50% duty-cycle of 40ns.
 	 */
-	ppbaud = DIV_ROUND_UP(40, fclk_period_ns) - 1;
+	ppbaud = DIV_ROUND_UP(500, fclk_period_ns) - 1;
 	pplow = 0;
 
 	/*
@@ -521,7 +521,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 	 */
 	odhpp = 1;
 	high_period_ns = (ppbaud + 1) * fclk_period_ns;
-	odbaud = DIV_ROUND_UP(240 - high_period_ns, high_period_ns) - 1;
+	odbaud = DIV_ROUND_UP(540 - high_period_ns, high_period_ns) - 1;
 	od_low_period_ns = (odbaud + 1) * high_period_ns;
 
 	switch (bus->mode) {
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:48:05 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:48:05 -0400
Subject: [PATCH v2 2/6] i3c: master: svc: fix race condition in ibi work thread
In-Reply-To: <20231018154809.3304558-1-Frank.Li@nxp.com>
References: <20231018154809.3304558-1-Frank.Li@nxp.com>
Message-ID: <20231018154809.3304558-3-Frank.Li@nxp.com>

The ibi work thread operates asynchronously with other transfers, such as
svc_i3c_master_priv_xfers(). Introduce mutex protection to ensure the
completion of the entire i3c/i2c transaction.

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

Notes:
    Change from v1 to v2
    - update commit message
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index d29de5fe533e6..8cd708c965085 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -174,6 +174,7 @@ struct svc_i3c_regs_save {
  * @ibi.slots: Available IBI slots
  * @ibi.tbq_slot: To be queued IBI slot
  * @ibi.lock: IBI lock
+ * @lock: Transfer lock, protect between IBI work thread and callbacks from master
  */
 struct svc_i3c_master {
 	struct i3c_master_controller base;
@@ -202,6 +203,7 @@ struct svc_i3c_master {
 		/* Prevent races within IBI handlers */
 		spinlock_t lock;
 	} ibi;
+	struct mutex lock;
 };
 
 /**
@@ -383,6 +385,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 	u32 status, val;
 	int ret;
 
+	mutex_lock(&master->lock);
 	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
 	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
 	       SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -459,6 +462,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 
 reenable_ibis:
 	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+	mutex_unlock(&master->lock);
 }
 
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
@@ -1203,9 +1207,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = 0;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	kfree(buf);
@@ -1249,9 +1255,11 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = read_len;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	if (cmd->read_len != xfer_len)
 		ccc->dests[0].payload.len = cmd->read_len;
@@ -1308,9 +1316,11 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->continued = (i + 1) < nxfers;
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1346,9 +1356,11 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->continued = (i + 1 < nxfers);
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1539,6 +1551,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
 	INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
 	INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
+	mutex_init(&master->lock);
+
 	ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
 			       IRQF_NO_SUSPEND, "svc-i3c-irq", master);
 	if (ret)
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:48:06 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:48:06 -0400
Subject: [PATCH v2 3/6] i3c: master: svc: fix wrong data return when IBI happen during start frame
In-Reply-To: <20231018154809.3304558-1-Frank.Li@nxp.com>
References: <20231018154809.3304558-1-Frank.Li@nxp.com>
Message-ID: <20231018154809.3304558-4-Frank.Li@nxp.com>

     ???????     ????  ????  ????  ????  ????  ????  ????  ????  ??????
SCL: ?     ???????  ????  ????  ????  ????  ????  ????  ????  ????
     ????                       ???????     ???????     ?????????????
SDA:    ?????????????????????????     ???????     ???????           ??????
     xxx?    ??                                        ??    ??    ??    ?
   : xxx?IBI ??               Addr(0x0a)               ?? RW ??NACK?? S  ?

If an In-Band Interrupt (IBI) occurs and IBI work thread is not immediately
scheduled, When svc_i3c_master_priv_xfers() initiates the I3C transfer and
attempts to send address 0x7e, the target interprets it as an
IBI handler and returns the target address 0x0a.

However, svc_i3c_master_priv_xfers() does not handle this case and proceeds
with other transfers, resulting in incorrect data being returned.

Add IBIWON check in svc_i3c_master_xfer(). In case this situation occurs,
return a failure to the driver.

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

Notes:
    Change from v1 to v2
    - update commit message
    - fix typo yeild and falure

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 8cd708c965085..abebef666b2bb 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1010,6 +1010,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	u32 reg;
 	int ret;
 
+	/* clean SVC_I3C_MINT_IBIWON w1c bits */
+	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
 	writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
 	       xfer_type |
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
@@ -1028,6 +1031,23 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 	}
 
+	/*
+	 * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
+	 * with I3C Target Address.
+	 *
+	 * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
+	 * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
+	 * Role Request (i.e., Secondary Controller requests to become the Active Controller), or
+	 * a Hot-Join Request has been made.
+	 *
+	 * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure
+	 * and yield the above events handler.
+	 */
+	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
+		ret = -ENXIO;
+		goto emit_stop;
+	}
+
 	if (rnw)
 		ret = svc_i3c_master_read(master, in, xfer_len);
 	else
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:48:07 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:48:07 -0400
Subject: [PATCH v2 4/6] i3c: master: svc: fix ibi may not return mandatory data byte
In-Reply-To: <20231018154809.3304558-1-Frank.Li@nxp.com>
References: <20231018154809.3304558-1-Frank.Li@nxp.com>
Message-ID: <20231018154809.3304558-5-Frank.Li@nxp.com>

MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
creates an issue when the I3C clock is slow, and the CPU is running fast
enough that MSTATUS[RXPEND] may not be updated when the code reach checking
point. As a result, mandatory data are being missed.

Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data is
already in FIFO.

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

Notes:
    Change from v1 to v2
    - update commit message
      it also works without mandatory bytes

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index abebef666b2bb..dd06b7c9333f1 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -332,6 +332,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	struct i3c_ibi_slot *slot;
 	unsigned int count;
 	u32 mdatactrl;
+	int ret, val;
 	u8 *buf;
 
 	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
@@ -341,6 +342,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	slot->len = 0;
 	buf = slot->data;
 
+	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
+						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
+	if (ret) {
+		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
+		return ret;
+	}
+
 	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
 	       slot->len < SVC_I3C_FIFO_SIZE) {
 		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:48:08 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:48:08 -0400
Subject: [PATCH v2 5/6] i3c: master: svc: fix check wrong status register in irq handler
In-Reply-To: <20231018154809.3304558-1-Frank.Li@nxp.com>
References: <20231018154809.3304558-1-Frank.Li@nxp.com>
Message-ID: <20231018154809.3304558-6-Frank.Li@nxp.com>

svc_i3c_master_irq_handler() wrong check register SVC_I3C_MINTMASKED. It
should be SVC_I3C_MSTATUS.

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

Notes:
    Change from v1 to v2
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index dd06b7c9333f1..b113460f059c3 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -476,7 +476,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
 {
 	struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
-	u32 active = readl(master->regs + SVC_I3C_MINTMASKED);
+	u32 active = readl(master->regs + SVC_I3C_MSTATUS);
 
 	if (!SVC_I3C_MSTATUS_SLVSTART(active))
 		return IRQ_NONE;
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:48:09 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:48:09 -0400
Subject: [PATCH v2 6/6] i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
In-Reply-To: <20231018154809.3304558-1-Frank.Li@nxp.com>
References: <20231018154809.3304558-1-Frank.Li@nxp.com>
Message-ID: <20231018154809.3304558-7-Frank.Li@nxp.com>

If IBIWON timeout happen, SDA line will always keep low because miss emit
stop.

Call svc_i3c_master_emit_stop() to let i3c bus come back to idle statue
when IBIWON timeout happen.

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

Notes:
    Change from v1 to v2
    - Update commite message

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index b113460f059c3..1a57fdebaa26d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -404,6 +404,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
 	if (ret) {
 		dev_err(master->dev, "Timeout when polling for IBIWON\n");
+		svc_i3c_master_emit_stop(master);
 		goto reenable_ibis;
 	}
 
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:59:20 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:59:20 -0400
Subject: [PATCH v2 Resent 0/6] i3c: master: svc: collection of bugs fixes
Message-ID: <20231018155926.3305476-1-Frank.Li@nxp.com>

Each patch is indepedents. See commit message for detail.

Change from v1 to v2.
See each patch notes

Frank Li (6):
  i3c: master: svc: fix race condition in ibi work thread
  i3c: master: svc: fix wrong data return when IBI happen during start
    frame
  i3c: master: svc: fix ibi may not return mandatory data byte
  i3c: master: svc: fix check wrong status register in irq handler
  i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
  i3c: master: svc: fix random hot join failure since timeout error

 drivers/i3c/master/svc-i3c-master.c | 51 ++++++++++++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:59:21 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:59:21 -0400
Subject: [PATCH v2 Resent 1/6] i3c: master: svc: fix race condition in ibi work thread
In-Reply-To: <20231018155926.3305476-1-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
Message-ID: <20231018155926.3305476-2-Frank.Li@nxp.com>

The ibi work thread operates asynchronously with other transfers, such as
svc_i3c_master_priv_xfers(). Introduce mutex protection to ensure the
completion of the entire i3c/i2c transaction.

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

Notes:
    Change from v1 to v2
    - update commit message
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index d29de5fe533e6..8cd708c965085 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -174,6 +174,7 @@ struct svc_i3c_regs_save {
  * @ibi.slots: Available IBI slots
  * @ibi.tbq_slot: To be queued IBI slot
  * @ibi.lock: IBI lock
+ * @lock: Transfer lock, protect between IBI work thread and callbacks from master
  */
 struct svc_i3c_master {
 	struct i3c_master_controller base;
@@ -202,6 +203,7 @@ struct svc_i3c_master {
 		/* Prevent races within IBI handlers */
 		spinlock_t lock;
 	} ibi;
+	struct mutex lock;
 };
 
 /**
@@ -383,6 +385,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 	u32 status, val;
 	int ret;
 
+	mutex_lock(&master->lock);
 	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
 	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
 	       SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -459,6 +462,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 
 reenable_ibis:
 	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+	mutex_unlock(&master->lock);
 }
 
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
@@ -1203,9 +1207,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = 0;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	kfree(buf);
@@ -1249,9 +1255,11 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = read_len;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	if (cmd->read_len != xfer_len)
 		ccc->dests[0].payload.len = cmd->read_len;
@@ -1308,9 +1316,11 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->continued = (i + 1) < nxfers;
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1346,9 +1356,11 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->continued = (i + 1 < nxfers);
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1539,6 +1551,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
 	INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
 	INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
+	mutex_init(&master->lock);
+
 	ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
 			       IRQF_NO_SUSPEND, "svc-i3c-irq", master);
 	if (ret)
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:59:22 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:59:22 -0400
Subject: [PATCH v2 Resent 2/6] i3c: master: svc: fix wrong data return when IBI happen during start frame
In-Reply-To: <20231018155926.3305476-1-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
Message-ID: <20231018155926.3305476-3-Frank.Li@nxp.com>

     ???????     ????  ????  ????  ????  ????  ????  ????  ????  ??????
SCL: ?     ???????  ????  ????  ????  ????  ????  ????  ????  ????
     ????                       ???????     ???????     ?????????????
SDA:    ?????????????????????????     ???????     ???????           ??????
     xxx?    ??                                        ??    ??    ??    ?
   : xxx?IBI ??               Addr(0x0a)               ?? RW ??NACK?? S  ?

If an In-Band Interrupt (IBI) occurs and IBI work thread is not immediately
scheduled, When svc_i3c_master_priv_xfers() initiates the I3C transfer and
attempts to send address 0x7e, the target interprets it as an
IBI handler and returns the target address 0x0a.

However, svc_i3c_master_priv_xfers() does not handle this case and proceeds
with other transfers, resulting in incorrect data being returned.

Add IBIWON check in svc_i3c_master_xfer(). In case this situation occurs,
return a failure to the driver.

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

Notes:
    Change from v1 to v2
    - update commit message
    - fix typo yeild and falure

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 8cd708c965085..abebef666b2bb 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1010,6 +1010,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	u32 reg;
 	int ret;
 
+	/* clean SVC_I3C_MINT_IBIWON w1c bits */
+	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
 	writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
 	       xfer_type |
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
@@ -1028,6 +1031,23 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 	}
 
+	/*
+	 * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
+	 * with I3C Target Address.
+	 *
+	 * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
+	 * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
+	 * Role Request (i.e., Secondary Controller requests to become the Active Controller), or
+	 * a Hot-Join Request has been made.
+	 *
+	 * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure
+	 * and yield the above events handler.
+	 */
+	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
+		ret = -ENXIO;
+		goto emit_stop;
+	}
+
 	if (rnw)
 		ret = svc_i3c_master_read(master, in, xfer_len);
 	else
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:59:23 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:59:23 -0400
Subject: [PATCH v2 Resent 3/6] i3c: master: svc: fix ibi may not return mandatory data byte
In-Reply-To: <20231018155926.3305476-1-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
Message-ID: <20231018155926.3305476-4-Frank.Li@nxp.com>

MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
creates an issue when the I3C clock is slow, and the CPU is running fast
enough that MSTATUS[RXPEND] may not be updated when the code reach checking
point. As a result, mandatory data are being missed.

Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data is
already in FIFO.

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

Notes:
    Change from v1 to v2
    - update commit message
      it also works without mandatory bytes

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index abebef666b2bb..dd06b7c9333f1 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -332,6 +332,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	struct i3c_ibi_slot *slot;
 	unsigned int count;
 	u32 mdatactrl;
+	int ret, val;
 	u8 *buf;
 
 	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
@@ -341,6 +342,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	slot->len = 0;
 	buf = slot->data;
 
+	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
+						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
+	if (ret) {
+		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
+		return ret;
+	}
+
 	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
 	       slot->len < SVC_I3C_FIFO_SIZE) {
 		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:59:24 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:59:24 -0400
Subject: [PATCH v2 Resent 4/6] i3c: master: svc: fix check wrong status register in irq handler
In-Reply-To: <20231018155926.3305476-1-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
Message-ID: <20231018155926.3305476-5-Frank.Li@nxp.com>

svc_i3c_master_irq_handler() wrong check register SVC_I3C_MINTMASKED. It
should be SVC_I3C_MSTATUS.

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

Notes:
    Change from v1 to v2
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index dd06b7c9333f1..b113460f059c3 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -476,7 +476,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
 {
 	struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
-	u32 active = readl(master->regs + SVC_I3C_MINTMASKED);
+	u32 active = readl(master->regs + SVC_I3C_MSTATUS);
 
 	if (!SVC_I3C_MSTATUS_SLVSTART(active))
 		return IRQ_NONE;
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:59:25 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:59:25 -0400
Subject: [PATCH v2 Resent 5/6] i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
In-Reply-To: <20231018155926.3305476-1-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
Message-ID: <20231018155926.3305476-6-Frank.Li@nxp.com>

If IBIWON timeout happen, SDA line will always keep low because miss emit
stop.

Call svc_i3c_master_emit_stop() to let i3c bus come back to idle statue
when IBIWON timeout happen.

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

Notes:
    Change from v1 to v2
    - Update commite message

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index b113460f059c3..1a57fdebaa26d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -404,6 +404,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
 	if (ret) {
 		dev_err(master->dev, "Timeout when polling for IBIWON\n");
+		svc_i3c_master_emit_stop(master);
 		goto reenable_ibis;
 	}
 
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 08:59:26 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 11:59:26 -0400
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join failure since timeout error
In-Reply-To: <20231018155926.3305476-1-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
Message-ID: <20231018155926.3305476-7-Frank.Li@nxp.com>

master side report:
  silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000

BIT 20: TIMEOUT error
  The module has stalled too long in a frame. This happens when:
  - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
middle of a message,
  - No STOP was issued and between messages,
  - IBI manual is used and no decision was made.
  The maximum stall period is 10 KHz or 100 ?s.

This is a just warning. System irq thread schedule latency is possible
bigger than 100us. Just omit this waring.

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

Notes:
    Change from v1 to v2
    -none

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 1a57fdebaa26d..fedb31e0076c4 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -93,6 +93,7 @@
 #define SVC_I3C_MINTMASKED   0x098
 #define SVC_I3C_MERRWARN     0x09C
 #define   SVC_I3C_MERRWARN_NACK BIT(2)
+#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
 #define SVC_I3C_MDMACTRL     0x0A0
 #define SVC_I3C_MDATACTRL    0x0AC
 #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
@@ -226,6 +227,11 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
 	if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
 		merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
 		writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
+
+		/* ignore timeout error */
+		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
+			return false;
+
 		dev_err(master->dev,
 			"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
 			mstatus, merrwarn);
-- 
2.34.1



From Frank.li at nxp.com  Wed Oct 18 09:03:33 2023
From: Frank.li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 12:03:33 -0400
Subject: [PATCH v2 0/6] i3c: master: svc: collection of bugs fixes
In-Reply-To: <20231018154809.3304558-1-Frank.Li@nxp.com>
References: <20231018154809.3304558-1-Frank.Li@nxp.com>
Message-ID: <ZTAB1f+r4j8vjxQc@lizhi-Precision-Tower-5810>

On Wed, Oct 18, 2023 at 11:48:03AM -0400, Frank Li wrote:
> Each patch is indepedents. See commit message for detail.

Sorry, mistake sent patch "master reduce clock"
Please discard this patches.

Please check "v2-Resent"!

> 
> Change from v1 to v2.
> See each patch notes
> 
> Frank Li (6):
>   master reduce clock
>   i3c: master: svc: fix race condition in ibi work thread
>   i3c: master: svc: fix wrong data return when IBI happen during start
>     frame
>   i3c: master: svc: fix ibi may not return mandatory data byte
>   i3c: master: svc: fix check wrong status register in irq handler
>   i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
> 
>  drivers/i3c/master/svc-i3c-master.c | 49 +++++++++++++++++++++++++++--
>  1 file changed, 46 insertions(+), 3 deletions(-)
> 
> -- 
> 2.34.1
> 


From Frank.li at nxp.com  Wed Oct 18 13:18:18 2023
From: Frank.li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:18:18 -0400
Subject: [PATCH 4/5] i3c: master: svc return actual transfer data len
In-Reply-To: <af35a533-2126-4a61-a6cd-31ed1e615f2b@linux.intel.com>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
 <20231016154632.2851957-5-Frank.Li@nxp.com>
 <af35a533-2126-4a61-a6cd-31ed1e615f2b@linux.intel.com>
Message-ID: <ZTA9ir2zlFs7tC5D@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 11:33:34AM +0300, Jarkko Nikula wrote:
> Hi
> 
> On 10/16/23 18:46, Frank Li wrote:
> > I3C allow devices early terminate data transfer. So set "actual" to
> > indicate how much data get by i3c_priv_xfer.
> > 
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> >   drivers/i3c/master/svc-i3c-master.c | 7 +++++++
> >   1 file changed, 7 insertions(+)
> > 
> > diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> > index 3570b709cf60..444825aafa6f 100644
> > --- a/drivers/i3c/master/svc-i3c-master.c
> > +++ b/drivers/i3c/master/svc-i3c-master.c
> > @@ -138,6 +138,7 @@ struct svc_i3c_cmd {
> >   	const void *out;
> >   	unsigned int len;
> >   	unsigned int actual_len;
> > +	struct i3c_priv_xfer *xfer;
> >   	bool continued;
> >   };
> I'm thinking would it make sense to combine this and previous patch by
> removing the read_len/actual_len variable from this structure and use the
> added one (by the patch 2/5) from "struct i3c_priv_xfer" directly?

Some I2C transfer and CCC use svc_i3c_cmd, in such case xfer is NULL. Keep
len/actual_len is more simple.

Frank 


From Frank.li at nxp.com  Wed Oct 18 13:26:07 2023
From: Frank.li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:26:07 -0400
Subject: [PATCH 4/5] i3c: master: svc return actual transfer data len
In-Reply-To: <20231017161007.146a5199@xps-13>
References: <20231016154632.2851957-1-Frank.Li@nxp.com>
 <20231016154632.2851957-5-Frank.Li@nxp.com>
 <20231017161007.146a5199@xps-13>
Message-ID: <ZTA/X333KBXDN49p@lizhi-Precision-Tower-5810>

On Tue, Oct 17, 2023 at 04:10:07PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.Li at nxp.com wrote on Mon, 16 Oct 2023 11:46:31 -0400:
> 
> > I3C allow devices early terminate data transfer. So set "actual" to
> > indicate how much data get by i3c_priv_xfer.
> > 
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> >  drivers/i3c/master/svc-i3c-master.c | 7 +++++++
> >  1 file changed, 7 insertions(+)
> > 
> > diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> > index 3570b709cf60..444825aafa6f 100644
> > --- a/drivers/i3c/master/svc-i3c-master.c
> > +++ b/drivers/i3c/master/svc-i3c-master.c
> > @@ -138,6 +138,7 @@ struct svc_i3c_cmd {
> >  	const void *out;
> >  	unsigned int len;
> >  	unsigned int actual_len;
> > +	struct i3c_priv_xfer *xfer;
> >  	bool continued;
> >  };
> >  
> > @@ -1045,6 +1046,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
> >  
> >  	if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
> >  		ret = -ENXIO;
> > +		*actual_len = 0;
> >  		goto emit_stop;
> >  	}
> >  
> > @@ -1062,6 +1064,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
> >  	 */
> >  	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
> >  		ret = -ENXIO;
> > +		*actual_len = 0;
> >  		goto emit_stop;
> >  	}
> >  
> > @@ -1157,6 +1160,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
> >  					  cmd->addr, cmd->in, cmd->out,
> >  					  cmd->len, &cmd->actual_len,
> >  					  cmd->continued);
> > +		if (cmd->xfer)
> > +			cmd->xfer->actual = cmd->actual_len;
> 
> Just to be sure, wouldn't it be more natural to always fill cmd->xfer
> rather than checking it here?

cmd->xfer is NULL for i2c and ccc transfer. So need check it. 
I will add comments here

Frank
> 
> > +
> >  		if (ret)
> >  			break;
> >  	}
> > @@ -1344,6 +1350,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
> >  	for (i = 0; i < nxfers; i++) {
> >  		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
> >  
> > +		cmd->xfer = xfers + i;
> 
> Please follow the same pattern as below: = &xfers[i]
> 
> >  		cmd->addr = master->addrs[data->index];
> >  		cmd->rnw = xfers[i].rnw;
> >  		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
> 
> 
> Thanks,
> Miqu?l


From Frank.Li at nxp.com  Wed Oct 18 13:59:23 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:59:23 -0400
Subject: [PATCH v2 0/6] i3c: master: some improvment for i3c master
Message-ID: <20231018205929.3435110-1-Frank.Li@nxp.com>

These are dependent on bugs fixed serial: https://lore.kernel.org/imx/20231016153232.2851095-1-Frank.Li at nxp.com/T/#t
There are three major improvement

1. Add actual size in i3c_transfer because i3c allow target early termiate
transfer.
2. Add API for i3c_dev_gettstatus_format1 for i3c comand GET_STATUS.
3. svc master enable hotjoin default

Change from v1 to v2.
- Hotjoin control by sys entry, default enable hotjoin, which standard i3c
feature, user can disable by echo 0 > /sys/bus/i3c/i3c-0/hotjoin
- use actual_len, add document
- using &xfers[i]
- still keep check cmd->xfer because i2c and ccc transfer, xfer is NULL.
- fixed issue found by checkpatch --strict

Frank Li (6):
  i3c: master: add enable(disable) hot join in sys entry
  i3c: master: svc: add hot join support
  i3c: add actual_len in i3c_priv_xfer
  i3c: svc: rename read_len as actual_len
  i3c: master: svc return actual transfer data len
  i3c: add API i3c_dev_gettstatus_format1() to get target device status

 drivers/i3c/device.c                |  24 ++++++
 drivers/i3c/internals.h             |   1 +
 drivers/i3c/master.c                | 110 ++++++++++++++++++++++++++++
 drivers/i3c/master/svc-i3c-master.c |  91 ++++++++++++++++++-----
 include/linux/i3c/device.h          |   3 +
 include/linux/i3c/master.h          |   5 ++
 6 files changed, 217 insertions(+), 17 deletions(-)

-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 13:59:24 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:59:24 -0400
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys entry
In-Reply-To: <20231018205929.3435110-1-Frank.Li@nxp.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
Message-ID: <20231018205929.3435110-2-Frank.Li@nxp.com>

Add hotjoin entry in sys file system allow user enable/disable hotjoin
feature.

Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
Add api i3c_master_enable(disable)_hotjoin();

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master.c       | 84 ++++++++++++++++++++++++++++++++++++++
 include/linux/i3c/master.h |  5 +++
 2 files changed, 89 insertions(+)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 08aeb69a78003..ed5e27cd20811 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -526,6 +526,89 @@ static ssize_t i2c_scl_frequency_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(i2c_scl_frequency);
 
+static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
+{
+	int ret;
+
+	if (!master ||
+	    !master->ops ||
+	    !master->ops->enable_hotjoin ||
+	    !master->ops->disable_hotjoin
+	   )
+		return -EINVAL;
+
+	i3c_bus_normaluse_lock(&master->bus);
+
+	if (enable)
+		ret = master->ops->enable_hotjoin(master);
+	else
+		ret = master->ops->disable_hotjoin(master);
+
+	master->hotjoin = enable;
+
+	i3c_bus_normaluse_unlock(&master->bus);
+
+	return ret;
+}
+
+static ssize_t hotjoin_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
+	int ret;
+	long res;
+
+	if (!i3cbus->cur_master)
+		return -EINVAL;
+
+	if (kstrtol(buf, 10, &res))
+		return -EINVAL;
+
+	ret = i3c_set_hotjoin(i3cbus->cur_master->common.master, !!res);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/*
+ * i3c_master_enable_hotjoin - Enable hotjoin
+ * @master: I3C master object
+ *
+ * Return: a 0 in case of success, an negative error code otherwise.
+ */
+int i3c_master_enable_hotjoin(struct i3c_master_controller *master)
+{
+	return i3c_set_hotjoin(master, true);
+}
+EXPORT_SYMBOL_GPL(i3c_master_enable_hotjoin);
+
+/*
+ * i3c_master_disable_hotjoin - Disable hotjoin
+ * @master: I3C master object
+ *
+ * Return: a 0 in case of success, an negative error code otherwise.
+ */
+int i3c_master_disable_hotjoin(struct i3c_master_controller *master)
+{
+	return i3c_set_hotjoin(master, false);
+}
+EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin);
+
+static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf)
+{
+	struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
+	ssize_t ret;
+
+	i3c_bus_normaluse_lock(i3cbus);
+	ret = sysfs_emit(buf, "%d\n", i3cbus->cur_master->common.master->hotjoin);
+	i3c_bus_normaluse_unlock(i3cbus);
+
+	return ret;
+}
+
+static DEVICE_ATTR_RW(hotjoin);
+
 static struct attribute *i3c_masterdev_attrs[] = {
 	&dev_attr_mode.attr,
 	&dev_attr_current_master.attr,
@@ -536,6 +619,7 @@ static struct attribute *i3c_masterdev_attrs[] = {
 	&dev_attr_pid.attr,
 	&dev_attr_dynamic_address.attr,
 	&dev_attr_hdrcap.attr,
+	&dev_attr_hotjoin.attr,
 	NULL,
 };
 ATTRIBUTE_GROUPS(i3c_masterdev);
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 0b52da4f23467..65b8965968af2 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -452,6 +452,8 @@ struct i3c_master_controller_ops {
 	int (*disable_ibi)(struct i3c_dev_desc *dev);
 	void (*recycle_ibi_slot)(struct i3c_dev_desc *dev,
 				 struct i3c_ibi_slot *slot);
+	int (*enable_hotjoin)(struct i3c_master_controller *master);
+	int (*disable_hotjoin)(struct i3c_master_controller *master);
 };
 
 /**
@@ -487,6 +489,7 @@ struct i3c_master_controller {
 	const struct i3c_master_controller_ops *ops;
 	unsigned int secondary : 1;
 	unsigned int init_done : 1;
+	unsigned int hotjoin: 1;
 	struct {
 		struct list_head i3c;
 		struct list_head i2c;
@@ -543,6 +546,8 @@ int i3c_master_register(struct i3c_master_controller *master,
 			const struct i3c_master_controller_ops *ops,
 			bool secondary);
 void i3c_master_unregister(struct i3c_master_controller *master);
+int i3c_master_enable_hotjoin(struct i3c_master_controller *master);
+int i3c_master_disable_hotjoin(struct i3c_master_controller *master);
 
 /**
  * i3c_dev_get_master_data() - get master private data attached to an I3C
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 13:59:26 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:59:26 -0400
Subject: [PATCH v2 3/6] i3c: add actual_len in i3c_priv_xfer
In-Reply-To: <20231018205929.3435110-1-Frank.Li@nxp.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
Message-ID: <20231018205929.3435110-4-Frank.Li@nxp.com>

In MIPI I3C Specification:

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

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

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

diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 90fa83464f003..ef6217da8253b 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -54,6 +54,7 @@ enum i3c_hdr_mode {
  * struct i3c_priv_xfer - I3C SDR private transfer
  * @rnw: encodes the transfer direction. true for a read, false for a write
  * @len: transfer length in bytes of the transfer
+ * @actual_len: actual length in bytes are transferred by the controller
  * @data: input/output buffer
  * @data.in: input buffer. Must point to a DMA-able buffer
  * @data.out: output buffer. Must point to a DMA-able buffer
@@ -62,6 +63,7 @@ enum i3c_hdr_mode {
 struct i3c_priv_xfer {
 	u8 rnw;
 	u16 len;
+	u16 actual_len;
 	union {
 		void *in;
 		const void *out;
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 13:59:27 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:59:27 -0400
Subject: [PATCH v2 4/6] i3c: svc: rename read_len as actual_len
In-Reply-To: <20231018205929.3435110-1-Frank.Li@nxp.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
Message-ID: <20231018205929.3435110-5-Frank.Li@nxp.com>

I3C transfer (SDR), target can early terminate read transfer.
I3C transfer (HDR), target can end write transfer.
I2C transfer, target can NACK write transfer.

'actual_len' is better name than 'read_len'.

Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/master/svc-i3c-master.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index d8467607602af..6cd4cab26141c 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -137,7 +137,7 @@ struct svc_i3c_cmd {
 	u8 *in;
 	const void *out;
 	unsigned int len;
-	unsigned int read_len;
+	unsigned int actual_len;
 	bool continued;
 };
 
@@ -1029,7 +1029,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
 static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 			       bool rnw, unsigned int xfer_type, u8 addr,
 			       u8 *in, const u8 *out, unsigned int xfer_len,
-			       unsigned int *read_len, bool continued)
+			       unsigned int *actual_len, bool continued)
 {
 	u32 reg;
 	int ret;
@@ -1042,7 +1042,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
 	       SVC_I3C_MCTRL_DIR(rnw) |
 	       SVC_I3C_MCTRL_ADDR(addr) |
-	       SVC_I3C_MCTRL_RDTERM(*read_len),
+	       SVC_I3C_MCTRL_RDTERM(*actual_len),
 	       master->regs + SVC_I3C_MCTRL);
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
@@ -1080,7 +1080,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 
 	if (rnw)
-		*read_len = ret;
+		*actual_len = ret;
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
 				 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
@@ -1162,7 +1162,7 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 
 		ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
 					  cmd->addr, cmd->in, cmd->out,
-					  cmd->len, &cmd->read_len,
+					  cmd->len, &cmd->actual_len,
 					  cmd->continued);
 		if (ret)
 			break;
@@ -1248,7 +1248,7 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = NULL;
 	cmd->out = buf;
 	cmd->len = xfer_len;
-	cmd->read_len = 0;
+	cmd->actual_len = 0;
 	cmd->continued = false;
 
 	mutex_lock(&master->lock);
@@ -1268,7 +1268,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 					      struct i3c_ccc_cmd *ccc)
 {
 	unsigned int xfer_len = ccc->dests[0].payload.len;
-	unsigned int read_len = ccc->rnw ? xfer_len : 0;
+	unsigned int actual_len = ccc->rnw ? xfer_len : 0;
 	struct svc_i3c_xfer *xfer;
 	struct svc_i3c_cmd *cmd;
 	int ret;
@@ -1286,7 +1286,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = NULL;
 	cmd->out = &ccc->id;
 	cmd->len = 1;
-	cmd->read_len = 0;
+	cmd->actual_len = 0;
 	cmd->continued = true;
 
 	/* Directed message */
@@ -1296,7 +1296,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL;
 	cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data,
 	cmd->len = xfer_len;
-	cmd->read_len = read_len;
+	cmd->actual_len = actual_len;
 	cmd->continued = false;
 
 	mutex_lock(&master->lock);
@@ -1305,8 +1305,8 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 		svc_i3c_master_dequeue_xfer(master, xfer);
 	mutex_unlock(&master->lock);
 
-	if (cmd->read_len != xfer_len)
-		ccc->dests[0].payload.len = cmd->read_len;
+	if (cmd->actual_len != xfer_len)
+		ccc->dests[0].payload.len = cmd->actual_len;
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1356,7 +1356,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
 		cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
 		cmd->len = xfers[i].len;
-		cmd->read_len = xfers[i].rnw ? xfers[i].len : 0;
+		cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0;
 		cmd->continued = (i + 1) < nxfers;
 	}
 
@@ -1396,7 +1396,7 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->in = cmd->rnw ? xfers[i].buf : NULL;
 		cmd->out = cmd->rnw ? NULL : xfers[i].buf;
 		cmd->len = xfers[i].len;
-		cmd->read_len = cmd->rnw ? xfers[i].len : 0;
+		cmd->actual_len = cmd->rnw ? xfers[i].len : 0;
 		cmd->continued = (i + 1 < nxfers);
 	}
 
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 13:59:25 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:59:25 -0400
Subject: [PATCH v2 2/6] i3c: master: svc: add hot join support
In-Reply-To: <20231018205929.3435110-1-Frank.Li@nxp.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
Message-ID: <20231018205929.3435110-3-Frank.Li@nxp.com>

Add hot join support for svc master controller. Enable hot join defaultly.
User can use sys entry to disable hot join.

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index fedb31e0076c4..d8467607602af 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -128,6 +128,9 @@
 /* This parameter depends on the implementation and may be tuned */
 #define SVC_I3C_FIFO_SIZE 16
 
+#define SVC_I3C_EVENT_IBI	BIT(0)
+#define SVC_I3C_EVENT_HOTJOIN	BIT(1)
+
 struct svc_i3c_cmd {
 	u8 addr;
 	bool rnw;
@@ -205,6 +208,7 @@ struct svc_i3c_master {
 		spinlock_t lock;
 	} ibi;
 	struct mutex lock;
+	int enabled_events;
 };
 
 /**
@@ -425,13 +429,16 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 	switch (ibitype) {
 	case SVC_I3C_MSTATUS_IBITYPE_IBI:
 		dev = svc_i3c_master_dev_from_addr(master, ibiaddr);
-		if (!dev)
+		if (!dev || !(master->enabled_events & SVC_I3C_EVENT_IBI))
 			svc_i3c_master_nack_ibi(master);
 		else
 			svc_i3c_master_handle_ibi(master, dev);
 		break;
 	case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
-		svc_i3c_master_ack_ibi(master, false);
+		if (master->enabled_events & SVC_I3C_EVENT_HOTJOIN)
+			svc_i3c_master_ack_ibi(master, false);
+		else
+			svc_i3c_master_nack_ibi(master);
 		break;
 	case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
 		svc_i3c_master_nack_ibi(master);
@@ -468,7 +475,9 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 		svc_i3c_master_emit_stop(master);
 		break;
 	case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
-		queue_work(master->base.wq, &master->hj_work);
+		svc_i3c_master_emit_stop(master);
+		if (master->enabled_events & SVC_I3C_EVENT_HOTJOIN)
+			queue_work(master->base.wq, &master->hj_work);
 		break;
 	case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
 	default:
@@ -1468,6 +1477,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
 		return ret;
 	}
 
+	master->enabled_events |= SVC_I3C_EVENT_IBI;
 	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
 
 	return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
@@ -1479,7 +1489,9 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
 	struct svc_i3c_master *master = to_svc_i3c_master(m);
 	int ret;
 
-	svc_i3c_master_disable_interrupts(master);
+	master->enabled_events &= ~SVC_I3C_EVENT_IBI;
+	if (!master->enabled_events)
+		svc_i3c_master_disable_interrupts(master);
 
 	ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
 
@@ -1489,6 +1501,39 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
 	return ret;
 }
 
+static int svc_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
+{
+	struct svc_i3c_master *master = to_svc_i3c_master(m);
+	int ret;
+
+	ret = pm_runtime_resume_and_get(master->dev);
+	if (ret < 0) {
+		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
+		return ret;
+	}
+
+	master->enabled_events |= SVC_I3C_EVENT_HOTJOIN;
+
+	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+
+	return 0;
+}
+
+static int svc_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
+{
+	struct svc_i3c_master *master = to_svc_i3c_master(m);
+
+	master->enabled_events &= ~SVC_I3C_EVENT_HOTJOIN;
+
+	if (!master->enabled_events)
+		svc_i3c_master_disable_interrupts(master);
+
+	pm_runtime_mark_last_busy(master->dev);
+	pm_runtime_put_autosuspend(master->dev);
+
+	return 0;
+}
+
 static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
 					    struct i3c_ibi_slot *slot)
 {
@@ -1515,6 +1560,8 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
 	.recycle_ibi_slot = svc_i3c_master_recycle_ibi_slot,
 	.enable_ibi = svc_i3c_master_enable_ibi,
 	.disable_ibi = svc_i3c_master_disable_ibi,
+	.enable_hotjoin = svc_i3c_master_enable_hotjoin,
+	.disable_hotjoin = svc_i3c_master_disable_hotjoin,
 };
 
 static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master)
@@ -1627,6 +1674,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 	pm_runtime_mark_last_busy(&pdev->dev);
 	pm_runtime_put_autosuspend(&pdev->dev);
 
+	i3c_master_enable_hotjoin(&master->base);
+
 	return 0;
 
 rpm_disable:
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 13:59:28 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:59:28 -0400
Subject: [PATCH v2 5/6] i3c: master: svc return actual transfer data len
In-Reply-To: <20231018205929.3435110-1-Frank.Li@nxp.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
Message-ID: <20231018205929.3435110-6-Frank.Li@nxp.com>

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

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 6cd4cab26141c..17e953a47b1bb 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -138,6 +138,7 @@ struct svc_i3c_cmd {
 	const void *out;
 	unsigned int len;
 	unsigned int actual_len;
+	struct i3c_priv_xfer *xfer;
 	bool continued;
 };
 
@@ -1052,6 +1053,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 
 	if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
 		ret = -ENXIO;
+		*actual_len = 0;
 		goto emit_stop;
 	}
 
@@ -1069,6 +1071,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	 */
 	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
 		ret = -ENXIO;
+		*actual_len = 0;
 		goto emit_stop;
 	}
 
@@ -1164,6 +1167,10 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 					  cmd->addr, cmd->in, cmd->out,
 					  cmd->len, &cmd->actual_len,
 					  cmd->continued);
+		/* cmd->xfer is NULL if I2C or CCC transfer */
+		if (cmd->xfer)
+			cmd->xfer->actual_len = cmd->actual_len;
+
 		if (ret)
 			break;
 	}
@@ -1351,6 +1358,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 	for (i = 0; i < nxfers; i++) {
 		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
 
+		cmd->xfer = &xfers[i];
 		cmd->addr = master->addrs[data->index];
 		cmd->rnw = xfers[i].rnw;
 		cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 13:59:29 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 16:59:29 -0400
Subject: [PATCH v2 6/6] i3c: add API i3c_dev_gettstatus_format1() to get target device status
In-Reply-To: <20231018205929.3435110-1-Frank.Li@nxp.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
Message-ID: <20231018205929.3435110-7-Frank.Li@nxp.com>

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

Add API to fetch it with format1.

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

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



From Frank.Li at nxp.com  Wed Oct 18 14:11:09 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 17:11:09 -0400
Subject: [PATCH 1/1] tty: i3c: add tty over i3c master support
Message-ID: <20231018211111.3437929-1-Frank.Li@nxp.com>

Add new driver to allow tty over i3c master.

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

Notes:
    This patch depend on
    https://lore.kernel.org/imx/20231018205929.3435110-1-Frank.Li at nxp.com/T/#t

 drivers/tty/Kconfig   |   8 +
 drivers/tty/Makefile  |   1 +
 drivers/tty/i3c_tty.c | 466 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+)
 create mode 100644 drivers/tty/i3c_tty.c

diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 5646dc6242cd9..6d91fe6a211a1 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -412,6 +412,14 @@ config RPMSG_TTY
 	  To compile this driver as a module, choose M here: the module will be
 	  called rpmsg_tty.
 
+config I3C_TTY
+	tristate "tty over i3c"
+	depends on I3C
+	help
+	  Select this options if you'd like use tty over I3C master controller
+
+	  If unsure, say N
+
 endif # TTY
 
 source "drivers/tty/serdev/Kconfig"
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 07aca5184a55d..f329f9c7d308a 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -27,5 +27,6 @@ obj-$(CONFIG_GOLDFISH_TTY)	+= goldfish.o
 obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o
 obj-$(CONFIG_VCC)		+= vcc.o
 obj-$(CONFIG_RPMSG_TTY)		+= rpmsg_tty.o
+obj-$(CONFIG_I3C_TTY)		+= i3c_tty.o
 
 obj-y += ipwireless/
diff --git a/drivers/tty/i3c_tty.c b/drivers/tty/i3c_tty.c
new file mode 100644
index 0000000000000..fe45bf94a8cf2
--- /dev/null
+++ b/drivers/tty/i3c_tty.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 NXP.
+ *
+ * Author: Frank Li <Frank.Li at nxp.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.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_TTY_TRANS_SIZE	16
+#define I3C_TTY_RX_STOP		BIT(0)
+#define I3C_TTY_RETRY		20
+#define I3C_TTY_YIELD_US	100
+
+struct ttyi3c_port {
+	struct tty_port port;
+	int minor;
+	unsigned int txfifo_size;
+	unsigned int rxfifo_size;
+	struct circ_buf xmit;
+	spinlock_t xlock; /* protect xmit */
+	void *buffer;
+	struct i3c_device *i3cdev;
+	struct work_struct txwork;
+	struct work_struct rxwork;
+	struct completion txcomplete;
+	struct workqueue_struct *workqueue;
+	atomic_t status;
+};
+
+static const struct i3c_device_id i3c_ids[] = {
+	I3C_DEVICE(0x011B, 0x1000, NULL),
+	{ /* sentinel */ },
+};
+
+static int i3c_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
+
+	atomic_set(&sport->status, 0);
+
+	return i3c_device_enable_ibi(sport->i3cdev);
+}
+
+static void i3c_port_shutdown(struct tty_port *port)
+{
+	struct ttyi3c_port *sport =
+		container_of(port, struct ttyi3c_port, port);
+
+	i3c_device_disable_ibi(sport->i3cdev);
+}
+
+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 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;
+	}
+	spin_unlock_irqrestore(&sport->xlock, flags);
+
+	if (circ->head != circ->tail)
+		queue_work(sport->workqueue, &sport->txwork);
+
+	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;
+
+	queue_work(sport->workqueue, &sport->txwork);
+}
+
+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;
+
+	atomic_or(I3C_TTY_RX_STOP, &sport->status);
+}
+
+static void i3c_unthrottle(struct tty_struct *tty)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	atomic_andnot(I3C_TTY_RX_STOP, &sport->status);
+
+	queue_work(sport->workqueue, &sport->rxwork);
+}
+
+static int i3c_open(struct tty_struct *tty, struct file *filp)
+{
+	struct ttyi3c_port *sport = tty->driver_data;
+
+	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;
+
+	wait_for_completion_timeout(&sport->txcomplete, timeout);
+	reinit_completion(&sport->txcomplete);
+}
+
+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,
+};
+
+static void i3c_controller_irq_handler(struct i3c_device *dev,
+				       const struct i3c_ibi_payload *payload)
+{
+	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
+
+	queue_work(sport->workqueue, &sport->rxwork);
+}
+
+static void tty_i3c_rxwork(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, rxwork);
+	struct i3c_priv_xfer xfers;
+	int retry = I3C_TTY_RETRY;
+	u16 status = BIT(0);
+
+	do {
+		memset(&xfers, 0, sizeof(xfers));
+		xfers.data.in = sport->buffer;
+		xfers.len = I3C_TTY_TRANS_SIZE;
+		xfers.rnw = 1;
+
+		if (I3C_TTY_RX_STOP & atomic_read(&sport->status))
+			break;
+
+		i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
+
+		if (xfers.actual_len) {
+			tty_insert_flip_string(&sport->port, sport->buffer, xfers.actual_len);
+			retry = 20;
+			continue;
+		} else {
+			status = BIT(0);
+			i3c_device_getstatus_format1(sport->i3cdev, &status);
+			/*
+			 * Target side need some time to fill data into fifo. Target side may not
+			 * have hardware update status in real time. Software update status always
+			 * need some delays.
+			 *
+			 * Generally, target side have cicular buffer in memory, it will be moved
+			 * into FIFO by CPU or DMA. 'status' just show if cicular buffer empty. But
+			 * there are gap, espcially CPU have not response irq to fill FIFO in time.
+			 * So xfers.actual will be zero, wait for little time to avoid flood
+			 * transfer in i3c bus.
+			 */
+			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
+			retry--;
+		}
+
+	} while (retry && (status & BIT(0)));
+
+	tty_flip_buffer_push(&sport->port);
+}
+
+static void tty_i3c_txwork(struct work_struct *work)
+{
+	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, txwork);
+	struct circ_buf *circ = &sport->xmit;
+	int cnt = CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE);
+	struct i3c_priv_xfer xfers;
+	int retry = I3C_TTY_RETRY;
+	unsigned long flags;
+	int actual;
+	int ret;
+
+	while (cnt > 0 && retry) {
+		xfers.rnw = 0;
+		xfers.len = CIRC_CNT_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+		xfers.len = min_t(u16, 16, xfers.len);
+		xfers.data.out = circ->buf + circ->tail;
+
+		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
+		if (ret) {
+			/*
+			 * Target side may not move data out of FIFO. delay can't resolve problem,
+			 * just reduce some possiblity. Target can't end I3C SDR mode write
+			 * transfer, discard data is reasonable when FIFO overrun.
+			 */
+			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
+			retry--;
+		} else {
+			retry = 0;
+		}
+
+		actual = xfers.len;
+
+		circ->tail = (circ->tail + actual) & (UART_XMIT_SIZE - 1);
+
+		if (CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE) < WAKEUP_CHARS)
+			tty_port_tty_wakeup(&sport->port);
+
+		cnt = CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE);
+	}
+
+	spin_lock_irqsave(&sport->xlock, flags);
+	if (circ->head == circ->tail)
+		complete(&sport->txcomplete);
+	spin_unlock_irqrestore(&sport->xlock, flags);
+}
+
+static int i3c_probe(struct i3c_device *i3cdev)
+{
+	struct ttyi3c_port *port;
+	struct device *tty_dev;
+	struct i3c_ibi_setup req;
+	int minor;
+	int ret;
+
+	port = devm_kzalloc(&i3cdev->dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->i3cdev = i3cdev;
+	port->buffer = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
+	if (!port->buffer)
+		return -ENOMEM;
+
+	port->xmit.buf = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
+	if (!port->xmit.buf)
+		return -ENOMEM;
+
+	dev_set_drvdata(&i3cdev->dev, port);
+
+	req.max_payload_len = 8;
+	req.num_slots = 4;
+	req.handler = &i3c_controller_irq_handler;
+
+	ret = i3c_device_request_ibi(i3cdev, &req);
+	if (ret)
+		return -EINVAL;
+
+	mutex_lock(&i3c_tty_minors_lock);
+	minor = idr_alloc(&i3c_tty_minors, port, 0, I3C_TTY_MINORS, GFP_KERNEL);
+	mutex_unlock(&i3c_tty_minors_lock);
+
+	if (minor < 0)
+		return -EINVAL;
+
+	spin_lock_init(&port->xlock);
+	INIT_WORK(&port->txwork, tty_i3c_txwork);
+	INIT_WORK(&port->rxwork, tty_i3c_rxwork);
+	init_completion(&port->txcomplete);
+
+	port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&i3cdev->dev));
+	if (!port->workqueue)
+		return -ENOMEM;
+
+	tty_port_init(&port->port);
+	port->port.ops = &i3c_port_ops;
+
+	tty_dev = tty_port_register_device(&port->port, i3c_tty_driver, minor,
+					   &i3cdev->dev);
+	if (IS_ERR(tty_dev)) {
+		destroy_workqueue(port->workqueue);
+		return PTR_ERR(tty_dev);
+	}
+
+	port->minor = minor;
+
+	return 0;
+}
+
+void i3c_remove(struct i3c_device *dev)
+{
+	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
+
+	tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
+	cancel_work_sync(&sport->txwork);
+	destroy_workqueue(sport->workqueue);
+}
+
+static struct i3c_driver i3c_driver = {
+	.driver = {
+		.name = "ttyi3c",
+	},
+	.probe = i3c_probe,
+	.remove = i3c_remove,
+	.id_table = i3c_ids,
+};
+
+static int __init i3c_tty_init(void)
+{
+	int ret;
+
+	i3c_tty_driver = tty_alloc_driver(I3C_TTY_MINORS,
+					  TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
+
+	if (IS_ERR(i3c_tty_driver))
+		return PTR_ERR(i3c_tty_driver);
+
+	i3c_tty_driver->driver_name = "ttyI3C";
+	i3c_tty_driver->name = "ttyI3C";
+	i3c_tty_driver->minor_start = 0,
+	i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
+	i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
+	i3c_tty_driver->init_termios = tty_std_termios;
+	i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
+					       CLOCAL;
+	i3c_tty_driver->init_termios.c_lflag = 0;
+
+	tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
+
+	ret = tty_register_driver(i3c_tty_driver);
+	if (ret) {
+		tty_driver_kref_put(i3c_tty_driver);
+		return ret;
+	}
+
+	ret = i3c_driver_register(&i3c_driver);
+	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_driver_unregister(&i3c_driver);
+	tty_unregister_driver(i3c_tty_driver);
+	tty_driver_kref_put(i3c_tty_driver);
+	idr_destroy(&i3c_tty_minors);
+}
+
+module_init(i3c_tty_init);
+module_exit(i3c_tty_exit);
+
+MODULE_LICENSE("GPL");
-- 
2.34.1



From Frank.Li at nxp.com  Wed Oct 18 14:58:04 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 17:58:04 -0400
Subject: [PATCH 0/5] I3C slave mode support
Message-ID: <20231018215809.3477437-1-Frank.Li@nxp.com>

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/20231018211111.3437929-1-Frank.Li at nxp.com/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



From Frank.Li at nxp.com  Wed Oct 18 14:58:05 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 17:58:05 -0400
Subject: [PATCH 1/5] i3c: add slave mode support
In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
Message-ID: <20231018215809.3477437-2-Frank.Li@nxp.com>

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

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

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 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 <Frank.Li at nxp.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/i3c/slave.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(functions_mutex);
+static struct config_group *functions_group;
+static struct config_group *controllers_group;
+
+struct i3c_slave_func_group {
+	struct config_group group;
+	struct i3c_slave_func *func;
+};
+
+struct i3c_slave_ctrl_group {
+	struct config_group group;
+	struct i3c_slave_ctrl *ctrl;
+};
+
+static inline struct i3c_slave_func_group *to_i3c_slave_func_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct i3c_slave_func_group, group);
+}
+
+static inline struct i3c_slave_ctrl_group *to_i3c_slave_ctrl_group(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct i3c_slave_ctrl_group, group);
+}
+
+static int i3c_slave_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg)
+{
+	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg);
+	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
+	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
+	struct i3c_slave_func *func = func_group->func;
+	int ret;
+
+	ret = i3c_slave_ctrl_add_func(ctrl, func);
+	if (ret)
+		return ret;
+
+	ret = i3c_slave_func_bind(func);
+	if (ret) {
+		i3c_slave_ctrl_remove_func(ctrl, func);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void i3c_slave_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg)
+{
+	struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg->ci_parent);
+	struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg);
+	struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl;
+	struct i3c_slave_func *func = func_group->func;
+
+	i3c_slave_func_unbind(func);
+	i3c_slave_ctrl_remove_func(ctrl, func);
+}
+
+static 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 <Frank.Li at nxp.com>");
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 <Frank.Li at nxp.com>
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/i3c/slave.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(func_lock);
+static struct class *i3c_slave_ctrl_class;
+
+static void i3c_slave_func_dev_release(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+
+	kfree(func->name);
+	kfree(func);
+}
+
+static const struct device_type i3c_slave_func_type = {
+	.release        = i3c_slave_func_dev_release,
+};
+
+static int i3c_slave_func_match_driver(struct device *dev, struct device_driver *drv)
+{
+	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
+}
+
+static int i3c_slave_func_device_probe(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
+
+	if (!driver->probe)
+		return -ENODEV;
+
+	func->driver = driver;
+
+	return driver->probe(func);
+}
+
+static void i3c_slave_func_device_remove(struct device *dev)
+{
+	struct i3c_slave_func *func = to_i3c_slave_func(dev);
+	struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver);
+
+	if (driver->remove)
+		driver->remove(func);
+	func->driver = NULL;
+}
+
+static const struct bus_type i3c_slave_func_bus_type = {
+	.name = "i3c_slave_func",
+	.probe = i3c_slave_func_device_probe,
+	.remove = i3c_slave_func_device_remove,
+	.match = i3c_slave_func_match_driver,
+};
+
+static void i3c_slave_ctrl_release(struct device *dev)
+{
+	kfree(to_i3c_slave_ctrl(dev));
+}
+
+static void devm_i3c_slave_ctrl_release(struct device *dev, void *res)
+{
+	struct i3c_slave_ctrl *ctrl = *(struct i3c_slave_ctrl **)res;
+
+	i3c_slave_ctrl_destroy(ctrl);
+}
+
+struct i3c_slave_ctrl *
+__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+			     struct module *owner)
+{
+	struct i3c_slave_ctrl **ptr, *ctrl;
+
+	ptr = devres_alloc(devm_i3c_slave_ctrl_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	ctrl = __i3c_slave_ctrl_create(dev, ops, owner);
+	if (!IS_ERR(ctrl)) {
+		*ptr = ctrl;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return ctrl;
+}
+
+static int devm_i3c_slave_ctrl_match(struct device *dev, void *res, void *match_data)
+{
+	struct i3c_slave_ctrl **ptr = res;
+
+	return *ptr == match_data;
+}
+
+/**
+ * __i3c_slave_ctrl_create() - create a new slave controller device
+ * @dev: device that is creating the new slave controller
+ * @ops: function pointers for performing slave controller  operations
+ * @owner: the owner of the module that creates the slave controller device
+ *
+ * Return: Pointer to struct i3c_slave_ctrl
+ */
+struct i3c_slave_ctrl *
+__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops,
+			struct module *owner)
+{
+	struct i3c_slave_ctrl *ctrl;
+	int ret;
+
+	if (WARN_ON(!dev))
+		return ERR_PTR(-EINVAL);
+
+	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return ERR_PTR(-ENOMEM);
+
+	device_initialize(&ctrl->dev);
+	ctrl->dev.class = i3c_slave_ctrl_class;
+	ctrl->dev.parent = dev;
+	ctrl->dev.release = i3c_slave_ctrl_release;
+	ctrl->ops = ops;
+
+	ret = dev_set_name(&ctrl->dev, "%s", dev_name(dev));
+	if (ret)
+		goto put_dev;
+
+	ret = device_add(&ctrl->dev);
+	if (ret)
+		goto put_dev;
+
+	ctrl->group = i3c_slave_cfs_add_ctrl_group(ctrl);
+	if (!ctrl->group)
+		goto put_dev;
+
+	return ctrl;
+
+put_dev:
+	put_device(&ctrl->dev);
+	kfree(ctrl);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(__i3c_slave_ctrl_create);
+
+/**
+ * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device
+ * @dev: device that is creating the new slave controller device
+ * @ops: function pointers for performing slave controller operations
+ * @owner: the owner of the module that creates the slave controller device
+ *
+ * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it
+ * also associates the device with the i3c_slave using devres. On driver detach, release function is
+ * invoked on the devres data, then devres data is freed.
+ */
+void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl)
+{
+	int r;
+
+	r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match,
+			   ctrl);
+	dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy);
+
+/**
+ * i3c_slave_ctrl_destroy() - destroy the slave controller device
+ * @ctrl: the slave controller device that has to be destroyed
+ *
+ * Invoke to destroy the I3C slave device
+ */
+void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl)
+{
+	i3c_slave_cfs_remove_ctrl_group(ctrl->group);
+	device_unregister(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy);
+
+/**
+ * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller
+ * @ctrl: the controller device to which the slave function should be added
+ * @func: the slave function to be added
+ *
+ * An I3C slave device can have only one functions.
+ */
+int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	if (ctrl->func)
+		return -EBUSY;
+
+	ctrl->func = func;
+	func->ctrl = ctrl;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func);
+
+/**
+ * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller
+ * @ctrl: the controller device to which the slave function should be removed
+ * @func: the slave function to be removed
+ *
+ * An I3C slave device can have only one functions.
+ */
+void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func)
+{
+	ctrl->func = NULL;
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func);
+
+/**
+ * i3c_slave_ctrl() - get the I3C slave controller
+ * @name: device name of the slave controller
+ *
+ * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the
+ * slave controller
+ */
+struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name)
+{
+	int ret = -EINVAL;
+	struct i3c_slave_ctrl *ctrl;
+	struct device *dev;
+	struct class_dev_iter iter;
+
+	class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL);
+	while ((dev = class_dev_iter_next(&iter))) {
+		if (strcmp(name, dev_name(dev)))
+			continue;
+
+		ctrl = to_i3c_slave_ctrl(dev);
+		if (!try_module_get(ctrl->ops->owner)) {
+			ret = -EINVAL;
+			goto err;
+		}
+
+		class_dev_iter_exit(&iter);
+		get_device(&ctrl->dev);
+		return ctrl;
+	}
+
+err:
+	class_dev_iter_exit(&iter);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(i3c_slave_ctrl_get);
+
+/**
+ * i3c_slave_ctrl_put() - release the I3C endpoint controller
+ * @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 <Frank.Li at nxp.com>
+ */
+
+#ifndef I3C_SLAVE_H
+#define I3C_SLAVE_H
+
+#include <linux/device.h>
+
+struct i3c_slave_func;
+struct i3c_slave_ctrl;
+
+/**
+ * struct i3c_slave_func_ops - set of function pointers for performing i3c slave function operations
+ * @bind: ops to perform when a controller device has been bound to function device
+ * @unbind: ops to perform when a binding has been lost between a controller device and function
+ *	    device
+ */
+struct i3c_slave_func_ops {
+	int (*bind)(struct i3c_slave_func *func);
+	void (*unbind)(struct i3c_slave_func *func);
+};
+
+/**
+ * struct i3c_slave_func_driver - represents the I3C function driver
+ * @probe: ops to perform when a new function device has been bound to the function driver
+ * @remove: ops to perform when the binding between the function device and function driver is
+ *	    broken
+ * @driver: I3C Function driver
+ * @ops: set of function pointers for performing function operations
+ * @owner: the owner of the module that registers the I3C function driver
+ * @epf_group: list of configfs group corresponding to the I3C function driver
+ */
+struct i3c_slave_func_driver {
+	int (*probe)(struct i3c_slave_func *func);
+	void (*remove)(struct i3c_slave_func *func);
+
+	char *name;
+	struct device_driver driver;
+	struct i3c_slave_func_ops *ops;
+	struct module *owner;
+};
+
+/**
+ * struct i3c_slave_func - represents the I3C function device
+ * @dev: the I3C function device
+ * @name: the name of the I3C function device
+ * @driver: the function driver to which this function device is bound
+ * @group: configfs group associated with the EPF device
+ * @lock: mutex to protect i3c_slave_func_ops
+ * @ctrl: binded I3C controller device
+ * @is_bound: indicates if bind notification to function driver has been invoked
+ * @vednor_id: vendor id
+ * @part_id: part id
+ * @instance_id: instance id
+ * @ext_id: ext id
+ * @vendor_info: vendor info
+ * @static_addr: static address for I2C. It is 0 for I3C.
+ * @max_write_len: 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



From Frank.Li at nxp.com  Wed Oct 18 14:58:06 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 17:58:06 -0400
Subject: [PATCH 2/5] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave
In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
Message-ID: <20231018215809.3477437-3-Frank.Li@nxp.com>

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

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 .../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 <conor.culhane at silvaco.com>
@@ -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



From Frank.Li at nxp.com  Wed Oct 18 14:58:07 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 17:58:07 -0400
Subject: [PATCH 3/5] i3c: slave: add svc slave controller support
In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
Message-ID: <20231018215809.3477437-4-Frank.Li@nxp.com>

Add Silvaco I3C slave controller support

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 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 <Frank.Li at nxp.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/i3c/slave.h>
+#include <linux/i3c/slave.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/i3c/device.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+
+enum i3c_clks {
+	PCLK,
+	FCLK,
+	SCLK,
+	MAXCLK,
+};
+
+struct svc_i3c_slave {
+	struct device *dev;
+	void __iomem *regs;
+	int irq;
+	struct clk_bulk_data clks[MAXCLK];
+
+	struct list_head txq;
+	spinlock_t txq_lock; /* 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



From Frank.Li at nxp.com  Wed Oct 18 14:58:08 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 17:58:08 -0400
Subject: [PATCH 4/5] i3c: slave: func: add tty driver
In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
Message-ID: <20231018215809.3477437-5-Frank.Li@nxp.com>

Add tty over I3C slave function driver.

Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
 drivers/i3c/Kconfig       |   1 +
 drivers/i3c/Makefile      |   1 +
 drivers/i3c/func/Kconfig  |   9 +
 drivers/i3c/func/Makefile |   3 +
 drivers/i3c/func/tty.c    | 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 <Frank.Li at nxp.com>
+ */
+
+#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



From Frank.Li at nxp.com  Wed Oct 18 14:58:09 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 17:58:09 -0400
Subject: [PATCH 5/5] Documentation: i3c: Add I3C slave mode controller and function
In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
Message-ID: <20231018215809.3477437-6-Frank.Li@nxp.com>

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

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

diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst
index 783d6dad054b6..63fc51fc8bd58 100644
--- a/Documentation/driver-api/i3c/index.rst
+++ b/Documentation/driver-api/i3c/index.rst
@@ -9,3 +9,4 @@ I3C subsystem
    protocol
    device-driver-api
    master-driver-api
+   slave/index
diff --git a/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst b/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
new file mode 100644
index 0000000000000..d78fcbc4e5587
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================
+Configuring I3C Slave Using CONFIGFS
+=======================================
+
+:Author: Frank Li <Frank.Li at nxp.com>
+
+The I3C Slave Core exposes configfs entry (i3c_slave) to configure the I3C
+slave function and to bind the slave function
+with the slave controller. (For introducing other mechanisms to
+configure the I3C Slave Function refer to [1]).
+
+Mounting configfs
+=================
+
+The I3C Slave Core layer creates i3c_slave directory in the mounted configfs
+directory. configfs can be mounted using the following command::
+
+	mount -t configfs none /sys/kernel/config
+
+Directory Structure
+===================
+
+The i3c_slave configfs has two directories at its root: controllers and
+functions. Every Controller device present in the system will have an entry in
+the *controllers* directory and every Function driver present in the system
+will have an entry in the *functions* directory.
+::
+
+	/sys/kernel/config/i3c_slave/
+		.. controllers/
+		.. functions/
+
+Creating Function Device
+===================
+
+Every registered Function driver will be listed in controllers directory. The
+entries corresponding to Function driver will be created by the Function core.
+::
+
+	/sys/kernel/config/i3c_slave/functions/
+		.. <Function Driver1>/
+			... <Function Device 11>/
+			... <Function Device 21>/
+			... <Function Device 31>/
+		.. <Function Driver2>/
+			... <Function Device 12>/
+			... <Function Device 22>/
+
+In order to create a <Function device> of the type probed by <Function Driver>,
+the user has to create a directory inside <Function DriverN>.
+
+Every <Function device> directory consists of the following entries that can be
+used to configure the standard configuration header of the slave function.
+(These entries are created by the framework when any new <Function Device> is
+created)
+::
+
+		.. <Function Driver1>/
+			... <Function Device 11>/
+				... vendor_id
+				... part_id
+				... bcr
+				... dcr
+				... ext_id
+				... instance_id
+				... max_read_len
+				... max_write_len
+				... vendor_info
+
+Controller Device
+==========
+
+Every registered Controller device will be listed in controllers directory. The
+entries corresponding to Controller device will be created by the Controller
+core.
+::
+
+	/sys/kernel/config/i3c_slave/controllers/
+		.. <Controller Device1>/
+			... <Symlink Function Device11>/
+		.. <Controller Device2>/
+			... <Symlink Function Device21>/
+
+The <Controller Device> directory will have a list of symbolic links to
+<Function Device>. These symbolic links should be created by the user to
+represent the functions present in the slave device. Only <Function Device>
+that represents a physical function can be linked to a Controller device.
+
+::
+
+			 | controllers/
+				| <Directory: Controller name>/
+					| <Symbolic Link: Function>
+			 | functions/
+				| <Directory: Function driver>/
+					| <Directory: Function device>/
+						| vendor_id
+						| part_id
+						| bcr
+						| dcr
+						| ext_id
+						| instance_id
+						| max_read_len
+						| max_write_len
+						| vendor_info
+
+[1] Documentation/I3C/slave/pci-slave.rst
diff --git a/Documentation/driver-api/i3c/slave/i3c-slave.rst b/Documentation/driver-api/i3c/slave/i3c-slave.rst
new file mode 100644
index 0000000000000..363421241b594
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-slave.rst
@@ -0,0 +1,189 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+:Author: Frank Li <Frank.Li at nxp.com>
+
+This document is a guide to use the I3C Slave Framework in order to create
+slave controller driver, slave function driver, and using configfs
+interface to bind the function driver to the controller driver.
+
+Introduction
+============
+
+Linux has a comprehensive I3C subsystem to support I3C controllers that
+operates in master mode. The subsystem has capability to scan I3C bus,assign
+i3c device address, load I3C driver (based on Manufacturer ID, part ID),
+support other services like hot-join, In-Band Interrupt(IBI).
+
+However the I3C controller IP integrated in some SoCs is capable of operating
+either in Master mode or Slave mode. I3C Slave Framework will add slave mode
+support in Linux. This will help to run Linux in an slave system which can
+have a wide variety of use cases from testing or validation, co-processor
+accelerator, etc.
+
+I3C Slave Core
+=================
+
+The I3C Slave Core layer comprises 3 components: the Slave Controller
+library, the Slave Function library, and the configfs layer to bind the
+slave function with the slave controller.
+
+I3C Slave Controller Library
+------------------------------------
+
+The Controller library provides APIs to be used by the controller that can
+operate in slave mode. It also provides APIs to be used by function
+driver/library in order to implement a particular slave function.
+
+APIs for the I3C Slave controller Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C controller driver.
+
+* devm_i3c_slave_ctrl_create()/i3c_slave_ctrl_create()
+
+   The I3C controller driver should implement the following ops:
+
+	* set_config: ops to set i3c configuration
+	* enable: ops to enable controller
+	* disable: ops to disable controller
+	* raise_ibi: ops to raise IBI to master controller
+	* alloc_request: ops to alloc a transfer request
+	* free_request: ops to free a transfer request
+	* queue: ops to queue a request to transfer queue
+	* dequeue: ops to dequeue a request from transfer queue
+	* cancel_all_reqs: ops to cancel all request from transfer queue
+        * fifo_status: ops to get fifo status
+        * fifo_flush: ops to flush hardware fifo
+	* get_features: ops to get controller supported features
+
+   The I3C controller driver can then create a new Controller device by
+   invoking devm_i3c_slave_ctrl_create()/i3c_slave_ctrl_create().
+
+* devm_i3c_slave_ctrl_destroy()/i3c_slave_ctrl_destroy()
+
+   The I3C controller driver can destroy the Controller device created by
+   either devm_i3c_slave_ctrl_create() or i3c_slave_ctrl_create() using
+   devm_i3c_slave_ctrl_destroy() or i3c_slave_ctrl_destroy().
+
+I3C Slave Controller APIs for the I3C Slave Function Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave function driver.
+
+* i3c_slave_ctrl_set_config()
+
+   The I3C slave function driver should use i3c_slave_ctrl_set_config() to
+   write i3c configuration to the slave controller.
+
+* i3c_slave_ctrl_enable()/i3c_slave_ctrl_disable()
+
+   The I3C slave function driver should use i3c_slave_ctrl_enable()/
+   i3c_slave_ctrl_disable() to enable/disable i3c slave controller.
+
+* i3c_slave_ctrl_alloc_request()/i3c_slave_ctrl_free_request()
+
+   The I3C slave function driver should usei3c_slave_ctrl_alloc_request() /
+   i3c_slave_ctrl_free_request() to alloc/free a i3c request.
+
+* i3c_slave_ctrl_raise_ibi()
+
+   The I3C slave function driver should use i3c_slave_ctrl_raise_ibi() to
+   raise IBI.
+
+* i3c_slave_ctrl_queue()/i3c_slave_ctrl_dequeue()
+
+   The I3C slave function driver should use i3c_slave_ctrl_queue()/
+   i3c_slave_ctrl_dequeue(), to queue/dequeue I3C transfer to/from transfer
+   queue.
+
+* i3c_slave_ctrl_get_features()
+
+   The I3C slave function driver should use i3c_slave_ctrl_get_features()
+   to get I3C slave controller supported features.
+
+Other I3C Slave Controller APIs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are other APIs provided by the Controller library. These are used for
+binding the I3C Slave Function device with Controlller device. i3c-cfs.c can
+be used as reference for using these APIs.
+
+* i3c_slave_ctrl_get()
+
+   Get a reference to the I3C slave controller based on the device name of
+   the controller.
+
+* i3c_slave_ctrl_put()
+
+   Release the reference to the I3C slave controller obtained using
+   i3c_slave_ctrl_get()
+
+* i3c_slave_ctrl_add_func()
+
+   Add a I3C slave function to a I3C slave controller.
+
+* i3c_slave_ctrl_remove_func()
+
+   Remove the I3C slave function from I3C slave controller.
+
+I3C Slave Function Library
+----------------------------------
+
+The I3C Slave Function library provides APIs to be used by the function driver
+and the Controller library to provide slave mode functionality.
+
+I3C Slave Function APIs for the I3C Slave Function Driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave function driver.
+
+* i3c_slave_func_register_driver()
+
+   The I3C Slave Function driver should implement the following ops:
+	 * bind: ops to perform when a Controller device has been bound to
+	   Function device
+	 * unbind: ops to perform when a binding has been lost between a
+	   Controller device and Function device
+
+  The I3C Function driver can then register the I3C Function driver by using
+  i3c_slave_func_register_driver().
+
+* i3c_slave_func_unregister_driver()
+
+  The I3C Function driver can unregister the I3C Function driver by using
+  i3c_epf_unregister_driver().
+
+APIs for the I3C Slave Controller Library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section lists the APIs that the I3C Slave core provides to be used
+by the I3C slave controller library.
+
+Other I3C Slave APIs
+~~~~~~~~~~~~~~~~~~~~
+
+There are other APIs provided by the Function library. These are used to
+notify the function driver when the Function device is bound to the EPC device.
+i3c-cfs.c can be used as reference for using these APIs.
+
+* i3c_slave_func_create()
+
+   Create a new I3C Function device by passing the name of the I3C EPF device.
+   This name will be used to bind the Function device to a Function driver.
+
+* i3c_slave_func_destroy()
+
+   Destroy the created I3C Function device.
+
+* i3c_slave_func_bind()
+
+   i3c_slave_func_bind() should be invoked when the EPF device has been bound
+   to a Controller device.
+
+* i3c_slave_func_unbind()
+
+   i3c_slave_func_unbind() should be invoked when the binding between EPC
+   device and function device is lost.
diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-function.rst b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst
new file mode 100644
index 0000000000000..3c8521d7aa31a
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst
@@ -0,0 +1,103 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+PCI Test Function
+=================
+
+:Author: Kishon Vijay Abraham I <kishon at ti.com>
+
+Traditionally PCI RC has always been validated by using standard
+PCI cards like ethernet PCI cards or USB PCI cards or SATA PCI cards.
+However with the addition of EP-core in linux kernel, it is possible
+to configure a PCI controller that can operate in EP mode to work as
+a test device.
+
+The PCI endpoint test device is a virtual device (defined in software)
+used to test the endpoint functionality and serve as a sample driver
+for other PCI endpoint devices (to use the EP framework).
+
+The PCI endpoint test device has the following registers:
+
+	1) PCI_ENDPOINT_TEST_MAGIC
+	2) PCI_ENDPOINT_TEST_COMMAND
+	3) PCI_ENDPOINT_TEST_STATUS
+	4) PCI_ENDPOINT_TEST_SRC_ADDR
+	5) PCI_ENDPOINT_TEST_DST_ADDR
+	6) PCI_ENDPOINT_TEST_SIZE
+	7) PCI_ENDPOINT_TEST_CHECKSUM
+	8) PCI_ENDPOINT_TEST_IRQ_TYPE
+	9) PCI_ENDPOINT_TEST_IRQ_NUMBER
+
+* PCI_ENDPOINT_TEST_MAGIC
+
+This register will be used to test BAR0. A known pattern will be written
+and read back from MAGIC register to verify BAR0.
+
+* PCI_ENDPOINT_TEST_COMMAND
+
+This register will be used by the host driver to indicate the function
+that the endpoint device must perform.
+
+========	================================================================
+Bitfield	Description
+========	================================================================
+Bit 0		raise legacy IRQ
+Bit 1		raise MSI IRQ
+Bit 2		raise MSI-X IRQ
+Bit 3		read command (read data from RC buffer)
+Bit 4		write command (write data to RC buffer)
+Bit 5		copy command (copy data from one RC buffer to another RC buffer)
+========	================================================================
+
+* PCI_ENDPOINT_TEST_STATUS
+
+This register reflects the status of the PCI endpoint device.
+
+========	==============================
+Bitfield	Description
+========	==============================
+Bit 0		read success
+Bit 1		read fail
+Bit 2		write success
+Bit 3		write fail
+Bit 4		copy success
+Bit 5		copy fail
+Bit 6		IRQ raised
+Bit 7		source address is invalid
+Bit 8		destination address is invalid
+========	==============================
+
+* PCI_ENDPOINT_TEST_SRC_ADDR
+
+This register contains the source address (RC buffer address) for the
+COPY/READ command.
+
+* PCI_ENDPOINT_TEST_DST_ADDR
+
+This register contains the destination address (RC buffer address) for
+the COPY/WRITE command.
+
+* PCI_ENDPOINT_TEST_IRQ_TYPE
+
+This register contains the interrupt type (Legacy/MSI) triggered
+for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands.
+
+Possible types:
+
+======	==
+Legacy	0
+MSI	1
+MSI-X	2
+======	==
+
+* PCI_ENDPOINT_TEST_IRQ_NUMBER
+
+This register contains the triggered ID interrupt.
+
+Admissible values:
+
+======	===========
+Legacy	0
+MSI	[1 .. 32]
+MSI-X	[1 .. 2048]
+======	===========
diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
new file mode 100644
index 0000000000000..11c8900fd16f3
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+I3C TTY User Guide
+===================
+
+:Author: Frank Li <Frank.Li at nxp.com>
+
+This document is a guide to help users use i3c-slave-tty function driver
+and i3ctty master driver for testing I3C. The list of steps to be followed in the
+master side and slave side is given below.
+
+Endpoint Device
+===============
+
+Endpoint Controller Devices
+---------------------------
+
+To find the list of slave controller devices in the system::
+
+	# ls  /sys/class/i3c_slave/
+	  44330000.i3c-slave
+
+If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
+
+	# ls /sys/kernel/config/i3c_slave/controllers/
+	  44330000.i3c-slave
+
+
+Endpoint Function Drivers
+-------------------------
+
+To find the list of slave function drivers in the system::
+
+	# ls /sys/bus/i3c_slave_func/drivers
+	  tty
+
+If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
+
+	# ls /sys/kernel/config/i3c_slave/functions
+	  tty
+
+
+Creating i3c-slave-tty Device
+----------------------------
+
+I3C slave function device can be created using the configfs. To create
+i3c-slave-tty device, the following commands can be used::
+
+	# mount -t configfs none /sys/kernel/config
+	# cd /sys/kernel/config/i3c_slave/
+	# mkdir functions/tty/func1
+
+The "mkdir func1" above creates the i3c-slave-tty function device that will
+be probed by i3c tty driver.
+
+The I3C slave framework populates the directory with the following
+configurable fields::
+
+	# ls functions/tty/func1
+	bcr  dcr  ext_id  instance_id  max_read_len  max_write_len
+	part_id  vendor_id  vendor_info
+
+The I3C slave function driver populates these entries with default values
+when the device is bound to the driver. The i3c-slave-tty driver populates
+vendorid with 0xffff and interrupt_pin with 0x0001::
+
+	# cat functions/tty/func1/vendor_id
+	  0x0
+
+Configuring i3c-slave-tty Device
+-------------------------------
+
+The user can configure the i3c-slave-tty device using configfs entry. In order
+to change the vendorid, the following commands can be used::
+
+	# echo 0x011b > functions/tty/func1/vendor_id
+	# echo 0x1000 > functions/tty/func1/part_id
+	# echo 0x6 > functions/tty/t/bcr
+
+Binding i3c-slave-tty Device to slave Controller
+------------------------------------------------
+
+In order for the slave function device to be useful, it has to be bound to
+a I3C slave controller driver. Use the configfs to bind the function
+device to one of the controller driver present in the system::
+
+	# ln -s functions/pci_epf_test/func1 controllers/44330000.i3c-slave/
+
+I3C Master Device
+================
+
+Check I3C tty device is probed
+
+	# ls /sys/bus/i3c/devices/0-23610000000
+	0-23610000000:0  bcr  dcr  driver  dynamic_address  hdrcap
+	modalias  pid  power  subsystem  tty  uevent
+
+Using Slave TTY function Device
+-----------------------------------
+
+Host side:
+	cat /dev/ttyI3C0
+Slave side
+	echo abc >/dev/ttyI3C0
+
+You will see "abc" show at console.
+
+You can use other tty tool to test I3C slave tty device.
diff --git a/Documentation/driver-api/i3c/slave/index.rst b/Documentation/driver-api/i3c/slave/index.rst
new file mode 100644
index 0000000000000..69727ccf985db
--- /dev/null
+++ b/Documentation/driver-api/i3c/slave/index.rst
@@ -0,0 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+I3C Slave Framework
+======================
+
+.. toctree::
+   :maxdepth: 2
+
+   i3c-slave
+   i3c-slave-cfs
+   i3c-tty-howto
+
-- 
2.34.1



From Frank.li at nxp.com  Wed Oct 18 15:07:17 2023
From: Frank.li at nxp.com (Frank Li)
Date: Wed, 18 Oct 2023 18:07:17 -0400
Subject: [PATCH 5/5] Documentation: i3c: Add I3C slave mode controller
 and function
In-Reply-To: <20231018215809.3477437-6-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
 <20231018215809.3477437-6-Frank.Li@nxp.com>
Message-ID: <ZTBXFd1lOjWTiAI9@lizhi-Precision-Tower-5810>

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 <Frank.Li at nxp.com>
> ---
>  Documentation/driver-api/i3c/index.rst        |   1 +
>  .../driver-api/i3c/slave/i3c-slave-cfs.rst    | 109 ++++++++++
>  .../driver-api/i3c/slave/i3c-slave.rst        | 189 ++++++++++++++++++
>  .../driver-api/i3c/slave/i3c-tty-function.rst | 103 ++++++++++
>  .../driver-api/i3c/slave/i3c-tty-howto.rst    | 109 ++++++++++
>  Documentation/driver-api/i3c/slave/index.rst  |  13 ++
>  6 files changed, 524 insertions(+)
>  create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst
>  create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave.rst
>  create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-function.rst

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 <Frank.Li at nxp.com>
> +
> +The I3C Slave Core exposes configfs entry (i3c_slave) to configure the I3C
> +slave function and to bind the slave function
> +with the slave controller. (For introducing other mechanisms to
> +configure the I3C Slave Function refer to [1]).
> +
> +Mounting configfs
> +=================
> +
> +The I3C Slave Core layer creates i3c_slave directory in the mounted configfs
> +directory. configfs can be mounted using the following command::
> +
> +	mount -t configfs none /sys/kernel/config
> +
> +Directory Structure
> +===================
> +
> +The i3c_slave configfs has two directories at its root: controllers and
> +functions. Every Controller device present in the system will have an entry in
> +the *controllers* directory and every Function driver present in the system
> +will have an entry in the *functions* directory.
> +::
> +
> +	/sys/kernel/config/i3c_slave/
> +		.. controllers/
> +		.. functions/
> +
> +Creating Function Device
> +===================
> +
> +Every registered Function driver will be listed in controllers directory. The
> +entries corresponding to Function driver will be created by the Function core.
> +::
> +
> +	/sys/kernel/config/i3c_slave/functions/
> +		.. <Function Driver1>/
> +			... <Function Device 11>/
> +			... <Function Device 21>/
> +			... <Function Device 31>/
> +		.. <Function Driver2>/
> +			... <Function Device 12>/
> +			... <Function Device 22>/
> +
> +In order to create a <Function device> of the type probed by <Function Driver>,
> +the user has to create a directory inside <Function DriverN>.
> +
> +Every <Function device> directory consists of the following entries that can be
> +used to configure the standard configuration header of the slave function.
> +(These entries are created by the framework when any new <Function Device> is
> +created)
> +::
> +
> +		.. <Function Driver1>/
> +			... <Function Device 11>/
> +				... vendor_id
> +				... part_id
> +				... bcr
> +				... dcr
> +				... ext_id
> +				... instance_id
> +				... max_read_len
> +				... max_write_len
> +				... vendor_info
> +
> +Controller Device
> +==========
> +
> +Every registered Controller device will be listed in controllers directory. The
> +entries corresponding to Controller device will be created by the Controller
> +core.
> +::
> +
> +	/sys/kernel/config/i3c_slave/controllers/
> +		.. <Controller Device1>/
> +			... <Symlink Function Device11>/
> +		.. <Controller Device2>/
> +			... <Symlink Function Device21>/
> +
> +The <Controller Device> directory will have a list of symbolic links to
> +<Function Device>. These symbolic links should be created by the user to
> +represent the functions present in the slave device. Only <Function Device>
> +that represents a physical function can be linked to a Controller device.
> +
> +::
> +
> +			 | controllers/
> +				| <Directory: Controller name>/
> +					| <Symbolic Link: Function>
> +			 | functions/
> +				| <Directory: Function driver>/
> +					| <Directory: Function device>/
> +						| vendor_id
> +						| part_id
> +						| bcr
> +						| dcr
> +						| ext_id
> +						| instance_id
> +						| max_read_len
> +						| max_write_len
> +						| vendor_info
> +
> +[1] Documentation/I3C/slave/pci-slave.rst
> diff --git a/Documentation/driver-api/i3c/slave/i3c-slave.rst b/Documentation/driver-api/i3c/slave/i3c-slave.rst
> new file mode 100644
> index 0000000000000..363421241b594
> --- /dev/null
> +++ b/Documentation/driver-api/i3c/slave/i3c-slave.rst
> @@ -0,0 +1,189 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +:Author: Frank Li <Frank.Li at nxp.com>
> +
> +This document is a guide to use the I3C Slave Framework in order to create
> +slave controller driver, slave function driver, and using configfs
> +interface to bind the function driver to the controller driver.
> +
> +Introduction
> +============
> +
> +Linux has a comprehensive I3C subsystem to support I3C controllers that
> +operates in master mode. The subsystem has capability to scan I3C bus,assign
> +i3c device address, load I3C driver (based on Manufacturer ID, part ID),
> +support other services like hot-join, In-Band Interrupt(IBI).
> +
> +However the I3C controller IP integrated in some SoCs is capable of operating
> +either in Master mode or Slave mode. I3C Slave Framework will add slave mode
> +support in Linux. This will help to run Linux in an slave system which can
> +have a wide variety of use cases from testing or validation, co-processor
> +accelerator, etc.
> +
> +I3C Slave Core
> +=================
> +
> +The I3C Slave Core layer comprises 3 components: the Slave Controller
> +library, the Slave Function library, and the configfs layer to bind the
> +slave function with the slave controller.
> +
> +I3C Slave Controller Library
> +------------------------------------
> +
> +The Controller library provides APIs to be used by the controller that can
> +operate in slave mode. It also provides APIs to be used by function
> +driver/library in order to implement a particular slave function.
> +
> +APIs for the I3C Slave controller Driver
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +This section lists the APIs that the I3C Slave core provides to be used
> +by the I3C controller driver.
> +
> +* devm_i3c_slave_ctrl_create()/i3c_slave_ctrl_create()
> +
> +   The I3C controller driver should implement the following ops:
> +
> +	* set_config: ops to set i3c configuration
> +	* enable: ops to enable controller
> +	* disable: ops to disable controller
> +	* raise_ibi: ops to raise IBI to master controller
> +	* alloc_request: ops to alloc a transfer request
> +	* free_request: ops to free a transfer request
> +	* queue: ops to queue a request to transfer queue
> +	* dequeue: ops to dequeue a request from transfer queue
> +	* cancel_all_reqs: ops to cancel all request from transfer queue
> +        * fifo_status: ops to get fifo status
> +        * fifo_flush: ops to flush hardware fifo
> +	* get_features: ops to get controller supported features
> +
> +   The I3C controller driver can then create a new Controller device by
> +   invoking devm_i3c_slave_ctrl_create()/i3c_slave_ctrl_create().
> +
> +* devm_i3c_slave_ctrl_destroy()/i3c_slave_ctrl_destroy()
> +
> +   The I3C controller driver can destroy the Controller device created by
> +   either devm_i3c_slave_ctrl_create() or i3c_slave_ctrl_create() using
> +   devm_i3c_slave_ctrl_destroy() or i3c_slave_ctrl_destroy().
> +
> +I3C Slave Controller APIs for the I3C Slave Function Driver
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +This section lists the APIs that the I3C Slave core provides to be used
> +by the I3C slave function driver.
> +
> +* i3c_slave_ctrl_set_config()
> +
> +   The I3C slave function driver should use i3c_slave_ctrl_set_config() to
> +   write i3c configuration to the slave controller.
> +
> +* i3c_slave_ctrl_enable()/i3c_slave_ctrl_disable()
> +
> +   The I3C slave function driver should use i3c_slave_ctrl_enable()/
> +   i3c_slave_ctrl_disable() to enable/disable i3c slave controller.
> +
> +* i3c_slave_ctrl_alloc_request()/i3c_slave_ctrl_free_request()
> +
> +   The I3C slave function driver should usei3c_slave_ctrl_alloc_request() /
> +   i3c_slave_ctrl_free_request() to alloc/free a i3c request.
> +
> +* i3c_slave_ctrl_raise_ibi()
> +
> +   The I3C slave function driver should use i3c_slave_ctrl_raise_ibi() to
> +   raise IBI.
> +
> +* i3c_slave_ctrl_queue()/i3c_slave_ctrl_dequeue()
> +
> +   The I3C slave function driver should use i3c_slave_ctrl_queue()/
> +   i3c_slave_ctrl_dequeue(), to queue/dequeue I3C transfer to/from transfer
> +   queue.
> +
> +* i3c_slave_ctrl_get_features()
> +
> +   The I3C slave function driver should use i3c_slave_ctrl_get_features()
> +   to get I3C slave controller supported features.
> +
> +Other I3C Slave Controller APIs
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +There are other APIs provided by the Controller library. These are used for
> +binding the I3C Slave Function device with Controlller device. i3c-cfs.c can
> +be used as reference for using these APIs.
> +
> +* i3c_slave_ctrl_get()
> +
> +   Get a reference to the I3C slave controller based on the device name of
> +   the controller.
> +
> +* i3c_slave_ctrl_put()
> +
> +   Release the reference to the I3C slave controller obtained using
> +   i3c_slave_ctrl_get()
> +
> +* i3c_slave_ctrl_add_func()
> +
> +   Add a I3C slave function to a I3C slave controller.
> +
> +* i3c_slave_ctrl_remove_func()
> +
> +   Remove the I3C slave function from I3C slave controller.
> +
> +I3C Slave Function Library
> +----------------------------------
> +
> +The I3C Slave Function library provides APIs to be used by the function driver
> +and the Controller library to provide slave mode functionality.
> +
> +I3C Slave Function APIs for the I3C Slave Function Driver
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +This section lists the APIs that the I3C Slave core provides to be used
> +by the I3C slave function driver.
> +
> +* i3c_slave_func_register_driver()
> +
> +   The I3C Slave Function driver should implement the following ops:
> +	 * bind: ops to perform when a Controller device has been bound to
> +	   Function device
> +	 * unbind: ops to perform when a binding has been lost between a
> +	   Controller device and Function device
> +
> +  The I3C Function driver can then register the I3C Function driver by using
> +  i3c_slave_func_register_driver().
> +
> +* i3c_slave_func_unregister_driver()
> +
> +  The I3C Function driver can unregister the I3C Function driver by using
> +  i3c_epf_unregister_driver().
> +
> +APIs for the I3C Slave Controller Library
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +This section lists the APIs that the I3C Slave core provides to be used
> +by the I3C slave controller library.
> +
> +Other I3C Slave APIs
> +~~~~~~~~~~~~~~~~~~~~
> +
> +There are other APIs provided by the Function library. These are used to
> +notify the function driver when the Function device is bound to the EPC device.
> +i3c-cfs.c can be used as reference for using these APIs.
> +
> +* i3c_slave_func_create()
> +
> +   Create a new I3C Function device by passing the name of the I3C EPF device.
> +   This name will be used to bind the Function device to a Function driver.
> +
> +* i3c_slave_func_destroy()
> +
> +   Destroy the created I3C Function device.
> +
> +* i3c_slave_func_bind()
> +
> +   i3c_slave_func_bind() should be invoked when the EPF device has been bound
> +   to a Controller device.
> +
> +* i3c_slave_func_unbind()
> +
> +   i3c_slave_func_unbind() should be invoked when the binding between EPC
> +   device and function device is lost.
> diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-function.rst b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst
> new file mode 100644
> index 0000000000000..3c8521d7aa31a
> --- /dev/null
> +++ b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst
> @@ -0,0 +1,103 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +=================
> +PCI Test Function
> +=================
> +
> +:Author: Kishon Vijay Abraham I <kishon at ti.com>
> +
> +Traditionally PCI RC has always been validated by using standard
> +PCI cards like ethernet PCI cards or USB PCI cards or SATA PCI cards.
> +However with the addition of EP-core in linux kernel, it is possible
> +to configure a PCI controller that can operate in EP mode to work as
> +a test device.
> +
> +The PCI endpoint test device is a virtual device (defined in software)
> +used to test the endpoint functionality and serve as a sample driver
> +for other PCI endpoint devices (to use the EP framework).
> +
> +The PCI endpoint test device has the following registers:
> +
> +	1) PCI_ENDPOINT_TEST_MAGIC
> +	2) PCI_ENDPOINT_TEST_COMMAND
> +	3) PCI_ENDPOINT_TEST_STATUS
> +	4) PCI_ENDPOINT_TEST_SRC_ADDR
> +	5) PCI_ENDPOINT_TEST_DST_ADDR
> +	6) PCI_ENDPOINT_TEST_SIZE
> +	7) PCI_ENDPOINT_TEST_CHECKSUM
> +	8) PCI_ENDPOINT_TEST_IRQ_TYPE
> +	9) PCI_ENDPOINT_TEST_IRQ_NUMBER
> +
> +* PCI_ENDPOINT_TEST_MAGIC
> +
> +This register will be used to test BAR0. A known pattern will be written
> +and read back from MAGIC register to verify BAR0.
> +
> +* PCI_ENDPOINT_TEST_COMMAND
> +
> +This register will be used by the host driver to indicate the function
> +that the endpoint device must perform.
> +
> +========	================================================================
> +Bitfield	Description
> +========	================================================================
> +Bit 0		raise legacy IRQ
> +Bit 1		raise MSI IRQ
> +Bit 2		raise MSI-X IRQ
> +Bit 3		read command (read data from RC buffer)
> +Bit 4		write command (write data to RC buffer)
> +Bit 5		copy command (copy data from one RC buffer to another RC buffer)
> +========	================================================================
> +
> +* PCI_ENDPOINT_TEST_STATUS
> +
> +This register reflects the status of the PCI endpoint device.
> +
> +========	==============================
> +Bitfield	Description
> +========	==============================
> +Bit 0		read success
> +Bit 1		read fail
> +Bit 2		write success
> +Bit 3		write fail
> +Bit 4		copy success
> +Bit 5		copy fail
> +Bit 6		IRQ raised
> +Bit 7		source address is invalid
> +Bit 8		destination address is invalid
> +========	==============================
> +
> +* PCI_ENDPOINT_TEST_SRC_ADDR
> +
> +This register contains the source address (RC buffer address) for the
> +COPY/READ command.
> +
> +* PCI_ENDPOINT_TEST_DST_ADDR
> +
> +This register contains the destination address (RC buffer address) for
> +the COPY/WRITE command.
> +
> +* PCI_ENDPOINT_TEST_IRQ_TYPE
> +
> +This register contains the interrupt type (Legacy/MSI) triggered
> +for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands.
> +
> +Possible types:
> +
> +======	==
> +Legacy	0
> +MSI	1
> +MSI-X	2
> +======	==
> +
> +* PCI_ENDPOINT_TEST_IRQ_NUMBER
> +
> +This register contains the triggered ID interrupt.
> +
> +Admissible values:
> +
> +======	===========
> +Legacy	0
> +MSI	[1 .. 32]
> +MSI-X	[1 .. 2048]
> +======	===========
> diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
> new file mode 100644
> index 0000000000000..11c8900fd16f3
> --- /dev/null
> +++ b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst
> @@ -0,0 +1,109 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +===================
> +I3C TTY User Guide
> +===================
> +
> +:Author: Frank Li <Frank.Li at nxp.com>
> +
> +This document is a guide to help users use i3c-slave-tty function driver
> +and i3ctty master driver for testing I3C. The list of steps to be followed in the
> +master side and slave side is given below.
> +
> +Endpoint Device
> +===============
> +
> +Endpoint Controller Devices
> +---------------------------
> +
> +To find the list of slave controller devices in the system::
> +
> +	# ls  /sys/class/i3c_slave/
> +	  44330000.i3c-slave
> +
> +If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
> +
> +	# ls /sys/kernel/config/i3c_slave/controllers/
> +	  44330000.i3c-slave
> +
> +
> +Endpoint Function Drivers
> +-------------------------
> +
> +To find the list of slave function drivers in the system::
> +
> +	# ls /sys/bus/i3c_slave_func/drivers
> +	  tty
> +
> +If CONFIG_I3C_SLAVE_CONFIGFS is enabled::
> +
> +	# ls /sys/kernel/config/i3c_slave/functions
> +	  tty
> +
> +
> +Creating i3c-slave-tty Device
> +----------------------------
> +
> +I3C slave function device can be created using the configfs. To create
> +i3c-slave-tty device, the following commands can be used::
> +
> +	# mount -t configfs none /sys/kernel/config
> +	# cd /sys/kernel/config/i3c_slave/
> +	# mkdir functions/tty/func1
> +
> +The "mkdir func1" above creates the i3c-slave-tty function device that will
> +be probed by i3c tty driver.
> +
> +The I3C slave framework populates the directory with the following
> +configurable fields::
> +
> +	# ls functions/tty/func1
> +	bcr  dcr  ext_id  instance_id  max_read_len  max_write_len
> +	part_id  vendor_id  vendor_info
> +
> +The I3C slave function driver populates these entries with default values
> +when the device is bound to the driver. The i3c-slave-tty driver populates
> +vendorid with 0xffff and interrupt_pin with 0x0001::
> +
> +	# cat functions/tty/func1/vendor_id
> +	  0x0
> +
> +Configuring i3c-slave-tty Device
> +-------------------------------
> +
> +The user can configure the i3c-slave-tty device using configfs entry. In order
> +to change the vendorid, the following commands can be used::
> +
> +	# echo 0x011b > functions/tty/func1/vendor_id
> +	# echo 0x1000 > functions/tty/func1/part_id
> +	# echo 0x6 > functions/tty/t/bcr
> +
> +Binding i3c-slave-tty Device to slave Controller
> +------------------------------------------------
> +
> +In order for the slave function device to be useful, it has to be bound to
> +a I3C slave controller driver. Use the configfs to bind the function
> +device to one of the controller driver present in the system::
> +
> +	# ln -s functions/pci_epf_test/func1 controllers/44330000.i3c-slave/
> +
> +I3C Master Device
> +================
> +
> +Check I3C tty device is probed
> +
> +	# ls /sys/bus/i3c/devices/0-23610000000
> +	0-23610000000:0  bcr  dcr  driver  dynamic_address  hdrcap
> +	modalias  pid  power  subsystem  tty  uevent
> +
> +Using Slave TTY function Device
> +-----------------------------------
> +
> +Host side:
> +	cat /dev/ttyI3C0
> +Slave side
> +	echo abc >/dev/ttyI3C0
> +
> +You will see "abc" show at console.
> +
> +You can use other tty tool to test I3C slave tty device.
> diff --git a/Documentation/driver-api/i3c/slave/index.rst b/Documentation/driver-api/i3c/slave/index.rst
> new file mode 100644
> index 0000000000000..69727ccf985db
> --- /dev/null
> +++ b/Documentation/driver-api/i3c/slave/index.rst
> @@ -0,0 +1,13 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +======================
> +I3C Slave Framework
> +======================
> +
> +.. toctree::
> +   :maxdepth: 2
> +
> +   i3c-slave
> +   i3c-slave-cfs
> +   i3c-tty-howto
> +
> -- 
> 2.34.1
> 


From miquel.raynal at bootlin.com  Wed Oct 18 23:29:16 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Thu, 19 Oct 2023 08:29:16 +0200
Subject: [PATCH v2 Resent 3/6] i3c: master: svc: fix ibi may not return
 mandatory data byte
In-Reply-To: <20231018155926.3305476-4-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
	<20231018155926.3305476-4-Frank.Li@nxp.com>
Message-ID: <20231019082916.25a536fd@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:23 -0400:

> MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
> creates an issue when the I3C clock is slow, and the CPU is running fast
> enough that MSTATUS[RXPEND] may not be updated when the code reach checking
> point. As a result, mandatory data are being missed.

reaches

can be missed

> Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data is
> already in FIFO.
> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>     Change from v1 to v2
>     - update commit message
>       it also works without mandatory bytes

This could be mentioned in the commit log?

> 
>  drivers/i3c/master/svc-i3c-master.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index abebef666b2bb..dd06b7c9333f1 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -332,6 +332,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
>  	struct i3c_ibi_slot *slot;
>  	unsigned int count;
>  	u32 mdatactrl;
> +	int ret, val;
>  	u8 *buf;
>  
>  	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
> @@ -341,6 +342,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
>  	slot->len = 0;
>  	buf = slot->data;
>  
> +	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
> +						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
> +	if (ret) {
> +		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
> +		return ret;
> +	}
> +
>  	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
>  	       slot->len < SVC_I3C_FIFO_SIZE) {
>  		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Wed Oct 18 23:31:51 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Thu, 19 Oct 2023 08:31:51 +0200
Subject: [PATCH v2 Resent 5/6] i3c: master: svc: fix SDA keep low when
 polling IBIWON timeout happen
In-Reply-To: <20231018155926.3305476-6-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
	<20231018155926.3305476-6-Frank.Li@nxp.com>
Message-ID: <20231019083151.3bd10536@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:25 -0400:

> If IBIWON timeout happen, SDA line will always keep low because miss emit
> stop.

"
Upon IBIWON timeout, the SDA line will always be kept low if we don't
emit a stop. Calling svc_i3c_master_emit_stop() there will let the bus
return to idle state.
"

With this commit log,

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

> 
> Call svc_i3c_master_emit_stop() to let i3c bus come back to idle statue
> when IBIWON timeout happen.
> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>     Change from v1 to v2
>     - Update commite message
> 
>  drivers/i3c/master/svc-i3c-master.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index b113460f059c3..1a57fdebaa26d 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -404,6 +404,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
>  					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
>  	if (ret) {
>  		dev_err(master->dev, "Timeout when polling for IBIWON\n");
> +		svc_i3c_master_emit_stop(master);
>  		goto reenable_ibis;
>  	}
>  


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Wed Oct 18 23:44:52 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Thu, 19 Oct 2023 08:44:52 +0200
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <20231018155926.3305476-7-Frank.Li@nxp.com>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
	<20231018155926.3305476-7-Frank.Li@nxp.com>
Message-ID: <20231019084452.11fd0645@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:

> master side report:
>   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> 
> BIT 20: TIMEOUT error
>   The module has stalled too long in a frame. This happens when:
>   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> middle of a message,
>   - No STOP was issued and between messages,
>   - IBI manual is used and no decision was made.

I am still not convinced this should be ignored in all cases.

Case 1 is a problem because the hardware failed somehow.
Case 2 is fine I guess.
Case 3 is not possible in Linux, this will not be supported.

>   The maximum stall period is 10 KHz or 100 ?s.

s/10 KHz//

> 
> This is a just warning. System irq thread schedule latency is possible
> bigger than 100us. Just omit this waring.

This can be considered as being just a warning as the system IRQ
latency can easily be greater than 100us.

> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>     Change from v1 to v2
>     -none
> 
>  drivers/i3c/master/svc-i3c-master.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 1a57fdebaa26d..fedb31e0076c4 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -93,6 +93,7 @@
>  #define SVC_I3C_MINTMASKED   0x098
>  #define SVC_I3C_MERRWARN     0x09C
>  #define   SVC_I3C_MERRWARN_NACK BIT(2)
> +#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
>  #define SVC_I3C_MDMACTRL     0x0A0
>  #define SVC_I3C_MDATACTRL    0x0AC
>  #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
> @@ -226,6 +227,11 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
>  	if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
>  		merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
>  		writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
> +
> +		/* ignore timeout error */
> +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> +			return false;
> +
>  		dev_err(master->dev,
>  			"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
>  			mstatus, merrwarn);


Thanks,
Miqu?l


From krzysztof.kozlowski at linaro.org  Thu Oct 19 00:00:05 2023
From: krzysztof.kozlowski at linaro.org (Krzysztof Kozlowski)
Date: Thu, 19 Oct 2023 09:00:05 +0200
Subject: [PATCH 1/5] i3c: add slave mode support
In-Reply-To: <20231018215809.3477437-2-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
 <20231018215809.3477437-2-Frank.Li@nxp.com>
Message-ID: <dd06c2d3-e273-4356-835b-42619543dfab@linaro.org>

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 <Frank.Li at nxp.com>
> ---
>  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



From krzysztof.kozlowski at linaro.org  Thu Oct 19 00:00:48 2023
From: krzysztof.kozlowski at linaro.org (Krzysztof Kozlowski)
Date: Thu, 19 Oct 2023 09:00:48 +0200
Subject: [PATCH 2/5] dt-bindings: i3c: svc: add compatible string i3c:
 silvaco,i3c-slave
In-Reply-To: <20231018215809.3477437-3-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
 <20231018215809.3477437-3-Frank.Li@nxp.com>
Message-ID: <0e39b69b-c7ed-4fde-a050-7edf9a052e03@linaro.org>

On 18/10/2023 23:58, Frank Li wrote:
> Add compatible string 'silvaco,i3c-slave' for slave mode.
> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>  .../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 <conor.culhane at silvaco.com>
> @@ -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



From robh at kernel.org  Thu Oct 19 05:07:10 2023
From: robh at kernel.org (Rob Herring)
Date: Thu, 19 Oct 2023 07:07:10 -0500
Subject: [PATCH 2/5] dt-bindings: i3c: svc: add compatible string i3c:
 silvaco,i3c-slave
In-Reply-To: <20231018215809.3477437-3-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
 <20231018215809.3477437-3-Frank.Li@nxp.com>
Message-ID: <169771723040.549216.11346182362736118901.robh@kernel.org>


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 <Frank.Li at nxp.com>
> ---
>  .../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 at 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 at 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/20231018215809.3477437-3-Frank.Li at nxp.com

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.



From Frank.li at nxp.com  Thu Oct 19 07:49:08 2023
From: Frank.li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 10:49:08 -0400
Subject: [PATCH 1/1] tty: i3c: add tty over i3c master support
In-Reply-To: <caa8d113-3182-4f5f-affa-e5f8037498db@kernel.org>
References: <20231018211111.3437929-1-Frank.Li@nxp.com>
 <caa8d113-3182-4f5f-affa-e5f8037498db@kernel.org>
Message-ID: <ZTFB5FZiPBaguhlO@lizhi-Precision-Tower-5810>

On Thu, Oct 19, 2023 at 09:12:58AM +0200, Jiri Slaby wrote:
> On 18. 10. 23, 23:11, Frank Li wrote:
> > Add new driver to allow tty over i3c master.
> 
> What is it good for? Why we should consider this for inclusion at all?

UART console at least need TX/RX two pin. If we can use I3C as linux
console, the board will save two PAD because we always use I3C to connected
into some sensor or PMIC chip. 

Some small chip package, PIN is very valuable.

> 
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> > 
> > Notes:
> >      This patch depend on
> >      https://lore.kernel.org/imx/20231018205929.3435110-1-Frank.Li at nxp.com/T/#t
> > 
> >   drivers/tty/Kconfig   |   8 +
> >   drivers/tty/Makefile  |   1 +
> >   drivers/tty/i3c_tty.c | 466 ++++++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 475 insertions(+)
> >   create mode 100644 drivers/tty/i3c_tty.c
> > 
> > diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
> > index 5646dc6242cd9..6d91fe6a211a1 100644
> > --- a/drivers/tty/Kconfig
> > +++ b/drivers/tty/Kconfig
> > @@ -412,6 +412,14 @@ config RPMSG_TTY
> >   	  To compile this driver as a module, choose M here: the module will be
> >   	  called rpmsg_tty.
> > +config I3C_TTY
> > +	tristate "tty over i3c"
> 
> "TTY over I3C"
> 
> > +	depends on I3C
> > +	help
> > +	  Select this options if you'd like use tty over I3C master controller
> 
> TTY and add a period.
> 
> > --- /dev/null
> > +++ b/drivers/tty/i3c_tty.c
> > @@ -0,0 +1,466 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2023 NXP.
> > + *
> > + * Author: Frank Li <Frank.Li at nxp.com>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/module.h>
> > +#include <linux/i3c/device.h>
> > +#include <linux/i3c/master.h>
> > +#include <linux/slab.h>
> > +#include <linux/console.h>
> > +#include <linux/serial_core.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/workqueue.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_TTY_TRANS_SIZE	16
> > +#define I3C_TTY_RX_STOP		BIT(0)
> > +#define I3C_TTY_RETRY		20
> > +#define I3C_TTY_YIELD_US	100
> > +
> > +struct ttyi3c_port {
> > +	struct tty_port port;
> > +	int minor;
> > +	unsigned int txfifo_size;
> > +	unsigned int rxfifo_size;
> > +	struct circ_buf xmit;
> 
> Why can't you use port.xmit_fifo?
> 
> > +	spinlock_t xlock; /* protect xmit */
> > +	void *buffer;
> > +	struct i3c_device *i3cdev;
> > +	struct work_struct txwork;
> > +	struct work_struct rxwork;
> > +	struct completion txcomplete;
> > +	struct workqueue_struct *workqueue;
> 
> Why do you need a special wq? And even, why per port?

You are right! Do you any 'common wq' perfered? 

> 
> > +	atomic_t status;
> > +};
> > +
> > +static const struct i3c_device_id i3c_ids[] = {
> > +	I3C_DEVICE(0x011B, 0x1000, NULL),
> > +	{ /* sentinel */ },
> > +};
> > +
> > +static int i3c_port_activate(struct tty_port *port, struct tty_struct *tty)
> > +{
> > +	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
> > +
> > +	atomic_set(&sport->status, 0);
> > +
> > +	return i3c_device_enable_ibi(sport->i3cdev);
> > +}
> > +
> > +static void i3c_port_shutdown(struct tty_port *port)
> > +{
> > +	struct ttyi3c_port *sport =
> > +		container_of(port, struct ttyi3c_port, port);
> > +
> > +	i3c_device_disable_ibi(sport->i3cdev);
> > +}
> > +
> > +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 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;
> > +}
> 
> You don't need this at all. (Watch for XXX marks below.)
> 
> > +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;
> > +	}
> > +	spin_unlock_irqrestore(&sport->xlock, flags);
> 
> With kfifo, all this would be one line, right?
> 
> > +
> > +	if (circ->head != circ->tail)
> > +		queue_work(sport->workqueue, &sport->txwork);
> > +
> > +	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;
> > +
> > +	queue_work(sport->workqueue, &sport->txwork);
> > +}
> > +
> > +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;
> > +
> > +	atomic_or(I3C_TTY_RX_STOP, &sport->status);
> > +}
> > +
> > +static void i3c_unthrottle(struct tty_struct *tty)
> > +{
> > +	struct ttyi3c_port *sport = tty->driver_data;
> > +
> > +	atomic_andnot(I3C_TTY_RX_STOP, &sport->status);
> > +
> > +	queue_work(sport->workqueue, &sport->rxwork);
> > +}
> > +
> > +static int i3c_open(struct tty_struct *tty, struct file *filp)
> > +{
> > +	struct ttyi3c_port *sport = tty->driver_data;
> 
> XXX Here, you can simply do:
> 
> struct ttyi3c_port *sport = container_of(tty->port, struct ttyi3c_port,
> port);
> 
> tty->driver_data = sport;
> 
> 
> > +	return tty_port_open(&sport->port, tty, filp);
> > +}
> > +
> > +static void i3c_close(struct tty_struct *tty, struct file *filp)
> > +{
> > +	struct ttyi3c_port *sport = tty->driver_data;
> > +
> > +	if (!sport)
> > +		return;
> 
> How can that happen?
> 
> > +	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;
> > +
> > +	wait_for_completion_timeout(&sport->txcomplete, timeout);
> > +	reinit_completion(&sport->txcomplete);
> 
> Does this work in parallel?

I am not clear! What's your concern? txcmplete should be by tx work thread.

> 
> > +}
> > +
> > +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,
> > +};
> > +
> > +static void i3c_controller_irq_handler(struct i3c_device *dev,
> > +				       const struct i3c_ibi_payload *payload)
> > +{
> > +	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
> > +
> > +	queue_work(sport->workqueue, &sport->rxwork);
> 
> Doesn't ibi provide threaded irqs? Oh, wait, i3c_master_handle_ibi() is
> already a work!

Yes, but I need stop rxwork when i3c_unthrottle and start rxwork at
i3c_throttle. 
I am not sure if throttle/unthrottle allow sleep. I refer other drivers,
most just wakeup or disable some simple works.

> 
> > +}
> > +
> > +static void tty_i3c_rxwork(struct work_struct *work)
> > +{
> > +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, rxwork);
> > +	struct i3c_priv_xfer xfers;
> > +	int retry = I3C_TTY_RETRY;
> > +	u16 status = BIT(0);
> > +
> > +	do {
> > +		memset(&xfers, 0, sizeof(xfers));
> > +		xfers.data.in = sport->buffer;
> > +		xfers.len = I3C_TTY_TRANS_SIZE;
> > +		xfers.rnw = 1;
> > +
> > +		if (I3C_TTY_RX_STOP & atomic_read(&sport->status))
> 
> Hmm, why not much simpler (and yet atomic) {set,test,clear}_bit()?
> 
> > +			break;
> > +
> > +		i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> > +
> > +		if (xfers.actual_len) {
> > +			tty_insert_flip_string(&sport->port, sport->buffer, xfers.actual_len);
> 
> This can fail.
> 
> > +			retry = 20;
> > +			continue;
> > +		} else {
> 
> "} else {" uneeded.
> 
> > +			status = BIT(0);
> > +			i3c_device_getstatus_format1(sport->i3cdev, &status);
> > +			/*
> > +			 * Target side need some time to fill data into fifo. Target side may not
> 
> "needs"
> 
> > +			 * have hardware update status in real time. Software update status always
> > +			 * need some delays.
> 
> "needs"
> 
> > +			 *
> > +			 * Generally, target side have cicular buffer in memory, it will be moved
> "circular" all around.
> 
> > +			 * into FIFO by CPU or DMA. 'status' just show if cicular buffer empty. But
> > +			 * there are gap, espcially CPU have not response irq to fill FIFO in time.
> 
> espcially
> 
> > +			 * So xfers.actual will be zero, wait for little time to avoid flood
> > +			 * transfer in i3c bus.
> > +			 */
> > +			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> > +			retry--;
> > +		}
> > +
> > +	} while (retry && (status & BIT(0)));
> > +
> > +	tty_flip_buffer_push(&sport->port);
> > +}
> > +
> > +static void tty_i3c_txwork(struct work_struct *work)
> > +{
> > +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, txwork);
> > +	struct circ_buf *circ = &sport->xmit;
> > +	int cnt = CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE);
> > +	struct i3c_priv_xfer xfers;
> > +	int retry = I3C_TTY_RETRY;
> > +	unsigned long flags;
> > +	int actual;
> > +	int ret;
> > +
> > +	while (cnt > 0 && retry) {
> > +		xfers.rnw = 0;
> > +		xfers.len = CIRC_CNT_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
> > +		xfers.len = min_t(u16, 16, xfers.len);
> > +		xfers.data.out = circ->buf + circ->tail;
> > +
> > +		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> > +		if (ret) {
> > +			/*
> > +			 * Target side may not move data out of FIFO. delay can't resolve problem,
> > +			 * just reduce some possiblity. Target can't end I3C SDR mode write
> > +			 * transfer, discard data is reasonable when FIFO overrun.
> > +			 */
> > +			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> > +			retry--;
> > +		} else {
> > +			retry = 0;
> > +		}
> > +
> > +		actual = xfers.len;
> > +
> > +		circ->tail = (circ->tail + actual) & (UART_XMIT_SIZE - 1);
> > +
> > +		if (CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE) < WAKEUP_CHARS)
> > +			tty_port_tty_wakeup(&sport->port);
> > +
> > +		cnt = CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE);
> > +	}
> > +
> > +	spin_lock_irqsave(&sport->xlock, flags);
> > +	if (circ->head == circ->tail)
> > +		complete(&sport->txcomplete);
> > +	spin_unlock_irqrestore(&sport->xlock, flags);
> > +}
> > +
> > +static int i3c_probe(struct i3c_device *i3cdev)
> > +{
> > +	struct ttyi3c_port *port;
> > +	struct device *tty_dev;
> > +	struct i3c_ibi_setup req;
> > +	int minor;
> > +	int ret;
> > +
> > +	port = devm_kzalloc(&i3cdev->dev, sizeof(*port), GFP_KERNEL);
> > +	if (!port)
> > +		return -ENOMEM;
> > +
> > +	port->i3cdev = i3cdev;
> > +	port->buffer = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
> > +	if (!port->buffer)
> > +		return -ENOMEM;
> > +
> > +	port->xmit.buf = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
> > +	if (!port->xmit.buf)
> > +		return -ENOMEM;
> 
> You allocate two pages even if you never use the device?
> 
> > +	dev_set_drvdata(&i3cdev->dev, port);
> > +
> > +	req.max_payload_len = 8;
> > +	req.num_slots = 4;
> > +	req.handler = &i3c_controller_irq_handler;
> > +
> > +	ret = i3c_device_request_ibi(i3cdev, &req);
> > +	if (ret)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&i3c_tty_minors_lock);
> > +	minor = idr_alloc(&i3c_tty_minors, port, 0, I3C_TTY_MINORS, GFP_KERNEL);
> > +	mutex_unlock(&i3c_tty_minors_lock);
> > +
> > +	if (minor < 0)
> > +		return -EINVAL;
> > +
> > +	spin_lock_init(&port->xlock);
> > +	INIT_WORK(&port->txwork, tty_i3c_txwork);
> > +	INIT_WORK(&port->rxwork, tty_i3c_rxwork);
> > +	init_completion(&port->txcomplete);
> > +
> > +	port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&i3cdev->dev));
> > +	if (!port->workqueue)
> > +		return -ENOMEM;
> > +
> > +	tty_port_init(&port->port);
> > +	port->port.ops = &i3c_port_ops;
> > +
> > +	tty_dev = tty_port_register_device(&port->port, i3c_tty_driver, minor,
> > +					   &i3cdev->dev);
> > +	if (IS_ERR(tty_dev)) {
> > +		destroy_workqueue(port->workqueue);
> 
> What about tty_port_put() (it drops the idr too)? And free ibi?
> 
> > +		return PTR_ERR(tty_dev);
> > +	}
> > +
> > +	port->minor = minor;
> > +
> > +	return 0;
> > +}
> > +
> > +void i3c_remove(struct i3c_device *dev)
> > +{
> > +	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
> > +
> > +	tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
> > +	cancel_work_sync(&sport->txwork);
> > +	destroy_workqueue(sport->workqueue);
> 
> The same as above.
> 
> > +}
> > +
> > +static struct i3c_driver i3c_driver = {
> > +	.driver = {
> > +		.name = "ttyi3c",
> > +	},
> > +	.probe = i3c_probe,
> > +	.remove = i3c_remove,
> > +	.id_table = i3c_ids,
> > +};
> > +
> > +static int __init i3c_tty_init(void)
> > +{
> > +	int ret;
> > +
> > +	i3c_tty_driver = tty_alloc_driver(I3C_TTY_MINORS,
> > +					  TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
> > +
> > +	if (IS_ERR(i3c_tty_driver))
> > +		return PTR_ERR(i3c_tty_driver);
> > +
> > +	i3c_tty_driver->driver_name = "ttyI3C";
> > +	i3c_tty_driver->name = "ttyI3C";
> > +	i3c_tty_driver->minor_start = 0,
> > +	i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
> > +	i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
> > +	i3c_tty_driver->init_termios = tty_std_termios;
> > +	i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
> > +					       CLOCAL;
> > +	i3c_tty_driver->init_termios.c_lflag = 0;
> > +
> > +	tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
> > +
> > +	ret = tty_register_driver(i3c_tty_driver);
> > +	if (ret) {
> > +		tty_driver_kref_put(i3c_tty_driver);
> > +		return ret;
> > +	}
> > +
> > +	ret = i3c_driver_register(&i3c_driver);
> > +	if (ret) {
> > +		tty_unregister_driver(i3c_tty_driver);
> > +		tty_driver_kref_put(i3c_tty_driver);
> 
> Use goto + fail paths. And in i3c_probe() too.
> 
> > +	}
> > +
> > +	return ret;
> > +}
> 
> 
> regards,
> -- 
> js
> suse labs
> 


From Frank.li at nxp.com  Thu Oct 19 08:02:10 2023
From: Frank.li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 11:02:10 -0400
Subject: [PATCH 1/5] i3c: add slave mode support
In-Reply-To: <dd06c2d3-e273-4356-835b-42619543dfab@linaro.org>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
 <20231018215809.3477437-2-Frank.Li@nxp.com>
 <dd06c2d3-e273-4356-835b-42619543dfab@linaro.org>
Message-ID: <ZTFE8hrRgPBrb7e3@lizhi-Precision-Tower-5810>

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 <Frank.Li at nxp.com>
> > ---
> >  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
> 


From gregkh at linuxfoundation.org  Thu Oct 19 08:38:55 2023
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Thu, 19 Oct 2023 17:38:55 +0200
Subject: [PATCH 1/1] tty: i3c: add tty over i3c master support
In-Reply-To: <20231018211111.3437929-1-Frank.Li@nxp.com>
References: <20231018211111.3437929-1-Frank.Li@nxp.com>
Message-ID: <2023101906-rimmed-unmarked-0ba2@gregkh>

On Wed, Oct 18, 2023 at 05:11:09PM -0400, Frank Li wrote:
> Add new driver to allow tty over i3c master.

As Jiri said, you need a lot more description here before we can even
start to review it.

Please fix up and send a v2.

thanks,

greg k-h


From Frank.li at nxp.com  Thu Oct 19 08:39:42 2023
From: Frank.li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 11:39:42 -0400
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <20231019084452.11fd0645@xps-13>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
 <20231018155926.3305476-7-Frank.Li@nxp.com>
 <20231019084452.11fd0645@xps-13>
Message-ID: <ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>

On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> 
> > master side report:
> >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > 
> > BIT 20: TIMEOUT error
> >   The module has stalled too long in a frame. This happens when:
> >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > middle of a message,
> >   - No STOP was issued and between messages,
> >   - IBI manual is used and no decision was made.
> 
> I am still not convinced this should be ignored in all cases.
> 
> Case 1 is a problem because the hardware failed somehow.

But so far, no action to handle this case in current code.

In svc_i3c_master_xfer() have not check this flags. also have not enable
ERRWARN irq.

If we met this case, we can add new functions/argument to handle this.
Then we can real debug the code and recover bus.

Without this patch, simplest add some debug message before issue
SVC_I3C_MCTRL_REQUEST_AUTO_IBI, TIMEOUT will be set.

And svc_i3c_master_error() was only called by svc_i3c_master_ibi_work().
So I can think only case 3 happen in svc_i3c_master_ibi_work().

Frank

> Case 2 is fine I guess.
> Case 3 is not possible in Linux, this will not be supported.
> 
> >   The maximum stall period is 10 KHz or 100 ?s.
> 
> s/10 KHz//
> 
> > 
> > This is a just warning. System irq thread schedule latency is possible
> > bigger than 100us. Just omit this waring.
> 
> This can be considered as being just a warning as the system IRQ
> latency can easily be greater than 100us.
> 
> > 
> > Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> > Cc: stable at vger.kernel.org
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> > 
> > Notes:
> >     Change from v1 to v2
> >     -none
> > 
> >  drivers/i3c/master/svc-i3c-master.c | 6 ++++++
> >  1 file changed, 6 insertions(+)
> > 
> > diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> > index 1a57fdebaa26d..fedb31e0076c4 100644
> > --- a/drivers/i3c/master/svc-i3c-master.c
> > +++ b/drivers/i3c/master/svc-i3c-master.c
> > @@ -93,6 +93,7 @@
> >  #define SVC_I3C_MINTMASKED   0x098
> >  #define SVC_I3C_MERRWARN     0x09C
> >  #define   SVC_I3C_MERRWARN_NACK BIT(2)
> > +#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
> >  #define SVC_I3C_MDMACTRL     0x0A0
> >  #define SVC_I3C_MDATACTRL    0x0AC
> >  #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
> > @@ -226,6 +227,11 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
> >  	if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
> >  		merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
> >  		writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
> > +
> > +		/* ignore timeout error */
> > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > +			return false;
> > +
> >  		dev_err(master->dev,
> >  			"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> >  			mstatus, merrwarn);
> 
> 
> Thanks,
> Miqu?l


From gregkh at linuxfoundation.org  Thu Oct 19 08:46:42 2023
From: gregkh at linuxfoundation.org (Greg KH)
Date: Thu, 19 Oct 2023 17:46:42 +0200
Subject: [PATCH 1/5] i3c: add slave mode support
In-Reply-To: <ZTFE8hrRgPBrb7e3@lizhi-Precision-Tower-5810>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
 <20231018215809.3477437-2-Frank.Li@nxp.com>
 <dd06c2d3-e273-4356-835b-42619543dfab@linaro.org>
 <ZTFE8hrRgPBrb7e3@lizhi-Precision-Tower-5810>
Message-ID: <2023101935-papaya-legible-5849@gregkh>

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 <Frank.Li at nxp.com>
> > > ---
> > >  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?


From gregkh at linuxfoundation.org  Thu Oct 19 10:06:49 2023
From: gregkh at linuxfoundation.org (Greg KH)
Date: Thu, 19 Oct 2023 19:06:49 +0200
Subject: [PATCH 1/5] i3c: add slave mode support
In-Reply-To: <2023101935-papaya-legible-5849@gregkh>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
 <20231018215809.3477437-2-Frank.Li@nxp.com>
 <dd06c2d3-e273-4356-835b-42619543dfab@linaro.org>
 <ZTFE8hrRgPBrb7e3@lizhi-Precision-Tower-5810>
 <2023101935-papaya-legible-5849@gregkh>
Message-ID: <2023101959-smile-oxidizing-7010@gregkh>

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 <Frank.Li at nxp.com>
> > > > ---
> > > >  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


From Frank.li at nxp.com  Thu Oct 19 13:21:34 2023
From: Frank.li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 16:21:34 -0400
Subject: [PATCH 1/1] tty: i3c: add tty over i3c master support
In-Reply-To: <caa8d113-3182-4f5f-affa-e5f8037498db@kernel.org>
References: <20231018211111.3437929-1-Frank.Li@nxp.com>
 <caa8d113-3182-4f5f-affa-e5f8037498db@kernel.org>
Message-ID: <ZTGPziszYR6xd8Ee@lizhi-Precision-Tower-5810>

On Thu, Oct 19, 2023 at 09:12:58AM +0200, Jiri Slaby wrote:
> On 18. 10. 23, 23:11, Frank Li wrote:
> > Add new driver to allow tty over i3c master.
> 
> What is it good for? Why we should consider this for inclusion at all?
> 
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> > 
> > Notes:
> >      This patch depend on
> >      https://lore.kernel.org/imx/20231018205929.3435110-1-Frank.Li at nxp.com/T/#t
> > 
> >   drivers/tty/Kconfig   |   8 +
> >   drivers/tty/Makefile  |   1 +
> >   drivers/tty/i3c_tty.c | 466 ++++++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 475 insertions(+)
> >   create mode 100644 drivers/tty/i3c_tty.c
> > 
> > diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
> > index 5646dc6242cd9..6d91fe6a211a1 100644
> > --- a/drivers/tty/Kconfig
> > +++ b/drivers/tty/Kconfig
> > @@ -412,6 +412,14 @@ config RPMSG_TTY
> >   	  To compile this driver as a module, choose M here: the module will be
> >   	  called rpmsg_tty.
> > +config I3C_TTY
> > +	tristate "tty over i3c"
> 
> "TTY over I3C"
> 
> > +	depends on I3C
> > +	help
> > +	  Select this options if you'd like use tty over I3C master controller
> 
> TTY and add a period.
> 
> > --- /dev/null
> > +++ b/drivers/tty/i3c_tty.c
> > @@ -0,0 +1,466 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright 2023 NXP.
> > + *
> > + * Author: Frank Li <Frank.Li at nxp.com>
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/module.h>
> > +#include <linux/i3c/device.h>
> > +#include <linux/i3c/master.h>
> > +#include <linux/slab.h>
> > +#include <linux/console.h>
> > +#include <linux/serial_core.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/workqueue.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_TTY_TRANS_SIZE	16
> > +#define I3C_TTY_RX_STOP		BIT(0)
> > +#define I3C_TTY_RETRY		20
> > +#define I3C_TTY_YIELD_US	100
> > +
> > +struct ttyi3c_port {
> > +	struct tty_port port;
> > +	int minor;
> > +	unsigned int txfifo_size;
> > +	unsigned int rxfifo_size;
> > +	struct circ_buf xmit;
> 
> Why can't you use port.xmit_fifo?
> 
> > +	spinlock_t xlock; /* protect xmit */
> > +	void *buffer;
> > +	struct i3c_device *i3cdev;
> > +	struct work_struct txwork;
> > +	struct work_struct rxwork;
> > +	struct completion txcomplete;
> > +	struct workqueue_struct *workqueue;
> 
> Why do you need a special wq? And even, why per port?
> 
> > +	atomic_t status;
> > +};
> > +
> > +static const struct i3c_device_id i3c_ids[] = {
> > +	I3C_DEVICE(0x011B, 0x1000, NULL),
> > +	{ /* sentinel */ },
> > +};
> > +
> > +static int i3c_port_activate(struct tty_port *port, struct tty_struct *tty)
> > +{
> > +	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
> > +
> > +	atomic_set(&sport->status, 0);
> > +
> > +	return i3c_device_enable_ibi(sport->i3cdev);
> > +}
> > +
> > +static void i3c_port_shutdown(struct tty_port *port)
> > +{
> > +	struct ttyi3c_port *sport =
> > +		container_of(port, struct ttyi3c_port, port);
> > +
> > +	i3c_device_disable_ibi(sport->i3cdev);
> > +}
> > +
> > +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 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;
> > +}
> 
> You don't need this at all. (Watch for XXX marks below.)
> 
> > +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;
> > +	}
> > +	spin_unlock_irqrestore(&sport->xlock, flags);
> 
> With kfifo, all this would be one line, right?
> 
> > +
> > +	if (circ->head != circ->tail)
> > +		queue_work(sport->workqueue, &sport->txwork);
> > +
> > +	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;
> > +
> > +	queue_work(sport->workqueue, &sport->txwork);
> > +}
> > +
> > +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;
> > +
> > +	atomic_or(I3C_TTY_RX_STOP, &sport->status);
> > +}
> > +
> > +static void i3c_unthrottle(struct tty_struct *tty)
> > +{
> > +	struct ttyi3c_port *sport = tty->driver_data;
> > +
> > +	atomic_andnot(I3C_TTY_RX_STOP, &sport->status);
> > +
> > +	queue_work(sport->workqueue, &sport->rxwork);
> > +}
> > +
> > +static int i3c_open(struct tty_struct *tty, struct file *filp)
> > +{
> > +	struct ttyi3c_port *sport = tty->driver_data;
> 
> XXX Here, you can simply do:
> 
> struct ttyi3c_port *sport = container_of(tty->port, struct ttyi3c_port,
> port);
> 
> tty->driver_data = sport;
> 
> 
> > +	return tty_port_open(&sport->port, tty, filp);
> > +}
> > +
> > +static void i3c_close(struct tty_struct *tty, struct file *filp)
> > +{
> > +	struct ttyi3c_port *sport = tty->driver_data;
> > +
> > +	if (!sport)
> > +		return;
> 
> How can that happen?
> 
> > +	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;
> > +
> > +	wait_for_completion_timeout(&sport->txcomplete, timeout);
> > +	reinit_completion(&sport->txcomplete);
> 
> Does this work in parallel?
> 
> > +}
> > +
> > +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,
> > +};
> > +
> > +static void i3c_controller_irq_handler(struct i3c_device *dev,
> > +				       const struct i3c_ibi_payload *payload)
> > +{
> > +	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
> > +
> > +	queue_work(sport->workqueue, &sport->rxwork);
> 
> Doesn't ibi provide threaded irqs? Oh, wait, i3c_master_handle_ibi() is
> already a work!

rxwork need be trigger by two sources: one from IBI irq, another is from
i3c_unthrottle(). 

If unthrottle just clear flags and IBI irq already missed, so rx will stop
work util new IBI coming.

I hope rxwork can continue to fetch left data. i3c_tty driver can't issue
a fake IBI here.

So using sperate rxwork. both IBI and unthrottle to trigger such work, make
rx can get all data from slave side.

Frank

> 
> > +}
> > +
> > +static void tty_i3c_rxwork(struct work_struct *work)
> > +{
> > +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, rxwork);
> > +	struct i3c_priv_xfer xfers;
> > +	int retry = I3C_TTY_RETRY;
> > +	u16 status = BIT(0);
> > +
> > +	do {
> > +		memset(&xfers, 0, sizeof(xfers));
> > +		xfers.data.in = sport->buffer;
> > +		xfers.len = I3C_TTY_TRANS_SIZE;
> > +		xfers.rnw = 1;
> > +
> > +		if (I3C_TTY_RX_STOP & atomic_read(&sport->status))
> 
> Hmm, why not much simpler (and yet atomic) {set,test,clear}_bit()?
> 
> > +			break;
> > +
> > +		i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> > +
> > +		if (xfers.actual_len) {
> > +			tty_insert_flip_string(&sport->port, sport->buffer, xfers.actual_len);
> 
> This can fail.
> 
> > +			retry = 20;
> > +			continue;
> > +		} else {
> 
> "} else {" uneeded.
> 
> > +			status = BIT(0);
> > +			i3c_device_getstatus_format1(sport->i3cdev, &status);
> > +			/*
> > +			 * Target side need some time to fill data into fifo. Target side may not
> 
> "needs"
> 
> > +			 * have hardware update status in real time. Software update status always
> > +			 * need some delays.
> 
> "needs"
> 
> > +			 *
> > +			 * Generally, target side have cicular buffer in memory, it will be moved
> "circular" all around.
> 
> > +			 * into FIFO by CPU or DMA. 'status' just show if cicular buffer empty. But
> > +			 * there are gap, espcially CPU have not response irq to fill FIFO in time.
> 
> espcially
> 
> > +			 * So xfers.actual will be zero, wait for little time to avoid flood
> > +			 * transfer in i3c bus.
> > +			 */
> > +			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> > +			retry--;
> > +		}
> > +
> > +	} while (retry && (status & BIT(0)));
> > +
> > +	tty_flip_buffer_push(&sport->port);
> > +}
> > +
> > +static void tty_i3c_txwork(struct work_struct *work)
> > +{
> > +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, txwork);
> > +	struct circ_buf *circ = &sport->xmit;
> > +	int cnt = CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE);
> > +	struct i3c_priv_xfer xfers;
> > +	int retry = I3C_TTY_RETRY;
> > +	unsigned long flags;
> > +	int actual;
> > +	int ret;
> > +
> > +	while (cnt > 0 && retry) {
> > +		xfers.rnw = 0;
> > +		xfers.len = CIRC_CNT_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
> > +		xfers.len = min_t(u16, 16, xfers.len);
> > +		xfers.data.out = circ->buf + circ->tail;
> > +
> > +		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> > +		if (ret) {
> > +			/*
> > +			 * Target side may not move data out of FIFO. delay can't resolve problem,
> > +			 * just reduce some possiblity. Target can't end I3C SDR mode write
> > +			 * transfer, discard data is reasonable when FIFO overrun.
> > +			 */
> > +			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> > +			retry--;
> > +		} else {
> > +			retry = 0;
> > +		}
> > +
> > +		actual = xfers.len;
> > +
> > +		circ->tail = (circ->tail + actual) & (UART_XMIT_SIZE - 1);
> > +
> > +		if (CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE) < WAKEUP_CHARS)
> > +			tty_port_tty_wakeup(&sport->port);
> > +
> > +		cnt = CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE);
> > +	}
> > +
> > +	spin_lock_irqsave(&sport->xlock, flags);
> > +	if (circ->head == circ->tail)
> > +		complete(&sport->txcomplete);
> > +	spin_unlock_irqrestore(&sport->xlock, flags);
> > +}
> > +
> > +static int i3c_probe(struct i3c_device *i3cdev)
> > +{
> > +	struct ttyi3c_port *port;
> > +	struct device *tty_dev;
> > +	struct i3c_ibi_setup req;
> > +	int minor;
> > +	int ret;
> > +
> > +	port = devm_kzalloc(&i3cdev->dev, sizeof(*port), GFP_KERNEL);
> > +	if (!port)
> > +		return -ENOMEM;
> > +
> > +	port->i3cdev = i3cdev;
> > +	port->buffer = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
> > +	if (!port->buffer)
> > +		return -ENOMEM;
> > +
> > +	port->xmit.buf = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
> > +	if (!port->xmit.buf)
> > +		return -ENOMEM;
> 
> You allocate two pages even if you never use the device?
> 
> > +	dev_set_drvdata(&i3cdev->dev, port);
> > +
> > +	req.max_payload_len = 8;
> > +	req.num_slots = 4;
> > +	req.handler = &i3c_controller_irq_handler;
> > +
> > +	ret = i3c_device_request_ibi(i3cdev, &req);
> > +	if (ret)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&i3c_tty_minors_lock);
> > +	minor = idr_alloc(&i3c_tty_minors, port, 0, I3C_TTY_MINORS, GFP_KERNEL);
> > +	mutex_unlock(&i3c_tty_minors_lock);
> > +
> > +	if (minor < 0)
> > +		return -EINVAL;
> > +
> > +	spin_lock_init(&port->xlock);
> > +	INIT_WORK(&port->txwork, tty_i3c_txwork);
> > +	INIT_WORK(&port->rxwork, tty_i3c_rxwork);
> > +	init_completion(&port->txcomplete);
> > +
> > +	port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&i3cdev->dev));
> > +	if (!port->workqueue)
> > +		return -ENOMEM;
> > +
> > +	tty_port_init(&port->port);
> > +	port->port.ops = &i3c_port_ops;
> > +
> > +	tty_dev = tty_port_register_device(&port->port, i3c_tty_driver, minor,
> > +					   &i3cdev->dev);
> > +	if (IS_ERR(tty_dev)) {
> > +		destroy_workqueue(port->workqueue);
> 
> What about tty_port_put() (it drops the idr too)? And free ibi?
> 
> > +		return PTR_ERR(tty_dev);
> > +	}
> > +
> > +	port->minor = minor;
> > +
> > +	return 0;
> > +}
> > +
> > +void i3c_remove(struct i3c_device *dev)
> > +{
> > +	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
> > +
> > +	tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
> > +	cancel_work_sync(&sport->txwork);
> > +	destroy_workqueue(sport->workqueue);
> 
> The same as above.
> 
> > +}
> > +
> > +static struct i3c_driver i3c_driver = {
> > +	.driver = {
> > +		.name = "ttyi3c",
> > +	},
> > +	.probe = i3c_probe,
> > +	.remove = i3c_remove,
> > +	.id_table = i3c_ids,
> > +};
> > +
> > +static int __init i3c_tty_init(void)
> > +{
> > +	int ret;
> > +
> > +	i3c_tty_driver = tty_alloc_driver(I3C_TTY_MINORS,
> > +					  TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
> > +
> > +	if (IS_ERR(i3c_tty_driver))
> > +		return PTR_ERR(i3c_tty_driver);
> > +
> > +	i3c_tty_driver->driver_name = "ttyI3C";
> > +	i3c_tty_driver->name = "ttyI3C";
> > +	i3c_tty_driver->minor_start = 0,
> > +	i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
> > +	i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
> > +	i3c_tty_driver->init_termios = tty_std_termios;
> > +	i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
> > +					       CLOCAL;
> > +	i3c_tty_driver->init_termios.c_lflag = 0;
> > +
> > +	tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
> > +
> > +	ret = tty_register_driver(i3c_tty_driver);
> > +	if (ret) {
> > +		tty_driver_kref_put(i3c_tty_driver);
> > +		return ret;
> > +	}
> > +
> > +	ret = i3c_driver_register(&i3c_driver);
> > +	if (ret) {
> > +		tty_unregister_driver(i3c_tty_driver);
> > +		tty_driver_kref_put(i3c_tty_driver);
> 
> Use goto + fail paths. And in i3c_probe() too.
> 
> > +	}
> > +
> > +	return ret;
> > +}
> 
> 
> regards,
> -- 
> js
> suse labs
> 


From yujie.liu at intel.com  Wed Oct 18 18:23:13 2023
From: yujie.liu at intel.com (kernel test robot)
Date: Thu, 19 Oct 2023 09:23:13 +0800
Subject: [PATCH v2 2/6] i3c: master: svc: add hot join support
In-Reply-To: <20231018205929.3435110-3-Frank.Li@nxp.com>
Message-ID: <202310190611.b14M4mH3-lkp@intel.com>

Hi Frank,

kernel test robot noticed the following build warnings:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.6-rc6 next-20231018]
[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-master-add-enable-disable-hot-join-in-sys-entry/20231019-051444
base:   linus/master
patch link:    https://lore.kernel.org/r/20231018205929.3435110-3-Frank.Li%40nxp.com
patch subject: [PATCH v2 2/6] i3c: master: svc: add hot join support
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20231019/202310190611.b14M4mH3-lkp at intel.com/config)
compiler: m68k-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231019/202310190611.b14M4mH3-lkp at intel.com/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 <yujie.liu at intel.com>
| Closes: https://lore.kernel.org/r/202310190611.b14M4mH3-lkp at intel.com/

All warnings (new ones prefixed by >>):

   drivers/i3c/master/svc-i3c-master.c:212: warning: Function parameter or member 'lock' not described in 'svc_i3c_master'
>> drivers/i3c/master/svc-i3c-master.c:212: warning: Function parameter or member 'enabled_events' not described in 'svc_i3c_master'
   2 warnings as Errors


vim +212 drivers/i3c/master/svc-i3c-master.c

1c5ee2a77b1bac Clark Wang    2023-05-17  157  
dd3c52846d5954 Miquel Raynal 2021-01-21  158  /**
dd3c52846d5954 Miquel Raynal 2021-01-21  159   * struct svc_i3c_master - Silvaco I3C Master structure
dd3c52846d5954 Miquel Raynal 2021-01-21  160   * @base: I3C master controller
dd3c52846d5954 Miquel Raynal 2021-01-21  161   * @dev: Corresponding device
dd3c52846d5954 Miquel Raynal 2021-01-21  162   * @regs: Memory mapping
5496eac6ad7428 Miquel Raynal 2023-08-17  163   * @saved_regs: Volatile values for PM operations
dd3c52846d5954 Miquel Raynal 2021-01-21  164   * @free_slots: Bit array of available slots
dd3c52846d5954 Miquel Raynal 2021-01-21  165   * @addrs: Array containing the dynamic addresses of each attached device
dd3c52846d5954 Miquel Raynal 2021-01-21  166   * @descs: Array of descriptors, one per attached device
dd3c52846d5954 Miquel Raynal 2021-01-21  167   * @hj_work: Hot-join work
dd3c52846d5954 Miquel Raynal 2021-01-21  168   * @ibi_work: IBI work
dd3c52846d5954 Miquel Raynal 2021-01-21  169   * @irq: Main interrupt
dd3c52846d5954 Miquel Raynal 2021-01-21  170   * @pclk: System clock
dd3c52846d5954 Miquel Raynal 2021-01-21  171   * @fclk: Fast clock (bus)
dd3c52846d5954 Miquel Raynal 2021-01-21  172   * @sclk: Slow clock (other events)
dd3c52846d5954 Miquel Raynal 2021-01-21  173   * @xferqueue: Transfer queue structure
dd3c52846d5954 Miquel Raynal 2021-01-21  174   * @xferqueue.list: List member
dd3c52846d5954 Miquel Raynal 2021-01-21  175   * @xferqueue.cur: Current ongoing transfer
dd3c52846d5954 Miquel Raynal 2021-01-21  176   * @xferqueue.lock: Queue lock
dd3c52846d5954 Miquel Raynal 2021-01-21  177   * @ibi: IBI structure
dd3c52846d5954 Miquel Raynal 2021-01-21  178   * @ibi.num_slots: Number of slots available in @ibi.slots
dd3c52846d5954 Miquel Raynal 2021-01-21  179   * @ibi.slots: Available IBI slots
dd3c52846d5954 Miquel Raynal 2021-01-21  180   * @ibi.tbq_slot: To be queued IBI slot
dd3c52846d5954 Miquel Raynal 2021-01-21  181   * @ibi.lock: IBI lock

* @lock: <description>
* @enabled_events: <description>

dd3c52846d5954 Miquel Raynal 2021-01-21  182   */
dd3c52846d5954 Miquel Raynal 2021-01-21  183  struct svc_i3c_master {
dd3c52846d5954 Miquel Raynal 2021-01-21  184  	struct i3c_master_controller base;
dd3c52846d5954 Miquel Raynal 2021-01-21  185  	struct device *dev;
dd3c52846d5954 Miquel Raynal 2021-01-21  186  	void __iomem *regs;
1c5ee2a77b1bac Clark Wang    2023-05-17  187  	struct svc_i3c_regs_save saved_regs;
dd3c52846d5954 Miquel Raynal 2021-01-21  188  	u32 free_slots;
dd3c52846d5954 Miquel Raynal 2021-01-21  189  	u8 addrs[SVC_I3C_MAX_DEVS];
dd3c52846d5954 Miquel Raynal 2021-01-21  190  	struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS];
dd3c52846d5954 Miquel Raynal 2021-01-21  191  	struct work_struct hj_work;
dd3c52846d5954 Miquel Raynal 2021-01-21  192  	struct work_struct ibi_work;
dd3c52846d5954 Miquel Raynal 2021-01-21  193  	int irq;
dd3c52846d5954 Miquel Raynal 2021-01-21  194  	struct clk *pclk;
dd3c52846d5954 Miquel Raynal 2021-01-21  195  	struct clk *fclk;
dd3c52846d5954 Miquel Raynal 2021-01-21  196  	struct clk *sclk;
dd3c52846d5954 Miquel Raynal 2021-01-21  197  	struct {
dd3c52846d5954 Miquel Raynal 2021-01-21  198  		struct list_head list;
dd3c52846d5954 Miquel Raynal 2021-01-21  199  		struct svc_i3c_xfer *cur;
dd3c52846d5954 Miquel Raynal 2021-01-21  200  		/* Prevent races between transfers */
dd3c52846d5954 Miquel Raynal 2021-01-21  201  		spinlock_t lock;
dd3c52846d5954 Miquel Raynal 2021-01-21  202  	} xferqueue;
dd3c52846d5954 Miquel Raynal 2021-01-21  203  	struct {
dd3c52846d5954 Miquel Raynal 2021-01-21  204  		unsigned int num_slots;
dd3c52846d5954 Miquel Raynal 2021-01-21  205  		struct i3c_dev_desc **slots;
dd3c52846d5954 Miquel Raynal 2021-01-21  206  		struct i3c_ibi_slot *tbq_slot;
dd3c52846d5954 Miquel Raynal 2021-01-21  207  		/* Prevent races within IBI handlers */
dd3c52846d5954 Miquel Raynal 2021-01-21  208  		spinlock_t lock;
dd3c52846d5954 Miquel Raynal 2021-01-21  209  	} ibi;
85bebb554b09b1 Frank Li      2023-10-16  210  	struct mutex lock;
e00571e38f466c Frank Li      2023-10-18  211  	int enabled_events;
dd3c52846d5954 Miquel Raynal 2021-01-21 @212  };
dd3c52846d5954 Miquel Raynal 2021-01-21  213  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki



From jirislaby at kernel.org  Thu Oct 19 00:12:58 2023
From: jirislaby at kernel.org (Jiri Slaby)
Date: Thu, 19 Oct 2023 09:12:58 +0200
Subject: [PATCH 1/1] tty: i3c: add tty over i3c master support
In-Reply-To: <20231018211111.3437929-1-Frank.Li@nxp.com>
References: <20231018211111.3437929-1-Frank.Li@nxp.com>
Message-ID: <caa8d113-3182-4f5f-affa-e5f8037498db@kernel.org>

On 18. 10. 23, 23:11, Frank Li wrote:
> Add new driver to allow tty over i3c master.

What is it good for? Why we should consider this for inclusion at all?

> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>      This patch depend on
>      https://lore.kernel.org/imx/20231018205929.3435110-1-Frank.Li at nxp.com/T/#t
> 
>   drivers/tty/Kconfig   |   8 +
>   drivers/tty/Makefile  |   1 +
>   drivers/tty/i3c_tty.c | 466 ++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 475 insertions(+)
>   create mode 100644 drivers/tty/i3c_tty.c
> 
> diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
> index 5646dc6242cd9..6d91fe6a211a1 100644
> --- a/drivers/tty/Kconfig
> +++ b/drivers/tty/Kconfig
> @@ -412,6 +412,14 @@ config RPMSG_TTY
>   	  To compile this driver as a module, choose M here: the module will be
>   	  called rpmsg_tty.
>   
> +config I3C_TTY
> +	tristate "tty over i3c"

"TTY over I3C"

> +	depends on I3C
> +	help
> +	  Select this options if you'd like use tty over I3C master controller

TTY and add a period.

> --- /dev/null
> +++ b/drivers/tty/i3c_tty.c
> @@ -0,0 +1,466 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2023 NXP.
> + *
> + * Author: Frank Li <Frank.Li at nxp.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/i3c/device.h>
> +#include <linux/i3c/master.h>
> +#include <linux/slab.h>
> +#include <linux/console.h>
> +#include <linux/serial_core.h>
> +#include <linux/interrupt.h>
> +#include <linux/workqueue.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_TTY_TRANS_SIZE	16
> +#define I3C_TTY_RX_STOP		BIT(0)
> +#define I3C_TTY_RETRY		20
> +#define I3C_TTY_YIELD_US	100
> +
> +struct ttyi3c_port {
> +	struct tty_port port;
> +	int minor;
> +	unsigned int txfifo_size;
> +	unsigned int rxfifo_size;
> +	struct circ_buf xmit;

Why can't you use port.xmit_fifo?

> +	spinlock_t xlock; /* protect xmit */
> +	void *buffer;
> +	struct i3c_device *i3cdev;
> +	struct work_struct txwork;
> +	struct work_struct rxwork;
> +	struct completion txcomplete;
> +	struct workqueue_struct *workqueue;

Why do you need a special wq? And even, why per port?

> +	atomic_t status;
> +};
> +
> +static const struct i3c_device_id i3c_ids[] = {
> +	I3C_DEVICE(0x011B, 0x1000, NULL),
> +	{ /* sentinel */ },
> +};
> +
> +static int i3c_port_activate(struct tty_port *port, struct tty_struct *tty)
> +{
> +	struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port);
> +
> +	atomic_set(&sport->status, 0);
> +
> +	return i3c_device_enable_ibi(sport->i3cdev);
> +}
> +
> +static void i3c_port_shutdown(struct tty_port *port)
> +{
> +	struct ttyi3c_port *sport =
> +		container_of(port, struct ttyi3c_port, port);
> +
> +	i3c_device_disable_ibi(sport->i3cdev);
> +}
> +
> +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 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;
> +}

You don't need this at all. (Watch for XXX marks below.)

> +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;
> +	}
> +	spin_unlock_irqrestore(&sport->xlock, flags);

With kfifo, all this would be one line, right?

> +
> +	if (circ->head != circ->tail)
> +		queue_work(sport->workqueue, &sport->txwork);
> +
> +	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;
> +
> +	queue_work(sport->workqueue, &sport->txwork);
> +}
> +
> +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;
> +
> +	atomic_or(I3C_TTY_RX_STOP, &sport->status);
> +}
> +
> +static void i3c_unthrottle(struct tty_struct *tty)
> +{
> +	struct ttyi3c_port *sport = tty->driver_data;
> +
> +	atomic_andnot(I3C_TTY_RX_STOP, &sport->status);
> +
> +	queue_work(sport->workqueue, &sport->rxwork);
> +}
> +
> +static int i3c_open(struct tty_struct *tty, struct file *filp)
> +{
> +	struct ttyi3c_port *sport = tty->driver_data;

XXX Here, you can simply do:

struct ttyi3c_port *sport = container_of(tty->port, struct ttyi3c_port, 
port);

tty->driver_data = sport;


> +	return tty_port_open(&sport->port, tty, filp);
> +}
> +
> +static void i3c_close(struct tty_struct *tty, struct file *filp)
> +{
> +	struct ttyi3c_port *sport = tty->driver_data;
> +
> +	if (!sport)
> +		return;

How can that happen?

> +	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;
> +
> +	wait_for_completion_timeout(&sport->txcomplete, timeout);
> +	reinit_completion(&sport->txcomplete);

Does this work in parallel?

> +}
> +
> +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,
> +};
> +
> +static void i3c_controller_irq_handler(struct i3c_device *dev,
> +				       const struct i3c_ibi_payload *payload)
> +{
> +	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
> +
> +	queue_work(sport->workqueue, &sport->rxwork);

Doesn't ibi provide threaded irqs? Oh, wait, i3c_master_handle_ibi() is 
already a work!

> +}
> +
> +static void tty_i3c_rxwork(struct work_struct *work)
> +{
> +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, rxwork);
> +	struct i3c_priv_xfer xfers;
> +	int retry = I3C_TTY_RETRY;
> +	u16 status = BIT(0);
> +
> +	do {
> +		memset(&xfers, 0, sizeof(xfers));
> +		xfers.data.in = sport->buffer;
> +		xfers.len = I3C_TTY_TRANS_SIZE;
> +		xfers.rnw = 1;
> +
> +		if (I3C_TTY_RX_STOP & atomic_read(&sport->status))

Hmm, why not much simpler (and yet atomic) {set,test,clear}_bit()?

> +			break;
> +
> +		i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> +
> +		if (xfers.actual_len) {
> +			tty_insert_flip_string(&sport->port, sport->buffer, xfers.actual_len);

This can fail.

> +			retry = 20;
> +			continue;
> +		} else {

"} else {" uneeded.

> +			status = BIT(0);
> +			i3c_device_getstatus_format1(sport->i3cdev, &status);
> +			/*
> +			 * Target side need some time to fill data into fifo. Target side may not

"needs"

> +			 * have hardware update status in real time. Software update status always
> +			 * need some delays.

"needs"

> +			 *
> +			 * Generally, target side have cicular buffer in memory, it will be moved
"circular" all around.

> +			 * into FIFO by CPU or DMA. 'status' just show if cicular buffer empty. But
> +			 * there are gap, espcially CPU have not response irq to fill FIFO in time.

espcially

> +			 * So xfers.actual will be zero, wait for little time to avoid flood
> +			 * transfer in i3c bus.
> +			 */
> +			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> +			retry--;
> +		}
> +
> +	} while (retry && (status & BIT(0)));
> +
> +	tty_flip_buffer_push(&sport->port);
> +}
> +
> +static void tty_i3c_txwork(struct work_struct *work)
> +{
> +	struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, txwork);
> +	struct circ_buf *circ = &sport->xmit;
> +	int cnt = CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE);
> +	struct i3c_priv_xfer xfers;
> +	int retry = I3C_TTY_RETRY;
> +	unsigned long flags;
> +	int actual;
> +	int ret;
> +
> +	while (cnt > 0 && retry) {
> +		xfers.rnw = 0;
> +		xfers.len = CIRC_CNT_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
> +		xfers.len = min_t(u16, 16, xfers.len);
> +		xfers.data.out = circ->buf + circ->tail;
> +
> +		ret = i3c_device_do_priv_xfers(sport->i3cdev, &xfers, 1);
> +		if (ret) {
> +			/*
> +			 * Target side may not move data out of FIFO. delay can't resolve problem,
> +			 * just reduce some possiblity. Target can't end I3C SDR mode write
> +			 * transfer, discard data is reasonable when FIFO overrun.
> +			 */
> +			usleep_range(I3C_TTY_YIELD_US, 10 * I3C_TTY_YIELD_US);
> +			retry--;
> +		} else {
> +			retry = 0;
> +		}
> +
> +		actual = xfers.len;
> +
> +		circ->tail = (circ->tail + actual) & (UART_XMIT_SIZE - 1);
> +
> +		if (CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE) < WAKEUP_CHARS)
> +			tty_port_tty_wakeup(&sport->port);
> +
> +		cnt = CIRC_CNT(circ->head, circ->tail, UART_XMIT_SIZE);
> +	}
> +
> +	spin_lock_irqsave(&sport->xlock, flags);
> +	if (circ->head == circ->tail)
> +		complete(&sport->txcomplete);
> +	spin_unlock_irqrestore(&sport->xlock, flags);
> +}
> +
> +static int i3c_probe(struct i3c_device *i3cdev)
> +{
> +	struct ttyi3c_port *port;
> +	struct device *tty_dev;
> +	struct i3c_ibi_setup req;
> +	int minor;
> +	int ret;
> +
> +	port = devm_kzalloc(&i3cdev->dev, sizeof(*port), GFP_KERNEL);
> +	if (!port)
> +		return -ENOMEM;
> +
> +	port->i3cdev = i3cdev;
> +	port->buffer = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
> +	if (!port->buffer)
> +		return -ENOMEM;
> +
> +	port->xmit.buf = devm_kzalloc(&i3cdev->dev, UART_XMIT_SIZE, GFP_KERNEL);
> +	if (!port->xmit.buf)
> +		return -ENOMEM;

You allocate two pages even if you never use the device?

> +	dev_set_drvdata(&i3cdev->dev, port);
> +
> +	req.max_payload_len = 8;
> +	req.num_slots = 4;
> +	req.handler = &i3c_controller_irq_handler;
> +
> +	ret = i3c_device_request_ibi(i3cdev, &req);
> +	if (ret)
> +		return -EINVAL;
> +
> +	mutex_lock(&i3c_tty_minors_lock);
> +	minor = idr_alloc(&i3c_tty_minors, port, 0, I3C_TTY_MINORS, GFP_KERNEL);
> +	mutex_unlock(&i3c_tty_minors_lock);
> +
> +	if (minor < 0)
> +		return -EINVAL;
> +
> +	spin_lock_init(&port->xlock);
> +	INIT_WORK(&port->txwork, tty_i3c_txwork);
> +	INIT_WORK(&port->rxwork, tty_i3c_rxwork);
> +	init_completion(&port->txcomplete);
> +
> +	port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&i3cdev->dev));
> +	if (!port->workqueue)
> +		return -ENOMEM;
> +
> +	tty_port_init(&port->port);
> +	port->port.ops = &i3c_port_ops;
> +
> +	tty_dev = tty_port_register_device(&port->port, i3c_tty_driver, minor,
> +					   &i3cdev->dev);
> +	if (IS_ERR(tty_dev)) {
> +		destroy_workqueue(port->workqueue);

What about tty_port_put() (it drops the idr too)? And free ibi?

> +		return PTR_ERR(tty_dev);
> +	}
> +
> +	port->minor = minor;
> +
> +	return 0;
> +}
> +
> +void i3c_remove(struct i3c_device *dev)
> +{
> +	struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
> +
> +	tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
> +	cancel_work_sync(&sport->txwork);
> +	destroy_workqueue(sport->workqueue);

The same as above.

> +}
> +
> +static struct i3c_driver i3c_driver = {
> +	.driver = {
> +		.name = "ttyi3c",
> +	},
> +	.probe = i3c_probe,
> +	.remove = i3c_remove,
> +	.id_table = i3c_ids,
> +};
> +
> +static int __init i3c_tty_init(void)
> +{
> +	int ret;
> +
> +	i3c_tty_driver = tty_alloc_driver(I3C_TTY_MINORS,
> +					  TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
> +
> +	if (IS_ERR(i3c_tty_driver))
> +		return PTR_ERR(i3c_tty_driver);
> +
> +	i3c_tty_driver->driver_name = "ttyI3C";
> +	i3c_tty_driver->name = "ttyI3C";
> +	i3c_tty_driver->minor_start = 0,
> +	i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
> +	i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL,
> +	i3c_tty_driver->init_termios = tty_std_termios;
> +	i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
> +					       CLOCAL;
> +	i3c_tty_driver->init_termios.c_lflag = 0;
> +
> +	tty_set_operations(i3c_tty_driver, &i3c_tty_ops);
> +
> +	ret = tty_register_driver(i3c_tty_driver);
> +	if (ret) {
> +		tty_driver_kref_put(i3c_tty_driver);
> +		return ret;
> +	}
> +
> +	ret = i3c_driver_register(&i3c_driver);
> +	if (ret) {
> +		tty_unregister_driver(i3c_tty_driver);
> +		tty_driver_kref_put(i3c_tty_driver);

Use goto + fail paths. And in i3c_probe() too.

> +	}
> +
> +	return ret;
> +}


regards,
-- 
js
suse labs



From jirislaby at kernel.org  Thu Oct 19 00:21:38 2023
From: jirislaby at kernel.org (Jiri Slaby)
Date: Thu, 19 Oct 2023 09:21:38 +0200
Subject: [PATCH 4/5] i3c: slave: func: add tty driver
In-Reply-To: <20231018215809.3477437-5-Frank.Li@nxp.com>
References: <20231018215809.3477437-1-Frank.Li@nxp.com>
 <20231018215809.3477437-5-Frank.Li@nxp.com>
Message-ID: <83a0f0a0-56b1-4021-a37d-ef68e7fab712@kernel.org>

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



From Frank.Li at nxp.com  Thu Oct 19 15:27:19 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 18:27:19 -0400
Subject: [PATCH v3 0/6] i3c: master: svc: collection of bugs fixes
Message-ID: <20231019222725.3598022-1-Frank.Li@nxp.com>

Each patch is indepedents. See commit message for detail.

Change from v1 to v3.
See each patch notes

If there are notes between v2 to v3, that's means not change.

Frank Li (6):
  i3c: master: svc: fix race condition in ibi work thread
  i3c: master: svc: fix wrong data return when IBI happen during start
    frame
  i3c: master: svc: fix ibi may not return mandatory data byte
  i3c: master: svc: fix check wrong status register in irq handler
  i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
  i3c: master: svc: fix random hot join failure since timeout error

 drivers/i3c/master/svc-i3c-master.c | 51 ++++++++++++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

-- 
2.34.1



From Frank.Li at nxp.com  Thu Oct 19 15:27:20 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 18:27:20 -0400
Subject: [PATCH v3 1/6] i3c: master: svc: fix race condition in ibi work thread
In-Reply-To: <20231019222725.3598022-1-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
Message-ID: <20231019222725.3598022-2-Frank.Li@nxp.com>

The ibi work thread operates asynchronously with other transfers, such as
svc_i3c_master_priv_xfers(). Introduce mutex protection to ensure the
completion of the entire i3c/i2c transaction.

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

Notes:
    Change from v1 to v2
    - update commit message
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index d29de5fe533e6..8cd708c965085 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -174,6 +174,7 @@ struct svc_i3c_regs_save {
  * @ibi.slots: Available IBI slots
  * @ibi.tbq_slot: To be queued IBI slot
  * @ibi.lock: IBI lock
+ * @lock: Transfer lock, protect between IBI work thread and callbacks from master
  */
 struct svc_i3c_master {
 	struct i3c_master_controller base;
@@ -202,6 +203,7 @@ struct svc_i3c_master {
 		/* Prevent races within IBI handlers */
 		spinlock_t lock;
 	} ibi;
+	struct mutex lock;
 };
 
 /**
@@ -383,6 +385,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 	u32 status, val;
 	int ret;
 
+	mutex_lock(&master->lock);
 	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
 	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
 	       SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -459,6 +462,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 
 reenable_ibis:
 	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+	mutex_unlock(&master->lock);
 }
 
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
@@ -1203,9 +1207,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = 0;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	kfree(buf);
@@ -1249,9 +1255,11 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = read_len;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	if (cmd->read_len != xfer_len)
 		ccc->dests[0].payload.len = cmd->read_len;
@@ -1308,9 +1316,11 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->continued = (i + 1) < nxfers;
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1346,9 +1356,11 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->continued = (i + 1 < nxfers);
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1539,6 +1551,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
 	INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
 	INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
+	mutex_init(&master->lock);
+
 	ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
 			       IRQF_NO_SUSPEND, "svc-i3c-irq", master);
 	if (ret)
-- 
2.34.1



From Frank.Li at nxp.com  Thu Oct 19 15:27:21 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 18:27:21 -0400
Subject: [PATCH v3 2/6] i3c: master: svc: fix wrong data return when IBI happen during start frame
In-Reply-To: <20231019222725.3598022-1-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
Message-ID: <20231019222725.3598022-3-Frank.Li@nxp.com>

     ???????     ????  ????  ????  ????  ????  ????  ????  ????  ??????
SCL: ?     ???????  ????  ????  ????  ????  ????  ????  ????  ????
     ????                       ???????     ???????     ?????????????
SDA:    ?????????????????????????     ???????     ???????           ??????
     xxx?    ??                                        ??    ??    ??    ?
   : xxx?IBI ??               Addr(0x0a)               ?? RW ??NACK?? S  ?

If an In-Band Interrupt (IBI) occurs and IBI work thread is not immediately
scheduled, When svc_i3c_master_priv_xfers() initiates the I3C transfer and
attempts to send address 0x7e, the target interprets it as an
IBI handler and returns the target address 0x0a.

However, svc_i3c_master_priv_xfers() does not handle this case and proceeds
with other transfers, resulting in incorrect data being returned.

Add IBIWON check in svc_i3c_master_xfer(). In case this situation occurs,
return a failure to the driver.

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

Notes:
    Change from v1 to v2
    - update commit message
    - fix typo yeild and falure

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 8cd708c965085..abebef666b2bb 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1010,6 +1010,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	u32 reg;
 	int ret;
 
+	/* clean SVC_I3C_MINT_IBIWON w1c bits */
+	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
 	writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
 	       xfer_type |
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
@@ -1028,6 +1031,23 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 	}
 
+	/*
+	 * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
+	 * with I3C Target Address.
+	 *
+	 * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
+	 * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
+	 * Role Request (i.e., Secondary Controller requests to become the Active Controller), or
+	 * a Hot-Join Request has been made.
+	 *
+	 * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure
+	 * and yield the above events handler.
+	 */
+	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
+		ret = -ENXIO;
+		goto emit_stop;
+	}
+
 	if (rnw)
 		ret = svc_i3c_master_read(master, in, xfer_len);
 	else
-- 
2.34.1



From Frank.Li at nxp.com  Thu Oct 19 15:27:22 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 18:27:22 -0400
Subject: [PATCH v3 3/6] i3c: master: svc: fix ibi may not return mandatory data byte
In-Reply-To: <20231019222725.3598022-1-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
Message-ID: <20231019222725.3598022-4-Frank.Li@nxp.com>

MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
creates an issue when the I3C clock is slow, and the CPU is running fast
enough that MSTATUS[RXPEND] may not be updated when the code reaches
checking point. As a result, mandatory data can be missed.

Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data is
already in FIFO. It also works without mandatory data.

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

Notes:
    Change from v2 to v3
    - update commit message and add no mandatory data in commits message
    
    Change from v1 to v2
    - update commit message
      it also works without mandatory bytes
    
    Change from v1 to v2
    - update commit message
      it also works without mandatory bytes

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index abebef666b2bb..dd06b7c9333f1 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -332,6 +332,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	struct i3c_ibi_slot *slot;
 	unsigned int count;
 	u32 mdatactrl;
+	int ret, val;
 	u8 *buf;
 
 	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
@@ -341,6 +342,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	slot->len = 0;
 	buf = slot->data;
 
+	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
+						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
+	if (ret) {
+		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
+		return ret;
+	}
+
 	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
 	       slot->len < SVC_I3C_FIFO_SIZE) {
 		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
-- 
2.34.1



From Frank.Li at nxp.com  Thu Oct 19 15:27:23 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 18:27:23 -0400
Subject: [PATCH v3 4/6] i3c: master: svc: fix check wrong status register in irq handler
In-Reply-To: <20231019222725.3598022-1-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
Message-ID: <20231019222725.3598022-5-Frank.Li@nxp.com>

svc_i3c_master_irq_handler() wrong check register SVC_I3C_MINTMASKED. It
should be SVC_I3C_MSTATUS.

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

Notes:
    Change from v1 to v2
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index dd06b7c9333f1..b113460f059c3 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -476,7 +476,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
 {
 	struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
-	u32 active = readl(master->regs + SVC_I3C_MINTMASKED);
+	u32 active = readl(master->regs + SVC_I3C_MSTATUS);
 
 	if (!SVC_I3C_MSTATUS_SLVSTART(active))
 		return IRQ_NONE;
-- 
2.34.1



From Frank.Li at nxp.com  Thu Oct 19 15:27:24 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 18:27:24 -0400
Subject: [PATCH v3 5/6] i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
In-Reply-To: <20231019222725.3598022-1-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
Message-ID: <20231019222725.3598022-6-Frank.Li@nxp.com>

Upon IBIWON timeout, the SDA line will always be kept low if we don't emit
a stop. Calling svc_i3c_master_emit_stop() there will let the bus return to
idle state.

Call svc_i3c_master_emit_stop() to let i3c bus come back to idle statue
when IBIWON timeout happen.

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

Notes:
    Change from v2 to v3
    - Update commit message and add review tag
    
    Change from v1 to v2
    - Update commite message
    
    Change from v1 to v2
    - Update commite message

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index b113460f059c3..1a57fdebaa26d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -404,6 +404,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
 	if (ret) {
 		dev_err(master->dev, "Timeout when polling for IBIWON\n");
+		svc_i3c_master_emit_stop(master);
 		goto reenable_ibis;
 	}
 
-- 
2.34.1



From Frank.Li at nxp.com  Thu Oct 19 15:27:25 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 18:27:25 -0400
Subject: [PATCH v3 6/6] i3c: master: svc: fix random hot join failure since timeout error
In-Reply-To: <20231019222725.3598022-1-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
Message-ID: <20231019222725.3598022-7-Frank.Li@nxp.com>

master side report:
  silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000

BIT 20: TIMEOUT error
  The module has stalled too long in a frame. This happens when:
  - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
middle of a message,
  - No STOP was issued and between messages,
  - IBI manual is used and no decision was made.
  The maximum stall period is 100 ?s.

This is a just warning. System irq thread schedule latency is possible
bigger than 100us. Just omit this waring.

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

Notes:
    Change from v2 to v3
    - remove 10k at commit message
    
    Change from v1 to v2
    -none
    
    Change from v1 to v2
    -none

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 1a57fdebaa26d..fedb31e0076c4 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -93,6 +93,7 @@
 #define SVC_I3C_MINTMASKED   0x098
 #define SVC_I3C_MERRWARN     0x09C
 #define   SVC_I3C_MERRWARN_NACK BIT(2)
+#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
 #define SVC_I3C_MDMACTRL     0x0A0
 #define SVC_I3C_MDATACTRL    0x0AC
 #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
@@ -226,6 +227,11 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
 	if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
 		merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
 		writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
+
+		/* ignore timeout error */
+		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
+			return false;
+
 		dev_err(master->dev,
 			"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
 			mstatus, merrwarn);
-- 
2.34.1



From Frank.li at nxp.com  Thu Oct 19 17:28:27 2023
From: Frank.li at nxp.com (Frank Li)
Date: Thu, 19 Oct 2023 20:28:27 -0400
Subject: [PATCH v3 5/6] i3c: master: svc: fix SDA keep low when polling
 IBIWON timeout happen
In-Reply-To: <20231019222725.3598022-6-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
 <20231019222725.3598022-6-Frank.Li@nxp.com>
Message-ID: <ZTHJq+9OKHVRaXGW@lizhi-Precision-Tower-5810>

On Thu, Oct 19, 2023 at 06:27:24PM -0400, Frank Li wrote:
> Upon IBIWON timeout, the SDA line will always be kept low if we don't emit
> a stop. Calling svc_i3c_master_emit_stop() there will let the bus return to
> idle state.
> 
> Call svc_i3c_master_emit_stop() to let i3c bus come back to idle statue
> when IBIWON timeout happen.

I forget delete this line. will remove it at next version.

Frank.

> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>     Change from v2 to v3
>     - Update commit message and add review tag
>     
>     Change from v1 to v2
>     - Update commite message
>     
>     Change from v1 to v2
>     - Update commite message
> 
>  drivers/i3c/master/svc-i3c-master.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index b113460f059c3..1a57fdebaa26d 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -404,6 +404,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
>  					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
>  	if (ret) {
>  		dev_err(master->dev, "Timeout when polling for IBIWON\n");
> +		svc_i3c_master_emit_stop(master);
>  		goto reenable_ibis;
>  	}
>  
> -- 
> 2.34.1
> 


From zbigniew.lukwinski at linux.intel.com  Fri Oct 20 01:55:27 2023
From: zbigniew.lukwinski at linux.intel.com (Zbigniew, Lukwinski)
Date: Fri, 20 Oct 2023 10:55:27 +0200
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys
 entry
In-Reply-To: <20231018205929.3435110-2-Frank.Li@nxp.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
 <20231018205929.3435110-2-Frank.Li@nxp.com>
Message-ID: <57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>

On 10/18/2023 10:59 PM, Frank Li wrote:
> Add hotjoin entry in sys file system allow user enable/disable hotjoin
> feature.
>
> Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
> Add api i3c_master_enable(disable)_hotjoin();

What is the use case for having HJ enable knob in sysfs available for 
user space other than for debug stuff? In other words, does user space 
really need to enable/disable HJ in runtime for other reason but debug? 
If it is only for debug maybe it? could be move to debugFS?

I think that maybe HJ enable knob shall be available in DT so you could 
control the default state for this feature also at compilation phase?

> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
>   drivers/i3c/master.c       | 84 ++++++++++++++++++++++++++++++++++++++
>   include/linux/i3c/master.h |  5 +++
>   2 files changed, 89 insertions(+)
>
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index 08aeb69a78003..ed5e27cd20811 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -526,6 +526,89 @@ static ssize_t i2c_scl_frequency_show(struct device *dev,
>   }
>   static DEVICE_ATTR_RO(i2c_scl_frequency);
>   
> +static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
> +{
> +	int ret;
> +
> +	if (!master ||
> +	    !master->ops ||
> +	    !master->ops->enable_hotjoin ||
> +	    !master->ops->disable_hotjoin
> +	   )
> +		return -EINVAL;
> +
> +	i3c_bus_normaluse_lock(&master->bus);
> +
> +	if (enable)
> +		ret = master->ops->enable_hotjoin(master);
> +	else
> +		ret = master->ops->disable_hotjoin(master);
> +
> +	master->hotjoin = enable;
> +
> +	i3c_bus_normaluse_unlock(&master->bus);
> +
> +	return ret;
> +}
> +
> +static ssize_t hotjoin_store(struct device *dev, struct device_attribute *attr,
> +			     const char *buf, size_t count)
> +{
> +	struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
> +	int ret;
> +	long res;
> +
> +	if (!i3cbus->cur_master)
> +		return -EINVAL;
> +
> +	if (kstrtol(buf, 10, &res))
> +		return -EINVAL;
> +
> +	ret = i3c_set_hotjoin(i3cbus->cur_master->common.master, !!res);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
> +/*
> + * i3c_master_enable_hotjoin - Enable hotjoin
> + * @master: I3C master object
> + *
> + * Return: a 0 in case of success, an negative error code otherwise.
> + */
> +int i3c_master_enable_hotjoin(struct i3c_master_controller *master)
> +{
> +	return i3c_set_hotjoin(master, true);
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_enable_hotjoin);
> +
> +/*
> + * i3c_master_disable_hotjoin - Disable hotjoin
> + * @master: I3C master object
> + *
> + * Return: a 0 in case of success, an negative error code otherwise.
> + */
> +int i3c_master_disable_hotjoin(struct i3c_master_controller *master)
> +{
> +	return i3c_set_hotjoin(master, false);
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin);
> +
> +static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf)
> +{
> +	struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
> +	ssize_t ret;
> +
> +	i3c_bus_normaluse_lock(i3cbus);
> +	ret = sysfs_emit(buf, "%d\n", i3cbus->cur_master->common.master->hotjoin);
> +	i3c_bus_normaluse_unlock(i3cbus);
> +
> +	return ret;
> +}
> +
> +static DEVICE_ATTR_RW(hotjoin);
> +
>   static struct attribute *i3c_masterdev_attrs[] = {
>   	&dev_attr_mode.attr,
>   	&dev_attr_current_master.attr,
> @@ -536,6 +619,7 @@ static struct attribute *i3c_masterdev_attrs[] = {
>   	&dev_attr_pid.attr,
>   	&dev_attr_dynamic_address.attr,
>   	&dev_attr_hdrcap.attr,
> +	&dev_attr_hotjoin.attr,
>   	NULL,
>   };
>   ATTRIBUTE_GROUPS(i3c_masterdev);
> diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
> index 0b52da4f23467..65b8965968af2 100644
> --- a/include/linux/i3c/master.h
> +++ b/include/linux/i3c/master.h
> @@ -452,6 +452,8 @@ struct i3c_master_controller_ops {
>   	int (*disable_ibi)(struct i3c_dev_desc *dev);
>   	void (*recycle_ibi_slot)(struct i3c_dev_desc *dev,
>   				 struct i3c_ibi_slot *slot);
> +	int (*enable_hotjoin)(struct i3c_master_controller *master);
> +	int (*disable_hotjoin)(struct i3c_master_controller *master);
>   };
>   
>   /**
> @@ -487,6 +489,7 @@ struct i3c_master_controller {
>   	const struct i3c_master_controller_ops *ops;
>   	unsigned int secondary : 1;
>   	unsigned int init_done : 1;
> +	unsigned int hotjoin: 1;
>   	struct {
>   		struct list_head i3c;
>   		struct list_head i2c;
> @@ -543,6 +546,8 @@ int i3c_master_register(struct i3c_master_controller *master,
>   			const struct i3c_master_controller_ops *ops,
>   			bool secondary);
>   void i3c_master_unregister(struct i3c_master_controller *master);
> +int i3c_master_enable_hotjoin(struct i3c_master_controller *master);
> +int i3c_master_disable_hotjoin(struct i3c_master_controller *master);
>   
>   /**
>    * i3c_dev_get_master_data() - get master private data attached to an I3C


From lkp at intel.com  Fri Oct 20 05:02:20 2023
From: lkp at intel.com (kernel test robot)
Date: Fri, 20 Oct 2023 20:02:20 +0800
Subject: [PATCH 1/1] tty: i3c: add tty over i3c master support
In-Reply-To: <20231018211111.3437929-1-Frank.Li@nxp.com>
References: <20231018211111.3437929-1-Frank.Li@nxp.com>
Message-ID: <202310201933.9lZn2Ebl-lkp@intel.com>

Hi Frank,

kernel test robot noticed the following build warnings:

[auto build test WARNING on tty/tty-testing]
[also build test WARNING on tty/tty-next tty/tty-linus linus/master v6.6-rc6 next-20231019]
[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/tty-i3c-add-tty-over-i3c-master-support/20231019-051407
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link:    https://lore.kernel.org/r/20231018211111.3437929-1-Frank.Li%40nxp.com
patch subject: [PATCH 1/1] tty: i3c: add tty over i3c master support
config: sparc-allyesconfig (https://download.01.org/0day-ci/archive/20231020/202310201933.9lZn2Ebl-lkp at intel.com/config)
compiler: sparc64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231020/202310201933.9lZn2Ebl-lkp at intel.com/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 <lkp at intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202310201933.9lZn2Ebl-lkp at intel.com/

All warnings (new ones prefixed by >>):

   drivers/tty/i3c_tty.c: In function 'tty_i3c_rxwork':
   drivers/tty/i3c_tty.c:265:26: error: 'struct i3c_priv_xfer' has no member named 'actual_len'
     265 |                 if (xfers.actual_len) {
         |                          ^
   drivers/tty/i3c_tty.c:266:82: error: 'struct i3c_priv_xfer' has no member named 'actual_len'
     266 |                         tty_insert_flip_string(&sport->port, sport->buffer, xfers.actual_len);
         |                                                                                  ^
   drivers/tty/i3c_tty.c:271:25: error: implicit declaration of function 'i3c_device_getstatus_format1' [-Werror=implicit-function-declaration]
     271 |                         i3c_device_getstatus_format1(sport->i3cdev, &status);
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/tty/i3c_tty.c: At top level:
>> drivers/tty/i3c_tty.c:400:6: warning: no previous prototype for 'i3c_remove' [-Wmissing-prototypes]
     400 | void i3c_remove(struct i3c_device *dev)
         |      ^~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/i3c_remove +400 drivers/tty/i3c_tty.c

   399	
 > 400	void i3c_remove(struct i3c_device *dev)
   401	{
   402		struct ttyi3c_port *sport = dev_get_drvdata(&dev->dev);
   403	
   404		tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor);
   405		cancel_work_sync(&sport->txwork);
   406		destroy_workqueue(sport->workqueue);
   407	}
   408	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


From miquel.raynal at bootlin.com  Fri Oct 20 06:45:28 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 15:45:28 +0200
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys
 entry
In-Reply-To: <57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
	<20231018205929.3435110-2-Frank.Li@nxp.com>
	<57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>
Message-ID: <20231020154528.2f5f68f5@xps-13>

Hi Lukwinski,

zbigniew.lukwinski at linux.intel.com wrote on Fri, 20 Oct 2023 10:55:27
+0200:

> On 10/18/2023 10:59 PM, Frank Li wrote:
> > Add hotjoin entry in sys file system allow user enable/disable hotjoin
> > feature.
> >
> > Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
> > Add api i3c_master_enable(disable)_hotjoin();  
> 
> What is the use case for having HJ enable knob in sysfs available for user space other than for debug stuff? In other words, does user space really need to enable/disable HJ in runtime for other reason but debug? If it is only for debug maybe it? could be move to debugFS?

I don't think hotjoin should be considered as a debug feature. The
problem here is the power consumption which is higher if you enable
this feature (you need to keep everything clocked and ready to handle
an IBI) whereas if your design is "fixed" (more like an I2C bus) you
may save power by disabling this feature.

A module parameter does not fit here because it's a per-bus
configuration.

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 06:49:36 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 15:49:36 +0200
Subject: [PATCH v3 3/6] i3c: master: svc: fix ibi may not return
 mandatory data byte
In-Reply-To: <20231019222725.3598022-4-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
	<20231019222725.3598022-4-Frank.Li@nxp.com>
Message-ID: <20231020154936.55a499ad@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Thu, 19 Oct 2023 18:27:22 -0400:

> MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
> creates an issue when the I3C clock is slow, and the CPU is running fast
> enough that MSTATUS[RXPEND] may not be updated when the code reaches
> checking point. As a result, mandatory data can be missed.
> 
> Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data is
> already in FIFO. It also works without mandatory data.
> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>

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

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 06:50:59 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 15:50:59 +0200
Subject: [PATCH v3 4/6] i3c: master: svc: fix check wrong status
 register in irq handler
In-Reply-To: <20231019222725.3598022-5-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
	<20231019222725.3598022-5-Frank.Li@nxp.com>
Message-ID: <20231020155059.4dfd4109@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Thu, 19 Oct 2023 18:27:23 -0400:

> svc_i3c_master_irq_handler() wrong check register SVC_I3C_MINTMASKED. It

This sentence still needs to be corrected, please read carefully my
reviews.

> should be SVC_I3C_MSTATUS.
> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 06:56:03 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 15:56:03 +0200
Subject: [PATCH v3 2/6] i3c: master: svc: fix wrong data return when IBI
 happen during start frame
In-Reply-To: <20231019222725.3598022-3-Frank.Li@nxp.com>
References: <20231019222725.3598022-1-Frank.Li@nxp.com>
	<20231019222725.3598022-3-Frank.Li@nxp.com>
Message-ID: <20231020155603.52cc7206@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Thu, 19 Oct 2023 18:27:21 -0400:

>      ???????     ????  ????  ????  ????  ????  ????  ????  ????  ??????
> SCL: ?     ???????  ????  ????  ????  ????  ????  ????  ????  ????
>      ????                       ???????     ???????     ?????????????
> SDA:    ?????????????????????????     ???????     ???????           ??????
>      xxx?    ??                                        ??    ??    ??    ?
>    : xxx?IBI ??               Addr(0x0a)               ?? RW ??NACK?? S  ?
> 
> If an In-Band Interrupt (IBI) occurs and IBI work thread is not immediately
> scheduled, When svc_i3c_master_priv_xfers() initiates the I3C transfer and

nit:		when

> attempts to send address 0x7e, the target interprets it as an
> IBI handler and returns the target address 0x0a.
> 
> However, svc_i3c_master_priv_xfers() does not handle this case and proceeds
> with other transfers, resulting in incorrect data being returned.
> 
> Add IBIWON check in svc_i3c_master_xfer(). In case this situation occurs,
> return a failure to the driver.
> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---


Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 07:06:45 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 16:06:45 +0200
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
	<20231018155926.3305476-7-Frank.Li@nxp.com>
	<20231019084452.11fd0645@xps-13>
	<ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
Message-ID: <20231020160645.67e678ee@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:

> On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> >   
> > > master side report:
> > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > 
> > > BIT 20: TIMEOUT error
> > >   The module has stalled too long in a frame. This happens when:
> > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > middle of a message,
> > >   - No STOP was issued and between messages,
> > >   - IBI manual is used and no decision was made.  
> > 
> > I am still not convinced this should be ignored in all cases.
> > 
> > Case 1 is a problem because the hardware failed somehow.  
> 
> But so far, no action to handle this case in current code.

Yes, but if you detect an issue and ignore it, it's not better than
reporting it without handling it. Instead of totally ignoring this I
would at least write a debug message (identical to what's below) before
returning false, even though I am not convinced unconditionally
returning false here is wise. If you fail a hardware sequence because
you added a printk, it's a problem. Maybe you consider this line as
noise, but I believe it's still an error condition. Maybe, however,
this bit gets set after the whole sequence, and this is just a "bus
is idle" condition. If that's the case, then you need some
additional heuristics to properly ignore the bit?

> In svc_i3c_master_xfer() have not check this flags. also have not enable
> ERRWARN irq.
> 
> If we met this case, we can add new functions/argument to handle this.
> Then we can real debug the code and recover bus.
> 
> Without this patch, simplest add some debug message before issue
> SVC_I3C_MCTRL_REQUEST_AUTO_IBI, TIMEOUT will be set.

Yes, and sometimes it won't be an issue, but sometimes it may. Maybe we
can find more advanced heuristics there.

> And svc_i3c_master_error() was only called by svc_i3c_master_ibi_work().
>
> So I can think only case 3 happen in svc_i3c_master_ibi_work().

Case 3 cannot be handled by Linux (because of the natural latency of
the OS).

> 
> Frank
> 
> > Case 2 is fine I guess.
> > Case 3 is not possible in Linux, this will not be supported.
> >   
> > >   The maximum stall period is 10 KHz or 100 ?s.  
> > 
> > s/10 KHz//
> >   
> > > 
> > > This is a just warning. System irq thread schedule latency is possible
> > > bigger than 100us. Just omit this waring.  
> > 
> > This can be considered as being just a warning as the system IRQ
> > latency can easily be greater than 100us.

This was skipped in your v3.

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

Thanks,
Miqu?l


From Frank.li at nxp.com  Fri Oct 20 07:18:55 2023
From: Frank.li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 10:18:55 -0400
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <20231020160645.67e678ee@xps-13>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
 <20231018155926.3305476-7-Frank.Li@nxp.com>
 <20231019084452.11fd0645@xps-13>
 <ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
 <20231020160645.67e678ee@xps-13>
Message-ID: <ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>

On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> 
> > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:
> > > Hi Frank,
> > > 
> > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > >   
> > > > master side report:
> > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > 
> > > > BIT 20: TIMEOUT error
> > > >   The module has stalled too long in a frame. This happens when:
> > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > middle of a message,
> > > >   - No STOP was issued and between messages,
> > > >   - IBI manual is used and no decision was made.  
> > > 
> > > I am still not convinced this should be ignored in all cases.
> > > 
> > > Case 1 is a problem because the hardware failed somehow.  
> > 
> > But so far, no action to handle this case in current code.
> 
> Yes, but if you detect an issue and ignore it, it's not better than
> reporting it without handling it. Instead of totally ignoring this I
> would at least write a debug message (identical to what's below) before
> returning false, even though I am not convinced unconditionally
> returning false here is wise. If you fail a hardware sequence because
> you added a printk, it's a problem. Maybe you consider this line as
> noise, but I believe it's still an error condition. Maybe, however,
> this bit gets set after the whole sequence, and this is just a "bus
> is idle" condition. If that's the case, then you need some
> additional heuristics to properly ignore the bit?
> 

                dev_err(master->dev,                                       
                        "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
                        mstatus, merrwarn);
+
+		/* ignore timeout error */
+		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
+			return false;
+

Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?

Frank


> > In svc_i3c_master_xfer() have not check this flags. also have not enable
> > ERRWARN irq.
> > 
> > If we met this case, we can add new functions/argument to handle this.
> > Then we can real debug the code and recover bus.
> > 
> > Without this patch, simplest add some debug message before issue
> > SVC_I3C_MCTRL_REQUEST_AUTO_IBI, TIMEOUT will be set.
> 
> Yes, and sometimes it won't be an issue, but sometimes it may. Maybe we
> can find more advanced heuristics there.
> 
> > And svc_i3c_master_error() was only called by svc_i3c_master_ibi_work().
> >
> > So I can think only case 3 happen in svc_i3c_master_ibi_work().
> 
> Case 3 cannot be handled by Linux (because of the natural latency of
> the OS).
> 
> > 
> > Frank
> > 
> > > Case 2 is fine I guess.
> > > Case 3 is not possible in Linux, this will not be supported.
> > >   
> > > >   The maximum stall period is 10 KHz or 100 ?s.  
> > > 
> > > s/10 KHz//
> > >   
> > > > 
> > > > This is a just warning. System irq thread schedule latency is possible
> > > > bigger than 100us. Just omit this waring.  
> > > 
> > > This can be considered as being just a warning as the system IRQ
> > > latency can easily be greater than 100us.
> 
> This was skipped in your v3.
> 
> > > > Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> > > > Cc: stable at vger.kernel.org
> > > > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > > > ---
> 
> Thanks,
> Miqu?l


From Frank.li at nxp.com  Fri Oct 20 07:20:57 2023
From: Frank.li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 10:20:57 -0400
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys
 entry
In-Reply-To: <20231020154528.2f5f68f5@xps-13>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
 <20231018205929.3435110-2-Frank.Li@nxp.com>
 <57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>
 <20231020154528.2f5f68f5@xps-13>
Message-ID: <ZTKMyW939rB6sSKq@lizhi-Precision-Tower-5810>

On Fri, Oct 20, 2023 at 03:45:28PM +0200, Miquel Raynal wrote:
> Hi Lukwinski,
> 
> zbigniew.lukwinski at linux.intel.com wrote on Fri, 20 Oct 2023 10:55:27
> +0200:
> 
> > On 10/18/2023 10:59 PM, Frank Li wrote:
> > > Add hotjoin entry in sys file system allow user enable/disable hotjoin
> > > feature.
> > >
> > > Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
> > > Add api i3c_master_enable(disable)_hotjoin();  
> > 
> > What is the use case for having HJ enable knob in sysfs available for user space other than for debug stuff? In other words, does user space really need to enable/disable HJ in runtime for other reason but debug? If it is only for debug maybe it? could be move to debugFS?
> 
> I don't think hotjoin should be considered as a debug feature. The
> problem here is the power consumption which is higher if you enable
> this feature (you need to keep everything clocked and ready to handle
> an IBI) whereas if your design is "fixed" (more like an I2C bus) you
> may save power by disabling this feature.
> 
> A module parameter does not fit here because it's a per-bus
> configuration.

I agree. sys entry is more flexiable. and let controller choose better
power saving policy for difference user case.

Frank

> 
> Thanks,
> Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 07:33:48 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 16:33:48 +0200
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys
 entry
In-Reply-To: <ZTKMyW939rB6sSKq@lizhi-Precision-Tower-5810>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
	<20231018205929.3435110-2-Frank.Li@nxp.com>
	<57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>
	<20231020154528.2f5f68f5@xps-13>
	<ZTKMyW939rB6sSKq@lizhi-Precision-Tower-5810>
Message-ID: <20231020163348.1752167d@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:20:57 -0400:

> On Fri, Oct 20, 2023 at 03:45:28PM +0200, Miquel Raynal wrote:
> > Hi Lukwinski,
> > 
> > zbigniew.lukwinski at linux.intel.com wrote on Fri, 20 Oct 2023 10:55:27
> > +0200:
> >   
> > > On 10/18/2023 10:59 PM, Frank Li wrote:  
> > > > Add hotjoin entry in sys file system allow user enable/disable hotjoin
> > > > feature.
> > > >
> > > > Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
> > > > Add api i3c_master_enable(disable)_hotjoin();    
> > > 
> > > What is the use case for having HJ enable knob in sysfs available for user space other than for debug stuff? In other words, does user space really need to enable/disable HJ in runtime for other reason but debug? If it is only for debug maybe it? could be move to debugFS?  
> > 
> > I don't think hotjoin should be considered as a debug feature. The
> > problem here is the power consumption which is higher if you enable
> > this feature (you need to keep everything clocked and ready to handle
> > an IBI) whereas if your design is "fixed" (more like an I2C bus) you
> > may save power by disabling this feature.
> > 
> > A module parameter does not fit here because it's a per-bus
> > configuration.  
> 
> I agree. sys entry is more flexiable. and let controller choose better
> power saving policy for difference user case.

Maybe it's not the first time this case is faced, would you mind
including power management maintainers in this discussion? Perhaps they
might have pointers or even have the solution already.

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 07:35:25 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 16:35:25 +0200
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
	<20231018155926.3305476-7-Frank.Li@nxp.com>
	<20231019084452.11fd0645@xps-13>
	<ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
	<20231020160645.67e678ee@xps-13>
	<ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
Message-ID: <20231020163525.66485920@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:

> On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> >   
> > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:  
> > > > Hi Frank,
> > > > 
> > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > >     
> > > > > master side report:
> > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > 
> > > > > BIT 20: TIMEOUT error
> > > > >   The module has stalled too long in a frame. This happens when:
> > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > middle of a message,
> > > > >   - No STOP was issued and between messages,
> > > > >   - IBI manual is used and no decision was made.    
> > > > 
> > > > I am still not convinced this should be ignored in all cases.
> > > > 
> > > > Case 1 is a problem because the hardware failed somehow.    
> > > 
> > > But so far, no action to handle this case in current code.  
> > 
> > Yes, but if you detect an issue and ignore it, it's not better than
> > reporting it without handling it. Instead of totally ignoring this I
> > would at least write a debug message (identical to what's below) before
> > returning false, even though I am not convinced unconditionally
> > returning false here is wise. If you fail a hardware sequence because
> > you added a printk, it's a problem. Maybe you consider this line as
> > noise, but I believe it's still an error condition. Maybe, however,
> > this bit gets set after the whole sequence, and this is just a "bus
> > is idle" condition. If that's the case, then you need some
> > additional heuristics to properly ignore the bit?
> >   
> 
>                 dev_err(master->dev,                                       
>                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
>                         mstatus, merrwarn);
> +
> +		/* ignore timeout error */
> +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> +			return false;
> +
> 
> Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?

I think you mentioned earlier that the problem was not the printk but
the return value. So perhaps there is a way to know if the timeout
happened after a transaction and was legitimate or not?

In any case we should probably lower the log level for this error.

Thanks,
Miqu?l


From Frank.li at nxp.com  Fri Oct 20 07:47:52 2023
From: Frank.li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 10:47:52 -0400
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <20231020163525.66485920@xps-13>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
 <20231018155926.3305476-7-Frank.Li@nxp.com>
 <20231019084452.11fd0645@xps-13>
 <ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
 <20231020160645.67e678ee@xps-13>
 <ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
 <20231020163525.66485920@xps-13>
Message-ID: <ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>

On Fri, Oct 20, 2023 at 04:35:25PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:
> 
> > On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:
> > > Hi Frank,
> > > 
> > > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> > >   
> > > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:  
> > > > > Hi Frank,
> > > > > 
> > > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > > >     
> > > > > > master side report:
> > > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > > 
> > > > > > BIT 20: TIMEOUT error
> > > > > >   The module has stalled too long in a frame. This happens when:
> > > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > > middle of a message,
> > > > > >   - No STOP was issued and between messages,
> > > > > >   - IBI manual is used and no decision was made.    
> > > > > 
> > > > > I am still not convinced this should be ignored in all cases.
> > > > > 
> > > > > Case 1 is a problem because the hardware failed somehow.    
> > > > 
> > > > But so far, no action to handle this case in current code.  
> > > 
> > > Yes, but if you detect an issue and ignore it, it's not better than
> > > reporting it without handling it. Instead of totally ignoring this I
> > > would at least write a debug message (identical to what's below) before
> > > returning false, even though I am not convinced unconditionally
> > > returning false here is wise. If you fail a hardware sequence because
> > > you added a printk, it's a problem. Maybe you consider this line as
> > > noise, but I believe it's still an error condition. Maybe, however,
> > > this bit gets set after the whole sequence, and this is just a "bus
> > > is idle" condition. If that's the case, then you need some
> > > additional heuristics to properly ignore the bit?
> > >   
> > 
> >                 dev_err(master->dev,                                       
> >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> >                         mstatus, merrwarn);
> > +
> > +		/* ignore timeout error */
> > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > +			return false;
> > +
> > 
> > Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?
> 
> I think you mentioned earlier that the problem was not the printk but
> the return value. So perhaps there is a way to know if the timeout
> happened after a transaction and was legitimate or not?

Error message just annoise user, don't impact function. But return false
let IBI thread running to avoid dead lock. 

> 
> In any case we should probably lower the log level for this error.

Only SVC_I3C_MERRWARN_TIMEOUT is warning

Maybe below logic is better

	if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
		dev_dbg(master->dev, 
                        "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
			mstatus, merrwarn);
		return false;
	} 
	
	dev_err(master->dev,                                     
                "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
                 mstatus, merrwarn); 
	....

Frank

> 
> Thanks,
> Miqu?l


From Frank.li at nxp.com  Fri Oct 20 08:17:17 2023
From: Frank.li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 11:17:17 -0400
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
 <20231018155926.3305476-7-Frank.Li@nxp.com>
 <20231019084452.11fd0645@xps-13>
 <ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
 <20231020160645.67e678ee@xps-13>
 <ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
 <20231020163525.66485920@xps-13>
 <ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
Message-ID: <ZTKZ/a5T6DLJz/ZQ@lizhi-Precision-Tower-5810>

On Fri, Oct 20, 2023 at 10:47:52AM -0400, Frank Li wrote:
> On Fri, Oct 20, 2023 at 04:35:25PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:
> > 
> > > On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:
> > > > Hi Frank,
> > > > 
> > > > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> > > >   
> > > > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:  
> > > > > > Hi Frank,
> > > > > > 
> > > > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > > > >     
> > > > > > > master side report:
> > > > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > > > 
> > > > > > > BIT 20: TIMEOUT error
> > > > > > >   The module has stalled too long in a frame. This happens when:
> > > > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > > > middle of a message,
> > > > > > >   - No STOP was issued and between messages,
> > > > > > >   - IBI manual is used and no decision was made.    
> > > > > > 
> > > > > > I am still not convinced this should be ignored in all cases.
> > > > > > 
> > > > > > Case 1 is a problem because the hardware failed somehow.    
> > > > > 
> > > > > But so far, no action to handle this case in current code.  
> > > > 
> > > > Yes, but if you detect an issue and ignore it, it's not better than
> > > > reporting it without handling it. Instead of totally ignoring this I
> > > > would at least write a debug message (identical to what's below) before
> > > > returning false, even though I am not convinced unconditionally
> > > > returning false here is wise. If you fail a hardware sequence because
> > > > you added a printk, it's a problem. Maybe you consider this line as
> > > > noise, but I believe it's still an error condition. Maybe, however,
> > > > this bit gets set after the whole sequence, and this is just a "bus
> > > > is idle" condition. If that's the case, then you need some
> > > > additional heuristics to properly ignore the bit?
> > > >   
> > > 
> > >                 dev_err(master->dev,                                       
> > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > >                         mstatus, merrwarn);
> > > +
> > > +		/* ignore timeout error */
> > > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > > +			return false;
> > > +
> > > 
> > > Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?
> > 
> > I think you mentioned earlier that the problem was not the printk but
> > the return value. So perhaps there is a way to know if the timeout
> > happened after a transaction and was legitimate or not?
> 
> Error message just annoise user, don't impact function. But return false
> let IBI thread running to avoid dead lock. 

I forget mention one thing. Any error message here will make SDA low for
longer.  Before emit stop, SDA is low.

I have not checked I3C spec yet about how long SDA will be allowed. it will
worser if message go through uart port. The bus will be locked longer.

It's better to print error message after emit_stop to reduce SDA low time.

Frank

> 
> > 
> > In any case we should probably lower the log level for this error.
> 
> Only SVC_I3C_MERRWARN_TIMEOUT is warning
> 
> Maybe below logic is better
> 
> 	if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
> 		dev_dbg(master->dev, 
>                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> 			mstatus, merrwarn);
> 		return false;
> 	} 
> 	
> 	dev_err(master->dev,                                     
>                 "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
>                  mstatus, merrwarn); 
> 	....
> 
> Frank
> 
> > 
> > Thanks,
> > Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 08:20:06 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 17:20:06 +0200
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
	<20231018155926.3305476-7-Frank.Li@nxp.com>
	<20231019084452.11fd0645@xps-13>
	<ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
	<20231020160645.67e678ee@xps-13>
	<ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
	<20231020163525.66485920@xps-13>
	<ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
Message-ID: <20231020172006.5de0998b@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:47:52 -0400:

> On Fri, Oct 20, 2023 at 04:35:25PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:
> >   
> > > On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:  
> > > > Hi Frank,
> > > > 
> > > > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> > > >     
> > > > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:    
> > > > > > Hi Frank,
> > > > > > 
> > > > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > > > >       
> > > > > > > master side report:
> > > > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > > > 
> > > > > > > BIT 20: TIMEOUT error
> > > > > > >   The module has stalled too long in a frame. This happens when:
> > > > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > > > middle of a message,
> > > > > > >   - No STOP was issued and between messages,
> > > > > > >   - IBI manual is used and no decision was made.      
> > > > > > 
> > > > > > I am still not convinced this should be ignored in all cases.
> > > > > > 
> > > > > > Case 1 is a problem because the hardware failed somehow.      
> > > > > 
> > > > > But so far, no action to handle this case in current code.    
> > > > 
> > > > Yes, but if you detect an issue and ignore it, it's not better than
> > > > reporting it without handling it. Instead of totally ignoring this I
> > > > would at least write a debug message (identical to what's below) before
> > > > returning false, even though I am not convinced unconditionally
> > > > returning false here is wise. If you fail a hardware sequence because
> > > > you added a printk, it's a problem. Maybe you consider this line as
> > > > noise, but I believe it's still an error condition. Maybe, however,
> > > > this bit gets set after the whole sequence, and this is just a "bus
> > > > is idle" condition. If that's the case, then you need some
> > > > additional heuristics to properly ignore the bit?
> > > >     
> > > 
> > >                 dev_err(master->dev,                                       
> > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > >                         mstatus, merrwarn);
> > > +
> > > +		/* ignore timeout error */
> > > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > > +			return false;
> > > +
> > > 
> > > Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?  
> > 
> > I think you mentioned earlier that the problem was not the printk but
> > the return value. So perhaps there is a way to know if the timeout
> > happened after a transaction and was legitimate or not?  
> 
> Error message just annoise user, don't impact function. But return false
> let IBI thread running to avoid dead lock. 
> 
> > 
> > In any case we should probably lower the log level for this error.  
> 
> Only SVC_I3C_MERRWARN_TIMEOUT is warning
> 
> Maybe below logic is better
> 
> 	if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
> 		dev_dbg(master->dev, 
>                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> 			mstatus, merrwarn);
> 		return false;
> 	} 
> 	
> 	dev_err(master->dev,                                     
>                 "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
>                  mstatus, merrwarn); 
> 	....
> 

Yes, this looks better but I wonder if we should add an additional
condition to just return false in this case; something saying "this
timeout is legitimate and has no impact".

Thanks,
Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 08:25:13 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 17:25:13 +0200
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout error
In-Reply-To: <ZTKZ/a5T6DLJz/ZQ@lizhi-Precision-Tower-5810>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
	<20231018155926.3305476-7-Frank.Li@nxp.com>
	<20231019084452.11fd0645@xps-13>
	<ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
	<20231020160645.67e678ee@xps-13>
	<ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
	<20231020163525.66485920@xps-13>
	<ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
	<ZTKZ/a5T6DLJz/ZQ@lizhi-Precision-Tower-5810>
Message-ID: <20231020172513.43a99144@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Fri, 20 Oct 2023 11:17:17 -0400:

> On Fri, Oct 20, 2023 at 10:47:52AM -0400, Frank Li wrote:
> > On Fri, Oct 20, 2023 at 04:35:25PM +0200, Miquel Raynal wrote:  
> > > Hi Frank,
> > > 
> > > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:
> > >   
> > > > On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:  
> > > > > Hi Frank,
> > > > > 
> > > > > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> > > > >     
> > > > > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:    
> > > > > > > Hi Frank,
> > > > > > > 
> > > > > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > > > > >       
> > > > > > > > master side report:
> > > > > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > > > > 
> > > > > > > > BIT 20: TIMEOUT error
> > > > > > > >   The module has stalled too long in a frame. This happens when:
> > > > > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > > > > middle of a message,
> > > > > > > >   - No STOP was issued and between messages,
> > > > > > > >   - IBI manual is used and no decision was made.      
> > > > > > > 
> > > > > > > I am still not convinced this should be ignored in all cases.
> > > > > > > 
> > > > > > > Case 1 is a problem because the hardware failed somehow.      
> > > > > > 
> > > > > > But so far, no action to handle this case in current code.    
> > > > > 
> > > > > Yes, but if you detect an issue and ignore it, it's not better than
> > > > > reporting it without handling it. Instead of totally ignoring this I
> > > > > would at least write a debug message (identical to what's below) before
> > > > > returning false, even though I am not convinced unconditionally
> > > > > returning false here is wise. If you fail a hardware sequence because
> > > > > you added a printk, it's a problem. Maybe you consider this line as
> > > > > noise, but I believe it's still an error condition. Maybe, however,
> > > > > this bit gets set after the whole sequence, and this is just a "bus
> > > > > is idle" condition. If that's the case, then you need some
> > > > > additional heuristics to properly ignore the bit?
> > > > >     
> > > > 
> > > >                 dev_err(master->dev,                                       
> > > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > >                         mstatus, merrwarn);
> > > > +
> > > > +		/* ignore timeout error */
> > > > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > > > +			return false;
> > > > +
> > > > 
> > > > Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?  
> > > 
> > > I think you mentioned earlier that the problem was not the printk but
> > > the return value. So perhaps there is a way to know if the timeout
> > > happened after a transaction and was legitimate or not?  
> > 
> > Error message just annoise user, don't impact function. But return false
> > let IBI thread running to avoid dead lock.   
> 
> I forget mention one thing. Any error message here will make SDA low for
> longer.  Before emit stop, SDA is low.
> 
> I have not checked I3C spec yet about how long SDA will be allowed. it will
> worser if message go through uart port. The bus will be locked longer.
> 
> It's better to print error message after emit_stop to reduce SDA low time.

That's fine I guess.

Thanks,
Miqu?l


From Frank.li at nxp.com  Fri Oct 20 08:47:48 2023
From: Frank.li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 11:47:48 -0400
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout errory
In-Reply-To: <20231020172006.5de0998b@xps-13>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
 <20231018155926.3305476-7-Frank.Li@nxp.com>
 <20231019084452.11fd0645@xps-13>
 <ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
 <20231020160645.67e678ee@xps-13>
 <ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
 <20231020163525.66485920@xps-13>
 <ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
 <20231020172006.5de0998b@xps-13>
Message-ID: <ZTKhJOAAz4CaupEf@lizhi-Precision-Tower-5810>

On Fri, Oct 20, 2023 at 05:20:06PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:47:52 -0400:
> 
> > On Fri, Oct 20, 2023 at 04:35:25PM +0200, Miquel Raynal wrote:
> > > Hi Frank,
> > > 
> > > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:
> > >   
> > > > On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:  
> > > > > Hi Frank,
> > > > > 
> > > > > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> > > > >     
> > > > > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:    
> > > > > > > Hi Frank,
> > > > > > > 
> > > > > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > > > > >       
> > > > > > > > master side report:
> > > > > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > > > > 
> > > > > > > > BIT 20: TIMEOUT error
> > > > > > > >   The module has stalled too long in a frame. This happens when:
> > > > > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > > > > middle of a message,
> > > > > > > >   - No STOP was issued and between messages,
> > > > > > > >   - IBI manual is used and no decision was made.      
> > > > > > > 
> > > > > > > I am still not convinced this should be ignored in all cases.
> > > > > > > 
> > > > > > > Case 1 is a problem because the hardware failed somehow.      
> > > > > > 
> > > > > > But so far, no action to handle this case in current code.    
> > > > > 
> > > > > Yes, but if you detect an issue and ignore it, it's not better than
> > > > > reporting it without handling it. Instead of totally ignoring this I
> > > > > would at least write a debug message (identical to what's below) before
> > > > > returning false, even though I am not convinced unconditionally
> > > > > returning false here is wise. If you fail a hardware sequence because
> > > > > you added a printk, it's a problem. Maybe you consider this line as
> > > > > noise, but I believe it's still an error condition. Maybe, however,
> > > > > this bit gets set after the whole sequence, and this is just a "bus
> > > > > is idle" condition. If that's the case, then you need some
> > > > > additional heuristics to properly ignore the bit?
> > > > >     
> > > > 
> > > >                 dev_err(master->dev,                                       
> > > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > >                         mstatus, merrwarn);
> > > > +
> > > > +		/* ignore timeout error */
> > > > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > > > +			return false;
> > > > +
> > > > 
> > > > Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?  
> > > 
> > > I think you mentioned earlier that the problem was not the printk but
> > > the return value. So perhaps there is a way to know if the timeout
> > > happened after a transaction and was legitimate or not?  
> > 
> > Error message just annoise user, don't impact function. But return false
> > let IBI thread running to avoid dead lock. 
> > 
> > > 
> > > In any case we should probably lower the log level for this error.  
> > 
> > Only SVC_I3C_MERRWARN_TIMEOUT is warning
> > 
> > Maybe below logic is better
> > 
> > 	if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
> > 		dev_dbg(master->dev, 
> >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > 			mstatus, merrwarn);
> > 		return false;
> > 	} 
> > 	
> > 	dev_err(master->dev,                                     
> >                 "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> >                  mstatus, merrwarn); 
> > 	....
> > 
> 
> Yes, this looks better but I wonder if we should add an additional
> condition to just return false in this case; 

What's additional condition we can check? 

> something saying "this
> timeout is legitimate and has no impact".

Add comments "this timeout is legitimate and has no impact" or dev_dbg
print that?

> 
> Thanks,
> Miqu?l


From Frank.Li at nxp.com  Fri Oct 20 09:00:27 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 12:00:27 -0400
Subject: [PATCH v2 1/1] tty: i3c: add TTY over I3C master support
Message-ID: <20231020160027.3663772-1-Frank.Li@nxp.com>

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

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

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

Notes:
    This patch depend on
    https://lore.kernel.org/imx/20231018205929.3435110-1-Frank.Li at nxp.com/T/#t
    
    Change from v1 to v2
    - update commit message.
    - using goto for err handle
    - using one working queue for all tty-i3c device
    - fixed typo found by js
    - update kconfig help
    - using kfifo
    
    Some idea about i3c tty in future
    1. early console support
       - early console is useful to debug boot problem. It requires as simple
    as possible.
        i3c SDR mode, 9th bit is parity. I3C target can't block master send out
    data. so never block kernel boot even without any expected target connected.
    i3c master may send out a boardcast message as early console to every
    target devices. Only a "i3c-usb-acm" can show it to host's minicom.  And
    this "i3c-usb-acm" can plug in at any time. Write a boardcast message is
    quite simple at I3C controller. After kernel system boot to certain phase,
    switch to this driver.
    
    2. multi port supports. create multi virtual uart ports, so firmware, such
    as arm trust firmware can dump message to difference virtual ports.
    
    3. compatible with UART network adaptor, which defined at MIPI DEBUG for
    I3C spec.
    
    This driver is just first step.
    
    This patch depend on
    https://lore.kernel.org/imx/20231018205929.3435110-1-Frank.Li at nxp.com/T/#t

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

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



From Frank.li at nxp.com  Fri Oct 20 09:24:47 2023
From: Frank.li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 12:24:47 -0400
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys
 entry
In-Reply-To: <20231020163348.1752167d@xps-13>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
 <20231018205929.3435110-2-Frank.Li@nxp.com>
 <57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>
 <20231020154528.2f5f68f5@xps-13>
 <ZTKMyW939rB6sSKq@lizhi-Precision-Tower-5810>
 <20231020163348.1752167d@xps-13>
Message-ID: <ZTKpzwKIfekMx7X3@lizhi-Precision-Tower-5810>

On Fri, Oct 20, 2023 at 04:33:48PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:20:57 -0400:
> 
> > On Fri, Oct 20, 2023 at 03:45:28PM +0200, Miquel Raynal wrote:
> > > Hi Lukwinski,
> > > 
> > > zbigniew.lukwinski at linux.intel.com wrote on Fri, 20 Oct 2023 10:55:27
> > > +0200:
> > >   
> > > > On 10/18/2023 10:59 PM, Frank Li wrote:  
> > > > > Add hotjoin entry in sys file system allow user enable/disable hotjoin
> > > > > feature.
> > > > >
> > > > > Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
> > > > > Add api i3c_master_enable(disable)_hotjoin();    
> > > > 
> > > > What is the use case for having HJ enable knob in sysfs available for user space other than for debug stuff? In other words, does user space really need to enable/disable HJ in runtime for other reason but debug? If it is only for debug maybe it? could be move to debugFS?  
> > > 
> > > I don't think hotjoin should be considered as a debug feature. The
> > > problem here is the power consumption which is higher if you enable
> > > this feature (you need to keep everything clocked and ready to handle
> > > an IBI) whereas if your design is "fixed" (more like an I2C bus) you
> > > may save power by disabling this feature.
> > > 
> > > A module parameter does not fit here because it's a per-bus
> > > configuration.  
> > 
> > I agree. sys entry is more flexiable. and let controller choose better
> > power saving policy for difference user case.
> 
> Maybe it's not the first time this case is faced, would you mind
> including power management maintainers in this discussion? Perhaps they
> might have pointers or even have the solution already.

@Ulf and pm experts.

I3C have a features, which call hotjoin. Some controller need enable clock
and some power domain if support hotjoin.

there are two kinds user case.

case1:  All devices attached into I3C bus, not hotjoin happen. So
controller can use runtime_pm frame to make clock and power domain on only
when transferring data.

case2: some devices can dynamitc join when system running. Some clocks or
power domain need be kept to make hotjoin event detect logic work.

In one system, two cases may exist at same time. I3C bus1 for case1, I3C
bus2 for case 2. 

I add sys entry in I3C bus driver to let user can turn on/off this feature
for specific controller. 

My question: any exited solution can handle these in current power
mnanagement system. 

Frank

> 
> Thanks,
> Miqu?l


From miquel.raynal at bootlin.com  Fri Oct 20 10:03:37 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Fri, 20 Oct 2023 19:03:37 +0200
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout errory
In-Reply-To: <ZTKhJOAAz4CaupEf@lizhi-Precision-Tower-5810>
References: <20231018155926.3305476-1-Frank.Li@nxp.com>
	<20231018155926.3305476-7-Frank.Li@nxp.com>
	<20231019084452.11fd0645@xps-13>
	<ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
	<20231020160645.67e678ee@xps-13>
	<ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
	<20231020163525.66485920@xps-13>
	<ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
	<20231020172006.5de0998b@xps-13>
	<ZTKhJOAAz4CaupEf@lizhi-Precision-Tower-5810>
Message-ID: <20231020190337.13ef5638@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Fri, 20 Oct 2023 11:47:48 -0400:

> On Fri, Oct 20, 2023 at 05:20:06PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:47:52 -0400:
> >   
> > > On Fri, Oct 20, 2023 at 04:35:25PM +0200, Miquel Raynal wrote:  
> > > > Hi Frank,
> > > > 
> > > > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:
> > > >     
> > > > > On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:    
> > > > > > Hi Frank,
> > > > > > 
> > > > > > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> > > > > >       
> > > > > > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:      
> > > > > > > > Hi Frank,
> > > > > > > > 
> > > > > > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > > > > > >         
> > > > > > > > > master side report:
> > > > > > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > > > > > 
> > > > > > > > > BIT 20: TIMEOUT error
> > > > > > > > >   The module has stalled too long in a frame. This happens when:
> > > > > > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > > > > > middle of a message,
> > > > > > > > >   - No STOP was issued and between messages,
> > > > > > > > >   - IBI manual is used and no decision was made.        
> > > > > > > > 
> > > > > > > > I am still not convinced this should be ignored in all cases.
> > > > > > > > 
> > > > > > > > Case 1 is a problem because the hardware failed somehow.        
> > > > > > > 
> > > > > > > But so far, no action to handle this case in current code.      
> > > > > > 
> > > > > > Yes, but if you detect an issue and ignore it, it's not better than
> > > > > > reporting it without handling it. Instead of totally ignoring this I
> > > > > > would at least write a debug message (identical to what's below) before
> > > > > > returning false, even though I am not convinced unconditionally
> > > > > > returning false here is wise. If you fail a hardware sequence because
> > > > > > you added a printk, it's a problem. Maybe you consider this line as
> > > > > > noise, but I believe it's still an error condition. Maybe, however,
> > > > > > this bit gets set after the whole sequence, and this is just a "bus
> > > > > > is idle" condition. If that's the case, then you need some
> > > > > > additional heuristics to properly ignore the bit?
> > > > > >       
> > > > > 
> > > > >                 dev_err(master->dev,                                       
> > > > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > > >                         mstatus, merrwarn);
> > > > > +
> > > > > +		/* ignore timeout error */
> > > > > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > > > > +			return false;
> > > > > +
> > > > > 
> > > > > Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?    
> > > > 
> > > > I think you mentioned earlier that the problem was not the printk but
> > > > the return value. So perhaps there is a way to know if the timeout
> > > > happened after a transaction and was legitimate or not?    
> > > 
> > > Error message just annoise user, don't impact function. But return false
> > > let IBI thread running to avoid dead lock. 
> > >   
> > > > 
> > > > In any case we should probably lower the log level for this error.    
> > > 
> > > Only SVC_I3C_MERRWARN_TIMEOUT is warning
> > > 
> > > Maybe below logic is better
> > > 
> > > 	if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
> > > 		dev_dbg(master->dev, 
> > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > 			mstatus, merrwarn);
> > > 		return false;
> > > 	} 
> > > 	
> > > 	dev_err(master->dev,                                     
> > >                 "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > >                  mstatus, merrwarn); 
> > > 	....
> > >   
> > 
> > Yes, this looks better but I wonder if we should add an additional
> > condition to just return false in this case;   
> 
> What's additional condition we can check?

Well, you're the one bothered with an error case which is not a real
error. You're saying "this error is never a problem" and I am saying
that I believe it is not a problem is your particular case, but in
general there might be situations where it *is* a problem. So you need
to find proper conditions to check against in order to determine
whether this is just an info with no consequence or an error.

> > something saying "this
> > timeout is legitimate and has no impact".  
> 
> Add comments "this timeout is legitimate and has no impact" or dev_dbg
> print that?

No I'm talking about the additional heuristics.

Thanks,
Miqu?l


From Frank.li at nxp.com  Fri Oct 20 12:58:25 2023
From: Frank.li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 15:58:25 -0400
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout errory
In-Reply-To: <20231020190337.13ef5638@xps-13>
References: <20231018155926.3305476-7-Frank.Li@nxp.com>
 <20231019084452.11fd0645@xps-13>
 <ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
 <20231020160645.67e678ee@xps-13>
 <ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
 <20231020163525.66485920@xps-13>
 <ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
 <20231020172006.5de0998b@xps-13>
 <ZTKhJOAAz4CaupEf@lizhi-Precision-Tower-5810>
 <20231020190337.13ef5638@xps-13>
Message-ID: <ZTLb4f4WieyLnb6m@lizhi-Precision-Tower-5810>

On Fri, Oct 20, 2023 at 07:03:37PM +0200, Miquel Raynal wrote:
> Hi Frank,
> 
> Frank.li at nxp.com wrote on Fri, 20 Oct 2023 11:47:48 -0400:
> 
> > On Fri, Oct 20, 2023 at 05:20:06PM +0200, Miquel Raynal wrote:
> > > Hi Frank,
> > > 
> > > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:47:52 -0400:
> > >   
> > > > On Fri, Oct 20, 2023 at 04:35:25PM +0200, Miquel Raynal wrote:  
> > > > > Hi Frank,
> > > > > 
> > > > > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:
> > > > >     
> > > > > > On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:    
> > > > > > > Hi Frank,
> > > > > > > 
> > > > > > > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> > > > > > >       
> > > > > > > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:      
> > > > > > > > > Hi Frank,
> > > > > > > > > 
> > > > > > > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > > > > > > >         
> > > > > > > > > > master side report:
> > > > > > > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > > > > > > 
> > > > > > > > > > BIT 20: TIMEOUT error
> > > > > > > > > >   The module has stalled too long in a frame. This happens when:
> > > > > > > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > > > > > > middle of a message,
> > > > > > > > > >   - No STOP was issued and between messages,
> > > > > > > > > >   - IBI manual is used and no decision was made.        
> > > > > > > > > 
> > > > > > > > > I am still not convinced this should be ignored in all cases.
> > > > > > > > > 
> > > > > > > > > Case 1 is a problem because the hardware failed somehow.        
> > > > > > > > 
> > > > > > > > But so far, no action to handle this case in current code.      
> > > > > > > 
> > > > > > > Yes, but if you detect an issue and ignore it, it's not better than
> > > > > > > reporting it without handling it. Instead of totally ignoring this I
> > > > > > > would at least write a debug message (identical to what's below) before
> > > > > > > returning false, even though I am not convinced unconditionally
> > > > > > > returning false here is wise. If you fail a hardware sequence because
> > > > > > > you added a printk, it's a problem. Maybe you consider this line as
> > > > > > > noise, but I believe it's still an error condition. Maybe, however,
> > > > > > > this bit gets set after the whole sequence, and this is just a "bus
> > > > > > > is idle" condition. If that's the case, then you need some
> > > > > > > additional heuristics to properly ignore the bit?
> > > > > > >       
> > > > > > 
> > > > > >                 dev_err(master->dev,                                       
> > > > > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > > > >                         mstatus, merrwarn);
> > > > > > +
> > > > > > +		/* ignore timeout error */
> > > > > > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > > > > > +			return false;
> > > > > > +
> > > > > > 
> > > > > > Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?    
> > > > > 
> > > > > I think you mentioned earlier that the problem was not the printk but
> > > > > the return value. So perhaps there is a way to know if the timeout
> > > > > happened after a transaction and was legitimate or not?    
> > > > 
> > > > Error message just annoise user, don't impact function. But return false
> > > > let IBI thread running to avoid dead lock. 
> > > >   
> > > > > 
> > > > > In any case we should probably lower the log level for this error.    
> > > > 
> > > > Only SVC_I3C_MERRWARN_TIMEOUT is warning
> > > > 
> > > > Maybe below logic is better
> > > > 
> > > > 	if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
> > > > 		dev_dbg(master->dev, 
> > > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > > 			mstatus, merrwarn);
> > > > 		return false;
> > > > 	} 
> > > > 	
> > > > 	dev_err(master->dev,                                     
> > > >                 "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > >                  mstatus, merrwarn); 
> > > > 	....
> > > >   
> > > 
> > > Yes, this looks better but I wonder if we should add an additional
> > > condition to just return false in this case;   
> > 
> > What's additional condition we can check?
> 
> Well, you're the one bothered with an error case which is not a real
> error. You're saying "this error is never a problem" and I am saying
> that I believe it is not a problem is your particular case, but in
> general there might be situations where it *is* a problem. So you need
> to find proper conditions to check against in order to determine
> whether this is just an info with no consequence or an error.

I checked R** code of this TIMEOUT, which is quite simple, set to 1 if SDA
is low over 100us if I understand correctly. I also checked, if I add delay
before emit stop, TIMEOUT will be set. (Read can auto emit stop accoring to
RDTERM, so just saw TIMEOUT at write transaction).

TIMEOUT just means condition "I3C bus's SDA low over 100us" happened since
written 1 to TIMEOUT.

I think "I3C bus's SDA over 100us" means nothing for linux drivers.

I think there are NO sitation where it *is* a problem. If it was problem,
there are NO solution to resolve it at linux driver side. And I think it
already happen many times silencely. 

Frank

> 
> > > something saying "this
> > > timeout is legitimate and has no impact".  
> > 
> > Add comments "this timeout is legitimate and has no impact" or dev_dbg
> > print that?
> 
> No I'm talking about the additional heuristics.
> 
> Thanks,
> Miqu?l


From alexandre.belloni at bootlin.com  Fri Oct 20 13:25:19 2023
From: alexandre.belloni at bootlin.com (Alexandre Belloni)
Date: Fri, 20 Oct 2023 22:25:19 +0200
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys
 entry
In-Reply-To: <8a7ac52e-f102-6f5e-35ab-217e6ecc6ba5@linux.intel.com>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
 <20231018205929.3435110-2-Frank.Li@nxp.com>
 <57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>
 <20231020154528.2f5f68f5@xps-13>
 <ZTKMyW939rB6sSKq@lizhi-Precision-Tower-5810>
 <20231020163348.1752167d@xps-13>
 <8a7ac52e-f102-6f5e-35ab-217e6ecc6ba5@linux.intel.com>
Message-ID: <20231020202519b2449707@mail.local>

On 20/10/2023 17:12:53+0200, Zbigniew, Lukwinski wrote:
> On 10/20/2023 4:33 PM, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.li at nxp.com  wrote on Fri, 20 Oct 2023 10:20:57 -0400:
> > 
> > > On Fri, Oct 20, 2023 at 03:45:28PM +0200, Miquel Raynal wrote:
> > > > Hi Lukwinski,
> > > > 
> > > > zbigniew.lukwinski at linux.intel.com  wrote on Fri, 20 Oct 2023 10:55:27
> > > > +0200:
> > > > > On 10/18/2023 10:59 PM, Frank Li wrote:
> > > > > > Add hotjoin entry in sys file system allow user enable/disable hotjoin
> > > > > > feature.
> > > > > > 
> > > > > > Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
> > > > > > Add api i3c_master_enable(disable)_hotjoin();
> > > > > What is the use case for having HJ enable knob in sysfs available for user space other than for debug stuff? In other words, does user space really need to enable/disable HJ in runtime for other reason but debug? If it is only for debug maybe it? could be move to debugFS?
> > > > I don't think hotjoin should be considered as a debug feature. The
> > > > problem here is the power consumption which is higher if you enable
> > > > this feature (you need to keep everything clocked and ready to handle
> > > > an IBI) whereas if your design is "fixed" (more like an I2C bus) you
> > > > may save power by disabling this feature.
> > > > 
> > > > A module parameter does not fit here because it's a per-bus
> > > > configuration.
> > > I agree. sys entry is more flexiable. and let controller choose better
> > > power saving policy for difference user case.
> > Maybe it's not the first time this case is faced, would you mind
> > including power management maintainers in this discussion? Perhaps they
> > might have pointers or even have the solution already.
> 
> I did not mind HJ as debug feature. But enabling / disabling the HJ sounds
> to me like debug option.
> 
> So the flow you are considering here is like this:?
> 
>     1. system boot with HJ enabled, so HJ works during initial bus
>        discovery
>     2. some entity in user space decides to disable HJ because power
>        consumption?
>     3. some entity in use space decide some time later to re-enable HJ
>        because some reason?
> 
> I am just wondering whether there is real use case when you starts with HJ
> enabled and than disable it
> 
> in runtime or start with HJ disabled and enable it in runtime. If you are
> taking care about power saving
> 
> ?let's keep HJ disabled all the time. Default state for HJ could be
> controlled by DT entry.
> 

This would be HW configuration and not HW description.


> 
> > Thanks,
> > Miqu?l

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


From Frank.li at nxp.com  Fri Oct 20 13:36:06 2023
From: Frank.li at nxp.com (Frank Li)
Date: Fri, 20 Oct 2023 16:36:06 -0400
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys
 entry
In-Reply-To: <20231020202519b2449707@mail.local>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
 <20231018205929.3435110-2-Frank.Li@nxp.com>
 <57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>
 <20231020154528.2f5f68f5@xps-13>
 <ZTKMyW939rB6sSKq@lizhi-Precision-Tower-5810>
 <20231020163348.1752167d@xps-13>
 <8a7ac52e-f102-6f5e-35ab-217e6ecc6ba5@linux.intel.com>
 <20231020202519b2449707@mail.local>
Message-ID: <ZTLktpI2rbAT4Ztb@lizhi-Precision-Tower-5810>

On Fri, Oct 20, 2023 at 10:25:19PM +0200, Alexandre Belloni wrote:
> On 20/10/2023 17:12:53+0200, Zbigniew, Lukwinski wrote:
> > On 10/20/2023 4:33 PM, Miquel Raynal wrote:
> > > Hi Frank,
> > > 
> > > Frank.li at nxp.com  wrote on Fri, 20 Oct 2023 10:20:57 -0400:
> > > 
> > > > On Fri, Oct 20, 2023 at 03:45:28PM +0200, Miquel Raynal wrote:
> > > > > Hi Lukwinski,
> > > > > 
> > > > > zbigniew.lukwinski at linux.intel.com  wrote on Fri, 20 Oct 2023 10:55:27
> > > > > +0200:
> > > > > > On 10/18/2023 10:59 PM, Frank Li wrote:
> > > > > > > Add hotjoin entry in sys file system allow user enable/disable hotjoin
> > > > > > > feature.
> > > > > > > 
> > > > > > > Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
> > > > > > > Add api i3c_master_enable(disable)_hotjoin();
> > > > > > What is the use case for having HJ enable knob in sysfs available for user space other than for debug stuff? In other words, does user space really need to enable/disable HJ in runtime for other reason but debug? If it is only for debug maybe it? could be move to debugFS?
> > > > > I don't think hotjoin should be considered as a debug feature. The
> > > > > problem here is the power consumption which is higher if you enable
> > > > > this feature (you need to keep everything clocked and ready to handle
> > > > > an IBI) whereas if your design is "fixed" (more like an I2C bus) you
> > > > > may save power by disabling this feature.
> > > > > 
> > > > > A module parameter does not fit here because it's a per-bus
> > > > > configuration.
> > > > I agree. sys entry is more flexiable. and let controller choose better
> > > > power saving policy for difference user case.
> > > Maybe it's not the first time this case is faced, would you mind
> > > including power management maintainers in this discussion? Perhaps they
> > > might have pointers or even have the solution already.
> > 
> > I did not mind HJ as debug feature. But enabling / disabling the HJ sounds
> > to me like debug option.
> > 
> > So the flow you are considering here is like this:?
> > 
> >     1. system boot with HJ enabled, so HJ works during initial bus
> >        discovery
> >     2. some entity in user space decides to disable HJ because power
> >        consumption?
> >     3. some entity in use space decide some time later to re-enable HJ
> >        because some reason?
> > 
> > I am just wondering whether there is real use case when you starts with HJ
> > enabled and than disable it

I think it is validate user case. Assume a I3C GPS device, user only use
it when open map. Before map application open, enable i3c hotjoin and
power on GPS module. After map application close, disable i3c hotjoin and
power off GPS module.

Frank

> > 
> > in runtime or start with HJ disabled and enable it in runtime. If you are
> > taking care about power saving
> > 
> > ?let's keep HJ disabled all the time. Default state for HJ could be
> > controlled by DT entry.
> > 
> 
> This would be HW configuration and not HW description.

Yes, DT maintainer may not accept this entry because it is not HW
description.

> 
> 
> > 
> > > Thanks,
> > > Miqu?l
> 
> -- 
> Alexandre Belloni, co-owner and COO, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com


From zbigniew.lukwinski at linux.intel.com  Fri Oct 20 13:42:20 2023
From: zbigniew.lukwinski at linux.intel.com (Zbigniew, Lukwinski)
Date: Fri, 20 Oct 2023 22:42:20 +0200
Subject: [PATCH v2 1/6] i3c: master: add enable(disable) hot join in sys
 entry
In-Reply-To: <ZTLktpI2rbAT4Ztb@lizhi-Precision-Tower-5810>
References: <20231018205929.3435110-1-Frank.Li@nxp.com>
 <20231018205929.3435110-2-Frank.Li@nxp.com>
 <57efe6de-cf65-32ed-fd1e-a388e1676c70@linux.intel.com>
 <20231020154528.2f5f68f5@xps-13>
 <ZTKMyW939rB6sSKq@lizhi-Precision-Tower-5810>
 <20231020163348.1752167d@xps-13>
 <8a7ac52e-f102-6f5e-35ab-217e6ecc6ba5@linux.intel.com>
 <20231020202519b2449707@mail.local>
 <ZTLktpI2rbAT4Ztb@lizhi-Precision-Tower-5810>
Message-ID: <86955c20-22e6-0d02-813f-02f0096c2819@linux.intel.com>

On 10/20/2023 10:36 PM, Frank Li wrote:
> On Fri, Oct 20, 2023 at 10:25:19PM +0200, Alexandre Belloni wrote:
>> On 20/10/2023 17:12:53+0200, Zbigniew, Lukwinski wrote:
>>> On 10/20/2023 4:33 PM, Miquel Raynal wrote:
>>>> Hi Frank,
>>>>
>>>> Frank.li at nxp.com  wrote on Fri, 20 Oct 2023 10:20:57 -0400:
>>>>
>>>>> On Fri, Oct 20, 2023 at 03:45:28PM +0200, Miquel Raynal wrote:
>>>>>> Hi Lukwinski,
>>>>>>
>>>>>> zbigniew.lukwinski at linux.intel.com  wrote on Fri, 20 Oct 2023 10:55:27
>>>>>> +0200:
>>>>>>> On 10/18/2023 10:59 PM, Frank Li wrote:
>>>>>>>> Add hotjoin entry in sys file system allow user enable/disable hotjoin
>>>>>>>> feature.
>>>>>>>>
>>>>>>>> Add (*enable(disable)_hotjoin)() to i3c_master_controller_ops.
>>>>>>>> Add api i3c_master_enable(disable)_hotjoin();
>>>>>>> What is the use case for having HJ enable knob in sysfs available for user space other than for debug stuff? In other words, does user space really need to enable/disable HJ in runtime for other reason but debug? If it is only for debug maybe it? could be move to debugFS?
>>>>>> I don't think hotjoin should be considered as a debug feature. The
>>>>>> problem here is the power consumption which is higher if you enable
>>>>>> this feature (you need to keep everything clocked and ready to handle
>>>>>> an IBI) whereas if your design is "fixed" (more like an I2C bus) you
>>>>>> may save power by disabling this feature.
>>>>>>
>>>>>> A module parameter does not fit here because it's a per-bus
>>>>>> configuration.
>>>>> I agree. sys entry is more flexiable. and let controller choose better
>>>>> power saving policy for difference user case.
>>>> Maybe it's not the first time this case is faced, would you mind
>>>> including power management maintainers in this discussion? Perhaps they
>>>> might have pointers or even have the solution already.
>>>
>>> I did not mind HJ as debug feature. But enabling / disabling the HJ sounds
>>> to me like debug option.
>>>
>>> So the flow you are considering here is like this:?
>>>
>>>      1. system boot with HJ enabled, so HJ works during initial bus
>>>         discovery
>>>      2. some entity in user space decides to disable HJ because power
>>>         consumption?
>>>      3. some entity in use space decide some time later to re-enable HJ
>>>         because some reason?
>>>
>>> I am just wondering whether there is real use case when you starts with HJ
>>> enabled and than disable it
> 
> I think it is validate user case. Assume a I3C GPS device, user only use
> it when open map. Before map application open, enable i3c hotjoin and
> power on GPS module. After map application close, disable i3c hotjoin and
> power off GPS module.
> 
Got it. I think we are on the same page. Thanks for explanations!
> Frank
> 
>>>
>>> in runtime or start with HJ disabled and enable it in runtime. If you are
>>> taking care about power saving
>>>
>>>  ?let's keep HJ disabled all the time. Default state for HJ could be
>>> controlled by DT entry.
>>>
>>
>> This would be HW configuration and not HW description.
> 
> Yes, DT maintainer may not accept this entry because it is not HW
> description.
> 
Sure. Makes sense.
>>
>>
>>>
>>>> Thanks,
>>>> Miqu?l
>>
>> -- 
>> Alexandre Belloni, co-owner and COO, Bootlin
>> Embedded Linux and Kernel engineering
>> https://bootlin.com



From gregkh at linuxfoundation.org  Sat Oct 21 10:02:40 2023
From: gregkh at linuxfoundation.org (Greg KH)
Date: Sat, 21 Oct 2023 19:02:40 +0200
Subject: [PATCH v2 1/1] tty: i3c: add TTY over I3C master support
In-Reply-To: <20231020160027.3663772-1-Frank.Li@nxp.com>
References: <20231020160027.3663772-1-Frank.Li@nxp.com>
Message-ID: <2023102105-outfit-legroom-1633@gregkh>

Note, your subject line needs to change.

On Fri, Oct 20, 2023 at 12:00:27PM -0400, Frank Li wrote:
> In typical embedded Linux systems, UART consoles require at least two pins,
> TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> present, we can save these two pins by using this driver. Pins is crucial

"Pins are crucial"

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

But where is the serial data ending up at?  Not a normal uart, what is
on the other end?  And do line settings mean anything here?

> 
> Signed-off-by: Frank Li <Frank.Li at nxp.com>
> ---
> 
> Notes:
>     This patch depend on
>     https://lore.kernel.org/imx/20231018205929.3435110-1-Frank.Li at nxp.com/T/#t

Let's wait for those to be accepted first, right?

> +static DEFINE_IDR(i3c_tty_minors);
> +static DEFINE_MUTEX(i3c_tty_minors_lock);

I thought idr didn't need a mutex anymore, are you sure this is still
needed?

> +static struct tty_driver *i3c_tty_driver;
> +
> +#define I3C_TTY_MINORS		256

Do you really need 256 minors?

> +#define I3C_TTY_TRANS_SIZE	16
> +#define I3C_TTY_RX_STOP		0
> +#define I3C_TTY_RETRY		20
> +#define I3C_TTY_YIELD_US	100
> +
> +struct ttyi3c_port {
> +	struct tty_port port;
> +	int minor;
> +	spinlock_t xlock; /* protect xmit */
> +	char tx_buff[I3C_TTY_TRANS_SIZE];
> +	char rx_buff[I3C_TTY_TRANS_SIZE];
> +	struct i3c_device *i3cdev;
> +	struct work_struct txwork;
> +	struct work_struct rxwork;
> +	struct completion txcomplete;
> +	unsigned long status;
> +	int buf_overrun;

You set buf_overrun but never do anything with it.  Why care about it?

Other than these minor things, looks sane, nice work.

thanks,

greg k-h


From miquel.raynal at bootlin.com  Mon Oct 23 00:48:53 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Mon, 23 Oct 2023 09:48:53 +0200
Subject: [PATCH v2 Resent 6/6] i3c: master: svc: fix random hot join
 failure since timeout errory
In-Reply-To: <ZTLb4f4WieyLnb6m@lizhi-Precision-Tower-5810>
References: <20231018155926.3305476-7-Frank.Li@nxp.com>
	<20231019084452.11fd0645@xps-13>
	<ZTFNvrsSoHOS3P2g@lizhi-Precision-Tower-5810>
	<20231020160645.67e678ee@xps-13>
	<ZTKMTwU6cVAfGCKG@lizhi-Precision-Tower-5810>
	<20231020163525.66485920@xps-13>
	<ZTKTGG7J55Sj19Nd@lizhi-Precision-Tower-5810>
	<20231020172006.5de0998b@xps-13>
	<ZTKhJOAAz4CaupEf@lizhi-Precision-Tower-5810>
	<20231020190337.13ef5638@xps-13>
	<ZTLb4f4WieyLnb6m@lizhi-Precision-Tower-5810>
Message-ID: <20231023094853.2510967f@xps-13>

Hi Frank,

Frank.li at nxp.com wrote on Fri, 20 Oct 2023 15:58:25 -0400:

> On Fri, Oct 20, 2023 at 07:03:37PM +0200, Miquel Raynal wrote:
> > Hi Frank,
> > 
> > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 11:47:48 -0400:
> >   
> > > On Fri, Oct 20, 2023 at 05:20:06PM +0200, Miquel Raynal wrote:  
> > > > Hi Frank,
> > > > 
> > > > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:47:52 -0400:
> > > >     
> > > > > On Fri, Oct 20, 2023 at 04:35:25PM +0200, Miquel Raynal wrote:    
> > > > > > Hi Frank,
> > > > > > 
> > > > > > Frank.li at nxp.com wrote on Fri, 20 Oct 2023 10:18:55 -0400:
> > > > > >       
> > > > > > > On Fri, Oct 20, 2023 at 04:06:45PM +0200, Miquel Raynal wrote:      
> > > > > > > > Hi Frank,
> > > > > > > > 
> > > > > > > > Frank.li at nxp.com wrote on Thu, 19 Oct 2023 11:39:42 -0400:
> > > > > > > >         
> > > > > > > > > On Thu, Oct 19, 2023 at 08:44:52AM +0200, Miquel Raynal wrote:        
> > > > > > > > > > Hi Frank,
> > > > > > > > > > 
> > > > > > > > > > Frank.Li at nxp.com wrote on Wed, 18 Oct 2023 11:59:26 -0400:
> > > > > > > > > >           
> > > > > > > > > > > master side report:
> > > > > > > > > > >   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> > > > > > > > > > > 
> > > > > > > > > > > BIT 20: TIMEOUT error
> > > > > > > > > > >   The module has stalled too long in a frame. This happens when:
> > > > > > > > > > >   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> > > > > > > > > > > middle of a message,
> > > > > > > > > > >   - No STOP was issued and between messages,
> > > > > > > > > > >   - IBI manual is used and no decision was made.          
> > > > > > > > > > 
> > > > > > > > > > I am still not convinced this should be ignored in all cases.
> > > > > > > > > > 
> > > > > > > > > > Case 1 is a problem because the hardware failed somehow.          
> > > > > > > > > 
> > > > > > > > > But so far, no action to handle this case in current code.        
> > > > > > > > 
> > > > > > > > Yes, but if you detect an issue and ignore it, it's not better than
> > > > > > > > reporting it without handling it. Instead of totally ignoring this I
> > > > > > > > would at least write a debug message (identical to what's below) before
> > > > > > > > returning false, even though I am not convinced unconditionally
> > > > > > > > returning false here is wise. If you fail a hardware sequence because
> > > > > > > > you added a printk, it's a problem. Maybe you consider this line as
> > > > > > > > noise, but I believe it's still an error condition. Maybe, however,
> > > > > > > > this bit gets set after the whole sequence, and this is just a "bus
> > > > > > > > is idle" condition. If that's the case, then you need some
> > > > > > > > additional heuristics to properly ignore the bit?
> > > > > > > >         
> > > > > > > 
> > > > > > >                 dev_err(master->dev,                                       
> > > > > > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > > > > >                         mstatus, merrwarn);
> > > > > > > +
> > > > > > > +		/* ignore timeout error */
> > > > > > > +		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT)
> > > > > > > +			return false;
> > > > > > > +
> > > > > > > 
> > > > > > > Is it okay move SVC_I3C_MERRWARN_TIMEOUT after dev_err?      
> > > > > > 
> > > > > > I think you mentioned earlier that the problem was not the printk but
> > > > > > the return value. So perhaps there is a way to know if the timeout
> > > > > > happened after a transaction and was legitimate or not?      
> > > > > 
> > > > > Error message just annoise user, don't impact function. But return false
> > > > > let IBI thread running to avoid dead lock. 
> > > > >     
> > > > > > 
> > > > > > In any case we should probably lower the log level for this error.      
> > > > > 
> > > > > Only SVC_I3C_MERRWARN_TIMEOUT is warning
> > > > > 
> > > > > Maybe below logic is better
> > > > > 
> > > > > 	if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
> > > > > 		dev_dbg(master->dev, 
> > > > >                         "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > > > 			mstatus, merrwarn);
> > > > > 		return false;
> > > > > 	} 
> > > > > 	
> > > > > 	dev_err(master->dev,                                     
> > > > >                 "Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
> > > > >                  mstatus, merrwarn); 
> > > > > 	....
> > > > >     
> > > > 
> > > > Yes, this looks better but I wonder if we should add an additional
> > > > condition to just return false in this case;     
> > > 
> > > What's additional condition we can check?  
> > 
> > Well, you're the one bothered with an error case which is not a real
> > error. You're saying "this error is never a problem" and I am saying
> > that I believe it is not a problem is your particular case, but in
> > general there might be situations where it *is* a problem. So you need
> > to find proper conditions to check against in order to determine
> > whether this is just an info with no consequence or an error.  
> 
> I checked R** code of this TIMEOUT, which is quite simple, set to 1 if SDA
> is low over 100us if I understand correctly. I also checked, if I add delay
> before emit stop, TIMEOUT will be set. (Read can auto emit stop accoring to
> RDTERM, so just saw TIMEOUT at write transaction).
> 
> TIMEOUT just means condition "I3C bus's SDA low over 100us" happened since
> written 1 to TIMEOUT.
> 
> I think "I3C bus's SDA over 100us" means nothing for linux drivers.
> 
> I think there are NO sitation where it *is* a problem. If it was problem,
> there are NO solution to resolve it at linux driver side. And I think it
> already happen many times silencely. 

Ok then, I'll opt for your last proposal of printing the error message
at the debug loglevel and return false.

Thanks,
Miqu?l


From Frank.Li at nxp.com  Mon Oct 23 09:16:52 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 23 Oct 2023 12:16:52 -0400
Subject: [PATCH v4 0/6] i3c: master: svc: collection of bugs fixes
Message-ID: <20231023161658.3890811-1-Frank.Li@nxp.com>

Each patch is indepedents. See commit message for detail.

Change from v1 to v4.
See each patch notes

If there are not notes between v2 to v4, that's means not change.

Frank Li (6):
  i3c: master: svc: fix race condition in ibi work thread
  i3c: master: svc: fix wrong data return when IBI happen during start
    frame
  i3c: master: svc: fix ibi may not return mandatory data byte
  i3c: master: svc: fix check wrong status register in irq handler
  i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
  i3c: master: svc: fix random hot join failure since timeout error

 drivers/i3c/master/svc-i3c-master.c | 54 ++++++++++++++++++++++++++++-
 1 file changed, 53 insertions(+), 1 deletion(-)

-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 23 09:16:53 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 23 Oct 2023 12:16:53 -0400
Subject: [PATCH v4 1/6] i3c: master: svc: fix race condition in ibi work thread
In-Reply-To: <20231023161658.3890811-1-Frank.Li@nxp.com>
References: <20231023161658.3890811-1-Frank.Li@nxp.com>
Message-ID: <20231023161658.3890811-2-Frank.Li@nxp.com>

The ibi work thread operates asynchronously with other transfers, such as
svc_i3c_master_priv_xfers(). Introduce mutex protection to ensure the
completion of the entire i3c/i2c transaction.

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

Notes:
    Change from v1 to v2
    - update commit message
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index d29de5fe533e6..8cd708c965085 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -174,6 +174,7 @@ struct svc_i3c_regs_save {
  * @ibi.slots: Available IBI slots
  * @ibi.tbq_slot: To be queued IBI slot
  * @ibi.lock: IBI lock
+ * @lock: Transfer lock, protect between IBI work thread and callbacks from master
  */
 struct svc_i3c_master {
 	struct i3c_master_controller base;
@@ -202,6 +203,7 @@ struct svc_i3c_master {
 		/* Prevent races within IBI handlers */
 		spinlock_t lock;
 	} ibi;
+	struct mutex lock;
 };
 
 /**
@@ -383,6 +385,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 	u32 status, val;
 	int ret;
 
+	mutex_lock(&master->lock);
 	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
 	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
 	       SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -459,6 +462,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 
 reenable_ibis:
 	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+	mutex_unlock(&master->lock);
 }
 
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
@@ -1203,9 +1207,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = 0;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	kfree(buf);
@@ -1249,9 +1255,11 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	cmd->read_len = read_len;
 	cmd->continued = false;
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	if (cmd->read_len != xfer_len)
 		ccc->dests[0].payload.len = cmd->read_len;
@@ -1308,9 +1316,11 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 		cmd->continued = (i + 1) < nxfers;
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1346,9 +1356,11 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 		cmd->continued = (i + 1 < nxfers);
 	}
 
+	mutex_lock(&master->lock);
 	svc_i3c_master_enqueue_xfer(master, xfer);
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
+	mutex_unlock(&master->lock);
 
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
@@ -1539,6 +1551,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
 	INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
 	INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
+	mutex_init(&master->lock);
+
 	ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
 			       IRQF_NO_SUSPEND, "svc-i3c-irq", master);
 	if (ret)
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 23 09:16:54 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 23 Oct 2023 12:16:54 -0400
Subject: [PATCH v4 2/6] i3c: master: svc: fix wrong data return when IBI happen during start frame
In-Reply-To: <20231023161658.3890811-1-Frank.Li@nxp.com>
References: <20231023161658.3890811-1-Frank.Li@nxp.com>
Message-ID: <20231023161658.3890811-3-Frank.Li@nxp.com>

     ???????     ????  ????  ????  ????  ????  ????  ????  ????  ??????
SCL: ?     ???????  ????  ????  ????  ????  ????  ????  ????  ????
     ????                       ???????     ???????     ?????????????
SDA:    ?????????????????????????     ???????     ???????           ??????
     xxx?    ??                                        ??    ??    ??    ?
   : xxx?IBI ??               Addr(0x0a)               ?? RW ??NACK?? S  ?

If an In-Band Interrupt (IBI) occurs and IBI work thread is not immediately
scheduled, when svc_i3c_master_priv_xfers() initiates the I3C transfer and
attempts to send address 0x7e, the target interprets it as an
IBI handler and returns the target address 0x0a.

However, svc_i3c_master_priv_xfers() does not handle this case and proceeds
with other transfers, resulting in incorrect data being returned.

Add IBIWON check in svc_i3c_master_xfer(). In case this situation occurs,
return a failure to the driver.

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

Notes:
    Change from v3 to v4
    - When -> when
    
    Change from v1 to v2
    - update commit message
    - fix typo yeild and falure
    
    Change from v1 to v2
    - update commit message
    - fix typo yeild and falure

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 8cd708c965085..abebef666b2bb 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1010,6 +1010,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	u32 reg;
 	int ret;
 
+	/* clean SVC_I3C_MINT_IBIWON w1c bits */
+	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
 	writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
 	       xfer_type |
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
@@ -1028,6 +1031,23 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		goto emit_stop;
 	}
 
+	/*
+	 * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
+	 * with I3C Target Address.
+	 *
+	 * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
+	 * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
+	 * Role Request (i.e., Secondary Controller requests to become the Active Controller), or
+	 * a Hot-Join Request has been made.
+	 *
+	 * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure
+	 * and yield the above events handler.
+	 */
+	if (SVC_I3C_MSTATUS_IBIWON(reg)) {
+		ret = -ENXIO;
+		goto emit_stop;
+	}
+
 	if (rnw)
 		ret = svc_i3c_master_read(master, in, xfer_len);
 	else
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 23 09:16:55 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 23 Oct 2023 12:16:55 -0400
Subject: [PATCH v4 3/6] i3c: master: svc: fix ibi may not return mandatory data byte
In-Reply-To: <20231023161658.3890811-1-Frank.Li@nxp.com>
References: <20231023161658.3890811-1-Frank.Li@nxp.com>
Message-ID: <20231023161658.3890811-4-Frank.Li@nxp.com>

MSTATUS[RXPEND] is only updated after the data transfer cycle started. This
creates an issue when the I3C clock is slow, and the CPU is running fast
enough that MSTATUS[RXPEND] may not be updated when the code reaches
checking point. As a result, mandatory data can be missed.

Add a wait for MSTATUS[COMPLETE] to ensure that all mandatory data is
already in FIFO. It also works without mandatory data.

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

Notes:
    Change from v3 to v4
    - add review tag Miquel
    
    Change from v2 to v3
    - update commit message and add no mandatory data in commits message
    
    Change from v1 to v2
    - update commit message
      it also works without mandatory bytes
    
    Change from v1 to v2
    - update commit message
      it also works without mandatory bytes
    
    Change from v2 to v3
    - update commit message and add no mandatory data in commits message
    
    Change from v1 to v2
    - update commit message
      it also works without mandatory bytes
    
    Change from v1 to v2
    - update commit message
      it also works without mandatory bytes

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index abebef666b2bb..dd06b7c9333f1 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -332,6 +332,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	struct i3c_ibi_slot *slot;
 	unsigned int count;
 	u32 mdatactrl;
+	int ret, val;
 	u8 *buf;
 
 	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
@@ -341,6 +342,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 	slot->len = 0;
 	buf = slot->data;
 
+	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
+						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
+	if (ret) {
+		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
+		return ret;
+	}
+
 	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
 	       slot->len < SVC_I3C_FIFO_SIZE) {
 		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 23 09:16:56 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 23 Oct 2023 12:16:56 -0400
Subject: [PATCH v4 4/6] i3c: master: svc: fix check wrong status register in irq handler
In-Reply-To: <20231023161658.3890811-1-Frank.Li@nxp.com>
References: <20231023161658.3890811-1-Frank.Li@nxp.com>
Message-ID: <20231023161658.3890811-5-Frank.Li@nxp.com>

svc_i3c_master_irq_handler() wrongly checks register SVC_I3C_MINTMASKED. It
should be SVC_I3C_MSTATUS.

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

Notes:
    Change from v2 to v3
    - 'wrong check' -> 'wrongly checks'
    
    Change from v1 to v2
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>
    
    Change from v1 to v2
    - Add Reviewed-by: Miquel Raynal <miquel.raynal at bootlin.com>

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index dd06b7c9333f1..b113460f059c3 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -476,7 +476,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
 {
 	struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
-	u32 active = readl(master->regs + SVC_I3C_MINTMASKED);
+	u32 active = readl(master->regs + SVC_I3C_MSTATUS);
 
 	if (!SVC_I3C_MSTATUS_SLVSTART(active))
 		return IRQ_NONE;
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 23 09:16:57 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 23 Oct 2023 12:16:57 -0400
Subject: [PATCH v4 5/6] i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
In-Reply-To: <20231023161658.3890811-1-Frank.Li@nxp.com>
References: <20231023161658.3890811-1-Frank.Li@nxp.com>
Message-ID: <20231023161658.3890811-6-Frank.Li@nxp.com>

Upon IBIWON timeout, the SDA line will always be kept low if we don't emit
a stop. Calling svc_i3c_master_emit_stop() there will let the bus return to
idle state.

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

Notes:
    Change from v3 to v4
    - remove duplidate in commit message
     Call svc_i3c_master_emit_stop() to let i3c bus come back to idle statue
     when IBIWON timeout happen.
    
    Change from v2 to v3
    - Update commit message and add review tag
    
    Change from v1 to v2
    - Update commite message
    
    Change from v1 to v2
    - Update commite message
    
    Change from v2 to v3
    - Update commit message and add review tag
    
    Change from v1 to v2
    - Update commite message
    
    Change from v1 to v2
    - Update commite message

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index b113460f059c3..1a57fdebaa26d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -404,6 +404,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
 	if (ret) {
 		dev_err(master->dev, "Timeout when polling for IBIWON\n");
+		svc_i3c_master_emit_stop(master);
 		goto reenable_ibis;
 	}
 
-- 
2.34.1



From Frank.Li at nxp.com  Mon Oct 23 09:16:58 2023
From: Frank.Li at nxp.com (Frank Li)
Date: Mon, 23 Oct 2023 12:16:58 -0400
Subject: [PATCH v4 6/6] i3c: master: svc: fix random hot join failure since timeout error
In-Reply-To: <20231023161658.3890811-1-Frank.Li@nxp.com>
References: <20231023161658.3890811-1-Frank.Li@nxp.com>
Message-ID: <20231023161658.3890811-7-Frank.Li@nxp.com>

master side report:
  silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000

BIT 20: TIMEOUT error
  The module has stalled too long in a frame. This happens when:
  - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
middle of a message,
  - No STOP was issued and between messages,
  - IBI manual is used and no decision was made.
  The maximum stall period is 100 ?s.

This can be considered as being just a warning as the system IRQ latency
can easily be greater than 100us.

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

Notes:
    Change from v3 to v4
    - fixed missed update commit message
     Add dev_dbg for TIMEOUT case
     This can be considered as being just a warning as the system IRQ latency
    can easily be greater than 100us.
    
    Change from v2 to v3
    - remove 10k at commit message
    
    Change from v1 to v2
    -none
    
    Change from v1 to v2
    -none
    
    Change from v2 to v3
    - remove 10k at commit message
    
    Change from v1 to v2
    -none
    
    Change from v1 to v2
    -none

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

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 1a57fdebaa26d..6b6bdd163af4f 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -93,6 +93,7 @@
 #define SVC_I3C_MINTMASKED   0x098
 #define SVC_I3C_MERRWARN     0x09C
 #define   SVC_I3C_MERRWARN_NACK BIT(2)
+#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
 #define SVC_I3C_MDMACTRL     0x0A0
 #define SVC_I3C_MDATACTRL    0x0AC
 #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
@@ -226,6 +227,14 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
 	if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
 		merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
 		writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
+
+		/* Ignore timeout error */
+		if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
+			dev_dbg(master->dev, "Warning condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
+				mstatus, merrwarn);
+			return false;
+		}
+
 		dev_err(master->dev,
 			"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
 			mstatus, merrwarn);
-- 
2.34.1



From Frank.li at nxp.com  Mon Oct 23 09:26:42 2023
From: Frank.li at nxp.com (Frank Li)
Date: Mon, 23 Oct 2023 12:26:42 -0400
Subject: [PATCH v2 1/1] tty: i3c: add TTY over I3C master support
In-Reply-To: <2023102105-outfit-legroom-1633@gregkh>
References: <20231020160027.3663772-1-Frank.Li@nxp.com>
 <2023102105-outfit-legroom-1633@gregkh>
Message-ID: <ZTaewidgtcDaBega@lizhi-Precision-Tower-5810>

On Sat, Oct 21, 2023 at 07:02:40PM +0200, Greg KH wrote:
> Note, your subject line needs to change.
> 
> On Fri, Oct 20, 2023 at 12:00:27PM -0400, Frank Li wrote:
> > In typical embedded Linux systems, UART consoles require at least two pins,
> > TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> > present, we can save these two pins by using this driver. Pins is crucial
> 
> "Pins are crucial"
> 
> > resources, especially in small chip packages.
> > 
> > This introduces support for using the I3C bus to transfer console tty data,
> > effectively replacing the need for dedicated UART pins. This not only
> > conserves valuable pin resources but also facilitates testing of I3C's
> > advanced features, including early termination, in-band interrupt (IBI)
> > support, and the creation of more complex data patterns. Additionally,
> > it aids in identifying and addressing issues within the I3C controller
> > driver.
> 
> But where is the serial data ending up at?  Not a normal uart, what is
> on the other end?  And do line settings mean anything here?

Currently, it use slave i3c code. 
https://lore.kernel.org/imx/20231018215809.3477437-1-Frank.Li at nxp.com/T/#t

idealy build an i3c->usb dongle to bride it to usb acm. 

> 
> > 
> > Signed-off-by: Frank Li <Frank.Li at nxp.com>
> > ---
> > 
> > Notes:
> >     This patch depend on
> >     https://lore.kernel.org/imx/20231018205929.3435110-1-Frank.Li at nxp.com/T/#t
> 
> Let's wait for those to be accepted first, right?

Yes.

> 
> > +static DEFINE_IDR(i3c_tty_minors);
> > +static DEFINE_MUTEX(i3c_tty_minors_lock);
> 
> I thought idr didn't need a mutex anymore, are you sure this is still
> needed?
> 
> > +static struct tty_driver *i3c_tty_driver;
> > +
> > +#define I3C_TTY_MINORS		256
> 
> Do you really need 256 minors?

Any resource concern about it. Maybe 32/64 is enough. I refer from USB tty
driver.

> 
> > +#define I3C_TTY_TRANS_SIZE	16
> > +#define I3C_TTY_RX_STOP		0
> > +#define I3C_TTY_RETRY		20
> > +#define I3C_TTY_YIELD_US	100
> > +
> > +struct ttyi3c_port {
> > +	struct tty_port port;
> > +	int minor;
> > +	spinlock_t xlock; /* protect xmit */
> > +	char tx_buff[I3C_TTY_TRANS_SIZE];
> > +	char rx_buff[I3C_TTY_TRANS_SIZE];
> > +	struct i3c_device *i3cdev;
> > +	struct work_struct txwork;
> > +	struct work_struct rxwork;
> > +	struct completion txcomplete;
> > +	unsigned long status;
> > +	int buf_overrun;
> 
> You set buf_overrun but never do anything with it.  Why care about it?
> 
> Other than these minor things, looks sane, nice work.
> 
> thanks,
> 
> greg k-h


From miquel.raynal at bootlin.com  Mon Oct 23 12:10:28 2023
From: miquel.raynal at bootlin.com (Miquel Raynal)
Date: Mon, 23 Oct 2023 21:10:28 +0200
Subject: [PATCH v4 6/6] i3c: master: svc: fix random hot join failure
 since timeout error
In-Reply-To: <20231023161658.3890811-7-Frank.Li@nxp.com>
References: <20231023161658.3890811-1-Frank.Li@nxp.com>
	<20231023161658.3890811-7-Frank.Li@nxp.com>
Message-ID: <20231023211028.3a4613df@xps-13>

Hi Frank,

Frank.Li at nxp.com wrote on Mon, 23 Oct 2023 12:16:58 -0400:

> master side report:
>   silvaco-i3c-master 44330000.i3c-master: Error condition: MSTATUS 0x020090c7, MERRWARN 0x00100000
> 
> BIT 20: TIMEOUT error
>   The module has stalled too long in a frame. This happens when:
>   - The TX FIFO or RX FIFO is not handled and the bus is stuck in the
> middle of a message,
>   - No STOP was issued and between messages,
>   - IBI manual is used and no decision was made.
>   The maximum stall period is 100 ?s.
> 
> This can be considered as being just a warning as the system IRQ latency
> can easily be greater than 100us.
> 
> Fixes: dd3c52846d59 ("i3c: master: svc: Add Silvaco I3C master driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Frank Li <Frank.Li at nxp.com>

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

Thanks,
Miqu?l


From gregkh at linuxfoundation.org  Tue Oct 24 02:30:33 2023
From: gregkh at linuxfoundation.org (Greg KH)
Date: Tue, 24 Oct 2023 11:30:33 +0200
Subject: [PATCH v2 1/1] tty: i3c: add TTY over I3C master support
In-Reply-To: <ZTaewidgtcDaBega@lizhi-Precision-Tower-5810>
References: <20231020160027.3663772-1-Frank.Li@nxp.com>
 <2023102105-outfit-legroom-1633@gregkh>
 <ZTaewidgtcDaBega@lizhi-Precision-Tower-5810>
Message-ID: <2023102457-galore-uphill-4c84@gregkh>

On Mon, Oct 23, 2023 at 12:26:42PM -0400, Frank Li wrote:
> On Sat, Oct 21, 2023 at 07:02:40PM +0200, Greg KH wrote:
> > Note, your subject line needs to change.
> > 
> > On Fri, Oct 20, 2023 at 12:00:27PM -0400, Frank Li wrote:
> > > In typical embedded Linux systems, UART consoles require at least two pins,
> > > TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> > > present, we can save these two pins by using this driver. Pins is crucial
> > 
> > "Pins are crucial"
> > 
> > > resources, especially in small chip packages.
> > > 
> > > This introduces support for using the I3C bus to transfer console tty data,
> > > effectively replacing the need for dedicated UART pins. This not only
> > > conserves valuable pin resources but also facilitates testing of I3C's
> > > advanced features, including early termination, in-band interrupt (IBI)
> > > support, and the creation of more complex data patterns. Additionally,
> > > it aids in identifying and addressing issues within the I3C controller
> > > driver.
> > 
> > But where is the serial data ending up at?  Not a normal uart, what is
> > on the other end?  And do line settings mean anything here?
> 
> Currently, it use slave i3c code. 
> https://lore.kernel.org/imx/20231018215809.3477437-1-Frank.Li at nxp.com/T/#t
> 
> idealy build an i3c->usb dongle to bride it to usb acm. 

So no one has built such a thing yet to determine if any of this works?

> > > +static DEFINE_IDR(i3c_tty_minors);
> > > +static DEFINE_MUTEX(i3c_tty_minors_lock);
> > 
> > I thought idr didn't need a mutex anymore, are you sure this is still
> > needed?
> > 
> > > +static struct tty_driver *i3c_tty_driver;
> > > +
> > > +#define I3C_TTY_MINORS		256
> > 
> > Do you really need 256 minors?
> 
> Any resource concern about it. Maybe 32/64 is enough. I refer from USB tty
> driver.

USB serial devices are quite common, and in some places, replaced PCI
serial cards for modem connections.  So for them, we do actually use all
256 minors.

But for this, it's a debugging device, how are you going to have so many
different debugging ports on a system at once?

How about making it small, like 8, and see if you ever actually exceed
that in real life?

thanks,

greg k-h


From Frank.li at nxp.com  Tue Oct 24 07:31:51 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 24 Oct 2023 10:31:51 -0400
Subject: [PATCH v2 1/1] tty: i3c: add TTY over I3C master support
In-Reply-To: <2023102457-galore-uphill-4c84@gregkh>
References: <20231020160027.3663772-1-Frank.Li@nxp.com>
 <2023102105-outfit-legroom-1633@gregkh>
 <ZTaewidgtcDaBega@lizhi-Precision-Tower-5810>
 <2023102457-galore-uphill-4c84@gregkh>
Message-ID: <ZTfVV3DW8jqH6ek9@lizhi-Precision-Tower-5810>

On Tue, Oct 24, 2023 at 11:30:33AM +0200, Greg KH wrote:
> On Mon, Oct 23, 2023 at 12:26:42PM -0400, Frank Li wrote:
> > On Sat, Oct 21, 2023 at 07:02:40PM +0200, Greg KH wrote:
> > > Note, your subject line needs to change.
> > > 
> > > On Fri, Oct 20, 2023 at 12:00:27PM -0400, Frank Li wrote:
> > > > In typical embedded Linux systems, UART consoles require at least two pins,
> > > > TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> > > > present, we can save these two pins by using this driver. Pins is crucial
> > > 
> > > "Pins are crucial"
> > > 
> > > > resources, especially in small chip packages.
> > > > 
> > > > This introduces support for using the I3C bus to transfer console tty data,
> > > > effectively replacing the need for dedicated UART pins. This not only
> > > > conserves valuable pin resources but also facilitates testing of I3C's
> > > > advanced features, including early termination, in-band interrupt (IBI)
> > > > support, and the creation of more complex data patterns. Additionally,
> > > > it aids in identifying and addressing issues within the I3C controller
> > > > driver.
> > > 
> > > But where is the serial data ending up at?  Not a normal uart, what is
> > > on the other end?  And do line settings mean anything here?
> > 
> > Currently, it use slave i3c code. 
> > https://lore.kernel.org/imx/20231018215809.3477437-1-Frank.Li at nxp.com/T/#t
> > 
> > idealy build an i3c->usb dongle to bride it to usb acm. 
> 
> So no one has built such a thing yet to determine if any of this works?

It is easy to proof concept by I3C slave code and USB gadget ACM, then pipe
two tty (ttyACM0 and ttySI3C0 together).

Of we also can implement a USB to I3C class standard, base on this, reuse
this tty driver at host side.

Frank

> 
> > > > +static DEFINE_IDR(i3c_tty_minors);
> > > > +static DEFINE_MUTEX(i3c_tty_minors_lock);
> > > 
> > > I thought idr didn't need a mutex anymore, are you sure this is still
> > > needed?
> > > 
> > > > +static struct tty_driver *i3c_tty_driver;
> > > > +
> > > > +#define I3C_TTY_MINORS		256
> > > 
> > > Do you really need 256 minors?
> > 
> > Any resource concern about it. Maybe 32/64 is enough. I refer from USB tty
> > driver.
> 
> USB serial devices are quite common, and in some places, replaced PCI
> serial cards for modem connections.  So for them, we do actually use all
> 256 minors.
> 
> But for this, it's a debugging device, how are you going to have so many
> different debugging ports on a system at once?
> 
> How about making it small, like 8, and see if you ever actually exceed
> that in real life?
> 
> thanks,
> 
> greg k-h


From gregkh at linuxfoundation.org  Tue Oct 24 08:05:47 2023
From: gregkh at linuxfoundation.org (Greg KH)
Date: Tue, 24 Oct 2023 17:05:47 +0200
Subject: [PATCH v2 1/1] tty: i3c: add TTY over I3C master support
In-Reply-To: <ZTfVV3DW8jqH6ek9@lizhi-Precision-Tower-5810>
References: <20231020160027.3663772-1-Frank.Li@nxp.com>
 <2023102105-outfit-legroom-1633@gregkh>
 <ZTaewidgtcDaBega@lizhi-Precision-Tower-5810>
 <2023102457-galore-uphill-4c84@gregkh>
 <ZTfVV3DW8jqH6ek9@lizhi-Precision-Tower-5810>
Message-ID: <2023102442-immorally-repost-e736@gregkh>

On Tue, Oct 24, 2023 at 10:31:51AM -0400, Frank Li wrote:
> On Tue, Oct 24, 2023 at 11:30:33AM +0200, Greg KH wrote:
> > On Mon, Oct 23, 2023 at 12:26:42PM -0400, Frank Li wrote:
> > > On Sat, Oct 21, 2023 at 07:02:40PM +0200, Greg KH wrote:
> > > > Note, your subject line needs to change.
> > > > 
> > > > On Fri, Oct 20, 2023 at 12:00:27PM -0400, Frank Li wrote:
> > > > > In typical embedded Linux systems, UART consoles require at least two pins,
> > > > > TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> > > > > present, we can save these two pins by using this driver. Pins is crucial
> > > > 
> > > > "Pins are crucial"
> > > > 
> > > > > resources, especially in small chip packages.
> > > > > 
> > > > > This introduces support for using the I3C bus to transfer console tty data,
> > > > > effectively replacing the need for dedicated UART pins. This not only
> > > > > conserves valuable pin resources but also facilitates testing of I3C's
> > > > > advanced features, including early termination, in-band interrupt (IBI)
> > > > > support, and the creation of more complex data patterns. Additionally,
> > > > > it aids in identifying and addressing issues within the I3C controller
> > > > > driver.
> > > > 
> > > > But where is the serial data ending up at?  Not a normal uart, what is
> > > > on the other end?  And do line settings mean anything here?
> > > 
> > > Currently, it use slave i3c code. 
> > > https://lore.kernel.org/imx/20231018215809.3477437-1-Frank.Li at nxp.com/T/#t
> > > 
> > > idealy build an i3c->usb dongle to bride it to usb acm. 
> > 
> > So no one has built such a thing yet to determine if any of this works?
> 
> It is easy to proof concept by I3C slave code and USB gadget ACM, then pipe
> two tty (ttyACM0 and ttySI3C0 together).

So you have not actually tested this?  why write a driver that no one is
using?

> Of we also can implement a USB to I3C class standard, base on this, reuse
> this tty driver at host side.

Is there a USB I3C standard?  I see i3c descriptors assigned by the
USB-IF, but haven't dug to see if there's more than that anywhere...

thanks,

greg k-h


From Frank.li at nxp.com  Tue Oct 24 08:59:53 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 24 Oct 2023 11:59:53 -0400
Subject: [PATCH v2 1/1] tty: i3c: add TTY over I3C master support
In-Reply-To: <2023102442-immorally-repost-e736@gregkh>
References: <20231020160027.3663772-1-Frank.Li@nxp.com>
 <2023102105-outfit-legroom-1633@gregkh>
 <ZTaewidgtcDaBega@lizhi-Precision-Tower-5810>
 <2023102457-galore-uphill-4c84@gregkh>
 <ZTfVV3DW8jqH6ek9@lizhi-Precision-Tower-5810>
 <2023102442-immorally-repost-e736@gregkh>
Message-ID: <ZTfp+c60YOiwDUFd@lizhi-Precision-Tower-5810>

On Tue, Oct 24, 2023 at 05:05:47PM +0200, Greg KH wrote:
> On Tue, Oct 24, 2023 at 10:31:51AM -0400, Frank Li wrote:
> > On Tue, Oct 24, 2023 at 11:30:33AM +0200, Greg KH wrote:
> > > On Mon, Oct 23, 2023 at 12:26:42PM -0400, Frank Li wrote:
> > > > On Sat, Oct 21, 2023 at 07:02:40PM +0200, Greg KH wrote:
> > > > > Note, your subject line needs to change.
> > > > > 
> > > > > On Fri, Oct 20, 2023 at 12:00:27PM -0400, Frank Li wrote:
> > > > > > In typical embedded Linux systems, UART consoles require at least two pins,
> > > > > > TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> > > > > > present, we can save these two pins by using this driver. Pins is crucial
> > > > > 
> > > > > "Pins are crucial"
> > > > > 
> > > > > > resources, especially in small chip packages.
> > > > > > 
> > > > > > This introduces support for using the I3C bus to transfer console tty data,
> > > > > > effectively replacing the need for dedicated UART pins. This not only
> > > > > > conserves valuable pin resources but also facilitates testing of I3C's
> > > > > > advanced features, including early termination, in-band interrupt (IBI)
> > > > > > support, and the creation of more complex data patterns. Additionally,
> > > > > > it aids in identifying and addressing issues within the I3C controller
> > > > > > driver.
> > > > > 
> > > > > But where is the serial data ending up at?  Not a normal uart, what is
> > > > > on the other end?  And do line settings mean anything here?
> > > > 
> > > > Currently, it use slave i3c code. 
> > > > https://lore.kernel.org/imx/20231018215809.3477437-1-Frank.Li at nxp.com/T/#t
> > > > 
> > > > idealy build an i3c->usb dongle to bride it to usb acm. 
> > > 
> > > So no one has built such a thing yet to determine if any of this works?
> > 
> > It is easy to proof concept by I3C slave code and USB gadget ACM, then pipe
> > two tty (ttyACM0 and ttySI3C0 together).
> 
> So you have not actually tested this?  why write a driver that no one is
> using?

I3c slave side tty at
https://lore.kernel.org/imx/20231018215809.3477437-1-Frank.Li at nxp.com/T/#t

I just have not tested tty to USB part. This patch is major for tty -> i3c
master -> i3c taret -> tty now.

As my previous said, two major purpose now. 
1. Save two pads.
2. Test i3c master and i3c target driver. I3C target driver frame was
written by me and posted. It needs a driver to verify it works. In stead of
write a test driver (such as i2c slave, pci-endpoint), it is better to
write an actual function driver, such as tty. It can exchange message
between two boards, which connected by SDA/SCL.

Frank
> 
> > Of we also can implement a USB to I3C class standard, base on this, reuse
> > this tty driver at host side.
> 
> Is there a USB I3C standard?  I see i3c descriptors assigned by the
> USB-IF, but haven't dug to see if there's more than that anywhere...
> 
> thanks,
> 
> greg k-h


From Frank.li at nxp.com  Tue Oct 24 09:04:40 2023
From: Frank.li at nxp.com (Frank Li)
Date: Tue, 24 Oct 2023 12:04:40 -0400
Subject: [PATCH v2 1/1] tty: i3c: add TTY over I3C master support
In-Reply-To: <ZTfp+c60YOiwDUFd@lizhi-Precision-Tower-5810>
References: <20231020160027.3663772-1-Frank.Li@nxp.com>
 <2023102105-outfit-legroom-1633@gregkh>
 <ZTaewidgtcDaBega@lizhi-Precision-Tower-5810>
 <2023102457-galore-uphill-4c84@gregkh>
 <ZTfVV3DW8jqH6ek9@lizhi-Precision-Tower-5810>
 <2023102442-immorally-repost-e736@gregkh>
 <ZTfp+c60YOiwDUFd@lizhi-Precision-Tower-5810>
Message-ID: <ZTfrGAyf194yxX4q@lizhi-Precision-Tower-5810>

On Tue, Oct 24, 2023 at 11:59:53AM -0400, Frank Li wrote:
> On Tue, Oct 24, 2023 at 05:05:47PM +0200, Greg KH wrote:
> > On Tue, Oct 24, 2023 at 10:31:51AM -0400, Frank Li wrote:
> > > On Tue, Oct 24, 2023 at 11:30:33AM +0200, Greg KH wrote:
> > > > On Mon, Oct 23, 2023 at 12:26:42PM -0400, Frank Li wrote:
> > > > > On Sat, Oct 21, 2023 at 07:02:40PM +0200, Greg KH wrote:
> > > > > > Note, your subject line needs to change.
> > > > > > 
> > > > > > On Fri, Oct 20, 2023 at 12:00:27PM -0400, Frank Li wrote:
> > > > > > > In typical embedded Linux systems, UART consoles require at least two pins,
> > > > > > > TX and RX. In scenarios where I2C/I3C devices like sensors or PMICs are
> > > > > > > present, we can save these two pins by using this driver. Pins is crucial
> > > > > > 
> > > > > > "Pins are crucial"
> > > > > > 
> > > > > > > resources, especially in small chip packages.
> > > > > > > 
> > > > > > > This introduces support for using the I3C bus to transfer console tty data,
> > > > > > > effectively replacing the need for dedicated UART pins. This not only
> > > > > > > conserves valuable pin resources but also facilitates testing of I3C's
> > > > > > > advanced features, including early termination, in-band interrupt (IBI)
> > > > > > > support, and the creation of more complex data patterns. Additionally,
> > > > > > > it aids in identifying and addressing issues within the I3C controller
> > > > > > > driver.
> > > > > > 
> > > > > > But where is the serial data ending up at?  Not a normal uart, what is
> > > > > > on the other end?  And do line settings mean anything here?
> > > > > 
> > > > > Currently, it use slave i3c code. 
> > > > > https://lore.kernel.org/imx/20231018215809.3477437-1-Frank.Li at nxp.com/T/#t
> > > > > 
> > > > > idealy build an i3c->usb dongle to bride it to usb acm. 
> > > > 
> > > > So no one has built such a thing yet to determine if any of this works?
> > > 
> > > It is easy to proof concept by I3C slave code and USB gadget ACM, then pipe
> > > two tty (ttyACM0 and ttySI3C0 together).
> > 
> > So you have not actually tested this?  why write a driver that no one is
> > using?
> 
> I3c slave side tty at
> https://lore.kernel.org/imx/20231018215809.3477437-1-Frank.Li at nxp.com/T/#t
> 
> I just have not tested tty to USB part. This patch is major for tty -> i3c
> master -> i3c taret -> tty now.
> 
> As my previous said, two major purpose now. 
> 1. Save two pads.
> 2. Test i3c master and i3c target driver. I3C target driver frame was
> written by me and posted. It needs a driver to verify it works. In stead of
> write a test driver (such as i2c slave, pci-endpoint), it is better to
> write an actual function driver, such as tty. It can exchange message
> between two boards, which connected by SDA/SCL.
> 
> Frank
> > 
> > > Of we also can implement a USB to I3C class standard, base on this, reuse
> > > this tty driver at host side.
> > 
> > Is there a USB I3C standard?  I see i3c descriptors assigned by the
> > USB-IF, but haven't dug to see if there's more than that anywhere...

Sorry, forget answer this quesiton. Yes, it is standard class. Spec is in
usb.org. I have not found actual usage mode yet. 

> > 
> > thanks,
> > 
> > greg k-h


From yujie.liu at intel.com  Sun Oct 22 18:11:57 2023
From: yujie.liu at intel.com (kernel test robot)
Date: Mon, 23 Oct 2023 09:11:57 +0800
Subject: [PATCH 1/5] i3c: add slave mode support
In-Reply-To: <20231018215809.3477437-2-Frank.Li@nxp.com>
Message-ID: <202310201829.Hgq2x9nm-lkp@intel.com>

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/202310201829.Hgq2x9nm-lkp at intel.com/config)
compiler: sparc64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231020/202310201829.Hgq2x9nm-lkp at intel.com/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 <yujie.liu at intel.com>
| Closes: https://lore.kernel.org/r/202310201829.Hgq2x9nm-lkp at intel.com/

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



From yujie.liu at intel.com  Sun Oct 22 18:20:37 2023
From: yujie.liu at intel.com (kernel test robot)
Date: Mon, 23 Oct 2023 09:20:37 +0800
Subject: [PATCH 1/5] i3c: add slave mode support
In-Reply-To: <20231018215809.3477437-2-Frank.Li@nxp.com>
Message-ID: <202310211527.TeVTMxA7-lkp@intel.com>

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/202310211527.TeVTMxA7-lkp at intel.com/config)
compiler: microblaze-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231021/202310211527.TeVTMxA7-lkp at intel.com/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 <yujie.liu at intel.com>
| Closes: https://lore.kernel.org/r/202310211527.TeVTMxA7-lkp at intel.com/

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



From yujie.liu at intel.com  Sun Oct 22 18:29:26 2023
From: yujie.liu at intel.com (kernel test robot)
Date: Mon, 23 Oct 2023 09:29:26 +0800
Subject: [PATCH 3/5] i3c: slave: add svc slave controller support
In-Reply-To: <20231018215809.3477437-4-Frank.Li@nxp.com>
Message-ID: <202310201945.nRKjcul0-lkp@intel.com>

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/202310201945.nRKjcul0-lkp at intel.com/config)
compiler: sparc64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231020/202310201945.nRKjcul0-lkp at intel.com/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 <yujie.liu at intel.com>
| Closes: https://lore.kernel.org/r/202310201945.nRKjcul0-lkp at intel.com/

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



From billy_tsai at aspeedtech.com  Mon Oct 23 01:02:37 2023
From: billy_tsai at aspeedtech.com (Billy Tsai)
Date: Mon, 23 Oct 2023 16:02:37 +0800
Subject: [PATCH v1] i3c/master/mipi-i3c-hci: Fix a kernel panic for accessing DAT_data.
Message-ID: <20231023080237.560936-1-billy_tsai@aspeedtech.com>

The `i3c_master_bus_init` function may attach the I2C devices before the
I3C bus initialization. In this flow, the DAT `alloc_entry`` will be used
before the DAT `init`. Additionally, if the `i3c_master_bus_init` fails,
the DAT `cleanup` will execute before the device is detached, which will
execue DAT `free_entry` function. The above scenario can cause the driver
to use DAT_data when it is NULL.

Signed-off-by: Billy Tsai <billy_tsai at aspeedtech.com>
---
 drivers/i3c/master/mipi-i3c-hci/dat_v1.c | 29 ++++++++++++++++--------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
index 97bb49ff5b53..47b9b4d4ed3f 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
@@ -64,15 +64,17 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
 		return -EOPNOTSUPP;
 	}
 
-	/* use a bitmap for faster free slot search */
-	hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
-	if (!hci->DAT_data)
-		return -ENOMEM;
-
-	/* clear them */
-	for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
-		dat_w0_write(dat_idx, 0);
-		dat_w1_write(dat_idx, 0);
+	if (!hci->DAT_data) {
+		/* use a bitmap for faster free slot search */
+		hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
+		if (!hci->DAT_data)
+			return -ENOMEM;
+
+		/* clear them */
+		for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
+			dat_w0_write(dat_idx, 0);
+			dat_w1_write(dat_idx, 0);
+		}
 	}
 
 	return 0;
@@ -87,7 +89,13 @@ static void hci_dat_v1_cleanup(struct i3c_hci *hci)
 static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
 {
 	unsigned int dat_idx;
+	int ret;
 
+	if (!hci->DAT_data) {
+		ret = hci_dat_v1_init(hci);
+		if (ret)
+			return ret;
+	}
 	dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
 	if (dat_idx >= hci->DAT_entries)
 		return -ENOENT;
@@ -103,7 +111,8 @@ static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
 {
 	dat_w0_write(dat_idx, 0);
 	dat_w1_write(dat_idx, 0);
-	__clear_bit(dat_idx, hci->DAT_data);
+	if (hci->DAT_data)
+		__clear_bit(dat_idx, hci->DAT_data);
 }
 
 static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
-- 
2.25.1



