[edk2-devel] [RFC 2/6] Silicon/Bcm283x: Add core I2C drivers

Jeremy Linton posted 6 patches 1 year, 4 months ago
[edk2-devel] [RFC 2/6] Silicon/Bcm283x: Add core I2C drivers
Posted by Jeremy Linton 1 year, 4 months ago
There are a number of I2C interfaces on the rpi, some of which are
available on the GPIO connector and are utilized by various HATs. In
particular we are interested in the RTCs (usually based on DS1037)
which are attached to GPIO2/3 which can be pin muxed to I2C #1.

This commit adds a basic runtime utilizable I2C Dxe in preparation
for a platform driver which can bind it to the ds1307 RTC driver.

Signed-off-by: Jeremy Linton <jeremy.linton@arm.com>
---
 .../Broadcom/Drivers/I2cDxe/ComponentName.c   | 181 ++++++++++
 .../Broadcom/Drivers/I2cDxe/DriverBinding.c   | 237 ++++++++++++++
 Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.c      | 309 ++++++++++++++++++
 Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.h      |  55 ++++
 Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.inf    |  56 ++++
 5 files changed, 838 insertions(+)
 create mode 100644 Silicon/Broadcom/Drivers/I2cDxe/ComponentName.c
 create mode 100644 Silicon/Broadcom/Drivers/I2cDxe/DriverBinding.c
 create mode 100644 Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.c
 create mode 100644 Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.h
 create mode 100644 Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.inf

