1 /******************************************************************************
3 * Copyright (C) 2017 Xilinx, Inc. All rights reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * Use of the Software is limited solely to applications:
16 * (a) running on a Xilinx device, or
17 * (b) that interact with a Xilinx device through a bus or interconnect.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
24 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * Except as contained in this notice, the name of the Xilinx shall not be used
28 * in advertising or otherwise to promote the sale, use or other dealings in
29 * this Software without prior written authorization from Xilinx.
31 ******************************************************************************/
32 /****************************************************************************/
35 * @file xusbpsu_hibernation.c
37 * This patch adds hibernation support to usbpsu driver when dwc3 is operating
42 * MODIFICATION HISTORY:
44 * Ver Who Date Changes
45 * ----- ----- -------- -----------------------------------------------------
46 * 1.0 Mayank 12/01/18 First release
50 *****************************************************************************/
52 /***************************** Include Files ********************************/
55 #include "xusbpsu_hw.h"
56 #include "xusbpsu_endpoint.h"
58 /************************** Constant Definitions *****************************/
60 #define NUM_OF_NONSTICKY_REGS 27
62 #define XUSBPSU_HIBER_SCRATCHBUF_SIZE 4096U
64 #define XUSBPSU_NON_STICKY_SAVE_RETRIES 500U
65 #define XUSBPSU_PWR_STATE_RETRIES 1500U
66 #define XUSBPSU_CTRL_RDY_RETRIES 5000U
67 #define XUSBPSU_TIMEOUT 1000U
69 /**************************** Type Definitions *******************************/
72 /***************** Macros (Inline Functions) Definitions *********************/
75 /************************** Function Prototypes ******************************/
78 /************************** Variable Definitions *****************************/
80 static u8 ScratchBuf[XUSBPSU_HIBER_SCRATCHBUF_SIZE];
82 /* Registers saved during hibernation and restored at wakeup */
83 static u32 save_reg_addr[] = {
92 XUSBPSU_GTXFIFOSIZ(0),
93 XUSBPSU_GTXFIFOSIZ(1),
94 XUSBPSU_GTXFIFOSIZ(2),
95 XUSBPSU_GTXFIFOSIZ(3),
96 XUSBPSU_GTXFIFOSIZ(4),
97 XUSBPSU_GTXFIFOSIZ(5),
98 XUSBPSU_GTXFIFOSIZ(6),
99 XUSBPSU_GTXFIFOSIZ(7),
100 XUSBPSU_GTXFIFOSIZ(8),
101 XUSBPSU_GTXFIFOSIZ(9),
102 XUSBPSU_GTXFIFOSIZ(10),
103 XUSBPSU_GTXFIFOSIZ(11),
104 XUSBPSU_GTXFIFOSIZ(12),
105 XUSBPSU_GTXFIFOSIZ(13),
106 XUSBPSU_GTXFIFOSIZ(14),
107 XUSBPSU_GTXFIFOSIZ(15),
108 XUSBPSU_GRXFIFOSIZ(0),
109 XUSBPSU_GUSB3PIPECTL(0),
110 XUSBPSU_GUSB2PHYCFG(0),
112 static u32 saved_regs[NUM_OF_NONSTICKY_REGS];
114 /*****************************************************************************/
116 * Save non sticky registers
118 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
125 ******************************************************************************/
126 static void save_regs(struct XUsbPsu *InstancePtr)
130 for (i = 0; i < NUM_OF_NONSTICKY_REGS; i++)
131 saved_regs[i] = XUsbPsu_ReadReg(InstancePtr, save_reg_addr[i]);
134 /*****************************************************************************/
136 * Restore non sticky registers
138 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
145 ******************************************************************************/
146 static void restore_regs(struct XUsbPsu *InstancePtr)
150 for (i = 0; i < NUM_OF_NONSTICKY_REGS; i++)
151 XUsbPsu_WriteReg(InstancePtr, save_reg_addr[i], saved_regs[i]);
154 /*****************************************************************************/
156 * Send generic command for gadget
158 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
160 * @param cmd is command to be sent
161 * @param param is parameter for the command, to be written in DGCMDPAR
165 * - XST_SUCCESS on success
166 * - XST_FAILURE on timeout
167 * - XST_REGISTER_ERROR on status error
171 ******************************************************************************/
172 s32 XUsbPsu_SendGadgetGenericCmd(struct XUsbPsu *InstancePtr, u32 cmd,
175 u32 RegVal, retry = 500;
176 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_DGCMDPAR, param);
177 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_DGCMD, cmd | XUSBPSU_DGCMD_CMDACT);
180 RegVal = XUsbPsu_ReadReg(InstancePtr, XUSBPSU_DGCMD);
181 if (!(RegVal & XUSBPSU_DGCMD_CMDACT)) {
182 if (XUSBPSU_DGCMD_STATUS(RegVal))
183 return XST_REGISTER_ERROR;
191 /*****************************************************************************/
193 * Sets scratchpad buffers
195 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
198 * @return XST_SUCCESS on success or else error code
202 ******************************************************************************/
203 s32 XUsbPsu_SetupScratchpad(struct XUsbPsu *InstancePtr)
206 Ret = XUsbPsu_SendGadgetGenericCmd(InstancePtr,
207 XUSBPSU_DGCMD_SET_SCRATCHPAD_ADDR_LO, (UINTPTR)ScratchBuf & 0xffffffff);
209 xil_printf("Failed to set scratchpad low addr: %d\n", Ret);
213 Ret = XUsbPsu_SendGadgetGenericCmd(InstancePtr,
214 XUSBPSU_DGCMD_SET_SCRATCHPAD_ADDR_HI, ((UINTPTR)ScratchBuf >> 16) >> 16);
216 xil_printf("Failed to set scratchpad high addr: %d\n", Ret);
223 /*****************************************************************************/
225 * Initialize to handle hibernation event when it comes
227 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
234 ******************************************************************************/
235 void XUsbPsu_InitHibernation(struct XUsbPsu *InstancePtr)
239 InstancePtr->IsHibernated = 0;
241 memset(ScratchBuf, 0, sizeof(ScratchBuf));
242 if (InstancePtr->ConfigPtr->IsCacheCoherent == 0)
243 Xil_DCacheFlushRange((INTPTR)ScratchBuf, XUSBPSU_HIBER_SCRATCHBUF_SIZE);
245 XUsbPsu_SetupScratchpad(InstancePtr);
247 /* enable PHY suspend */
248 RegVal = XUsbPsu_ReadReg(InstancePtr, XUSBPSU_GUSB2PHYCFG(0));
249 RegVal |= XUSBPSU_GUSB2PHYCFG_SUSPHY;
250 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_GUSB2PHYCFG(0), RegVal);
252 RegVal = XUsbPsu_ReadReg(InstancePtr, XUSBPSU_GUSB3PIPECTL(0));
253 RegVal |= XUSBPSU_GUSB3PIPECTL_SUSPHY;
254 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_GUSB3PIPECTL(0), RegVal);
257 /*****************************************************************************/
259 * Handle hibernation event
261 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
268 ******************************************************************************/
269 void Xusbpsu_HibernationIntr(struct XUsbPsu *InstancePtr)
274 XusbPsuLinkState LinkState;
277 switch(XUsbPsu_GetLinkState(InstancePtr)) {
278 case XUSBPSU_LINK_STATE_SS_DIS:
279 case XUSBPSU_LINK_STATE_U3:
282 /* fake hiber interrupt */
283 xil_printf("got fake interrupt\r\n");
287 if (InstancePtr->Ep0State == XUSBPSU_EP0_SETUP_PHASE) {
288 XUsbPsu_StopTransfer(InstancePtr, 0, XUSBPSU_EP_DIR_OUT, TRUE);
289 XUsbPsu_RecvSetup(InstancePtr);
292 /* stop active transfers for all endpoints including control
293 * endpoints force rm bit should be 0 when we do this */
294 for (EpNum = 0; EpNum < XUSBPSU_ENDPOINTS_NUM; EpNum++) {
295 struct XUsbPsu_Ep *Ept;
297 Ept = &InstancePtr->eps[EpNum];
301 if (!(Ept->EpStatus & XUSBPSU_EP_ENABLED))
304 /* save srsource index for later use */
305 XUsbPsu_StopTransfer(InstancePtr, Ept->UsbEpNum,
306 Ept->Direction, FALSE);
308 XUsbPsu_SaveEndpointState(InstancePtr, Ept);
312 * ack events, don't process them; h/w decrements the count by the value
315 RegVal = XUsbPsu_ReadReg(InstancePtr, XUSBPSU_GEVNTCOUNT(0));
316 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_GEVNTCOUNT(0), RegVal);
317 InstancePtr->Evt.Count = 0;
318 InstancePtr->Evt.Flags &= ~XUSBPSU_EVENT_PENDING;
320 if (XUsbPsu_Stop(InstancePtr)) {
321 xil_printf("Failed to stop USB core\r\n");
325 RegVal = XUsbPsu_ReadReg(InstancePtr, XUSBPSU_DCTL);
327 /* Check the link state and if it is disconnected, set
330 LinkState = XUsbPsu_GetLinkState(InstancePtr);
331 if (LinkState == XUSBPSU_LINK_STATE_SS_DIS) {
332 RegVal &= ~XUSBPSU_DCTL_KEEP_CONNECT;
333 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_DCTL, RegVal);
335 /* update LinkState to be used while wakeup */
336 InstancePtr->LinkState = XUSBPSU_LINK_STATE_SS_DIS;
339 save_regs(InstancePtr);
341 /* ask core to save state */
342 RegVal |= XUSBPSU_DCTL_CSS;
343 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_DCTL, RegVal);
345 /* wait till core saves */
346 if (XUsbPsu_Wait_Clear_Timeout(InstancePtr, XUSBPSU_DSTS,
347 XUSBPSU_DSTS_SSS, XUSBPSU_NON_STICKY_SAVE_RETRIES) == XST_FAILURE) {
348 xil_printf("Failed to save core state\r\n");
352 /* Enable PME to wakeup from hibernation */
353 XUsbPsu_WriteVendorReg(XIL_PME_ENABLE, XIL_PME_ENABLE_SIG_GEN);
355 /* change power state to D3 */
356 XUsbPsu_WriteVendorReg(XIL_REQ_PWR_STATE, XIL_REQ_PWR_STATE_D3);
358 /* wait till current state is changed to D3 */
359 retries = XUSBPSU_PWR_STATE_RETRIES;
361 RegVal = XUsbPsu_ReadVendorReg(XIL_CUR_PWR_STATE);
362 if ((RegVal & XIL_CUR_PWR_STATE_BITMASK) == XIL_CUR_PWR_STATE_D3)
365 XUsbSleep(XUSBPSU_TIMEOUT);
369 xil_printf("Failed to change power state to D3\r\n");
372 XUsbSleep(XUSBPSU_TIMEOUT);
374 RegVal = XUsbPsu_ReadLpdReg(RST_LPD_TOP);
375 if (InstancePtr->ConfigPtr->DeviceId == XPAR_XUSBPSU_0_DEVICE_ID)
376 XUsbPsu_WriteLpdReg(RST_LPD_TOP, RegVal | USB0_CORE_RST);
378 InstancePtr->IsHibernated = 1;
379 xil_printf("Hibernated!\r\n");
382 /*****************************************************************************/
384 * Restarts transfer for active endpoint
386 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
388 * @param EpNum is an endpoint number.
390 * @return XST_SUCCESS on success or else XST_FAILURE.
394 ******************************************************************************/
395 static s32 XUsbPsu_RestartEp(struct XUsbPsu *InstancePtr, u8 EpNum)
397 struct XUsbPsu_EpParams *Params;
398 struct XUsbPsu_Trb *TrbPtr;
399 struct XUsbPsu_Ep *Ept;
403 Xil_AssertNonvoid(InstancePtr != NULL);
405 Params = XUsbPsu_GetEpParams(InstancePtr);
406 Xil_AssertNonvoid(Params != NULL);
408 Ept = &InstancePtr->eps[EpNum];
410 /* check if we need to restart transfer */
411 if (!Ept->ResourceIndex && Ept->PhyEpNum)
415 TrbPtr = &Ept->EpTrb[Ept->TrbDequeue];
417 TrbPtr = &InstancePtr->Ep0_Trb;
420 Xil_AssertNonvoid(TrbPtr != NULL);
422 TrbPtr->Ctrl |= XUSBPSU_TRB_CTRL_HWO;
424 if (InstancePtr->ConfigPtr->IsCacheCoherent == 0) {
425 Xil_DCacheFlushRange((INTPTR)TrbPtr, sizeof(struct XUsbPsu_Trb));
426 Xil_DCacheInvalidateRange((INTPTR)Ept->BufferPtr, Ept->RequestedBytes);
430 Params->Param1 = (UINTPTR)TrbPtr;
432 Cmd = XUSBPSU_DEPCMD_STARTTRANSFER;
434 Ret = XUsbPsu_SendEpCmd(InstancePtr, Ept->UsbEpNum, Ept->Direction,
439 Ept->EpStatus |= XUSBPSU_EP_BUSY;
440 Ept->ResourceIndex = (u8)XUsbPsu_EpGetTransferIndex(InstancePtr,
441 Ept->UsbEpNum, Ept->Direction);
446 /*****************************************************************************/
448 * Restarts EP0 endpoint
450 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
453 * @return XST_SUCCESS on success or else XST_FAILURE.
457 ******************************************************************************/
458 static s32 XUsbPsu_RestoreEp0(struct XUsbPsu *InstancePtr)
460 struct XUsbPsu_Ep *Ept;
464 for (EpNum = 0; EpNum < 2; EpNum++) {
465 Ept = &InstancePtr->eps[EpNum];
470 if (!(Ept->EpStatus & XUSBPSU_EP_ENABLED))
473 Ret = XUsbPsu_EpEnable(InstancePtr, Ept->UsbEpNum,
474 Ept->Direction, Ept->MaxSize, Ept->Type, TRUE);
476 xil_printf("Failed to enable EP %d on wakeup: %d\r\n",
481 if (Ept->EpStatus & XUSBPSU_EP_STALL) {
482 XUsbPsu_Ep0StallRestart(InstancePtr);
484 Ret = XUsbPsu_RestartEp(InstancePtr, Ept->PhyEpNum);
486 xil_printf("Failed to restart EP %d on wakeup: %d\r\n",
496 /*****************************************************************************/
498 * Restarts non EP0 endpoints
500 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
503 * @return XST_SUCCESS on success or else XST_FAILURE.
507 ******************************************************************************/
508 static s32 XUsbPsu_RestoreEps(struct XUsbPsu *InstancePtr)
510 struct XUsbPsu_Ep *Ept;
514 for (EpNum = 2; EpNum < XUSBPSU_ENDPOINTS_NUM; EpNum++) {
515 Ept = &InstancePtr->eps[EpNum];
520 if (!(Ept->EpStatus & XUSBPSU_EP_ENABLED))
523 Ret = XUsbPsu_EpEnable(InstancePtr, Ept->UsbEpNum,
524 Ept->Direction, Ept->MaxSize, Ept->Type, TRUE);
526 xil_printf("Failed to enable EP %d on wakeup: %d\r\n",
532 for (EpNum = 2; EpNum < XUSBPSU_ENDPOINTS_NUM; EpNum++) {
533 Ept = &InstancePtr->eps[EpNum];
538 if (!(Ept->EpStatus & XUSBPSU_EP_ENABLED))
541 if (Ept->EpStatus & XUSBPSU_EP_STALL) {
542 XUsbPsu_EpSetStall(InstancePtr, Ept->UsbEpNum, Ept->Direction);
544 Ret = XUsbPsu_RestartEp(InstancePtr, Ept->PhyEpNum);
546 xil_printf("Failed to restart EP %d on wakeup: %d\r\n",
556 /*****************************************************************************/
558 * Handle wakeup event
560 * @param InstancePtr is a pointer to the XUsbPsu instance to be worked
567 ******************************************************************************/
568 void XUsbPsu_WakeupIntr(struct XUsbPsu *InstancePtr)
570 u32 RegVal, link_state;
574 RegVal = XUsbPsu_ReadLpdReg(RST_LPD_TOP);
575 if (InstancePtr->ConfigPtr->DeviceId == XPAR_XUSBPSU_0_DEVICE_ID)
576 XUsbPsu_WriteLpdReg(RST_LPD_TOP, RegVal & ~USB0_CORE_RST);
578 /* change power state to D0 */
579 XUsbPsu_WriteVendorReg(XIL_REQ_PWR_STATE, XIL_REQ_PWR_STATE_D0);
581 /* wait till current state is changed to D0 */
582 retries = XUSBPSU_PWR_STATE_RETRIES;
584 RegVal = XUsbPsu_ReadVendorReg(XIL_CUR_PWR_STATE);
585 if ((RegVal & XIL_CUR_PWR_STATE_BITMASK) == XIL_CUR_PWR_STATE_D0)
588 XUsbSleep(XUSBPSU_TIMEOUT);
592 xil_printf("Failed to change power state to D0\r\n");
596 /* ask core to restore non-sticky registers */
597 XUsbPsu_SetupScratchpad(InstancePtr);
599 RegVal = XUsbPsu_ReadReg(InstancePtr, XUSBPSU_DCTL);
600 RegVal |= XUSBPSU_DCTL_CRS;
601 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_DCTL, RegVal);
603 /* wait till non-sticky registers are restored */
604 if (XUsbPsu_Wait_Clear_Timeout(InstancePtr, XUSBPSU_DSTS,
605 XUSBPSU_DSTS_RSS, XUSBPSU_NON_STICKY_SAVE_RETRIES) == XST_FAILURE) {
606 xil_printf("Failed to restore USB core\r\n");
610 restore_regs(InstancePtr);
612 /* setup event buffers */
613 XUsbPsu_EventBuffersSetup(InstancePtr);
615 /* nothing to do when in OTG host mode */
616 if (XUsbPsu_ReadReg(InstancePtr, XUSBPSU_GSTS) & XUSBPSU_GSTS_CUR_MODE)
619 if (XUsbPsu_RestoreEp0(InstancePtr)) {
620 xil_printf("Failed to restore EP0\r\n");
624 /* start controller */
625 if (XUsbPsu_Start(InstancePtr)) {
626 xil_printf("Failed to start core on wakeup\r\n");
630 /* Wait until device controller is ready */
631 if (XUsbPsu_Wait_Clear_Timeout(InstancePtr, XUSBPSU_DSTS,
632 XUSBPSU_DSTS_DCNRD, XUSBPSU_CTRL_RDY_RETRIES) == XST_FAILURE) {
633 xil_printf("Failed to ready device controller\r\n");
638 * there can be suprious wakeup events , so wait for some time and check
641 XUsbSleep(XUSBPSU_TIMEOUT * 10);
643 link_state = XUsbPsu_GetLinkState(InstancePtr);
646 case XUSBPSU_LINK_STATE_RESET:
647 RegVal = XUsbPsu_ReadReg(InstancePtr, XUSBPSU_DSTS);
648 RegVal &= ~XUSBPSU_DCFG_DEVADDR_MASK;
649 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_DSTS, RegVal);
651 if (XUsbPsu_SetLinkState(InstancePtr, XUSBPSU_LINK_STATE_RECOV)) {
652 xil_printf("Failed to put link in Recovery\r\n");
656 case XUSBPSU_LINK_STATE_SS_DIS:
657 RegVal = XUsbPsu_ReadReg(InstancePtr, XUSBPSU_DCTL);
658 RegVal &= ~XUSBPSU_DCTL_KEEP_CONNECT;
659 XUsbPsu_WriteReg(InstancePtr, XUSBPSU_DCTL, RegVal);
662 case XUSBPSU_LINK_STATE_U3:
663 /* enter hibernation again */
667 if (XUsbPsu_SetLinkState(InstancePtr, XUSBPSU_LINK_STATE_RECOV)) {
668 xil_printf("Failed to put link in Recovery\r\n");
674 if (XUsbPsu_RestoreEps(InstancePtr)) {
675 xil_printf("Failed to restore EPs\r\n");
679 InstancePtr->IsHibernated = 0;
682 Xusbpsu_HibernationIntr(InstancePtr);
686 xil_printf("We are back from hibernation!\r\n");