meta-gbmc-staging: sync i3c from Nuvoton' GitHub

Sync commit: 24b8d16672f91d26abe146ecc79628264686fdfe
We encountered some bugs when testing peci-i3c.
It has been solved in the latest commit.

- Ignore the ibi event during driver probe
- fix stuck in svc_i3c_master_read
- workaround for i3c timing violation
- Add error handling
- Fix scl frequency setting
- Fix interruption during FIFO write
- Disable ibi interrupt in driver's remove()
- Fix race conditiion in ibi isr
- Fix kfree Oops
- Handle IBI request in the irq handler
- Reset I3C controller on probe
- Send STOP as early as possible in DMA transfer

Tested:
image built locally

Fusion-Link: fusion2/8e17eb3d-b5fe-3bc3-8580-9a0e5f9bbdea
Fusion-Link: fusion2/8c1a4b6b-52c2-3293-b43d-4295902d7d6d
Fusion-Link: fusion2/e6ad3d57-538a-3795-a9c9-5f4118947a29
Smoke-Bug-Id: 363143269
Google-Bug-Id: 319571644
Change-Id: I520f28eb4ac18ffe50204125d4f131ec0c99c917
Signed-off-by: Charles Hsu <charles.hsu@quanta.corp-partner.google.com>
diff --git a/recipes-kernel/linux/5.15/0001-driver-i3c-add-intel-i3c-mctp-support.patch b/recipes-kernel/linux/5.15/0001-driver-i3c-add-intel-i3c-mctp-support.patch
new file mode 100644
index 0000000..cbec22c
--- /dev/null
+++ b/recipes-kernel/linux/5.15/0001-driver-i3c-add-intel-i3c-mctp-support.patch
@@ -0,0 +1,1719 @@
+From 735ce4e3869de7570deb27cd6e69780ab61cfed8 Mon Sep 17 00:00:00 2001
+From: Stanley Chu <yschu@nuvoton.com>
+Date: Tue, 18 Jun 2024 10:30:31 +0800
+Subject: [PATCH] driver: i3c: add intel i3c mctp support 5.15
+
+Signed-off-by: Stanley Chu <yschu@nuvoton.com>
+---
+ drivers/i3c/device.c                |  66 +++
+ drivers/i3c/internals.h             |   3 +
+ drivers/i3c/master.c                | 153 ++++++-
+ drivers/i3c/master/svc-i3c-master.c |   2 +
+ drivers/i3c/mctp/i3c-mctp.c         | 645 ++++++++++++++++++++++------
+ drivers/i3c/mctp/i3c-target-mctp.c  | 101 ++++-
+ include/linux/i3c/device.h          |  26 ++
+ include/linux/i3c/master.h          |   2 +
+ include/linux/i3c/mctp/i3c-mctp.h   |   7 +-
+ include/linux/i3c/target.h          |   3 +
+ include/uapi/linux/i3c/i3c-mctp.h   |  48 +++
+ 11 files changed, 909 insertions(+), 147 deletions(-)
+ create mode 100644 include/uapi/linux/i3c/i3c-mctp.h
+
+diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c
+index eb0935aa397c..141d81a79a35 100644
+--- a/drivers/i3c/device.c
++++ b/drivers/i3c/device.c
+@@ -73,6 +73,40 @@ int i3c_device_generate_ibi(struct i3c_device *dev, const u8 *data, int len)
+ }
+ EXPORT_SYMBOL_GPL(i3c_device_generate_ibi);
+ 
++/**
++ * i3c_device_put_read_data() - put read data and optionally notify primary master
++ *
++ * @dev: target device
++ * @xfers: array of transfers
++ * @nxfers: number of transfers
++ * @ibi_data: IBI payload
++ * @ibi_len: IBI payload length in bytes
++ *
++ * Put read data into the target buffer and optionally notify primary master
++ *
++ * Return: 0 in case of success, a negative error code otherwise.
++ */
++int i3c_device_put_read_data(struct i3c_device *dev, struct i3c_priv_xfer *xfers, int nxfers,
++			     const u8 *ibi_data, int ibi_len)
++{
++	int ret, i;
++
++	if (nxfers < 1)
++		return 0;
++
++	for (i = 0; i < nxfers; i++) {
++		if (!xfers[i].len || !xfers[i].data.in)
++			return -EINVAL;
++	}
++
++	i3c_bus_normaluse_lock(dev->bus);
++	ret = i3c_dev_put_read_data_locked(dev->desc, xfers, nxfers, ibi_data, ibi_len);
++	i3c_bus_normaluse_unlock(dev->bus);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(i3c_device_put_read_data);
++
+ /**
+  * i3c_device_get_info() - get I3C device information
+  *
+@@ -345,6 +379,38 @@ int i3c_device_getstatus_ccc(struct i3c_device *dev, struct i3c_device_info *inf
+ }
+ EXPORT_SYMBOL_GPL(i3c_device_getstatus_ccc);
+ 
++/**
++ * i3c_device_control_pec() - enable or disable PEC support in HW
++ *
++ * @dev: I3C device to get the status for
++ * @pec: flag telling whether PEC support shall be enabled or disabled
++ *
++ * Try to enable or disable HW support for PEC (Packet Error Check).
++ * In case no HW support for PEC, software implementation could be used.
++ *
++ * Return: 0 in case of success, -EOPNOTSUPP in case PEC is not supported by HW,
++ *         other negative error codes when PEC enabling failed.
++ */
++int i3c_device_control_pec(struct i3c_device *dev, bool pec)
++{
++	return i3c_dev_control_pec(dev->desc, pec);
++}
++EXPORT_SYMBOL_GPL(i3c_device_control_pec);
++
++/**
++ * i3c_device_register_event_cb() - register callback for I3C framework event.
++ * @dev: the I3C device driver handle.
++ * @ev: I3C framework event callback
++ *
++ * This function allows I3C device driver to register for I3C framework events.
++ * Provided callback will be used by controller driver to publish events.
++ */
++void i3c_device_register_event_cb(struct i3c_device *dev, i3c_event_cb event_cb)
++{
++	dev->desc->event_cb = event_cb;
++}
++EXPORT_SYMBOL_GPL(i3c_device_register_event_cb);
++
+ /**
+  * i3c_device_setmrl_ccc() - set maximum read length
+  *
+diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h
+index b34c4460c0b6..e912032db088 100644
+--- a/drivers/i3c/internals.h
++++ b/drivers/i3c/internals.h
+@@ -35,4 +35,7 @@ int i3c_master_setmwl_locked(struct i3c_master_controller *master,
+ 			     struct i3c_device_info *info, __be16 write_len);
+ int i3c_for_each_dev(void *data, int (*fn)(struct device *, void *));
+ int i3c_dev_generate_ibi_locked(struct i3c_dev_desc *dev, const u8 *data, int len);
++int i3c_dev_put_read_data_locked(struct i3c_dev_desc *dev, struct i3c_priv_xfer *xfers,
++				 int nxfers, const u8 *ibi_data, int ibi_len);
++int i3c_dev_control_pec(struct i3c_dev_desc *dev, bool pec);
+ #endif /* I3C_INTERNAL_H */
+diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
+index 31c7fb9036c3..45bb1513b507 100644
+--- a/drivers/i3c/master.c
++++ b/drivers/i3c/master.c
+@@ -104,12 +104,14 @@ static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev)
+ }
+ 
+ static const struct device_type i3c_device_type;
++static const struct device_type i3c_target_device_type;
+ 
+ static struct i3c_bus *dev_to_i3cbus(struct device *dev)
+ {
+ 	struct i3c_master_controller *master;
+ 
+-	if (dev->type == &i3c_device_type)
++	if (dev->type == &i3c_device_type ||
++	    dev->type == &i3c_target_device_type)
+ 		return dev_to_i3cdev(dev)->bus;
+ 
+ 	master = dev_to_i3cmaster(dev);
+@@ -121,7 +123,8 @@ static struct i3c_dev_desc *dev_to_i3cdesc(struct device *dev)
+ {
+ 	struct i3c_master_controller *master;
+ 
+-	if (dev->type == &i3c_device_type)
++	if (dev->type == &i3c_device_type ||
++	    dev->type == &i3c_target_device_type)
+ 		return dev_to_i3cdev(dev)->desc;
+ 
+ 	master = dev_to_i3cmaster(dev);
+@@ -263,6 +266,25 @@ static ssize_t modalias_show(struct device *dev,
+ }
+ static DEVICE_ATTR_RO(modalias);
+ 
++static ssize_t status_show(struct device *dev,
++			   struct device_attribute *da,
++			   char *buf)
++{
++	struct i3c_dev_desc *desc = dev_to_i3cdesc(dev);
++	struct i3c_bus *bus = dev_to_i3cbus(dev);
++	ssize_t ret;
++
++	i3c_bus_normaluse_lock(bus);
++	ret = i3c_dev_getstatus_locked(desc, &desc->info);
++	if (!ret)
++		ret = sysfs_emit(buf, "%x\n", desc->info.status);
++
++	i3c_bus_normaluse_unlock(bus);
++
++	return ret;
++}
++static DEVICE_ATTR_RO(status);
++
+ static struct attribute *i3c_device_attrs[] = {
+ 	&dev_attr_bcr.attr,
+ 	&dev_attr_dcr.attr,
+@@ -270,6 +292,7 @@ static struct attribute *i3c_device_attrs[] = {
+ 	&dev_attr_dynamic_address.attr,
+ 	&dev_attr_hdrcap.attr,
+ 	&dev_attr_modalias.attr,
++	&dev_attr_status.attr,
+ 	NULL,
+ };
+ ATTRIBUTE_GROUPS(i3c_device);
+@@ -299,7 +322,15 @@ static const struct device_type i3c_device_type = {
+ 	.uevent = i3c_device_uevent,
+ };
+ 
+-const struct device_type i3c_target_device_type = {
++static struct attribute *i3c_target_device_attrs[] = {
++	&dev_attr_dynamic_address.attr,
++	NULL,
++};
++ATTRIBUTE_GROUPS(i3c_target_device);
++
++
++static const struct device_type i3c_target_device_type = {
++	.groups = i3c_target_device_groups,
+ };
+ 
+ static int i3c_device_match(struct device *dev, struct device_driver *drv)
+@@ -485,6 +516,27 @@ static const char * const i3c_bus_mode_strings[] = {
+ 	[I3C_BUS_MODE_MIXED_SLOW] = "mixed-slow",
+ };
+ 
++/**
++ * i3c_device_publish_event() - publish I3C framework event to all interested
++ *				devices
++ * @master: master used to handle devices
++ * @ev: I3C framework event to publish
++ */
++static void i3c_device_publish_event(struct i3c_master_controller *master,
++				     enum i3c_event ev)
++{
++	struct i3c_dev_desc *i3cdev;
++
++	i3c_bus_for_each_i3cdev(&master->bus, i3cdev) {
++		if (i3cdev->event_cb)
++			i3cdev->event_cb(i3cdev->dev, ev);
++	}
++}
++
++static int i3c_master_rstdaa_locked(struct i3c_master_controller *master,
++				    u8 addr);
++
++
+ static ssize_t mode_show(struct device *dev,
+ 			 struct device_attribute *da,
+ 			 char *buf)
+@@ -565,6 +617,58 @@ static ssize_t discover_store(struct device *dev, struct device_attribute *da,
+ }
+ static DEVICE_ATTR_WO(discover);
+ 
++static ssize_t rescan_store(struct device *dev, struct device_attribute *attr,
++			    const char *buf, size_t count)
++{
++	struct i3c_master_controller *master = dev_to_i3cmaster(dev);
++	struct i3c_bus *bus = i3c_master_get_bus(master);
++	bool res;
++	int ret;
++
++	ret = kstrtobool(buf, &res);
++	if (ret)
++		return ret;
++
++	if (!res)
++		return count;
++
++	i3c_device_publish_event(master, i3c_event_prepare_for_rescan);
++
++	i3c_bus_maintenance_lock(bus);
++
++	ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
++				      I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
++				      I3C_CCC_EVENT_HJ);
++	if (ret && ret != I3C_ERROR_M2) {
++		dev_dbg(&master->dev,
++			"Failed to run broadcast DISEC for rescan, ret=%d\n", ret);
++		i3c_bus_maintenance_unlock(bus);
++		return ret;
++	}
++
++	ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR);
++	if (ret && ret != I3C_ERROR_M2) {
++		dev_dbg(&master->dev,
++			"Failed to run RSTDAA for rescan, ret=%d\n", ret);
++		i3c_bus_maintenance_unlock(bus);
++		return ret;
++	}
++
++	i3c_bus_maintenance_unlock(bus);
++
++	ret = i3c_master_do_daa(master);
++	if (ret) {
++		dev_dbg(&master->dev, "Failed to run DAA for rescan, ret=%d\n",
++			ret);
++		return ret;
++	}
++
++	i3c_device_publish_event(master, i3c_event_rescan_done);
++
++	return count;
++}
++static DEVICE_ATTR_WO(rescan);
++
+ static struct attribute *i3c_masterdev_attrs[] = {
+ 	&dev_attr_mode.attr,
+ 	&dev_attr_current_master.attr,
+@@ -576,6 +680,7 @@ static struct attribute *i3c_masterdev_attrs[] = {
+ 	&dev_attr_dynamic_address.attr,
+ 	&dev_attr_hdrcap.attr,
+ 	&dev_attr_discover.attr,
++	&dev_attr_rescan.attr,
+ 	NULL,
+ };
+ ATTRIBUTE_GROUPS(i3c_masterdev);
+@@ -2088,6 +2193,7 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
+ 	olddev = i3c_master_search_i3c_dev_duplicate(newdev);
+ 	if (olddev) {
+ 		newdev->dev = olddev->dev;
++		newdev->event_cb = olddev->event_cb;
+ 		if (newdev->dev)
+ 			newdev->dev->desc = newdev;
+ 
+@@ -3062,6 +3168,27 @@ int i3c_dev_generate_ibi_locked(struct i3c_dev_desc *dev, const u8 *data, int le
+ 	return master->target_ops->generate_ibi(dev, data, len);
+ }
+ 
++int i3c_dev_put_read_data_locked(struct i3c_dev_desc *dev, struct i3c_priv_xfer *xfers,
++				 int nxfers, const u8 *ibi_data, int ibi_len)
++{
++	struct i3c_master_controller *master;
++
++	if (!dev)
++		return -ENOENT;
++
++	master = i3c_dev_get_master(dev);
++	if (!master)
++		return -EINVAL;
++
++	if (!master->target)
++		return -EINVAL;
++
++	if (!master->target_ops->put_read_data)
++		return -EOPNOTSUPP;
++
++	return master->target_ops->put_read_data(dev, xfers, nxfers, ibi_data, ibi_len);
++}
++
+ int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev)
+ {
+ 	struct i3c_master_controller *master;
+@@ -3186,6 +3313,26 @@ int i3c_for_each_dev(void *data, int (*fn)(struct device *, void *))
+ }
+ EXPORT_SYMBOL_GPL(i3c_for_each_dev);
+ 
++int i3c_dev_control_pec(struct i3c_dev_desc *dev, bool pec)
++{
++	struct i3c_master_controller *master = i3c_dev_get_master(dev);
++
++	if (!master->pec_supported)
++		return -EOPNOTSUPP;
++
++	dev->info.pec = pec;
++
++	/*
++	 * TODO: There are two cases which shall be covered
++	 * 1. Controller doesn't support PEC.
++	 *    In this case we could just fallback to SW implementation.
++	 * 2. Device doesn't support PEC.
++	 *    Then we really can't use PEC - and should error-out.
++	 */
++
++	return 0;
++}
++
+ static int __init i3c_init(void)
+ {
+ 	return bus_register(&i3c_bus_type);
+diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
+index 6fdaa8b9de5d..bd47947b6beb 100644
+--- a/drivers/i3c/master/svc-i3c-master.c
++++ b/drivers/i3c/master/svc-i3c-master.c
+@@ -2589,6 +2589,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
+ 
+ 	svc_i3c_setup_dma(pdev, master);
+ 	svc_i3c_init_debugfs(pdev, master);
++ 	
++	master->base.pec_supported = true;
+ 
+ 	/* Register the master */
+ 	ret = i3c_register(&master->base, &pdev->dev,
+diff --git a/drivers/i3c/mctp/i3c-mctp.c b/drivers/i3c/mctp/i3c-mctp.c
+index 46cc210377c0..09d56ad09cc2 100644
+--- a/drivers/i3c/mctp/i3c-mctp.c
++++ b/drivers/i3c/mctp/i3c-mctp.c
+@@ -3,6 +3,8 @@
+ 
+ #include <linux/cdev.h>
+ #include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/list_sort.h>
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
+ #include <linux/poll.h>
+@@ -14,9 +16,12 @@
+ #include <linux/workqueue.h>
+ 
+ #include <linux/i3c/device.h>
++#include <linux/i3c/master.h>
+ 
+ #include <linux/i3c/mctp/i3c-mctp.h>
+ 
++#include <uapi/linux/i3c/i3c-mctp.h>
++
+ #define I3C_MCTP_MINORS				32
+ #define CCC_DEVICE_STATUS_PENDING_INTR(x)	(((x) & GENMASK(3, 0)) >> 0)
+ #define POLLING_TIMEOUT_MS			50
+@@ -24,13 +29,13 @@
+ #define RX_RING_COUNT				16
+ #define I3C_MCTP_MIN_TRANSFER_SIZE		69
+ #define I3C_MCTP_IBI_PAYLOAD_SIZE		2
+-#define POLL_MODE				BIT(0)
+ 
+-static const struct i3c_device_id i3c_mctp_ids[] = {
+-	I3C_DEVICE(0x319, 0x8000, (void *)POLL_MODE),
+-	I3C_CLASS(0xCC, 0),
+-	{ },
+-};
++/* MCTP header definitions */
++#define MCTP_HDR_SRC_EID_OFFSET			2
++
++#define MAX_PROCESS_COUNT			255
++
++#define IS_BMC_NON_LEGACY(pid) (((pid) & 0xF) == 0xB)
+ 
+ struct i3c_mctp {
+ 	struct i3c_device *i3c;
+@@ -39,32 +44,53 @@ struct i3c_mctp {
+ 	struct delayed_work polling_work;
+ 	struct platform_device *i3c_peci;
+ 	int id;
+-	/*
+-	 * Restrict an access to the /dev descriptor to one
+-	 * user at a time.
+-	 */
+-	spinlock_t device_file_lock;
+-	int device_open;
+ 	/* Currently only one userspace client is supported */
+ 	struct i3c_mctp_client *default_client;
+ 	struct i3c_mctp_client *peci_client;
+ 	u16 max_read_len;
+ 	u16 max_write_len;
+-	bool poll_mode;
++	struct list_head endpoints;
++	size_t endpoints_count;
++	/*
++	 * endpoints_lock protects list of endpoints
++	 */
++	struct mutex endpoints_lock;
++	spinlock_t clients_lock; /* to protect PECI and default client accesses */
++	u8 eid;
++	/*
++	 * As there can be more than one process opening the /dev file - we need
++	 * a counter to take care of device cleanup in case the file is opened
++	 * on device removal. We also need a locker to avoid potential race
++	 * conditions.
++	 */
++	spinlock_t file_lock;
++	u8 process_count;
++	bool ibi_enabled;
+ };
+ 
+ struct i3c_mctp_client {
++	struct kref ref;
+ 	struct i3c_mctp *priv;
+ 	struct ptr_ring rx_queue;
+ 	wait_queue_head_t wait_queue;
+ };
+ 
++struct i3c_mctp_endpoint {
++	struct i3c_mctp_eid_info eid_info;
++	struct list_head link;
++};
++
+ static struct class *i3c_mctp_class;
+ static dev_t i3c_mctp_devt;
+ static DEFINE_IDA(i3c_mctp_ida);
+ 
+ static struct kmem_cache *packet_cache;
+ 
++static inline u8 i3cdev_get_dynaddr(struct i3c_device *i3cdev)
++{
++	return i3cdev->desc->info.dyn_addr;
++}
++
+ /**
+  * i3c_mctp_packet_alloc() - allocates i3c_mctp_packet
+  *
+@@ -92,13 +118,39 @@ void i3c_mctp_packet_free(void *packet)
+ }
+ EXPORT_SYMBOL_GPL(i3c_mctp_packet_free);
+ 
+-static void i3c_mctp_client_free(struct i3c_mctp_client *client)
++/**
++ * i3c_mctp_flush_rx_queue() - flushes mctp client rx queue
++ *
++ * @client: pointer to the i3c mctp client whose rx queue should be flushed
++ */
++void i3c_mctp_flush_rx_queue(struct i3c_mctp_client *client)
++{
++	struct i3c_mctp_packet *packet;
++
++	while ((packet = ptr_ring_consume_bh(&client->rx_queue)))
++		i3c_mctp_packet_free(packet);
++}
++EXPORT_SYMBOL_GPL(i3c_mctp_flush_rx_queue);
++
++static void i3c_mctp_client_free(struct kref *ref)
+ {
++	struct i3c_mctp_client *client = container_of(ref, typeof(*client), ref);
++
+ 	ptr_ring_cleanup(&client->rx_queue, &i3c_mctp_packet_free);
+ 
+ 	kfree(client);
+ }
+ 
++static void i3c_mctp_client_get(struct i3c_mctp_client *client)
++{
++	kref_get(&client->ref);
++}
++
++static void i3c_mctp_client_put(struct i3c_mctp_client *client)
++{
++	kref_put(&client->ref, &i3c_mctp_client_free);
++}
++
+ static struct i3c_mctp_client *i3c_mctp_client_alloc(struct i3c_mctp *priv)
+ {
+ 	struct i3c_mctp_client *client;
+@@ -106,15 +158,39 @@ static struct i3c_mctp_client *i3c_mctp_client_alloc(struct i3c_mctp *priv)
+ 
+ 	client = kzalloc(sizeof(*client), GFP_KERNEL);
+ 	if (!client)
+-		goto out;
++		return ERR_PTR(-ENOMEM);
+ 
++	kref_init(&client->ref);
+ 	client->priv = priv;
+ 	ret = ptr_ring_init(&client->rx_queue, RX_RING_COUNT, GFP_KERNEL);
+ 	if (ret)
+-		return ERR_PTR(ret);
++		goto out;
++
+ 	init_waitqueue_head(&client->wait_queue);
+-out:
++
+ 	return client;
++out:
++	i3c_mctp_client_put(client);
++	return ERR_PTR(ret);
++}
++
++static int i3c_mctp_register_default_client(struct i3c_mctp *priv, struct file *file)
++{
++	struct i3c_mctp_client *client;
++
++	if (priv->default_client)
++		return -EBUSY;
++
++	client = i3c_mctp_client_alloc(priv);
++	if (IS_ERR(client))
++		return PTR_ERR(client);
++
++	file->private_data = client;
++	spin_lock(&priv->clients_lock);
++	priv->default_client = client;
++	spin_unlock(&priv->clients_lock);
++
++	return 0;
+ }
+ 
+ static struct i3c_mctp_client *i3c_mctp_find_client(struct i3c_mctp *priv,
+@@ -135,11 +211,13 @@ static struct i3c_mctp_client *i3c_mctp_find_client(struct i3c_mctp *priv,
+ 
+ static struct i3c_mctp_packet *i3c_mctp_read_packet(struct i3c_device *i3c)
+ {
+-	struct i3c_mctp *priv = dev_get_drvdata(i3cdev_to_dev(i3c));
++	struct i3c_mctp *priv = i3cdev_get_drvdata(i3c);
+ 	struct i3c_mctp_packet *rx_packet;
+ 	struct i3c_priv_xfer xfers = {
+ 		.rnw = true,
+ 	};
++	u8 *data, pec;
++	u8 addr;
+ 	int ret;
+ 
+ 	rx_packet = i3c_mctp_packet_alloc(GFP_KERNEL);
+@@ -150,7 +228,6 @@ static struct i3c_mctp_packet *i3c_mctp_read_packet(struct i3c_device *i3c)
+ 	xfers.len = rx_packet->size;
+ 	xfers.data.in = &rx_packet->data;
+ 
+-	/* Check against packet size + PEC byte to make sure that we always try to read max */
+ 	if (priv->max_read_len < xfers.len) {
+ 		dev_dbg(i3cdev_to_dev(i3c), "Length mismatch. MRL = %d, xfers.len = %d",
+ 			priv->max_read_len, xfers.len);
+@@ -163,6 +240,23 @@ static struct i3c_mctp_packet *i3c_mctp_read_packet(struct i3c_device *i3c)
+ 		i3c_mctp_packet_free(rx_packet);
+ 		return ERR_PTR(ret);
+ 	}
++
++	/* check PEC, including address+rw bits */
++	if (xfers.len < I3C_MCTP_HDR_SIZE + 1) {
++		dev_info(i3cdev_to_dev(i3c), "Short mctp packet\n");
++		return ERR_PTR(-EINVAL);
++	}
++	data = (u8 *)&rx_packet->data;
++	addr = (i3cdev_get_dynaddr(i3c) << 1) | 1;
++	pec = i2c_smbus_pec(0, &addr, 1);
++	pec = i2c_smbus_pec(pec, data, xfers.len - 1);
++	if (pec != data[xfers.len - 1]) {
++		dev_info(i3cdev_to_dev(i3c), "got pec 0x%02x, should be 0x%02x\n",
++			 data[xfers.len - 1], pec);
++		return ERR_PTR(-EINVAL);
++	}
++	xfers.len--;
++
+ 	rx_packet->size = xfers.len;
+ 
+ 	return rx_packet;
+@@ -170,14 +264,27 @@ static struct i3c_mctp_packet *i3c_mctp_read_packet(struct i3c_device *i3c)
+ 
+ static void i3c_mctp_dispatch_packet(struct i3c_mctp *priv, struct i3c_mctp_packet *packet)
+ {
+-	struct i3c_mctp_client *client = i3c_mctp_find_client(priv, packet);
++	struct i3c_mctp_client *client;
+ 	int ret;
+ 
++	spin_lock(&priv->clients_lock);
++	client = i3c_mctp_find_client(priv, packet);
++	if (client)
++		i3c_mctp_client_get(client);
++	spin_unlock(&priv->clients_lock);
++
++	if (!client) {
++		i3c_mctp_packet_free(packet);
++		return;
++	}
++
+ 	ret = ptr_ring_produce(&client->rx_queue, packet);
+ 	if (ret)
+ 		i3c_mctp_packet_free(packet);
+ 	else
+ 		wake_up_all(&client->wait_queue);
++
++	i3c_mctp_client_put(client);
+ }
+ 
+ static void i3c_mctp_polling_work(struct work_struct *work)
+@@ -205,84 +312,116 @@ static void i3c_mctp_polling_work(struct work_struct *work)
+ 	schedule_delayed_work(&priv->polling_work, msecs_to_jiffies(POLLING_TIMEOUT_MS));
+ }
+ 
++static int i3c_mctp_open(struct inode *inode, struct file *file)
++{
++	struct i3c_mctp *priv = container_of(inode->i_cdev, struct i3c_mctp, cdev);
++	int ret = 0;
++
++	spin_lock(&priv->file_lock);
++	if (priv->process_count >= MAX_PROCESS_COUNT) {
++		ret = -EBUSY;
++		goto out_unlock;
++	}
++
++	priv->process_count++;
++
++out_unlock:
++	spin_unlock(&priv->file_lock);
++
++	return ret;
++}
++
+ static ssize_t i3c_mctp_write(struct file *file, const char __user *buf, size_t count,
+ 			      loff_t *f_pos)
+ {
+-	struct i3c_mctp *priv = file->private_data;
+-	struct i3c_device *i3c = priv->i3c;
+-	struct i3c_priv_xfer xfers = {
+-		.rnw = false,
+-		.len = count,
+-	};
+-	u8 *data;
++	struct i3c_mctp_client *client = file->private_data;
++	struct i3c_mctp_packet *tx_packet;
+ 	int ret;
+ 
+-	/*
+-	 * Check against packet size + PEC byte
+-	 * to not send more data than it was set in the probe
+-	 */
+-	if (priv->max_write_len < xfers.len + 1) {
+-		dev_dbg(i3cdev_to_dev(i3c), "Length mismatch. MWL = %d, xfers.len = %d",
+-			priv->max_write_len, xfers.len);
++	if (!client || !client->priv)
++		return -EBADF;
++
++	if (count < I3C_MCTP_MIN_PACKET_SIZE)
+ 		return -EINVAL;
++
++	if (count > sizeof(tx_packet->data))
++		return -ENOSPC;
++
++	tx_packet = i3c_mctp_packet_alloc(GFP_KERNEL);
++	if (!tx_packet)
++		return -ENOMEM;
++
++	if (copy_from_user(&tx_packet->data, buf, count)) {
++		dev_err(client->priv->dev, "copy from user failed\n");
++		ret = -EFAULT;
++		goto out_packet;
+ 	}
+ 
+-	data = memdup_user(buf, count);
+-	if (IS_ERR(data))
+-		return PTR_ERR(data);
++	tx_packet->size = count;
+ 
+-	xfers.data.out = data;
++	ret = i3c_mctp_send_packet(client->priv->i3c, tx_packet);
++	if (ret)
++		goto out_packet;
+ 
+-	ret = i3c_device_do_priv_xfers(i3c, &xfers, 1);
+-	kfree(data);
+-	return ret ?: count;
++	ret = count;
++
++out_packet:
++	i3c_mctp_packet_free(tx_packet);
++	return ret;
+ }
+ 
+ static ssize_t i3c_mctp_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
+ {
+-	struct i3c_mctp *priv = file->private_data;
+-	struct i3c_mctp_client *client = priv->default_client;
++	struct i3c_mctp_client *client = file->private_data;
+ 	struct i3c_mctp_packet *rx_packet;
+ 
++	if (!client)
++		return -EBADF;
++
++	if (count < I3C_MCTP_MIN_PACKET_SIZE)
++		return -EINVAL;
++
++	if (count > sizeof(rx_packet->data))
++		count = sizeof(rx_packet->data);
++
+ 	rx_packet = ptr_ring_consume(&client->rx_queue);
+ 	if (!rx_packet)
+-		return 0;
++		return -EAGAIN;
+ 
+ 	if (count > rx_packet->size)
+ 		count = rx_packet->size;
+ 
+ 	if (copy_to_user(buf, &rx_packet->data, count))
+-		return -EFAULT;
++		count = -EFAULT;
+ 
+ 	i3c_mctp_packet_free(rx_packet);
+ 
+ 	return count;
+ }
+ 
+-static int i3c_mctp_open(struct inode *inode, struct file *file)
++static int i3c_mctp_release(struct inode *inode, struct file *file)
+ {
+ 	struct i3c_mctp *priv = container_of(inode->i_cdev, struct i3c_mctp, cdev);
++	struct i3c_mctp_client *client = file->private_data;
+ 
+-	spin_lock(&priv->device_file_lock);
+-	if (priv->device_open) {
+-		spin_unlock(&priv->device_file_lock);
+-		return -EBUSY;
++	if (inode->i_cdev && priv) {
++		spin_lock(&priv->file_lock);
++		priv->process_count--;
++		spin_unlock(&priv->file_lock);
+ 	}
+-	priv->device_open++;
+-	spin_unlock(&priv->device_file_lock);
+ 
+-	file->private_data = priv;
++	if (!client)
++		return 0;
+ 
+-	return 0;
+-}
++	if (!client->priv)
++		goto out;
+ 
+-static int i3c_mctp_release(struct inode *inode, struct file *file)
+-{
+-	struct i3c_mctp *priv = file->private_data;
++	spin_lock(&client->priv->clients_lock);
++	client->priv->default_client = NULL;
++	spin_unlock(&client->priv->clients_lock);
+ 
+-	spin_lock(&priv->device_file_lock);
+-	priv->device_open--;
+-	spin_unlock(&priv->device_file_lock);
++out:
++	i3c_mctp_client_put(client);
+ 
+ 	file->private_data = NULL;
+ 
+@@ -291,24 +430,177 @@ static int i3c_mctp_release(struct inode *inode, struct file *file)
+ 
+ static __poll_t i3c_mctp_poll(struct file *file, struct poll_table_struct *pt)
+ {
+-	struct i3c_mctp *priv = file->private_data;
++	struct i3c_mctp_client *client = file->private_data;
+ 	__poll_t ret = 0;
+ 
+-	poll_wait(file, &priv->default_client->wait_queue, pt);
++	if (!client)
++		return ret;
++
++	poll_wait(file, &client->wait_queue, pt);
+ 
+-	if (__ptr_ring_peek(&priv->default_client->rx_queue))
++	if (__ptr_ring_peek(&client->rx_queue))
+ 		ret |= EPOLLIN;
+ 
+ 	return ret;
+ }
+ 
++static int
++eid_info_cmp(void *priv, const struct list_head *a, const struct list_head *b)
++{
++	struct i3c_mctp_endpoint *endpoint_a;
++	struct i3c_mctp_endpoint *endpoint_b;
++
++	if (a == b)
++		return 0;
++
++	endpoint_a = list_entry(a, typeof(*endpoint_a), link);
++	endpoint_b = list_entry(b, typeof(*endpoint_b), link);
++
++	if (endpoint_a->eid_info.eid < endpoint_b->eid_info.eid)
++		return -1;
++	else if (endpoint_a->eid_info.eid > endpoint_b->eid_info.eid)
++		return 1;
++
++	return 0;
++}
++
++static void i3c_mctp_eid_info_list_remove(struct list_head *list)
++{
++	struct i3c_mctp_endpoint *endpoint;
++	struct i3c_mctp_endpoint *tmp;
++
++	list_for_each_entry_safe(endpoint, tmp, list, link) {
++		list_del(&endpoint->link);
++		kfree(endpoint);
++	}
++}
++
++static bool
++i3c_mctp_eid_info_list_valid(struct list_head *list)
++{
++	struct i3c_mctp_endpoint *endpoint;
++	struct i3c_mctp_endpoint *next;
++
++	list_for_each_entry(endpoint, list, link) {
++		next = list_next_entry(endpoint, link);
++		if (&next->link == list)
++			break;
++
++		/* duplicated eids */
++		if (next->eid_info.eid == endpoint->eid_info.eid)
++			return false;
++	}
++
++	return true;
++}
++
++static int
++i3c_mctp_set_eid_info(struct i3c_mctp *priv, struct i3c_mctp_set_eid_info __user *userbuf)
++{
++	struct list_head list = LIST_HEAD_INIT(list);
++	struct i3c_mctp_set_eid_info set_eid;
++	struct i3c_mctp_endpoint *endpoint;
++	void *user_ptr;
++	int ret = 0;
++	size_t i;
++
++	if (copy_from_user(&set_eid, userbuf, sizeof(set_eid))) {
++		dev_err(priv->dev, "copy from user failed\n");
++		return -EFAULT;
++	}
++
++	if (set_eid.count > I3C_MCTP_EID_INFO_MAX)
++		return -EINVAL;
++
++	user_ptr = u64_to_user_ptr(set_eid.ptr);
++	for (i = 0; i < set_eid.count; i++) {
++		endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL);
++		if (!endpoint) {
++			ret = -ENOMEM;
++			goto out;
++		}
++		memset(endpoint, 0, sizeof(*endpoint));
++
++		ret = copy_from_user(&endpoint->eid_info,
++				     &(((struct i3c_mctp_eid_info *)user_ptr)[i]),
++				     sizeof(struct i3c_mctp_eid_info));
++
++		if (ret) {
++			dev_err(priv->dev, "copy from user failed\n");
++			kfree(endpoint);
++			ret = -EFAULT;
++			goto out;
++		}
++
++		list_add_tail(&endpoint->link, &list);
++	}
++
++	list_sort(NULL, &list, eid_info_cmp);
++	if (!i3c_mctp_eid_info_list_valid(&list)) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	mutex_lock(&priv->endpoints_lock);
++	if (list_empty(&priv->endpoints))
++		list_splice_init(&list, &priv->endpoints);
++	else
++		list_swap(&list, &priv->endpoints);
++	priv->endpoints_count = set_eid.count;
++	mutex_unlock(&priv->endpoints_lock);
++out:
++	i3c_mctp_eid_info_list_remove(&list);
++	return ret;
++}
++
++static int i3c_mctp_set_own_eid(struct i3c_mctp *priv, void __user *userbuf)
++{
++	struct i3c_mctp_set_own_eid data;
++
++	if (copy_from_user(&data, userbuf, sizeof(data))) {
++		dev_err(priv->dev, "copy from user failed\n");
++		return -EFAULT;
++	}
++
++	priv->eid = data.eid;
++
++	return 0;
++}
++
++static long
++i3c_mctp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++	struct i3c_mctp *priv = container_of(file_inode(file)->i_cdev, struct i3c_mctp, cdev);
++	void __user *userbuf = (void __user *)arg;
++	int ret;
++
++	if (!file_inode(file)->i_cdev)
++		return -ENODEV;
++
++	switch (cmd) {
++	case I3C_MCTP_IOCTL_SET_EID_INFO:
++		ret = i3c_mctp_set_eid_info(priv, userbuf);
++		break;
++	case I3C_MCTP_IOCTL_SET_OWN_EID:
++		ret = i3c_mctp_set_own_eid(priv, userbuf);
++		break;
++	case I3C_MCTP_IOCTL_REGISTER_DEFAULT_CLIENT:
++		ret = i3c_mctp_register_default_client(priv, file);
++		break;
++	default:
++		break;
++	}
++	return ret;
++}
++
+ static const struct file_operations i3c_mctp_fops = {
+ 	.owner = THIS_MODULE,
++	.open = i3c_mctp_open,
+ 	.read = i3c_mctp_read,
+ 	.write = i3c_mctp_write,
+ 	.poll = i3c_mctp_poll,
+-	.open = i3c_mctp_open,
+ 	.release = i3c_mctp_release,
++	.unlocked_ioctl = i3c_mctp_ioctl,
+ };
+ 
+ /**
+@@ -319,14 +611,16 @@ static const struct file_operations i3c_mctp_fops = {
+  */
+ struct i3c_mctp_client *i3c_mctp_add_peci_client(struct i3c_device *i3c)
+ {
+-	struct i3c_mctp *priv = dev_get_drvdata(i3cdev_to_dev(i3c));
++	struct i3c_mctp *priv = i3cdev_get_drvdata(i3c);
+ 	struct i3c_mctp_client *client;
+ 
+ 	client = i3c_mctp_client_alloc(priv);
+ 	if (IS_ERR(client))
+-		return ERR_PTR(-ENOMEM);
++		return client;
+ 
++	spin_lock(&priv->clients_lock);
+ 	priv->peci_client = client;
++	spin_unlock(&priv->clients_lock);
+ 
+ 	return priv->peci_client;
+ }
+@@ -340,9 +634,11 @@ void i3c_mctp_remove_peci_client(struct i3c_mctp_client *client)
+ {
+ 	struct i3c_mctp *priv = client->priv;
+ 
+-	i3c_mctp_client_free(priv->peci_client);
+-
++	spin_lock(&priv->clients_lock);
+ 	priv->peci_client = NULL;
++	spin_unlock(&priv->clients_lock);
++
++	i3c_mctp_client_put(client);
+ }
+ EXPORT_SYMBOL_GPL(i3c_mctp_remove_peci_client);
+ 
+@@ -363,15 +659,21 @@ static struct i3c_mctp *i3c_mctp_alloc(struct i3c_device *i3c)
+ 
+ 	priv->id = id;
+ 	priv->i3c = i3c;
++	priv->eid = 0;
++	priv->process_count = 0;
++
++	INIT_LIST_HEAD(&priv->endpoints);
++	mutex_init(&priv->endpoints_lock);
+ 
+-	spin_lock_init(&priv->device_file_lock);
++	spin_lock_init(&priv->clients_lock);
++	spin_lock_init(&priv->file_lock);
+ 
+ 	return priv;
+ }
+ 
+ static void i3c_mctp_ibi_handler(struct i3c_device *dev, const struct i3c_ibi_payload *payload)
+ {
+-	struct i3c_mctp *priv = dev_get_drvdata(i3cdev_to_dev(dev));
++	struct i3c_mctp *priv = i3cdev_get_drvdata(dev);
+ 	struct i3c_mctp_packet *rx_packet;
+ 
+ 	rx_packet = i3c_mctp_read_packet(dev);
+@@ -426,23 +728,44 @@ static void i3c_mctp_free(struct i3c_driver *drv)
+ 
+ static int i3c_mctp_enable_ibi(struct i3c_device *i3cdev)
+ {
++	struct i3c_mctp *priv = i3cdev_get_drvdata(i3cdev);
+ 	struct i3c_ibi_setup ibireq = {
+ 		.handler = i3c_mctp_ibi_handler,
+ 		.max_payload_len = 2,
+ 		.num_slots = 10,
+ 	};
+-	int ret;
++	int ret = 0;
+ 
++	if (priv->ibi_enabled)
++		return ret;
+ 	ret = i3c_device_request_ibi(i3cdev, &ibireq);
+ 	if (ret)
+ 		return ret;
+ 	ret = i3c_device_enable_ibi(i3cdev);
+ 	if (ret)
+ 		i3c_device_free_ibi(i3cdev);
++	else
++		priv->ibi_enabled = true;
+ 
+ 	return ret;
+ }
+ 
++static void i3c_mctp_disable_ibi(struct i3c_device *i3cdev)
++{
++	struct i3c_mctp *priv = i3cdev_get_drvdata(i3cdev);
++	int ret;
++
++	ret = i3c_device_disable_ibi(i3cdev);
++	if (!ret) {
++		i3c_device_free_ibi(i3cdev);
++	} else {
++		dev_warn(i3cdev_to_dev(i3cdev), "Failed to disable IBI, ret = %d", ret);
++		return;
++	}
++
++	priv->ibi_enabled = false;
++}
++
+ /**
+  * i3c_mctp_get_eid() - receive MCTP EID assigned to the device
+  *
+@@ -455,9 +778,25 @@ static int i3c_mctp_enable_ibi(struct i3c_device *i3cdev)
+  */
+ int i3c_mctp_get_eid(struct i3c_mctp_client *client, u8 domain_id, u8 *eid)
+ {
+-	/* TODO: Implement EID assignment basing on domain ID */
+-	*eid = 1;
+-	return 0;
++	struct i3c_mctp_endpoint *endpoint;
++	struct i3c_device_info info;
++	int ret = -ENOENT;
++
++	i3c_device_get_info(client->priv->i3c, &info);
++
++	mutex_lock(&client->priv->endpoints_lock);
++
++	list_for_each_entry(endpoint, &client->priv->endpoints, link) {
++		if (endpoint->eid_info.domain_id == domain_id &&
++		    endpoint->eid_info.dyn_addr == info.dyn_addr) {
++			*eid = endpoint->eid_info.eid;
++			ret = 0;
++			break;
++		}
++	}
++
++	mutex_unlock(&client->priv->endpoints_lock);
++	return ret;
+ }
+ EXPORT_SYMBOL_GPL(i3c_mctp_get_eid);
+ 
+@@ -471,13 +810,45 @@ EXPORT_SYMBOL_GPL(i3c_mctp_get_eid);
+  */
+ int i3c_mctp_send_packet(struct i3c_device *i3c, struct i3c_mctp_packet *tx_packet)
+ {
+-	struct i3c_priv_xfer xfers = {
+-		.rnw = false,
+-		.len = tx_packet->size,
+-		.data.out = &tx_packet->data,
+-	};
+-
+-	return i3c_device_do_priv_xfers(i3c, &xfers, 1);
++    struct i3c_mctp *priv = i3cdev_get_drvdata(i3c);
++    u8 *protocol_hdr = (u8 *)tx_packet->data.protocol_hdr;
++    struct i3c_priv_xfer xfers = {
++        .rnw = false,
++        .len = tx_packet->size,
++        .data.out = &tx_packet->data,
++    };
++ 
++    u8 *data, pec;
++    u8 addr;
++ 
++    spin_lock(&priv->clients_lock);
++    if (i3c_mctp_find_client(priv, tx_packet) == priv->peci_client)
++        protocol_hdr[MCTP_HDR_SRC_EID_OFFSET] = priv->eid;
++    spin_unlock(&priv->clients_lock);
++ 
++    /* No space for PEC byte */
++    if (tx_packet->size == sizeof(tx_packet->data))
++        return -EINVAL;
++    /* Add PEC */
++    data = (u8 *)&tx_packet->data;
++    addr = i3cdev_get_dynaddr(priv->i3c) << 1;
++    pec = i2c_smbus_pec(0, &addr, 1);
++    pec = i2c_smbus_pec(pec, data, tx_packet->size);
++    data[tx_packet->size] = pec;
++    xfers.len++;
++    print_hex_dump_bytes("MCTP TX : ", DUMP_PREFIX_NONE, data, xfers.len);
++ 
++    /*
++     * Check against packet size + PEC byte
++     * to not send more data than it was set in the probe
++     */
++    if (priv->max_write_len < xfers.len) {
++        dev_dbg(i3cdev_to_dev(i3c), "Length mismatch. MWL = %d, xfers.len = %d",
++            priv->max_write_len, xfers.len);
++        return -EINVAL;
++    }
++ 
++    return i3c_device_do_priv_xfers(i3c, &xfers, 1);
+ }
+ EXPORT_SYMBOL_GPL(i3c_mctp_send_packet);
+ 
+@@ -513,11 +884,37 @@ struct i3c_mctp_packet *i3c_mctp_receive_packet(struct i3c_mctp_client *client,
+ }
+ EXPORT_SYMBOL_GPL(i3c_mctp_receive_packet);
+ 
++static void i3c_mctp_i3c_event_cb(struct i3c_device *dev, enum i3c_event event)
++{
++	struct i3c_mctp *priv = i3cdev_get_drvdata(dev);
++	struct i3c_device_info info;
++
++	switch (event) {
++	case i3c_event_prepare_for_rescan:
++		/*
++		 * Disable IBI and polling mode blindly.
++		 */
++		i3c_mctp_disable_ibi(dev);
++		cancel_delayed_work(&priv->polling_work);
++		break;
++	case i3c_event_rescan_done:
++		i3c_device_get_info(dev, &info);
++		if (i3c_mctp_enable_ibi(dev)) {
++			if (!delayed_work_pending(&priv->polling_work)) {
++				INIT_DELAYED_WORK(&priv->polling_work, i3c_mctp_polling_work);
++				schedule_delayed_work(&priv->polling_work,
++						      msecs_to_jiffies(POLLING_TIMEOUT_MS));
++			}
++		}
++		break;
++	default:
++		break;
++	}
++}
++
+ static int i3c_mctp_probe(struct i3c_device *i3cdev)
+ {
+-	int ibi_payload_size = I3C_MCTP_IBI_PAYLOAD_SIZE;
+ 	struct device *dev = i3cdev_to_dev(i3cdev);
+-	const struct i3c_device_id *id = i3c_device_match_id(i3cdev, i3c_mctp_ids);
+ 	struct i3c_device_info info;
+ 	struct i3c_mctp *priv;
+ 	int ret;
+@@ -542,51 +939,47 @@ static int i3c_mctp_probe(struct i3c_device *i3cdev)
+ 		goto error;
+ 	}
+ 
+-	priv->default_client = i3c_mctp_client_alloc(priv);
+-	if (IS_ERR(priv->default_client))
++	ret = i3c_device_control_pec(i3cdev, true);
++	if (ret)
+ 		goto error;
+ 
+-	dev_set_drvdata(i3cdev_to_dev(i3cdev), priv);
++	i3cdev_set_drvdata(i3cdev, priv);
+ 
+-	priv->i3c_peci = platform_device_register_data(i3cdev_to_dev(i3cdev), "peci-i3c", priv->id,
+-						       NULL, 0);
++	i3c_device_get_info(i3cdev, &info);
++
++	if (!IS_BMC_NON_LEGACY(info.pid))
++		priv->i3c_peci = platform_device_register_data(i3cdev_to_dev(i3cdev), "peci-i3c",
++							       priv->id, NULL, 0);
+ 	if (IS_ERR(priv->i3c_peci))
+ 		dev_warn(priv->dev, "failed to register peci-i3c device\n");
+ 
+-	if (id->data == (void *)POLL_MODE || i3c_mctp_enable_ibi(i3cdev)) {
+-		INIT_DELAYED_WORK(&priv->polling_work, i3c_mctp_polling_work);
+-		schedule_delayed_work(&priv->polling_work, msecs_to_jiffies(POLLING_TIMEOUT_MS));
+-		ibi_payload_size = 0;
+-		priv->poll_mode = true;
+-	}
+-
+-	i3c_device_get_info(i3cdev, &info);
+-
+-	ret = i3c_device_getmrl_ccc(i3cdev, &info);
+-	if (ret || info.max_read_len < I3C_MCTP_MIN_TRANSFER_SIZE)
+-		ret = i3c_device_setmrl_ccc(i3cdev, &info, I3C_MCTP_MIN_TRANSFER_SIZE,
+-					    ibi_payload_size);
++	if (info.max_read_len < I3C_MCTP_MIN_TRANSFER_SIZE)
++		ret = i3c_device_setmrl_ccc(i3cdev, &info, cpu_to_be16(I3C_MCTP_MIN_TRANSFER_SIZE),
++					    I3C_MCTP_IBI_PAYLOAD_SIZE);
+ 	if (ret && info.max_read_len < I3C_MCTP_MIN_TRANSFER_SIZE) {
+-		dev_err(dev, "Failed to set MRL!, ret = %d\n", ret);
+-		goto error_peci;
++		dev_info(dev, "Failed to set MRL, ret = %d, running with default: %d\n", ret,
++			 I3C_MCTP_MIN_TRANSFER_SIZE);
++		info.max_read_len = I3C_MCTP_MIN_TRANSFER_SIZE;
+ 	}
+ 	priv->max_read_len = info.max_read_len;
+ 
+-	ret = i3c_device_getmwl_ccc(i3cdev, &info);
+-	if (ret || info.max_write_len < I3C_MCTP_MIN_TRANSFER_SIZE)
+-		ret = i3c_device_setmwl_ccc(i3cdev, &info, I3C_MCTP_MIN_TRANSFER_SIZE);
++	if (info.max_write_len < I3C_MCTP_MIN_TRANSFER_SIZE)
++		ret = i3c_device_setmwl_ccc(i3cdev, &info, cpu_to_be16(I3C_MCTP_MIN_TRANSFER_SIZE));
+ 	if (ret && info.max_write_len < I3C_MCTP_MIN_TRANSFER_SIZE) {
+-		dev_err(dev, "Failed to set MWL!, ret = %d\n", ret);
+-		goto error_peci;
++		dev_info(dev, "Failed to set MWL, ret = %d, running with default: %d\n", ret,
++			 I3C_MCTP_MIN_TRANSFER_SIZE);
++		info.max_write_len = I3C_MCTP_MIN_TRANSFER_SIZE;
+ 	}
+ 	priv->max_write_len = info.max_write_len;
+ 
++	if (i3c_mctp_enable_ibi(i3cdev)) {
++		INIT_DELAYED_WORK(&priv->polling_work, i3c_mctp_polling_work);
++		schedule_delayed_work(&priv->polling_work, msecs_to_jiffies(POLLING_TIMEOUT_MS));
++	}
++	i3c_device_register_event_cb(i3cdev, i3c_mctp_i3c_event_cb);
++
+ 	return 0;
+ 
+-error_peci:
+-	platform_device_unregister(priv->i3c_peci);
+-	i3c_device_disable_ibi(i3cdev);
+-	i3c_device_free_ibi(i3cdev);
+ error:
+ 	cdev_del(&priv->cdev);
+ error_cdev:
+@@ -596,21 +989,33 @@ static int i3c_mctp_probe(struct i3c_device *i3cdev)
+ 
+ static void i3c_mctp_remove(struct i3c_device *i3cdev)
+ {
+-	struct i3c_mctp *priv = dev_get_drvdata(i3cdev_to_dev(i3cdev));
++	struct i3c_mctp *priv = i3cdev_get_drvdata(i3cdev);
++	int i;
++
++	if (priv->default_client)
++		priv->default_client->priv = NULL;
+ 
+-	if (priv->poll_mode)
+-		cancel_delayed_work_sync(&priv->polling_work);
+-	i3c_device_disable_ibi(i3cdev);
+-	i3c_device_free_ibi(i3cdev);
+-	i3c_mctp_client_free(priv->default_client);
+-	priv->default_client = NULL;
++	spin_lock(&priv->file_lock);
++	for (i = 0; i < priv->process_count; i++) {
++		kobject_put(&priv->cdev.kobj);
++		module_put(priv->cdev.owner);
++	}
++	spin_unlock(&priv->file_lock);
++
++	cancel_delayed_work(&priv->polling_work);
+ 	platform_device_unregister(priv->i3c_peci);
+ 
+ 	device_destroy(i3c_mctp_class, MKDEV(MAJOR(i3c_mctp_devt), priv->id));
+ 	cdev_del(&priv->cdev);
++	i3c_mctp_eid_info_list_remove(&priv->endpoints);
+ 	ida_free(&i3c_mctp_ida, priv->id);
+ }
+ 
++static const struct i3c_device_id i3c_mctp_ids[] = {
++	I3C_CLASS(0xCC, 0x0),
++	{ },
++};
++
+ static struct i3c_driver i3c_mctp_drv = {
+ 	.driver.name = "i3c-mctp",
+ 	.id_table = i3c_mctp_ids,
+diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mctp.c
+index 448b4bf8d376..0739e9eb98ab 100644
+--- a/drivers/i3c/mctp/i3c-target-mctp.c
++++ b/drivers/i3c/mctp/i3c-target-mctp.c
+@@ -2,6 +2,7 @@
+ /* Copyright (C) 2022 Intel Corporation.*/
+ 
+ #include <linux/cdev.h>
++#include <linux/i2c.h>
+ #include <linux/idr.h>
+ #include <linux/module.h>
+ #include <linux/poll.h>
+@@ -9,10 +10,17 @@
+ #include <linux/workqueue.h>
+ 
+ #include <linux/i3c/device.h>
++#include <linux/i3c/master.h>
++
++#include <linux/i3c/mctp/i3c-mctp.h>
+ 
+ #define I3C_TARGET_MCTP_MINORS	32
+ #define RX_RING_COUNT		16
+ 
++#define I3C_TARGET_IBI_PLD_SIZE	2
++
++#define I3C_TARGET_MCTP_MDB	0xAE
++
+ static struct class *i3c_target_mctp_class;
+ static dev_t i3c_target_mctp_devt;
+ static DEFINE_IDA(i3c_target_mctp_ida);
+@@ -25,6 +33,7 @@ struct i3c_target_mctp {
+ 	int id;
+ 	struct mctp_client *client;
+ 	spinlock_t client_lock; /* to protect client access */
++	struct device *dev;
+ };
+ 
+ struct mctp_client {
+@@ -39,6 +48,18 @@ struct mctp_packet {
+ 	u16 count;
+ };
+ 
++static u8 i3c_pec_calculate(u8 addr, const u8 *data, size_t count)
++{
++	u8 pec;
++
++	/*
++	 * MCTP over I3C PEC calulcaton is done with same algorithm SMBus PEC does.
++	 * Target device address (with RnW bit) is also included to PEC calculation.
++	 */
++	pec = i2c_smbus_pec(0x00, &addr, sizeof(addr));
++	return i2c_smbus_pec(pec, (u8 *)data, count);
++}
++
+ static void *i3c_target_mctp_packet_alloc(u16 count)
+ {
+ 	struct mctp_packet *packet;
+@@ -105,10 +126,26 @@ static void i3c_target_mctp_client_put(struct mctp_client *client)
+ static void
+ i3c_target_mctp_rx_packet_enqueue(struct i3c_device *i3cdev, const u8 *data, size_t count)
+ {
+-	struct i3c_target_mctp *priv = dev_get_drvdata(i3cdev_to_dev(i3cdev));
++	struct i3c_target_mctp *priv = i3cdev_get_drvdata(i3cdev);
+ 	struct mctp_client *client;
+ 	struct mctp_packet *packet;
++	size_t len;
+ 	int ret;
++	u8 addr;
++	u8 pec;
++
++	/* One byte for PEC */
++	if (count < (I3C_MCTP_MIN_PACKET_SIZE + 1) || count > (I3C_MCTP_PACKET_SIZE + 1))
++		return;
++
++	len = count - 1; /* PEC is the last byte */
++
++	addr = i3cdev->desc->info.dyn_addr << 1;
++	pec = i3c_pec_calculate(addr, data, len);
++	if (pec != data[count - 1]) {
++		dev_warn(i3cdev_to_dev(i3cdev), "PEC verification failed for incoming message\n");
++		return;
++	}
+ 
+ 	spin_lock(&priv->client_lock);
+ 	client = priv->client;
+@@ -119,11 +156,11 @@ i3c_target_mctp_rx_packet_enqueue(struct i3c_device *i3cdev, const u8 *data, siz
+ 	if (!client)
+ 		return;
+ 
+-	packet = i3c_target_mctp_packet_alloc(count);
++	packet = i3c_target_mctp_packet_alloc(len);
+ 	if (!packet)
+ 		goto err;
+ 
+-	memcpy(packet->data, data, count);
++	memcpy(packet->data, data, len);
+ 
+ 	ret = ptr_ring_produce(&client->rx_queue, packet);
+ 	if (ret)
+@@ -164,10 +201,14 @@ static void i3c_target_mctp_delete_client(struct mctp_client *client)
+ {
+ 	struct i3c_target_mctp *priv = client->priv;
+ 
++	if (!priv)
++		goto out;
++
+ 	spin_lock_irq(&priv->client_lock);
+ 	priv->client = NULL;
+ 	spin_unlock_irq(&priv->client_lock);
+ 
++out:
+ 	i3c_target_mctp_client_put(client);
+ }
+ 
+@@ -202,7 +243,7 @@ static ssize_t i3c_target_mctp_read(struct file *file, char __user *buf,
+ 
+ 	rx_packet = ptr_ring_consume_irq(&client->rx_queue);
+ 	if (!rx_packet)
+-		return 0;
++		return -EAGAIN;
+ 
+ 	if (count < rx_packet->count) {
+ 		count = -EINVAL;
+@@ -222,13 +263,25 @@ static ssize_t i3c_target_mctp_read(struct file *file, char __user *buf,
+ static ssize_t i3c_target_mctp_write(struct file *file, const char __user *buf,
+ 				     size_t count, loff_t *ppos)
+ {
++	const size_t total_count = count + 1; /* One extra byte for PEC */
+ 	struct mctp_client *client = file->private_data;
+ 	struct i3c_target_mctp *priv = client->priv;
+ 	struct i3c_priv_xfer xfers[1] = {};
+ 	u8 *tx_data;
++	u8 ibi_payload[I3C_TARGET_IBI_PLD_SIZE] = {I3C_TARGET_MCTP_MDB, 0xAE};
+ 	int ret;
++	u8 addr;
++
++	if (!priv)
++		return -ENODEV;
++
++	if (count < I3C_MCTP_MIN_PACKET_SIZE)
++		return -EINVAL;
+ 
+-	tx_data = kzalloc(count, GFP_KERNEL);
++	if (count > I3C_MCTP_PACKET_SIZE)
++		return -ENOSPC;
++
++	tx_data = kzalloc(total_count, GFP_KERNEL);
+ 	if (!tx_data)
+ 		return -ENOMEM;
+ 
+@@ -237,21 +290,18 @@ static ssize_t i3c_target_mctp_write(struct file *file, const char __user *buf,
+ 		goto out_packet;
+ 	}
+ 
++	addr = (priv->i3cdev->desc->info.dyn_addr << 1) | 0x01;
++	tx_data[total_count - 1] = i3c_pec_calculate(addr, tx_data, count);
++
+ 	xfers[0].data.out = tx_data;
+-	xfers[0].len = count;
++	xfers[0].len = total_count;
+ 
+-	ret = i3c_device_do_priv_xfers(priv->i3cdev, xfers, ARRAY_SIZE(xfers));
++	ret = i3c_device_put_read_data(priv->i3cdev, xfers, ARRAY_SIZE(xfers),
++				       &ibi_payload[0], ARRAY_SIZE(ibi_payload));
+ 	if (ret)
+ 		goto out_packet;
+ 	ret = count;
+ 
+-	/*
+-	 * TODO: Add support for IBI generation - it should be done only if IBI
+-	 * are enabled (the Active Controller may disabled them using CCC for
+-	 * that). Otherwise (if IBIs are disabled), we should make sure that when
+-	 * Active Controller issues GETSTATUS CCC the return value indicates
+-	 * that data is ready.
+-	 */
+ out_packet:
+ 	kfree(tx_data);
+ 	return ret;
+@@ -294,7 +344,7 @@ static int i3c_target_mctp_probe(struct i3c_device *i3cdev)
+ {
+ 	struct device *parent = i3cdev_to_dev(i3cdev);
+ 	struct i3c_target_mctp *priv;
+-	struct device *dev;
++	dev_t devt;
+ 	int ret;
+ 
+ 	priv = devm_kzalloc(parent, sizeof(*priv), GFP_KERNEL);
+@@ -311,16 +361,17 @@ static int i3c_target_mctp_probe(struct i3c_device *i3cdev)
+ 
+ 	cdev_init(&priv->cdev, &i3c_target_mctp_fops);
+ 	priv->cdev.owner = THIS_MODULE;
+-	ret = cdev_add(&priv->cdev, i3c_target_mctp_devt, 1);
++	devt = MKDEV(MAJOR(i3c_target_mctp_devt), priv->id);
++	ret = cdev_add(&priv->cdev, devt, 1);
+ 	if (ret) {
+ 		ida_free(&i3c_target_mctp_ida, priv->id);
+ 		return ret;
+ 	}
+ 
+-	dev = device_create(i3c_target_mctp_class, parent, i3c_target_mctp_devt,
+-			    NULL, "i3c-mctp-target-%d", priv->id);
+-	if (IS_ERR(dev)) {
+-		ret = PTR_ERR(dev);
++	priv->dev = device_create(i3c_target_mctp_class, parent, devt, NULL,
++				  "i3c-mctp-target-%d", priv->id);
++	if (IS_ERR(priv->dev)) {
++		ret = PTR_ERR(priv->dev);
+ 		goto err;
+ 	}
+ 
+@@ -338,9 +389,15 @@ static int i3c_target_mctp_probe(struct i3c_device *i3cdev)
+ 
+ static void i3c_target_mctp_remove(struct i3c_device *i3cdev)
+ {
+-	struct i3c_target_mctp *priv = dev_get_drvdata(i3cdev_to_dev(i3cdev));
++	struct i3c_target_mctp *priv = i3cdev_get_drvdata(i3cdev);
++
++	if (priv->client) {
++		priv->client->priv = NULL;
++		kobject_put(&priv->cdev.kobj);
++		module_put(priv->cdev.owner);
++	}
+ 
+-	device_destroy(i3c_target_mctp_class, i3c_target_mctp_devt);
++	device_destroy(i3c_target_mctp_class, priv->dev->devt);
+ 	cdev_del(&priv->cdev);
+ 	ida_free(&i3c_target_mctp_ida, priv->id);
+ }
+diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
+index 3e6ee6817d81..4811e870a8cb 100644
+--- a/include/linux/i3c/device.h
++++ b/include/linux/i3c/device.h
+@@ -129,6 +129,7 @@ struct i3c_device_info {
+ 	u32 max_read_turnaround;
+ 	u16 max_read_len;
+ 	u16 max_write_len;
++	u8 pec;
+ 	__be16 status;
+ };
+ 
+@@ -297,6 +298,8 @@ int i3c_device_do_priv_xfers(struct i3c_device *dev,
+ 			     int nxfers);
+ 
+ int i3c_device_generate_ibi(struct i3c_device *dev, const u8 *data, int len);
++int i3c_device_put_read_data(struct i3c_device *dev, struct i3c_priv_xfer *xfers, int nxfers,
++			     const u8 *ibi_data, int ibi_len);
+ 
+ void i3c_device_get_info(struct i3c_device *dev, struct i3c_device_info *info);
+ 
+@@ -351,4 +354,27 @@ struct i3c_target_read_setup {
+ 
+ int i3c_target_read_register(struct i3c_device *dev, const struct i3c_target_read_setup *setup);
+ 
++int i3c_device_control_pec(struct i3c_device *dev, bool pec);
++
++/**
++ * enum i3c_event - List of possible events could be send/published to
++ *		    registered devices.
++ * @i3c_event_prepare_for_rescan: Event send when controller driver is going to
++ *				  run bus discovery again.
++ * @i3c_event_rescan_done: Event send when controller driver run bus discovery
++ *			   again.
++ */
++enum i3c_event {
++	i3c_event_prepare_for_rescan = 0,
++	i3c_event_rescan_done,
++};
++
++/**
++ * i3c_event_cb - callback registered by device driver and used by controller
++ *		  driver to publish event.
++ */
++typedef void (*i3c_event_cb)(struct i3c_device *dev, enum i3c_event event);
++
++void i3c_device_register_event_cb(struct i3c_device *dev, i3c_event_cb cb);
++
+ #endif /* I3C_DEV_H */
+diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
+index a449c5ebe735..9ea3b4ad0804 100644
+--- a/include/linux/i3c/master.h
++++ b/include/linux/i3c/master.h
+@@ -230,6 +230,7 @@ struct i3c_dev_desc {
+ 	struct i3c_device_ibi_info *ibi;
+ 	struct i3c_device *dev;
+ 	const struct i3c_dev_boardinfo *boardinfo;
++	i3c_event_cb event_cb;
+ };
+ 
+ /**
+@@ -509,6 +510,7 @@ struct i3c_master_controller {
+ 	struct i2c_adapter i2c;
+ 	const struct i3c_master_controller_ops *ops;
+ 	const struct i3c_target_ops *target_ops;
++	unsigned int pec_supported : 1;
+ 	unsigned int target : 1;
+ 	unsigned int secondary : 1;
+ 	unsigned int init_done : 1;
+diff --git a/include/linux/i3c/mctp/i3c-mctp.h b/include/linux/i3c/mctp/i3c-mctp.h
+index 596759faa87d..da3b1185c5de 100644
+--- a/include/linux/i3c/mctp/i3c-mctp.h
++++ b/include/linux/i3c/mctp/i3c-mctp.h
+@@ -4,8 +4,8 @@
+ #ifndef I3C_MCTP_H
+ #define I3C_MCTP_H
+ 
+-#define I3C_MCTP_PACKET_SIZE	255
+-#define I3C_MCTP_PAYLOAD_SIZE	251
++#define I3C_MCTP_PACKET_SIZE	69
++#define I3C_MCTP_PAYLOAD_SIZE	65
+ #define I3C_MCTP_HDR_SIZE	4
+ 
+ /* PECI MCTP Intel VDM definitions */
+@@ -18,6 +18,8 @@
+ #define MCTP_MSG_HDR_VENDOR_OFFSET	1
+ #define MCTP_MSG_HDR_OPCODE_OFFSET	4
+ 
++#define I3C_MCTP_MIN_PACKET_SIZE	5
++
+ struct i3c_mctp_client;
+ 
+ struct mctp_protocol_hdr {
+@@ -39,6 +41,7 @@ struct i3c_mctp_packet {
+ 
+ void *i3c_mctp_packet_alloc(gfp_t flags);
+ void i3c_mctp_packet_free(void *packet);
++void i3c_mctp_flush_rx_queue(struct i3c_mctp_client *client);
+ 
+ int i3c_mctp_get_eid(struct i3c_mctp_client *client, u8 domain_id, u8 *eid);
+ int i3c_mctp_send_packet(struct i3c_device *i3c, struct i3c_mctp_packet *tx_packet);
+diff --git a/include/linux/i3c/target.h b/include/linux/i3c/target.h
+index 9e71124b5325..cf260b96daa1 100644
+--- a/include/linux/i3c/target.h
++++ b/include/linux/i3c/target.h
+@@ -14,6 +14,9 @@ struct i3c_target_ops {
+ 	void (*bus_cleanup)(struct i3c_master_controller *master);
+ 	int (*priv_xfers)(struct i3c_dev_desc *dev, struct i3c_priv_xfer *xfers, int nxfers);
+ 	int (*generate_ibi)(struct i3c_dev_desc *dev, const u8 *data, int len);
++	int (*put_read_data)(struct i3c_dev_desc *dev, struct i3c_priv_xfer *xfers, int nxfers,
++			     const u8 *data, int len);
++	u8 (*get_dyn_addr)(struct i3c_master_controller *master);
+ };
+ 
+ int i3c_target_register(struct i3c_master_controller *master, struct device *parent,
+diff --git a/include/uapi/linux/i3c/i3c-mctp.h b/include/uapi/linux/i3c/i3c-mctp.h
+new file mode 100644
+index 000000000000..59870c876f56
+--- /dev/null
++++ b/include/uapi/linux/i3c/i3c-mctp.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/* Copyright (c) 2022 Intel Corporation */
++
++#ifndef _UAPI_LINUX_I3C_MCTP_H
++#define _UAPI_LINUX_I3C_MCTP_H
++
++#include <linux/ioctl.h>
++#include <linux/types.h>
++
++/*
++ * maximum possible number of struct eid_info elements stored in list
++ */
++#define I3C_MCTP_EID_INFO_MAX 256
++
++/*
++ * MCTP operations
++ * @I3C_MCTP_SET_EID_INFO: write or overwrite already existing list of
++ * CPU EID and Domain ID mappings
++ * @I3C_MCTP_SET_OWN_EID: write/overwrite own EID information
++ * @I3C_MCTP_IOCTL_REGISTER_DEFAULT_CLIENT: register the client to
++ * process MCTP packets over I3C
++ */
++
++struct i3c_mctp_eid_info {
++	__u8 eid;
++	__u8 dyn_addr;
++	__u8 domain_id;
++};
++
++struct i3c_mctp_set_eid_info {
++	__u64 ptr;
++	__u16 count;
++};
++
++struct i3c_mctp_set_own_eid {
++	__u8 eid;
++};
++
++#define I3C_MCTP_IOCTL_BASE    0x69
++
++#define I3C_MCTP_IOCTL_SET_EID_INFO \
++	_IOW(I3C_MCTP_IOCTL_BASE, 0x41, struct i3c_mctp_set_eid_info)
++#define I3C_MCTP_IOCTL_SET_OWN_EID \
++	_IOW(I3C_MCTP_IOCTL_BASE, 0x42, struct i3c_mctp_set_own_eid)
++#define I3C_MCTP_IOCTL_REGISTER_DEFAULT_CLIENT \
++	_IO(I3C_MCTP_IOCTL_BASE, 0x43)
++
++#endif /* _UAPI_LINUX_I3C_MCTP_H */
+-- 
+2.34.1
+
diff --git a/recipes-kernel/linux/5.15/1025-drivers-i3c-mctp-sync-npcm-i3c-mctp.patch b/recipes-kernel/linux/5.15/1025-drivers-i3c-mctp-sync-npcm-i3c-mctp.patch
index ed53c9c..803e3e3 100644
--- a/recipes-kernel/linux/5.15/1025-drivers-i3c-mctp-sync-npcm-i3c-mctp.patch
+++ b/recipes-kernel/linux/5.15/1025-drivers-i3c-mctp-sync-npcm-i3c-mctp.patch
@@ -1,34 +1,37 @@
-From 0334fbe6d38cb2bfef28b2e54fb76c5a0d709338 Mon Sep 17 00:00:00 2001
+From 0447505c0b877e70521171d11006b72b2ef9aef6 Mon Sep 17 00:00:00 2001
 From: David Wang <davidwang@quantatw.com>
 Date: Mon, 6 Nov 2023 16:01:32 +0800
 Subject: [PATCH] drivers: i3c/mctp: sync npcm i3c/mctp
 
 ---
- drivers/i3c/Kconfig                 |   31 +
- drivers/i3c/Makefile                |    4 +
- drivers/i3c/device.c                |  161 ++++
- drivers/i3c/i3c-hub.c               |  708 ++++++++++++++
- drivers/i3c/i3c-npcm-bic.c          |  237 +++++
- drivers/i3c/i3cdev.c                |  435 +++++++++
- drivers/i3c/internals.h             |   12 +
- drivers/i3c/master.c                |  501 +++++++++-
- drivers/i3c/master/svc-i3c-master.c | 1332 +++++++++++++++++++++++----
- drivers/i3c/mctp/Kconfig            |   14 +
- drivers/i3c/mctp/Makefile           |    3 +
- drivers/i3c/mctp/i3c-mctp.c         |  624 +++++++++++++
- drivers/i3c/mctp/i3c-target-mctp.c  |  389 ++++++++
- drivers/net/mctp/Kconfig            |    9 +
- drivers/net/mctp/Makefile           |    1 +
- drivers/net/mctp/mctp-i3c.c         |  777 ++++++++++++++++
- include/linux/i3c/ccc.h             |   11 +
- include/linux/i3c/device.h          |   19 +
- include/linux/i3c/master.h          |   37 +
- include/linux/i3c/mctp/i3c-mctp.h   |   50 +
- include/linux/i3c/target.h          |   23 +
- include/uapi/linux/i3c/i3cdev.h     |   38 +
- net/mctp/af_mctp.c                  |    7 -
- net/mctp/route.c                    |    6 -
- 24 files changed, 5245 insertions(+), 184 deletions(-)
+ drivers/i3c/Kconfig                      |   31 +
+ drivers/i3c/Makefile                     |    4 +
+ drivers/i3c/device.c                     |  161 ++
+ drivers/i3c/i3c-hub.c                    |  708 ++++++++
+ drivers/i3c/i3c-npcm-bic.c               |  237 +++
+ drivers/i3c/i3cdev.c                     |  435 +++++
+ drivers/i3c/internals.h                  |   12 +
+ drivers/i3c/master.c                     |  537 +++++-
+ drivers/i3c/master/i3c-master-cdns.c     |   13 +-
+ drivers/i3c/master/mipi-i3c-hci/dat_v1.c |   29 +-
+ drivers/i3c/master/mipi-i3c-hci/dma.c    |   12 +-
+ drivers/i3c/master/svc-i3c-master.c      | 1884 ++++++++++++++++++----
+ drivers/i3c/mctp/Kconfig                 |   14 +
+ drivers/i3c/mctp/Makefile                |    3 +
+ drivers/i3c/mctp/i3c-mctp.c              |  624 +++++++
+ drivers/i3c/mctp/i3c-target-mctp.c       |  389 +++++
+ drivers/net/mctp/Kconfig                 |    9 +
+ drivers/net/mctp/Makefile                |    1 +
+ drivers/net/mctp/mctp-i3c.c              |  777 +++++++++
+ include/linux/i3c/ccc.h                  |   11 +
+ include/linux/i3c/device.h               |   19 +
+ include/linux/i3c/master.h               |   37 +
+ include/linux/i3c/mctp/i3c-mctp.h        |   50 +
+ include/linux/i3c/target.h               |   23 +
+ include/uapi/linux/i3c/i3cdev.h          |   38 +
+ net/mctp/af_mctp.c                       |    7 -
+ net/mctp/route.c                         |    6 -
+ 27 files changed, 5728 insertions(+), 343 deletions(-)
  create mode 100644 drivers/i3c/i3c-hub.c
  create mode 100644 drivers/i3c/i3c-npcm-bic.c
  create mode 100644 drivers/i3c/i3cdev.c
@@ -1237,7 +1240,7 @@
 +MODULE_LICENSE("GPL v2");
 diff --git a/drivers/i3c/i3cdev.c b/drivers/i3c/i3cdev.c
 new file mode 100644
-index 000000000000..d8e0b9de6e59
+index 000000000000..ed16125fc758
 --- /dev/null
 +++ b/drivers/i3c/i3cdev.c
 @@ -0,0 +1,435 @@
@@ -1422,7 +1425,7 @@
 +	}
 +
 +	if (ret < 0) {
-+		i--;
++		nxfers = i;
 +		goto err_free_mem;
 +	}
 +
@@ -1440,7 +1443,7 @@
 +	}
 +
 +err_free_mem:
-+	for (; i >= 0; i--)
++	for (i = 0; i < nxfers; i++)
 +		kfree(data_ptrs[i]);
 +	kfree(data_ptrs);
 +err_free_k_xfer:
@@ -1707,7 +1710,7 @@
 +int i3c_dev_generate_ibi_locked(struct i3c_dev_desc *dev, const u8 *data, int len);
  #endif /* I3C_INTERNAL_H */
 diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
-index b6abbb0acbbd..a4f4f0dd8757 100644
+index b6abbb0acbbd..9ade31fbae5f 100644
 --- a/drivers/i3c/master.c
 +++ b/drivers/i3c/master.c
 @@ -21,6 +21,7 @@
@@ -1793,7 +1796,36 @@
  static const char * const i3c_bus_mode_strings[] = {
  	[I3C_BUS_MODE_PURE] = "pure",
  	[I3C_BUS_MODE_MIXED_FAST] = "mixed-fast",
-@@ -542,7 +579,7 @@ static void i3c_masterdev_release(struct device *dev)
+@@ -514,6 +551,20 @@ static ssize_t i2c_scl_frequency_show(struct device *dev,
+ }
+ static DEVICE_ATTR_RO(i2c_scl_frequency);
+ 
++static ssize_t discover_store(struct device *dev, struct device_attribute *da,
++			      const char *buf, size_t count)
++{
++	struct i3c_master_controller *master;
++	ssize_t ret = count;
++
++	master = dev_to_i3cmaster(dev);
++	dev_dbg(&master->dev, "Request master to do DAA\n");
++	i3c_master_do_daa(master);
++
++	return ret;
++}
++static DEVICE_ATTR_WO(discover);
++
+ static struct attribute *i3c_masterdev_attrs[] = {
+ 	&dev_attr_mode.attr,
+ 	&dev_attr_current_master.attr,
+@@ -524,6 +575,7 @@ static struct attribute *i3c_masterdev_attrs[] = {
+ 	&dev_attr_pid.attr,
+ 	&dev_attr_dynamic_address.attr,
+ 	&dev_attr_hdrcap.attr,
++	&dev_attr_discover.attr,
+ 	NULL,
+ };
+ ATTRIBUTE_GROUPS(i3c_masterdev);
+@@ -542,7 +594,7 @@ static void i3c_masterdev_release(struct device *dev)
  	of_node_put(dev->of_node);
  }
  
@@ -1802,7 +1834,7 @@
  	.groups	= i3c_masterdev_groups,
  };
  
-@@ -775,6 +812,20 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master,
+@@ -775,6 +827,20 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master,
  	return ret;
  }
  
@@ -1823,7 +1855,7 @@
  /**
   * i3c_master_entdaa_locked() - start a DAA (Dynamic Address Assignment)
   *				procedure
-@@ -1001,8 +1052,27 @@ static int i3c_master_setnewda_locked(struct i3c_master_controller *master,
+@@ -1001,8 +1067,27 @@ static int i3c_master_setnewda_locked(struct i3c_master_controller *master,
  	return i3c_master_setda_locked(master, oldaddr, newaddr, false);
  }
  
@@ -1853,7 +1885,7 @@
  {
  	struct i3c_ccc_cmd_dest dest;
  	struct i3c_ccc_mrl *mrl;
-@@ -1043,8 +1113,7 @@ static int i3c_master_getmrl_locked(struct i3c_master_controller *master,
+@@ -1043,8 +1128,7 @@ static int i3c_master_getmrl_locked(struct i3c_master_controller *master,
  	return ret;
  }
  
@@ -1863,7 +1895,7 @@
  {
  	struct i3c_ccc_cmd_dest dest;
  	struct i3c_ccc_mwl *mwl;
-@@ -1073,6 +1142,52 @@ static int i3c_master_getmwl_locked(struct i3c_master_controller *master,
+@@ -1073,6 +1157,52 @@ static int i3c_master_getmwl_locked(struct i3c_master_controller *master,
  	return ret;
  }
  
@@ -1916,7 +1948,7 @@
  static int i3c_master_getmxds_locked(struct i3c_master_controller *master,
  				     struct i3c_device_info *info)
  {
-@@ -1220,6 +1335,32 @@ static int i3c_master_getdcr_locked(struct i3c_master_controller *master,
+@@ -1220,6 +1350,32 @@ static int i3c_master_getdcr_locked(struct i3c_master_controller *master,
  	return ret;
  }
  
@@ -1949,7 +1981,17 @@
  static int i3c_master_retrieve_dev_info(struct i3c_dev_desc *dev)
  {
  	struct i3c_master_controller *master = i3c_dev_get_master(dev);
-@@ -1431,6 +1572,36 @@ static void i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev)
+@@ -1380,6 +1536,9 @@ static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
+ 		i3c_bus_set_addr_slot_status(&master->bus,
+ 					     dev->info.dyn_addr,
+ 					     I3C_ADDR_SLOT_I3C_DEV);
++		if (old_dyn_addr)
++			i3c_bus_set_addr_slot_status(&master->bus, old_dyn_addr,
++						     I3C_ADDR_SLOT_FREE);
+ 	}
+ 
+ 	if (master->ops->reattach_i3c_dev) {
+@@ -1431,6 +1590,36 @@ static void i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev)
  		master->ops->detach_i2c_dev(dev);
  }
  
@@ -1986,7 +2028,20 @@
  static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master,
  					  struct i3c_dev_boardinfo *boardinfo)
  {
-@@ -1746,6 +1917,18 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
+@@ -1506,11 +1695,9 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
+ 			desc->dev->dev.of_node = desc->boardinfo->of_node;
+ 
+ 		ret = device_register(&desc->dev->dev);
+-		if (ret) {
++		if (ret)
+ 			dev_err(&master->dev,
+ 				"Failed to add I3C device (err = %d)\n", ret);
+-			put_device(&desc->dev->dev);
+-		}
+ 	}
+ }
+ 
+@@ -1746,6 +1933,18 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
  	 * i3c_master_add_i3c_dev_locked().
  	 */
  	list_for_each_entry(i3cboardinfo, &master->boardinfo.i3c, node) {
@@ -2005,7 +2060,7 @@
  
  		/*
  		 * We don't reserve a dynamic address for devices that
-@@ -1777,6 +1960,16 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
+@@ -1777,6 +1976,16 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
  			i3c_master_early_i3c_dev_add(master, i3cboardinfo);
  	}
  
@@ -2022,7 +2077,35 @@
  	ret = i3c_master_do_daa(master);
  	if (ret)
  		goto err_rstdaa;
-@@ -2128,6 +2321,9 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master)
+@@ -1896,7 +2105,15 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
+ 
+ 			if (olddev->ibi->enabled) {
+ 				enable_ibi = true;
+-				i3c_dev_disable_ibi_locked(olddev);
++				ret = i3c_dev_disable_ibi_locked(olddev);
++				/*
++				 * If olddev is not active on the bus,
++				 * disable_ibi will get NACK.
++				 * Set ibi->enabled to false to
++				 * avoid warning message in free_ibi.
++				 */
++				if (ret == I3C_ERROR_M2)
++					olddev->ibi->enabled = false;
+ 			}
+ 
+ 			i3c_dev_free_ibi_locked(olddev);
+@@ -1909,10 +2126,6 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
+ 		i3c_master_free_i3c_dev(olddev);
+ 	}
+ 
+-	ret = i3c_master_reattach_i3c_dev(newdev, old_dyn_addr);
+-	if (ret)
+-		goto err_detach_dev;
+-
+ 	/*
+ 	 * Depending on our previous state, the expected dynamic address might
+ 	 * differ:
+@@ -2128,6 +2341,9 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master)
  	if (!of_property_read_u32(i3cbus_np, "i3c-scl-hz", &val))
  		master->bus.scl_rate.i3c = val;
  
@@ -2032,7 +2115,7 @@
  	return 0;
  }
  
-@@ -2550,6 +2746,8 @@ int i3c_master_register(struct i3c_master_controller *master,
+@@ -2550,6 +2766,8 @@ int i3c_master_register(struct i3c_master_controller *master,
  	if (ret)
  		goto err_del_dev;
  
@@ -2041,7 +2124,7 @@
  	/*
  	 * We're done initializing the bus and the controller, we can now
  	 * register I3C devices discovered during the initial DAA.
-@@ -2584,6 +2782,8 @@ EXPORT_SYMBOL_GPL(i3c_master_register);
+@@ -2584,6 +2802,8 @@ EXPORT_SYMBOL_GPL(i3c_master_register);
   */
  int i3c_master_unregister(struct i3c_master_controller *master)
  {
@@ -2050,7 +2133,7 @@
  	i3c_master_i2c_adapter_cleanup(master);
  	i3c_master_unregister_i3c_devs(master);
  	i3c_master_bus_cleanup(master);
-@@ -2593,6 +2793,216 @@ int i3c_master_unregister(struct i3c_master_controller *master)
+@@ -2593,6 +2813,216 @@ int i3c_master_unregister(struct i3c_master_controller *master)
  }
  EXPORT_SYMBOL_GPL(i3c_master_unregister);
  
@@ -2267,7 +2350,7 @@
  int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
  				 struct i3c_priv_xfer *xfers,
  				 int nxfers)
-@@ -2606,10 +3016,38 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
+@@ -2606,10 +3036,38 @@ int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
  	if (!master || !xfers)
  		return -EINVAL;
  
@@ -2279,7 +2362,8 @@
 +
 +		return master->ops->priv_xfers(dev, xfers, nxfers);
 +	}
-+
+ 
+-	return master->ops->priv_xfers(dev, xfers, nxfers);
 +	if (!master->target_ops->priv_xfers)
 +		return -EOPNOTSUPP;
 +
@@ -2290,8 +2374,7 @@
 +
 +{
 +	struct i3c_master_controller *master;
- 
--	return master->ops->priv_xfers(dev, xfers, nxfers);
++
 +	if (!dev)
 +		return -ENOENT;
 +
@@ -2309,7 +2392,7 @@
  }
  
  int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev)
-@@ -2697,6 +3135,45 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
+@@ -2697,6 +3155,45 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
  	dev->ibi = NULL;
  }
  
@@ -2355,11 +2438,169 @@
  static int __init i3c_init(void)
  {
  	return bus_register(&i3c_bus_type);
+diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
+index b9cfda6ae9ae..5b37ffe5ad5b 100644
+--- a/drivers/i3c/master/i3c-master-cdns.c
++++ b/drivers/i3c/master/i3c-master-cdns.c
+@@ -77,8 +77,7 @@
+ #define PRESCL_CTRL0			0x14
+ #define PRESCL_CTRL0_I2C(x)		((x) << 16)
+ #define PRESCL_CTRL0_I3C(x)		(x)
+-#define PRESCL_CTRL0_I3C_MAX		GENMASK(9, 0)
+-#define PRESCL_CTRL0_I2C_MAX		GENMASK(15, 0)
++#define PRESCL_CTRL0_MAX		GENMASK(9, 0)
+ 
+ #define PRESCL_CTRL1			0x18
+ #define PRESCL_CTRL1_PP_LOW_MASK	GENMASK(15, 8)
+@@ -193,7 +192,7 @@
+ #define SLV_STATUS1_HJ_DIS		BIT(18)
+ #define SLV_STATUS1_MR_DIS		BIT(17)
+ #define SLV_STATUS1_PROT_ERR		BIT(16)
+-#define SLV_STATUS1_DA(s)		(((s) & GENMASK(15, 9)) >> 9)
++#define SLV_STATUS1_DA(x)		(((s) & GENMASK(15, 9)) >> 9)
+ #define SLV_STATUS1_HAS_DA		BIT(8)
+ #define SLV_STATUS1_DDR_RX_FULL		BIT(7)
+ #define SLV_STATUS1_DDR_TX_FULL		BIT(6)
+@@ -1235,7 +1234,7 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
+ 		return -EINVAL;
+ 
+ 	pres = DIV_ROUND_UP(sysclk_rate, (bus->scl_rate.i3c * 4)) - 1;
+-	if (pres > PRESCL_CTRL0_I3C_MAX)
++	if (pres > PRESCL_CTRL0_MAX)
+ 		return -ERANGE;
+ 
+ 	bus->scl_rate.i3c = sysclk_rate / ((pres + 1) * 4);
+@@ -1248,7 +1247,7 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
+ 	max_i2cfreq = bus->scl_rate.i2c;
+ 
+ 	pres = (sysclk_rate / (max_i2cfreq * 5)) - 1;
+-	if (pres > PRESCL_CTRL0_I2C_MAX)
++	if (pres > PRESCL_CTRL0_MAX)
+ 		return -ERANGE;
+ 
+ 	bus->scl_rate.i2c = sysclk_rate / ((pres + 1) * 5);
+@@ -1625,13 +1624,13 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
+ 	/* Device ID0 is reserved to describe this master. */
+ 	master->maxdevs = CONF_STATUS0_DEVS_NUM(val);
+ 	master->free_rr_slots = GENMASK(master->maxdevs, 1);
+-	master->caps.ibirfifodepth = CONF_STATUS0_IBIR_DEPTH(val);
+-	master->caps.cmdrfifodepth = CONF_STATUS0_CMDR_DEPTH(val);
+ 
+ 	val = readl(master->regs + CONF_STATUS1);
+ 	master->caps.cmdfifodepth = CONF_STATUS1_CMD_DEPTH(val);
+ 	master->caps.rxfifodepth = CONF_STATUS1_RX_DEPTH(val);
+ 	master->caps.txfifodepth = CONF_STATUS1_TX_DEPTH(val);
++	master->caps.ibirfifodepth = CONF_STATUS0_IBIR_DEPTH(val);
++	master->caps.cmdrfifodepth = CONF_STATUS0_CMDR_DEPTH(val);
+ 
+ 	spin_lock_init(&master->ibi.lock);
+ 	master->ibi.num_slots = CONF_STATUS1_IBI_HW_RES(val);
+diff --git a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
+index 47b9b4d4ed3f..97bb49ff5b53 100644
+--- a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
++++ b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c
+@@ -64,17 +64,15 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
+ 		return -EOPNOTSUPP;
+ 	}
+ 
+-	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);
+-		}
++	/* 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;
+@@ -89,13 +87,7 @@ 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;
+@@ -111,8 +103,7 @@ 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);
+-	if (hci->DAT_data)
+-		__clear_bit(dat_idx, hci->DAT_data);
++	__clear_bit(dat_idx, hci->DAT_data);
+ }
+ 
+ static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
+diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
+index b9b6be186438..af873a9be050 100644
+--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
++++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
+@@ -291,10 +291,7 @@ static int hci_dma_init(struct i3c_hci *hci)
+ 
+ 		rh->ibi_chunk_sz = dma_get_cache_alignment();
+ 		rh->ibi_chunk_sz *= IBI_CHUNK_CACHELINES;
+-		if (rh->ibi_chunk_sz > 256) {
+-			ret = -EINVAL;
+-			goto err_out;
+-		}
++		BUG_ON(rh->ibi_chunk_sz > 256);
+ 
+ 		ibi_status_ring_sz = rh->ibi_status_sz * rh->ibi_status_entries;
+ 		ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total;
+@@ -348,8 +345,6 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
+ 
+ 	for (i = 0; i < n; i++) {
+ 		xfer = xfer_list + i;
+-		if (!xfer->data)
+-			continue;
+ 		dma_unmap_single(&hci->master.dev,
+ 				 xfer->data_dma, xfer->data_len,
+ 				 xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+@@ -455,9 +450,10 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
+ 		/*
+ 		 * We're deep in it if ever this condition is ever met.
+ 		 * Hardware might still be writing to memory, etc.
++		 * Better suspend the world than risking silent corruption.
+ 		 */
+ 		dev_crit(&hci->master.dev, "unable to abort the ring\n");
+-		WARN_ON(1);
++		BUG();
+ 	}
+ 
+ 	for (i = 0; i < n; i++) {
+@@ -738,7 +734,7 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask)
+ 	unsigned int i;
+ 	bool handled = false;
+ 
+-	for (i = 0; mask && i < rings->total; i++) {
++	for (i = 0; mask && i < 8; i++) {
+ 		struct hci_rh_data *rh;
+ 		u32 status;
+ 
 diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
-index 7fc82b003b96..78777084958e 100644
+index 7fc82b003b96..121e4061b2a8 100644
 --- a/drivers/i3c/master/svc-i3c-master.c
 +++ b/drivers/i3c/master/svc-i3c-master.c
-@@ -10,14 +10,61 @@
+@@ -10,14 +10,62 @@
  #include <linux/bitfield.h>
  #include <linux/clk.h>
  #include <linux/completion.h>
@@ -2374,6 +2615,7 @@
  #include <linux/module.h>
 +#include <linux/mutex.h>
  #include <linux/of.h>
++#include <linux/reset.h>
 +#include <linux/pinctrl/consumer.h>
  #include <linux/platform_device.h>
 +#include <linux/pm_runtime.h>
@@ -2421,7 +2663,7 @@
  
  /* Master Mode Registers */
  #define SVC_I3C_MCONFIG      0x000
-@@ -30,6 +77,7 @@
+@@ -30,18 +78,22 @@
  #define   SVC_I3C_MCONFIG_ODBAUD(x) FIELD_PREP(GENMASK(23, 16), (x))
  #define   SVC_I3C_MCONFIG_ODHPP(x) FIELD_PREP(BIT(24), (x))
  #define   SVC_I3C_MCONFIG_SKEW(x) FIELD_PREP(GENMASK(27, 25), (x))
@@ -2429,7 +2671,10 @@
  #define   SVC_I3C_MCONFIG_I2CBAUD(x) FIELD_PREP(GENMASK(31, 28), (x))
  
  #define SVC_I3C_MCTRL        0x084
-@@ -39,9 +87,11 @@
+ #define   SVC_I3C_MCTRL_REQUEST_MASK GENMASK(2, 0)
++#define   SVC_I3C_MCTRL_REQUEST(x) FIELD_GET(GENMASK(2, 0), (x))
+ #define   SVC_I3C_MCTRL_REQUEST_NONE 0
+ #define   SVC_I3C_MCTRL_REQUEST_START_ADDR 1
  #define   SVC_I3C_MCTRL_REQUEST_STOP 2
  #define   SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK 3
  #define   SVC_I3C_MCTRL_REQUEST_PROC_DAA 4
@@ -2441,11 +2686,21 @@
  #define   SVC_I3C_MCTRL_IBIRESP_AUTO 0
  #define   SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE 0
  #define   SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE BIT(7)
-@@ -90,7 +140,12 @@
+@@ -57,6 +109,8 @@
+ #define   SVC_I3C_MSTATUS_STATE(x) FIELD_GET(GENMASK(2, 0), (x))
+ #define   SVC_I3C_MSTATUS_STATE_DAA(x) (SVC_I3C_MSTATUS_STATE(x) == 5)
+ #define   SVC_I3C_MSTATUS_STATE_IDLE(x) (SVC_I3C_MSTATUS_STATE(x) == 0)
++#define   SVC_I3C_MSTATUS_STATE_SLVREQ(x) (SVC_I3C_MSTATUS_STATE(x) == 1)
++#define   SVC_I3C_MSTATUS_STATE_IBIACK(x) (SVC_I3C_MSTATUS_STATE(x) == 6)
+ #define   SVC_I3C_MSTATUS_BETWEEN(x) FIELD_GET(BIT(4), (x))
+ #define   SVC_I3C_MSTATUS_NACKED(x) FIELD_GET(BIT(5), (x))
+ #define   SVC_I3C_MSTATUS_IBITYPE(x) FIELD_GET(GENMASK(7, 6), (x))
+@@ -90,7 +144,13 @@
  #define SVC_I3C_MINTCLR      0x094
  #define SVC_I3C_MINTMASKED   0x098
  #define SVC_I3C_MERRWARN     0x09C
 +#define   SVC_I3C_MERRWARN_NACK(x) FIELD_GET(BIT(2), (x))
++#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
 +#define   SVC_I3C_MERRWARN_HCRC(x) FIELD_GET(BIT(10), (x))
  #define SVC_I3C_MDMACTRL     0x0A0
 +#define   SVC_I3C_MDMACTRL_DMAFB(x) FIELD_PREP(GENMASK(1, 0), (x))
@@ -2454,7 +2709,7 @@
  #define SVC_I3C_MDATACTRL    0x0AC
  #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
  #define   SVC_I3C_MDATACTRL_FLUSHRB BIT(1)
-@@ -98,6 +153,7 @@
+@@ -98,6 +158,7 @@
  #define   SVC_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL GENMASK(5, 4)
  #define   SVC_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY 0
  #define   SVC_I3C_MDATACTRL_RXCOUNT(x) FIELD_GET(GENMASK(28, 24), (x))
@@ -2462,7 +2717,7 @@
  #define   SVC_I3C_MDATACTRL_TXFULL BIT(30)
  #define   SVC_I3C_MDATACTRL_RXEMPTY BIT(31)
  
-@@ -118,10 +174,40 @@
+@@ -118,10 +179,44 @@
  #define   SVC_MDYNADDR_VALID BIT(0)
  #define   SVC_MDYNADDR_ADDR(x) FIELD_PREP(GENMASK(7, 1), (x))
  
@@ -2478,6 +2733,10 @@
  #define SVC_I3C_FIFO_SIZE 16
 +#define SVC_I3C_MAX_IBI_PAYLOAD_SIZE 8
 +#define SVC_I3C_MAX_RDTERM 255
++#define SVC_I3C_MAX_PPBAUD 15
++#define SVC_I3C_MAX_PPLOW 15
++#define SVC_I3C_MAX_ODBAUD 255
++#define SVC_I3C_MAX_I2CBAUD 15
 +#define I3C_SCL_PP_PERIOD_NS_MIN 40
 +#define I3C_SCL_OD_LOW_PERIOD_NS_MIN 200
 +
@@ -2503,7 +2762,7 @@
  
  struct svc_i3c_cmd {
  	u8 addr;
-@@ -131,6 +217,7 @@ struct svc_i3c_cmd {
+@@ -131,6 +226,7 @@ struct svc_i3c_cmd {
  	unsigned int len;
  	unsigned int read_len;
  	bool continued;
@@ -2511,20 +2770,55 @@
  };
  
  struct svc_i3c_xfer {
-@@ -142,6 +229,12 @@ struct svc_i3c_xfer {
+@@ -142,16 +238,28 @@ struct svc_i3c_xfer {
  	struct svc_i3c_cmd cmds[];
  };
  
++struct svc_i3c_regs_save {
++	u32 mconfig;
++	u32 mdynaddr;
++};
++
 +struct npcm_dma_xfer_desc {
 +	const u8 *out;
 +	u8 *in;
 +	u32 len;
 +	bool rnw;
++	bool end;
 +};
  /**
   * struct svc_i3c_master - Silvaco I3C Master structure
   * @base: I3C master controller
-@@ -180,11 +273,17 @@ struct svc_i3c_master {
+  * @dev: Corresponding device
+  * @regs: Memory mapping
++ * @saved_regs: Volatile values for PM operations
+  * @free_slots: Bit array of available slots
+  * @addrs: Array containing the dynamic addresses of each attached device
+  * @descs: Array of descriptors, one per attached device
+  * @hj_work: Hot-join work
+- * @ibi_work: IBI work
+  * @irq: Main interrupt
+  * @pclk: System clock
+  * @fclk: Fast clock (bus)
+@@ -165,26 +273,31 @@ struct svc_i3c_xfer {
+  * @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
++ * @lock: Transfer lock, prevent concurrent daa/priv_xfer/ccc
++ * @req_lock: protect between IBI isr and bus operation request
+  */
+ struct svc_i3c_master {
+ 	struct i3c_master_controller base;
+ 	struct device *dev;
+ 	void __iomem *regs;
++	struct svc_i3c_regs_save saved_regs;
+ 	u32 free_slots;
+ 	u8 addrs[SVC_I3C_MAX_DEVS];
+ 	struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS];
+ 	struct work_struct hj_work;
+-	struct work_struct ibi_work;
+ 	int irq;
  	struct clk *pclk;
  	struct clk *fclk;
  	struct clk *sclk;
@@ -2537,15 +2831,16 @@
  	struct {
  		struct list_head list;
  		struct svc_i3c_xfer *cur;
- 		/* Prevent races between transfers */
+-		/* Prevent races between transfers */
 -		spinlock_t lock;
-+		struct mutex lock;
  	} xferqueue;
  	struct {
  		unsigned int num_slots;
-@@ -194,6 +293,29 @@ struct svc_i3c_master {
+@@ -193,7 +306,38 @@ struct svc_i3c_master {
+ 		/* Prevent races within IBI handlers */
  		spinlock_t lock;
  	} ibi;
++	spinlock_t req_lock;
  	struct mutex lock;
 +	struct dentry *debugfs;
 +
@@ -2565,31 +2860,53 @@
 +	dma_addr_t dma_tx_addr;
 +	dma_addr_t dma_rx_addr;
 +	struct npcm_dma_xfer_desc dma_xfer;
-+	u32 mint_save;
 +
 +	bool en_hj;
 +	bool hdr_ddr;
 +	bool hdr_mode;
++	bool dma_started;
++	bool probe_done;
++
++	/* Statistic report */
++	u8 err_code;
++	u64 err_cnt;
++	u64 rd_ibiwon_cnt;
++	u64 wr_ibiwon_cnt;
  };
  
  /**
-@@ -226,6 +348,15 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
- 	return false;
- }
+@@ -208,6 +352,15 @@ struct svc_i3c_i2c_dev_data {
+ 	struct i3c_generic_ibi_pool *ibi_pool;
+ };
  
-+static void svc_i3c_master_set_sda_skew(struct svc_i3c_master *master, int skew)
-+{
-+	u32 val;
++static int svc_i3c_master_wait_for_complete(struct svc_i3c_master *master);
++static void svc_i3c_master_stop_dma(struct svc_i3c_master *master);
 +
-+	val = readl(master->regs + SVC_I3C_MCONFIG) & ~SVC_I3C_MCONFIG_SKEW_MASK;
-+	val |= SVC_I3C_MCONFIG_SKEW(skew);
-+	writel(val, master->regs + SVC_I3C_MCONFIG);
++static void svc_i3c_master_err_stats(struct svc_i3c_master *master, u8 code)
++{
++	master->err_cnt++;
++	master->err_code = code;
 +}
 +
- static void svc_i3c_master_enable_interrupts(struct svc_i3c_master *master, u32 mask)
+ static bool svc_i3c_master_error(struct svc_i3c_master *master)
  {
- 	writel(mask, master->regs + SVC_I3C_MINTSET);
-@@ -238,6 +369,40 @@ static void svc_i3c_master_disable_interrupts(struct svc_i3c_master *master)
+ 	u32 mstatus, merrwarn;
+@@ -216,6 +369,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);
+@@ -238,6 +399,45 @@ static void svc_i3c_master_disable_interrupts(struct svc_i3c_master *master)
  	writel(mask, master->regs + SVC_I3C_MINTCLR);
  }
  
@@ -2607,6 +2924,11 @@
 +	       master->regs + SVC_I3C_MDATACTRL);
 +}
 +
++static void svc_i3c_master_flush_rx_fifo(struct svc_i3c_master *master)
++{
++	writel(SVC_I3C_MDATACTRL_FLUSHRB, master->regs + SVC_I3C_MDATACTRL);
++}
++
 +static void svc_i3c_master_reset_fifo_trigger(struct svc_i3c_master *master)
 +{
 +	u32 reg;
@@ -2630,102 +2952,232 @@
  static inline struct svc_i3c_master *
  to_svc_i3c_master(struct i3c_master_controller *master)
  {
-@@ -247,9 +412,19 @@ to_svc_i3c_master(struct i3c_master_controller *master)
- static void svc_i3c_master_hj_work(struct work_struct *work)
- {
+@@ -249,6 +449,7 @@ static void svc_i3c_master_hj_work(struct work_struct *work)
  	struct svc_i3c_master *master;
-+	u32 mint;
  
  	master = container_of(work, struct svc_i3c_master, hj_work);
 +
-+	/* disable IBI/HJ interrupt during DAA */
-+	mint = readl(master->regs + SVC_I3C_MINTSET);
-+	if (mint & SVC_I3C_MINT_SLVSTART)
-+		writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MINTCLR);
-+
  	i3c_master_do_daa(&master->base);
-+
-+	if (mint & SVC_I3C_MINT_SLVSTART)
-+		writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MINTSET);
  }
  
- static struct i3c_dev_desc *
-@@ -270,7 +445,19 @@ svc_i3c_master_dev_from_addr(struct svc_i3c_master *master,
+@@ -268,9 +469,64 @@ svc_i3c_master_dev_from_addr(struct svc_i3c_master *master,
+ 	return master->descs[i];
+ }
  
++static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
++				   bool mandatory_byte)
++{
++	unsigned int ibi_ack_nack;
++	u32 reg;
++
++	ibi_ack_nack = SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK;
++	if (mandatory_byte)
++		ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE |
++			SVC_I3C_MCTRL_RDTERM(SVC_I3C_MAX_IBI_PAYLOAD_SIZE);
++	else
++		ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE;
++
++	writel(ibi_ack_nack, master->regs + SVC_I3C_MCTRL);
++	readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
++			   SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
++}
++
++static void svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
++{
++	u32 reg;
++
++	writel(SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK |
++	       SVC_I3C_MCTRL_IBIRESP_NACK,
++	       master->regs + SVC_I3C_MCTRL);
++	readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
++			   SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
++}
++
  static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
  {
 -	writel(SVC_I3C_MCTRL_REQUEST_STOP, master->regs + SVC_I3C_MCTRL);
-+	u32 mint;
++	u32 reg = readl(master->regs + SVC_I3C_MSTATUS);
 +
-+	/* Temporarily disable slvstart interrupt to prevent spurious event */
-+	mint = readl(master->regs + SVC_I3C_MINTSET);
-+	if (mint & SVC_I3C_MINT_SLVSTART)
-+		writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MINTCLR);
++	/* Do not emit stop in the IDLE or SLVREQ state */
++	if (SVC_I3C_MSTATUS_STATE_IDLE(reg) || SVC_I3C_MSTATUS_STATE_SLVREQ(reg))
++		return;
++
++	/*
++	 * The spurious IBI event may change controller state to IBIACK, switch state
++	 * to NORMACT before emitSTOP request.
++	 */
++	if (SVC_I3C_MSTATUS_STATE_IBIACK(reg)) {
++		svc_i3c_master_nack_ibi(master);
++		writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
++	}
 +
 +	if (master->hdr_mode) {
 +		writel(SVC_I3C_MCTRL_REQUEST_FORCE_EXIT, master->regs + SVC_I3C_MCTRL);
 +		master->hdr_mode = false;
 +	} else {
 +		writel(SVC_I3C_MCTRL_REQUEST_STOP, master->regs + SVC_I3C_MCTRL);
++		/*
++		 * Wait for STOP condition to complete, in case the subsequent
++		 * EmitStartAddr request is issued too early.
++		 */
++		readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
++				   SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
 +	}
  
  	/*
  	 * This delay is necessary after the emission of a stop, otherwise eg.
-@@ -279,12 +466,14 @@ static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
- 	 * correctly if a start condition follows too rapidly.
- 	 */
+@@ -281,126 +537,134 @@ static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
  	udelay(1);
--}
+ }
  
 -static void svc_i3c_master_clear_merrwarn(struct svc_i3c_master *master)
 -{
 -	writel(readl(master->regs + SVC_I3C_MERRWARN),
 -	       master->regs + SVC_I3C_MERRWARN);
-+	/*
-+	 * Workaround: clear the spurious SlaveStart event under
-+	 * bad signals condition.
-+	 */
-+	writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS);
-+	if (mint & SVC_I3C_MINT_SLVSTART)
-+		writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MINTSET);
- }
- 
+-}
+-
  static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
-@@ -311,13 +500,22 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
- 		return ret;
+ 				     struct i3c_dev_desc *dev)
+ {
+ 	struct svc_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
+ 	struct i3c_ibi_slot *slot;
+ 	unsigned int count;
+-	u32 mdatactrl;
+-	int ret, val;
++	u32 mdatactrl, val;
++	int ret;
+ 	u8 *buf;
+ 
++	if (!data) {
++		dev_err_ratelimited(master->dev, "No data for addr 0x%x\n",
++			dev->info.dyn_addr);
++		goto no_ibi_pool;
++	}
++	if (!data->ibi_pool) {
++		dev_err_ratelimited(master->dev, "No ibi pool for addr 0x%x\n",
++			master->addrs[data->index]);
++		goto no_ibi_pool;
++	}
+ 	slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
+-	if (!slot)
+-		return -ENOSPC;
++	if (!slot) {
++		dev_err_ratelimited(master->dev, "No free ibi slot\n");
++		goto no_ibi_pool;
++	}
+ 
+ 	slot->len = 0;
+ 	buf = slot->data;
+ 
++	/*
++	 * Sometimes I3C HW returns to IDLE state after IBIRCV completed,
++	 * continue when state becomes IDLE.
++	 */
+ 	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
+-						SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
++						SVC_I3C_MSTATUS_COMPLETE(val) |
++						SVC_I3C_MSTATUS_STATE_IDLE(val),
++						0, 1000);
+ 	if (ret) {
+ 		dev_err(master->dev, "Timeout when polling for COMPLETE\n");
+-		return ret;
++		/* The event is wrong, do not deliver it to upper layer. */
++		if (SVC_I3C_MSTATUS_RXPEND(val))
++			svc_i3c_master_flush_rx_fifo(master);
++		i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
++		slot = NULL;
++		svc_i3c_master_err_stats(master, ETIMEDOUT);
++		goto handle_done;
  	}
  
--	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
+ 	while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
 -	       slot->len < SVC_I3C_FIFO_SIZE) {
-+	while (slot->len < SVC_I3C_MAX_IBI_PAYLOAD_SIZE) {
-+		if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
-+			readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
-+						   SVC_I3C_MSTATUS_RXPEND(val), 0, 1000);
-+		val = readl(master->regs + SVC_I3C_MSTATUS);
-+		if (!SVC_I3C_MSTATUS_RXPEND(val))
-+			break;
-+
++	       slot->len < SVC_I3C_MAX_IBI_PAYLOAD_SIZE) {
  		mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
  		count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl);
 -		readsl(master->regs + SVC_I3C_MRDATAB, buf, count);
 +		readsb(master->regs + SVC_I3C_MRDATAB, buf, count);
  		slot->len += count;
  		buf += count;
-+
-+		if (SVC_I3C_MSTATUS_COMPLETE(val))
-+			break;
  	}
  
++handle_done:
  	master->ibi.tbq_slot = slot;
-@@ -380,6 +578,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
- 	if (ret) {
- 		dev_err(master->dev, "Timeout when polling for IBIWON\n");
- 		svc_i3c_master_emit_stop(master);
-+		svc_i3c_master_clear_merrwarn(master);
- 		goto reenable_ibis;
- 	}
  
-@@ -387,6 +586,9 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
+-	return 0;
+-}
+-
+-static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
+-				   bool mandatory_byte)
+-{
+-	unsigned int ibi_ack_nack;
+-
+-	ibi_ack_nack = SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK;
+-	if (mandatory_byte)
+-		ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITH_BYTE;
+-	else
+-		ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE;
+-
+-	writel(ibi_ack_nack, master->regs + SVC_I3C_MCTRL);
+-}
++	return ret;
+ 
+-static void svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
+-{
+-	writel(SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK |
+-	       SVC_I3C_MCTRL_IBIRESP_NACK,
+-	       master->regs + SVC_I3C_MCTRL);
++no_ibi_pool:
++	/* No ibi pool, drop the payload if received  */
++	readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
++				   SVC_I3C_MSTATUS_COMPLETE(val) |
++				   SVC_I3C_MSTATUS_STATE_IDLE(val),
++				   0, 1000);
++	svc_i3c_master_flush_rx_fifo(master);
++	return -ENOSPC;
+ }
+ 
+-static void svc_i3c_master_ibi_work(struct work_struct *work)
++static int svc_i3c_master_handle_ibiwon(struct svc_i3c_master *master, bool autoibi)
+ {
+-	struct svc_i3c_master *master = container_of(work, struct svc_i3c_master, ibi_work);
+ 	struct svc_i3c_i2c_dev_data *data;
+ 	unsigned int ibitype, ibiaddr;
+ 	struct i3c_dev_desc *dev;
+-	u32 status, val;
+-	int ret;
+-
+-	mutex_lock(&master->lock);
+-	/*
+-	 * IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing
+-	 * readl_relaxed_poll_timeout() to return immediately. Consequently,
+-	 * ibitype will be 0 since it was last updated only after the 8th SCL
+-	 * cycle, leading to missed client IBI handlers.
+-	 *
+-	 * A typical scenario is when IBIWON occurs and bus arbitration is lost
+-	 * at svc_i3c_master_priv_xfers().
+-	 *
+-	 * Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
+-	 */
+-	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+-
+-	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
+-	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
+-	       SVC_I3C_MCTRL_IBIRESP_AUTO,
+-	       master->regs + SVC_I3C_MCTRL);
+-
+-	/* Wait for IBIWON, should take approximately 100us */
+-	ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
+-					 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;
+-	}
++	u32 status;
++	int ret = 0;
+ 
+ 	status = readl(master->regs + SVC_I3C_MSTATUS);
  	ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
  	ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);
  
@@ -2735,68 +3187,234 @@
  	/* Handle the critical responses to IBI's */
  	switch (ibitype) {
  	case SVC_I3C_MSTATUS_IBITYPE_IBI:
-@@ -434,6 +636,10 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
- 		svc_i3c_master_emit_stop(master);
+ 		dev = svc_i3c_master_dev_from_addr(master, ibiaddr);
+-		if (!dev)
+-			svc_i3c_master_nack_ibi(master);
+-		else
+-			svc_i3c_master_handle_ibi(master, dev);
++		/* Bypass the invalid ibi with address 0 */
++		if (!dev || ibiaddr == 0) {
++			if (!autoibi) {
++				svc_i3c_master_nack_ibi(master);
++				break;
++			}
++			/*
++			 * Wait for complete to make sure the subsequent emitSTOP
++			 * request will be performed in the correct state(NORMACT).
++			 */
++			readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, status,
++						   SVC_I3C_MSTATUS_COMPLETE(status),
++						   0, 1000);
++			/* Flush the garbage data */
++			if (SVC_I3C_MSTATUS_RXPEND(status))
++				svc_i3c_master_flush_rx_fifo(master);
++			break;
++		}
++		if (!autoibi) {
++			if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
++				svc_i3c_master_ack_ibi(master, true);
++			else
++				svc_i3c_master_ack_ibi(master, false);
++		}
++		svc_i3c_master_handle_ibi(master, dev);
  		break;
  	case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
-+		readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
-+					   SVC_I3C_MSTATUS_MCTRLDONE(val), 0, 1000);
+ 		svc_i3c_master_ack_ibi(master, false);
+ 		break;
+ 	case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
+ 		svc_i3c_master_nack_ibi(master);
++		status = readl(master->regs + SVC_I3C_MSTATUS);
++		/* Invalid event may be reported as MR request
++		 * and sometimes produce dummy bytes. Flush the garbage data.
++		 */
++		if (SVC_I3C_MSTATUS_RXPEND(status))
++			svc_i3c_master_flush_rx_fifo(master);
+ 		break;
+ 	default:
+ 		break;
+@@ -419,48 +683,155 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
+ 			master->ibi.tbq_slot = NULL;
+ 		}
+ 
+-		svc_i3c_master_emit_stop(master);
+-
+-		goto reenable_ibis;
++		dev_err(master->dev, "svc_i3c_master_error in ibiwon\n");
++		/*
++		 * No need to emit stop here because the caller should do it
++		 * if return error
++		 */
++		ret = -EIO;
++		svc_i3c_master_err_stats(master, EIO);
++		goto clear_ibiwon;
+ 	}
+ 
+ 	/* Handle the non critical tasks */
+ 	switch (ibitype) {
+ 	case SVC_I3C_MSTATUS_IBITYPE_IBI:
+-		if (dev) {
++		/*
++		 * Sometimes I3C HW returns to IDLE state after IBIRCV completed,
++		 * do not emit STOP in the idle state.
++		 */
++		svc_i3c_master_emit_stop(master);
++		if (dev && master->ibi.tbq_slot) {
+ 			i3c_master_queue_ibi(dev, master->ibi.tbq_slot);
+ 			master->ibi.tbq_slot = NULL;
+ 		}
+-		svc_i3c_master_emit_stop(master);
+ 		break;
+ 	case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
 +		/* Emit stop to avoid the INVREQ error after DAA process */
 +		svc_i3c_master_emit_stop(master);
  		queue_work(master->base.wq, &master->hj_work);
  		break;
  	case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
-@@ -442,6 +648,10 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
++		ret = -EOPNOTSUPP;
++		svc_i3c_master_err_stats(master, EOPNOTSUPP);
+ 	default:
+ 		break;
  	}
  
- reenable_ibis:
+-reenable_ibis:
+-	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+-	mutex_unlock(&master->lock);
++clear_ibiwon:
 +	/* clear IBIWON status */
 +	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
-+	/* Clear AUTOIBI in case it is not started yet */
-+	writel(0, master->regs + SVC_I3C_MCTRL);
- 	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
- 	mutex_unlock(&master->lock);
++	return ret;
++}
++
++static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
++{
++	u32 val, mstatus;
++	int ret;
++
++	spin_lock(&master->req_lock);
++
++	/* Check slave ibi handled not yet */
++	mstatus = readl(master->regs + SVC_I3C_MSTATUS);
++	if (!SVC_I3C_MSTATUS_STATE_SLVREQ(mstatus))
++		goto ibi_out;
++
++	/*
++	 * IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing
++	 * readl_relaxed_poll_timeout() to return immediately. Consequently,
++	 * ibitype will be 0 since it was last updated only after the 8th SCL
++	 * cycle, leading to missed client IBI handlers.
++	 *
++	 * A typical scenario is when IBIWON occurs and bus arbitration is lost
++	 * at svc_i3c_master_priv_xfers().
++	 *
++	 * Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
++	 */
++	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
++
++	/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
++	writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
++	       SVC_I3C_MCTRL_IBIRESP_AUTO |
++	       SVC_I3C_MCTRL_RDTERM(SVC_I3C_MAX_IBI_PAYLOAD_SIZE),
++	       master->regs + SVC_I3C_MCTRL);
++
++	/* Wait for IBIWON, should take approximately 100us */
++	ret = readl_relaxed_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, val,
++					 SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
++	if (ret) {
++		/* Cancle AUTOIBI if not started */
++		val = readl(master->regs + SVC_I3C_MCTRL);
++		if (SVC_I3C_MCTRL_REQUEST(val) == SVC_I3C_MCTRL_REQUEST_AUTO_IBI)
++			writel(0, master->regs + SVC_I3C_MCTRL);
++
++		dev_err(master->dev, "Timeout when polling for IBIWON\n");
++		svc_i3c_master_clear_merrwarn(master);
++		svc_i3c_master_emit_stop(master);
++		svc_i3c_master_err_stats(master, ETIMEDOUT);
++		goto ibi_out;
++	}
++
++	if (svc_i3c_master_handle_ibiwon(master, true))
++		svc_i3c_master_emit_stop(master);
++ibi_out:
++	spin_unlock(&master->req_lock);
  }
