/* SCCSID = "src/dev/usb/UHCI/UHCCOMPL.C, usb, c.basedd 98/07/10" */
/*
*   Licensed Material -- Property of IBM
*
*   (c) Copyright IBM Corp. 1997, 1998  All Rights Reserved
*/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  UHCCOMPL.C                                            */
/*                                                                            */
/*   DESCRIPTIVE NAME:  UHCI compliant USB host driver initialization         */
/*                      completion routines.                                  */
/*                                                                            */
/*   FUNCTION: These routines handle the presence check for the USB           */
/*             ports.                                                         */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             UHCIInitComplete                                               */
/*             UHCIGetUSBDIDC                                                 */
/*             UHCIResetHost                                                  */
/*             UHCIShutDown                                                   */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/03/01  MB                                                      */
/*          98/11/24  MB              Added shut down strategy worker routine,*/
/*                                    fixes shutdown problem on IBM PC 365    */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/
#include "uhci.h"


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  UHCIInitComplete                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Initialization complete                         */
/*                                                                    */
/* FUNCTION:  The function of this routine is to finish driver        */
/*            initialization performing the following:                */
/*             1) search for USBD driver IDC routine                  */
/*             2) register within USBD.                               */
/*                                                                    */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  UHCIInitComplete                                     */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  pRP-> kernel request packet                                */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pRP->Status = STATUS_DONE                                 */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void UHCIInitComplete( RPH FAR *pRP )
{
   RP_GENIOCTL    rp_USBDReg;
   USBHCD         regData;
#ifdef DEBUG
   dsPrint( DBG_HLVLFLOW, "UHCI: UHCIInitComplete started\r\n" );
#endif

   pRP->Status = STATUS_DONE;

   if (!gpUSBDIDC || !gdsUSBIDC)
      UHCIGetUSBDIDC(); // get USBD driver communication rtne addr

   if (!gpUSBDIDC || !gdsUSBIDC)
      pRP->Status = STATUS_DONE | STERR | STATUS_ERR_UNKCMD;

   // register HCD layer driver within USBD
   if (pRP->Status == STATUS_DONE)
   {
      setmem((PSZ)&rp_USBDReg, 0, sizeof(rp_USBDReg));
      setmem((PSZ)&regData, 0, sizeof(regData));
      rp_USBDReg.rph.Cmd=CMDGenIOCTL;   // IOCTL
      rp_USBDReg.Category=USB_IDC_CATEGORY_HOST;
      rp_USBDReg.Function=USB_IDC_FUNCTION_REGISTER;
      rp_USBDReg.ParmPacket=(PVOID)&regData;
      regData.usbIDC=(PUSBIDCEntry)&UHCIidc;
      regData.usbDS=GetDS();
      regData.hcdID=&ghcdID;
      regData.hcdCount=1;
      regData.rmData[0].hDriver=ghDriver;
      regData.rmData[0].hAdapter=ghAdapter;

      USBCallIDC( gpUSBDIDC, gdsUSBIDC, (RP_GENIOCTL FAR *)&rp_USBDReg );
      pRP->Status=rp_USBDReg.rph.Status;
   }

#ifdef DEBUG
   dsPrint1( DBG_HLVLFLOW, "UHCI: UHCIInitComplete finished. Status=%x\r\n", (USHORT)pRP->Status );
#endif
   return;
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  UHCIGetUSBDIDC                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  Retrieve USBD driver IDC address                */
/*                                                                    */
/* FUNCTION:  The function retrieves USBD communication routine       */
/*            address and driver's data segment address.              */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  UHCIGetUSBDIDC                                       */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  none                                                       */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: sets gpUSBDIDC and gdsUSBIDC to USBD driver IDC           */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_AttachDD                             */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void UHCIGetUSBDIDC(void)
{
   setmem((PSZ)&gDDTable, 0, sizeof(gDDTable));

   gpUSBDIDC=NULL;
   gdsUSBIDC=0;

   if (DevHelp_AttachDD( gUSBDriverName, (NPBYTE)&gDDTable))
      return;      /* Couldn't find USBD's IDC */

   gpUSBDIDC = (PUSBIDCEntry)gDDTable.ProtIDCEntry;
   gdsUSBIDC = gDDTable.ProtIDC_DS;
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  UHCIResetHost                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Reset host controller                           */
/*                                                                    */
/* FUNCTION:  The function of this routine is to clear USB frame      */
/*            array, TD array and reset USB host controller.          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  UHCIResetHost                                        */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  pRP-> kernel request packet                                */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pRP->Status = STATUS_DONE                                 */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
#pragma optimize("eglt", off)
void UHCIResetHost( RP_GENIOCTL FAR *pRP_GENIOCTL )
{
   LONG        initBuffAddr;
   USHORT      buffAlign;
   USHORT      usbCMD;
   USHORT      frameIndex, tdIndex;
   USHORT      retryCount;
   ISOINFO FAR *currIsoInfo, FAR *nextIsoInfo;
   USHORT      rootReqIndex;

   // reset root hub internals
   gRootHubAddress=0;
   gRootHubConfig=0;
   for (rootReqIndex=0; rootReqIndex<ROOT_MAX_REQ; rootReqIndex++)
      gRootHubRP[rootReqIndex].rph.Status=0;   //  mark all root requests completed
   gRootReqStatus=ROOT_HUB_NOREQ;         // root hub request status
   g0QH=NULL;
   g0Time=0;               // timer count for default address request
   gInterruptFlags=0;      // clear USB status register flags
   gHostReset=FALSE;
   gFirstBottomQH=NULL;           // pointer to the first bottom level QH
   gLastBottomQH=NULL;            // pointer to the last bottom level QH
   gNextForIRQ=NULL;
   gInterruptsDisabled=FALSE;
   gIsoPktSize=0;

   setmem((PSZ)&gFrameData, UHCI_LP_T, sizeof(gFrameData));

   // release all isohronous request data
   for (currIsoInfo=gFirstIso; currIsoInfo; currIsoInfo=nextIsoInfo)
   {
      nextIsoInfo=currIsoInfo->next;
      ReleaseIsoStruct( currIsoInfo );
   }
   gFirstIso=NULL;
   gLastIso=NULL;

   DevHelp_VirtToPhys(&gFrameData, &gPhyFrameListAddr);

   if (gPhyFrameListAddr&(~UHCI_FLBAS_MASK))
   {  // get frame base list address aligned on 4K boundary
      initBuffAddr=gPhyFrameListAddr;
      gPhyFrameListAddr&=UHCI_FLBAS_MASK;
      gPhyFrameListAddr+=(LONG)MAX_SCHEDULE_LEHGTH;
      buffAlign=(USHORT)(gPhyFrameListAddr-initBuffAddr);
   }
   else
      buffAlign=0;
   gFrameListAddr=(LONG *)((CHAR *)gFrameData+buffAlign);

   // calculate QH/TD array starting address and element count
   gTDListStart=(TD *)((CHAR *)gFrameListAddr+MAX_SCHEDULE_LEHGTH);
   gTDCount=((SHORT)((CHAR *)&gFrameData-(CHAR *)gTDListStart)+sizeof(gFrameData))/sizeof(TD);
   gLastTDId=TOP_QH_COUNT;

   // reset QH/TD array - set all element physical address and mark unused
   DevHelp_VirtToPhys(gTDListStart, &(gTDListStart[0].phyTDAddr));
   for (tdIndex=0; tdIndex<gTDCount; tdIndex++)
   {
      gTDListStart[tdIndex].LP=UHCI_LP_UNUSED;
      if (tdIndex)
         gTDListStart[tdIndex].phyTDAddr=gTDListStart[tdIndex-1].phyTDAddr+sizeof(TD);
   }

   // reset schedule and iso request QH entries
   for (frameIndex=0; frameIndex<MAX_SCHEDULE_ENTRIES; frameIndex++)
   {
      gFrameListAddr[frameIndex]=UHCI_LP_UNUSED;
   }

   // reset USB Host Controller
   usbCMD=UHCI_USBCMD_HCRESET;
   outp16(gusbCmdReg, usbCMD);
   for (retryCount=0;retryCount<ROOT_HUB_RESET_RETRIES;retryCount++)  // loop till Host Controller finishes reset
   {
      CLI(); DevHelp_ProcBlock((ULONG)ghDriver,1,(USHORT)0);
      inp16(gusbCmdReg, usbCMD);
      if (!(usbCMD&UHCI_USBCMD_HCRESET))
         break;
   }
    
   // reset frame index register
   outp16(gusbFrnumReg, 0);

   // set base frame address
   outp32(gusbBaseAddReg, gPhyFrameListAddr);

   // allow all interrupts
   outp16(gusbIntrReg, UHCI_USBINTR_SPIE|UHCI_USBINTR_IOCE|UHCI_USBINTR_RIE|UHCI_USBINTR_TCRCIE);

   // reset status register
   outp16(gusbStsReg, UHCI_USBSTS_HALTED | UHCI_USBSTS_HCPE |
          UHCI_USBSTS_HSE | UHCI_USBSTS_RDE | UHCI_USBSTS_USBEI | UHCI_USBSTS_USBI);
   
   // start Host
   usbCMD=UHCI_USBCMD_CF|UHCI_USBCMD_RS;
   outp16(gusbCmdReg, usbCMD);
   for (retryCount=0;retryCount<ROOT_HUB_RESET_RETRIES;retryCount++)  // loop till Host Controller starts
   {
      CLI(); DevHelp_ProcBlock((ULONG)ghDriver,1,(USHORT)0);
      inp16(gusbCmdReg, usbCMD);
      if (usbCMD&UHCI_USBCMD_RS)
         break;
   }
   CLI(); DevHelp_ProcBlock((ULONG)ghDriver,1,(USHORT)0);
   
   // get reclamation packet size
   inp16(gusbCmdReg, usbCMD);
   if (usbCMD&UHCI_USBCMD_MAXP64)
      gMaxReclPktSize=64;
   else
      gMaxReclPktSize=32;

   gHostReset=TRUE;  // host controller reset completed
}
#pragma optimize("", on)

#pragma optimize("eglt", off)
/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  UHCIShutDown                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Shut Down                                       */
/*                                                                    */
/* FUNCTION:  The function of this routine is to stop USB host,       */
/*            arm root hub processing thread to finish all requests.  */
/*                                                                    */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  UHCIShutDown                                         */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  pRP-> kernel request packet                                */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pRP->Status = STATUS_DONE                                 */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void UHCIShutDown( RPH FAR *pRP )
{
   RPSHDOWN FAR            *pShutDown=(RPSHDOWN FAR *)pRP;

#ifdef DEBUG
   dsPrint( DBG_HLVLFLOW, "UHCI: UHCIShutDown started\r\n" );
#endif

   pRP->Status = STATUS_DONE;

   if(gStopHostOnShutDown)
   { 
      switch (pShutDown->functionCode)
      {
      case START_SHUTDOWN:
         outp16(gusbCmdReg, 0);  // stop USB host
         USBExecutionStall( 1 ); // wait for 1ms to allow host to stop
         break;
      case END_SHUTDOWN:
         DevHelp_ArmCtxHook( 0, gRHubHookHandle );  // claim root hub request processing
         break;
      default:
         break;
      }
   }

#ifdef DEBUG
   dsPrint1( DBG_HLVLFLOW, "UHCI: UHCIShutDown finished. Status=%x\r\n", (USHORT)pRP->Status );
#endif
   return;
}
#pragma optimize("", on)


