i3c: master: svc: Describe member 'saved_regs'
[linux-2.6-microblaze.git] / drivers / i3c / master / svc-i3c-master.c
index e3f4541..17b4ce1 100644 (file)
@@ -92,6 +92,7 @@
 #define SVC_I3C_MINTCLR      0x094
 #define SVC_I3C_MINTMASKED   0x098
 #define SVC_I3C_MERRWARN     0x09C
+#define   SVC_I3C_MERRWARN_NACK BIT(2)
 #define SVC_I3C_MDMACTRL     0x0A0
 #define SVC_I3C_MDATACTRL    0x0AC
 #define   SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
@@ -145,11 +146,17 @@ struct svc_i3c_xfer {
        struct svc_i3c_cmd cmds[];
 };
 
+struct svc_i3c_regs_save {
+       u32 mconfig;
+       u32 mdynaddr;
+};
+
 /**
  * struct svc_i3c_master - Silvaco I3C Master structure
  * @base: I3C master controller
  * @dev: Corresponding device
  * @regs: Memory mapping
+ * @saved_regs: Volatile values for PM operations
  * @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
@@ -173,6 +180,7 @@ struct svc_i3c_master {
        struct i3c_master_controller base;
        struct device *dev;
        void __iomem *regs;
+       struct svc_i3c_regs_save saved_regs;
        u32 free_slots;
        u8 addrs[SVC_I3C_MAX_DEVS];
        struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS];
@@ -1008,6 +1016,11 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
        if (ret)
                goto emit_stop;
 
+       if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
+               ret = -ENXIO;
+               goto emit_stop;
+       }
+
        if (rnw)
                ret = svc_i3c_master_read(master, in, xfer_len);
        else
@@ -1090,12 +1103,6 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
        if (!xfer)
                return;
 
-       ret = pm_runtime_resume_and_get(master->dev);
-       if (ret < 0) {
-               dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
-               return;
-       }
-
        svc_i3c_master_clear_merrwarn(master);
        svc_i3c_master_flush_fifo(master);
 
@@ -1110,9 +1117,6 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
                        break;
        }
 
-       pm_runtime_mark_last_busy(master->dev);
-       pm_runtime_put_autosuspend(master->dev);
-
        xfer->ret = ret;
        complete(&xfer->comp);
 
@@ -1133,6 +1137,13 @@ static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master,
                                        struct svc_i3c_xfer *xfer)
 {
        unsigned long flags;
+       int ret;
+
+       ret = pm_runtime_resume_and_get(master->dev);
+       if (ret < 0) {
+               dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
+               return;
+       }
 
        init_completion(&xfer->comp);
        spin_lock_irqsave(&master->xferqueue.lock, flags);
@@ -1143,6 +1154,9 @@ static void svc_i3c_master_enqueue_xfer(struct svc_i3c_master *master,
                svc_i3c_master_start_xfer_locked(master);
        }
        spin_unlock_irqrestore(&master->xferqueue.lock, flags);
+
+       pm_runtime_mark_last_busy(master->dev);
+       pm_runtime_put_autosuspend(master->dev);
 }
 
 static bool
@@ -1505,8 +1519,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
                return PTR_ERR(master->sclk);
 
        master->irq = platform_get_irq(pdev, 0);
-       if (master->irq <= 0)
-               return -ENOENT;
+       if (master->irq < 0)
+               return master->irq;
 
        master->dev = dev;
 
@@ -1579,10 +1593,28 @@ static void svc_i3c_master_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
 }
 
+static void svc_i3c_save_regs(struct svc_i3c_master *master)
+{
+       master->saved_regs.mconfig = readl(master->regs + SVC_I3C_MCONFIG);
+       master->saved_regs.mdynaddr = readl(master->regs + SVC_I3C_MDYNADDR);
+}
+
+static void svc_i3c_restore_regs(struct svc_i3c_master *master)
+{
+       if (readl(master->regs + SVC_I3C_MDYNADDR) !=
+           master->saved_regs.mdynaddr) {
+               writel(master->saved_regs.mconfig,
+                      master->regs + SVC_I3C_MCONFIG);
+               writel(master->saved_regs.mdynaddr,
+                      master->regs + SVC_I3C_MDYNADDR);
+       }
+}
+
 static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
 {
        struct svc_i3c_master *master = dev_get_drvdata(dev);
 
+       svc_i3c_save_regs(master);
        svc_i3c_master_unprepare_clks(master);
        pinctrl_pm_select_sleep_state(dev);
 
@@ -1596,6 +1628,8 @@ static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
        pinctrl_pm_select_default_state(dev);
        svc_i3c_master_prepare_clks(master);
 
+       svc_i3c_restore_regs(master);
+
        return 0;
 }