-@@ -451,16 +661,23 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
+ 
+ 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_MSTATUS);
+-	u32 active = readl(master->regs + SVC_I3C_MSTATUS);
++	u32 active = readl(master->regs + SVC_I3C_MINTMASKED), mstatus;
++
++	if (SVC_I3C_MSTATUS_COMPLETE(active)) {
++		/* Clear COMPLETE status before emit STOP */
++		writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
++		/* Disable COMPLETE interrupt */
++		writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MINTCLR);
++
++		if (master->dma_xfer.end) {
++			/* Stop DMA to prevent receiving the data of other transaction */
++			svc_i3c_master_stop_dma(master);
++			svc_i3c_master_emit_stop(master);
++		}
  
 -	if (!SVC_I3C_MSTATUS_SLVSTART(active))
 -		return IRQ_NONE;
-+	if (SVC_I3C_MSTATUS_COMPLETE(active)) {
-+		/* Disable COMPLETE interrupt */
-+		writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MINTCLR);
++		complete(&master->xfer_comp);
  
 -	/* Clear the interrupt status */
 -	writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS);
-+		complete(&master->xfer_comp);
++		return IRQ_HANDLED;
 +	}
  
 -	svc_i3c_master_disable_interrupts(master);
 +	if (SVC_I3C_MSTATUS_SLVSTART(active)) {
 +		/* Clear the interrupt status */
 +		writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS);
