diff mbox series

[v9] usb: dwc3: debugfs: Prevent any register access when devices is runtime suspended

Message ID 20230505155103.30098-1-quic_ugoswami@quicinc.com
State New
Headers show
Series [v9] usb: dwc3: debugfs: Prevent any register access when devices is runtime suspended | expand

Commit Message

Udipto Goswami May 5, 2023, 3:51 p.m. UTC
When the dwc3 device is runtime suspended, various required clocks would
get disabled 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: 62ba09d6bb63 ("usb: dwc3: debugfs: Dump internal LSP and ep registers")
Cc: stable@vger.kernel.org
Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com>
---
v9: Fixed function dwc3_rx_fifo_size_show & return values in function
	dwc3_link_state_write along with minor changes for code symmetry.
v8: Replace pm_runtime_get_sync with pm_runtime_resume_and get.
v7: Replaced pm_runtime_put with pm_runtime_put_sync & returned proper values.
v6: Added changes to handle get_dync failure appropriately.
v5: Reworked the patch to resume dwc3 while accessing the registers.
v4: Introduced pm_runtime_get_if_in_use in order to make sure dwc3 isn't
	suspended while accessing the registers.
v3: Replace pr_err to dev_err. 
v2: Replaced return 0 with -EINVAL & seq_puts with pr_err.

 drivers/usb/dwc3/debugfs.c | 109 +++++++++++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)

Comments

Thinh Nguyen May 6, 2023, 1:30 a.m. UTC | #1
On Fri, May 05, 2023, Udipto Goswami wrote:
> When the dwc3 device is runtime suspended, various required clocks would
> get disabled 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: 62ba09d6bb63 ("usb: dwc3: debugfs: Dump internal LSP and ep registers")

This fix goes before the above change.

This also touches on many places and is more than 100 lines. While this
is a fix, I'm not sure if Cc stable is needed. Perhaps others can
comment.

Thanks,
Thinh

> Cc: stable@vger.kernel.org
> Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com>
> ---
> v9: Fixed function dwc3_rx_fifo_size_show & return values in function
> 	dwc3_link_state_write along with minor changes for code symmetry.
> v8: Replace pm_runtime_get_sync with pm_runtime_resume_and get.
> v7: Replaced pm_runtime_put with pm_runtime_put_sync & returned proper values.
> v6: Added changes to handle get_dync failure appropriately.
> v5: Reworked the patch to resume dwc3 while accessing the registers.
> v4: Introduced pm_runtime_get_if_in_use in order to make sure dwc3 isn't
> 	suspended while accessing the registers.
> v3: Replace pr_err to dev_err. 
> v2: Replaced return 0 with -EINVAL & seq_puts with pr_err.
> 
>  drivers/usb/dwc3/debugfs.c | 109 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 109 insertions(+)
> 
> diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
> index e4a2560b9dc0..ebf03468fac4 100644
> --- a/drivers/usb/dwc3/debugfs.c
> +++ b/drivers/usb/dwc3/debugfs.c
> @@ -332,6 +332,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);
> @@ -350,6 +355,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;
>  }
>  
> @@ -395,6 +402,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);
> @@ -414,6 +426,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;
>  }
>  
> @@ -463,6 +477,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);
> @@ -493,6 +512,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;
>  }
>  
> @@ -509,6 +530,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;
> @@ -526,10 +548,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;
>  }
>  
> @@ -548,12 +576,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;
>  	}
>  
> @@ -566,6 +600,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;
>  }
>  
> @@ -584,6 +620,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;
> @@ -603,10 +640,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;
>  	}
>  
> @@ -616,12 +658,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;
>  }
>  
> @@ -645,6 +690,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);
> @@ -657,6 +707,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;
>  }
>  
> @@ -667,6 +719,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);
> @@ -679,6 +736,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;
>  }
>  
> @@ -688,12 +747,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;
>  }
>  
> @@ -703,12 +769,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;
>  }
>  
> @@ -718,12 +791,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;
>  }
>  
> @@ -733,12 +813,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;
>  }
>  
> @@ -748,12 +835,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;
>  }
>  
> @@ -798,6 +892,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) {
> @@ -827,6 +926,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;
>  }
>  
> @@ -839,6 +940,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);
> @@ -851,6 +957,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;
>  }
>  
> @@ -910,6 +1018,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;
> -- 
> 2.17.1
>
Johan Hovold May 8, 2023, 11:34 a.m. UTC | #2
On Sat, May 06, 2023 at 01:30:52AM +0000, Thinh Nguyen wrote:

Udipto, looks like you just ignored my comment about fixing up the patch
Subject.

> On Fri, May 05, 2023, Udipto Goswami wrote:
> > When the dwc3 device is runtime suspended, various required clocks would
> > get disabled 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: 62ba09d6bb63 ("usb: dwc3: debugfs: Dump internal LSP and ep registers")
> 
> This fix goes before the above change.

