[PATCH 4.14 062/267] usb: dwc3: debugfs: Properly print/set link state for HS

From: Greg Kroah-Hartman
Date: Mon Dec 16 2019 - 12:53:43 EST


From: Thinh Nguyen <thinh.nguyen@xxxxxxxxxxxx>

[ Upstream commit 0d36dede457873404becd7c9cb9d0f2bcfd0dcd9 ]

Highspeed device and below has different state names than superspeed and
higher. Add proper checks and printouts of link states for highspeed and
below.

Signed-off-by: Thinh Nguyen <thinhn@xxxxxxxxxxxx>
Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
drivers/usb/dwc3/debug.h | 29 +++++++++++++++++++++++++++++
drivers/usb/dwc3/debugfs.c | 19 +++++++++++++++++--
2 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 5e9c070ec8747..1b4c2f8bb3daf 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -124,6 +124,35 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
}
}

+/**
+ * dwc3_gadget_hs_link_string - returns highspeed and below link name
+ * @link_state: link state code
+ */
+static inline const char *
+dwc3_gadget_hs_link_string(enum dwc3_link_state link_state)
+{
+ switch (link_state) {
+ case DWC3_LINK_STATE_U0:
+ return "On";
+ case DWC3_LINK_STATE_U2:
+ return "Sleep";
+ case DWC3_LINK_STATE_U3:
+ return "Suspend";
+ case DWC3_LINK_STATE_SS_DIS:
+ return "Disconnected";
+ case DWC3_LINK_STATE_RX_DET:
+ return "Early Suspend";
+ case DWC3_LINK_STATE_RECOV:
+ return "Recovery";
+ case DWC3_LINK_STATE_RESET:
+ return "Reset";
+ case DWC3_LINK_STATE_RESUME:
+ return "Resume";
+ default:
+ return "UNKNOWN link state\n";
+ }
+}
+
/**
* dwc3_trb_type_string - returns TRB type as a string
* @type: the type of the TRB
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 4e09be80e59f9..0d6a6a168a7ec 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -436,13 +436,17 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
unsigned long flags;
enum dwc3_link_state state;
u32 reg;
+ u8 speed;

spin_lock_irqsave(&dwc->lock, flags);
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
state = DWC3_DSTS_USBLNKST(reg);
- spin_unlock_irqrestore(&dwc->lock, flags);
+ speed = reg & DWC3_DSTS_CONNECTSPD;

- seq_printf(s, "%s\n", dwc3_gadget_link_string(state));
+ seq_printf(s, "%s\n", (speed >= DWC3_DSTS_SUPERSPEED) ?
+ dwc3_gadget_link_string(state) :
+ dwc3_gadget_hs_link_string(state));
+ spin_unlock_irqrestore(&dwc->lock, flags);

return 0;
}
@@ -460,6 +464,8 @@ static ssize_t dwc3_link_state_write(struct file *file,
unsigned long flags;
enum dwc3_link_state state = 0;
char buf[32];
+ u32 reg;
+ u8 speed;

if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
@@ -480,6 +486,15 @@ static ssize_t dwc3_link_state_write(struct file *file,
return -EINVAL;

spin_lock_irqsave(&dwc->lock, flags);
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ speed = reg & DWC3_DSTS_CONNECTSPD;
+
+ if (speed < DWC3_DSTS_SUPERSPEED &&
+ state != DWC3_LINK_STATE_RECOV) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -EINVAL;
+ }
+
dwc3_gadget_set_link_state(dwc, state);
spin_unlock_irqrestore(&dwc->lock, flags);

--
2.20.1