Merge tag 'i3c/for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 5 Nov 2023 02:25:36 +0000 (16:25 -1000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 5 Nov 2023 02:25:36 +0000 (16:25 -1000)
Pull i3c updates from Alexandre Belloni:
 "There are now more fixes because as stated in my previous pull
  request, people now have access to actual hardware.

  Core:

   - handle IBI in the proper order

  Drivers:

   - cdns: fix status register access

   - mipi-i3c-hci: many fixes now that the driver has been actually
     tested

   - svc: many IBI fixes, correct compatible string, fix hot join corner
     cases"

* tag 'i3c/for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: (29 commits)
  i3c: master: handle IBIs in order they came
  i3c: master: mipi-i3c-hci: Fix a kernel panic for accessing DAT_data.
  i3c: master: svc: fix compatibility string mismatch with binding doc
  i3c: master: svc: fix random hot join failure since timeout error
  i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen
  i3c: master: svc: fix check wrong status register in irq handler
  i3c: master: svc: fix ibi may not return mandatory data byte
  i3c: master: svc: fix wrong data return when IBI happen during start frame
  i3c: master: svc: fix race condition in ibi work thread
  i3c: Fix typo "Provisional ID" to "Provisioned ID"
  i3c: Fix potential refcount leak in i3c_master_register_new_i3c_devs
  i3c: mipi-i3c-hci: Resume controller after aborted transfer
  i3c: mipi-i3c-hci: Resume controller explicitly
  i3c: mipi-i3c-hci: Fix missing xfer->completion in hci_cmd_v1_daa()
  i3c: mipi-i3c-hci: Do not unmap region not mapped for transfer
  i3c: mipi-i3c-hci: Set number of SW enabled Ring Bundles earlier
  i3c: mipi-i3c-hci: Fix race between bus cleanup and interrupt
  i3c: mipi-i3c-hci: Set ring start request together with enable
  i3c: mipi-i3c-hci: Remove BUG() when Ring Abort request times out
  i3c: mipi-i3c-hci: Fix out of bounds access in hci_dma_irq_handler
  ...

13 files changed:
Documentation/ABI/testing/sysfs-bus-i3c
Documentation/devicetree/bindings/i3c/i3c.yaml
Documentation/driver-api/i3c/protocol.rst
drivers/i3c/master.c
drivers/i3c/master/dw-i3c-master.c
drivers/i3c/master/i3c-master-cdns.c
drivers/i3c/master/mipi-i3c-hci/cmd_v1.c
drivers/i3c/master/mipi-i3c-hci/core.c
drivers/i3c/master/mipi-i3c-hci/dat_v1.c
drivers/i3c/master/mipi-i3c-hci/dma.c
drivers/i3c/master/svc-i3c-master.c
include/linux/i3c/device.h
include/linux/i3c/master.h

index 1f4a2662335bb1a68cd401c1a6b85056994eb94b..e5248fd67a563b489d8c4a65a7b32ddd2605d952 100644 (file)
@@ -67,7 +67,7 @@ What:         /sys/bus/i3c/devices/i3c-<bus-id>/pid
 KernelVersion:  5.0
 Contact:       linux-i3c@vger.kernel.org
 Description:
-               PID stands for Provisional ID and is used to uniquely identify
+               PID stands for Provisioned ID and is used to uniquely identify
                a device on a bus. This PID contains information about the
                vendor, the part and an instance ID so that several devices of
                the same type can be connected on the same bus.
@@ -123,7 +123,7 @@ What:               /sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>/pid
 KernelVersion:  5.0
 Contact:       linux-i3c@vger.kernel.org
 Description:
-               PID stands for Provisional ID and is used to uniquely identify
+               PID stands for Provisioned ID and is used to uniquely identify
                a device on a bus. This PID contains information about the
                vendor, the part and an instance ID so that several devices of
                the same type can be connected on the same bus.
index d9483fbd24543925b0d1eeec4809bbc6de94fc7b..c816e295d5651f372c4884751c9954a953d8838a 100644 (file)
@@ -125,12 +125,12 @@ patternProperties:
                 minimum: 0
                 maximum: 0x7f
               - description: |
