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 \ + "