]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - drivers/i3c/master/dw-i3c-master.c
i3c: dw: Add hot-join support.
[thirdparty/kernel/stable.git] / drivers / i3c / master / dw-i3c-master.c
index 276153e10f5a404a0453e42e37f3b4ea3896b98d..0ec00e644bd47142b29290f03a84d8657f1dd513 100644 (file)
@@ -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;