++
++		/* Read I3C state */
++		mstatus = readl(master->regs + SVC_I3C_MSTATUS);
++
++		if (SVC_I3C_MSTATUS_STATE_SLVREQ(mstatus)) {
++			svc_i3c_master_ibi_isr(master);
++		} else {
++			/*
++			 * Workaround:
++			 * SlaveStart event under bad signals condition. SLVSTART bit in
++			 * MSTATUS may set even slave device doesn't holding I3C_SDA low,
++			 * but actual SlaveStart event may happened concurently in this
++			 * bad signals condition handler. Give a chance to check current
++			 * work state and intmask to avoid actual SlaveStart cannot be
++			 * trigger after we clear SlaveStart interrupt status.
++			 */
++
++			/* Check if state change after we clear interrupt status */
++			active = readl(master->regs + SVC_I3C_MINTMASKED);
++			mstatus = readl(master->regs + SVC_I3C_MSTATUS);
  
 -	/* Handle the interrupt in a non atomic context */
 -	queue_work(master->base.wq, &master->ibi_work);
-+		/* Disable SLVSTART interrupt */
-+		writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MINTCLR);
-+
-+		/* Handle the interrupt in a non atomic context */
-+		queue_work(master->base.wq, &master->ibi_work);
++			if (SVC_I3C_MSTATUS_STATE_SLVREQ(mstatus)) {
++				if (!SVC_I3C_MSTATUS_SLVSTART(active)) {
++					svc_i3c_master_ibi_isr(master);
++				} else {
++					/* handle interrupt in next time */
++				}
++			}
++		}
 +	}
  
  	return IRQ_HANDLED;
  }
