summaryrefslogtreecommitdiff
path: root/drivers/i3c/master
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i3c/master')
-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
9 files changed, 67 insertions, 72 deletions
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);