summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2017-09-07 15:13:18 +0200
committerchrome-bot <chrome-bot@chromium.org>2017-09-07 15:01:04 -0700
commit096ea20ed18095a967e1829b6ae60b13453416be (patch)
tree80d40bbe0cd50766f758299fb9f906257a1127dd
parent8c9f9ad861a48903aef5bd923741e12aa802c65c (diff)
downloadchrome-ec-096ea20ed18095a967e1829b6ae60b13453416be.tar.gz
g: restore DATA PID after USB suspend/resume
In USB FS on a bulk/interrupt endpoint, the transactions normally toggles between DATA0 and DATA1 PIDs. After a USB suspend/resume cycle, we need to restart from the PID we were at before suspend. In our current code, when going to deep-sleep during USB suspend, we are re-initializing everything when the MCU restarts at each resume. So we set implicitly the PID to DATA0. The USB Hardware IP just silently discards the packet when the PID of an incoming OUT packet is not matching the expectation in the endpoint register. In order to preserve DATA PIDS, record the state of the PID toggling on each endpoint when going to deep-sleep and restore it during the USB initialization. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=b:38160821 TEST=manual, plug a HG proto2 on a Linux host machine and enable 'auto-suspend' for this USB device. Let it go to sleep and wake-it up by sending a U2FHID request. Repeat the process several times and see that the key answers every time (while it was failing after the second cycle before). Change-Id: I75e2cfc39f22483d9e9b32c5f8b887dbafc37108 Reviewed-on: https://chromium-review.googlesource.com/655238 Commit-Ready: Marius Schilder <mschilder@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Marius Schilder <mschilder@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--chip/g/idle.c9
-rw-r--r--chip/g/init_chip.h1
-rw-r--r--chip/g/registers.h4
-rw-r--r--chip/g/usb.c40
-rw-r--r--include/usb_api.h12
5 files changed, 55 insertions, 11 deletions
diff --git a/chip/g/idle.c b/chip/g/idle.c
index 78e4e9a1ff..0bff45da84 100644
--- a/chip/g/idle.c
+++ b/chip/g/idle.c
@@ -15,6 +15,7 @@
#include "system.h"
#include "task.h"
#include "timer.h"
+#include "usb_api.h"
#include "util.h"
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
@@ -124,13 +125,9 @@ static void prepare_to_sleep(void)
/* Make sure the usb clock is enabled */
clock_enable_module(MODULE_USB, 1);
- /*
- * Preserve some state prior to deep sleep. Pretty much all we
- * need is the device address, since everything else can be
- * reinitialized on resume.
- */
+ /* Preserve some state from USB hardware prior to deep sleep. */
if (!GREAD_FIELD(USB, PCGCCTL, RSTPDWNMODULE))
- GREG32(PMU, PWRDN_SCRATCH18) = GR_USB_DCFG;
+ usb_save_suspended_state();
/* Increment the deep sleep count */
GREG32(PMU, PWRDN_SCRATCH17) =
diff --git a/chip/g/init_chip.h b/chip/g/init_chip.h
index cd820e701d..0daff279b7 100644
--- a/chip/g/init_chip.h
+++ b/chip/g/init_chip.h
@@ -22,6 +22,7 @@
* SCRATCH16 - Indicator that firmware is running for debug purposes
* SCRATCH17 - deep sleep count
* SCRATCH18 - Preserving USB_DCFG through deep sleep
+ * SCRATCH19 - Preserving USB data sequencing PID through deep sleep
*
* PWRDN_SCRATCH 28 - 31 - Reserved for boot rom
*/
diff --git a/chip/g/registers.h b/chip/g/registers.h
index dd063894e7..de80896b70 100644
--- a/chip/g/registers.h
+++ b/chip/g/registers.h
@@ -506,7 +506,7 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer,
#define DXEPCTL_TXFNUM(n) ((n) << GC_USB_DIEPCTL1_TXFNUM_LSB)
#define DXEPCTL_STALL (1 << GC_USB_DIEPCTL0_STALL_LSB)
#define DXEPCTL_CNAK (1 << GC_USB_DIEPCTL0_CNAK_LSB)
-#define DXEPCTL_DPID (1 << GC_USB_DIEPCTL0_DPID_LSB)
+#define DXEPCTL_DPID (1 << GC_USB_DIEPCTL1_DPID_LSB)
#define DXEPCTL_SNAK (1 << GC_USB_DIEPCTL0_SNAK_LSB)
#define DXEPCTL_NAKSTS (1 << GC_USB_DIEPCTL0_NAKSTS_LSB)
#define DXEPCTL_EPENA (1 << GC_USB_DIEPCTL0_EPENA_LSB)
@@ -514,6 +514,8 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer,
#define DXEPCTL_USBACTEP (1 << GC_USB_DIEPCTL0_USBACTEP_LSB)
#define DXEPCTL_MPS64 (0 << GC_USB_DIEPCTL0_MPS_LSB)
#define DXEPCTL_MPS(cnt) ((cnt) << GC_USB_DIEPCTL1_MPS_LSB)
+#define DXEPCTL_SET_D0PID (1 << 28)
+#define DXEPCTL_SET_D1PID (1 << 29)
#define DXEPTSIZ_SUPCNT(n) ((n) << GC_USB_DOEPTSIZ0_SUPCNT_LSB)
#define DXEPTSIZ_PKTCNT(n) ((n) << GC_USB_DIEPTSIZ0_PKTCNT_LSB)
diff --git a/chip/g/usb.c b/chip/g/usb.c
index 5b3b50dbab..fbfcff17bd 100644
--- a/chip/g/usb.c
+++ b/chip/g/usb.c
@@ -1231,6 +1231,41 @@ void usb_disconnect(void)
configuration_value = 0;
}
+void usb_save_suspended_state(void)
+{
+ int i;
+ uint32_t pid = 0;
+
+ /* Record the state the DATA PIDs toggling on each endpoint. */
+ for (i = 1; i < USB_EP_COUNT; i++) {
+ if (GR_USB_DOEPCTL(i) & DXEPCTL_DPID)
+ pid |= (1 << i);
+ if (GR_USB_DIEPCTL(i) & DXEPCTL_DPID)
+ pid |= (1 << (i + 16));
+ }
+ /* Save the USB device address */
+ GREG32(PMU, PWRDN_SCRATCH18) = GR_USB_DCFG;
+ GREG32(PMU, PWRDN_SCRATCH19) = pid;
+
+}
+
+void usb_restore_suspended_state(void)
+{
+ int i;
+ uint32_t pid;
+
+ /* restore the USB device address (the DEVADDR field). */
+ GR_USB_DCFG = GREG32(PMU, PWRDN_SCRATCH18);
+ /* Restore the DATA PIDs on endpoints. */
+ pid = GREG32(PMU, PWRDN_SCRATCH19);
+ for (i = 1; i < USB_EP_COUNT; i++) {
+ GR_USB_DOEPCTL(i) = pid & (1 << i) ?
+ DXEPCTL_SET_D1PID : DXEPCTL_SET_D0PID;
+ GR_USB_DIEPCTL(i) = pid & (1 << (i + 16)) ?
+ DXEPCTL_SET_D1PID : DXEPCTL_SET_D0PID;
+ }
+}
+
void usb_init(void)
{
int i, resume;
@@ -1303,10 +1338,7 @@ void usb_init(void)
usb_disconnect();
if (resume)
- /* DEVADDR is preserved in the USB module during deep sleep,
- * but it doesn't show up in USB_DCFG on resume. If we don't
- * restore it manually too, it doesn't work. */
- GR_USB_DCFG = GREG32(PMU, PWRDN_SCRATCH18);
+ usb_restore_suspended_state();
else
/* Init: USB2 FS, Scatter/Gather DMA, DEVADDR = 0x00 */
GR_USB_DCFG |= DCFG_DEVSPD_FS48 | DCFG_DESCDMA;
diff --git a/include/usb_api.h b/include/usb_api.h
index 6a08916031..8778dcc54e 100644
--- a/include/usb_api.h
+++ b/include/usb_api.h
@@ -49,6 +49,18 @@ void usb_release(void);
int usb_is_suspended(void);
/*
+ * Preserve in non-volatile memory the state of the USB hardware registers
+ * which cannot be simply re-initialized when powered up again.
+ */
+void usb_save_suspended_state(void);
+
+/*
+ * Restore from non-volatile memory the state of the USB hardware registers
+ * which was lost by powering them down.
+ */
+void usb_restore_suspended_state(void);
+
+/*
* Tell the host to wake up. Does nothing if CONFIG_USB_REMOTE_WAKEUP is not
* defined.
*