-@@ -471,61 +688,81 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
+@@ -471,96 +842,161 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
  	struct i3c_bus *bus = i3c_master_get_bus(m);
  	struct i3c_device_info info = {};
  	unsigned long fclk_rate, fclk_period_ns;
 -	unsigned int high_period_ns, od_low_period_ns;
 -	u32 ppbaud, pplow, odhpp, odbaud, i2cbaud, reg;
++	unsigned long i3c_scl_rate, i2c_scl_rate;
 +	unsigned int pp_high_period_ns, od_low_period_ns, i2c_period_ns;
 +	unsigned int scl_period_ns;
-+	u32 ppbaud, pplow, odhpp, odbaud, odstop = 0, i2cbaud, reg, div;
++	int ppbaud, pplow, odhpp, odbaud, odstop = 0, i2cbaud, skew;
++	u32 reg;
  	int ret;
  
 +	ret = pm_runtime_resume_and_get(master->dev);
@@ -2828,8 +3446,12 @@
 +	if (master->scl_timing.i3c_pp_hi >= I3C_SCL_PP_PERIOD_NS_MIN &&
 +	    master->scl_timing.i3c_pp_lo >= master->scl_timing.i3c_pp_hi) {
 +		ppbaud = DIV_ROUND_UP(master->scl_timing.i3c_pp_hi, fclk_period_ns) - 1;
++		if (ppbaud > SVC_I3C_MAX_PPBAUD)
++			ppbaud = SVC_I3C_MAX_PPBAUD;
 +		pplow = DIV_ROUND_UP(master->scl_timing.i3c_pp_lo, fclk_period_ns)
 +			- (ppbaud + 1);
++		if (pplow > SVC_I3C_MAX_PPLOW)
++			pplow = SVC_I3C_MAX_PPLOW;
 +		bus->scl_rate.i3c = 1000000000 / (((ppbaud + 1) * 2 + pplow) * fclk_period_ns);
 +	} else {
 +		scl_period_ns = DIV_ROUND_UP(1000000000, bus->scl_rate.i3c);
@@ -2842,6 +3464,8 @@
 +			ppbaud = DIV_ROUND_UP((scl_period_ns / 2), fclk_period_ns) - 1;
 +			pplow = 0;
 +		}