-                  First half of the Provisional ID (following the PID
+                  First half of the Provisioned ID (following the PID
                   definition provided by the I3C specification).
 
                   Contains the manufacturer ID left-shifted by 1.
               - description: |
-                  Second half of the Provisional ID (following the PID
+                  Second half of the Provisioned ID (following the PID
                   definition provided by the I3C specification).
 
                   Contains the ORing of the part ID left-shifted by 16,
index 02653defa011d3173001583c6eea300dfb0a85c2..23a0b93c62b1e1765d3e71fc43280c98acf75126 100644 (file)
@@ -71,8 +71,8 @@ During DAA, each I3C device reports 3 important things:
   related capabilities
 * DCR: Device Characteristic Register. This 8-bit register describes the
   functionalities provided by the device
-* Provisional ID: A 48-bit unique identifier. On a given bus there should be no
-  Provisional ID collision, otherwise the discovery mechanism may fail.
+* Provisioned ID: A 48-bit unique identifier. On a given bus there should be no
+  Provisioned ID collision, otherwise the discovery mechanism may fail.
 
 I3C slave events
 ================
index 959ec5269376fb42d78a8faecef8ffa0aa37104b..95caa162706f5543a91fb40cffaa2927b25b64c7 100644 (file)
@@ -1556,9 +1556,11 @@ 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);
+               }
        }
 }
 
@@ -2340,7 +2342,7 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master)
        adap->dev.parent = master->dev.parent;
        adap->owner = master->dev.parent->driver->owner;
        adap->algo = &i3c_master_i2c_algo;
-       strncpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
+       strscpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
 
        /* FIXME: Should we allow i3c masters to override these values? */
        adap->timeout = 1000;
@@ -2403,7 +2405,7 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
 void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
 {
        atomic_inc(&dev->ibi->pending_ibis);
-       queue_work(dev->common.master->wq, &slot->work);
+       queue_work(dev->ibi->wq, &slot->work);
 }
 EXPORT_SYMBOL_GPL(i3c_master_queue_ibi);
 
@@ -2660,6 +2662,10 @@ int i3c_master_register(struct i3c_master_controller *master,
        device_initialize(&master->dev);
        dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
 
+       master->dev.dma_mask = parent->dma_mask;
+       master->dev.coherent_dma_mask = parent->coherent_dma_mask;
+       master->dev.dma_parms = parent->dma_parms;
+
        ret = of_populate_i3c_bus(master);
        if (ret)
                goto err_put_dev;
@@ -2848,6 +2854,12 @@ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
        if (!ibi)
                return -ENOMEM;
 
+       ibi->wq = alloc_ordered_workqueue(dev_name(i3cdev_to_dev(dev->dev)), WQ_MEM_RECLAIM);
+       if (!ibi->wq) {
+               kfree(ibi);
+               return -ENOMEM;
+       }
+
        atomic_set(&ibi->pending_ibis, 0);
        init_completion(&ibi->all_ibis_handled);
        ibi->handler = req->handler;
@@ -2875,6 +2887,12 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
                WARN_ON(i3c_dev_disable_ibi_locked(dev));
 
        master->ops->free_ibi(dev);
+
+       if (dev->ibi->wq) {
+               destroy_workqueue(dev->ibi->wq);
+               dev->ibi->wq = NULL;
+       }
+
        kfree(dev->ibi);
        dev->ibi = NULL;
 }
