/* * drvhostlib.c- Sigmastar * * Copyright (C) 2018 Sigmastar Technology Corp. * * Author: jiang.ann * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include "hal/platform.h" #include "inc/common.h" #include "inc/drvhostlib.h" #ifdef USB_LIB_DEBUG #define USB_LIB_PRINTF MS_MSG #else #define USB_LIB_PRINTF(fmt, ...) #endif #ifdef USB_LIB_BULK_DEBUG #define USB_LIB_BULK_PRINTF MS_MSG #else #define USB_LIB_BULK_PRINTF(fmt, ...) #endif #define Scsi_Max_Transfer_Len 16*1024 #define wTimeWaitForConnect_ms 9500 #ifdef USB_EMBEDDED_STANDARD_COMMAND MS_UINT8 OTGH_GETDESCRIPTOR_DEVICE_8[] = {0x80,0x06,0x00,0x01,0x00,0x00,0x08,0x00}; MS_UINT8 OTGH_GETDESCRIPTOR_DEVICE[] = {0x80,0x06,0x00,0x01,0x00,0x00,0x12,0x00}; MS_UINT8 OTGH_GETDESCRIPTOR_CONFIG[] = {0x80,0x06,0x00,0x02,0x00,0x00,0x08,0x00}; MS_UINT8 OTGH_SETADDRESS[] = {0x00,0x05,0x03,0x00,0x00,0x00,0x00,0x00}; MS_UINT8 OTGH_SETCONFIGURATION[] = {0x00,0x09,0x01,0x00,0x00,0x00,0x00,0x00}; MS_UINT8 OTGH_SET_FEATURE_OTG[] = {0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00}; /* Set Device Descriptor */ MS_UINT8 OTGH_SETDESCRIPTOR_DEVICE[] = {0x00,0x07,0x00,0x01,0x00,0x00,0x12,0x00}; #endif MS_UINT8 QtdBuf[ Host20_qTD_SIZE * Host20_qTD_MAX + 0x20*4] __attribute__ ((aligned (128))); qHD_Structure Host20_qHD_List_Control0 __attribute__ ((aligned (128))); qHD_Structure Host20_qHD_List_Control1 __attribute__ ((aligned (128))); qHD_Structure Host20_qHD_List_Bulk0 __attribute__ ((aligned (128))); qHD_Structure Host20_qHD_List_Bulk1 __attribute__ ((aligned (128))); qHD_Structure Host20_qHD_List_Intr __attribute__ ((aligned (128))); Periodic_Frame_List_Structure Host20_FramList __attribute__ ((aligned (4096))); static MS_UINT16 waIntervalMap[11]={1,2,4,8,16,32,64,128,256,512,1024}; MS_UINT8 Host20_qTD_Manage[Host20_qTD_MAX]; // 1=>Free 2=>used MS_UINT8 UsbCtrlBuf[0x100] __attribute__ ((aligned (128))); MS_UINT8 UsbDataBuf[0x100] __attribute__ ((aligned (128))); /* debug functions for dump QTD & QH data */ void Dump_QTD(MS_UINT32 addr); void Dump_Data(MS_UINT32 addr, MS_UINT16 sz); // TODO: FIXME or REMOVE IT #if defined(USB_BUF_ALIGNED_PATCH) #define ALIGNED_BUF_SIZE 24 * 1024 __declspec( align(4096) ) MS_UINT8 usb_temp_buf[ALIGNED_BUF_SIZE]; #endif #if defined(CONFIG_USB_BOUNCE_BUF_PATCH) #define BOUNCE_BUF_SIZE 24 * 1024 MS_UINT8 usb_bounce_buf[BOUNCE_BUF_SIZE] __attribute__ ((aligned (4096))); #endif static struct ehci_hcd ms_ehci; MS_U32 MsOS_USB_PA2KSEG1(MS_U32 addr) { //return ((MS_U32)(((MS_U32)addr) | 0x80000000)); return addr; } MS_U32 MsOS_USB_PA2KSEG0(MS_U32 addr) { //return ((MS_U32)(((MS_U32)addr) | 0x40000000)); return addr; } MS_U32 MsOS_USB_VA2PA(MS_U32 addr) { //return ((MS_U32)(((MS_U32)addr) & 0x1fffffff)); return addr; } /* ==================================================================== * Function Name: flib_Host20_Bulk_Init * Description: * Input: none * OutPut: none * ==================================================================== */ void flib_Host20_Bulk_Init(struct usb_device *dev, struct usb_hcd *hcd) { struct usb_endpoint_descriptor *ep,*ep2; struct ehci_hcd *ehci = hcd->ehci; MS_UINT8 ii; ep = &dev->config.if_desc[0].ep_desc[0]; ep2 = &dev->config.if_desc[0].ep_desc[1]; USB_LIB_PRINTF("[USB] no_of_ep: %d\n", dev->config.if_desc[0].no_of_ep); for (ii=0; iiconfig.if_desc[0].no_of_ep; ii++) { if (dev->config.if_desc[0].ep_desc[ii].bmAttributes == OTGH_ED_BULK) { USB_LIB_PRINTF("[USB] find bulk ep: %d\n", ii); ep=&dev->config.if_desc[0].ep_desc[ii]; ii++; break; } } for (;iiconfig.if_desc[0].no_of_ep; ii++) { if (dev->config.if_desc[0].ep_desc[ii].bmAttributes == OTGH_ED_BULK) { USB_LIB_PRINTF("[USB] find bulk ep2: %d\n", ii); ep2=&dev->config.if_desc[0].ep_desc[ii]; break; } } USB_LIB_PRINTF("\n[USB] bulk max packet size: ep(%s) 0x%x, ep2(%s) 0x%x\n", (ep->bEndpointAddress & 0x80) ? "in" : "out", ep->wMaxPacketSize, (ep2->bEndpointAddress & 0x80) ? "in" : "out", ep2->wMaxPacketSize); /* <5>.Hook the qHD */ if ((ep->bmAttributes)==OTGH_ED_BULK) { /* <5.1>.stop Asynchronous Schedule */ flib_Host20_Asynchronous_Setting(hcd, HOST20_Disable); /* <5.2>.Hook the qHD for ED0~ED3 */ /* Circle the QH: CtrQH -> Bulk0QH -> Bulk1QH -> CtrQH */ ehci->qh_control1->bNextQHDPointer = (VA2PA((MS_UINT32)ehci->qh_bulk0) >> 5); ehci->qh_bulk0->bNextQHDPointer = (VA2PA((MS_UINT32)ehci->qh_bulk1) >> 5); ehci->qh_bulk1->bNextQHDPointer = (VA2PA((MS_UINT32)ehci->qh_control0) >> 5); #if (_USB_FLUSH_BUFFER == 1) /* make sure QH chain modification has flushed to mem */ Chip_Flush_Memory(); #endif /* <5.2>.Enable Asynchronous Schedule */ flib_Host20_Asynchronous_Setting(hcd, HOST20_Enable); } #if 0 // moved to ubootglue.c FirstBulkIn = 1; FirstBulkOut = 1; #endif ehci->spBulkInqTD = NULL; /* Setting Max Packet Size */ ehci->qh_bulk0->bMaxPacketSize = ep->wMaxPacketSize; //wMaxPacketSize; ehci->qh_bulk1->bMaxPacketSize = ep2->wMaxPacketSize; //wMaxPacketSize; /* set device address */ ehci->qh_bulk0->bDeviceAddress = dev->devnum; ehci->qh_bulk1->bDeviceAddress = dev->devnum; /* set endpoint# address */ ehci->qh_bulk0->bEdNumber = (ep->bEndpointAddress) & 0x0f; ehci->qh_bulk1->bEdNumber = (ep2->bEndpointAddress) & 0x0f; //set endpoint address ehci->ep_bulk0_dir = (((ep->bEndpointAddress) & 0x80) > 0) ? OTGH_Dir_IN : OTGH_Dir_Out; USB_LIB_PRINTF("[USB] bulk0 is %s\n", ehci->ep_bulk0_dir ? "in" : "out"); } /* ==================================================================== * Function Name: flib_Host20_Interrupt_Init * Description: * //Reserve=> <1>.If Periodic Enable => Disable Periodic * <2>.Disable all the Frame List (Terminal=1) * <3>.Hang the Interrupt-qHD-1 to Frame List * * Input: wInterval=1~16 => Full Speed => 1ms ~ 32 sec * High Speed => 125us ~ 40.96 sec * OutPut: * ==================================================================== */ void flib_Host20_Interrupt_Init(struct usb_device *dev, struct usb_hcd *hcd) { MS_UINT32 i,j; MS_UINT32 wSize; MS_UINT8 x; MS_UINT8 *pData; MS_UINT16 wForceInterval = 0; struct usb_interface *iface; struct ehci_hcd *ehci = hcd->ehci; MS_UINT32 ehci_base = hcd->ehc_base; iface = &dev->config.if_desc[0]; /* <1>.Disable the Periodic */ flib_Host20_Periodic_Setting(hcd, HOST20_Disable); /* <2>.Init qHD for Interrupt(7~9) Scan the ED */ x = mwHost20Port(ehci_base,0x10); x = (x&0xF3)|((MS_UINT8)HOST20_USBCMD_FrameListSize_256<<2); mwHost20Port_wr(ehci_base,0x10, x); for (i=0; i < (iface->desc.bNumEndpoints); i++) { /* that is an interrupt endpoint */ if (iface->ep_desc[i].bmAttributes==OTGH_ED_INT) { wSize = iface->ep_desc[i].wMaxPacketSize; wForceInterval = iface->ep_desc[i].bInterval; ehci->qh_intr =(qHD_Structure*)KSEG02KSEG1(&Host20_qHD_List_Intr); flush_cache((ulong)&Host20_qHD_List_Intr, sizeof(qHD_Structure)); pData=(MS_UINT8*)ehci->qh_intr; for (j = 0; (j < sizeof(qHD_Structure)) && (jqh_intr, HOST20_HD_Type_QH, dev->devnum, 0, (i+1), wSize); ehci->qh_intr->bHighBandwidth = 1; ehci->qh_intr->bInterruptScheduleMask = 1; ehci->qh_intr->bEdSpeed = hcd->bSpeed; ehci->qh_intr->bDataToggleControl = 0; /* interrupt schedule */ ehci->qh_intr->bInterruptScheduleMask = 1; /* 1~3, MULTI =1 */ ehci->qh_intr->bHighBandwidth = 1; /* support only one interrupt pipe */ break; } } /* point to itself */ ehci->qh_intr->bNextQHDPointer = (VA2PA((MS_UINT32)ehci->qh_intr) >> 5); /* terminated */ ehci->qh_intr->bTerminate = 1; ehci->spIntInqTD = NULL; ehci->framelist = (Periodic_Frame_List_Structure*) KSEG02KSEG1(&Host20_FramList); flush_cache((ulong)&Host20_FramList, sizeof(Periodic_Frame_List_Structure)); pData=(MS_UINT8 *)ehci->framelist; for (j=0;j.Link qHD to the FameListCell by wInterval */ for (i = 0; i < Host20_Preiodic_Frame_List_MAX; i++) ehci->framelist->sCell[i].bTerminal = 1; /* Find the Interval-X */ x = 0; while(waIntervalMap[x] 10) { MS_MSG("[USB] Interval Input Error...\n"); return; } }; for (i = 0; i < Host20_Preiodic_Frame_List_MAX; i = i + waIntervalMap[x]) { ehci->framelist->sCell[i].bLinkPointer = (VA2PA((MS_UINT32)(ehci->qh_intr))>>5); ehci->framelist->sCell[i].bTerminal = 0; ehci->framelist->sCell[i].bType = HOST20_HD_Type_QH; } /* <4>.Set Periodic Base Address */ mwHost20_PeriodicBaseAddr_Set(hcd->ehc_base, VA2PA((MS_UINT32)ehci->framelist)); /* <5>.Enable the periodic */ flib_Host20_Periodic_Setting(hcd, HOST20_Enable); } /* ==================================================================== * Function Name: flib_Host20_Periodic_Setting * Description: * Input: * OutPut: * ==================================================================== */ void flib_Host20_Periodic_Setting(struct usb_hcd *hcd, MS_UINT8 bOption) { MS_UINT32 ehci_base = hcd->ehc_base; MS_UINT32 polling_count = 0; if (bOption == HOST20_Enable) { /* <1>.If Already enable => return */ if (mwHost20_USBSTS_PeriodicStatus_Rd(ehci_base) > 0) return ; /* <2>.Disable Periodic */ mbHost20_USBCMD_PeriodicEnable_Set(ehci_base); /* <3>.Polling Status */ while (polling_count++ < 16 * 125) { if (mwHost20_USBSTS_PeriodicStatus_Rd(ehci_base) > 0) { return; /* succeed */ } udelay(1); /* delay 1us */ } /* timeout: 16 microframes */ MS_MSG("[USB] timeout when starting periodic schedule\n"); } else if (bOption == HOST20_Disable) { /* <1>.If Already Disable => return */ if (mwHost20_USBSTS_PeriodicStatus_Rd(ehci_base) == 0) return ; /* <2>.Enable Periodic */ mbHost20_USBCMD_PeriodicEnable_Clr(ehci_base); /* <3>.Polling Status */ while (polling_count++ < 16 * 125) { if (mwHost20_USBSTS_PeriodicStatus_Rd(ehci_base) == 0) { return; /* succeed */ } udelay(1); /* delay 1us */ } /* timeout: 16 microframes */ MS_MSG("[USB] timeout when stopping periodic schedule\n"); } else { MS_MSG("[USB] ??? Input Error 'flib_Host20_Periodic_Setting'...\n"); } } /* ==================================================================== * Function Name: flib_Host20_Issue_Interrupt * Description: * Input: * OutPut: * ==================================================================== */ MS_UINT8 flib_Host20_Issue_Interrupt(struct usb_hcd *hcd, MS_UINT32 pwBuffer, MS_UINT16 hwSize) { qTD_Structure *spTempqTD; struct ehci_hcd *ehci = hcd->ehci; MS_UINT32 workbuf = 0; MS_UINT8 bResult; if ( pwBuffer != (MS_UINT32) KSEG02KSEG1(pwBuffer) ) { flush_cache((ulong)pwBuffer,hwSize); //flush buffer to uncached buffer #if (_USB_FLUSH_BUFFER == 1) Chip_Flush_Memory(); #endif pwBuffer=(MS_UINT32)KSEG02KSEG1(pwBuffer); } /* <1>.Fill TD */ /* The qTD will be release in the function "Send" */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL) return HOST20_FATAL; workbuf = pwBuffer; spTempqTD->bTotalBytes=hwSize; /* cache issue, if cache enable */ spTempqTD->ArrayBufferPointer_Word[0] = VA2PA(workbuf); spTempqTD->ArrayBufferPointer_Word[1] = 0; spTempqTD->ArrayBufferPointer_Word[2] = 0; spTempqTD->ArrayBufferPointer_Word[3] = 0; spTempqTD->ArrayBufferPointer_Word[4] = 0; /* <2>.Analysis the Direction */ spTempqTD->bPID=HOST20_qTD_PID_IN; /* <3>.Send TD */ bResult=flib_Host20_Send_qTD2(hcd, spTempqTD, ehci->qh_intr, 10); return bResult; } /* ==================================================================== * Function Name: flib_Host20_Issue_Interrupt_NonBlock * Description: * Input: * OutPut: * ==================================================================== */ MS_UINT8 flib_Host20_Issue_Interrupt_NonBlock(struct usb_hcd *hcd, MS_UINT32 pwBuffer, MS_UINT16 hwSize, MS_U32 *actual_len) { qTD_Structure *spTempqTD; struct ehci_hcd *ehci = hcd->ehci; MS_UINT32 workbuf = 0; MS_UINT8 bResult=HOST20_OK; if (mwHost20_PORTSC_ConnectStatus_Rd(hcd->ehc_base) == 0) { MS_MSG("[USB] Port%d device disconn\n", hcd->port_index); bResult = HOST20_FATAL; hcd->urb_status |= USB_ST_DISCONNECT; flib_Host20_Periodic_Setting(hcd, HOST20_Disable); ehci->qh_intr->bOverlay_Status=0; //clear halt status flib_Host20_ReleaseStructure(hcd, Host20_MEM_TYPE_qTD, (MS_UINT32)ehci->spIntInqTD); return bResult; } if (hcd->FirstIntIn == 1) { // If periodic still running then return, else release first qtd point if (ehci->spIntInqTD->bStatus_Active==1) hcd->IntrIn_Complete = 0; else { hcd->IntrIn_Complete = 1; hcd->FirstIntIn = 0; *actual_len = (MS_UINT32)(hwSize - (MS_UINT16)ehci->spIntInqTD->bTotalBytes); flib_Host20_ReleaseStructure(hcd, Host20_MEM_TYPE_qTD, (MS_UINT32)ehci->spIntInqTD); } return bResult; } hcd->IntrIn_Complete = 0; hcd->FirstIntIn = 1; ehci->spIntInqTD = (qTD_Structure*)PA2VA((( MS_UINT32)(ehci->qh_intr->bOverlay_NextqTD))<<5); if ( pwBuffer != (MS_UINT32) KSEG02KSEG1(pwBuffer) ) { flush_cache((ulong)pwBuffer,hwSize); //flush buffer to uncached buffer #if (_USB_FLUSH_BUFFER == 1) Chip_Flush_Memory(); #endif pwBuffer=(MS_UINT32)KSEG02KSEG1(pwBuffer); } workbuf = pwBuffer; /* <1>.Fill TD */ /* The qTD will be release in the function "Send" */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); spTempqTD->bTotalBytes=hwSize; /* cache issue, if cache enable */ spTempqTD->ArrayBufferPointer_Word[0] = VA2PA(workbuf); spTempqTD->ArrayBufferPointer_Word[1] = 0; spTempqTD->ArrayBufferPointer_Word[2] = 0; spTempqTD->ArrayBufferPointer_Word[3] = 0; spTempqTD->ArrayBufferPointer_Word[4] = 0; /* <2>.Analysis the Direction */ spTempqTD->bPID=HOST20_qTD_PID_IN; /* <3>.Send TD */ bResult=flib_Host20_Send_qTD_Intr(hcd, spTempqTD, ehci->qh_intr); return bResult; } /* ********************************************************************************* * ********************************************************************************* * *** Group-1:Main Function *** * ********************************************************************************* * ********************************************************************************* */ unsigned char mwHost20Port(int addr, int bOffset) { if (bOffset & 1) return(*((MS_UINT8 volatile *) ( addr + bOffset*2 - 1))); else /* even */ return(*((MS_UINT8 volatile *) ( addr + bOffset*2))); } void mwHost20Port_wr(int addr, int bOffset, int value) { if (bOffset & 1) (*((MS_UINT8 volatile *) ( addr + bOffset*2 -1))) = value; else /* even */ (*((MS_UINT8 volatile *) ( addr + bOffset*2))) = value; } /* bOffset should be 32 bits alignment */ void mwHost20Portw(int addr, int bOffset, int value) { *((MS_UINT32 volatile *)(addr + bOffset*2)) = value & 0xffff; *((MS_UINT32 volatile *)(addr + bOffset*2 + 4)) = (value >> 16) & 0xffff; } int mwHost20Bit_Rd(int addr, int bByte, int wBitNum) { return (mwHost20Port(addr, bByte) & wBitNum); } void mwHost20Bit_Set(int addr, int bByte, int wBitNum) { MS_UINT8 temp; temp = mwHost20Port(addr, bByte); temp |= wBitNum; mwHost20Port_wr(addr, bByte, temp); } void mwHost20Bit_Clr(int addr, int bByte, int wBitNum) { MS_UINT8 temp; temp = mwHost20Port(addr, bByte); temp &= ~wBitNum; mwHost20Port_wr(addr, bByte,temp); } /* ==================================================================== * Function Name: flib_Host20_Init * Description: Init the Host HW and prepare the ED/TD * <1>.Init All the Data Structure * <1.1>.Build control list * <1.2>.Build Bulk list * <1.3>.Build Interrupt list * <1.4>.Build ISO list (Reserved) * <2>.Reset the chip * <3>.Set HW register * <3.1>.Enable FIFO-Enable(0x100->Bit5) & FPGA-Mode-Half-Speed (0x100->Bit1) * <3.2>.Enable interrupts * <3.3>.Periodic List Base Address register * <3.4>.USBCMD (Interrupt/Threshod/Frame List/Run-Stop) * * Input: wTimeWaitForConnect_ms:The time of waitting for connecting * OutPut: 0:Device do not connect * 1:Host init ok * 2:Bus Rest Fail * ==================================================================== */ MS_UINT8 flib_Host20_Init(struct usb_hcd *hcd, MS_UINT8 wForDevice_B, MS_UINT16 wDelay) { MS_UINT8 wValue = 0; MS_UINT16 wTimer_ms = 0; MS_UINT32 ehci_base = hcd->ehc_base; MS_UINT32 utmi_base = hcd->utmi_base; USB_DELAY(wDelay); if (mwHost20_PORTSC_ConnectStatus_Rd(ehci_base) == 0) { MS_MSG("[USB] No USB is connecting\n"); return (0); } /* <1>.Waiting for the Device connect */ if (wForDevice_B == 0) { /* host controller reset */ flib_Host20_Reset_HC(hcd); } wValue = 0; wTimer_ms = 0; do { wValue = mwHost20_PORTSC_ConnectStatus_Rd(ehci_base); if (wValue==0) { /* 10, wait 1 ms */ USB_DELAY(1); wTimer_ms++; } /* Case1:Waiting for 10 sec=10000 */ /* Case2:Waiting for 100 ms =100 */ if (wTimer_ms > wTimeWaitForConnect_ms) { MS_MSG("[USB] ??? Waiting for Peripheral Connecting Fail...\n"); return (0); } }while(wValue == 0); mwHost20_Misc_EOF1Time_Set(ehci_base,Host20_EOF1Time); /* <2>.Init All the Data Structure & write Base Address register */ flib_Host20_InitStructure(hcd); /* Write Base Address to Register */ USB_LIB_PRINTF("[USB] Async base addr: 0x%x \n", hcd->ehci->async); mwHost20_CurrentAsynchronousAddr_Set(ehci_base, hcd->ehci->async); USB_LIB_PRINTF("[USB] Reg 0x28: 0x%lx 0x%lx\n", *((MS_UINT32 volatile *)(ehci_base + 0x28*2)), *((MS_UINT32 volatile *)(ehci_base + 0x28*2 + 4))); if (wForDevice_B == 0) { if (flib_Host20_PortBusReset(hcd) > 0) return(2); } if (hcd->bSpeed == 0) /* full speed */ { mwHost20Bit_Clr(utmi_base, 0x9, MS_BIT7); mwHost20_Misc_EOF1Time_Set(ehci_base, 2); } else if (hcd->bSpeed == 2) /* high speed */ { /* HS rx robust enable */ mwHost20Bit_Set(utmi_base, 0x9, MS_BIT7); mwHost20_Misc_EOF1Time_Set(ehci_base, 3); } flib_Host20_QHD_Control_Init(hcd); return (1); } /* ==================================================================== * Function Name: flib_Host20_Close * Description: * <1>.Suspend Host * <2>.Disable the interrupt * <3>.Clear Interrupt Status * <4>.Issue HW Reset * <5>.Free the Allocated Memory * Input: * OutPut: * ==================================================================== */ MS_UINT8 flib_Host20_Close(struct usb_hcd *hcd, MS_UINT8 bForDeviceB) { MS_UINT32 wTemp; MS_UINT32 ehci_base = hcd->ehc_base; if (mwHost20_USBINTR_Rd(ehci_base) > 0) { /* <1>.Suspend Host */ if (bForDeviceB == 0) { flib_Host20_Suspend(hcd); } else flib_Host20_StopRun_Setting(hcd, HOST20_Disable); /* <2>.Disable the interrupt */ mwHost20_USBINTR_Set(ehci_base, 0); /* <3>.Clear Interrupt Status */ wTemp = mwHost20_USBSTS_Rd(ehci_base); wTemp = wTemp & 0x0000003F; mwHost20_USBSTS_Set(ehci_base, wTemp); } return (1); } inline void flib_Host20_Fix_DataLength_ByteAlign(struct usb_hcd *hcd) { mwHost20Bit_Set(hcd->ehc_base, 0x81, MS_BIT6); } /* ==================================================================== * Function Name: flib_Host20_StopRun_Setting * Description: * Input: * OutPut: * ==================================================================== */ void flib_Host20_StopRun_Setting(struct usb_hcd *hcd, MS_UINT8 bOption) { MS_UINT32 ehci_base = hcd->ehc_base; MS_UINT32 polling_count = 0; if (bOption == HOST20_Enable) { if (mbHost20_USBCMD_RunStop_Rd(ehci_base) > 0) return; mbHost20_USBCMD_RunStop_Set(ehci_base); while (polling_count++ < 16 * 125) { if (mwHost20_USBSTS_HCHalted_Rd(ehci_base) == 0) { return; /* succeed */ } udelay(1); /* delay 1us */ } /* timeout: 16 microframes */ MS_MSG("[USB] timeout when starting HC\n"); } else if (bOption == HOST20_Disable) { if (mbHost20_USBCMD_RunStop_Rd(ehci_base) == 0) return; mbHost20_USBCMD_RunStop_Clr(ehci_base); while (polling_count++ < 16 * 125) { if (mwHost20_USBSTS_HCHalted_Rd(ehci_base) > 0) { return; /* succeed */ } udelay(1); /* delay 1us */ } /* timeout: 16 microframes */ MS_MSG("[USB] timeout when stopping HC\n"); } else { MS_MSG("[USB] ??? Input Error 'flib_Host20_StopRun_Setting'...\n"); MS_MSG("[USB] USBCMD's Run/Stop bit remains unchanged\n"); } } /* ==================================================================== * Function Name: flib_Host20_Reset_HC * Description: * Input: * OutPut: * ==================================================================== */ void flib_Host20_Reset_HC(struct usb_hcd *hcd) { MS_UINT32 ehci_base = hcd->ehc_base; MS_UINT32 tmp; /* host controller reset */ mbHost20_USBCMD_HCReset_Set(ehci_base); tmp = 0; while(mbHost20_USBCMD_HCReset_Rd(ehci_base)>0) { tmp++; USB_DELAY(1); /* 100ms timeout*/ if(tmp > 100) { MS_MSG("[USB] can not reset UHC\n"); break; } } } /* ==================================================================== * Function Name: flib_Host20_Asynchronous_Setting * Description: * Input: * OutPut: * ==================================================================== */ void flib_Host20_Asynchronous_Setting(struct usb_hcd *hcd, MS_UINT8 bOption) { MS_UINT32 ehci_base = hcd->ehc_base; MS_UINT32 polling_count = 0; if (bOption == HOST20_Enable) { if (mwHost20_USBSTS_AsynchronousStatus_Rd(ehci_base) > 0) return; mbHost20_USBCMD_AsynchronousEnable_Set(ehci_base); while (polling_count++ < 16 * 125) { if (mwHost20_USBSTS_AsynchronousStatus_Rd(ehci_base) > 0) { return; /* succeed */ } udelay(1); /* delay 1us */ } /* timeout: 16 microframes */ MS_MSG("[USB] timeout when starting asynchronous schedule\n"); } else if (bOption==HOST20_Disable) { if (mwHost20_USBSTS_AsynchronousStatus_Rd(ehci_base) == 0) return; mbHost20_USBCMD_AsynchronousEnable_Clr(ehci_base); while (polling_count++ < 16 * 125) { if (mwHost20_USBSTS_AsynchronousStatus_Rd(ehci_base) == 0) { return; /* succeed */ } udelay(1); /* delay 1us */ } /* timeout: 16 microframes */ MS_MSG("[USB] timeout when stopping asynchronous schedule\n"); } else { MS_MSG("[USB] ??? Input Error 'flib_Host20_Asynchronous_Setting'...\n"); // while(1); } } #if defined( USB_BAD_DEVICE_RETRY_PATCH ) extern MS_BOOL force_FS; void flib_Host20_ForceSpeed(struct usb_hcd *hcd, MS_U32 port) { MS_UINT32 ehci_base = hcd->ehc_base; MS_MSG("[USB] flib_Host20_ForceSpeed ++\n"); writeb(readb(ehci_base+0x40*2) | 0x80, ehci_base+0x40*2); /* force full speed */ writeb(readb(ehci_base+0x34*2) | 0x40, ehci_base+0x34*2); MS_MSG("[USB] Force HS Reg: %x\n", readb(ehci_base+0x40*2)); } #endif /* ==================================================================== * Function Name: flib_Host20_PortBusReset * Description: * <1>.Waiting for HCHalted=0 * <2>.Write PortEnable=0(Reserved for Faraday-IP) * <3>.Write PortReset=0 * <4>.Wait time * <5>.Write PortReset=0 * <6>.Waiting for IRS->PortChangeDetect * Input: * OutPut: * ==================================================================== */ MS_UINT8 flib_Host20_PortBusReset(struct usb_hcd *hcd) { MS_UINT32 wTmp; MS_UINT32 ehci_base = hcd->ehc_base; MS_UINT32 utmi_base = hcd->utmi_base; #if defined( USB_BAD_DEVICE_RETRY_PATCH ) if(force_FS == TRUE) flib_Host20_ForceSpeed(hcd, hcd->port_index); #endif /* <1>.Disable RunStop */ USB_LIB_PRINTF("[USB] disable run\n"); if (mbHost20_USBCMD_RunStop_Rd(ehci_base) > 0) flib_Host20_StopRun_Setting(hcd, HOST20_Disable); /* <2>.Write PortReset=0 */ mwHost20Portw(utmi_base, 0x2C, 0x00000010); mwHost20Port_wr(utmi_base, 0x2A, 0x80); mwHost20_PORTSC_PortReset_Set(ehci_base); /* <3>.Total Wait time=>70ms */ USB_DELAY(50); mwHost20Port_wr(utmi_base, 0x2A, UTMI_DISCON_LEVEL_2A); USB_DELAY(20); // flib_Debug_LED_Off_All();; /* GPIO-High */ /* <4>.Write PortReset=0 */ mwHost20_PORTSC_PortReset_Clr(ehci_base); mwHost20Portw(utmi_base, 0x2C, UTMI_ALL_EYE_SETTING); /* <5>.Waiting for IRS->PortChangeDetect */ wTmp = 0; while (1) { if (mwHost20_PORTSC_PortReset_Rd(ehci_base) == 0) break; wTmp++; if (wTmp > 20000) { MS_MSG("[USB] ??? Error waiting for Bus Reset Fail...==> Reset HW Control\n"); flib_Host20_Reset_HC(hcd); return (1); } } #if 0 if (mwHost20_PORTSC_ForceSuspend_Rd()) { USB_LIB_PRINTF("[USB] port suspend\n"); mwHost20_PORTSC_ForceResume_Set(); /* force resume */ USBDELAY(14); mwHost20_PORTSC_ForceResume_Clr(); } #endif /* UTMI TX/RX Reset */ mwHost20Bit_Set(utmi_base, 0x6, MS_BIT0 | MS_BIT1); mwHost20Bit_Clr(utmi_base, 0x6, MS_BIT0 | MS_BIT1); /* <6>.Enable RunStop Bit */ flib_Host20_StopRun_Setting(hcd, HOST20_Enable); /* wait some slow device to be ready */ USB_DELAY(5); /* <7>.Detect Speed */ hcd->bSpeed= mwOTG20_Control_HOST_SPD_TYP_Rd(ehci_base); MS_MSG ("[USB] Host Speed:%x \n", hcd->bSpeed); /* <8>.Delay 20 ms ?? */ return (0); } /* ==================================================================== * Function Name: flib_Host20_Suspend * Description: * <1>.Make sure PortEnable=1 * <2>.Write PORTSC->Suspend=1 * <3>.Waiting for the ISR->PORTSC->Suspend=1 * Input: * OutPut: 0:OK * 1:Fail * ==================================================================== */ MS_UINT8 flib_Host20_Suspend(struct usb_hcd *hcd) { MS_UINT32 ehci_base = hcd->ehc_base; if (mbHost20_USBCMD_RunStop_Rd(ehci_base) == 0) return(1); /* <1>.Make sure PortEnable=1 */ if (mwHost20_PORTSC_EnableDisable_Rd(ehci_base) == 0) return(1); /* <2>.Write PORTSC->Suspend=1 */ /* For Faraday HW request */ flib_Host20_StopRun_Setting(hcd, HOST20_Disable); /* <3>.Write PORTSC->Suspend=1 */ mwHost20_PORTSC_ForceSuspend_Set(ehci_base); /* <4>.Waiting for the ISR->PORTSC->Suspend=1 */ #if 0 flib_Host20_TimerEnable_UnLock(1); /* 1sec */ bExitFlag = 0; do { if (mwHost20_PORTSC_ForceSuspend_Rd() > 0) bExitFlag = 1; if (gwOTG_Timer_Counter>5) { bExitFlag = 1; MS_MSG("[USB] >>> Fail => Time Out for Waiting ForceSuspend...\n"); } } while(bExitFlag==0); #else while(mwHost20_PORTSC_ForceSuspend_Rd(ehci_base)==0); #endif return (0); } /* ==================================================================== * Function Name: flib_Host20_Issue_Control * Description: * <1>.Analysis the Controller Command => 3 type * <2>.Case-1:"Setup/In/Out' Format..." * (get status/get descriptor/get configuration/get interface) * <3>.Case-2:'Setup/In' Format... => Faraday Driver will not need * (clear feature/set feature/set address/set Configuration/set interface ) * <4>.Case-3:'Setup/Out/In' * (set descriptor) * Input: * OutPut: 0: OK * X: >0 => Fail * ==================================================================== */ MS_UINT8 flib_Host20_Issue_Control (struct usb_hcd *hcd, MS_UINT8 bEdNum, MS_UINT8* pbCmd, MS_UINT16 hwDataSize, MS_UINT8* pbData) { qTD_Structure *spTempqTD; MS_UINT8 bReturnValue, bOneMore=1, bIsInput; qHD_Structure *qh_ptr; struct ehci_hcd *ehci = hcd->ehci; if (bEdNum == 0) qh_ptr = ehci->qh_control0; else { ehci->qh_control1->bDeviceAddress = bEdNum; qh_ptr = ehci->qh_control1; } /* <0>.Allocate qTD & Data Buffer */ /* 0=>qTD */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL){ return HOST20_FATAL; } /* <2.1>.Setup packet */ /* .Fill qTD */ spTempqTD->bPID = HOST20_qTD_PID_SETUP; /* Bit8~9 */ spTempqTD->bTotalBytes = 8; /* Bit16~30 */ spTempqTD->bDataToggle = 0; /* Bit31 */ memcpy(ehci->ep0_buffer, pbCmd, 8); spTempqTD->ArrayBufferPointer_Word[0] = VA2PA((MS_UINT32)ehci->ep0_buffer); /* debug dump QTD data */ // Dump_QTD(spTempqTD); // Dump_Data(XXX, XXX); /* .Send qTD */ bReturnValue= flib_Host20_Send_qTD(hcd, spTempqTD, qh_ptr, Host20_Ctrl_Timeout); if (bReturnValue > 0) goto exit_issue_control; /* <1>.Analysis the Controller Command */ #if 1 bIsInput = usb_pipein(*pbCmd); if (hwDataSize > 0) /* Data Stage */ { /* .Fill qTD */ /* 0=>qTD */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL){ return HOST20_FATAL; } if (bIsInput) spTempqTD->bPID = HOST20_qTD_PID_IN; /* Bit8~9 */ else { spTempqTD->bPID = HOST20_qTD_PID_OUT; /* Bit8~9 */ memcpy(ehci->ep0_buffer, pbData, hwDataSize); } spTempqTD->bTotalBytes = hwDataSize; /* Bit16~30 */ spTempqTD->bDataToggle = 1; /* Bit31 */ spTempqTD->ArrayBufferPointer_Word[0] = VA2PA((MS_UINT32)ehci->ep0_buffer); /* .Send qTD */ bReturnValue = flib_Host20_Send_qTD(hcd, spTempqTD, qh_ptr,Host20_Ctrl_Timeout); if (bReturnValue>0) goto exit_issue_control; /* .Waiting for result */ if (bIsInput) memcpy((MS_UINT8 *)pbData, ehci->ep0_buffer, hwDataSize); // Dump_Data((MS_UINT16)pbData, hwDataSize); } // Send short packet if (bOneMore) { spTempqTD=flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL){ return HOST20_FATAL; } if (bIsInput) spTempqTD->bPID = HOST20_qTD_PID_OUT; /* Bit8~9 */ else spTempqTD->bPID = HOST20_qTD_PID_IN; /* Bit8~9 */ spTempqTD->bTotalBytes = 0; /* Bit16~30 */ spTempqTD->bDataToggle = 1; /* Bit31 */ bReturnValue = flib_Host20_Send_qTD(hcd, spTempqTD, qh_ptr, Host20_Ctrl_Timeout); if (bReturnValue > 0) goto exit_issue_control; } #else USB_LIB_PRINTF("usb_pipein:%d\n",usb_pipein(*pbCmd)); USB_LIB_PRINTF("hwDataSize:%d\n",hwDataSize); /* by Standard Request codes */ switch (*(pbCmd + 1)) { /* <2>.Case-1:"Setup/In/Out' Format..." */ case 0: /* get status */ case 6: /* get descriptor */ case 8: /* get configuration */ case 10: /* get interface */ case 0xfe: /* get Max Lun */ /* <2.2>.In packet */ SetupRead: /* .Fill qTD */ /* 0=>qTD */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL){ return HOST20_FATAL; } spTempqTD->bPID = HOST20_qTD_PID_IN; /* Bit8~9 */ spTempqTD->bTotalBytes = hwDataSize; /* Bit16~30 */ spTempqTD->bDataToggle = 1; /* Bit31 */ spTempqTD->ArrayBufferPointer_Word[0] = VA2PA((MS_UINT32)ehci->ep0_buffer); /* .Send qTD */ bReturnValue = flib_Host20_Send_qTD(hcd, spTempqTD, qh_ptr,Host20_Ctrl_Timeout); if (bReturnValue>0) goto exit_issue_control; /* .Waiting for result */ memcpy((MS_UINT8 *)pbData, ehci->ep0_buffer, hwDataSize); // Dump_Data((MS_UINT16)pbData, hwDataSize); /* <2.3>.Out packet */ /* .Fill qTD */ /* 0=>qTD */ spTempqTD=flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL){ return HOST20_FATAL; } spTempqTD->bPID = HOST20_qTD_PID_OUT; /* Bit8~9 */ spTempqTD->bTotalBytes = 0; /* Bit16~30 */ spTempqTD->bDataToggle = 1; /* Bit31 */ /* .Send qTD */ bReturnValue = flib_Host20_Send_qTD(hcd, spTempqTD, qh_ptr, Host20_Ctrl_Timeout); if (bReturnValue > 0) goto exit_issue_control; break; /* <3>.Case-2:'Setup/In' Format... => Faraday Driver will not need */ case 0xf0: /* read */ case 0xf1: /* write */ if (*(pbCmd) == 0xc0) goto SetupRead; else if (*(pbCmd) == 0x40) goto SetupWrite; break; case 1: /* clear feature */ case 3: /* set feature */ case 5: /* set address */ case 9: /* set Configuration */ case 11: /* set interface */ case 0xff: /* device reset */ /* <3.2>.In packet */ /* .Fill qTD */ /* 0=>qTD */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL){ return HOST20_FATAL; } spTempqTD->bPID = HOST20_qTD_PID_IN; /* Bit8~9 */ spTempqTD->bTotalBytes = hwDataSize; /* Bit16~30 */ spTempqTD->bDataToggle = 1; /* Bit31 */ spTempqTD->ArrayBufferPointer_Word[0] = VA2PA((MS_UINT32)ehci->ep0_buffer); /* .Send qTD */ bReturnValue = flib_Host20_Send_qTD(hcd, spTempqTD, qh_ptr, Host20_Ctrl_Timeout); if (bReturnValue > 0) goto exit_issue_control; /* .Copy Result */ // memcpy(pbData,pUsbCtrlBuf, hwDataSize); break; /* <4>.Case-3:'Setup/Out/In' */ case 7: /* set descriptor */ /* <4.2>.Out packet */ /* .Fill qTD */ SetupWrite: /* 0=>qTD */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL){ return HOST20_FATAL; } spTempqTD->bPID = HOST20_qTD_PID_OUT; /* Bit8~9 */ spTempqTD->bTotalBytes = hwDataSize; /* Bit16~30 */ spTempqTD->bDataToggle = 1; /* Bit31 */ spTempqTD->ArrayBufferPointer_Word[0] = VA2PA((MS_UINT32)ehci->ep0_buffer); memcpy(ehci->ep0_buffer, pbData, hwDataSize); /* .Send qTD */ bReturnValue = flib_Host20_Send_qTD(hcd, spTempqTD, qh_ptr, Host20_Ctrl_Timeout); if (bReturnValue > 0) goto exit_issue_control; /* <4.3>.In packet */ /* .Fill qTD */ /* 0=>qTD */ spTempqTD=flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL){ return HOST20_FATAL; } spTempqTD->bPID = HOST20_qTD_PID_IN; /* Bit8~9 */ spTempqTD->bTotalBytes = 0; /* Bit16~30 */ spTempqTD->bDataToggle = 1; /* Bit31 */ /* .Send qTD */ bReturnValue = flib_Host20_Send_qTD(hcd, spTempqTD, qh_ptr, Host20_Ctrl_Timeout); if (bReturnValue > 0) goto exit_issue_control; break; default: if (*(pbCmd) & 0x80) goto SetupRead; else if (!(*(pbCmd) & 0x80)) goto SetupWrite; break; } #endif return (0); exit_issue_control: return (bReturnValue); } void Dump_QTD(MS_UINT32 addr) { MS_UINT8 i; USB_LIB_PRINTF("[USB] QH/QTD:%lx -> \n", addr); for (i = 0; i < 0x20 ; i = i+4) { USB_LIB_PRINTF("%x ", *(unsigned char volatile *)(addr + i+ 3)); USB_LIB_PRINTF("%x ", *(unsigned char volatile *)(addr + i+ 2)); USB_LIB_PRINTF("%x ", *(unsigned char volatile *)(addr + i+ 1)); USB_LIB_PRINTF("%x ", *(unsigned char volatile *)(addr + i)); USB_LIB_PRINTF("\n"); } } void Dump_Data(MS_UINT32 addr, MS_UINT16 sz) { MS_UINT16 i, xxx=0; USB_LIB_PRINTF("[USB] addr:%lx -> \n", addr); while (sz >= 0x10) { USB_LIB_PRINTF("\nADDR %x -> ", xxx); for (i = 0; i < 0x10; i++) USB_LIB_PRINTF("%x ", *(unsigned char volatile *)(addr + i)); sz -= 0x10; addr += 0x10; xxx += 0x10; } USB_LIB_PRINTF("\nADDR %x -> ", xxx); for (i = 0; i < sz ; i++) USB_LIB_PRINTF("%02x ", *(unsigned char volatile *)(addr + i)); } MS_UINT8 flib_Host20_Send_Receive_Bulk_Data(struct usb_hcd *hcd, void *buffer, int len, int dir_out) { MS_UINT32 wTotalLengthRemain = 0; MS_UINT32 buf = 0; MS_UINT8 result = 0; MS_UINT32 TransferLen = 0; MS_UINT32 TransBuf; /* <1>.To fill the data buffer */ wTotalLengthRemain = len; buf = (MS_UINT32)buffer; /* <2>.Issue Transfer */ while (wTotalLengthRemain) { if(wTotalLengthRemain > Scsi_Max_Transfer_Len) TransferLen = Scsi_Max_Transfer_Len; else TransferLen = wTotalLengthRemain; if (dir_out) { #if defined(CONFIG_USB_BOUNCE_BUF_PATCH) TransBuf = (MS_UINT32) usb_bounce_buf; memcpy((void*)TransBuf, (void*)buf, TransferLen); #else TransBuf = buf; #endif result = flib_Host20_Issue_Bulk (hcd, TransferLen , TransBuf, OTGH_Dir_Out); if (result != HOST20_OK) return result; } else { #if defined(CONFIG_USB_BOUNCE_BUF_PATCH) TransBuf = (MS_UINT32) usb_bounce_buf; #else TransBuf = buf; #endif result = flib_Host20_Issue_Bulk (hcd, TransferLen , TransBuf, OTGH_Dir_IN); if (result != HOST20_OK) return result; #if defined(CONFIG_USB_BOUNCE_BUF_PATCH) memcpy((void*)buf, (void*)TransBuf, TransferLen); #endif } buf += TransferLen; wTotalLengthRemain -= TransferLen; } return(result); } /* ==================================================================== * Function Name: flib_Host20_Issue_Bulk * Description: Input data must be 4K-Alignment * <1>.MaxSize=20 K * <2>.Support Only 1-TD * Input: * OutPut: * ==================================================================== */ MS_UINT8 flib_Host20_Issue_Bulk (struct usb_hcd *hcd, MS_UINT32 hwSize, MS_UINT32 pwBuffer, MS_UINT8 bDirection) { MS_UINT8 bTemp = 0, i = 0; MS_UINT32 count = 0; MS_UINT32 addr = 0, workbuf = 0; #if defined(USB_BUF_ALIGNED_PATCH) MS_UINT32 mybuf = 0; #endif #if 0 int TranSize = 0; #endif qTD_Structure *spTempqTD = NULL; qHD_Structure *spTempqH = NULL; struct ehci_hcd *ehci = hcd->ehci; if (pwBuffer !=(MS_UINT32) KSEG02KSEG1(pwBuffer) ) { #if defined(USB_BUF_ALIGNED_PATCH) /* ?? any buf issue template */ /* flush should be 8 bytes aligned */ if (pwBuffer & 0x7) { USB_LIB_PRINTF("[USB] Use 8byte aligned buf\n"); mybuf = (MS_UINT32) KSEG02KSEG1(usb_temp_buf); if (mybuf) { MS_MSG("[USB] usb memory get uncache buf failed!\n"); return HOST20_FATAL; } } else #endif { USB_LIB_BULK_PRINTF("[USB] Flush Data & MIU PIPE\n"); /* flush/invalidate data to DRAM */ flush_cache((ulong)pwBuffer,hwSize); #if (_USB_FLUSH_BUFFER == 1) Chip_Flush_Memory(); #endif /* uncached buffer */ pwBuffer=(MS_UINT32)KSEG02KSEG1(pwBuffer); } } #if defined(USB_BUF_ALIGNED_PATCH) /* ?? any buf issue template */ else { /* flush should be 8 bytes aligned */ if (pwBuffer & 0x7) { USB_LIB_PRINTF("[USB] Use 8byte aligned buf\n"); mybuf = (MS_UINT32) KSEG02KSEG1(usb_temp_buf); if(mybuf) { MS_MSG("[USB] usb memory get uncache buf failed!\n"); return HOST20_FATAL; } } } #endif /* The qTD will be release in the function "Send" */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); if(spTempqTD == NULL) { return HOST20_FATAL; } spTempqTD->bTotalBytes = hwSize ; /* not allow buffer over 16K for my usage */ #if defined(USB_BUF_ALIGNED_PATCH) /* ?? any buf issue template */ /* use original buf */ if (!mybuf) workbuf = mybuf; else #endif workbuf = pwBuffer; spTempqTD->ArrayBufferPointer_Word[0] = VA2PA(workbuf); /* rest of that page */ count = 0x1000 - (workbuf & 0x0fff); /* ... iff needed */ if ( hwSize < count) count = hwSize; else { workbuf += 0x1000; workbuf &= ~0x0fff; /* per-qtd limit: from 16K to 20K (best alignment) */ for (i = 1; (count < hwSize) && (i < 5); i++) { addr = workbuf; spTempqTD->ArrayBufferPointer_Word[i] = VA2PA(addr); workbuf += 0x1000; if ((count + 0x1000) < hwSize) count += 0x1000; else count = hwSize; } } spTempqH = (bDirection == ehci->ep_bulk0_dir) ? ehci->qh_bulk0 : ehci->qh_bulk1; USB_LIB_BULK_PRINTF("[USB] direction: %s, using qh_bulk%d\n", bDirection ? "in" : "out", (bDirection == ehci->ep_bulk0_dir) ? 0 : 1); /* <2>.Analysis the Direction */ if (bDirection==OTGH_Dir_IN) { spTempqTD->bPID = HOST20_qTD_PID_IN; if (hcd->FirstBulkIn) { spTempqTD->bDataToggle = 0; spTempqH->bDataToggleControl = 1; } } else { spTempqTD->bPID=HOST20_qTD_PID_OUT; if (hcd->FirstBulkOut) { spTempqTD->bDataToggle = 0; spTempqH->bDataToggleControl = 1; } #if defined(USB_BUF_ALIGNED_PATCH) /* ?? any buf issue template */ if (mybuf) memcpy((void*)mybuf, (void*)pwBuffer, hwSize); /* copy to 8byte aligned buffer */ #endif } /* <3>.Send TD */ bTemp = flib_Host20_Send_qTD(hcd, spTempqTD ,spTempqH,Host20_Timeout); if ((hcd->FirstBulkIn) && (bDirection==OTGH_Dir_IN)) { spTempqH->bDataToggleControl = 0; hcd->FirstBulkIn = 0; } if ((hcd->FirstBulkOut) && (bDirection==OTGH_Dir_Out)) { spTempqH->bDataToggleControl = 0; hcd->FirstBulkOut = 0; } if (bDirection == OTGH_Dir_IN) { #if defined(USB_BUF_ALIGNED_PATCH) /* ?? any buf issue template */ if (mybuf) memcpy((void*)pwBuffer, (void*)mybuf, hwSize); /* copy from 8byte aligned buffer */ #endif #if 0 TranSize = hwSize - hcd->total_bytes; if ((TranSize % 8) == 7) { USB_LIB_PRINTF("[USB] patch wallace bug\n"); *((MS_UINT8*)workbuf+TranSize-1)=*((MS_UINT8*)workbuf+TranSize+3); //fix hardware bug } else if ((TranSize % 8)==3) { USB_LIB_PRINTF("[USB] patch wallace bug\n"); *((MS_UINT8*)workbuf+TranSize-1)=*((MS_UINT8*)workbuf+TranSize+7); //fix hardware bug } #endif } return (bTemp); } /* ********************************************************************************** * ********************************************************************************** *** Group-4:Structure Function *** * ********************************************************************************** * ********************************************************************************** */ /* ==================================================================== * Function Name: flib_Host20_InitStructure * Description: * 1.Init qHD for Control * qHD_C-->qHD_C-->qHD_C * 2.Init qHD for Bulk * |-------------------------| * qHD_C-->qHD_C-->qHD_B-->qHD_B * * 3.Init qHD for Interrupt * 4.Init iTD for ISO (Reserved for feature) * Input:Type =0 =>iTD * =1 =>qTD * =2 * OutPut: 0:Fail * 1:ok *==================================================================== */ void flib_Host20_InitStructure(struct usb_hcd *hcd) { MS_UINT16 i; MS_UINT8 *pData; struct ehci_hcd *ehci; hcd->ehci = &ms_ehci; ehci = hcd->ehci; ehci->ep0_buffer = (MS_UINT8 *)KSEG02KSEG1(UsbCtrlBuf); ehci->qtd_pool = (MS_UINT8 *)KSEG02KSEG1(QtdBuf); flush_cache((ulong)UsbDataBuf, sizeof(UsbDataBuf)); hcd->ehci->pUsbDataBuf = (MS_UINT8 *)KSEG02KSEG1(UsbDataBuf); /* <1>.Clear memory */ pData = ehci->qtd_pool; for (i=0 ; i < (Host20_qTD_SIZE*Host20_qTD_MAX+0x20) ; i++) pData[i]=0; //if ((MS_UINT32)Host20_STRUCTURE_qTD_BASE_ADDRESS & 0x10) // Host20_STRUCTURE_qTD_BASE_ADDRESS += 0x10; /* make it aligned with 32 */ ehci->qh_control0 = (qHD_Structure*)KSEG02KSEG1(&Host20_qHD_List_Control0); ehci->qh_control1 = (qHD_Structure*)KSEG02KSEG1(&Host20_qHD_List_Control1); ehci->qh_bulk0 = (qHD_Structure*)KSEG02KSEG1(&Host20_qHD_List_Bulk0); ehci->qh_bulk1 = (qHD_Structure*)KSEG02KSEG1(&Host20_qHD_List_Bulk1); pData = (MS_UINT8*)ehci->qh_control0; for (i = 0 ; i < sizeof(qHD_Structure); i++) pData[i]=0; pData = (MS_UINT8*)ehci->qh_control1; for (i = 0 ; i < sizeof(qHD_Structure); i++) pData[i]=0; pData = (MS_UINT8*)ehci->qh_bulk0; for (i = 0 ; i < sizeof(qHD_Structure); i++) pData[i]=0; pData = (MS_UINT8*)ehci->qh_bulk1; for ( i=0 ; i < sizeof(qHD_Structure); i++) pData[i]=0; ehci->async = VA2PA((MS_UINT32)ehci->qh_control0); /* <2>.For qTD & iTD & 4K-Buffer Manage init */ ehci->qtd_manage = &Host20_qTD_Manage[0]; for (i = 0;i < Host20_qTD_MAX; i++) ehci->qtd_manage[i] = Host20_MEM_FREE; // psHost20_qHD_List_Control[0]->bType = HOST20_HD_Type_QH; /* Address=0,Head=1,EndPt=0,Size */ flib_Host20_Allocate_QHD(hcd, ehci->qh_control0, HOST20_HD_Type_QH, 0, 1, 0, 64); /* Address=1,Head=0,EndPt=0,Size */ flib_Host20_Allocate_QHD(hcd, ehci->qh_control1, HOST20_HD_Type_QH, 1, 0, 0, 64); /* Address=1,Head=0,EndPt=1,Size */ flib_Host20_Allocate_QHD(hcd, ehci->qh_bulk0, HOST20_HD_Type_QH, 1, 0, 1, 64); /* Address=1,Head=0,EndPt=2,Size */ flib_Host20_Allocate_QHD(hcd, ehci->qh_bulk1, HOST20_HD_Type_QH, 1, 0, 2, 64); /* <3.3>.Link the qHD (contol) */ ehci->qh_control0->bNextQHDPointer = (VA2PA((MS_UINT32)ehci->qh_control1) >>5); ehci->qh_control1->bNextQHDPointer = (VA2PA((MS_UINT32)ehci->qh_control0) >>5); /* for QHs address checking*/ #if 0 MS_MSG("ctrl0 %x size %x\n", &Host20_qHD_List_Control0, sizeof(qHD_Structure)); MS_MSG("ctrl1 %x size %x\n", &Host20_qHD_List_Control1, sizeof(qHD_Structure)); MS_MSG("bulk0 %x size %x\n", &Host20_qHD_List_Bulk0, sizeof(qHD_Structure)); MS_MSG("bulk1 %x size %x\n", &Host20_qHD_List_Bulk1, sizeof(qHD_Structure)); #endif } /* ==================================================================== * Function Name: flib_Host20_GetStructure * Description: * * Input:Type =0 =>qTD * =1 =>iTD * =2 =>4K Buffer * OutPut: 0:Fail * ~0:Addrress * ==================================================================== */ qTD_Structure *flib_Host20_GetStructure(struct usb_hcd *hcd, MS_UINT8 Type) { MS_UINT32 i; MS_UINT8 bFound = 0; qTD_Structure *spTempqTD; struct ehci_hcd *ehci = hcd->ehci; switch(Type) { // For qTD case Host20_MEM_TYPE_qTD: for (i = 0;i < Host20_qTD_MAX; i++) if (ehci->qtd_manage[i] == Host20_MEM_FREE) { bFound = 1; ehci->qtd_manage[i] = Host20_MEM_USED; break; } if (bFound == 1) { spTempqTD = (qTD_Structure *)((MS_UINT32)ehci->qtd_pool + i*Host20_qTD_SIZE); memset((unsigned char *)spTempqTD, 0, Host20_qTD_SIZE); spTempqTD->bTerminate = 1; /* Bit0 */ spTempqTD->bStatus_Active = 0; /* Bit7 */ spTempqTD->bInterruptOnComplete = 1; /* Bit15 */ spTempqTD->bAlternateTerminate = 1; spTempqTD->bErrorCounter = 3; // USB_LIB_PRINTF("[USB] get QTD:%x\n", (MS_U32) spTempqTD); return (spTempqTD); } else MS_MSG("[USB] QTD underrun!\n"); break; default: return 0; break; } /* Not Found... */ return (0); } /* ==================================================================== * Function Name: flib_Host20_ReleaseStructure * Description: * * Input:Type =0 =>qTD * =1 =>iTD * =2 * OutPut: 0:Fail * ~0:Addrress *==================================================================== */ void flib_Host20_ReleaseStructure(struct usb_hcd *hcd, MS_UINT8 Type, MS_UINT32 pwAddress) { MS_UINT8 i; MS_UINT32 wReleaseNum; MS_UINT8 *pData; struct ehci_hcd *ehci = hcd->ehci; // USB_LIB_PRINTF("[USB] release QTD:%x\n",pwAddress); pData = (MS_UINT8*)pwAddress; switch(Type) { case Host20_MEM_TYPE_qTD: if (pwAddress<(MS_UINT32)ehci->qtd_pool) { MS_MSG("[USB] ??? Memory release area fail...\n"); return; } if ((pwAddress-(MS_UINT32)ehci->qtd_pool) == 0) wReleaseNum = 0; else wReleaseNum=(pwAddress-(MS_UINT32)ehci->qtd_pool)/Host20_qTD_SIZE; /* ERROR FIX Prevent Tool 070522 */ if (wReleaseNum >= Host20_qTD_MAX) { MS_MSG("[USB] ??? Memory release area fail...\n"); return; } ehci->qtd_manage[wReleaseNum]=Host20_MEM_FREE; /* qTD size=32 bytes */ for (i = 0;i < Host20_qTD_SIZE; i++) *(pData+i) = 0; break; } } /* ==================================================================== * Function Name: flib_Host20_QHD_Control_Init * Description: * * Input:Type =0 =>qTD * =1 =>iTD * =2 * OutPut: 0:Fail * ~0:Addrress * ==================================================================== */ void flib_Host20_QHD_Control_Init(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd->ehci; /* <1>.Init Control-0/1 */ ehci->qh_control0->bEdSpeed = hcd->bSpeed; ehci->qh_control0->bInactiveOnNextTransaction = 0; ehci->qh_control0->bDataToggleControl = 1; ehci->qh_control1->bEdSpeed = hcd->bSpeed; ehci->qh_control1->bInactiveOnNextTransaction = 0; ehci->qh_control1->bDataToggleControl = 1; /* <2>.Init Bulk-0/1 */ ehci->qh_bulk0->bEdSpeed = hcd->bSpeed; ehci->qh_bulk0->bInactiveOnNextTransaction = 0; ehci->qh_bulk0->bDataToggleControl = 0; ehci->qh_bulk1->bEdSpeed = hcd->bSpeed; ehci->qh_bulk1->bInactiveOnNextTransaction = 0; ehci->qh_bulk1->bDataToggleControl = 0; /* <12>.Enable Asynchronous */ USB_LIB_PRINTF("[USB] enable aynch \n"); mbHost20_USBCMD_AsynchronousEnable_Set(hcd->ehc_base); /* Temp;;Bruce */ } void flib_Host20_SetControl1MaxPacket(struct usb_hcd *hcd, MS_UINT8 max) { struct ehci_hcd *ehci = hcd->ehci; ehci->qh_control1->bMaxPacketSize = max; USB_LIB_PRINTF("[USB] control1 max:%x\n", ehci->qh_control1->bMaxPacketSize); } /* ==================================================================== * Function Name: flib_Host20_Allocate_QHD * Description: * * Input:Type =0 =>qTD * =1 =>iTD * =2 * OutPut: 0:Fail * ~0:Addrress * ==================================================================== */ void flib_Host20_Allocate_QHD(struct usb_hcd *hcd, qHD_Structure *psQHTemp, MS_UINT8 bNextType, MS_UINT8 bAddress, MS_UINT8 bHead, MS_UINT8 bEndPt, MS_UINT32 wMaxPacketSize) { qTD_Structure *spTempqTD; psQHTemp->bTerminate = 0; /* Bit0 */ psQHTemp->bType = bNextType; /* Bit2~1 */ psQHTemp->bDeviceAddress = bAddress; /* Bit0~6 */ psQHTemp->bEdNumber = bEndPt; /* Bit11~8 */ psQHTemp->bHeadOfReclamationListFlag = bHead; /* Bit15 */ psQHTemp->bMaxPacketSize = wMaxPacketSize; /* Bit16~26 */ psQHTemp->bNakCounter = Host20_QHD_Nat_Counter; psQHTemp->bOverlay_NextTerminate = 1; psQHTemp->bOverlay_AlternateNextTerminate = 1; /* <2>.allocate dumy qTD */ /* <2.1>.Allocate qTD */ spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); /* 0=>qTD */ psQHTemp->bOverlay_NextqTD = (VA2PA((MS_UINT32)spTempqTD) >> 5); /* <2.3>.Active the qTD */ psQHTemp->bOverlay_NextTerminate = 0; } /* ==================================================================== * Function Name: flib_Host20_CheckingForResult_QHD * Description: * Input:Type * OutPut: * ==================================================================== */ MS_UINT8 flib_Host20_CheckingForResult_QHD(struct usb_hcd *hcd, qHD_Structure *spTempqHD) { MS_UINT32 bQHStatus; MS_UINT16 wIntStatus; struct ehci_hcd *ehci = hcd->ehci; wIntStatus = mwHost20_USBSTS_Rd(hcd->ehc_base); hcd->urb_status = 0; /* <2>.Checking for the Error type interrupt => Halt the system */ if (wIntStatus & HOST20_USBINTR_SystemError) { MS_MSG("[USB] USBSTS System Error... Halt the system...\n"); ehci->bSendStatusError = 1; } if (wIntStatus & HOST20_USBINTR_USBError) { MS_MSG("[USB] USBSTS USB Error...\n"); mwHost20_USBSTS_USBError_Set(hcd->ehc_base); ehci->bSendStatusError = 1; } if (ehci->bSendStatusError == 0) return HOST20_OK; USB_LIB_PRINTF("[USB] Send Error USBSTS Status:%x\n",wIntStatus); /* <1>.Analysis the qHD Status */ ehci->bSendStatusError = 0; MS_MSG("[USB] USB Error Interrupt Event...\n"); bQHStatus = spTempqHD->bOverlay_Status; USB_LIB_PRINTF("[USB] bQHStatus:%lx\n",bQHStatus); /* stop to check ehci status */ // while (1) {}; if (bQHStatus & HOST20_qTD_STATUS_Halted) { /* clear halt status */ spTempqHD->bOverlay_Status = 0; spTempqHD->bOverlay_TotalBytes = 0; spTempqHD->bOverlay_Direction = 0; /* no other error status */ if (bQHStatus == HOST20_qTD_STATUS_Halted) { MS_MSG("[USB] qHD Status STALL\n"); hcd->urb_status |= USB_ST_STALLED; return HOST20_DEVICE_STALL; } else MS_MSG("[USB] qHD Status => Halted (Stall/Babble/Error Counter=0)...0x%lx\n", bQHStatus); } if (bQHStatus & HOST20_qTD_STATUS_BufferError) { hcd->urb_status |= USB_ST_BUF_ERR; MS_MSG("[USB] qHD Status => HOST20_qTD_STATUS_BufferError...\n"); } if (bQHStatus & HOST20_qTD_STATUS_Babble) { MS_MSG("[USB] qHD Status => HOST20_qTD_STATUS_Babble !!! \n"); /* don't fix babble error for Bert */ spTempqHD->bOverlay_Status = 0; hcd->urb_status |= USB_ST_BABBLE_DET; return HOST20_OK; } if (bQHStatus & HOST20_qTD_STATUS_TransactionError) { MS_MSG("[USB] qHD Status => HOST20_qTD_STATUS_TransactionError...\n"); hcd->urb_status |= USB_ST_CRC_ERR; } if (bQHStatus & HOST20_qTD_STATUS_MissMicroFrame) { MS_MSG("[USB] qHD Status => HOST20_qTD_STATUS_MissMicroFrame...\n"); hcd->urb_status |= USB_ST_BIT_ERR; } /* <2>.Clear the status */ spTempqHD->bOverlay_Status = 0; return HOST20_FAIL; } MS_UINT8 flib_Host20_Send_qTD_Intr(struct usb_hcd *hcd, qTD_Structure *spHeadqTD ,qHD_Structure *spTempqHD) { MS_UINT8 bReturnValue; qTD_Structure *spNewDumyqTD; qTD_Structure *spOldDumyqTD; qTD_Structure *spLastqTD; MS_UINT32 ehci_base = hcd->ehc_base; spOldDumyqTD=(qTD_Structure*)PA2VA((( MS_UINT32)(spTempqHD->bOverlay_NextqTD))<<5); memcpy(spOldDumyqTD,spHeadqTD,Host20_qTD_SIZE); //<2>.Prepare new dumy qTD spNewDumyqTD=spHeadqTD; memset((void *)spNewDumyqTD ,0, Host20_qTD_SIZE); spNewDumyqTD->bTerminate=1; //<3>.Find spLastqTD & link spLastqTD to NewDumyqTD & Set NewDumyqTD->T=1 spLastqTD=spOldDumyqTD; while(spLastqTD->bTerminate==0) { spLastqTD=(qTD_Structure*)PA2VA((( MS_UINT32)(spLastqTD->bNextQTDPointer))<<5); }; spLastqTD->bNextQTDPointer=VA2PA((MS_UINT32)spNewDumyqTD)>>5; spLastqTD->bTerminate=0; //Link Alternate qTD pointer spLastqTD->bAlternateQTDPointer=(VA2PA(( MS_UINT32)spNewDumyqTD)>>5); spLastqTD->bAlternateTerminate=0; //<4>.Set OldDumyqTD->Active=1 //Dump_QTD(spOldDumyqTD); //Dump_QTD(spNewDumyqTD); while (mwHost20_USBSTS_Rd(ehci_base) & 0x3b) { mwHost20_USBSTS_Set(ehci_base,0x3b); //clear interrupt, don't clear port change int } spOldDumyqTD->bStatus_Active=1; flush_cache((ulong)spOldDumyqTD,0x10); //wried, seems like cache has bug, //CPU always keep its own copy, //even we use non-cache memory bReturnValue=HOST20_OK; return (bReturnValue); } MS_UINT8 flib_Host20_Send_qTD2(struct usb_hcd *hcd, qTD_Structure *spHeadqTD ,qHD_Structure *spTempqHD, MS_UINT32 wTimeOutSec) { MS_UINT8 bReturnValue; qTD_Structure *spNewDumyqTD; qTD_Structure *spOldDumyqTD; qTD_Structure *spReleaseqTD; qTD_Structure *spReleaseqTDNext; qTD_Structure *spLastqTD; MS_UINT32 ehci_base = hcd->ehc_base; unsigned long wTimes; //if (wTimeOutSec==0) //{ // DEBUG_USB(printf("error, timeout sec is zero\n")); //} spOldDumyqTD=(qTD_Structure*)PA2VA((( MS_UINT32)(spTempqHD->bOverlay_NextqTD))<<5); //spTempqHD->bOverlay_Status|=HOST20_qTD_STATUS_Halted; while (mbHost20_USBCMD_PeriodicEnable_Rd(ehci_base)) { mbHost20_USBCMD_PeriodicEnable_Clr(ehci_base); } //spHeadqTD->bTerminate=1; //set to terminate memcpy(spOldDumyqTD,spHeadqTD,Host20_qTD_SIZE); //spOldDumyqTD->bStatus_Halted=1; //spOldDumyqTD->bStatus_Active=0; //<2>.Prepare new dumy qTD spNewDumyqTD=spHeadqTD; memset((void *)spNewDumyqTD ,0, Host20_qTD_SIZE); spNewDumyqTD->bTerminate=1; //spNewDumyqTD->bAlternateTerminate=1; //spNewDumyqTD->bStatus_Halted=1; //<3>.Find spLastqTD & link spLastqTD to NewDumyqTD & Set NewDumyqTD->T=1 spLastqTD=spOldDumyqTD; while(spLastqTD->bTerminate==0) { spLastqTD=(qTD_Structure*)PA2VA((( MS_UINT32)(spLastqTD->bNextQTDPointer))<<5); }; spLastqTD->bNextQTDPointer=VA2PA((MS_UINT32)spNewDumyqTD)>>5; spLastqTD->bTerminate=0; //Link Alternate qTD pointer spLastqTD->bAlternateQTDPointer=(VA2PA(( MS_UINT32)spNewDumyqTD)>>5); spLastqTD->bAlternateTerminate=0; //<4>.Set OldDumyqTD->Active=1 //gwLastqTDSendOK=0; //sAttachDevice.psSendLastqTD=spLastqTD; //sAttachDevice.bSendStatusError=0; //Dump_QTD(spOldDumyqTD); //Dump_QTD(spNewDumyqTD); while (mwHost20_USBSTS_Rd(ehci_base) & 0x3b) { mwHost20_USBSTS_Set(ehci_base,0x3b); //clear interrupt, don't clear port change int } //bExitLoop=0; //spOldDumyqTD->bStatus_Halted=0; //spOldDumyqTD->bStatus_Halted=0; #if 0 printf("spOldDumyqTD: %X\n", spOldDumyqTD); printf("bTerminate: %X\n", spOldDumyqTD->bTerminate); printf("bNextQTDPointer: %X\n", spOldDumyqTD->bNextQTDPointer); printf("bAlternateTerminate: %X\n", spOldDumyqTD->bAlternateTerminate); printf("bAlternateQTDPointer: %X\n", spOldDumyqTD->bAlternateQTDPointer); printf("bStatus_PingState: %X\n", spOldDumyqTD->bStatus_PingState); printf("bStatus_SplitState: %X\n", spOldDumyqTD->bStatus_SplitState); printf("bStatus_MissMicroFrame: %X\n", spOldDumyqTD->bStatus_MissMicroFrame); printf("bStatus_Transaction_Err: %X\n", spOldDumyqTD->bStatus_Transaction_Err); printf("bStatus_Babble: %X\n", spOldDumyqTD->bStatus_Babble); printf("bStatus_Buffer_Err: %X\n", spOldDumyqTD->bStatus_Buffer_Err); printf("bStatus_Halted: %X\n", spOldDumyqTD->bStatus_Halted); printf("bStatus_Active: %X\n", spOldDumyqTD->bStatus_Active); printf("bPID: %X\n", spOldDumyqTD->bPID); printf("bErrorCounter: %X\n", spOldDumyqTD->bErrorCounter); printf("CurrentPage: %X\n", spOldDumyqTD->CurrentPage); printf("bInterruptOnComplete: %X\n", spOldDumyqTD->bInterruptOnComplete); printf("bTotalBytes: %X\n", spOldDumyqTD->bTotalBytes); printf("bDataToggle: %X\n", spOldDumyqTD->bDataToggle); printf("ArrayBufferPointer_Word: %X\n", spOldDumyqTD->ArrayBufferPointer_Word[0]); #endif //flib_Host20_StopRun_Setting(HOST20_Enable); //printf("RunStop: %X\n", mbHost20_USBCMD_RunStop_Rd()); spOldDumyqTD->bStatus_Active=1; while (mbHost20_USBCMD_PeriodicEnable_Rd(ehci_base)==0) { mbHost20_USBCMD_PeriodicEnable_Set(ehci_base); } //spTempqHD->bOverlay_Status&=~HOST20_qTD_STATUS_Halted; //wait until asynchronous scheduler is idle //mbHost20_USBCMD_RunStop_Set(); //spTempqHD->bOverlay_Status&=~0x40; //clr HALT bit, start this queue head wTimes=0; //<5>.Waiting for result //EAL=0; while (1) { flush_cache((ulong)spOldDumyqTD,0x10); //wried, seems like cache has bug, //CPU always keep its own copy, //even we use non-cache memory wTimes++; if ((spOldDumyqTD->bStatus_Active==0) && ( mwHost20_USBSTS_Rd(ehci_base)& HOST20_USBINTR_CompletionOfTransaction)) //wait until Status_Active become 0 { bReturnValue=HOST20_OK; break; //bExitLoop=1; //printf("bReturn:%02bx\n", bReturnValue); } //if (bExitLoop) break; #if 1 if (mwHost20_USBSTS_Rd(ehci_base)&(HOST20_USBINTR_SystemError+HOST20_USBINTR_USBError)) { bReturnValue=HOST20_OK; //if (spOldDumyqTD->bStatus_Active==1) //{ // printf("something wrong..USBINTR:%02bx\n",mwHost20_USBSTS_Rd()); // printf("QH status:%02bx\n",spTempqHD->bOverlay_Status); //} break; //USB interrupt happened } #endif wTimes++; if (mwHost20_PORTSC_ConnectStatus_Rd(ehci_base) == 0) { MS_MSG("[USB] Port%d device disconn\n", hcd->port_index); bReturnValue = HOST20_FATAL; hcd->urb_status |= USB_ST_DISCONNECT; break; } udelay(1); /* Timeout: Xs*/ if (wTimes > wTimeOutSec * 1000 * 1000) { bReturnValue = HOST20_FATAL; hcd->urb_status |= USB_ST_TIMEOUT; /* host controller reset */ // mbHost20_USBCMD_HCReset_Set(); // while(mbHost20_USBCMD_HCReset_Rd() > 0); MS_MSG("[USB] >>> Fail => Time Out for Send qTD...\n"); USB_LIB_PRINTF("[USB] Reg 0x28: 0x%lx 0x%lx\n", *((MS_UINT32 volatile *)(ehci_base+0x28*2)), *((MS_UINT32 volatile *)(ehci_base+0x28*2+4))); USB_LIB_PRINTF("[USB] QH: \n"); Dump_QTD((MS_UINT32)spTempqHD); USB_LIB_PRINTF("[USB] TD: \n"); Dump_QTD((MS_UINT32)spOldDumyqTD); // while(1); break; } } while (mbHost20_USBCMD_PeriodicEnable_Rd(ehci_base)) { mbHost20_USBCMD_PeriodicEnable_Clr(ehci_base); } //<6>.Checking the Result if (bReturnValue!=HOST20_FATAL) bReturnValue=flib_Host20_CheckingForResult_QHD(hcd, spTempqHD); if (bReturnValue==HOST20_TRANSACTION_ERROR) { //printf("Transaction Error\n"); spTempqHD->bOverlay_Status=0; //clear halt status spTempqHD->bOverlay_CurrentqTD=VA2PA((MS_UINT32)spNewDumyqTD)>>5; } //<5>.Release the all the qTD (Not include spNewDumyqTD) #if 1 spReleaseqTD=spOldDumyqTD; do { //spReleaseqTDNext=((UINT32)(spReleaseqTD->bNextQTDPointerL))<<5 + ((UINT32)(spReleaseqTD->bNextQTDPointerH))<<16 ; spReleaseqTDNext=(qTD_Structure*)PA2VA(((MS_UINT32)(spReleaseqTD->bNextQTDPointer))<<5); flib_Host20_ReleaseStructure(hcd, Host20_MEM_TYPE_qTD,(MS_UINT32)spReleaseqTD); spReleaseqTD=spReleaseqTDNext; } while(((MS_UINT32)spReleaseqTD)!=((MS_UINT32)spNewDumyqTD)); #endif return (bReturnValue); } /* ==================================================================== * Function Name: flib_Host20_Send_qTD * Description: * Case-1:1qTD * Case-2:2qTD * Case-3:3qTD above * Input:Type * OutPut: 0 => OK * 1 => TimeOut * ==================================================================== */ MS_UINT8 flib_Host20_Send_qTD(struct usb_hcd *hcd, qTD_Structure *spHeadqTD, qHD_Structure *spTempqHD, MS_UINT32 wTimeOutSec) { MS_UINT8 bReturnValue; MS_UINT32 wTimes; MS_UINT32 ehci_base = hcd->ehc_base; struct ehci_hcd *ehci = hcd->ehci; qTD_Structure *spNewDumyqTD; qTD_Structure *spOldDumyqTD; qTD_Structure *spReleaseqTD; qTD_Structure *spReleaseqTDNext; qTD_Structure *spLastqTD; if (wTimeOutSec==0) MS_MSG("[USB] error, timeout sec is zero\n"); spOldDumyqTD=(qTD_Structure*)PA2VA(((MS_UINT32)(spTempqHD->bOverlay_NextqTD)) << 5); // spTempqHD->bOverlay_Status |= HOST20_qTD_STATUS_Halted; #if 0 while (mbHost20_USBCMD_AsynchronousEnable_Rd()) { /* pause asynchronous scheduler */ mbHost20_USBCMD_AsynchronousEnable_Clr(); } #endif /* set to terminate */ memcpy(spOldDumyqTD, spHeadqTD, Host20_qTD_SIZE); /* <2>.Prepare new dumy qTD */ spNewDumyqTD = spHeadqTD; memset((unsigned char *)spNewDumyqTD, 0, Host20_qTD_SIZE); spNewDumyqTD->bTerminate = 1; // spNewDumyqTD->bAlternateTerminate = 1; // spNewDumyqTD->bStatus_Halted = 1; /* <3>.Find spLastqTD & link spLastqTD to NewDumyqTD & Set NewDumyqTD->T=1 */ spLastqTD = spOldDumyqTD; while(spLastqTD->bTerminate == 0) { spLastqTD = (qTD_Structure*)PA2VA((((MS_UINT32)(spLastqTD->bNextQTDPointer)) << 5)); }; spLastqTD->bNextQTDPointer = VA2PA((MS_UINT32)spNewDumyqTD) >> 5; spLastqTD->bTerminate = 0; /* Link Alternate qTD pointer */ spLastqTD->bAlternateQTDPointer = VA2PA((MS_UINT32)spNewDumyqTD) >> 5; spLastqTD->bAlternateTerminate = 0; ehci->bSendStatusError = 0; #if 0 USB_LIB_PRINTF("[USB] Before ... \n"); USB_LIB_PRINTF("[USB] QH: \n"); Dump_QTD((MS_UINT32)spTempqHD); USB_LIB_PRINTF("[USB] TD: \n"); Dump_QTD((MS_UINT32)spOldDumyqTD); USB_LIB_PRINTF("[USB] After ... \n"); #endif // while (mwHost20_USBSTS_Rd() & 0x3f) // { mwHost20_USBSTS_Set(ehci_base, 0x3f); /* clear interrupt */ // } // spOldDumyqTD->bStatus_Halted = 0; // spOldDumyqTD->bStatus_Halted = 0; spOldDumyqTD->bStatus_Active = 1; #if (_USB_FLUSH_BUFFER == 1) Chip_Flush_Memory(); #endif // while (1){}; #if 0 while (mbHost20_USBCMD_AsynchronousEnable_Rd() == 0) { /* re start asynchronous scheduler */ mbHost20_USBCMD_AsynchronousEnable_Set(); } #endif // spTempqHD->bOverlay_Status &= ~HOST20_qTD_STATUS_Halted; /* wait until asynchronous scheduler is idle */ // mbHost20_USBCMD_RunStop_Set(); /* clr HALT bit, start this queue head */ // spTempqHD->bOverlay_Status &= ~0x40; wTimes=0; while (1) { /* ******************************** * * wried, seems like cache has bug, * * CPU always keep its own copy, * * even we use non-cache memory * *********************************** */ flush_cache((ulong)spOldDumyqTD,0x10); // mb(); if ( mwHost20_USBSTS_Rd(ehci_base) & HOST20_USBINTR_CompletionOfTransaction ) { #if (_USB_FLUSH_BUFFER == 1) Chip_Read_Memory(); #endif if (spOldDumyqTD->bStatus_Active == 0) { bReturnValue = HOST20_OK; break; } } if (mwHost20_USBSTS_Rd(ehci_base) & (HOST20_USBINTR_SystemError+HOST20_USBINTR_USBError)) { bReturnValue = HOST20_OK; break; /* USB interrupt happened */ } // if (gwOTG_Timer_Counter>wTimeOutSec) wTimes++; if (mwHost20_PORTSC_ConnectStatus_Rd(ehci_base) == 0) { MS_MSG("[USB] Port%d device disconn\n", hcd->port_index); bReturnValue = HOST20_FATAL; hcd->urb_status |= USB_ST_DISCONNECT; break; } udelay(1); /* Timeout: Xs*/ if (wTimes > wTimeOutSec * 1000 * 1000) { bReturnValue = HOST20_FATAL; hcd->urb_status |= USB_ST_TIMEOUT; /* host controller reset */ // mbHost20_USBCMD_HCReset_Set(); // while(mbHost20_USBCMD_HCReset_Rd() > 0); MS_MSG("[USB] >>> Fail => Time Out for Send qTD...\n"); USB_LIB_PRINTF("[USB] Reg 0x28: 0x%lx 0x%lx\n", *((MS_UINT32 volatile *)(ehci_base+0x28*2)), *((MS_UINT32 volatile *)(ehci_base+0x28*2+4))); USB_LIB_PRINTF("[USB] QH: \n"); Dump_QTD((MS_UINT32)spTempqHD); USB_LIB_PRINTF("[USB] TD: \n"); Dump_QTD((MS_UINT32)spOldDumyqTD); // while(1); break; } } hcd->total_bytes = spOldDumyqTD->bTotalBytes; /* <6>.Checking the Result */ if (bReturnValue != HOST20_FATAL) bReturnValue = flib_Host20_CheckingForResult_QHD(hcd, spTempqHD); /* <5>.Release the all the qTD (Not include spNewDumyqTD) */ spReleaseqTD=spOldDumyqTD; do { spReleaseqTDNext=(qTD_Structure*)PA2VA(((MS_UINT32)(spReleaseqTD->bNextQTDPointer)) << 5); flib_Host20_ReleaseStructure(hcd, Host20_MEM_TYPE_qTD,(MS_UINT32)spReleaseqTD); spReleaseqTD = spReleaseqTDNext; } while(((MS_UINT32)spReleaseqTD) != ((MS_UINT32)spNewDumyqTD)); //<6>.Double Check the QH overlay status. Adjust it if need. if( spTempqHD->bOverlay_NextqTD != (VA2PA(( MS_UINT32)spNewDumyqTD)>>5) || spTempqHD->bOverlay_Status & MS_BIT7 ) { // Bug of WD 3.0 disk. Disconnection happens while data face of SETUP transaction. // If chain qTDs after disconnection/timeout, QH overlay will not be advanced by HW. // It muss up qTD chain layout. QH doesn't stop at old dumy and stare at new dumy. // SW advance QH overlay manually no matter HW advancing or not. // Run bit is cleared by HQ when disconnection, so it is safe to modify the QH. MS_MSG("[Warning] Adjust bad qTD chain..\r\n"); spTempqHD->bOverlay_Status = spTempqHD->bOverlay_Status & ~MS_BIT7; spTempqHD->bOverlay_CurrentqTD = VA2PA(( MS_UINT32)spOldDumyqTD) >> 5; spTempqHD->bOverlay_NextqTD = VA2PA(( MS_UINT32)spNewDumyqTD) >> 5; spTempqHD->bOverlay_AlternateqTD = VA2PA(( MS_UINT32)spNewDumyqTD) >> 5; } return (bReturnValue); } MS_UINT8 flib_Host20_Start_BulkIn(struct usb_hcd *hcd, MS_UINT16 hwSize, MS_UINT32 pwBuffer) { qTD_Structure *spTempqTD = NULL; MS_UINT32 workbuf=0; qHD_Structure *spTempqH = NULL; qTD_Structure *spNewDumyqTD; qTD_Structure *spOldDumyqTD; qTD_Structure *spLastqTD; if ( pwBuffer != (MS_UINT32) KSEG02KSEG1(pwBuffer) ) { flush_cache((ulong)pwBuffer,hwSize); //flush buffer to uncached buffer #if (_USB_FLUSH_BUFFER == 1) Chip_Flush_Memory(); #endif pwBuffer=(MS_UINT32)KSEG02KSEG1(pwBuffer); } spTempqTD = flib_Host20_GetStructure(hcd, Host20_MEM_TYPE_qTD); //The qTD will be release in the function "Send" if(spTempqTD == NULL) return HOST20_FATAL; spTempqTD->bTotalBytes = hwSize ; workbuf = pwBuffer; spTempqTD->ArrayBufferPointer_Word[0] = VA2PA(workbuf); spTempqH = (hcd->ehci->ep_bulk0_dir == OTGH_Dir_IN) ? hcd->ehci->qh_bulk0 : hcd->ehci->qh_bulk1; spTempqTD->bPID=HOST20_qTD_PID_IN; if (hcd->FirstBulkIn) { spTempqTD->bDataToggle=0; spTempqH->bDataToggleControl=1; } //<3>.Send TD spOldDumyqTD=(qTD_Structure*)PA2VA(((MS_UINT32)(spTempqH->bOverlay_NextqTD))<<5); hcd->ehci->spBulkInqTD = spOldDumyqTD; USB_LIB_BULK_PRINTF("[USB] spBulkInqTD: %x\n", (MS_U32)hcd->ehci->spBulkInqTD); memcpy(spOldDumyqTD, spTempqTD, Host20_qTD_SIZE); //<2>.Prepare new dumy qTD spNewDumyqTD = spTempqTD; memset((unsigned char *)spNewDumyqTD , 0, Host20_qTD_SIZE); spNewDumyqTD->bTerminate = 1; spLastqTD = spOldDumyqTD; while(spLastqTD->bTerminate==0) { spLastqTD=(qTD_Structure*)PA2VA((((MS_UINT32)(spLastqTD->bNextQTDPointer))<<5)); }; spLastqTD->bNextQTDPointer = VA2PA((MS_UINT32)spNewDumyqTD)>>5; spLastqTD->bTerminate = 0; spLastqTD->bAlternateQTDPointer = VA2PA((MS_UINT32)spNewDumyqTD)>>5; spLastqTD->bAlternateTerminate = 0; hcd->ehci->bSendStatusError = 0; mwHost20_USBSTS_Set(hcd->ehc_base, 0x3f); /* clear interrupt */ spOldDumyqTD->bStatus_Active = 1; #if (_USB_FLUSH_BUFFER == 1) Chip_Flush_Memory(); #endif if (hcd->FirstBulkIn) { spTempqH->bDataToggleControl=0; hcd->FirstBulkIn=0; } return HOST20_OK; } MS_UINT8 flib_Host20_Get_BulkIn_Data(struct usb_hcd *hcd, struct usb_device *dev, unsigned int pipe, MS_U32 buf, MS_U32 length, MS_U32 *actual_len, int timeout) { MS_U32 uTotalSize = 0, uRemainSize; MS_U16 wMaxPacketSize; MS_UINT8 bReturnValue = HOST20_OK; qHD_Structure *spTempqHD; USB_LIB_BULK_PRINTF("[USB] flib_Host20_Get_BulkIn_Data enter...\n"); timeout *= 1000; /* convert from ms to us */ dev->status = hcd->urb_status = 0; wMaxPacketSize = dev->epmaxpacketin[((pipe>>15) & 0xf)]; spTempqHD = (hcd->ehci->ep_bulk0_dir == OTGH_Dir_IN) ? hcd->ehci->qh_bulk0 : hcd->ehci->qh_bulk1; if (hcd->ehci->spBulkInqTD== NULL) { //Start a BulkIn with length MaxPktSize flib_Host20_Start_BulkIn(hcd, wMaxPacketSize, (MS_U32)hcd->ehci->pUsbDataBuf); } do { if ( mwHost20_USBSTS_Rd(hcd->ehc_base) & HOST20_USBINTR_CompletionOfTransaction ) { #if (_USB_FLUSH_BUFFER == 1) Chip_Read_Memory(); #endif if (hcd->ehci->spBulkInqTD->bStatus_Active == 0) { bReturnValue = HOST20_OK; // Add total return size uRemainSize = (MS_U16)hcd->ehci->spBulkInqTD->bTotalBytes; uTotalSize += wMaxPacketSize - uRemainSize; if (uRemainSize > 0) { //printf("short data\n"); goto Trans_Done; } if (uTotalSize >= length) goto Trans_Done;; //Start a BulkIn with length MaxPktSize flib_Host20_Start_BulkIn(hcd, wMaxPacketSize, (MS_U32)(hcd->ehci->pUsbDataBuf+uTotalSize)); } } if (mwHost20_USBSTS_Rd(hcd->ehc_base) & (HOST20_USBINTR_SystemError+HOST20_USBINTR_USBError)) { bReturnValue=HOST20_OK; goto Trans_Done; //USB interrupt happened } if (mwHost20_PORTSC_ConnectStatus_Rd(hcd->ehc_base) == 0) { MS_MSG("[USB] Port%d device disconn\n", hcd->port_index); bReturnValue = HOST20_FATAL; hcd->urb_status |= USB_ST_DISCONNECT; goto Trans_Done; } udelay(1); } while(timeout-->0); *actual_len = 0; return (bReturnValue); //Timeout return Trans_Done: USB_LIB_BULK_PRINTF("[USB] Trans %d bytes\n", uTotalSize); memcpy((void*)buf, hcd->ehci->pUsbDataBuf, uTotalSize); *actual_len = uTotalSize; //<6>.Checking the Result if (bReturnValue!=HOST20_FATAL) bReturnValue=flib_Host20_CheckingForResult_QHD(hcd, spTempqHD); //<5>.Release the all the qTD (Not include spNewDumyqTD) flib_Host20_ReleaseStructure(hcd, Host20_MEM_TYPE_qTD, (MS_UINT32)(hcd->ehci->spBulkInqTD)); hcd->ehci->spBulkInqTD = NULL; dev->status = hcd->urb_status; return (bReturnValue); } //------- xHCI -------- void U3phy_MS28_init(struct xhc_comp *xhci) { #ifdef XHCI_SINGLE_PORT_ENABLE_MAC writeb(readb((void*)(xhci->u3phy_D_base+0x84*2))|0x40, (void*)(xhci->u3phy_D_base+0x84*2)); // open XHCI MAC clock #endif //-- 28 hpm mstar only--- writew(0x0104, (void*) (xhci->u3phy_A_base+0x6*2)); // for Enable 1G clock pass to UTMI //[2] reg_pd_usb3_purt [7:6] reg_gcr_hpd_vsel //U3phy initial sequence writew(0x0, (void*) (xhci->u3phy_A_base)); // power on rx atop writew(0x0, (void*) (xhci->u3phy_A_base+0x2*2)); // power on tx atop //writew(0x0910, (void*) (U3PHY_D_base+0x4*2)); // the same as default writew(0x0, (void*) (xhci->u3phy_A_base+0x3A*2)); // overwrite power on rx/tx atop writew(0x0160, (void*) (xhci->u3phy_D_base+0x18*2)); writew(0x0, (void*) (xhci->u3phy_D_base+0x20*2)); // power on u3_phy clockgen writew(0x0, (void*) (xhci->u3phy_D_base+0x22*2)); // power on u3_phy clockgen #ifdef XHCI_ENABLE_PD_OVERRIDE writew(0x308, (void*) (xhci->u3phy_A_base+0x3A*2)); // [9,8,3] PD_TXCLK_USB3TXPLL, PD_USB3_IBIAS, PD_USB3TXPLL override enable writeb(readb((void*)(xhci->u3phy_A_base+0x3*2-1)) & 0xbb, (void*)(xhci->u3phy_A_base+0x3*2-1)); // override value 0 #endif writeb(0xF4, (void*) (xhci->u3phy_D_base+0x12*2)); //TX lock threshold if(xhci->xhci_base != 0) { //disable compliance mode writeb(readb((void*)(xhci->xhci_base+0x6817)) | MS_BIT7, (void*)(xhci->xhci_base+0x6817)); } } void xhci_enable_clock(void) { static int clock_enable = 0; #ifdef XHCI_PORT0_ADDR struct xhc_comp xc = XHC_COMP_PORT0; #endif #ifdef XHCI_PORT1_ADDR struct xhc_comp xc1 = XHC_COMP_PORT1; #endif if (clock_enable) return; #ifdef XHCI_PORT0_ADDR printf("xhci_enable_clock\n"); U3phy_MS28_init(&xc); clock_enable = 1; #endif #ifdef XHCI_PORT1_ADDR U3phy_MS28_init(&xc1); #endif } MS_UINT32 xhci_port_state_to_neutral(MS_UINT32 state) { /* Save read-only status and port state */ return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); } void xhci_ssport_set_state(struct xhc_comp *xhci, int bOn) { MS_UINT32 temp; temp = readl((void*)(xhci->xhci_port_addr)); printf("port status 0x%x: 0x%lx\n", xhci->xhci_port_addr, temp); if (bOn) { if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_SS_DISABLED) { temp = xhci_port_state_to_neutral(temp); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | USB_SS_PORT_LS_RX_DETECT; writel(temp, (void*)(xhci->xhci_port_addr)); wait_ms(10); temp = readl((void*)(xhci->xhci_port_addr)); printf("port status: 0x%lx\n", temp); } } else { if ((temp & PORT_PLS_MASK) != USB_SS_PORT_LS_SS_DISABLED) { temp = xhci_port_state_to_neutral(temp); writel(temp | PORT_PE, (void*)(xhci->xhci_port_addr)); wait_ms(10); temp = readl((void*)(xhci->xhci_port_addr)); printf("port status: 0x%lx\n", temp); } } } //--------------------