/** @file SIO chip specific implementation. @copyright INTEL CONFIDENTIAL Copyright 2010 - 2017 Intel Corporation. The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material may contain trade secrets and proprietary and confidential information of Intel Corporation and its suppliers and licensors, and is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel's prior express written permission. No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing. Unless otherwise agreed by Intel in writing, you may not remove or alter this notice or any other notice embedded in Materials by Intel or Intel's suppliers or licensors in any way. This file contains 'Framework Code' and is licensed as such under the terms of your license agreement with Intel or your vendor. This file may not be modified, except as allowed by additional terms of your license agreement. @par Specification Reference: **/ #include "SioDriver.h" #include #include LOCAL_IO_WRITE8 mIoWrite8 = IoWrite8; // // COM 1 UART Controller // ACPI_SIO_RESOURCES_IO_IRQ mCom1Resources = { { ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR, COM1_ADDRESS, 8 }, { ACPI_IRQ_NOFLAG_DESCRIPTOR, COM1_IRQ_BIT }, { ACPI_END_TAG_DESCRIPTOR, 0 } }; ACPI_SIO_RESOURCES_IO mHwMonResources = { { ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR, HWMON_BASE_ADDRESS, 8 }, { ACPI_END_TAG_DESCRIPTOR, 0 } }; // // Table of SIO Controllers. This table assumes COM1 (UART_A) will always // be enabled. Compile error will happen if SIO_SUPPORT_UART_A is undefined. // DEVICE_INFO mDeviceInfo[] = { { { EISA_PNP_ID(0x501), 0 }, SIO_COM1, RESOURCE_IO | RESOURCE_IRQ, (ACPI_SMALL_RESOURCE_HEADER *) &mCom1Resources, (ACPI_SMALL_RESOURCE_HEADER *) &mCom1Resources }, // COM 1 UART Controller { { EISA_PNP_ID(0xc08), 0 }, SIO_HWMON, RESOURCE_IO, (ACPI_SMALL_RESOURCE_HEADER *) &mHwMonResources, (ACPI_SMALL_RESOURCE_HEADER *) &mHwMonResources, } // PS/2 Mouse Controller }; // // Using HWMON_REGISTER_PAIR for convinience, as it's already defined as // a register/value pair. Just defining some register/value pairs that need // to be programmed. // This table accomplishes the following tasks: Programing multipurpose pins // mostly to GPIO, except for those required by UART A; disabling all GPIO, // floppy controller, LPT, and UART B to reduce power consumption; enabling // PS2 controller. Though the PS2 controller is not physically connected, it // is needed for legacy USB keyboard (DOS and when installing WINXP). // Because there is no register 0xaa (0xaa is used to exit SIO configuration // space), register 0xaa is used as a marker for end of table. // HWMON_REGISTER_PAIR mGenericRegisters[] = { {0x1a, 0x80}, {0x1b, 0x58}, {0x22, 0xd6}, {0x25, 0x0b}, {0x2c, 0x80}, {0x07, 0x00}, {0x30, 0x00}, {0x07, 0x01}, {0x30, 0x00}, {0x07, 0x02}, {0xf0, 0x20}, {0x07, 0x03}, {0x30, 0x00}, {0x07, 0x05}, {0x61, 0x60}, {0x63, 0x64}, {0x70, 0x01}, {0x72, 0x0c}, {0x30, 0x01}, {0x07, 0x07}, {0x30, 0x00}, {0x07, 0x08}, {0x30, 0x00}, {0x07, 0x09}, {0x30, 0x00}, {0xaa, 0xff} // end of table. }; /** Record the 8-bit I/O port writing to boot script. @param[in] Port The I/O port to write. @param[in] Value The value to write to the I/O port. @retval Value The value written the I/O port. **/ UINT8 EFIAPI S3BootScriptSaveIoWrite8 ( IN UINTN Port, IN UINT8 Value ) { S3BootScriptSaveIoWrite ( S3BootScriptWidthUint8, Port, 1, &Value ); return Value; } /** Find the DEVICE_INFO for specified Device. @param[in] Device Pointer to the EFI_SIO_ACPI_DEVICE_ID. @retval DEVICE_INFO* Pointer to the DEVICE_INFO. **/ DEVICE_INFO * DeviceSearch ( IN EFI_SIO_ACPI_DEVICE_ID *Device ) { UINTN Index; for (Index = 0; Index < sizeof (mDeviceInfo) / sizeof (mDeviceInfo[0]); Index++) { if (CompareMem (Device, &mDeviceInfo[Index].Device, sizeof (*Device)) == 0) { return &mDeviceInfo[Index]; } } ASSERT (FALSE); return NULL; } /** Perform a 8-bit I/O write to SIO register. @param[in] Index The register index. @param[in] Data The value to write to register. **/ VOID WriteRegister ( IN UINT8 Index, IN UINT8 Data ) { mIoWrite8 (SIO_INDEX_PORT, Index); mIoWrite8 (SIO_DATA_PORT, Data); } /** Perform a 8-bit I/O read from SIO register. @param[in] Index The register index. @retval Value The value written to the register. **/ UINT8 ReadRegister ( IN UINT8 Index ) { IoWrite8 (SIO_INDEX_PORT, Index); return IoRead8 (SIO_DATA_PORT); } /** Program the SIO chip to enter the configure mode. **/ VOID EnterConfigMode ( VOID ) { mIoWrite8 (SIO_CONFIG_PORT, 0x87); mIoWrite8 (SIO_CONFIG_PORT, 0x87); } /** Program the SIO chip to exit the configure mode. **/ VOID ExitConfigMode ( VOID ) { mIoWrite8 (SIO_CONFIG_PORT, 0xAA); } /** Program the SIO chip to select the specified device. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. @retval DEVICE_INFO Pointer to DEVICE_INFO. **/ DEVICE_INFO * DeviceSelect ( IN EFI_SIO_ACPI_DEVICE_ID *Device ) { DEVICE_INFO *DeviceInfo; DeviceInfo = DeviceSearch (Device); WriteRegister (REG_SIO_LOGICAL_DEVICE, DeviceInfo->DeviceId); return DeviceInfo; } /** Program the SIO chip to set the IO base address of the specified device. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. @param[in] BaseAddress The base address to set. **/ VOID DeviceSetIo ( IN EFI_SIO_ACPI_DEVICE_ID *Device, IN UINT16 BaseAddress ) { DEVICE_INFO *DeviceInfo; DeviceInfo = DeviceSearch (Device); if ((DeviceInfo->ResourceMask & RESOURCE_IO) == RESOURCE_IO) { WriteRegister (R_SIO_BASE_ADDRESS_HIGH, (UINT8) (BaseAddress >> 8)); WriteRegister (R_SIO_BASE_ADDRESS_LOW, (UINT8) BaseAddress); } } /** Program the SIO chip to set the IRQ of the specified device. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. @param[in] Irq The IRQ to set. **/ VOID DeviceSetIrq ( IN EFI_SIO_ACPI_DEVICE_ID *Device, IN UINT8 Irq ) { DEVICE_INFO *DeviceInfo; DeviceInfo = DeviceSearch (Device); if ((DeviceInfo->ResourceMask & RESOURCE_IRQ) == RESOURCE_IRQ) { WriteRegister (R_SIO_PRIMARY_INTERRUPT_SELECT, Irq); } } /** Program the SIO chip to set the DMA of the specified device. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. @param[in] Dma The DMA to set. **/ VOID DeviceSetDma ( IN EFI_SIO_ACPI_DEVICE_ID *Device, IN UINT8 Dma ) { DEVICE_INFO *DeviceInfo; DeviceInfo = DeviceSearch (Device); if ((DeviceInfo->ResourceMask & RESOURCE_DMA) == RESOURCE_DMA) { WriteRegister (R_SIO_DMA_CHANNEL_SELECT, Dma); } } /** Get the ACPI resources for specified device. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. @param[out] Resources Pointer to ACPI_RESOURCE_HEADER_PTR. @retval EFI_SUCCESS The resources are returned successfully. **/ EFI_STATUS DeviceGetResources ( IN EFI_SIO_ACPI_DEVICE_ID *Device, OUT ACPI_RESOURCE_HEADER_PTR *Resources ) { DEVICE_INFO *DeviceInfo; DeviceInfo = DeviceSearch (Device); *Resources = DeviceInfo->Resources; return EFI_SUCCESS; } /** Set the ACPI resources for specified device. The SIO chip is programmed to use the new resources and the resources setting are saved. The function assumes the resources are valid. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. @param[in] Resources ACPI_RESOURCE_HEADER_PTR. @retval EFI_SUCCESS The resources are set successfully. **/ EFI_STATUS DeviceSetResources ( IN EFI_SIO_ACPI_DEVICE_ID *Device, IN ACPI_RESOURCE_HEADER_PTR Resources ) { DEVICE_INFO *DeviceInfo; ACPI_RESOURCE_HEADER_PTR ResourcePtr; ACPI_RESOURCE_HEADER_PTR ResourcePtr2; EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *FixedIo; EFI_ACPI_IRQ_DESCRIPTOR *Irq; EFI_ACPI_DMA_DESCRIPTOR *Dma; ResourcePtr = Resources; DeviceInfo = DeviceSearch (Device); EnterConfigMode (); DeviceSelect (Device); while (ResourcePtr.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) { switch (ResourcePtr.SmallHeader->Byte) { case ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR: FixedIo = (EFI_ACPI_FIXED_LOCATION_IO_PORT_DESCRIPTOR *) ResourcePtr.SmallHeader; DeviceSetIo (Device, FixedIo->BaseAddress); break; case ACPI_IRQ_NOFLAG_DESCRIPTOR: case ACPI_IRQ_DESCRIPTOR: Irq = (EFI_ACPI_IRQ_DESCRIPTOR *) ResourcePtr.SmallHeader; DeviceSetIrq (Device, (UINT8) LowBitSet32 ((UINT32) Irq->Mask)); break; case ACPI_DMA_DESCRIPTOR: Dma = (EFI_ACPI_DMA_DESCRIPTOR *) ResourcePtr.SmallHeader; DeviceSetDma (Device, (UINT8) LowBitSet32 ((UINT32) Dma->ChannelMask)); break; } // // Update the current resource // ResourcePtr2 = DeviceInfo->Resources; while (ResourcePtr2.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR) { if (ResourcePtr2.SmallHeader->Byte == ResourcePtr.SmallHeader->Byte) { if (ResourcePtr2.SmallHeader->Bits.Type == 0) { CopyMem ( ResourcePtr2.SmallHeader, ResourcePtr.SmallHeader, ResourcePtr.SmallHeader->Bits.Length + sizeof (*ResourcePtr.SmallHeader) ); } else { CopyMem ( ResourcePtr2.LargeHeader, ResourcePtr.LargeHeader, ResourcePtr.LargeHeader->Length + sizeof (*ResourcePtr.LargeHeader) ); } break; } if (ResourcePtr2.SmallHeader->Bits.Type == 0) { ResourcePtr2.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) ResourcePtr2.SmallHeader + ResourcePtr2.SmallHeader->Bits.Length + sizeof (*ResourcePtr2.SmallHeader)); } else { ResourcePtr2.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) ResourcePtr2.LargeHeader + ResourcePtr2.LargeHeader->Length + sizeof (*ResourcePtr2.LargeHeader)); } } // // Should find a match // ASSERT (ResourcePtr2.SmallHeader->Byte != ACPI_END_TAG_DESCRIPTOR); if (ResourcePtr.SmallHeader->Bits.Type == 0) { ResourcePtr.SmallHeader = (ACPI_SMALL_RESOURCE_HEADER *) ((UINT8 *) ResourcePtr.SmallHeader + ResourcePtr.SmallHeader->Bits.Length + sizeof (*ResourcePtr.SmallHeader)); } else { ResourcePtr.LargeHeader = (ACPI_LARGE_RESOURCE_HEADER *) ((UINT8 *) ResourcePtr.LargeHeader + ResourcePtr.LargeHeader->Length + sizeof (*ResourcePtr.LargeHeader)); } } ExitConfigMode (); return EFI_SUCCESS; } /** Get the possible ACPI resources for specified device. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. @param[out] Resources Pointer to ACPI_RESOURCE_HEADER_PTR. @retval EFI_SUCCESS The resources are returned successfully. **/ EFI_STATUS DevicePossibleResources ( IN EFI_SIO_ACPI_DEVICE_ID *Device, OUT ACPI_RESOURCE_HEADER_PTR *Resources ) { DEVICE_INFO *DeviceInfo; DeviceInfo = DeviceSearch (Device); *Resources = DeviceInfo->PossibleResources; return EFI_SUCCESS; } /** Program the SIO chip to enable the specified device using the default resource. @param[in] Device Pointer to EFI_SIO_ACPI_DEVICE_ID. **/ VOID DeviceEnable ( IN EFI_SIO_ACPI_DEVICE_ID *Device ) { ACPI_RESOURCE_HEADER_PTR Resources; DeviceGetResources (Device, &Resources); DeviceSetResources (Device, Resources); EnterConfigMode (); DeviceSelect (Device); WriteRegister (R_SIO_ACTIVATE, 1); ExitConfigMode (); } /** Return the supported devices. @param[out] Devices Pointer to pointer of EFI_SIO_ACPI_DEVICE_ID. Caller is responsible to free the buffer. @param[out] Count Pointer to UINTN holding the device count. **/ VOID DeviceGetList ( OUT EFI_SIO_ACPI_DEVICE_ID **Devices, OUT UINTN *Count ) { EFI_SIO_ACPI_DEVICE_ID *LocalDevices; UINTN LocalCount; // // Allocate enough memory for simplicity // LocalDevices = AllocatePool (sizeof (EFI_SIO_ACPI_DEVICE_ID) * 5); ASSERT (LocalDevices != NULL); if (LocalDevices != NULL){ LocalCount = 0; CopyMem (&LocalDevices[LocalCount], &mDeviceInfo[0].Device, sizeof (EFI_SIO_ACPI_DEVICE_ID)); LocalCount ++; *Devices = LocalDevices; *Count = LocalCount; } } /** Initialize the SIO chip for S3. **/ VOID SioInitAndSaveS3 ( VOID ) { EFI_SIO_ACPI_DEVICE_ID *Devices; HWMON_REGISTER_PAIR *RegisterPtr; UINTN Index; UINTN Count; // // LPC device is connected to PCH LPC interface on the // platform. Most PCH initialization work is done in Platform Code // such as Platform PEIM and Platform DXE driver. // Please make sure the initialization works are ready // This driver (NuvotonNct6776F) assumes that all PCH initialization has // been done before this driver is started to manage the device. // // // Use S3BootScriptSaveIoWrite8(...) function. This will simply add the // required IO to the S3 table. // mIoWrite8 = S3BootScriptSaveIoWrite8; // // The procedure DeviceGetList has ben modified to only return the UART A. // However, UART A is actually initialized at PEI, so here UART A is only // saved to the SIO table, no actual IO is required. // DeviceGetList (&Devices, &Count); for (Index = 0; Index < Count; Index++) { DeviceEnable (&Devices[Index]); } FreePool (Devices); // // Generic programming: SIO global registers, disable unused devices, force // all unused possible GPIO pins to input GPIO. This will both access the // IO and save it to the S3 table. This initialization was not done at PEI. // RegisterPtr = mGenericRegisters; // // Use S3IoWrite8(...) function. This will both access the IO and save it // to the S3 table. // mIoWrite8 = S3IoWrite8; EnterConfigMode(); while (RegisterPtr->Register != 0xaa) { WriteRegister (RegisterPtr->Register, RegisterPtr->Value); RegisterPtr++; } ExitConfigMode(); // // Return to simple IO access. // mIoWrite8 = IoWrite8; } /* Here starts Hardware Monitor Specific Code */ VOID SetHwMonRegister ( IN UINT8 Register, IN UINT8 Value ) { S3IoWrite8 (HWMON_INDEX_ADDRESS, Register); S3IoWrite8 (HWMON_DATA_ADDRESS, Value); } EFI_STATUS EFIAPI Nct6776fHwMonStart( VOID ) { EFI_STATUS Status = 0; EFI_SIO_ACPI_DEVICE_ID HwMonDevice; DEBUG ((DEBUG_INFO, "SIO Nct6776fHwMonStart\n")); //Move Lpc decode programing to Platform code. //S3IoWrite32 (0xcf8, LpcControlRegister); //S3IoWrite32 (0xcfc, (UINT32) (0x001C0001 | HWMON_BASE_ADDRESS)); mIoWrite8 = S3IoWrite8; HwMonDevice.HID = EISA_PNP_ID(0xc08); HwMonDevice.UID = 0; DeviceEnable(&HwMonDevice); mIoWrite8 = IoWrite8; return Status; } EFI_STATUS EFIAPI Nct6776fHwMonSetBankValues ( IN HWMON_REGISTER_PAIR *RegisterPair, IN UINT8 Bank, IN UINT8 RegisterCount ) { EFI_STATUS Status = 0; HWMON_REGISTER_PAIR *BankValuesPtr; UINT8 BankValue = 0x80; UINT8 i; BankValue |= Bank; BankValuesPtr = RegisterPair; SetHwMonRegister (0x4e, BankValue); for (i=0; iRegister, BankValuesPtr->Value ); BankValuesPtr++; } return Status; } EFI_STATUS EFIAPI Nct6776fHwMonReadRegister ( IN UINT8 Register, OUT UINT8 *Value ) { EFI_STATUS Status = 0; IoWrite8 (HWMON_INDEX_ADDRESS, Register); *Value = IoRead8 (HWMON_DATA_ADDRESS); return Status; }