++		if (ppbaud > SVC_I3C_MAX_PPBAUD)
++			ppbaud = SVC_I3C_MAX_PPBAUD;
 +	}
 +	pp_high_period_ns = (ppbaud + 1) * fclk_period_ns;
  
@@ -2887,18 +3511,27 @@
 +	} else {
 +		/* Set default OD timing: 1MHz/1000ns with 50% duty cycle */
 +		odhpp = 0;
-+		pp_high_period_ns = (ppbaud + 1) * fclk_period_ns;
 +		odbaud = DIV_ROUND_UP(500, pp_high_period_ns) - 1;
  	}
++	if (odbaud > SVC_I3C_MAX_ODBAUD)
++		odbaud = SVC_I3C_MAX_ODBAUD;
 +	od_low_period_ns = (odbaud + 1) * pp_high_period_ns;
 +
 +	/* Configure for I2C mode */
 +	i2c_period_ns = DIV_ROUND_UP(1000000000, bus->scl_rate.i2c);
-+	div = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns);
-+	i2cbaud = (div / 2) + (div % 2);
++	if (i2c_period_ns < od_low_period_ns * 2)
++		i2c_period_ns = od_low_period_ns * 2;
++	i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
++	if (i2cbaud > SVC_I3C_MAX_I2CBAUD)
++		i2cbaud = SVC_I3C_MAX_I2CBAUD;
++
++	i3c_scl_rate = 1000000000 / (((ppbaud + 1) * 2 + pplow) * fclk_period_ns);
++	i2c_scl_rate = 1000000000 / ((i2cbaud + 2) * od_low_period_ns);
 +
 +	if (bus->mode != I3C_BUS_MODE_PURE)
 +		odstop = 1;
