diff options
-rw-r--r-- | chip/g/idle.c | 9 | ||||
-rw-r--r-- | chip/g/init_chip.h | 1 | ||||
-rw-r--r-- | chip/g/registers.h | 4 | ||||
-rw-r--r-- | chip/g/usb.c | 40 | ||||
-rw-r--r-- | include/usb_api.h | 12 |
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. * |