/** @file

  Copyright (c) 2004  - 2014, Intel Corporation. All rights reserved.<BR>
                                                                                   

  This program and the accompanying materials are licensed and made available under

  the terms and conditions of the BSD License that accompanies this distribution.  

  The full text of the license may be found at                                     

  http://opensource.org/licenses/bsd-license.php.                                  

                                                                                   

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,            

  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.    

                                                                                   



Module Name:

    LpcDriver.c

Abstract:

    EFI Lpc Driver for a Generic PC Platform



--*/

#include "LpcDriver.h"
#include "IndustryStandard/Pci22.h"

//
// This driver is for ACPI(PNP0A03,0)/PCI(0x1f,0)
//

//
//  Lpc Driver Global Variables
//

EFI_DRIVER_BINDING_PROTOCOL gLpcDriver = {
  LpcDriverSupported,
  LpcDriverStart,
  LpcDriverStop,
  0x10,
  NULL,
  NULL
};

LPC_DEV mLpc = {
  LPC_DEV_SIGNATURE,
  NULL,
  {
    IsaDeviceEnumerate,
    IsaDeviceSetPower,
    IsaGetCurrentResource,
    IsaGetPossibleResource,
    IsaSetResource,
    IsaEnableDevice,
    IsaInitDevice,
    LpcInterfaceInit
  },
  NULL
};

BOOLEAN  InitExecuted = FALSE;

/**
    the entry point of the Lpc driver

**/
EFI_STATUS
EFIAPI
LpcDriverEntryPoint(
  IN EFI_HANDLE           ImageHandle,
  IN EFI_SYSTEM_TABLE     *SystemTable
  )
{


  return EfiLibInstallDriverBinding (ImageHandle, SystemTable, &gLpcDriver, ImageHandle);
}

/**

  ControllerDriver Protocol Method

**/
EFI_STATUS
EFIAPI
LpcDriverSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
  IN EFI_HANDLE                     Controller,
  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
  )
{
  EFI_STATUS                Status;
  EFI_PCI_IO_PROTOCOL       *PciIo;
  EFI_DEVICE_PATH_PROTOCOL  *IsaBridgeDevicePath;

  ACPI_HID_DEVICE_PATH      *AcpiNode;
  PCI_DEVICE_PATH           *PciNode;
  PCI_TYPE00                Pci;

  //
  // Get the ISA bridge's Device Path and test it
  // the following code is specific
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **)&IsaBridgeDevicePath,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = EFI_SUCCESS;
  AcpiNode =  (ACPI_HID_DEVICE_PATH *)IsaBridgeDevicePath;
  if (AcpiNode->Header.Type != ACPI_DEVICE_PATH ||
      AcpiNode->Header.SubType != ACPI_DP ||
      DevicePathNodeLength (&AcpiNode->Header) != sizeof(ACPI_HID_DEVICE_PATH) ||
      AcpiNode -> HID != EISA_PNP_ID(0x0A03) ||
      AcpiNode -> UID != 0 ) {
    Status = EFI_UNSUPPORTED;
  } else {
    //
    // Get the next node
    //
    IsaBridgeDevicePath = NextDevicePathNode (IsaBridgeDevicePath);
    PciNode  = (PCI_DEVICE_PATH *)IsaBridgeDevicePath;
    if (PciNode->Header.Type != HARDWARE_DEVICE_PATH ||
        PciNode->Header.SubType != HW_PCI_DP ||
        DevicePathNodeLength (&PciNode->Header) != sizeof (PCI_DEVICE_PATH) ||
        PciNode -> Function != 0x00 ||
        PciNode -> Device != 0x1f ) {
      Status = EFI_UNSUPPORTED;
    }
  }

  gBS->CloseProtocol (
         Controller,
         &gEfiDevicePathProtocolGuid,
         This->DriverBindingHandle,
         Controller
         );

  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  //
  // Get PciIo protocol instance
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiPciIoProtocolGuid,
                  (VOID **)&PciIo,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );

  if (EFI_ERROR(Status)) {
    return Status;
  }

  Status = PciIo->Pci.Read (
                        PciIo,
                        EfiPciIoWidthUint32,
                        0,
                        sizeof(Pci) / sizeof(UINT32),
                        &Pci
                        );

  if (!EFI_ERROR (Status)) {
    Status = EFI_SUCCESS; //TODO: force return success as temp solution EFI_UNSUPPORTED;
    if ((Pci.Hdr.Command & 0x03) == 0x03) {
      if (Pci.Hdr.ClassCode[2] == PCI_CLASS_BRIDGE) {
        //
        // See if this is a standard PCI to ISA Bridge from the Base Code
        // and Class Code
        //
        if (Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA) {
          Status = EFI_SUCCESS;
        } else {
        }

        //
        // See if this is an Intel PCI to ISA bridge in Positive Decode Mode
        //
        if (Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA_PDECODE &&
            Pci.Hdr.VendorId == 0x8086 &&
            Pci.Hdr.DeviceId == 0x7110) {
          Status = EFI_SUCCESS;
        } else {
        }
      } else {
      }
    }
    else {
    }
  }

  gBS->CloseProtocol (
         Controller,
         &gEfiPciIoProtocolGuid,
         This->DriverBindingHandle,
         Controller
         );
  return Status;
}


