mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
i3c: master: Consolidate Hot-Join DAA work in the core
Three master drivers (dw-i3c-master, i3c-master-cdns, svc-i3c-master) each carry an essentially identical Hot-Join handler: a struct work_struct embedded in their private state, a work function that just calls i3c_master_do_daa() on the embedded i3c_master_controller, plus matching INIT_WORK()/cancel_work_sync() boilerplate in probe/remove (and shutdown for dw-i3c). The IBI/ISR paths then queue that work onto master->wq, which already lives in the core. Move this pattern into the I3C core: - Add struct work_struct hj_work to struct i3c_master_controller and initialise it in i3c_master_register() with a core-provided handler i3c_master_hj_work_fn() that performs i3c_master_do_daa(). - Cancel the work in i3c_master_unregister() so all controllers get correct teardown ordering against the workqueue for free. - Export i3c_master_queue_hotjoin() as the single entry point drivers call from their Hot-Join IBI handler. Convert the three existing users to the new API: drop their private hj_work fields, work functions, INIT_WORK() and cancel_work_sync() calls, and replace the queue_work(master->wq, &drv->hj_work) call sites with i3c_master_queue_hotjoin(&drv->base). The dw-i3c shutdown path still needs to flush pending Hot-Join work before tearing down the hardware, so it is updated to cancel master->base.hj_work directly. No functional change intended: the work is still queued on the same master->wq, runs the same i3c_master_do_daa(), and is cancelled at controller teardown. Future Hot-Join improvements now only need to be made in one place. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Link: https://patch.msgid.link/20260608054312.10604-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
parent
5b130aadc3
commit
828c613023
|
|
@ -633,6 +633,13 @@ 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);
|
||||
|
||||
i3c_master_do_daa(master);
|
||||
}
|
||||
|
||||
static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -711,6 +718,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);
|
||||
|
|
@ -3084,6 +3103,7 @@ int i3c_master_register(struct i3c_master_controller *master,
|
|||
ret = -ENOMEM;
|
||||
goto err_put_dev;
|
||||
}
|
||||
INIT_WORK(&master->hj_work, i3c_master_hj_work_fn);
|
||||
|
||||
ret = i3c_master_bus_init(master);
|
||||
if (ret)
|
||||
|
|
@ -3146,6 +3166,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);
|
||||
cancel_work_sync(&master->hj_work);
|
||||
|
||||
if (master->ops->set_dev_nack_retry)
|
||||
device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count);
|
||||
|
|
|
|||
|
|
@ -1449,7 +1449,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,
|
||||
|
|
@ -1558,14 +1558,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)
|
||||
{
|
||||
|
|
@ -1655,8 +1647,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);
|
||||
|
|
@ -1678,7 +1668,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() */
|
||||
|
|
@ -1823,7 +1812,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);
|
||||
|
|
|
|||
|
|
@ -69,8 +69,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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -2039,7 +2029,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,
|
||||
|
|
@ -2098,7 +2087,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);
|
||||
|
|
|
|||
|
|
@ -520,6 +520,8 @@ struct i3c_master_controller_ops {
|
|||
* in a thread context. Typical examples are Hot Join processing which
|
||||
* requires taking the bus lock in maintenance, which in turn, can only
|
||||
* be done from a sleep-able context
|
||||
* @hj_work: work item used to run DAA after a Hot-Join event is detected.
|
||||
* Queued to @wq by i3c_master_queue_hotjoin()
|
||||
* @dev_nack_retry_count: retry count when slave device nack
|
||||
*
|
||||
* A &struct i3c_master_controller has to be registered to the I3C subsystem
|
||||
|
|
@ -543,6 +545,7 @@ struct i3c_master_controller {
|
|||
} boardinfo;
|
||||
struct i3c_bus bus;
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct hj_work;
|
||||
unsigned int dev_nack_retry_count;
|
||||
};
|
||||
|
||||
|
|
@ -623,6 +626,7 @@ int i3c_master_register(struct i3c_master_controller *master,
|
|||
void i3c_master_unregister(struct i3c_master_controller *master);
|
||||
int i3c_master_enable_hotjoin(struct i3c_master_controller *master);
|
||||
int i3c_master_disable_hotjoin(struct i3c_master_controller *master);
|
||||
void i3c_master_queue_hotjoin(struct i3c_master_controller *master);
|
||||
|
||||
/**
|
||||
* i3c_dev_get_master_data() - get master private data attached to an I3C
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user