From: Linus Torvalds Date: Sun, 5 Nov 2023 02:25:36 +0000 (-1000) Subject: Merge tag 'i3c/for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux X-Git-Tag: v6.7-rc1~58 X-Git-Url: http://git.samba.org/sfrench/?a=commitdiff_plain;h=1c41041124bd14dd6610da256a3da4e5b74ce6b1;hp=b8cc56d0414e2330d9fe05342843512b1ad8cdb7;p=sfrench%2Fcifs-2.6.git Merge tag 'i3c/for-6.7' of git://git./linux/kernel/git/i3c/linux 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 ... --- diff --git a/Documentation/ABI/testing/sysfs-bus-i3c b/Documentation/ABI/testing/sysfs-bus-i3c index 1f4a2662335b..e5248fd67a56 100644 --- a/Documentation/ABI/testing/sysfs-bus-i3c +++ b/Documentation/ABI/testing/sysfs-bus-i3c @@ -67,7 +67,7 @@ What: /sys/bus/i3c/devices/i3c-/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-/-/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. diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml index d9483fbd2454..c816e295d565 100644 --- a/Documentation/devicetree/bindings/i3c/i3c.yaml +++ b/Documentation/devicetree/bindings/i3c/i3c.yaml @@ -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, diff --git a/Documentation/driver-api/i3c/protocol.rst b/Documentation/driver-api/i3c/protocol.rst index 02653defa011..23a0b93c62b1 100644 --- a/Documentation/driver-api/i3c/protocol.rst +++ b/Documentation/driver-api/i3c/protocol.rst @@ -71,8 +71,8 @@ During DAA, each I3C device reports 3 important things: related capabilities * DCR: Device Characteristic Register. This 8-bit register describes the functionalities provided by the device -* Provisional ID: A 48-bit unique identifier. On a given bus there should be no - Provisional ID collision, otherwise the discovery mechanism may fail. +* Provisioned ID: A 48-bit unique identifier. On a given bus there should be no + Provisioned ID collision, otherwise the discovery mechanism may fail. I3C slave events ================ diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 959ec5269376..95caa162706f 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -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; } diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 9332ae5f6419..ef5751e91cc9 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -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 { diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 49551db71bc9..bcbe8f914149 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -191,7 +191,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(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); diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c index 6a781f89b0e4..2b2323aa6714 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c @@ -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)) { diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 837af83c85f4..1ae56a5699c6 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -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 "); MODULE_DESCRIPTION("MIPI I3C HCI driver"); diff --git a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c index 97bb49ff5b53..47b9b4d4ed3f 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c @@ -64,15 +64,17 @@ static int hci_dat_v1_init(struct i3c_hci *hci) return -EOPNOTSUPP; } - /* use a bitmap for faster free slot search */ - hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL); - if (!hci->DAT_data) - return -ENOMEM; - - /* clear them */ - for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) { - dat_w0_write(dat_idx, 0); - dat_w1_write(dat_idx, 0); + if (!hci->DAT_data) { + /* use a bitmap for faster free slot search */ + hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL); + if (!hci->DAT_data) + return -ENOMEM; + + /* clear them */ + for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) { + dat_w0_write(dat_idx, 0); + dat_w1_write(dat_idx, 0); + } } return 0; @@ -87,7 +89,13 @@ static void hci_dat_v1_cleanup(struct i3c_hci *hci) static int hci_dat_v1_alloc_entry(struct i3c_hci *hci) { unsigned int dat_idx; + int ret; + if (!hci->DAT_data) { + ret = hci_dat_v1_init(hci); + if (ret) + return ret; + } dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries); if (dat_idx >= hci->DAT_entries) return -ENOENT; @@ -103,7 +111,8 @@ static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx) { dat_w0_write(dat_idx, 0); dat_w1_write(dat_idx, 0); - __clear_bit(dat_idx, hci->DAT_data); + if (hci->DAT_data) + __clear_bit(dat_idx, hci->DAT_data); } static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci, diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index 2990ac9eaade..c805a8497319 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -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); diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 8f8295acdadb..cf703c00f633 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -93,6 +93,7 @@ #define SVC_I3C_MINTMASKED 0x098 #define SVC_I3C_MERRWARN 0x09C #define SVC_I3C_MERRWARN_NACK BIT(2) +#define SVC_I3C_MERRWARN_TIMEOUT BIT(20) #define SVC_I3C_MDMACTRL 0x0A0 #define SVC_I3C_MDATACTRL 0x0AC #define SVC_I3C_MDATACTRL_FLUSHTB BIT(0) @@ -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); diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h index 90fa83464f00..84ed77c04940 100644 --- a/include/linux/i3c/device.h +++ b/include/linux/i3c/device.h @@ -96,7 +96,7 @@ enum i3c_dcr { /** * struct i3c_device_info - I3C device information - * @pid: Provisional ID + * @pid: Provisioned ID * @bcr: Bus Characteristic Register * @dcr: Device Characteristic Register * @static_addr: static/I2C address diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index db909ef79be4..24c1863b86e2 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -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