Skip to content

Commit

Permalink
usb: dwc3: debugfs: Resume dwc3 before accessing registers
Browse files Browse the repository at this point in the history
commit 614ce6a 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: 72246da ("usb: Introduce DesignWare USB3 DRD Driver")
Cc: [email protected] #3.2: 30332ee: debugfs: regset32: Add Runtime PM support
Signed-off-by: Udipto Goswami <[email protected]>
Reviewed-by: Johan Hovold <[email protected]>
Tested-by: Johan Hovold <[email protected]>
Acked-by: Thinh Nguyen <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
Udipto Goswami authored and gregkh committed May 24, 2023
1 parent 720be29 commit c51a131
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions drivers/usb/dwc3/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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);
Expand All @@ -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;
}

Expand All @@ -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);
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand All @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit c51a131

Please sign in to comment.