summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.
*