diff --git a/Silicon/Broadcom/Drivers/I2cDxe/ComponentName.c b/Silicon/Broadcom/Drivers/I2cDxe/ComponentName.c
new file mode 100644
index 0000000000..75b4fdb3fe
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/I2cDxe/ComponentName.c
@@ -0,0 +1,181 @@
+/** @file
+
+  Copyright 2018-2019 NXP
+  Sourced and reworked from edk2/NXP I2C stack
+  Copyright 2022 Arm, Jeremy Linton
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "I2cDxe.h"
+
+STATIC EFI_UNICODE_STRING_TABLE mBcmI2cDriverNameTable[] = {
+  {
+    "en",
+    (CHAR16 *)L"Bcm I2C Driver"
+  },
+  { }
+};
+
+STATIC EFI_UNICODE_STRING_TABLE mBcmI2cControllerNameTable[] = {
+  {
+    "en",
+    (CHAR16 *)L"Bcm 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
+BcmI2cGetDriverName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  CHAR8                         *Language,
+  OUT CHAR16                        **DriverName
+  )
+{
+  return LookupUnicodeString2 (Language,
+                               This->SupportedLanguages,
+                               mBcmI2cDriverNameTable,
+                               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
+BcmI2cGetControllerName (
+  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,
+                               mBcmI2cControllerNameTable,
+                               ControllerName,
+                               FALSE);
+}
+
+//
+// EFI Component Name 2 Protocol
+//
+EFI_COMPONENT_NAME2_PROTOCOL gBcmI2cDriverComponentName2 = {
+  BcmI2cGetDriverName,
+  BcmI2cGetControllerName,
+  "en"
+};
diff --git a/Silicon/Broadcom/Drivers/I2cDxe/DriverBinding.c b/Silicon/Broadcom/Drivers/I2cDxe/DriverBinding.c
new file mode 100644
index 0000000000..a6cd7987eb
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/I2cDxe/DriverBinding.c
@@ -0,0 +1,237 @@
+/** @file
+
+  Copyright 2018-2019 NXP
+  Sourced and reworked from edk2/NXP I2C stack
+  Copyright 2022 Arm, Jeremy Linton
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/DriverBinding.h>
+
+#include "I2cDxe.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
+BcmI2cDriverBindingSupported (
+  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 non-discoverable device
+  //
+  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, &gBcmNonDiscoverableI2cMasterGuid)) {
+    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
+BcmI2cDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  )
+{
+  return BcmI2cInit (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
+BcmI2cDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                  ControllerHandle,
+  IN  UINTN                       NumberOfChildren,
+  IN  EFI_HANDLE                  *ChildHandleBuffer OPTIONAL
+  )
+{
+  return BcmI2cRelease (This->DriverBindingHandle, ControllerHandle);
+}
+
+
+STATIC EFI_DRIVER_BINDING_PROTOCOL  gBcmI2cDriverBinding = {
+  BcmI2cDriverBindingSupported,
+  BcmI2cDriverBindingStart,
+  BcmI2cDriverBindingStop,
+  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
+I2cDxeEntryPoint (
+  IN  EFI_HANDLE          ImageHandle,
+  IN  EFI_SYSTEM_TABLE    *SystemTable
+  )
+{
+  EFI_STATUS    Status;
+
+  //
+  //  Add the driver to the list of drivers
+  //
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle, SystemTable, &gBcmI2cDriverBinding, ImageHandle,
+             NULL, &gBcmI2cDriverComponentName2);
+  ASSERT_EFI_ERROR (Status);
+
+  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
+I2cDxeUnload (
+  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,
+                  &gBcmI2cDriverBinding,
+                  NULL
+                  );
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.c b/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.c
new file mode 100644
index 0000000000..8aedaa1f58
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.c
@@ -0,0 +1,309 @@
+/** I2cDxe.c
+  I2c driver APIs for read, write, initialize, set speed and reset
+
+  Sourced and reworked from edk2/NXP I2C stack
+  Copyright 2022 Arm, Jeremy Linton
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <IndustryStandard/Bcm2836.h>
+
+#include "I2cDxe.h"
+
+STATIC CONST EFI_I2C_CONTROLLER_CAPABILITIES mI2cControllerCapabilities = {
+  0,
+  0,
+  0,
+  0
+};
+
+/**
+  Function to set i2c bus frequency
+
+  @param   This            Pointer to I2c master protocol
+  @param   BusClockHertz   value to be set
+
+  @retval EFI_SUCCESS      Operation successfull
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SetBusFrequency (
+  IN CONST EFI_I2C_MASTER_PROTOCOL   *This,
+  IN OUT UINTN                       *BusClockHertz
+ )
+{
+  UINTN                    I2cBase;
+  UINT64                   I2cClock;
+  BCM_I2C_MASTER           *I2c;
+
+  I2c = BCM_I2C_FROM_THIS (This);
+
+  I2cBase = I2c->ControllerBase;
+
+  // depend on ConfigDxe? On the NXP this only sets the clock and resets the bus
+  // Here we are hardcoding the I2C clock until we have a need not to.
+  I2cClock = 50000;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Function to reset I2c Controller
+
+  @param  This             Pointer to I2c master protocol
+
+  @return EFI_SUCCESS      Operation successfull
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Reset (
+  IN CONST EFI_I2C_MASTER_PROTOCOL *This
+  )
+{
+  return EFI_SUCCESS;
+}
+
+STATIC
+void
+StatusPoll(
+  UINTN              I2cBase,
+  UINTN              Mask
+    )
+{
+  UINTN              Retry;
+
+  Retry = 0;
+  while ((MmioRead32 (I2cBase + BCM2835_I2C_S) & Mask) != Mask) {
+    Retry++;
+  }
+}
+
+
+
+STATIC
+EFI_STATUS
+SingleTransfer(
+  UINTN              I2cBase,
+  UINTN              SlaveAddress,
+  EFI_I2C_OPERATION  *Operation
+)
+{
+  EFI_STATUS               Status;
+  UINTN                    Index;
+  UINTN                    FifoState;
+
+  // clear all the status
+  MmioWrite32 (I2cBase + BCM2835_I2C_C, 0x10 ); // fifo clear
+  MmioWrite32 (I2cBase + BCM2835_I2C_S, 0x302);
+
+  // don't support 10 bit addr for now. (see 3.3 in 2711 manual)
+  // the problem with arm is that you never know if there are undocumented
+  // acces restrictions (aka 8 bit reg, but it needs to be read with a 32-bit instr)
+  MmioWrite8 (I2cBase + BCM2835_I2C_A, (UINT8)SlaveAddress);
+  MmioWrite32 (I2cBase + BCM2835_I2C_DLEN, Operation->LengthInBytes );
+
+  if (Operation->Flags & I2C_FLAG_READ) {
+    FifoState = 0x20; // fifo has data
+    MmioWrite32 (I2cBase + BCM2835_I2C_C, 0x8081 ); // Enable, start, fifo clear, read
+  } else {
+    FifoState = 0x10; // fifo can accept data
+    MmioWrite32 (I2cBase + BCM2835_I2C_C, 0x8080 ); // Enable, start, fifo clear, write
+  }
+
+  for (Index = 0; Index < Operation->LengthInBytes; Index++) {
+
+    StatusPoll (I2cBase, FifoState);
+
+    if (Operation->Flags & I2C_FLAG_READ) {
+      Operation->Buffer[Index] = MmioRead8 (I2cBase + BCM2835_I2C_FIFO);
+    } else {
+      MmioWrite8 (I2cBase + BCM2835_I2C_FIFO, Operation->Buffer[Index]);
+    }
+  }
+
+  StatusPoll (I2cBase, 0x02);
+
+  Status = 0;
+  return Status;
+}
+
+volatile UINTN forcewrite;
+
+STATIC
+EFI_STATUS
+EFIAPI
+StartRequest (
+  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
+  )
+{
+  BCM_I2C_MASTER           *I2c;
+  UINTN                    I2cBase;
+  EFI_STATUS               Status;
+  EFI_TPL                  Tpl;
+  BOOLEAN                  AtRuntime;
+  UINTN                    Index;
+
+  AtRuntime = EfiAtRuntime ();
+  if (!AtRuntime) {
+    Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+  }
+
+  I2c = BCM_I2C_FROM_THIS (This);
+
+  I2cBase = I2c->ControllerBase;
+
+  for (Index = 0; Index < RequestPacket->OperationCount; Index++ ) {
+
+    Status = SingleTransfer (I2cBase, SlaveAddress,  &RequestPacket->Operation[Index]);
+
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+  }
+
+  if (!AtRuntime) {
+    gBS->RestoreTPL (Tpl);
+  }
+
+  return Status;
+}
+
+STATIC
+VOID
+EFIAPI
+BcmI2cVirtualAddressChangeEvent (
+  IN EFI_EVENT Event,
+  IN VOID *Context
+  )
+{
+  BCM_I2C_MASTER            *I2c;
+
+  I2c = (BCM_I2C_MASTER *)Context;
+
+  EfiConvertPointer (0x0, (VOID**)&I2c->ControllerBase);
+  EfiConvertPointer (0x0, (VOID**)&I2c);
+}
+
+
+EFI_STATUS
+BcmI2cInit (
+  IN EFI_HANDLE             DriverBindingHandle,
+  IN EFI_HANDLE             ControllerHandle
+  )
+{
+  EFI_STATUS                RetVal;
+  NON_DISCOVERABLE_DEVICE   *Dev;
+  BCM_I2C_MASTER            *I2c;
+  EFI_EVENT                 VirtualAddressChangeEvent;
+
+  RetVal = gBS->OpenProtocol (ControllerHandle,
+                              &gEdkiiNonDiscoverableDeviceProtocolGuid,
+                              (VOID **)&Dev, DriverBindingHandle,
+                              ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (EFI_ERROR (RetVal)) {
+    return RetVal;
+  }
+
+  I2c = AllocateRuntimeZeroPool (sizeof (BCM_I2C_MASTER));
+
+  I2c->Signature                            = BCM_I2C_SIGNATURE;
+  I2c->I2cMaster.SetBusFrequency            = SetBusFrequency;
+  I2c->I2cMaster.Reset                      = Reset;
+  I2c->I2cMaster.StartRequest               = StartRequest;
+  I2c->I2cMaster.I2cControllerCapabilities  = &mI2cControllerCapabilities;
+  I2c->Dev                                  = Dev;
+
+  CopyGuid (&I2c->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);
+  I2c->DevicePath.MmioBase = I2c->Dev->Resources[0].AddrRangeMin;
+  I2c->ControllerBase = I2c->Dev->Resources[0].AddrRangeMin;
+  SetDevicePathNodeLength (&I2c->DevicePath.Vendor,
+    sizeof (I2c->DevicePath) - sizeof (I2c->DevicePath.End));
+  SetDevicePathEndNode (&I2c->DevicePath.End);
+
+  RetVal = gBS->InstallMultipleProtocolInterfaces (&ControllerHandle,
+                  &gEfiI2cMasterProtocolGuid, (VOID**)&I2c->I2cMaster,
+                  &gEfiDevicePathProtocolGuid, &I2c->DevicePath,
+                  NULL);
+
+  if (EFI_ERROR (RetVal)) {
+    FreePool (I2c);
+    gBS->CloseProtocol (ControllerHandle,
+                        &gEdkiiNonDiscoverableDeviceProtocolGuid,
+                        DriverBindingHandle,
+                        ControllerHandle);
+  } else {
+    RetVal = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  BcmI2cVirtualAddressChangeEvent,
+                  (VOID *)I2c,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &VirtualAddressChangeEvent
+                );
+
+    ASSERT_EFI_ERROR (RetVal);
+
+  }
+
+  return RetVal;
+}
+
+EFI_STATUS
+BcmI2cRelease (
+  IN EFI_HANDLE                 DriverBindingHandle,
+  IN EFI_HANDLE                 ControllerHandle
+  )
+{
+  EFI_I2C_MASTER_PROTOCOL       *I2cMaster;
+  EFI_STATUS                    RetVal;
+  BCM_I2C_MASTER                *I2c;
+
+  RetVal = gBS->HandleProtocol (ControllerHandle,
+                                &gEfiI2cMasterProtocolGuid,
+                                (VOID **)&I2cMaster);
+  ASSERT_EFI_ERROR (RetVal);
+  if (EFI_ERROR (RetVal)) {
+    return RetVal;
+  }
+
+  I2c = BCM_I2C_FROM_THIS (I2cMaster);
+
+  RetVal = gBS->UninstallMultipleProtocolInterfaces (ControllerHandle,
+                  &gEfiI2cMasterProtocolGuid, I2cMaster,
+                  &gEfiDevicePathProtocolGuid, &I2c->DevicePath,
+                  NULL);
+  if (EFI_ERROR (RetVal)) {
+    return RetVal;
+  }
+
+  RetVal = gBS->CloseProtocol (ControllerHandle,
+                               &gEdkiiNonDiscoverableDeviceProtocolGuid,
+                               DriverBindingHandle,
+                               ControllerHandle);
+  ASSERT_EFI_ERROR (RetVal);
+  if (EFI_ERROR (RetVal)) {
+    return RetVal;
+  }
+
+  gBS->FreePool (I2c);
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.h b/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.h
new file mode 100644
index 0000000000..e9ddc0e7bd
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.h
@@ -0,0 +1,55 @@
+/** I2cDxe.h
+  Header defining the constant, base address amd function for I2C controller
+
+  Copyright 2017-2020 NXP
+  Sourced and reworked from edk2/NXP I2C stack
+  Copyright 2022 Arm, Jeremy Linton
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef I2C_DXE_H_
+#define I2C_DXE_H_
+
+#include <Library/UefiLib.h>
+#include <Uefi.h>
+
+#include <Protocol/I2cMaster.h>
+#include <Protocol/NonDiscoverableDevice.h>
+
+#define BCM_I2C_SIGNATURE         SIGNATURE_32 ('B', 'I', '2', 'C')
+#define BCM_I2C_FROM_THIS(a)      CR ((a), BCM_I2C_MASTER, \
+                                    I2cMaster, BCM_I2C_SIGNATURE)
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gBcmI2cDriverComponentName2;
+
+#pragma pack(1)
+typedef struct {
+  VENDOR_DEVICE_PATH              Vendor;
+  UINT64                          MmioBase;
+  EFI_DEVICE_PATH_PROTOCOL        End;
+} BCM_I2C_DEVICE_PATH;
+#pragma pack()
+
+typedef struct {
+  UINT32                          Signature;
+  EFI_I2C_MASTER_PROTOCOL         I2cMaster;
+  BCM_I2C_DEVICE_PATH             DevicePath;
+  NON_DISCOVERABLE_DEVICE         *Dev;
+  UINTN                           ControllerBase;
+} BCM_I2C_MASTER;
+
+EFI_STATUS
+BcmI2cInit (
+  IN EFI_HANDLE  DriverBindingHandle,
+  IN EFI_HANDLE  ControllerHandle
+  );
+
+EFI_STATUS
+BcmI2cRelease (
+  IN EFI_HANDLE  DriverBindingHandle,
+  IN EFI_HANDLE  ControllerHandle
+  );
+
+#endif //I2C_DXE_H_
diff --git a/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.inf b/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.inf
new file mode 100644
index 0000000000..d8b6130616
--- /dev/null
+++ b/Silicon/Broadcom/Drivers/I2cDxe/I2cDxe.inf
@@ -0,0 +1,56 @@
+#  @file
+#
+#  Component description file for I2c driver
+#
+#  Copyright (c) 2015, Freescale Semiconductor, Inc. All rights reserved.
+#  Copyright 2017-2020 NXP
+#  Sourced and reworked from edk2/NXP I2C stack
+#  Copyright 2022 Arm, Jeremy Linton
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+
+[Defines]
+  INF_VERSION                    = 0x0001001A
+  BASE_NAME                      = I2cDxe
+  FILE_GUID                      = 09e767d9-9c1e-405d-86c3-fb7ce355f948
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = I2cDxeEntryPoint
+  UNLOAD                         = I2cDxeUnload
+
+[Sources.common]
+  ComponentName.c
+  DriverBinding.c
+  I2cDxe.c
+
+[LibraryClasses]
+  ArmLib
+  BaseMemoryLib
+  DevicePathLib
+  IoLib
+  MemoryAllocationLib
+  PcdLib
+  TimerLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+  UefiRuntimeLib
+
+[Guids]
+  gBcmNonDiscoverableI2cMasterGuid
+  gEfiEventVirtualAddressChangeGuid
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  Platform/RaspberryPi/RaspberryPi.dec
+  Silicon/Broadcom/Bcm283x/Bcm283x.dec
+
+[Protocols]
+  gEdkiiNonDiscoverableDeviceProtocolGuid    ## TO_START
+  gEfiI2cMasterProtocolGuid                  ## BY_START
+
+[Depex]
+  TRUE
-- 
2.43.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113559): https://edk2.groups.io/g/devel/message/113559
Mute This Topic: https://groups.io/mt/103653098/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-