summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2017-11-03 09:40:18 +0000
committerArd Biesheuvel <ard.biesheuvel@linaro.org>2017-11-10 14:12:38 +0000
commit8e7a8c1ce2ed2e52fc787ec145e3d6b77b86ae6e (patch)
treebd37c2c2b77f44a259d03b0cbbe7bc0ec0f4ff87
parent5fb155bcfc3a071920b5f8211e1e4ed505b58194 (diff)
Silicon/Socionext: implement I2C master protocol for SynQuacer I2C
Add a driver that produces the I2C master protocol on top of the I2C controllers that are implemented in the SynQuacer Socionext SoC. Note that this supports two modes simultaneously: I2C controllers that are only usable at boot time, and usable via the I2C protocol stack, and I2C controllers that are dedicated for the RTC or other runtime components. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
-rw-r--r--Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/ComponentName.c185
-rw-r--r--Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/DriverBinding.c238
-rw-r--r--Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c586
-rw-r--r--Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.h162
-rw-r--r--Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.inf59
-rw-r--r--Silicon/Socionext/SynQuacer/SynQuacer.dec5
6 files changed, 1235 insertions, 0 deletions
diff --git a/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/ComponentName.c b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/ComponentName.c
new file mode 100644
index 0000000..8d7aa41
--- /dev/null
+++ b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/ComponentName.c
@@ -0,0 +1,185 @@
+/** @file
+
+ Copyright (c) 2017, Linaro Ltd. All rights reserved.
+
+ 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"
+
+STATIC EFI_UNICODE_STRING_TABLE mSynQuacerI2cDriverNameTable[] = {
+ {
+ "en",
+ (CHAR16 *)L"Socionext SynQuacer I2C Driver"
+ },
+ { }
+};
+
+STATIC EFI_UNICODE_STRING_TABLE mSynQuacerI2cControllerNameTable[] = {
+ {
+ "en",
+ (CHAR16 *)L"Socionext SynQuacer I2C Controller"
+ },
+ { }
+};
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param Language[in] A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param DriverName[out] A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SynQuacerI2cGetDriverName (
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (Language,
+ This->SupportedLanguages,
+ mSynQuacerI2cDriverNameTable,
+ DriverName,
+ FALSE);
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param ControllerHandle[in] The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param ChildHandle[in] The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param Language[in] A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param ControllerName[out] A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SynQuacerI2cGetControllerName (
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ if (ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return LookupUnicodeString2 (Language,
+ This->SupportedLanguages,
+ mSynQuacerI2cControllerNameTable,
+ ControllerName,
+ FALSE);
+}
+
+//
+// EFI Component Name 2 Protocol
+//
+EFI_COMPONENT_NAME2_PROTOCOL gSynQuacerI2cDriverComponentName2 = {
+ SynQuacerI2cGetDriverName,
+ SynQuacerI2cGetControllerName,
+ "en"
+};
diff --git a/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/DriverBinding.c b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/DriverBinding.c
new file mode 100644
index 0000000..22e5340
--- /dev/null
+++ b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/DriverBinding.c
@@ -0,0 +1,238 @@
+/** @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"
+
+/**
+ Tests to see if this driver supports a given controller.
+
+ @param This[in] A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param ControllerHandle[in] The handle of the controller to test.
+ @param RemainingDevicePath[in] The remaining device path.
+ (Ignored - this is not a bus driver.)
+
+ @retval EFI_SUCCESS The driver supports this controller.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle is
+ already being managed by the driver specified
+ by This.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle is
+ not supported by the driver specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+SynQuacerI2cDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ NON_DISCOVERABLE_DEVICE *Dev;
+ EFI_STATUS Status;
+
+ //
+ // Connect to the USB stack
+ //
+ Status = gBS->OpenProtocol (ControllerHandle,
+ &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ (VOID **) &Dev,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (CompareGuid (Dev->Type, &gSynQuacerNonDiscoverableI2cMasterGuid) ||
+ CompareGuid (Dev->Type, &gSynQuacerNonDiscoverableRuntimeI2cMasterGuid)) {
+ Status = EFI_SUCCESS;
+ } else {
+ Status = EFI_UNSUPPORTED;
+ }
+
+ //
+ // Clean up.
+ //
+ gBS->CloseProtocol (ControllerHandle,
+ &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle);
+
+ return Status;
+}
+
+
+/**
+ Starts a device controller or a bus controller.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the device to start. This
+ handle must support a protocol interface that
+ supplies an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath The remaining portion of the device path.
+ (Ignored - this is not a bus driver.)
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a
+ device error.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
+ lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+SynQuacerI2cDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ return SynQuacerI2cInit (This->DriverBindingHandle, ControllerHandle);
+}
+
+
+/**
+ Stops a device controller or a bus controller.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle
+ must support a bus specific I/O protocol for the
+ driver to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in
+ ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be
+ NULL if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device
+ error.
+
+**/
+EFI_STATUS
+EFIAPI
+SynQuacerI2cDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ return SynQuacerI2cRelease (This->DriverBindingHandle, ControllerHandle);
+}
+
+
+STATIC EFI_DRIVER_BINDING_PROTOCOL gSynQuacerI2cDriverBinding = {
+ SynQuacerI2cDriverBindingSupported,
+ SynQuacerI2cDriverBindingStart,
+ SynQuacerI2cDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+
+/**
+ The entry point of I2c UEFI Driver.
+
+ @param ImageHandle The image handle of the UEFI Driver.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The Driver or UEFI Driver exited normally.
+ @retval EFI_INCOMPATIBLE_VERSION _gUefiDriverRevision is greater than
+ SystemTable->Hdr.Revision.
+
+**/
+EFI_STATUS
+EFIAPI
+SynQuacerI2cDxeEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Add the driver to the list of drivers
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle, SystemTable, &gSynQuacerI2cDriverBinding, ImageHandle,
+ NULL, &gSynQuacerI2cDriverComponentName2);
+ ASSERT_EFI_ERROR (Status);
+
+ DEBUG ((DEBUG_INFO, "*** Installed SynQuacer I2C UEFI driver! ***\n"));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Unload function for the I2c UEFI Driver.
+
+ @param ImageHandle[in] The allocated handle for the EFI image
+
+ @retval EFI_SUCCESS The driver was unloaded successfully
+ @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
+
+**/
+EFI_STATUS
+EFIAPI
+SynQuacerI2cDxeUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN HandleCount;
+ UINTN Index;
+
+ //
+ // Retrieve all USB I/O handles in the handle database
+ //
+ Status = gBS->LocateHandleBuffer (ByProtocol,
+ &gEdkiiNonDiscoverableDeviceProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Disconnect the driver from the handles in the handle database
+ //
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->DisconnectController (HandleBuffer[Index],
+ gImageHandle,
+ NULL);
+ }
+
+ //
+ // Free the handle array
+ //
+ gBS->FreePool (HandleBuffer);
+
+ //
+ // Uninstall protocols installed by the driver in its entrypoint
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gSynQuacerI2cDriverBinding,
+ NULL
+ );
+
+ return EFI_SUCCESS;
+}
diff --git a/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c
new file mode 100644
index 0000000..0a2d052
--- /dev/null
+++ b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.c
@@ -0,0 +1,586 @@
+/** @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 __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);
+ }
+
+ __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)) {
+ __DEBUG ((DEBUG_INFO, "%a: bus is busy\n", __FUNCTION__));
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (Bsr & F_I2C_BSR_BB) { // Bus is busy
+ __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) {
+ __DEBUG ((DEBUG_WARN, "%a: is not in master mode\n", __FUNCTION__));
+ return EFI_DEVICE_ERROR;
+ }
+ __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)) {
+ __DEBUG ((DEBUG_WARN, "%a: Timeout waiting for interrupt - %r\n",
+ Status));
+ break;
+ }
+
+ if (MmioRead8 (I2c->MmioBase + F_I2C_REG_BSR) & F_I2C_BSR_LRB) {
+ __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) {
+ __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)) {
+ __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)) {
+ __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)) {
+ __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) {
+ __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)) {
+ __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;
+}
diff --git a/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.h b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.h
new file mode 100644
index 0000000..8c3d7ef
--- /dev/null
+++ b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.h
@@ -0,0 +1,162 @@
+/** @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.
+
+**/
+
+#ifndef __SYNQUACER_I2C_DXE_H__
+#define __SYNQUACER_I2C_DXE_H__
+
+#include <PiDxe.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <Protocol/I2cMaster.h>
+#include <Protocol/NonDiscoverableDevice.h>
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gSynQuacerI2cDriverComponentName2;
+
+#define SYNQUACER_I2C_SIGNATURE SIGNATURE_32 ('S', 'I', '2', 'C')
+#define SYNQUACER_I2C_FROM_THIS(a) CR (a, SYNQUACER_I2C_MASTER, \
+ I2cMaster, SYNQUACER_I2C_SIGNATURE)
+
+#pragma pack(1)
+typedef struct {
+ VENDOR_DEVICE_PATH Vendor;
+ UINT64 MmioBase;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} SYNQUACER_I2C_DEVICE_PATH;
+#pragma pack()
+
+typedef struct {
+ UINT32 Signature;
+ EFI_I2C_MASTER_PROTOCOL I2cMaster;
+ EFI_PHYSICAL_ADDRESS MmioBase;
+ SYNQUACER_I2C_DEVICE_PATH DevicePath;
+ NON_DISCOVERABLE_DEVICE *Dev;
+ EFI_EVENT VirtualAddressChangeEvent;
+ BOOLEAN Runtime;
+} SYNQUACER_I2C_MASTER;
+
+EFI_STATUS
+SynQuacerI2cInit (
+ IN EFI_HANDLE DriverBindingHandle,
+ IN EFI_HANDLE ControllerHandle
+ );
+
+EFI_STATUS
+SynQuacerI2cRelease (
+ IN EFI_HANDLE DriverBindingHandle,
+ IN EFI_HANDLE ControllerHandle
+ );
+
+#define REFCLK_RATE FixedPcdGet32 (PcdI2cReferenceClock)
+
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#define F_I2C_SPEED_SM 100000
+#define F_I2C_SPEED_FM 400000
+
+// I2C register adress definitions
+#define F_I2C_REG_BSR (0x00 << 2) // Bus Status Regster
+#define F_I2C_REG_BCR (0x01 << 2) // Bus Control Register
+#define F_I2C_REG_CCR (0x02 << 2) // Clock Control Register
+#define F_I2C_REG_ADR (0x03 << 2) // Address Register
+#define F_I2C_REG_DAR (0x04 << 2) // Data Register
+#define F_I2C_REG_CSR (0x05 << 2) // Expansion CS Register
+#define F_I2C_REG_FSR (0x06 << 2) // Bus Clock Frequency Register
+#define F_I2C_REG_BC2R (0x07 << 2) // Bus Control 2 Register
+
+// I2C register bit definitions
+#define F_I2C_BSR_FBT BIT0 // First Byte Transfer
+#define F_I2C_BSR_GCA BIT1 // General Call Address
+#define F_I2C_BSR_AAS BIT2 // Address as Slave
+#define F_I2C_BSR_TRX BIT3 // Transfer/Receive
+#define F_I2C_BSR_LRB BIT4 // Last Received Bit
+#define F_I2C_BSR_AL BIT5 // Arbitration Lost
+#define F_I2C_BSR_RSC BIT6 // Repeated Start Condition
+#define F_I2C_BSR_BB BIT7 // Bus Busy
+
+#define F_I2C_BCR_INT BIT0 // Interrupt
+#define F_I2C_BCR_INTE BIT1 // Interrupt Enable
+#define F_I2C_BCR_GCAA BIT2 // General Call Access Acknowledge
+#define F_I2C_BCR_ACK BIT3 // Acknowledge
+#define F_I2C_BCR_MSS BIT4 // Master Slave Select
+#define F_I2C_BCR_SCC BIT5 // Start Condition Continue
+#define F_I2C_BCR_BEIE BIT6 // Bus Error Interrupt Enable
+#define F_I2C_BCR_BER BIT7 // Bus Error
+
+#define F_I2C_CCR_CS_MASK 0x1f // CCR Clock Period Select
+#define F_I2C_CCR_EN BIT5 // Enable
+#define F_I2C_CCR_FM BIT6 // Speed Mode Select
+
+#define F_I2C_CSR_CS_MASK 0x3f // CSR Clock Period Select
+
+#define F_I2C_BC2R_SCLL BIT0 // SCL Low Drive
+#define F_I2C_BC2R_SDAL BIT1 // SDA Low Drive
+#define F_I2C_BC2R_SCLS BIT4 // SCL Status
+#define F_I2C_BC2R_SDAS BIT5 // SDA Status
+
+// PCLK frequency
+#define F_I2C_BUS_CLK_FR(rate) ((rate) / 20000000 + 1)
+
+#define F_I2C_MIN_CLK_RATE (14 * 1000000) // min frequency 14 MHz
+#define F_I2C_MAX_CLK_RATE (200 * 1000000) // max frequency 200 MHz
+#define F_I2C_CLK_RATE_18M (18 * 1000000) // threshold freq 18 MHz
+
+// STANDARD MODE frequency
+#define F_I2C_CLK_MASTER_STANDARD(rate) \
+ DIV_ROUND_UP (DIV_ROUND_UP ((rate), 100000) - 2, 2)
+
+// FAST MODE frequency
+#define F_I2C_CLK_MASTER_FAST(rate) \
+ DIV_ROUND_UP ((DIV_ROUND_UP ((rate), 400000) - 2) * 2, 3)
+
+// (clkrate <= 18000000)
+// calculate the value of CS bits in CCR register in standard mode
+#define F_I2C_CCR_CS_STANDARD_MAX_18M(rate) \
+ ((F_I2C_CLK_MASTER_STANDARD (rate) - 65) & F_I2C_CCR_CS_MASK)
+
+// calculate the value of CS bits in CSR register in standard mode
+#define F_I2C_CSR_CS_STANDARD_MAX_18M(rate) 0x00
+
+// calculate the value of CS bits in CCR register in fast mode
+#define F_I2C_CCR_CS_FAST_MAX_18M(rate) \
+ ((F_I2C_CLK_MASTER_FAST (rate) - 1) & F_I2C_CCR_CS_MASK)
+
+// calculate the value of CS bits in CSR register in fast mode
+#define F_I2C_CSR_CS_FAST_MAX_18M(rate) 0x00
+
+// (clkrate > 18000000)
+// calculate the value of CS bits in CCR register in standard mode */
+#define F_I2C_CCR_CS_STANDARD_MIN_18M(rate) \
+ ((F_I2C_CLK_MASTER_STANDARD (rate) - 1) & F_I2C_CCR_CS_MASK)
+
+// calculate the value of CS bits in CSR register in standard mode
+#define F_I2C_CSR_CS_STANDARD_MIN_18M(rate) \
+ (((F_I2C_CLK_MASTER_STANDARD (rate) - 1) >> 5) & F_I2C_CSR_CS_MASK)
+
+// calculate the value of CS bits in CCR register in fast mode
+#define F_I2C_CCR_CS_FAST_MIN_18M(rate) \
+ ((F_I2C_CLK_MASTER_FAST (rate) - 1) & F_I2C_CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register in fast mode */
+#define F_I2C_CSR_CS_FAST_MIN_18M(rate) \
+ (((F_I2C_CLK_MASTER_FAST (rate) - 1) >> 5) & F_I2C_CSR_CS_MASK)
+
+#endif
diff --git a/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.inf b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.inf
new file mode 100644
index 0000000..fa71536
--- /dev/null
+++ b/Silicon/Socionext/SynQuacer/Drivers/SynQuacerI2cDxe/SynQuacerI2cDxe.inf
@@ -0,0 +1,59 @@
+#/** @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.
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x0001001A
+ BASE_NAME = SynQuacerI2cDxe
+ FILE_GUID = ab5ed376-dd38-4d22-ae89-b645a4a3236e
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = SynQuacerI2cDxeEntryPoint
+ UNLOAD = SynQuacerI2cDxeUnload
+
+[Sources.common]
+ ComponentName.c
+ DriverBinding.c
+ SynQuacerI2cDxe.c
+ SynQuacerI2cDxe.h
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ Silicon/Socionext/SynQuacer/SynQuacer.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ DxeServicesTableLib
+ IoLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiLib
+ UefiDriverEntryPoint
+ UefiRuntimeLib
+
+[Guids]
+ gEfiEventVirtualAddressChangeGuid
+ gSynQuacerNonDiscoverableI2cMasterGuid
+ gSynQuacerNonDiscoverableRuntimeI2cMasterGuid
+
+[Protocols]
+ gEdkiiNonDiscoverableDeviceProtocolGuid ## TO_START
+ gEfiI2cMasterProtocolGuid ## BY_START
+
+[FixedPcd]
+ gSynQuacerTokenSpaceGuid.PcdI2cReferenceClock
+
+[Depex]
+ TRUE
diff --git a/Silicon/Socionext/SynQuacer/SynQuacer.dec b/Silicon/Socionext/SynQuacer/SynQuacer.dec
index c115504..76a9b70 100644
--- a/Silicon/Socionext/SynQuacer/SynQuacer.dec
+++ b/Silicon/Socionext/SynQuacer/SynQuacer.dec
@@ -22,6 +22,9 @@
[Guids]
gSynQuacerTokenSpaceGuid = { 0x4d04555b, 0xdfdc, 0x418a, { 0x8a, 0xab, 0x07, 0xce, 0xef, 0x46, 0x82, 0xbb } }
+ gSynQuacerNonDiscoverableI2cMasterGuid = { 0x364ee675, 0x9e44, 0x42b7, { 0xa5, 0xe4, 0x92, 0x84, 0xdb, 0x85, 0xda, 0x09 } }
+ gSynQuacerNonDiscoverableRuntimeI2cMasterGuid = { 0x5f35aa9b, 0x8c6f, 0x4828, { 0xbd, 0x44, 0x7c, 0xc0, 0xeb, 0x2d, 0xfe, 0xb9 } }
+
[Ppis]
gSynQuacerDramInfoPpiGuid = { 0x3e1d7356, 0xdda4, 0x4b1a, { 0x93, 0x46, 0xbf, 0x89, 0x1c, 0x86, 0x46, 0xcc } }
@@ -32,3 +35,5 @@
gSynQuacerTokenSpaceGuid.PcdNetsecPhyAddress|0|UINT8|0x00000003
gSynQuacerTokenSpaceGuid.PcdClearSettingsGpioPin|0|UINT32|0x00000004
+
+ gSynQuacerTokenSpaceGuid.PcdI2cReferenceClock|62500000|UINT32|0x00000005