index 9332ae5f641903e45f1c2602f6d2b89de5943d32..ef5751e91cc9eff0a9a75e663e6d408a87209070 100644 (file)
@@ -233,7 +233,7 @@ struct dw_i3c_xfer {
        struct completion comp;
        int ret;
        unsigned int ncmds;
-       struct dw_i3c_cmd cmds[];
+       struct dw_i3c_cmd cmds[] __counted_by(ncmds);
 };
 
 struct dw_i3c_i2c_dev_data {
index 49551db71bc96ba9df786f097dbfec9598dcab6d..bcbe8f914149b29d69147e1ccbefbf45339ba314 100644 (file)
 #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(x)              (((s) & GENMASK(15, 9)) >> 9)
+#define SLV_STATUS1_DA(s)              (((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)
@@ -387,7 +387,7 @@ struct cdns_i3c_xfer {
        struct completion comp;
        int ret;
        unsigned int ncmds;
-       struct cdns_i3c_cmd cmds[];
+       struct cdns_i3c_cmd cmds[] __counted_by(ncmds);
 };
 
 struct cdns_i3c_data {
@@ -1623,13 +1623,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);
index 6a781f89b0e4066beef10dcf641d242e97504600..2b2323aa671416075454b781212ca36b66360a5a 100644 (file)
@@ -332,6 +332,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
                        CMD_A0_DEV_COUNT(1) |
                        CMD_A0_ROC | CMD_A0_TOC;
                xfer->cmd_desc[1] = 0;
+               xfer->completion = &done;
                hci->io->queue_xfer(hci, xfer, 1);
                if (!wait_for_completion_timeout(&done, HZ) &&
                    hci->io->dequeue_xfer(hci, xfer, 1)) {
index 837af83c85f4ed8d14fc733897abef9a27d69eb4..1ae56a5699c6b63eee19e73031d28658e5e1d3ca 100644 (file)
@@ -161,10 +161,12 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
 static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
 {
        struct i3c_hci *hci = to_i3c_hci(m);
+       struct platform_device *pdev = to_platform_device(m->dev.parent);
 
        DBG("");
 
        reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
+       synchronize_irq(platform_get_irq(pdev, 0));
        hci->io->cleanup(hci);
        if (hci->cmd == &mipi_i3c_hci_cmd_v1)
                mipi_i3c_hci_dat_v1.cleanup(hci);
@@ -172,8 +174,7 @@ static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
 
 void mipi_i3c_hci_resume(struct i3c_hci *hci)
 {
-       /* the HC_CONTROL_RESUME bit is R/W1C so just read and write back */
-       reg_write(HC_CONTROL, reg_read(HC_CONTROL));
+       reg_set(HC_CONTROL, HC_CONTROL_RESUME);
 }
 
 /* located here rather than pio.c because needed bits are in core reg space */
@@ -610,17 +611,17 @@ static int i3c_hci_init(struct i3c_hci *hci)
        offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
        hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
        hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
-       hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval);
+       hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8;
        dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
-                hci->DAT_entries, hci->DAT_entry_size * 4, offset);
+                hci->DAT_entries, hci->DAT_entry_size, offset);
 
        regval = reg_read(DCT_SECTION);
        offset = FIELD_GET(DCT_TABLE_OFFSET, regval);
        hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
        hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
-       hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval);
+       hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16;
        dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
-                hci->DCT_entries, hci->DCT_entry_size * 4, offset);
+                hci->DCT_entries, hci->DCT_entry_size, offset);
 
        regval = reg_read(RING_HEADERS_SECTION);
        offset = FIELD_GET(RING_HEADERS_OFFSET, regval);
@@ -787,6 +788,7 @@ static struct platform_driver i3c_hci_driver = {
        },
 };
 module_platform_driver(i3c_hci_driver);
+MODULE_ALIAS("platform:mipi-i3c-hci");
 
 MODULE_AUTHOR("Nicolas Pitre <npitre@baylibre.com>");
 MODULE_DESCRIPTION("MIPI I3C HCI driver");
index 97bb49ff5b53bd8887303471a8b6446470156a81..47b9b4d4ed3fc0ff5e05ed4d4d2d5b28ae246ded 100644 (file)
@@ -64,15 +64,17 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
                return -EOPNOTSUPP;
        }
 
