diff options
Diffstat (limited to 'drivers/i3c')
| -rw-r--r-- | drivers/i3c/master.c | 143 | ||||
| -rw-r--r-- | drivers/i3c/master/dw-i3c-master.c | 15 | ||||
| -rw-r--r-- | drivers/i3c/master/dw-i3c-master.h | 2 | ||||
| -rw-r--r-- | drivers/i3c/master/i3c-master-cdns.c | 14 | ||||
| -rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/core.c | 48 | ||||
| -rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/dma.c | 9 | ||||
| -rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/hci.h | 1 | ||||
| -rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/ibi.h | 13 | ||||
| -rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/pio.c | 7 | ||||
| -rw-r--r-- | drivers/i3c/master/svc-i3c-master.c | 30 |
10 files changed, 176 insertions, 106 deletions
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 5cd4e5da2233..74c04e19f86c 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -368,14 +368,6 @@ static void i3c_device_remove(struct device *dev) driver->remove(i3cdev); } -const struct bus_type i3c_bus_type = { - .name = "i3c", - .match = i3c_device_match, - .probe = i3c_device_probe, - .remove = i3c_device_remove, -}; -EXPORT_SYMBOL_GPL(i3c_bus_type); - static enum i3c_addr_slot_status i3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask) { @@ -633,6 +625,14 @@ static ssize_t i2c_scl_frequency_show(struct device *dev, } static DEVICE_ATTR_RO(i2c_scl_frequency); +static void i3c_master_hj_work_fn(struct work_struct *work) +{ + struct i3c_master_controller *master = container_of(work, typeof(*master), hj_work); + + if (!master->shutting_down) + i3c_master_do_daa(master); +} + static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) { int ret; @@ -649,9 +649,11 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) return ret; } - i3c_bus_normaluse_lock(&master->bus); + i3c_bus_maintenance_lock(&master->bus); - if (enable) + if (master->shutting_down) + ret = -ENODEV; + else if (enable) ret = master->ops->enable_hotjoin(master); else ret = master->ops->disable_hotjoin(master); @@ -659,7 +661,7 @@ static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable) if (!ret) master->hotjoin = enable; - i3c_bus_normaluse_unlock(&master->bus); + i3c_bus_maintenance_unlock(&master->bus); if ((enable && ret) || (!enable && !ret) || master->rpm_ibi_allowed) i3c_master_rpm_put(master); @@ -711,6 +713,18 @@ int i3c_master_disable_hotjoin(struct i3c_master_controller *master) } EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin); +/** + * i3c_master_queue_hotjoin - Queue DAA processing after a Hot-Join event + * @master: I3C master object + * + * Queue the hot-join worker on the master's workqueue. + */ +void i3c_master_queue_hotjoin(struct i3c_master_controller *master) +{ + queue_work(master->wq, &master->hj_work); +} +EXPORT_SYMBOL_GPL(i3c_master_queue_hotjoin); + static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_bus *i3cbus = dev_to_i3cbus(dev); @@ -728,7 +742,14 @@ static DEVICE_ATTR_RW(hotjoin); static ssize_t dev_nack_retry_count_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); + struct i3c_bus *i3cbus = dev_to_i3cbus(dev); + ssize_t ret; + + i3c_bus_normaluse_lock(i3cbus); + ret = sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count); + i3c_bus_normaluse_unlock(i3cbus); + + return ret; } static ssize_t dev_nack_retry_count_store(struct device *dev, @@ -744,16 +765,19 @@ static ssize_t dev_nack_retry_count_store(struct device *dev, if (ret) return ret; + ret = i3c_master_rpm_get(master); + if (ret) + return ret; + i3c_bus_maintenance_lock(i3cbus); ret = master->ops->set_dev_nack_retry(master, val); + if (!ret) + master->dev_nack_retry_count = val; i3c_bus_maintenance_unlock(i3cbus); - if (ret) - return ret; - - master->dev_nack_retry_count = val; + i3c_master_rpm_put(master); - return count; + return ret ?: count; } static DEVICE_ATTR_RW(dev_nack_retry_count); @@ -818,6 +842,31 @@ static const struct device_type i3c_masterdev_type = { .groups = i3c_masterdev_groups, }; +static void i3c_master_shutdown(struct i3c_master_controller *master) +{ + i3c_bus_maintenance_lock(&master->bus); + master->shutting_down = true; + i3c_bus_maintenance_unlock(&master->bus); + + cancel_work_sync(&master->hj_work); + cancel_work_sync(&master->reg_work); +} + +static void i3c_device_shutdown(struct device *dev) +{ + if (dev->type == &i3c_masterdev_type) + i3c_master_shutdown(dev_to_i3cmaster(dev)); +} + +const struct bus_type i3c_bus_type = { + .name = "i3c", + .match = i3c_device_match, + .probe = i3c_device_probe, + .remove = i3c_device_remove, + .shutdown = i3c_device_shutdown, +}; +EXPORT_SYMBOL_GPL(i3c_bus_type); + static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode, unsigned long max_i2c_scl_rate) { @@ -1800,6 +1849,16 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) } } +static void i3c_master_reg_work_fn(struct work_struct *work) +{ + struct i3c_master_controller *master = container_of(work, typeof(*master), reg_work); + + i3c_bus_normaluse_lock(&master->bus); + if (!master->shutting_down) + i3c_master_register_new_i3c_devs(master); + i3c_bus_normaluse_unlock(&master->bus); +} + /** * i3c_master_do_daa_ext() - Dynamic Address Assignment (extended version) * @master: controller @@ -1827,19 +1886,20 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) i3c_bus_maintenance_lock(&master->bus); - if (rstdaa) - rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); - - ret = master->ops->do_daa(master); + if (master->shutting_down) { + ret = -ENODEV; + } else { + if (rstdaa) + rstret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); + ret = master->ops->do_daa(master); + } i3c_bus_maintenance_unlock(&master->bus); if (ret) goto out; - i3c_bus_normaluse_lock(&master->bus); - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, &master->reg_work); out: i3c_master_rpm_put(master); @@ -2274,12 +2334,11 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, bool enable_ibi = false; int ret; - if (!master) - return -EINVAL; - newdev = i3c_master_alloc_i3c_dev(master, &info); - if (IS_ERR(newdev)) - return PTR_ERR(newdev); + if (IS_ERR(newdev)) { + ret = PTR_ERR(newdev); + goto err_prevent_addr_reuse; + } ret = i3c_master_attach_i3c_dev(master, newdev); if (ret) @@ -2401,6 +2460,16 @@ err_detach_dev: err_free_dev: i3c_master_free_i3c_dev(newdev); +err_prevent_addr_reuse: + /* + * Although the device has not been added, the address has been + * assigned. Prevent the address from being used again. + */ + if (i3c_bus_get_addr_slot_status(&master->bus, addr) == I3C_ADDR_SLOT_FREE) + i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV); + + dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret); + return ret; } EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); @@ -3079,11 +3148,13 @@ int i3c_master_register(struct i3c_master_controller *master, if (ret) goto err_put_dev; - master->wq = alloc_workqueue("%s", WQ_PERCPU, 0, dev_name(parent)); + master->wq = alloc_workqueue("%s", WQ_PERCPU | WQ_FREEZABLE, 0, dev_name(parent)); if (!master->wq) { ret = -ENOMEM; goto err_put_dev; } + INIT_WORK(&master->hj_work, i3c_master_hj_work_fn); + INIT_WORK(&master->reg_work, i3c_master_reg_work_fn); ret = i3c_master_bus_init(master); if (ret) @@ -3109,12 +3180,15 @@ int i3c_master_register(struct i3c_master_controller *master, /* * We're done initializing the bus and the controller, we can now - * register I3C devices discovered during the initial DAA. + * register I3C devices discovered during the initial DAA. Device + * registration is done via reg_work because that keeps a single + * registration code path and ensures the worker is the only writer + * of desc->dev. Flush the work to preserve synchronous probe-time + * behavior. */ master->init_done = true; - i3c_bus_normaluse_lock(&master->bus); - i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); + queue_work(master->wq, &master->reg_work); + flush_work(&master->reg_work); if (master->ops->set_dev_nack_retry) device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); @@ -3146,6 +3220,7 @@ EXPORT_SYMBOL_GPL(i3c_master_register); void i3c_master_unregister(struct i3c_master_controller *master) { i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE); + i3c_master_shutdown(master); if (master->ops->set_dev_nack_retry) device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 655693a2187e..eb9a13a73684 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1445,7 +1445,7 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master) if (IBI_TYPE_SIRQ(reg)) { dw_i3c_master_handle_ibi_sir(master, reg); } else if (IBI_TYPE_HJ(reg)) { - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); } else { len = IBI_QUEUE_STATUS_DATA_LEN(reg); dev_info(&master->base.dev, @@ -1554,14 +1554,6 @@ static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = { .set_dat_ibi = dw_i3c_platform_set_dat_ibi_nop, }; -static void dw_i3c_hj_work(struct work_struct *work) -{ - struct dw_i3c_master *master = - container_of(work, typeof(*master), hj_work); - - i3c_master_do_daa(&master->base); -} - int dw_i3c_common_probe(struct dw_i3c_master *master, struct platform_device *pdev) { @@ -1636,8 +1628,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK) pm_runtime_get_noresume(&pdev->dev); - INIT_WORK(&master->hj_work, dw_i3c_hj_work); - device_set_of_node_from_dev(&master->base.i2c.dev, &pdev->dev); ret = i3c_master_register(&master->base, &pdev->dev, &dw_mipi_i3c_ops, false); @@ -1659,7 +1649,6 @@ EXPORT_SYMBOL_GPL(dw_i3c_common_probe); void dw_i3c_common_remove(struct dw_i3c_master *master) { - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); /* Balance pm_runtime_get_noresume() from probe() */ @@ -1804,7 +1793,7 @@ static void dw_i3c_shutdown(struct platform_device *pdev) return; } - cancel_work_sync(&master->hj_work); + cancel_work_sync(&master->base.hj_work); /* Disable interrupts */ writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN); diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index c5cb695c16ab..2f029bd36232 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -68,8 +68,6 @@ struct dw_i3c_master { /* platform-specific data */ const struct dw_i3c_platform_ops *platform_ops; - - struct work_struct hj_work; }; struct dw_i3c_platform_ops { diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 5cfec6761494..6d221596ea35 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -398,7 +398,6 @@ struct cdns_i3c_data { }; struct cdns_i3c_master { - struct work_struct hj_work; struct i3c_master_controller base; u32 free_rr_slots; unsigned int maxdevs; @@ -1357,7 +1356,7 @@ static void cnds_i3c_master_demux_ibis(struct cdns_i3c_master *master) case IBIR_TYPE_HJ: WARN_ON(IBIR_XFER_BYTES(ibir) || (ibir & IBIR_ERROR)); - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); break; case IBIR_TYPE_MR: @@ -1528,15 +1527,6 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = { .recycle_ibi_slot = cdns_i3c_master_recycle_ibi_slot, }; -static void cdns_i3c_master_hj(struct work_struct *work) -{ - struct cdns_i3c_master *master = container_of(work, - struct cdns_i3c_master, - hj_work); - - i3c_master_do_daa(&master->base); -} - static struct cdns_i3c_data cdns_i3c_devdata = { .thd_delay_ns = 10, }; @@ -1584,7 +1574,6 @@ static int cdns_i3c_master_probe(struct platform_device *pdev) spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); - INIT_WORK(&master->hj_work, cdns_i3c_master_hj); writel(0xffffffff, master->regs + MST_IDR); writel(0xffffffff, master->regs + SLV_IDR); ret = devm_request_irq(&pdev->dev, irq, cdns_i3c_master_interrupt, 0, @@ -1627,7 +1616,6 @@ static void cdns_i3c_master_remove(struct platform_device *pdev) { struct cdns_i3c_master *master = platform_get_drvdata(pdev); - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); } diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index b781dbed2165..388a42f4d2ec 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -22,6 +22,7 @@ #include "ext_caps.h" #include "cmd.h" #include "dat.h" +#include "ibi.h" /* * Host Controller Capabilities and Operation Registers @@ -124,6 +125,7 @@ static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci) static int i3c_hci_bus_init(struct i3c_master_controller *m) { struct i3c_hci *hci = to_i3c_hci(m); + struct device *dev = hci->master.dev.parent; struct i3c_device_info info; int ret; @@ -144,6 +146,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) if (ret) return ret; + hci->ibi_devs = devm_kcalloc(dev, hci->DAT_entries, sizeof(*hci->ibi_devs), GFP_KERNEL); + if (!hci->ibi_devs) + return -ENOMEM; + ret = hci->io->init(hci); if (ret) return ret; @@ -556,14 +562,40 @@ static int i3c_hci_request_ibi(struct i3c_dev_desc *dev, return hci->io->request_ibi(hci, dev, req); } +static void __i3c_hci_disable_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev) +{ + struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); + + mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + scoped_guard(spinlock_irqsave, &hci->lock) + hci->ibi_devs[dev_data->dat_idx] = NULL; +} + static void i3c_hci_free_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); + /* Must ensure the IBI has been disabled */ + __i3c_hci_disable_ibi(hci, dev); hci->io->free_ibi(hci, dev); } +struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr) +{ + int dat_idx; + + lockdep_assert_held(&hci->lock); + + for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) { + struct i3c_dev_desc *dev = hci->ibi_devs[dat_idx]; + + if (dev && dev->info.dyn_addr == addr) + return dev; + } + return NULL; +} + static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); @@ -571,6 +603,8 @@ static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev) struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); mipi_i3c_hci_dat_v1.clear_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + scoped_guard(spinlock_irqsave, &hci->lock) + hci->ibi_devs[dev_data->dat_idx] = dev; return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); } @@ -578,9 +612,8 @@ static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); + __i3c_hci_disable_ibi(hci, dev); return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); } @@ -762,15 +795,10 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci) int i3c_hci_rpm_suspend(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); - int ret; - ret = i3c_hci_bus_disable(hci); - if (ret) { - /* Fall back to software reset to disable the bus */ - ret = i3c_hci_software_reset(hci); - i3c_hci_sync_irq_inactive(hci); - return ret; - } + /* Fall back to software reset to disable the bus */ + if (i3c_hci_bus_disable(hci)) + i3c_hci_software_reset(hci); hci->io->suspend(hci); diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c index e4daaa612055..8517d2c14e8b 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dma.c +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c @@ -554,7 +554,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci, if (ring_status & RING_STATUS_RUNNING) { /* stop the ring */ reinit_completion(&rh->op_done); - rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_ABORT); + rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT); wait_for_completion_timeout(&rh->op_done, HZ); ring_status = rh_reg_read(RING_STATUS); if (ring_status & RING_STATUS_RUNNING) { @@ -783,8 +783,11 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) /* determine who this is for */ dev = i3c_hci_addr_to_dev(hci, ibi_addr); if (!dev) { - dev_err(&hci->master.dev, - "IBI for unknown device %#x\n", ibi_addr); + /* + * Either an IBI received just before IBI's were disabled, or + * the controller is broken. Assume the former. + */ + dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi_addr); goto done; } diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index f17f43494c1b..b623e4c22c6c 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -60,6 +60,7 @@ struct i3c_hci { unsigned int DAT_entry_size; void *DAT_data; struct dat_words *DAT; + struct i3c_dev_desc **ibi_devs; unsigned int DCT_entries; unsigned int DCT_entry_size; u8 version_major; diff --git a/drivers/i3c/master/mipi-i3c-hci/ibi.h b/drivers/i3c/master/mipi-i3c-hci/ibi.h index e1f98e264da0..073ca67b7d04 100644 --- a/drivers/i3c/master/mipi-i3c-hci/ibi.h +++ b/drivers/i3c/master/mipi-i3c-hci/ibi.h @@ -26,17 +26,6 @@ #define IBI_DATA_LENGTH GENMASK(7, 0) /* handy helpers */ -static inline struct i3c_dev_desc * -i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr) -{ - struct i3c_bus *bus = i3c_master_get_bus(&hci->master); - struct i3c_dev_desc *dev; - - i3c_bus_for_each_i3cdev(bus, dev) { - if (dev->info.dyn_addr == addr) - return dev; - } - return NULL; -} +struct i3c_dev_desc *i3c_hci_addr_to_dev(struct i3c_hci *hci, unsigned int addr); #endif diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c index 8f48a81e65ab..c99c85693214 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -863,8 +863,11 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio) dev = i3c_hci_addr_to_dev(hci, ibi->addr); if (!dev) { - dev_err(&hci->master.dev, - "IBI for unknown device %#x\n", ibi->addr); + /* + * Either an IBI received just before IBI's were disabled, or + * the controller is broken. Assume the former. + */ + dev_dbg(&hci->master.dev, "IBI when not enabled at address %#x\n", ibi->addr); return true; } diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index e2d99a3ac07d..893710d05a2e 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -208,7 +208,6 @@ struct svc_i3c_drvdata { * @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 * @irq: Main interrupt * @num_clks: I3C clock number * @fclk: Fast clock (bus) @@ -235,7 +234,6 @@ struct svc_i3c_master { u32 free_slots; u8 addrs[SVC_I3C_MAX_DEVS]; struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS]; - struct work_struct hj_work; int irq; int num_clks; struct clk *fclk; @@ -366,14 +364,6 @@ to_svc_i3c_master(struct i3c_master_controller *master) return container_of(master, struct svc_i3c_master, base); } -static void svc_i3c_master_hj_work(struct work_struct *work) -{ - struct svc_i3c_master *master; - - master = container_of(work, struct svc_i3c_master, hj_work); - i3c_master_do_daa(&master->base); -} - static struct i3c_dev_desc * svc_i3c_master_dev_from_addr(struct svc_i3c_master *master, unsigned int ibiaddr) @@ -651,7 +641,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master) case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN: svc_i3c_master_emit_stop(master); if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN)) - queue_work(master->base.wq, &master->hj_work); + i3c_master_queue_hotjoin(&master->base); break; case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST: svc_i3c_master_emit_stop(master); @@ -672,10 +662,18 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id) /* Clear the interrupt status */ writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS); - /* Ignore the false event */ - if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART) && - !SVC_I3C_MSTATUS_STATE_SLVREQ(active)) - return IRQ_HANDLED; + if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART)) { + /* + * Re-read MSTATUS to obtain the latest state and avoid + * missing an IBI that arrives after MSTATUS is latched + * but before SLVSTART is cleared. + */ + active = readl(master->regs + SVC_I3C_MSTATUS); + + /* Ignore the false event */ + if (!SVC_I3C_MSTATUS_STATE_SLVREQ(active)) + return IRQ_HANDLED; + } /* * The SDA line remains low until the request is processed. @@ -2022,7 +2020,6 @@ static int svc_i3c_master_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "can't enable I3C clocks\n"); - INIT_WORK(&master->hj_work, svc_i3c_master_hj_work); mutex_init(&master->lock); ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler, @@ -2081,7 +2078,6 @@ static void svc_i3c_master_remove(struct platform_device *pdev) { struct svc_i3c_master *master = platform_get_drvdata(pdev); - cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); pm_runtime_dont_use_autosuspend(&pdev->dev); |
