summaryrefslogtreecommitdiff
path: root/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c
diff options
context:
space:
mode:
Diffstat (limited to 'Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c')
-rw-r--r--Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c588
1 files changed, 588 insertions, 0 deletions
diff --git a/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c
new file mode 100644
index 00000000..fb404b17
--- /dev/null
+++ b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c
@@ -0,0 +1,588 @@
+/** @file
+
+ Copyright (c) 2017, 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 "SynQuacerI2cDxe.h"
+
+#define BOOTTIME_DEBUG(x) do { if (!EfiAtRuntime()) DEBUG (x); } while (0)
+
+//
+// We cannot use Stall () or timer events at runtime, so we need to busy-wait
+// for the controller to signal the completion interrupts. This value was
+// arbitrarily chosen, and does not appear to produce any premature timeouts
+// nor does it result in noticeable stalls in case of bus errors.
+//
+#define WAIT_FOR_INTERRUPT_TIMEOUT 50000
+
+/**
+ Set the frequency for the I2C clock line.
+
+ This routine must be called at or below TPL_NOTIFY.
+
+ The software and controller do a best case effort of using the specified
+ frequency for the I2C bus. If the frequency does not match exactly then
+ the I2C master protocol selects the next lower frequency to avoid
+ exceeding the operating conditions for any of the I2C devices on the bus.
+ For example if 400 KHz was specified and the controller's divide network
+ only supports 402 KHz or 398 KHz then the I2C master protocol selects 398
+ KHz. If there are not lower frequencies available, then return
+ EFI_UNSUPPORTED.
+
+ @param[in] This Pointer to an EFI_I2C_MASTER_PROTOCOL structure
+ @param[in] BusClockHertz Pointer to the requested I2C bus clock frequency
+ in Hertz. Upon return this value contains the
+ actual frequency in use by the I2C controller.
+
+ @retval EFI_SUCCESS The bus frequency was set successfully.
+ @retval EFI_ALREADY_STARTED The controller is busy with another transaction.
+ @retval EFI_INVALID_PARAMETER BusClockHertz is NULL
+ @retval EFI_UNSUPPORTED The controller does not support this frequency.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SynQuacerI2cSetBusFrequency (
+ IN CONST EFI_I2C_MASTER_PROTOCOL *This,
+ IN OUT UINTN *BusClockHertz
+ )
+{
+ SYNQUACER_I2C_MASTER *I2c;
+ UINT8 Ccr, Csr;
+
+ I2c = SYNQUACER_I2C_FROM_THIS (This);
+
+ if (BusClockHertz == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*BusClockHertz >= F_I2C_SPEED_FM) {
+ if (REFCLK_RATE <= F_I2C_CLK_RATE_18M) {
+ Ccr = F_I2C_CCR_CS_FAST_MAX_18M (REFCLK_RATE);
+ Csr = F_I2C_CSR_CS_FAST_MAX_18M (REFCLK_RATE);
+ } else {
+ Ccr = F_I2C_CCR_CS_FAST_MIN_18M (REFCLK_RATE);
+ Csr = F_I2C_CSR_CS_FAST_MIN_18M (REFCLK_RATE);
+ }
+
+ // Set Clock and enable, Set fast mode
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_CCR,
+ Ccr | F_I2C_CCR_FM | F_I2C_CCR_EN);
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_CSR, Csr);
+
+ *BusClockHertz = F_I2C_SPEED_FM;
+
+ } else if (*BusClockHertz >= F_I2C_SPEED_SM) {
+ if (REFCLK_RATE <= F_I2C_CLK_RATE_18M) {
+ Ccr = F_I2C_CCR_CS_STANDARD_MAX_18M (REFCLK_RATE);
+ Csr = F_I2C_CSR_CS_STANDARD_MAX_18M (REFCLK_RATE);
+ } else {
+ Ccr = F_I2C_CCR_CS_STANDARD_MIN_18M (REFCLK_RATE);
+ Csr = F_I2C_CSR_CS_STANDARD_MIN_18M (REFCLK_RATE);
+ }
+
+ // Set Clock and enable, Set standard mode
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_CCR, Ccr | F_I2C_CCR_EN);
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_CSR, Csr);
+
+ *BusClockHertz = F_I2C_SPEED_SM;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ MemoryFence ();
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reset the I2C controller and configure it for use
+
+ This routine must be called at or below TPL_NOTIFY.
+
+ The I2C controller is reset. The caller must call SetBusFrequench() after
+ calling Reset().
+
+ @param[in] This Pointer to an EFI_I2C_MASTER_PROTOCOL structure.
+
+ @retval EFI_SUCCESS The reset completed successfully.
+ @retval EFI_ALREADY_STARTED The controller is busy with another transaction.
+ @retval EFI_DEVICE_ERROR The reset operation failed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SynQuacerI2cReset (
+ IN CONST EFI_I2C_MASTER_PROTOCOL *This
+ )
+{
+ SYNQUACER_I2C_MASTER *I2c;
+
+ I2c = SYNQUACER_I2C_FROM_THIS (This);
+
+ // Disable the clock
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_CCR, 0);
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_CSR, 0);
+
+ MemoryFence ();
+
+ // Set own Address
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_ADR, 0);
+
+ // Set PCLK frequency
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_FSR, F_I2C_BUS_CLK_FR (REFCLK_RATE));
+
+ // clear IRQ (INT=0, BER=0), Interrupt Disable
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_BCR, 0);
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_BC2R, 0);
+
+ MemoryFence ();
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SynQuacerI2cMasterStart (
+ IN SYNQUACER_I2C_MASTER *I2c,
+ IN UINTN SlaveAddress,
+ IN EFI_I2C_OPERATION *Op
+ )
+{
+ UINT8 Bsr;
+ UINT8 Bcr;
+
+ if (Op->Flags & I2C_FLAG_READ) {
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_DAR, (SlaveAddress << 1) | 1);
+ } else {
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_DAR, SlaveAddress << 1);
+ }
+
+ BOOTTIME_DEBUG ((DEBUG_INFO, "%a: slave:0x%02x\n", __FUNCTION__,
+ SlaveAddress));
+
+ Bsr = MmioRead8 (I2c->MmioBase + F_I2C_REG_BSR);
+ Bcr = MmioRead8 (I2c->MmioBase + F_I2C_REG_BCR);
+
+ if ((Bsr & F_I2C_BSR_BB) && !(Bcr & F_I2C_BCR_MSS)) {
+ BOOTTIME_DEBUG ((DEBUG_INFO, "%a: bus is busy\n", __FUNCTION__));
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (Bsr & F_I2C_BSR_BB) { // Bus is busy
+ BOOTTIME_DEBUG ((DEBUG_INFO, "%a: Continuous Start\n", __FUNCTION__));
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_BCR, Bcr | F_I2C_BCR_SCC);
+ } else {
+ if (Bcr & F_I2C_BCR_MSS) {
+ BOOTTIME_DEBUG ((DEBUG_WARN,
+ "%a: is not in master mode\n", __FUNCTION__));
+ return EFI_DEVICE_ERROR;
+ }
+ BOOTTIME_DEBUG ((DEBUG_INFO, "%a: Start Condition\n", __FUNCTION__));
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_BCR,
+ Bcr | F_I2C_BCR_MSS | F_I2C_BCR_INTE | F_I2C_BCR_BEIE);
+ }
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+WaitForInterrupt (
+ IN SYNQUACER_I2C_MASTER *I2c
+ )
+{
+ UINT8 Bsr;
+ UINTN Timeout = WAIT_FOR_INTERRUPT_TIMEOUT;
+
+ do {
+ MemoryFence ();
+
+ Bsr = MmioRead8 (I2c->MmioBase + F_I2C_REG_BCR);
+ if (Bsr & F_I2C_BCR_INT) {
+ return EFI_SUCCESS;
+ }
+ } while (Timeout--);
+
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Start an I2C transaction on the host controller.
+
+ This routine must be called at or below TPL_NOTIFY. For synchronous
+ requests this routine must be called at or below TPL_CALLBACK.
+
+ This function initiates an I2C transaction on the controller. To
+ enable proper error handling by the I2C protocol stack, the I2C
+ master protocol does not support queuing but instead only manages
+ one I2C transaction at a time. This API requires that the I2C bus
+ is in the correct configuration for the I2C transaction.
+
+ The transaction is performed by sending a start-bit and selecting the
+ I2C device with the specified I2C slave address and then performing
+ the specified I2C operations. When multiple operations are requested
+ they are separated with a repeated start bit and the slave address.
+ The transaction is terminated with a stop bit.
+
+ When Event is NULL, StartRequest operates synchronously and returns
+ the I2C completion status as its return value.
+
+ When Event is not NULL, StartRequest synchronously returns EFI_SUCCESS
+ indicating that the I2C transaction was started asynchronously. The
+ transaction status value is returned in the buffer pointed to by
+ I2cStatus upon the completion of the I2C transaction when I2cStatus
+ is not NULL. After the transaction status is returned the Event is
+ signaled.
+
+ Note: The typical consumer of this API is the I2C host protocol.
+ Extreme care must be taken by other consumers of this API to prevent
+ confusing the third party I2C drivers due to a state change at the
+ I2C device which the third party I2C drivers did not initiate. I2C
+ platform specific code may use this API within these guidelines.
+
+ @param[in] This Pointer to an EFI_I2C_MASTER_PROTOCOL structure.
+ @param[in] SlaveAddress Address of the device on the I2C bus. Set the
+ I2C_ADDRESSING_10_BIT when using 10-bit addresses,
+ clear this bit for 7-bit addressing. Bits 0-6
+ are used for 7-bit I2C slave addresses and bits
+ 0-9 are used for 10-bit I2C slave addresses.
+ @param[in] RequestPacket Pointer to an EFI_I2C_REQUEST_PACKET
+ structure describing the I2C transaction.
+ @param[in] Event Event to signal for asynchronous transactions,
+ NULL for synchronous transactions
+ @param[out] I2cStatus Optional buffer to receive the I2C transaction
+ completion status
+
+ @retval EFI_SUCCESS The asynchronous transaction was successfully
+ started when Event is not NULL.
+ @retval EFI_SUCCESS The transaction completed successfully when
+ Event is NULL.
+ @retval EFI_ALREADY_STARTED The controller is busy with another transaction.
+ @retval EFI_BAD_BUFFER_SIZE The RequestPacket->LengthInBytes value is too
+ large.
+ @retval EFI_DEVICE_ERROR There was an I2C error (NACK) during the
+ transaction.
+ @retval EFI_INVALID_PARAMETER RequestPacket is NULL
+ @retval EFI_NOT_FOUND Reserved bit set in the SlaveAddress parameter
+ @retval EFI_NO_RESPONSE The I2C device is not responding to the slave
+ address. EFI_DEVICE_ERROR will be returned if
+ the controller cannot distinguish when the NACK
+ occurred.
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory for I2C transaction
+ @retval EFI_UNSUPPORTED The controller does not support the requested
+ transaction.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SynQuacerI2cStartRequest (
+ IN CONST EFI_I2C_MASTER_PROTOCOL *This,
+ IN UINTN SlaveAddress,
+ IN EFI_I2C_REQUEST_PACKET *RequestPacket,
+ IN EFI_EVENT Event OPTIONAL,
+ OUT EFI_STATUS *I2cStatus OPTIONAL
+ )
+{
+ SYNQUACER_I2C_MASTER *I2c;
+ UINTN Idx;
+ EFI_I2C_OPERATION *Op;
+ UINTN BufIdx;
+ EFI_STATUS Status;
+ EFI_TPL Tpl;
+ BOOLEAN AtRuntime;
+ UINT8 Bsr;
+ UINT8 Bcr;
+
+ I2c = SYNQUACER_I2C_FROM_THIS (This);
+
+ //
+ // We can only do synchronous operations at runtime
+ //
+ AtRuntime = EfiAtRuntime ();
+ if (AtRuntime && Event != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (!AtRuntime) {
+ Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+ }
+
+ for (Idx = 0, Op = RequestPacket->Operation, Status = EFI_SUCCESS;
+ Idx < RequestPacket->OperationCount && !EFI_ERROR (Status);
+ Idx++, Op++) {
+
+ Status = SynQuacerI2cMasterStart (I2c, SlaveAddress, Op);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = WaitForInterrupt (I2c);
+ if (EFI_ERROR (Status)) {
+ BOOTTIME_DEBUG ((DEBUG_WARN, "%a: Timeout waiting for interrupt - %r\n",
+ Status));
+ break;
+ }
+
+ if (MmioRead8 (I2c->MmioBase + F_I2C_REG_BSR) & F_I2C_BSR_LRB) {
+ BOOTTIME_DEBUG ((DEBUG_WARN, "%a: No ack received - %r\n", __FUNCTION__));
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ BufIdx = 0;
+ do {
+ Bsr = MmioRead8 (I2c->MmioBase + F_I2C_REG_BSR);
+ Bcr = MmioRead8 (I2c->MmioBase + F_I2C_REG_BCR);
+
+ if (Bcr & F_I2C_BCR_BER) {
+ BOOTTIME_DEBUG ((DEBUG_WARN, "%a: Bus error detected\n", __FUNCTION__));
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ if ((Bsr & F_I2C_BSR_AL) || !(Bcr & F_I2C_BCR_MSS)) {
+ BOOTTIME_DEBUG ((DEBUG_WARN, "%a: Arbitration lost\n", __FUNCTION__));
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ if (Op->Flags & I2C_FLAG_READ) {
+ if (BufIdx == Op->LengthInBytes - 1) {
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_BCR,
+ F_I2C_BCR_MSS | F_I2C_BCR_INTE | F_I2C_BCR_BEIE);
+ } else {
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_BCR,
+ F_I2C_BCR_MSS | F_I2C_BCR_INTE | F_I2C_BCR_BEIE | F_I2C_BCR_ACK);
+ }
+
+ Status = WaitForInterrupt (I2c);
+ if (EFI_ERROR (Status)) {
+ BOOTTIME_DEBUG ((DEBUG_WARN,
+ "%a: Timeout waiting for interrupt - %r\n", __FUNCTION__, Status));
+ break;
+ }
+
+ if (!(MmioRead8 (I2c->MmioBase + F_I2C_REG_BSR) & F_I2C_BSR_FBT)) {
+ Op->Buffer [BufIdx++] = MmioRead8 (I2c->MmioBase + F_I2C_REG_DAR);
+ }
+ } else {
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_DAR, Op->Buffer [BufIdx++]);
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_BCR,
+ F_I2C_BCR_MSS | F_I2C_BCR_INTE | F_I2C_BCR_BEIE);
+
+ Status = WaitForInterrupt (I2c);
+ if (EFI_ERROR (Status)) {
+ BOOTTIME_DEBUG ((DEBUG_WARN,
+ "%a: Timeout waiting for interrupt - %r\n", __FUNCTION__, Status));
+ break;
+ }
+
+ if (MmioRead8 (I2c->MmioBase + F_I2C_REG_BSR) & F_I2C_BSR_LRB) {
+ BOOTTIME_DEBUG ((DEBUG_WARN, "%a: No ack received\n", __FUNCTION__));
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+ }
+ } while (BufIdx < Op->LengthInBytes);
+ }
+
+ // Stop the transfer
+ MmioWrite8 (I2c->MmioBase + F_I2C_REG_BCR, 0);
+
+ if (!AtRuntime) {
+ gBS->RestoreTPL (Tpl);
+ }
+
+ if (Event) {
+ *I2cStatus = Status;
+ gBS->SignalEvent (Event);
+ }
+ return Status;
+}
+
+STATIC CONST EFI_I2C_CONTROLLER_CAPABILITIES mI2cControllerCapabilities = {
+ sizeof (EFI_I2C_CONTROLLER_CAPABILITIES), // StructureSizeInBytes
+ MAX_UINT32, // MaximumReceiveBytes
+ MAX_UINT32, // MaximumTransmitBytes
+ MAX_UINT32, // MaximumTotalBytes
+};
+
+STATIC
+VOID
+EFIAPI
+SynQuacerI2cVirtualNotifyEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ SYNQUACER_I2C_MASTER *I2c = Context;
+
+ EfiConvertPointer (0x0, (VOID **)&I2c->I2cMaster.SetBusFrequency);
+ EfiConvertPointer (0x0, (VOID **)&I2c->I2cMaster.Reset);
+ EfiConvertPointer (0x0, (VOID **)&I2c->I2cMaster.StartRequest);
+ EfiConvertPointer (0x0, (VOID **)&I2c->I2cMaster.I2cControllerCapabilities);
+ EfiConvertPointer (0x0, (VOID **)&I2c->MmioBase);
+}
+
+EFI_STATUS
+SynQuacerI2cInit (
+ IN EFI_HANDLE DriverBindingHandle,
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_STATUS Status;
+ NON_DISCOVERABLE_DEVICE *Dev;
+ SYNQUACER_I2C_MASTER *I2c;
+ BOOLEAN Runtime;
+
+ Status = gBS->OpenProtocol (ControllerHandle,
+ &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ (VOID **)&Dev, DriverBindingHandle,
+ ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Runtime = CompareGuid (Dev->Type,
+ &gSynQuacerNonDiscoverableRuntimeI2cMasterGuid);
+
+ // Allocate Resources
+ if (Runtime) {
+ I2c = AllocateRuntimeZeroPool (sizeof (SYNQUACER_I2C_MASTER));
+ } else {
+ I2c = AllocateZeroPool (sizeof (SYNQUACER_I2C_MASTER));
+ }
+ if (I2c == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto CloseProtocol;
+ }
+
+ I2c->Signature = SYNQUACER_I2C_SIGNATURE;
+ I2c->I2cMaster.SetBusFrequency = SynQuacerI2cSetBusFrequency;
+ I2c->I2cMaster.Reset = SynQuacerI2cReset;
+ I2c->I2cMaster.StartRequest = SynQuacerI2cStartRequest;
+ I2c->I2cMaster.I2cControllerCapabilities = &mI2cControllerCapabilities;
+ I2c->MmioBase = Dev->Resources[0].AddrRangeMin;
+ I2c->Dev = Dev;
+
+ if (Runtime) {
+ I2c->Runtime = TRUE;
+
+ // Declare the controller as EFI_MEMORY_RUNTIME
+ Status = gDS->AddMemorySpace (
+ EfiGcdMemoryTypeMemoryMappedIo,
+ Dev->Resources[0].AddrRangeMin,
+ Dev->Resources[0].AddrLen,
+ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
+ if (EFI_ERROR (Status)) {
+ BOOTTIME_DEBUG ((DEBUG_WARN, "%a: failed to add memory space - %r\n",
+ __FUNCTION__, Status));
+ }
+
+ Status = gDS->SetMemorySpaceAttributes (
+ Dev->Resources[0].AddrRangeMin,
+ Dev->Resources[0].AddrLen,
+ EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
+ if (EFI_ERROR (Status)) {
+ goto FreeDevice;
+ }
+
+ //
+ // Register for the virtual address change event
+ //
+ Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+ SynQuacerI2cVirtualNotifyEvent, I2c,
+ &gEfiEventVirtualAddressChangeGuid,
+ &I2c->VirtualAddressChangeEvent);
+ if (EFI_ERROR (Status)) {
+ goto FreeDevice;
+ }
+ }
+
+ CopyGuid (&I2c->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);
+ I2c->DevicePath.MmioBase = I2c->MmioBase;
+ SetDevicePathNodeLength (&I2c->DevicePath.Vendor,
+ sizeof (I2c->DevicePath) - sizeof (I2c->DevicePath.End));
+ SetDevicePathEndNode (&I2c->DevicePath.End);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (&ControllerHandle,
+ &gEfiI2cMasterProtocolGuid, &I2c->I2cMaster,
+ &gEfiDevicePathProtocolGuid, &I2c->DevicePath,
+ NULL);
+ if (EFI_ERROR (Status)) {
+ goto CloseEvent;
+ }
+ return EFI_SUCCESS;
+
+CloseEvent:
+ if (Runtime) {
+ gBS->CloseEvent (I2c->VirtualAddressChangeEvent);
+ }
+
+FreeDevice:
+ FreePool (I2c);
+
+CloseProtocol:
+ gBS->CloseProtocol (ControllerHandle,
+ &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ DriverBindingHandle,
+ ControllerHandle);
+ return Status;
+}
+
+EFI_STATUS
+SynQuacerI2cRelease (
+ IN EFI_HANDLE DriverBindingHandle,
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_I2C_MASTER_PROTOCOL *I2cMaster;
+ SYNQUACER_I2C_MASTER *I2c;
+ EFI_STATUS Status;
+
+ Status = gBS->HandleProtocol (ControllerHandle,
+ &gEfiI2cMasterProtocolGuid,
+ (VOID **)&I2cMaster);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ I2c = SYNQUACER_I2C_FROM_THIS (I2cMaster);
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (ControllerHandle,
+ &gEfiI2cMasterProtocolGuid, I2cMaster,
+ &gEfiDevicePathProtocolGuid, &I2c->DevicePath,
+ NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (I2c->Runtime) {
+ gBS->CloseEvent (I2c->VirtualAddressChangeEvent);
+ }
+
+ Status = gBS->CloseProtocol (ControllerHandle,
+ &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ DriverBindingHandle,
+ ControllerHandle);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ gBS->FreePool (I2c);
+
+ return EFI_SUCCESS;
+}