Yes, this clearly is not the commit that first introduced this issue.

Either add a Fixes tag for the oldest one or add one for each commit
that introduced debugfs attributes with this issues.

> This also touches on many places and is more than 100 lines. While this
> is a fix, I'm not sure if Cc stable is needed. Perhaps others can
> comment.

I believe this should be backported as it fixes a crash/hang.

The stable rules are flexible, but it may also be possible to break the
patch up in pieces and add a corresponding Fixes tag.

Johan
Udipto Goswami May 8, 2023, 3:48 p.m. UTC | #3
On 5/8/23 5:04 PM, Johan Hovold wrote:
> On Sat, May 06, 2023 at 01:30:52AM +0000, Thinh Nguyen wrote:
> 
> Udipto, looks like you just ignored my comment about fixing up the patch
> Subject.

Hi Johan,

Apologies for this, i missed the comment on the Subject will rectify it.

> 
>> On Fri, May 05, 2023, Udipto Goswami wrote:
>>> When the dwc3 device is runtime suspended, various required clocks would
>>> get disabled 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: 62ba09d6bb63 ("usb: dwc3: debugfs: Dump internal LSP and ep registers")
>>
>> This fix goes before the above change.
> 
> Yes, this clearly is not the commit that first introduced this issue.
> 
> Either add a Fixes tag for the oldest one or add one for each commit
> that introduced debugfs attributes with this issues.
> 
>> This also touches on many places and is more than 100 lines. While this
>> is a fix, I'm not sure if Cc stable is needed. Perhaps others can
>> comment.
> 
> I believe this should be backported as it fixes a crash/hang.
> 
> The stable rules are flexible, but it may also be possible to break the
> patch up in pieces and add a corresponding Fixes tag.

Agree, I will add a fixes tag for the oldest change that introduced the 
debugfs attributes instead of breaking it to multiple patches and adding 
fixes for each one. (I think the present code changes can stay in one 
patch as we are fixing the same issue in all the functions).

Let me know if you think otherwise?

Thanks,
-Udipto
Thinh Nguyen May 8, 2023, 10:26 p.m. UTC | #4
On Mon, May 08, 2023, Johan Hovold wrote:
> 
> The stable rules are flexible, but it may also be possible to break the
> patch up in pieces and add a corresponding Fixes tag.
> 
> Johan

In this case, I'd prefer to keep it all in a single patch to easily
track the change. Anyone who needs this for the older kernel can rework
the upstream version for backport.

Thanks,
Thinh
Johan Hovold May 9, 2023, 6:30 a.m. UTC | #5
On Mon, May 08, 2023 at 09:18:17PM +0530, Udipto Goswami wrote:

> > I believe this should be backported as it fixes a crash/hang.
> > 
> > The stable rules are flexible, but it may also be possible to break the
> > patch up in pieces and add a corresponding Fixes tag.
> 
> Agree, I will add a fixes tag for the oldest change that introduced the 
> debugfs attributes instead of breaking it to multiple patches and adding 
> fixes for each one. (I think the present code changes can stay in one 
> patch as we are fixing the same issue in all the functions).
> 
> Let me know if you think otherwise?

Sounds good to me. Note that the fix depends on 

	30332eeefec8 ("debugfs: regset32: Add Runtime PM support")

which went into 5.7.

This can be documented as

	Cc: stable@vger.kernel.org	# 3.2: 30332eeefec8: debugfs: regset32: Add Runtime PM support

(see Documentation/process/stable-kernel-rules.rst).

Note that this issue appears to have been there since the driver was
first merged in 3.2.

Johan
diff mbox series

Patch

diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index e4a2560b9dc0..ebf03468fac4 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -332,6 +332,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);
@@ -350,6 +355,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;
 }
 
@@ -395,6 +402,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);
@@ -414,6 +426,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;
 }
 
@@ -463,6 +477,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);
@@ -493,6 +512,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;
 }
 
@@ -509,6 +530,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;
@@ -526,10 +548,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;
 }
 
@@ -548,12 +576,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;
 	}
 
@@ -566,6 +600,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;
 }
 
@@ -584,6 +620,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;
@@ -603,10 +640,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;
 	}
 
@@ -616,12 +658,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;
 }
 
@@ -645,6 +690,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);
@@ -657,6 +707,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;
 }
 
@@ -667,6 +719,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);
@@ -679,6 +736,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;
 }
 
@@ -688,12 +747,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;
 }
 
@@ -703,12 +769,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;
 }
 
@@ -718,12 +791,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;
 }
 
@@ -733,12 +813,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;
 }
 
@@ -748,12 +835,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;
 }
 
@@ -798,6 +892,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) {
@@ -827,6 +926,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;
 }
 
@@ -839,6 +940,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);
@@ -851,6 +957,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;
 }
 
@@ -910,6 +1018,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;