/** @file

  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
  Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>

  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which 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.

**/

#include <PiDxe.h>

#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/NonDiscoverableDeviceRegistrationLib.h>
#include <Library/UefiBootServicesTableLib.h>

#include <Protocol/EmbeddedExternalDevice.h>

#include <TPS65950.h>
#include <Omap3530/Omap3530.h>

EMBEDDED_EXTERNAL_DEVICE   *gTPS65950;

#define HOST_CONTROLLER_OPERATION_REG_SIZE  0x44

STATIC
EFI_STATUS
ConfigureUSBHost (
  NON_DISCOVERABLE_DEVICE   *Device
  )
{
  EFI_STATUS Status;
  UINT8      Data = 0;

  // Take USB host out of force-standby mode
  MmioWrite32 (UHH_SYSCONFIG, UHH_SYSCONFIG_MIDLEMODE_NO_STANDBY
                            | UHH_SYSCONFIG_CLOCKACTIVITY_ON
                            | UHH_SYSCONFIG_SIDLEMODE_NO_STANDBY
                            | UHH_SYSCONFIG_ENAWAKEUP_ENABLE
                            | UHH_SYSCONFIG_AUTOIDLE_ALWAYS_RUN);
  MmioWrite32 (UHH_HOSTCONFIG, UHH_HOSTCONFIG_P3_CONNECT_STATUS_DISCONNECT
                             | UHH_HOSTCONFIG_P2_CONNECT_STATUS_DISCONNECT
                             | UHH_HOSTCONFIG_P1_CONNECT_STATUS_DISCONNECT
                             | UHH_HOSTCONFIG_ENA_INCR_ALIGN_DISABLE
                             | UHH_HOSTCONFIG_ENA_INCR16_ENABLE
                             | UHH_HOSTCONFIG_ENA_INCR8_ENABLE
                             | UHH_HOSTCONFIG_ENA_INCR4_ENABLE
                             | UHH_HOSTCONFIG_AUTOPPD_ON_OVERCUR_EN_ON
                             | UHH_HOSTCONFIG_P1_ULPI_BYPASS_ULPI_MODE);

  // USB reset (GPIO 147 - Port 5 pin 19) output high
  MmioAnd32 (GPIO5_BASE + GPIO_OE, ~BIT19);
  MmioWrite32 (GPIO5_BASE + GPIO_SETDATAOUT, BIT19);

  // Get the Power IC protocol
  Status = gBS->LocateProtocol (&gEmbeddedExternalDeviceProtocolGuid, NULL, (VOID **)&gTPS65950);
  ASSERT_EFI_ERROR (Status);

  // Power the USB PHY
  Data = VAUX_DEV_GRP_P1;
  Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, VAUX2_DEV_GRP), 1, &Data);
  ASSERT_EFI_ERROR(Status);

  Data = VAUX_DEDICATED_18V;
  Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, VAUX2_DEDICATED), 1, &Data);
  ASSERT_EFI_ERROR (Status);

  // Enable power to the USB hub
  Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID3, LEDEN), 1, &Data);
  ASSERT_EFI_ERROR (Status);

  // LEDAON controls the power to the USB host, PWM is disabled
  Data &= ~LEDAPWM;
  Data |= LEDAON;

  Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID3, LEDEN), 1, &Data);
  ASSERT_EFI_ERROR (Status);

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
PciEmulationEntryPoint (
  IN EFI_HANDLE       ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  UINT8                   CapabilityLength;
  UINT8                   PhysicalPorts;
  UINTN                   MemorySize;

  CapabilityLength = MmioRead8 (USB_EHCI_HCCAPBASE);
  PhysicalPorts    = MmioRead32 (USB_EHCI_HCCAPBASE + 0x4) & 0x0000000F;
  MemorySize       = CapabilityLength + HOST_CONTROLLER_OPERATION_REG_SIZE +
                     4 * PhysicalPorts - 1;

  return RegisterNonDiscoverableMmioDevice (
           NonDiscoverableDeviceTypeEhci,
           NonDiscoverableDeviceDmaTypeNonCoherent,
           ConfigureUSBHost,
           NULL,
           1,
           USB_EHCI_HCCAPBASE, MemorySize
           );
}