-       /* use a bitmap for faster free slot search */
-       hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
-       if (!hci->DAT_data)
-               return -ENOMEM;
-
-       /* clear them */
-       for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
-               dat_w0_write(dat_idx, 0);
-               dat_w1_write(dat_idx, 0);
+       if (!hci->DAT_data) {
+               /* use a bitmap for faster free slot search */
+               hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
+               if (!hci->DAT_data)
+                       return -ENOMEM;
+
+               /* clear them */
+               for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
+                       dat_w0_write(dat_idx, 0);
+                       dat_w1_write(dat_idx, 0);
+               }
        }
 
        return 0;
@@ -87,7 +89,13 @@ static void hci_dat_v1_cleanup(struct i3c_hci *hci)
 static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
 {
        unsigned int dat_idx;
+       int ret;
 
+       if (!hci->DAT_data) {
+               ret = hci_dat_v1_init(hci);
+               if (ret)
+                       return ret;
+       }
        dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
        if (dat_idx >= hci->DAT_entries)
                return -ENOENT;
@@ -103,7 +111,8 @@ static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
 {
        dat_w0_write(dat_idx, 0);
        dat_w1_write(dat_idx, 0);
-       __clear_bit(dat_idx, hci->DAT_data);
+       if (hci->DAT_data)
+               __clear_bit(dat_idx, hci->DAT_data);
 }
 
 static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
index 2990ac9eaade771fcb02da00694fd0a2850241b2..c805a8497319dbb76f51a97e107ab9206e2a3515 100644 (file)
@@ -139,7 +139,7 @@ struct hci_rh_data {
 
 struct hci_rings_data {
        unsigned int total;
-       struct hci_rh_data headers[];
+       struct hci_rh_data headers[] __counted_by(total);
 };
 
 struct hci_dma_dev_ibi_data {
@@ -229,6 +229,9 @@ static int hci_dma_init(struct i3c_hci *hci)
        hci->io_data = rings;
        rings->total = nr_rings;
 
+       regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
+       rhs_reg_write(CONTROL, regval);
+
        for (i = 0; i < rings->total; i++) {
                u32 offset = rhs_reg_read(RHn_OFFSET(i));
 
@@ -325,11 +328,10 @@ static int hci_dma_init(struct i3c_hci *hci)
                rh_reg_write(INTR_SIGNAL_ENABLE, regval);
 
 ring_ready:
-               rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
+               rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE |
+                                          RING_CTRL_RUN_STOP);
        }
 
-       regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
-       rhs_reg_write(CONTROL, regval);
        return 0;
 
 err_out:
@@ -345,6 +347,8 @@ 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);
@@ -450,10 +454,9 @@ 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");
-               BUG();
+               WARN_ON(1);
        }
 
        for (i = 0; i < n; i++) {
@@ -734,7 +737,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 < 8; i++) {
+       for (i = 0; mask && i < rings->total; i++) {
                struct hci_rh_data *rh;
                u32 status;
 
@@ -756,9 +759,11 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask)
                if (status & INTR_RING_OP)
                        complete(&rh->op_done);
 
-               if (status & INTR_TRANSFER_ABORT)
+               if (status & INTR_TRANSFER_ABORT) {
                        dev_notice_ratelimited(&hci->master.dev,
                                "ring %d: Transfer Aborted\n", i);
+                       mipi_i3c_hci_resume(hci);
+               }
                if (status & INTR_WARN_INS_STOP_MODE)
                        dev_warn_ratelimited(&hci->master.dev,
                                "ring %d: Inserted Stop on Mode Change\n", i);
index 8f8295acdadb3aa3aa039271beaf56fefed3617f..cf703c00f63349c7b989efe08365f080a119e2c2 100644 (file)
@@ -93,6 +93,7 @@
 #define SVC_I3C_MINTMASKED   0x098
 #define SVC_I3C_MERRWARN     0x09C
 #define   SVC_I3C_MERRWARN_NACK BIT(2)
+#define   SVC_I3C_MERRWARN_TIMEOUT BIT(20)
 #define SVC_I3C_MDMACTRL     0x0A0
 #define SVC_I3C_MDATACTRL    0x0AC
 #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
@@ -143,7 +144,7 @@ struct svc_i3c_xfer {
        int ret;
        unsigned int type;
        unsigned int ncmds;
-       struct svc_i3c_cmd cmds[];
+       struct svc_i3c_cmd cmds[] __counted_by(ncmds);
 };
 
 struct svc_i3c_regs_save {
@@ -175,6 +176,7 @@ struct svc_i3c_regs_save {
  * @ibi.slots: Available IBI slots
  * @ibi.tbq_slot: To be queued IBI slot
  * @ibi.lock: IBI lock
+ * @lock: Transfer lock, protect between IBI work thread and callbacks from master
  */
 struct svc_i3c_master {
        struct i3c_master_controller base;
@@ -203,6 +205,7 @@ struct svc_i3c_master {
                /* Prevent races within IBI handlers */
                spinlock_t lock;
        } ibi;
+       struct mutex lock;
 };
 
 /**
@@ -225,6 +228,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);
@@ -331,6 +342,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
        struct i3c_ibi_slot *slot;
        unsigned int count;
        u32 mdatactrl;
+       int ret, val;
        u8 *buf;
 
        slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
@@ -340,6 +352,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
        slot->len = 0;
        buf = slot->data;
 
+       ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
+                                               SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
+       if (ret) {
+               dev_err(master->dev, "Timeout when polling for COMPLETE\n");
+               return ret;
+       }
+
        while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))  &&
               slot->len < SVC_I3C_FIFO_SIZE) {
                mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
@@ -384,6 +403,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
        u32 status, val;
        int ret;
 
+       mutex_lock(&master->lock);
        /* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
        writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
               SVC_I3C_MCTRL_IBIRESP_AUTO,
@@ -394,6 +414,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
                                         SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
        if (ret) {
                dev_err(master->dev, "Timeout when polling for IBIWON\n");
+               svc_i3c_master_emit_stop(master);
                goto reenable_ibis;
        }
 
@@ -460,12 +481,13 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
 
 reenable_ibis:
        svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+       mutex_unlock(&master->lock);
 }
 
 static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
 {
        struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
-       u32 active = readl(master->regs + SVC_I3C_MINTMASKED);
+       u32 active = readl(master->regs + SVC_I3C_MSTATUS);
 
        if (!SVC_I3C_MSTATUS_SLVSTART(active))
                return IRQ_NONE;
@@ -765,7 +787,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
                        u8 data[6];
 
                        /*
-                        * We only care about the 48-bit provisional ID yet to
+                        * We only care about the 48-bit provisioned ID yet to
                         * be sure a device does not nack an address twice.
                         * Otherwise, we would just need to flush the RX FIFO.
                         */
@@ -1007,6 +1029,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
        u32 reg;
        int ret;
 
+       /* clean SVC_I3C_MINT_IBIWON w1c bits */
+       writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
        writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
               xfer_type |
               SVC_I3C_MCTRL_IBIRESP_NACK |
@@ -1025,6 +1050,23 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
                goto emit_stop;
        }
 
+       /*
+        * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
+        * with I3C Target Address.
+        *
+        * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
+        * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
+        * Role Request (i.e., Secondary Controller requests to become the Active Controller), or
+        * a Hot-Join Request has been made.
+        *
+        * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure
+        * and yield the above events handler.
+        */
+       if (SVC_I3C_MSTATUS_IBIWON(reg)) {
+               ret = -ENXIO;
+               goto emit_stop;
+       }
+
        if (rnw)
                ret = svc_i3c_master_read(master, in, xfer_len);
        else
@@ -1204,9 +1246,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
        cmd->read_len = 0;
        cmd->continued = false;
 
+       mutex_lock(&master->lock);
        svc_i3c_master_enqueue_xfer(master, xfer);
        if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
                svc_i3c_master_dequeue_xfer(master, xfer);
+       mutex_unlock(&master->lock);
 
        ret = xfer->ret;
        kfree(buf);
@@ -1250,9 +1294,11 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
        cmd->read_len = read_len;
        cmd->continued = false;
 
+       mutex_lock(&master->lock);
        svc_i3c_master_enqueue_xfer(master, xfer);
        if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
                svc_i3c_master_dequeue_xfer(master, xfer);
+       mutex_unlock(&master->lock);
 
        if (cmd->read_len != xfer_len)
                ccc->dests[0].payload.len = cmd->read_len;
@@ -1309,9 +1355,11 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
                cmd->continued = (i + 1) < nxfers;
        }
 
+       mutex_lock(&master->lock);
        svc_i3c_master_enqueue_xfer(master, xfer);
        if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
                svc_i3c_master_dequeue_xfer(master, xfer);
+       mutex_unlock(&master->lock);
 
        ret = xfer->ret;
        svc_i3c_master_free_xfer(xfer);
@@ -1347,9 +1395,11 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
                cmd->continued = (i + 1 < nxfers);
        }
 