/**
  Install EFI_ISA_ACPI_PROTOCOL

**/
EFI_STATUS
EFIAPI
LpcDriverStart (
  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
  IN EFI_HANDLE                     Controller,
  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
  )
{
  EFI_STATUS             Status;
  EFI_PCI_IO_PROTOCOL    *PciIo;

  //
  // Get Pci IO
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiPciIoProtocolGuid,
                  (VOID **)&PciIo,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );

  if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
    return Status;
  }

  mLpc.PciIo = PciIo;

  //
  // Install IsaAcpi interface, the Sio interface is not installed!
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &Controller,
                  &gEfiIsaAcpiProtocolGuid,
                  &mLpc.IsaAcpi,
                  NULL
                  );
  return Status;
}


EFI_STATUS
EFIAPI
LpcDriverStop (
  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,
  IN  EFI_HANDLE                     Controller,
  IN  UINTN                          NumberOfChildren,
  IN  EFI_HANDLE                     *ChildHandleBuffer
  )
{
  EFI_STATUS             Status;
  EFI_ISA_ACPI_PROTOCOL  *IsaAcpi;
  LPC_DEV                *LpcDev;

  //
  // Get EFI_ISA_ACPI_PROTOCOL interface
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiIsaAcpiProtocolGuid,
                  (VOID **)&IsaAcpi,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  LpcDev = LPC_ISA_ACPI_FROM_THIS (IsaAcpi);

  //
  // Uninstall protocol interface: EFI_ISA_ACPI_PROTOCOL
  //
  Status = gBS->UninstallProtocolInterface (
                  Controller,
                  &gEfiIsaAcpiProtocolGuid,
                  &LpcDev->IsaAcpi
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  gBS->CloseProtocol (
         Controller,
         &gEfiPciIoProtocolGuid,
         This->DriverBindingHandle,
         Controller
         );

  return EFI_SUCCESS;
}

VOID
LpcIoRead8 (
  IN  UINT16  Port,
  OUT UINT8   *Data
  )
{
  mLpc.PciIo->Io.Read(
                   mLpc.PciIo,
                   EfiPciWidthUint8,
                   EFI_PCI_IO_PASS_THROUGH_BAR,
                   Port,
                   1,
                   Data
                   );
}

VOID
LpcIoWrite8 (
  IN  UINT16  Port,
  IN  UINT8   Data
  )
{
  mLpc.PciIo->Io.Write(
                   mLpc.PciIo,
                   EfiPciWidthUint8,
                   EFI_PCI_IO_PASS_THROUGH_BAR,
                   Port,
                   1,
                   &Data
                   );
}