++
++	skew = min(7, ((ppbaud + 1) / 2));
  
  	reg = SVC_I3C_MCONFIG_MASTER_EN |
  	      SVC_I3C_MCONFIG_DISTO(0) |
@@ -2909,16 +3542,25 @@
  	      SVC_I3C_MCONFIG_PPBAUD(ppbaud) |
  	      SVC_I3C_MCONFIG_PPLOW(pplow) |
  	      SVC_I3C_MCONFIG_ODBAUD(odbaud) |
-@@ -534,33 +771,54 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
+ 	      SVC_I3C_MCONFIG_ODHPP(odhpp) |
+-	      SVC_I3C_MCONFIG_SKEW(0) |
++	      SVC_I3C_MCONFIG_SKEW(skew) |
  	      SVC_I3C_MCONFIG_I2CBAUD(i2cbaud);
  	writel(reg, master->regs + SVC_I3C_MCONFIG);
  
++	dev_dbg(master->dev, "dts: i3c rate=%lu, i2c rate=%lu\n",
++		bus->scl_rate.i3c, bus->scl_rate.i2c);
 +	dev_info(master->dev, "fclk=%lu, period_ns=%lu\n", fclk_rate, fclk_period_ns);
-+	dev_info(master->dev, "i3c scl_rate=%lu\n", bus->scl_rate.i3c);
++	dev_info(master->dev, "i3c scl_rate=%lu\n", i3c_scl_rate);
++	dev_info(master->dev, "i2c scl_rate=%lu\n", i2c_scl_rate);
 +	dev_info(master->dev, "pp_high=%u, pp_low=%lu\n", pp_high_period_ns,
 +			(ppbaud + 1 + pplow) * fclk_period_ns);
 +	dev_info(master->dev, "od_high=%d, od_low=%d\n", odhpp ? pp_high_period_ns : od_low_period_ns,
 +		 od_low_period_ns);
