summaryrefslogtreecommitdiff
path: root/drivers/i3c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i3c')
-rw-r--r--drivers/i3c/master.c143
-rw-r--r--drivers/i3c/master/dw-i3c-master.c15
-rw-r--r--drivers/i3c/master/dw-i3c-master.h2
-rw-r--r--drivers/i3c/master/i3c-master-cdns.c14
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/core.c48
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/dma.c9
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/hci.h1
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/ibi.h13
-rw-r--r--drivers/i3c/master/mipi-i3c-hci/pio.c7
-rw-r--r--drivers/i3c/master/svc-i3c-master.c30
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);