+       mutex_lock(&master->lock);
        svc_i3c_master_enqueue_xfer(master, xfer);
        if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
                svc_i3c_master_dequeue_xfer(master, xfer);
+       mutex_unlock(&master->lock);
 
        ret = xfer->ret;
        svc_i3c_master_free_xfer(xfer);
@@ -1540,6 +1590,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
        INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
        INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
+       mutex_init(&master->lock);
+
        ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
                               IRQF_NO_SUSPEND, "svc-i3c-irq", master);
        if (ret)
@@ -1651,7 +1703,7 @@ static const struct dev_pm_ops svc_i3c_pm_ops = {
 };
 
 static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
-       { .compatible = "silvaco,i3c-master},
+       { .compatible = "silvaco,i3c-master-v1"},
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
index 90fa83464f0039e4e71a2b1b3f76a58602d7ed32..84ed77c049400546e968016af548bce081805cca 100644 (file)
@@ -96,7 +96,7 @@ enum i3c_dcr {
 
 /**
  * struct i3c_device_info - I3C device information
- * @pid: Provisional ID
+ * @pid: Provisioned ID
  * @bcr: Bus Characteristic Register
  * @dcr: Device Characteristic Register
  * @static_addr: static/I2C address
index db909ef79be47dbbbd19fac1e6d4ff20de8ed1c0..24c1863b86e2b6940d263555515039fa0b317203 100644 (file)
@@ -135,6 +135,7 @@ struct i3c_ibi_slot {
  *                  rejected by the master
  * @num_slots: number of IBI slots reserved for this device
  * @enabled: reflect the IBI status
+ * @wq: workqueue used to execute IBI handlers.
  * @handler: IBI handler specified at i3c_device_request_ibi() call time. This
  *          handler will be called from the controller workqueue, and as such
  *          is allowed to sleep (though it is recommended to process the IBI
@@ -157,6 +158,7 @@ struct i3c_device_ibi_info {
        unsigned int max_payload_len;
        unsigned int num_slots;
        unsigned int enabled;
+       struct workqueue_struct *wq;
        void (*handler)(struct i3c_device *dev,
                        const struct i3c_ibi_payload *payload);
 };
@@ -172,7 +174,7 @@ struct i3c_device_ibi_info {
  *              assigned a dynamic address by the master. Will be used during
  *              bus initialization to assign it a specific dynamic address
  *              before starting DAA (Dynamic Address Assignment)
- * @pid: I3C Provisional ID exposed by the device. This is a unique identifier
+ * @pid: I3C Provisioned ID exposed by the device. This is a unique identifier
  *      that may be used to attach boardinfo to i3c_dev_desc when the device
  *      does not have a static address
  * @of_node: optional DT node in case the device has been described in the DT
@@ -475,7 +477,7 @@ struct i3c_master_controller_ops {
  * @boardinfo.i2c: list of I2C boardinfo objects
  * @boardinfo: board-level information attached to devices connected on the bus
  * @bus: I3C bus exposed by this master
- * @wq: workqueue used to execute IBI handlers. Can also be used by master
+ * @wq: workqueue which can be used by master
  *     drivers if they need to postpone operations that need to take place
  *     in a thread context. Typical examples are Hot Join processing which
  *     requires taking the bus lock in maintenance, which in turn, can only