++	dev_dbg(master->dev, "i2c_high=%u, i2c_low=%u\n", ((i2cbaud >> 1) + 1) * od_low_period_ns,
++			((i2cbaud >> 1) + 1 + (i2cbaud % 2)) * od_low_period_ns);
++	dev_dbg(master->dev, "ppbaud=%d, pplow=%d, odbaud=%d, i2cbaud=%d, skew=%d\n",
++		ppbaud, pplow, odbaud, i2cbaud, skew);
 +	dev_info(master->dev, "mconfig=0x%x\n", readl(master->regs + SVC_I3C_MCONFIG));
  	/* Master core's registration */
  	ret = i3c_master_get_free_addr(m, 0);
@@ -2968,7 +3610,7 @@
  }
  
  static int svc_i3c_master_reserve_slot(struct svc_i3c_master *master)
-@@ -679,8 +937,10 @@ static int svc_i3c_master_readb(struct svc_i3c_master *master, u8 *dst,
+@@ -679,8 +1115,10 @@ static int svc_i3c_master_readb(struct svc_i3c_master *master, u8 *dst,
  	u32 reg;
  
  	for (i = 0; i < len; i++) {
@@ -2981,7 +3623,27 @@
  		if (ret)
  			return ret;
  
-@@ -710,10 +970,11 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
+@@ -695,14 +1133,18 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
+ {
+ 	u64 prov_id[SVC_I3C_MAX_DEVS] = {}, nacking_prov_id = 0;
+ 	unsigned int dev_nb = 0, last_addr = 0;
++	unsigned long start = jiffies;
+ 	u32 reg;
+ 	int ret, i;
++	int dyn_addr;
++
++	svc_i3c_master_flush_fifo(master);
+ 
+ 	while (true) {
+ 		/* Enter/proceed with DAA */
+ 		writel(SVC_I3C_MCTRL_REQUEST_PROC_DAA |
+ 		       SVC_I3C_MCTRL_TYPE_I3C |
+-		       SVC_I3C_MCTRL_IBIRESP_NACK |
++		       SVC_I3C_MCTRL_IBIRESP_MANUAL |
+ 		       SVC_I3C_MCTRL_DIR(SVC_I3C_MCTRL_DIR_WRITE),
+ 		       master->regs + SVC_I3C_MCTRL);
+ 
+@@ -710,16 +1152,44 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
  		 * Either one slave will send its ID, or the assignment process
  		 * is done.
  		 */
@@ -2989,15 +3651,75 @@
 -					 SVC_I3C_MSTATUS_RXPEND(reg) |
 -					 SVC_I3C_MSTATUS_MCTRLDONE(reg),
 -					 1, 1000);
-+		ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
++		ret = readl_relaxed_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
 +						reg,
 +						SVC_I3C_MSTATUS_RXPEND(reg) |
 +						SVC_I3C_MSTATUS_MCTRLDONE(reg),
-+						1, 1000);
++						0, 1000);
  		if (ret)
  			return ret;
  
-@@ -771,11 +1032,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
++		if (time_after(jiffies, start + msecs_to_jiffies(3000))) {
++			svc_i3c_master_emit_stop(master);
++			dev_info(master->dev, "do_daa expired\n");
++			break;
++		}
++		/* runtime do_daa may ibiwon by others slave devices */
++		if (SVC_I3C_MSTATUS_IBIWON(reg)) {
++			ret = svc_i3c_master_handle_ibiwon(master, false);
++			if (ret) {
++				dev_err(master->dev, "daa: handle ibi event fail, ret=%d\n", ret);
++				return ret;
++			}
++			writel(SVC_I3C_MINT_MCTRLDONE, master->regs + SVC_I3C_MSTATUS);
++			continue;
++		}
++
++		if (dev_nb == SVC_I3C_MAX_DEVS) {
++			svc_i3c_master_emit_stop(master);
++			dev_info(master->dev, "Reach max devs\n");
++			break;
++		}
+ 		if (SVC_I3C_MSTATUS_RXPEND(reg)) {
+ 			u8 data[6];
+ 
++			/* Give the slave device a suitable dynamic address */
++			dyn_addr = i3c_master_get_free_addr(&master->base, last_addr + 1);
++			if (dyn_addr < 0)
++				return dyn_addr;
++			writel(dyn_addr, master->regs + SVC_I3C_MWDATAB);
++
+ 			/*
+ 			 * We only care about the 48-bit provisional ID yet to
+ 			 * be sure a device does not nack an address twice.
+@@ -737,8 +1207,13 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
+ 			if (ret)
+ 				return ret;
+ 		} else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
+-			if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
++			if ((SVC_I3C_MSTATUS_STATE_IDLE(reg) |
++			     SVC_I3C_MSTATUS_STATE_SLVREQ(reg)) &&
+ 			    SVC_I3C_MSTATUS_COMPLETE(reg)) {
++				/*
++				 * Sometimes the controller state is SLVREQ after
++				 * DAA request completed, treat it as normal end.
++				 */
+ 				/*
+ 				 * All devices received and acked they dynamic
+ 				 * address, this is the natural end of the DAA
+@@ -747,8 +1222,10 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
+ 				break;
+ 			} else if (SVC_I3C_MSTATUS_NACKED(reg)) {
+ 				/* No I3C devices attached */
+-				if (dev_nb == 0)
++				if (dev_nb == 0) {
++					svc_i3c_master_emit_stop(master);
+ 					break;
++				}
+ 
+ 				/*
+ 				 * A slave device nacked the address, this is
+@@ -771,24 +1248,18 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
  		}
  
  		/* Wait for the slave to be ready to receive its address */
@@ -3015,12 +3737,40 @@
  		if (ret)
  			return ret;
  
-@@ -855,35 +1117,53 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
+-		/* Give the slave device a suitable dynamic address */
+-		ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
+-		if (ret < 0)
+-			return ret;
+-
+-		addrs[dev_nb] = ret;
++		addrs[dev_nb] = dyn_addr;
+ 		dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n",
+ 			dev_nb, addrs[dev_nb]);
+-
+-		writel(addrs[dev_nb], master->regs + SVC_I3C_MWDATAB);
+ 		last_addr = addrs[dev_nb++];
+ 	}
+ 
+@@ -807,8 +1278,10 @@ static int svc_i3c_update_ibirules(struct svc_i3c_master *master)
+ 
+ 	/* Create the IBIRULES register for both cases */
+ 	i3c_bus_for_each_i3cdev(&master->base.bus, dev) {
+-		if (I3C_BCR_DEVICE_ROLE(dev->info.bcr) == I3C_BCR_I3C_MASTER)
+-			continue;
++		if (I3C_BCR_DEVICE_ROLE(dev->info.bcr) == I3C_BCR_I3C_MASTER) {
++			if (!(dev->info.bcr & I3C_BCR_IBI_REQ_CAP))
++				continue;
++		}
+ 
+ 		if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD) {
+ 			reg_mbyte |= SVC_I3C_IBIRULES_ADDR(mbyte_addr_ok,
+@@ -855,35 +1328,47 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
  {
  	struct svc_i3c_master *master = to_svc_i3c_master(m);
  	u8 addrs[SVC_I3C_MAX_DEVS];
 -	unsigned long flags;
  	unsigned int dev_nb;
++	unsigned long flags;
  	int ret, i;
  
 -	spin_lock_irqsave(&master->xferqueue.lock, flags);
@@ -3030,21 +3780,14 @@
 +		return ret;
 +	}
 +
-+	mutex_lock(&master->xferqueue.lock);
-+	local_irq_disable();
-+	/*
-+	 * Fix SCL/SDA timing issue during DAA.
-+	 * Set SKEW bit to 1 before initiating a DAA, set SKEW bit to 0
-+	 * after DAA is completed.
-+	 */
-+	svc_i3c_master_set_sda_skew(master, 1);
++	mutex_lock(&master->lock);
++	spin_lock_irqsave(&master->req_lock, flags);
  	ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
 -	spin_unlock_irqrestore(&master->xferqueue.lock, flags);
 -	if (ret)
 -		goto emit_stop;
-+	svc_i3c_master_set_sda_skew(master, 0);
-+	local_irq_enable();
-+	mutex_unlock(&master->xferqueue.lock);
++	spin_unlock_irqrestore(&master->req_lock, flags);
++	mutex_unlock(&master->lock);
 +	if (ret) {
 +		svc_i3c_master_emit_stop(master);
 +		svc_i3c_master_clear_merrwarn(master);
@@ -3082,7 +3825,7 @@
  
  	return ret;
  }
-@@ -891,27 +1171,35 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
+@@ -891,27 +1376,36 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
  static int svc_i3c_master_read(struct svc_i3c_master *master,
  			       u8 *in, unsigned int len)
  {
@@ -3092,10 +3835,11 @@
 +	u32 mdctrl, mstatus;
 +	bool completed = false;
 +	unsigned int count;
-+	unsigned long start = jiffies;
++	ktime_t timeout;
  
 -	while (offset < len) {
 -		unsigned int count;
++	timeout = ktime_add_ms(ktime_get(), 1000);
 +	while (!completed) {
 +		mstatus = readl(master->regs + SVC_I3C_MSTATUS);
 +		if (SVC_I3C_MSTATUS_COMPLETE(mstatus) != 0)
@@ -3107,8 +3851,8 @@
 -					 0, 1000);
 -		if (ret)
 -			return ret;
-+		if (time_after(jiffies, start + msecs_to_jiffies(1000))) {
-+			dev_dbg(master->dev, "I3C read timeout\n");
++		if (ktime_compare(ktime_get(), timeout) > 0) {
++			dev_err(master->dev, "I3C read timeout, count=%d/%d\n", offset, len);
 +			return -ETIMEDOUT;
 +		}
  
@@ -3129,7 +3873,7 @@
  }
  
  static int svc_i3c_master_write(struct svc_i3c_master *master,
-@@ -941,13 +1229,182 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
+@@ -941,67 +1435,340 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
  	return 0;
  }
  
@@ -3141,9 +3885,7 @@
 +
 +	/* Disable COMPLETE interrupt */
 +	writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MINTCLR);
-+
-+	/* Restore the interrupt mask */
-+	svc_i3c_master_enable_interrupts(master, master->mint_save);
++	master->dma_started = false;
 +}
 +
 +static void svc_i3c_master_write_dma_table(const u8 *src, u32 *dst, int len)
@@ -3181,11 +3923,6 @@
 +					       (u32 *)master->dma_tx_buf,
 +					       xfer->len);
 +
-+	/* Disable all other i3c interrupts */
-+	master->mint_save = readl(master->regs + SVC_I3C_MINTSET);
-+	svc_i3c_master_disable_interrupts(master);
-+	/* Use I3C Complete interrupt to notify the transaction compeltion */
-+	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_COMPLETE);
 +
 +	/*
 +	 * Setup I3C DMA control
@@ -3208,6 +3945,7 @@
 +	else
 +		val |= NPCM_GDMA_CTL_DAFIX | NPCM_GDMA_CTL_GDMAMS(1) | NPCM_GDMA_CTL_TWS(2);
 +	writel(val, master->dma_regs + NPCM_GDMA_CTL(ch));
++	master->dma_started = true;
 +
 +	return 0;
 +}
@@ -3221,8 +3959,10 @@
 +
 +	ret = wait_for_completion_timeout(&master->xfer_comp, msecs_to_jiffies(100));
 +	if (!ret) {
++		svc_i3c_master_stop_dma(master);
 +		dev_err(master->dev, "DMA transfer timeout (%s)\n", xfer->rnw ? "Read" : "write");
 +		dev_err(master->dev, "mstatus = 0x%02x\n", readl(master->regs + SVC_I3C_MSTATUS));
++		svc_i3c_master_err_stats(master, ETIMEDOUT);
 +		return -ETIMEDOUT;
 +	}
 +
@@ -3251,8 +3991,11 @@
  {
 -	u32 reg;
 -	int ret;
-+	u32 reg, rdterm = *read_len;
++	u32 reg, rdterm = *read_len, mstatus;
 +	int ret, i, count, space;
++	unsigned long flags;
++	unsigned long start;
++	u32 ibiresp;
 +
 +	if (rdterm > SVC_I3C_MAX_RDTERM)
 +		rdterm = SVC_I3C_MAX_RDTERM;
@@ -3281,7 +4024,9 @@
 +					 0, 1000);
 +		if (ret)
 +			return ret;
-+
+ 
+-	/* clean SVC_I3C_MINT_IBIWON w1c bits */
+-	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
 +		reg = readl(master->regs + SVC_I3C_MDATACTRL);
 +		space = SVC_I3C_FIFO_SIZE - SVC_I3C_MDATACTRL_TXCOUNT(reg);
 +		count = xfer_len > space ? space : xfer_len;
@@ -3294,29 +4039,42 @@
 +		}
 +		xfer_len -= count;
 +	}
++	/* Prevent fifo operation from delay by interrupt */
++	if (!use_dma)
++		local_irq_disable();
 +
++	/* Prevent DMA start while IBI isr is running */
++	spin_lock_irqsave(&master->req_lock, flags);
 +	if (use_dma) {
 +		if (xfer_len > MAX_DMA_COUNT) {
 +			dev_err(master->dev, "data is larger than buffer size (%d)\n",
 +				MAX_DMA_COUNT);
++			spin_unlock_irqrestore(&master->req_lock, flags);
 +			return -EINVAL;
 +		}
 +		master->dma_xfer.out = out;
 +		master->dma_xfer.in = in;
 +		master->dma_xfer.len = xfer_len;
 +		master->dma_xfer.rnw = rnw;
++		master->dma_xfer.end = !continued;
 +		init_completion(&master->xfer_comp);
 +		svc_i3c_master_start_dma(master);
 +	}
-+
-+	/* Prevent fifo operation from delay by interrupt */
-+	if (!use_dma)
-+		local_irq_disable();
  
- 	/* clean SVC_I3C_MINT_IBIWON w1c bits */
- 	writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
-@@ -957,7 +1414,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
- 	       SVC_I3C_MCTRL_IBIRESP_NACK |
++	start = jiffies;
++	/*
++	 * IBI payload size may be larger than rdterm, use manual IBI response
++	 * for read operation to set the proper RDTERM value in IBI ack request.
++	 */
++	if (master->probe_done)
++		ibiresp = rnw ? SVC_I3C_MCTRL_IBIRESP_MANUAL : SVC_I3C_MCTRL_IBIRESP_AUTO;
++	else
++		ibiresp = SVC_I3C_MCTRL_IBIRESP_MANUAL;
++retry_start:
+ 	writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
+ 	       xfer_type |
+-	       SVC_I3C_MCTRL_IBIRESP_NACK |
++	       ibiresp |
  	       SVC_I3C_MCTRL_DIR(rnw) |
  	       SVC_I3C_MCTRL_ADDR(addr) |
 -	       SVC_I3C_MCTRL_RDTERM(read_len),
