This is a note to let you know that I've just added the patch titled i3c: dw: Add hot-join support. to the 6.6-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary The filename of the patch is: i3c-dw-add-hot-join-support.patch and it can be found in the queue-6.6 subdirectory. If you, or anyone else, feels it should not be added to the stable tree, please let <stable@xxxxxxxxxxxxxxx> know about it. commit 17f65c38f105a6976c7ac97b8c835ba725f6882c Author: Billy Tsai <billy_tsai@xxxxxxxxxxxxxx> Date: Mon Apr 29 15:36:24 2024 +0800 i3c: dw: Add hot-join support. [ Upstream commit 1d08326020fba690cbb7b8f1b38ab4eab6745969 ] Add hot-join support for dw i3c master controller. By default, the hot-join acknowledgment is disabled, and the hardware will automatically send the DISEC CCC when it receives the hot-join request. Users can use the sys entry to enable it. Signed-off-by: Billy Tsai <billy_tsai@xxxxxxxxxxxxxx> Link: https://lore.kernel.org/r/20240429073624.256830-1-billy_tsai@xxxxxxxxxxxxxx Signed-off-by: Alexandre Belloni <alexandre.belloni@xxxxxxxxxxx> Stable-dep-of: b75439c945b9 ("i3c: dw: Fix use-after-free in dw_i3c_master driver due to race condition") Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx> diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 235235613c1b9..93e9d307e40ef 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1136,6 +1136,23 @@ static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev) data->ibi_pool = NULL; } +static void dw_i3c_master_enable_sir_signal(struct dw_i3c_master *master, bool enable) +{ + u32 reg; + + reg = readl(master->regs + INTR_STATUS_EN); + reg &= ~INTR_IBI_THLD_STAT; + if (enable) + reg |= INTR_IBI_THLD_STAT; + writel(reg, master->regs + INTR_STATUS_EN); + + reg = readl(master->regs + INTR_SIGNAL_EN); + reg &= ~INTR_IBI_THLD_STAT; + if (enable) + reg |= INTR_IBI_THLD_STAT; + writel(reg, master->regs + INTR_SIGNAL_EN); +} + static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master, struct i3c_dev_desc *dev, u8 idx, bool enable) @@ -1170,23 +1187,34 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master, } writel(reg, master->regs + IBI_SIR_REQ_REJECT); - if (global) { - reg = readl(master->regs + INTR_STATUS_EN); - reg &= ~INTR_IBI_THLD_STAT; - if (enable) - reg |= INTR_IBI_THLD_STAT; - writel(reg, master->regs + INTR_STATUS_EN); - - reg = readl(master->regs + INTR_SIGNAL_EN); - reg &= ~INTR_IBI_THLD_STAT; - if (enable) - reg |= INTR_IBI_THLD_STAT; - writel(reg, master->regs + INTR_SIGNAL_EN); - } + if (global) + dw_i3c_master_enable_sir_signal(master, enable); + spin_unlock_irqrestore(&master->devs_lock, flags); } +static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m) +{ + struct dw_i3c_master *master = to_dw_i3c_master(m); + + dw_i3c_master_enable_sir_signal(master, true); + writel(readl(master->regs + DEVICE_CTRL) & ~DEV_CTRL_HOT_JOIN_NACK, + master->regs + DEVICE_CTRL); + + return 0; +} + +static int dw_i3c_master_disable_hotjoin(struct i3c_master_controller *m) +{ + struct dw_i3c_master *master = to_dw_i3c_master(m); + + writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK, + master->regs + DEVICE_CTRL); + + return 0; +} + static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev) { struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); @@ -1326,6 +1354,8 @@ 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); } else { len = IBI_QUEUE_STATUS_DATA_LEN(reg); dev_info(&master->base.dev, @@ -1393,6 +1423,8 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ibi_ops = { .enable_ibi = dw_i3c_master_enable_ibi, .disable_ibi = dw_i3c_master_disable_ibi, .recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot, + .enable_hotjoin = dw_i3c_master_enable_hotjoin, + .disable_hotjoin = dw_i3c_master_disable_hotjoin, }; /* default platform ops implementations */ @@ -1412,6 +1444,14 @@ 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) { @@ -1469,6 +1509,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, if (master->ibi_capable) ops = &dw_mipi_i3c_ibi_ops; + INIT_WORK(&master->hj_work, dw_i3c_hj_work); ret = i3c_master_register(&master->base, &pdev->dev, ops, false); if (ret) goto err_assert_rst; diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index ab862c5d15fe7..4ab94aa72252e 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -57,6 +57,8 @@ 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 {