[PATCH 3/3] i3c: master: amd: Add hot-join support
Shubham Patil <[email protected]>
| Newsgroups | org.infradead.lists.linux-i3c,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Add hot-join support for the AMD AXI I3C master controller. By default, the hot-join acknowledgment is disabled. Users can use the sysfs entry to enable it. A hot-join event is serviced by re-running DAA from a work item so the i3c core enumerates the new device. Signed-off-by: Shubham Patil <[email protected]> --- drivers/i3c/master/amd-i3c-master.c | 77 ++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/drivers/i3c/master/amd-i3c-master.c b/drivers/i3c/master/amd-i3c-master.c index d47b04326ea4..62221b138bfe 100644 --- a/drivers/i3c/master/amd-i3c-master.c +++ b/drivers/i3c/master/amd-i3c-master.c @@ -25,6 +25,7 @@ #include <linux/spinlock.h> #include <linux/time.h> #include <linux/unaligned.h> +#include <linux/workqueue.h> #define XI3C_VERSION_OFFSET 0x00 /* Version Register */ #define XI3C_RESET_OFFSET 0x04 /* Soft Reset Register */ @@ -55,9 +56,11 @@ #define XI3C_CR_EN_MASK BIT(0) /* Core Enable */ #define XI3C_CR_RESUME_MASK BIT(2) /* Core Resume */ #define XI3C_CR_IBI_MASK BIT(3) /* IBI ACK enable */ +#define XI3C_CR_HJ_MASK BIT(4) /* Hot-Join ACK enable */ #define XI3C_SR_RESP_NOT_EMPTY_MASK BIT(4) /* Resp Fifo not empty status mask */ #define XI3C_RD_FIFO_NOT_EMPTY_MASK BIT(15) /* Read Fifo not empty status mask */ #define XI3C_INTR_IBI_MASK BIT(7) /* IBI event (INTR status/enable) */ +#define XI3C_INTR_HJ_MASK BIT(8) /* Hot-Join event */ #define XI3C_BCR_MASK GENMASK(23, 16) #define XI3C_DCR_MASK GENMASK(31, 24) @@ -191,11 +194,14 @@ struct xi3c_xfer { * @xfer_resp_valid is set. Guarded by @lock. * @xfer_resp_valid: True once the in-flight transfer's own response word has * been taken from the shared response FIFO. Guarded by @lock. - * @irq: Controller interrupt line, used for IBI events. Only valid when - * @ibi_capable is set. + * @irq: Controller interrupt line, used for IBI/Hot-Join events. Only valid + * when @ibi_capable is set. * @ibi_capable: True when the IP was synthesized with In-Band Interrupt - * support ("xlnx,ibi-capable"); also the condition for the - * controller interrupt being present. + * support ("xlnx,ibi-capable"). Since Hot-Join requests are + * ACKed by the IBI machinery, this is also the condition for + * the controller interrupt being present at all. + * @hj_capable: True when the IP was synthesized with Hot-Join support + * ("xlnx,hj-capable"); implies @ibi_capable. * @ops: Controller ops handed to the framework, assembled at probe time from * the base ops plus the callbacks the design actually supports. * @ibi: In-Band Interrupt slot tracking. @@ -205,6 +211,7 @@ struct xi3c_xfer { * controller-wide IBI ACK/interrupt is armed on the * first enable and disarmed on the last disable. Guarded * by @reg_lock. + * @hj_work: Deferred re-DAA triggered from the Hot-Join interrupt. */ struct xi3c_master { struct i3c_master_controller base; @@ -221,12 +228,14 @@ struct xi3c_master { bool xfer_resp_valid; int irq; bool ibi_capable; + bool hj_capable; struct i3c_master_controller_ops ops; struct { spinlock_t lock; /* protects slots[] against the IBI handler */ struct i3c_dev_desc *slots[XI3C_MAX_DEVS]; unsigned int enabled_count; } ibi; + struct work_struct hj_work; }; /** @@ -1474,6 +1483,40 @@ static void xi3c_master_handle_ibi(struct xi3c_master *master) xi3c_master_drain_ibi_fifo(master, len); } +static int xi3c_master_enable_hotjoin(struct i3c_master_controller *m) +{ + struct xi3c_master *master = to_xi3c_master(m); + + guard(spinlock_irqsave)(&master->reg_lock); + iowrite32(ioread32(master->membase + XI3C_CR_OFFSET) | XI3C_CR_HJ_MASK, + master->membase + XI3C_CR_OFFSET); + iowrite32(ioread32(master->membase + XI3C_INTR_RE_OFFSET) | + XI3C_INTR_HJ_MASK, master->membase + XI3C_INTR_RE_OFFSET); + + return 0; +} + +static int xi3c_master_disable_hotjoin(struct i3c_master_controller *m) +{ + struct xi3c_master *master = to_xi3c_master(m); + + guard(spinlock_irqsave)(&master->reg_lock); + iowrite32(ioread32(master->membase + XI3C_INTR_RE_OFFSET) & + ~XI3C_INTR_HJ_MASK, master->membase + XI3C_INTR_RE_OFFSET); + iowrite32(ioread32(master->membase + XI3C_CR_OFFSET) & ~XI3C_CR_HJ_MASK, + master->membase + XI3C_CR_OFFSET); + + return 0; +} + +static void xi3c_master_hj_work(struct work_struct *work) +{ + struct xi3c_master *master = container_of(work, struct xi3c_master, + hj_work); + + i3c_master_do_daa(&master->base); +} + static irqreturn_t xi3c_master_irq_handler(int irq, void *dev_id) { struct xi3c_master *master = dev_id; @@ -1494,6 +1537,9 @@ static irqreturn_t xi3c_master_irq_handler(int irq, void *dev_id) xi3c_master_handle_ibi(master); } + if (status & XI3C_INTR_HJ_MASK) + queue_work(master->base.wq, &master->hj_work); + return IRQ_HANDLED; } @@ -1514,6 +1560,11 @@ static void xi3c_master_init_ibi_ops(struct xi3c_master *master) master->ops.enable_ibi = xi3c_master_enable_ibi; master->ops.disable_ibi = xi3c_master_disable_ibi; master->ops.recycle_ibi_slot = xi3c_master_recycle_ibi_slot; + + if (master->hj_capable) { + master->ops.enable_hotjoin = xi3c_master_enable_hotjoin; + master->ops.disable_hotjoin = xi3c_master_disable_hotjoin; + } } static int xi3c_master_probe(struct platform_device *pdev) @@ -1545,13 +1596,21 @@ static int xi3c_master_probe(struct platform_device *pdev) spin_lock_init(&master->ibi.lock); spin_lock_init(&master->reg_lock); + INIT_WORK(&master->hj_work, xi3c_master_hj_work); master->ibi_capable = device_property_read_bool(master->dev, "xlnx,ibi-capable"); + master->hj_capable = device_property_read_bool(master->dev, + "xlnx,hj-capable"); + + /* Hot-Join requests are ACKed by the IBI machinery. */ + if (master->hj_capable && !master->ibi_capable) + return dev_err_probe(master->dev, -EINVAL, + "xlnx,hj-capable requires xlnx,ibi-capable\n"); /* - * The interrupt only carries IBI events, so it is only described for - * designs synthesized with that feature. + * The interrupt only carries IBI and Hot-Join events, so it is only + * described for designs synthesized with those features. */ if (master->ibi_capable) { xi3c_master_init_ibi_ops(master); @@ -1588,6 +1647,12 @@ static void xi3c_master_remove(struct platform_device *pdev) if (master->ibi_capable) disable_irq(master->irq); + if (master->hj_capable) { + /* Stop new Hot-Join IRQs before flushing, so the work can't be re-queued. */ + xi3c_master_disable_hotjoin(&master->base); + cancel_work_sync(&master->hj_work); + } + i3c_master_unregister(&master->base); } -- 2.34.1 -- linux-i3c mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-i3c