]> git.baikalelectronics.ru Git - kernel.git/commitdiff
usb: dwc3: debugfs: Resume dwc3 before accessing registers
authorUdipto Goswami <quic_ugoswami@quicinc.com>
Tue, 9 May 2023 14:48:36 +0000 (20:18 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 May 2023 16:32:48 +0000 (17:32 +0100)
commit 614ce6a2ea50068b45339257891e51e639ac9001 upstream.

When the dwc3 device is runtime suspended, various required clocks are in
disabled state and it is not guaranteed that access to any registers would
work. Depending on the SoC glue, a register read could be as benign as
returning 0 or be fatal enough to hang the system.

In order to prevent such scenarios of fatal errors, make sure to resume
dwc3 then allow the function to proceed.

Fixes: 1e90ce98e30b ("usb: Introduce DesignWare USB3 DRD Driver")
Cc: stable@vger.kernel.org #3.2: 94bd87e85813: debugfs: regset32: Add Runtime PM support
Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com>
Reviewed-by: Johan Hovold <johan+linaro@kernel.org>
Tested-by: Johan Hovold <johan+linaro@kernel.org>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/20230509144836.6803-1-quic_ugoswami@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/dwc3/debugfs.c

index 850df0e6bcabf657a0cceba2e0f607e772bb40cc..f0ffd2e5c6429dc0399f60ce5cea2041c7bb6b56 100644 (file)
@@ -327,6 +327,11 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused)
        unsigned int            current_mode;
        unsigned long           flags;
        u32                     reg;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        reg = dwc3_readl(dwc->regs, DWC3_GSTS);
@@ -345,6 +350,8 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused)
        }
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -390,6 +397,11 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
        struct dwc3             *dwc = s->private;
        unsigned long           flags;
        u32                     reg;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -409,6 +421,8 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
                seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
        }
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -458,6 +472,11 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
        struct dwc3             *dwc = s->private;
        unsigned long           flags;
        u32                     reg;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -488,6 +507,8 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
                seq_printf(s, "UNKNOWN %d\n", reg);
        }
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -504,6 +525,7 @@ static ssize_t dwc3_testmode_write(struct file *file,
        unsigned long           flags;
        u32                     testmode = 0;
        char                    buf[32];
+       int                     ret;
 
        if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
                return -EFAULT;
@@ -521,10 +543,16 @@ static ssize_t dwc3_testmode_write(struct file *file,
        else
                testmode = 0;
 
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
+
        spin_lock_irqsave(&dwc->lock, flags);
        dwc3_gadget_set_test_mode(dwc, testmode);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return count;
 }
 
@@ -543,12 +571,18 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
        enum dwc3_link_state    state;
        u32                     reg;
        u8                      speed;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        reg = dwc3_readl(dwc->regs, DWC3_GSTS);
        if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
                seq_puts(s, "Not available\n");
                spin_unlock_irqrestore(&dwc->lock, flags);
+               pm_runtime_put_sync(dwc->dev);
                return 0;
        }
 
@@ -561,6 +595,8 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
                   dwc3_gadget_hs_link_string(state));
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -579,6 +615,7 @@ static ssize_t dwc3_link_state_write(struct file *file,
        char                    buf[32];
        u32                     reg;
        u8                      speed;
+       int                     ret;
 
        if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
                return -EFAULT;
@@ -598,10 +635,15 @@ static ssize_t dwc3_link_state_write(struct file *file,
        else
                return -EINVAL;
 
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
+
        spin_lock_irqsave(&dwc->lock, flags);
        reg = dwc3_readl(dwc->regs, DWC3_GSTS);
        if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
                spin_unlock_irqrestore(&dwc->lock, flags);
+               pm_runtime_put_sync(dwc->dev);
                return -EINVAL;
        }
 
@@ -611,12 +653,15 @@ static ssize_t dwc3_link_state_write(struct file *file,
        if (speed < DWC3_DSTS_SUPERSPEED &&
            state != DWC3_LINK_STATE_RECOV) {
                spin_unlock_irqrestore(&dwc->lock, flags);
+               pm_runtime_put_sync(dwc->dev);
                return -EINVAL;
        }
 
        dwc3_gadget_set_link_state(dwc, state);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return count;
 }
 
@@ -640,6 +685,11 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
        unsigned long           flags;
        u32                     mdwidth;
        u32                     val;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
@@ -652,6 +702,8 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -662,6 +714,11 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
        unsigned long           flags;
        u32                     mdwidth;
        u32                     val;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
@@ -674,6 +731,8 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -683,12 +742,19 @@ static int dwc3_tx_request_queue_show(struct seq_file *s, void *unused)
        struct dwc3             *dwc = dep->dwc;
        unsigned long           flags;
        u32                     val;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_TXREQQ);
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -698,12 +764,19 @@ static int dwc3_rx_request_queue_show(struct seq_file *s, void *unused)
        struct dwc3             *dwc = dep->dwc;
        unsigned long           flags;
        u32                     val;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_RXREQQ);
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -713,12 +786,19 @@ static int dwc3_rx_info_queue_show(struct seq_file *s, void *unused)
        struct dwc3             *dwc = dep->dwc;
        unsigned long           flags;
        u32                     val;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_RXINFOQ);
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -728,12 +808,19 @@ static int dwc3_descriptor_fetch_queue_show(struct seq_file *s, void *unused)
        struct dwc3             *dwc = dep->dwc;
        unsigned long           flags;
        u32                     val;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_DESCFETCHQ);
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -743,12 +830,19 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused)
        struct dwc3             *dwc = dep->dwc;
        unsigned long           flags;
        u32                     val;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_EVENTQ);
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -793,6 +887,11 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
        struct dwc3             *dwc = dep->dwc;
        unsigned long           flags;
        int                     i;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        if (dep->number <= 1) {
@@ -822,6 +921,8 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
 out:
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -834,6 +935,11 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
        u32                     lower_32_bits;
        u32                     upper_32_bits;
        u32                     reg;
+       int                     ret;
+
+       ret = pm_runtime_resume_and_get(dwc->dev);
+       if (ret < 0)
+               return ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number);
@@ -846,6 +952,8 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
        seq_printf(s, "0x%016llx\n", ep_info);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       pm_runtime_put_sync(dwc->dev);
+
        return 0;
 }
 
@@ -905,6 +1013,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
        dwc->regset->regs = dwc3_regs;
        dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
        dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
+       dwc->regset->dev = dwc->dev;
 
        root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root);
        dwc->debug_root = root;