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, bail out of debugfs function is dwc3 is suspended. Signed-off-by: Oliver Neukum <oneukum@xxxxxxxx> Signed-off-by: Udipto Goswami <quic_ugoswami@xxxxxxxxxxx> --- v4: Introduced pm_runtime_get_if_in_use in order to make sure dwc3 isn't suspended while accessing the registers. drivers/usb/dwc3/debugfs.c | 191 ++++++++++++++++++++++++++++++++----- 1 file changed, 169 insertions(+), 22 deletions(-) diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 850df0e6bcab..e57cafb7da4b 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -543,13 +543,25 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) enum dwc3_link_state state; u32 reg; u8 speed; + int ret = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } 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); - return 0; + goto err; } reg = dwc3_readl(dwc->regs, DWC3_DSTS); @@ -559,9 +571,11 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) seq_printf(s, "%s\n", (speed >= DWC3_DSTS_SUPERSPEED) ? dwc3_gadget_link_string(state) : dwc3_gadget_hs_link_string(state)); +err: spin_unlock_irqrestore(&dwc->lock, flags); - - return 0; + pm_runtime_put(dwc->dev); +err_nolock: + return ret; } static int dwc3_link_state_open(struct inode *inode, struct file *file) @@ -579,6 +593,20 @@ static ssize_t dwc3_link_state_write(struct file *file, char buf[32]; u32 reg; u8 speed; + int ret = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; @@ -601,8 +629,8 @@ static ssize_t dwc3_link_state_write(struct file *file, 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); - return -EINVAL; + count = -EINVAL; + goto err; } reg = dwc3_readl(dwc->regs, DWC3_DSTS); @@ -610,13 +638,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); - return -EINVAL; + count = -EINVAL; + goto err; } dwc3_gadget_set_link_state(dwc, state); +err: spin_unlock_irqrestore(&dwc->lock, flags); - + pm_runtime_put(dwc->dev); +err_nolock: return count; } @@ -640,6 +670,19 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) unsigned long flags; u32 mdwidth; u32 val; + int ret = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_TXFIFO); @@ -650,9 +693,11 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused) val *= mdwidth; val >>= 3; seq_printf(s, "%u\n", val); - spin_unlock_irqrestore(&dwc->lock, flags); - return 0; + spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put(dwc->dev); +err_nolock: + return ret; } static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) @@ -662,6 +707,19 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) unsigned long flags; u32 mdwidth; u32 val; + int ret = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } spin_lock_irqsave(&dwc->lock, flags); val = dwc3_core_fifo_space(dep, DWC3_RXFIFO); @@ -673,8 +731,9 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused) val >>= 3; seq_printf(s, "%u\n", val); spin_unlock_irqrestore(&dwc->lock, flags); - - return 0; + pm_runtime_put(dwc->dev); +err_nolock: + return ret; } static int dwc3_tx_request_queue_show(struct seq_file *s, void *unused) @@ -683,13 +742,27 @@ 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 = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } 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); - - return 0; + pm_runtime_put(dwc->dev); +err_nolock: + return ret; } static int dwc3_rx_request_queue_show(struct seq_file *s, void *unused) @@ -698,13 +771,27 @@ 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 = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } 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); - - return 0; + pm_runtime_put(dwc->dev); +err_nolock: + return ret; } static int dwc3_rx_info_queue_show(struct seq_file *s, void *unused) @@ -713,13 +800,28 @@ 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 = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } 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(dwc->dev); +err_nolock: + return ret; - return 0; } static int dwc3_descriptor_fetch_queue_show(struct seq_file *s, void *unused) @@ -728,13 +830,29 @@ 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 = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } 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(dwc->dev); +err_nolock: + return ret; + - return 0; } static int dwc3_event_queue_show(struct seq_file *s, void *unused) @@ -743,13 +861,28 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused) struct dwc3 *dwc = dep->dwc; unsigned long flags; u32 val; + int ret = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } 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(dwc->dev); +err_nolock: + return ret; - return 0; } static int dwc3_transfer_type_show(struct seq_file *s, void *unused) @@ -834,6 +967,19 @@ 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 = 0; + + ret = pm_runtime_get_if_in_use(dwc->dev); + /* check if pm runtime get fails, bail out early */ + if (ret < 0) + goto err_nolock; + + if (!ret) { + ret = -EINVAL; + dev_err(dwc->dev, + "Invalid operation, DWC3 suspended!"); + goto err_nolock; + } spin_lock_irqsave(&dwc->lock, flags); reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number); @@ -845,8 +991,9 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused) ep_info = ((u64)upper_32_bits << 32) | lower_32_bits; seq_printf(s, "0x%016llx\n", ep_info); spin_unlock_irqrestore(&dwc->lock, flags); - - return 0; + pm_runtime_put(dwc->dev); +err_nolock: + return ret; } DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_size); -- 2.17.1