@@ -3324,18 +4082,97 @@
  	       master->regs + SVC_I3C_MCTRL);
  
  	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
-@@ -982,24 +1439,58 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
- 		goto emit_stop;
- 	}
+ 				 SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
+ 	if (ret)
+-		goto emit_stop;
++		goto emit_stop_locked;
  
--	if (rnw)
+-	/*
+-	 * 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;
++	mstatus = readl(master->regs + SVC_I3C_MSTATUS);
++	if (SVC_I3C_MSTATUS_IBIWON(mstatus)) {
++		/*
++		 * Unable to handle slave event before driver probe done.
++		 * Ignore the event and disable slave interrupts
++		 * (send a Repeated START and DISEC CCC).
++		 */
++		if (!master->probe_done) {
++			if (use_dma)
++				svc_i3c_master_stop_dma(master);
++			/* ACK the IBI and drop the payload */
++			svc_i3c_master_ack_ibi(master, true);
++			readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
++					   SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
++			svc_i3c_master_flush_fifo(master);
++			/* Send a Repeated Start followed by a DISEC CCC */
++			writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
++			       xfer_type | SVC_I3C_MCTRL_IBIRESP_NACK |
++			       SVC_I3C_MCTRL_DIR(0) |
++			       SVC_I3C_MCTRL_ADDR(I3C_BROADCAST_ADDR),
++			       master->regs + SVC_I3C_MCTRL);
++			writel(I3C_CCC_DISEC(true), master->regs + SVC_I3C_MWDATAB);
++			writel(I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR | I3C_CCC_EVENT_HJ,
++			       master->regs + SVC_I3C_MWDATABE);
++			readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
++					 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
++			svc_i3c_master_emit_stop(master);
++			writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
++			spin_unlock_irqrestore(&master->req_lock, flags);
++			if (!use_dma)
++				local_irq_enable();
++			/* Return EAGAIN to restart the transaction */
++			return -EAGAIN;
++		}
++		/* Stop RX DMA to prevent it from receving the ibi payload */
++		if (use_dma && rnw)
++			svc_i3c_master_stop_dma(master);
++		ret = svc_i3c_master_handle_ibiwon(master, !rnw);
++		if (ret) {
++			dev_err(master->dev, "xfer(rnw %d): handle ibi event fail, ret=%d\n",
++				rnw, ret);
++			goto emit_stop_locked;
++		}
++		if (rnw)
++			master->rd_ibiwon_cnt++;
++		else
++			master->wr_ibiwon_cnt++;
++		if (time_after(jiffies, start + msecs_to_jiffies(1000))) {
++			dev_info(master->dev, "abnormal ibiwon events\n");
++			goto emit_stop_locked;
++		}
++
++		if (use_dma && rnw)
++			svc_i3c_master_start_dma(master);
++
++		/* Clear COMPLETE status of this IBI transaction */
++		writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
++		goto retry_start;
++	}
++	/* Use COMPLETE interrupt as notification of transfer completion */
++	if (use_dma)
++		svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_COMPLETE);
++	spin_unlock_irqrestore(&master->req_lock, flags);
++
 +	reg = readl(master->regs + SVC_I3C_MSTATUS);
 +	if (SVC_I3C_MSTATUS_NACKED(reg)) {
 +		dev_dbg(master->dev, "addr 0x%x NACK\n", addr);
 +		ret = -EIO;
-+		goto emit_stop;
-+	}
-+
+ 		goto emit_stop;
+ 	}
+ 
+-	if (rnw)
 +	if (use_dma)
 +		ret = svc_i3c_master_wait_for_complete(master);
 +	else if (rnw)
@@ -3346,16 +4183,24 @@
 +	if (ret < 0)
  		goto emit_stop;
  
+-	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
+-				 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
+-	if (ret)
+-		goto emit_stop;
 +	if (rnw)
 +		*read_len = ret;
-+
- 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
- 				 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
- 	if (ret)
- 		goto emit_stop;
  
 -	if (!continued)
-+	writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
++	if (!use_dma) {
++		ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
++					 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
++		if (ret)
++			goto emit_stop;
++
++		/* If use_dma, COMPLETE bit is cleared in the isr */
++		writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
++	}
++
 +
 +	if (master->hdr_mode) {
 +		reg = readl(master->regs + SVC_I3C_MERRWARN);
@@ -3366,81 +4211,96 @@
 +		}
 +	}
 +
-+	if (!continued) {
++	if (!continued && !use_dma)
  		svc_i3c_master_emit_stop(master);
  
-+		/* Wait idle if stop is sent. */
-+		readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
-+				   SVC_I3C_MSTATUS_STATE_IDLE(reg), 0, 1000);
-+	}
 +	if (!use_dma)
 +		local_irq_enable();
 +
  	return 0;
  
  emit_stop:
-+	if (use_dma)
++	spin_lock_irqsave(&master->req_lock, flags);
++emit_stop_locked:
++	if (master->dma_started)
 +		svc_i3c_master_stop_dma(master);
-+	else
-+		local_irq_enable();
++	/*
++	 * If the read transfer is not completed, update RDTERM value to
++	 * terminate the transfer and let emitting STOP work normally.
++	 */
++	if (rnw && ret == -ETIMEDOUT) {
++		writel(SVC_I3C_MCTRL_RDTERM(1), master->regs + SVC_I3C_MCTRL);
++		svc_i3c_master_flush_fifo(master);
++		readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
++				   SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
++	}
  	svc_i3c_master_emit_stop(master);
  	svc_i3c_master_clear_merrwarn(master);
++	svc_i3c_master_flush_fifo(master);
++	spin_unlock_irqrestore(&master->req_lock, flags);
++	if (!use_dma)
++		local_irq_enable();
  
-@@ -1039,11 +1530,9 @@ static void svc_i3c_master_dequeue_xfer_locked(struct svc_i3c_master *master,
+ 	return ret;
+ }
+@@ -1039,28 +1806,34 @@ static void svc_i3c_master_dequeue_xfer_locked(struct svc_i3c_master *master,
  static void svc_i3c_master_dequeue_xfer(struct svc_i3c_master *master,
  					struct svc_i3c_xfer *xfer)
  {
 -	unsigned long flags;
 -
 -	spin_lock_irqsave(&master->xferqueue.lock, flags);
-+	mutex_lock(&master->xferqueue.lock);
  	svc_i3c_master_dequeue_xfer_locked(master, xfer);
 -	spin_unlock_irqrestore(&master->xferqueue.lock, flags);
-+	mutex_unlock(&master->xferqueue.lock);
  }
  
  static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
-@@ -1054,17 +1543,29 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
+ {
+ 	struct svc_i3c_xfer *xfer = master->xferqueue.cur;
++	unsigned long flags;
++	int retry = 2;
+ 	int ret, i;
+ 
  	if (!xfer)
  		return;
  
-+	ret = pm_runtime_resume_and_get(master->dev);
-+	if (ret < 0) {
-+		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
-+		return;
-+	}
-+
++	/* Prevent fifo flush while IBI isr is running */
++	spin_lock_irqsave(&master->req_lock, flags);
 +	svc_i3c_master_clear_merrwarn(master);
 +	svc_i3c_master_flush_fifo(master);
++	spin_unlock_irqrestore(&master->req_lock, flags);
 +
  	for (i = 0; i < xfer->ncmds; i++) {
  		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
- 
+-
++again:
  		ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
  					  cmd->addr, cmd->in, cmd->out,
 -					  cmd->len, cmd->read_len,
 -					  cmd->continued);
 +					  cmd->len, &cmd->read_len,
 +					  cmd->continued, cmd->use_dma);
++		if (ret == -EAGAIN && --retry)
++			goto again;
  		if (ret)
  			break;
  	}
- 
-+	pm_runtime_mark_last_busy(master->dev);
-+	pm_runtime_put_autosuspend(master->dev);
-+
- 	xfer->ret = ret;
- 	complete(&xfer->comp);
- 
-@@ -1084,17 +1585,15 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
+@@ -1084,17 +1857,25 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
  static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master,
  					struct svc_i3c_xfer *xfer)
  {
 -	unsigned long flags;
--
++	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;
++	}
+ 
  	init_completion(&xfer->comp);
 -	spin_lock_irqsave(&master->xferqueue.lock, flags);
-+	mutex_lock(&master->xferqueue.lock);
++
  	if (master->xferqueue.cur) {
  		list_add_tail(&xfer->node, &master->xferqueue.list);
  	} else {
@@ -3448,11 +4308,13 @@
  		svc_i3c_master_start_xfer_locked(master);
  	}
 -	spin_unlock_irqrestore(&master->xferqueue.lock, flags);
-+	mutex_unlock(&master->xferqueue.lock);
++
++	pm_runtime_mark_last_busy(master->dev);
++	pm_runtime_put_autosuspend(master->dev);
  }
  
  static bool
-@@ -1192,6 +1691,9 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
+@@ -1192,6 +1973,9 @@ 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);
  
@@ -3462,7 +4324,7 @@
  	ret = xfer->ret;
  	svc_i3c_master_free_xfer(xfer);
  
-@@ -1210,8 +1712,11 @@ static int svc_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
+@@ -1210,8 +1994,11 @@ static int svc_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
  	else
  		ret = svc_i3c_master_send_direct_ccc_cmd(master, cmd);
  
@@ -3475,7 +4337,7 @@
  
  	return ret;
  }
-@@ -1230,7 +1735,10 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
+@@ -1230,7 +2017,10 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
  	if (!xfer)
  		return -ENOMEM;
  
@@ -3487,7 +4349,7 @@
  
  	for (i = 0; i < nxfers; i++) {
  		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
-@@ -1242,6 +1750,8 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
+@@ -1242,6 +2032,8 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
  		cmd->len = xfers[i].len;
  		cmd->read_len = xfers[i].rnw ? xfers[i].len : 0;
  		cmd->continued = (i + 1) < nxfers;
@@ -3496,7 +4358,7 @@
  	}
  
  	mutex_lock(&master->lock);
-@@ -1250,6 +1760,12 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
+@@ -1250,6 +2042,12 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
  		svc_i3c_master_dequeue_xfer(master, xfer);
  	mutex_unlock(&master->lock);
  
@@ -3509,7 +4371,7 @@
  	ret = xfer->ret;
  	svc_i3c_master_free_xfer(xfer);
  
-@@ -1305,9 +1821,9 @@ static int svc_i3c_master_request_ibi(struct i3c_dev_desc *dev,
+@@ -1305,9 +2103,9 @@ static int svc_i3c_master_request_ibi(struct i3c_dev_desc *dev,
  	unsigned long flags;
  	unsigned int i;
  
@@ -3521,7 +4383,7 @@
  		return -ERANGE;
  	}
  
-@@ -1352,6 +1868,18 @@ static void svc_i3c_master_free_ibi(struct i3c_dev_desc *dev)
+@@ -1352,6 +2150,18 @@ static void svc_i3c_master_free_ibi(struct i3c_dev_desc *dev)
  static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
  {
  	struct i3c_master_controller *m = i3c_dev_get_master(dev);
@@ -3540,32 +4402,32 @@
  
  	return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
  }
-@@ -1359,8 +1887,17 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
+@@ -1359,8 +2169,17 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
  static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
  {
  	struct i3c_master_controller *m = i3c_dev_get_master(dev);
 +	struct svc_i3c_master *master = to_svc_i3c_master(m);
 +	int ret;
 +
-+	svc_i3c_master_disable_interrupts(master);
++	writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MINTCLR);
 +
 +	ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
-+
-+	pm_runtime_mark_last_busy(master->dev);
-+	pm_runtime_put_autosuspend(master->dev);
  
 -	return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
++	pm_runtime_mark_last_busy(master->dev);
++	pm_runtime_put_autosuspend(master->dev);
++
 +	return ret;
  }
  
  static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
-@@ -1391,29 +1928,437 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
+@@ -1391,29 +2210,442 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
  	.disable_ibi = svc_i3c_master_disable_ibi,
  };
  
 -static void svc_i3c_master_reset(struct svc_i3c_master *master)
 +static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master)
-+{
+ {
 +	int ret = 0;
 +
 +	ret = clk_prepare_enable(master->pclk);
@@ -3606,7 +4468,7 @@
 +}
 +
 +static void svc_i3c_slave_stop_dma(struct svc_i3c_master *master)
- {
++{
 +	writel(0, master->dma_regs + NPCM_GDMA_CTL(DMA_CH_TX));
 +	writel(0, master->dma_regs + NPCM_GDMA_CTL(DMA_CH_RX));
 +	writel(0, master->regs + SVC_I3C_MDMACTRL);
@@ -3936,6 +4798,10 @@
 +		return;
 +
 +	debugfs_create_file("debug", 0444, master->debugfs, master, &debug_fops);
++	debugfs_create_u64("err_cnt", 0444, master->debugfs, &master->err_cnt);
++	debugfs_create_u8("err_code", 0444, master->debugfs, &master->err_code);
++	debugfs_create_u64("rd_ibiwon_cnt", 0444, master->debugfs, &master->rd_ibiwon_cnt);
++	debugfs_create_u64("wr_ibiwon_cnt", 0444, master->debugfs, &master->wr_ibiwon_cnt);
 +}
 +
 +static int svc_i3c_setup_dma(struct platform_device *pdev, struct svc_i3c_master *master)
@@ -4004,12 +4870,20 @@
  {
  	struct device *dev = &pdev->dev;
  	struct svc_i3c_master *master;
++	struct reset_control *reset;
 +	const char *role;
 +	u32 val;
  	int ret;
  
  	master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
-@@ -1442,62 +2387,95 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
+@@ -1437,67 +2669,108 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
+ 		return PTR_ERR(master->sclk);
+ 
+ 	master->irq = platform_get_irq(pdev, 0);
+-	if (master->irq <= 0)
+-		return -ENOENT;
++	if (master->irq < 0)
++		return master->irq;
  
  	master->dev = dev;
  
@@ -4028,10 +4902,16 @@
 -	if (ret)
 -		goto err_disable_fclk;
 -
++	reset = devm_reset_control_get(&pdev->dev, NULL);
++	if (!IS_ERR(reset)) {
++		reset_control_assert(reset);
++		udelay(5);
++		reset_control_deassert(reset);
++	}
  	INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
- 	INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
- 	mutex_init(&master->lock);
- 
+-	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);
 +	ret = of_property_read_string(pdev->dev.of_node, "initial-role", &role);
@@ -4048,9 +4928,10 @@
  	master->free_slots = GENMASK(SVC_I3C_MAX_DEVS - 1, 0);
  
 -	spin_lock_init(&master->xferqueue.lock);
-+	mutex_init(&master->xferqueue.lock);
++	mutex_init(&master->lock);
  	INIT_LIST_HEAD(&master->xferqueue.list);
  
++	spin_lock_init(&master->req_lock);
  	spin_lock_init(&master->ibi.lock);
 +	spin_lock_init(&master->slave.lock);
  	master->ibi.num_slots = SVC_I3C_MAX_DEVS;
@@ -4091,6 +4972,9 @@
 +	if (!of_property_read_u32(dev->of_node, "i3c-od-scl-lo-period-ns", &val))
 +		master->scl_timing.i3c_od_lo = val;
 +
++	svc_i3c_master_clear_merrwarn(master);
++	svc_i3c_master_flush_fifo(master);
++
 +	svc_i3c_setup_dma(pdev, master);
 +	svc_i3c_init_debugfs(pdev, master);
 +
@@ -4103,20 +4987,18 @@
 -		goto err_disable_sclk;
 +		goto rpm_disable;
  
--	return 0;
 +	pm_runtime_mark_last_busy(&pdev->dev);
 +	pm_runtime_put_autosuspend(&pdev->dev);
- 
--err_disable_sclk:
--	clk_disable_unprepare(master->sclk);
++
 +	if (master->en_hj) {
 +		dev_info(master->dev, "enable hot-join\n");
 +		svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
 +	}
-+	return 0;
++	master->probe_done = true;
+ 	return 0;
  
--err_disable_fclk:
--	clk_disable_unprepare(master->fclk);
+-err_disable_sclk:
+-	clk_disable_unprepare(master->sclk);
 +rpm_disable:
 +	pm_runtime_dont_use_autosuspend(&pdev->dev);
 +	pm_runtime_put_noidle(&pdev->dev);
@@ -4124,6 +5006,9 @@
 +	pm_runtime_disable(&pdev->dev);
 +	debugfs_remove_recursive(master->debugfs);
  
+-err_disable_fclk:
+-	clk_disable_unprepare(master->fclk);
+-
 -err_disable_pclk:
 -	clk_disable_unprepare(master->pclk);
 +err_disable_clks:
@@ -4131,11 +5016,14 @@
  
  	return ret;
  }
-@@ -1507,21 +2485,56 @@ static int svc_i3c_master_remove(struct platform_device *pdev)
+@@ -1507,21 +2780,79 @@ static int svc_i3c_master_remove(struct platform_device *pdev)
  	struct svc_i3c_master *master = platform_get_drvdata(pdev);
  	int ret;
  
 -	ret = i3c_master_unregister(&master->base);
++	/* Avoid ibi events during driver unbinding */
++	writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MINTCLR);
++
 +	debugfs_remove_recursive(master->debugfs);
 +
 +	ret = i3c_unregister(&master->base);
@@ -4147,26 +5035,44 @@
 -	clk_disable_unprepare(master->sclk);
 +	pm_runtime_dont_use_autosuspend(&pdev->dev);
 +	pm_runtime_disable(&pdev->dev);
-+
+ 
 +	if (master->use_dma) {
 +		dma_free_coherent(master->dev, MAX_DMA_COUNT * 4, master->dma_tx_buf,
 +				  master->dma_tx_addr);
 +		dma_free_coherent(master->dev, MAX_DMA_COUNT, master->dma_rx_buf,
 +				  master->dma_rx_addr);
 +	}
-+	return 0;
+ 	return 0;
+ }
+ 
++static void svc_i3c_save_regs(struct svc_i3c_master *master)
++{
++	master->saved_regs.mconfig = readl(master->regs + SVC_I3C_MCONFIG);
++	master->saved_regs.mdynaddr = readl(master->regs + SVC_I3C_MDYNADDR);
++}
++
++static void svc_i3c_restore_regs(struct svc_i3c_master *master)
++{
++	if (readl(master->regs + SVC_I3C_MDYNADDR) !=
++	    master->saved_regs.mdynaddr) {
++		writel(master->saved_regs.mconfig,
++		       master->regs + SVC_I3C_MCONFIG);
++		writel(master->saved_regs.mdynaddr,
++		       master->regs + SVC_I3C_MDYNADDR);
++	}
 +}
 +
 +static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
 +{
 +	struct svc_i3c_master *master = dev_get_drvdata(dev);
 +
++	svc_i3c_save_regs(master);
 +	svc_i3c_master_unprepare_clks(master);
 +	pinctrl_pm_select_sleep_state(dev);
- 
- 	return 0;
- }
- 
++
++	return 0;
++}
++
 +static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
 +{
 +	struct svc_i3c_master *master = dev_get_drvdata(dev);
@@ -4174,6 +5080,8 @@
 +	pinctrl_pm_select_default_state(dev);
 +	svc_i3c_master_prepare_clks(master);
 +
++	svc_i3c_restore_regs(master);
++
 +	return 0;
 +}
 +
@@ -4192,7 +5100,7 @@
  
  static struct platform_driver svc_i3c_master = {
  	.probe = svc_i3c_master_probe,
-@@ -1529,6 +2542,7 @@ static struct platform_driver svc_i3c_master = {
+@@ -1529,6 +2860,7 @@ static struct platform_driver svc_i3c_master = {
  	.driver = {
  		.name = "silvaco-i3c-master",
  		.of_match_table = svc_i3c_master_of_match_tbl,
@@ -6440,5 +7348,5 @@
  
  	return rc;
 -- 
-2.47.0.rc1.288.g06298d1525-goog
+2.34.1
 
diff --git a/recipes-kernel/linux/linux-gbmc_5.15.bb b/recipes-kernel/linux/linux-gbmc_5.15.bb
index 9764cc2..4c03b20 100644
--- a/recipes-kernel/linux/linux-gbmc_5.15.bb
+++ b/recipes-kernel/linux/linux-gbmc_5.15.bb
@@ -66,3 +66,7 @@
   file://1031-soc-nuvoton-Add-mmbi-driver-401.patch \
   file://npcm8xx_defconfig \
   "
+
+SRC_URI:append:npcm8xx:intel = " \
+  file://0001-driver-i3c-add-intel-i3c-mctp-support.patch \
+  "