diff options
Diffstat (limited to 'MdeModulePkg/Bus/I2c/I2cDxe/I2cHost.c')
-rw-r--r-- | MdeModulePkg/Bus/I2c/I2cDxe/I2cHost.c | 1169 |
1 files changed, 1169 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/I2c/I2cDxe/I2cHost.c b/MdeModulePkg/Bus/I2c/I2cDxe/I2cHost.c new file mode 100644 index 0000000000..e153a4dc2b --- /dev/null +++ b/MdeModulePkg/Bus/I2c/I2cDxe/I2cHost.c @@ -0,0 +1,1169 @@ +/** @file
+ This file implements I2C Host Protocol which provides callers with the ability to
+ do I/O transactions to all of the devices on the I2C bus.
+
+ Copyright (c) 2013, Intel Corporation. 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 "I2cDxe.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gI2cHostDriverBinding = {
+ I2cHostDriverSupported,
+ I2cHostDriverStart,
+ I2cHostDriverStop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+//
+// Driver name table
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mI2cHostDriverNameTable[] = {
+ { "eng;en", L"I2c Host Driver" },
+ { NULL , NULL }
+};
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gI2cHostComponentName = {
+ (EFI_COMPONENT_NAME_GET_DRIVER_NAME) I2cHostComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) I2cHostComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gI2cHostComponentName2 = {
+ I2cHostComponentNameGetDriverName,
+ I2cHostComponentNameGetControllerName,
+ "en"
+};
+
+/**
+ 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.
+
+**/
+EFI_STATUS
+EFIAPI
+I2cHostComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mI2cHostDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This != &gI2cHostComponentName2)
+ );
+}
+
+/**
+ 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.
+
+**/
+EFI_STATUS
+EFIAPI
+I2cHostComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+I2cHostDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_I2C_MASTER_PROTOCOL *I2cMaster;
+ EFI_I2C_BUS_CONFIGURATION_MANAGEMENT_PROTOCOL *I2cBusConfigurationManagement;
+ EFI_STATUS Status;
+
+ //
+ // Locate I2C Bus Configuration Management Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiI2cBusConfigurationManagementProtocolGuid,
+ (VOID **)&I2cBusConfigurationManagement,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Close the protocol because we don't use it here
+ //
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiI2cBusConfigurationManagementProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ //
+ // Locate I2C Master Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiI2cMasterProtocolGuid,
+ (VOID **)&I2cMaster,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+I2cHostDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_I2C_MASTER_PROTOCOL *I2cMaster;
+ EFI_I2C_BUS_CONFIGURATION_MANAGEMENT_PROTOCOL *I2cBusConfigurationManagement;
+ I2C_HOST_CONTEXT *I2cHostContext;
+
+ I2cMaster = NULL;
+ I2cHostContext = NULL;
+ I2cBusConfigurationManagement = NULL;
+
+ //
+ // Locate I2C Bus Configuration Management Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiI2cBusConfigurationManagementProtocolGuid,
+ (VOID **)&I2cBusConfigurationManagement,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "I2cHost: Open I2C bus configuration error, Status = %r\n", Status));
+ return Status;
+ }
+
+ //
+ // Locate I2C Master Protocol
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiI2cMasterProtocolGuid,
+ (VOID **)&I2cMaster,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "I2cHost: Open I2C master error, Status = %r\n", Status));
+ goto Exit;
+ }
+
+ //
+ // Allocate the I2C Host Context structure
+ //
+ I2cHostContext = AllocateZeroPool (sizeof (I2C_HOST_CONTEXT));
+ if (I2cHostContext == NULL) {
+ DEBUG ((EFI_D_ERROR, "I2cHost: there is no enough memory to allocate.\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Initialize the context structure for the current I2C Controller
+ //
+ I2cHostContext->Signature = I2C_HOST_SIGNATURE;
+ I2cHostContext->I2cMaster = I2cMaster;
+ I2cHostContext->I2cBusConfigurationManagement = I2cBusConfigurationManagement;
+ I2cHostContext->I2cBusConfiguration = (UINTN) -1;
+ InitializeListHead(&I2cHostContext->RequestList);
+
+ //
+ // Reset the controller
+ //
+ Status = I2cMaster->Reset (I2cMaster);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "I2cHost: I2C controller reset failed!\n"));
+ goto Exit;
+ }
+
+ //
+ // Create the I2C transaction complete event
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_I2C_SYNC,
+ I2cHostRequestCompleteEvent,
+ I2cHostContext,
+ &I2cHostContext->I2cEvent
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "I2cHost: create complete event error, Status = %r\n", Status));
+ goto Exit;
+ }
+
+ //
+ // Get the bus management event
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_I2C_SYNC,
+ I2cHostI2cBusConfigurationAvailable,
+ I2cHostContext,
+ &I2cHostContext->I2cBusConfigurationEvent
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "I2cHost: create bus available event error, Status = %r\n", Status));
+ goto Exit;
+ }
+
+ //
+ // Build the I2C host protocol for the current I2C controller
+ //
+ I2cHostContext->I2cHost.QueueRequest = I2cHostQueueRequest;
+ I2cHostContext->I2cHost.I2cControllerCapabilities = I2cMaster->I2cControllerCapabilities;
+
+ //
+ // Install the driver protocol
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiI2cHostProtocolGuid,
+ &I2cHostContext->I2cHost,
+ NULL
+ );
+Exit:
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "I2cHost: Start() function failed, Status = %r\n", Status));
+ if (I2cBusConfigurationManagement != NULL) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiI2cBusConfigurationManagementProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ if ((I2cHostContext != NULL) && (I2cHostContext->I2cEvent != NULL)) {
+ gBS->CloseEvent (I2cHostContext->I2cEvent);
+ I2cHostContext->I2cEvent = NULL;
+ }
+
+ if ((I2cHostContext != NULL) && (I2cHostContext->I2cBusConfigurationEvent != NULL)) {
+ gBS->CloseEvent (I2cHostContext->I2cBusConfigurationEvent);
+ I2cHostContext->I2cBusConfigurationEvent = NULL;
+ }
+
+ //
+ // Release the context structure upon failure
+ //
+ if (I2cHostContext != NULL) {
+ FreePool (I2cHostContext);
+ }
+ }
+
+ //
+ // Return the operation status.
+ //
+ return Status;
+}
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_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
+I2cHostDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ I2C_HOST_CONTEXT *I2cHostContext;
+ EFI_I2C_HOST_PROTOCOL *I2cHost;
+ EFI_TPL TplPrevious;
+
+ TplPrevious = EfiGetCurrentTpl ();
+ if (TplPrevious > TPL_I2C_SYNC) {
+ DEBUG ((EFI_D_ERROR, "I2cHost: TPL %d is too high in Stop.\n", TplPrevious));
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiI2cHostProtocolGuid,
+ (VOID **) &I2cHost,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ I2cHostContext = I2C_HOST_CONTEXT_FROM_PROTOCOL (I2cHost);
+
+ //
+ // Raise TPL for critical section
+ //
+ TplPrevious = gBS->RaiseTPL (TPL_I2C_SYNC);
+
+ //
+ // If there is pending request or pending bus configuration, do not stop
+ //
+ Status = EFI_DEVICE_ERROR;
+ if (( !I2cHostContext->I2cBusConfigurationManagementPending )
+ && IsListEmpty (&I2cHostContext->RequestList)) {
+
+ //
+ // Remove the I2C host protocol
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gEfiI2cHostProtocolGuid,
+ I2cHost,
+ NULL
+ );
+ }
+
+ //
+ // Leave critical section
+ //
+ gBS->RestoreTPL (TplPrevious);
+ if (!EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiI2cBusConfigurationManagementProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ //
+ // Release I2c Host resources
+ //
+ if (I2cHostContext->I2cBusConfigurationEvent != NULL) {
+ gBS->CloseEvent (I2cHostContext->I2cBusConfigurationEvent);
+ I2cHostContext->I2cBusConfigurationEvent = NULL;
+ }
+
+ if (I2cHostContext->I2cEvent != NULL) {
+ gBS->CloseEvent (I2cHostContext->I2cEvent);
+ I2cHostContext->I2cEvent = NULL;
+ }
+
+ FreePool (I2cHostContext);
+ }
+
+ //
+ // Return the stop status
+ //
+ return Status;
+}
+
+/**
+ Handle the I2C bus configuration available event
+
+ This routine is called at TPL_I2C_SYNC.
+
+ @param[in] Event Address of an EFI_EVENT handle
+ @param[in] Context Address of an I2C_HOST_CONTEXT structure
+
+**/
+VOID
+EFIAPI
+I2cHostI2cBusConfigurationAvailable (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ I2C_HOST_CONTEXT *I2cHostContext;
+ EFI_I2C_MASTER_PROTOCOL *I2cMaster;
+ I2C_REQUEST *I2cRequest;
+ LIST_ENTRY *EntryHeader;
+ LIST_ENTRY *Entry;
+ EFI_STATUS Status;
+
+ //
+ // Mark this I2C bus configuration management operation as complete
+ //
+ I2cHostContext = (I2C_HOST_CONTEXT *)Context;
+ I2cMaster = I2cHostContext->I2cMaster;
+ ASSERT (I2cMaster != NULL);
+ //
+ // Clear flag to indicate I2C bus configuration is finished
+ //
+ I2cHostContext->I2cBusConfigurationManagementPending = FALSE;
+
+ //
+ // Validate the completion status
+ //
+ if (EFI_ERROR (I2cHostContext->Status)) {
+ //
+ // Setting I2C bus configuration failed before
+ //
+ I2cHostRequestComplete (I2cHostContext, I2cHostContext->Status);
+
+ //
+ // Unknown I2C bus configuration
+ // Force next operation to enable the I2C bus configuration
+ //
+ I2cHostContext->I2cBusConfiguration = (UINTN) -1;
+
+ //
+ // Do not continue current I2C request
+ //
+ return;
+ }
+
+ //
+ // Get the first request in the link with FIFO order
+ //
+ EntryHeader = &I2cHostContext->RequestList;
+ Entry = GetFirstNode (EntryHeader);
+ I2cRequest = I2C_REQUEST_FROM_ENTRY (Entry);
+
+ //
+ // Update the I2C bus configuration of the current I2C request
+ //
+ I2cHostContext->I2cBusConfiguration = I2cRequest->I2cBusConfiguration;
+
+ //
+ // Start an I2C operation on the host, the status is returned by I2cHostContext->Status
+ //
+ Status = I2cMaster->StartRequest (
+ I2cMaster,
+ I2cRequest->SlaveAddress,
+ I2cRequest->RequestPacket,
+ I2cHostContext->I2cEvent,
+ &I2cHostContext->Status
+ );
+}
+
+/**
+ Complete the current request
+
+ This routine is called at TPL_I2C_SYNC.
+
+ @param[in] I2cHostContext Address of an I2C_HOST_CONTEXT structure.
+ @param[in] Status Status of the I2C operation.
+
+ @return This routine returns the input status value.
+
+**/
+EFI_STATUS
+I2cHostRequestComplete (
+ I2C_HOST_CONTEXT *I2cHostContext,
+ EFI_STATUS Status
+ )
+{
+ I2C_REQUEST *I2cRequest;
+ LIST_ENTRY *EntryHeader;
+ LIST_ENTRY *Entry;
+
+ //
+ // Remove the current I2C request from the list
+ //
+ EntryHeader = &I2cHostContext->RequestList;
+ Entry = GetFirstNode (EntryHeader);
+ I2cRequest = I2C_REQUEST_FROM_ENTRY (Entry);
+
+ //
+ // Save the status for QueueRequest
+ //
+ if ( NULL != I2cRequest->Status ) {
+ *I2cRequest->Status = Status;
+ }
+
+ //
+ // Notify the user of the I2C request completion
+ //
+ if ( NULL != I2cRequest->Event ) {
+ gBS->SignalEvent (I2cRequest->Event);
+ }
+
+ //
+ // Done with this request, remove the current request from list
+ //
+ RemoveEntryList (&I2cRequest->Link);
+ FreePool (I2cRequest->RequestPacket);
+ FreePool (I2cRequest);
+
+ //
+ // If there is more I2C request, start next one
+ //
+ if(!IsListEmpty (EntryHeader)) {
+ I2cHostRequestEnable (I2cHostContext);
+ }
+
+ return Status;
+}
+
+/**
+ Handle the bus available event
+
+ This routine is called at TPL_I2C_SYNC.
+
+ @param[in] Event Address of an EFI_EVENT handle
+ @param[in] Context Address of an I2C_HOST_CONTEXT structure
+
+**/
+VOID
+EFIAPI
+I2cHostRequestCompleteEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ I2C_HOST_CONTEXT *I2cHostContext;
+
+ //
+ // Handle the completion event
+ //
+ I2cHostContext = (I2C_HOST_CONTEXT *)Context;
+ I2cHostRequestComplete (I2cHostContext, I2cHostContext->Status);
+}
+
+/**
+ Enable access to the I2C bus configuration
+
+ @param[in] I2cHostContext Address of an I2C_HOST_CONTEXT structure
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_ABORTED The request did not complete because the driver
+ was shutdown.
+ @retval EFI_BAD_BUFFER_SIZE The WriteBytes or ReadBytes buffer size is too large.
+ @retval EFI_DEVICE_ERROR There was an I2C error (NACK) during the operation.
+ This could indicate the slave device is not present.
+ @retval EFI_INVALID_PARAMETER RequestPacket is NULL
+ @retval EFI_NO_MAPPING Invalid I2cBusConfiguration value
+ @retval EFI_NO_RESPONSE The I2C device is not responding to the
+ slave address. EFI_DEVICE_ERROR may also be
+ returned if the controller can not distinguish
+ when the NACK occurred.
+ @retval EFI_NOT_FOUND I2C slave address exceeds maximum address
+ @retval EFI_NOT_READY I2C bus is busy or operation pending, wait for
+ the event and then read status.
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory for I2C operation
+ @retval EFI_TIMEOUT The transaction did not complete within an internally
+ specified timeout period.
+
+**/
+EFI_STATUS
+I2cHostRequestEnable (
+ I2C_HOST_CONTEXT *I2cHostContext
+ )
+{
+ UINTN I2cBusConfiguration;
+ CONST EFI_I2C_BUS_CONFIGURATION_MANAGEMENT_PROTOCOL *I2cBusConfigurationManagement;
+ I2C_REQUEST *I2cRequest;
+ EFI_STATUS Status;
+ EFI_TPL TplPrevious;
+ LIST_ENTRY *EntryHeader;
+ LIST_ENTRY *Entry;
+
+ //
+ // Assume pending request
+ //
+ Status = EFI_NOT_READY;
+
+ I2cBusConfigurationManagement = I2cHostContext->I2cBusConfigurationManagement;
+
+ //
+ // Validate the I2c bus configuration
+ //
+ EntryHeader = &I2cHostContext->RequestList;
+ Entry = GetFirstNode (EntryHeader);
+ I2cRequest = I2C_REQUEST_FROM_ENTRY (Entry);
+
+ I2cBusConfiguration = I2cRequest->I2cBusConfiguration;
+
+ if (I2cHostContext->I2cBusConfiguration != I2cBusConfiguration ) {
+ //
+ // Set flag to indicate I2C bus configuration is in progress
+ //
+ I2cHostContext->I2cBusConfigurationManagementPending = TRUE;
+ //
+ // Update bus configuration for this device's requesting bus configuration
+ //
+ Status = I2cBusConfigurationManagement->EnableI2cBusConfiguration (
+ I2cBusConfigurationManagement,
+ I2cBusConfiguration,
+ I2cHostContext->I2cBusConfigurationEvent,
+ &I2cHostContext->Status
+ );
+ } else {
+ //
+ // I2C bus configuration is same, no need change configuration and start I2c transaction directly
+ //
+ TplPrevious = gBS->RaiseTPL ( TPL_I2C_SYNC );
+
+ //
+ // Same I2C bus configuration
+ //
+ I2cHostContext->Status = EFI_SUCCESS;
+ I2cHostI2cBusConfigurationAvailable (I2cHostContext->I2cBusConfigurationEvent, I2cHostContext);
+
+ //
+ // Release the thread synchronization
+ //
+ gBS->RestoreTPL ( TplPrevious );
+ }
+ return Status;
+}
+
+/**
+ Queue an I2C operation for execution on the I2C controller.
+
+ This routine must be called at or below TPL_NOTIFY. For synchronous
+ requests this routine must be called at or below TPL_CALLBACK.
+
+ N.B. The typical consumers of this API are the I2C bus driver and
+ on rare occasions the I2C test application. 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
+ drivers may use this API within these guidelines.
+
+ This layer uses the concept of I2C bus configurations to describe
+ the I2C bus. An I2C bus configuration is defined as a unique
+ setting of the multiplexers and switches in the I2C bus which
+ enable access to one or more I2C devices. When using a switch
+ to divide a bus, due to speed differences, the I2C platform layer
+ would define an I2C bus configuration for the I2C devices on each
+ side of the switch. When using a multiplexer, the I2C platform
+ layer defines an I2C bus configuration for each of the selector
+ values required to control the multiplexer. See Figure 1 in the
+ <a href="http://www.nxp.com/documents/user_manual/UM10204.pdf">I<sup>2</sup>C
+ Specification</a> for a complex I2C bus configuration.
+
+ The I2C host driver processes all operations in FIFO order. Prior to
+ performing the operation, the I2C host driver calls the I2C platform
+ driver to reconfigure the switches and multiplexers in the I2C bus
+ enabling access to the specified I2C device. The I2C platform driver
+ also selects the maximum bus speed for the device. After the I2C bus
+ is configured, the I2C host driver calls the I2C port driver to
+ initialize the I2C controller and start the I2C operation.
+
+ @param[in] This Address of an EFI_I2C_HOST_PROTOCOL instance.
+ @param[in] I2cBusConfiguration I2C bus configuration to access the I2C
+ device.
+ @param[in] SlaveAddress Address of the device on the I2C bus.
+ @param[in] Event Event to set for asynchronous operations,
+ NULL for synchronous operations
+ @param[in] RequestPacket Address of an EFI_I2C_REQUEST_PACKET
+ structure describing the I2C operation
+ @param[out] I2cStatus Optional buffer to receive the I2C operation
+ completion status
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_ABORTED The request did not complete because the driver
+ was shutdown.
+ @retval EFI_BAD_BUFFER_SIZE The WriteBytes or ReadBytes buffer size is too large.
+ @retval EFI_DEVICE_ERROR There was an I2C error (NACK) during the operation.
+ This could indicate the slave device is not present.
+ @retval EFI_INVALID_PARAMETER RequestPacket is NULL
+ @retval EFI_INVALID_PARAMETER TPL is too high
+ @retval EFI_NO_MAPPING Invalid I2cBusConfiguration value
+ @retval EFI_NO_RESPONSE The I2C device is not responding to the
+ slave address. EFI_DEVICE_ERROR may also be
+ returned if the controller can not distinguish
+ when the NACK occurred.
+ @retval EFI_NOT_FOUND I2C slave address exceeds maximum address
+ @retval EFI_NOT_READY I2C bus is busy or operation pending, wait for
+ the event and then read status pointed to by
+ the request packet.
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory for I2C operation
+ @retval EFI_TIMEOUT The transaction did not complete within an internally
+ specified timeout period.
+
+**/
+EFI_STATUS
+EFIAPI
+I2cHostQueueRequest (
+ IN CONST EFI_I2C_HOST_PROTOCOL *This,
+ IN UINTN I2cBusConfiguration,
+ IN UINTN SlaveAddress,
+ IN EFI_EVENT Event OPTIONAL,
+ IN EFI_I2C_REQUEST_PACKET *RequestPacket,
+ OUT EFI_STATUS *I2cStatus OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT SyncEvent;
+ EFI_TPL TplPrevious;
+ I2C_REQUEST *I2cRequest;
+ I2C_HOST_CONTEXT *I2cHostContext;
+ BOOLEAN FirstRequest;
+ UINTN RequestPacketSize;
+
+ SyncEvent = NULL;
+ FirstRequest = FALSE;
+ Status = EFI_SUCCESS;
+
+ if (RequestPacket == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ I2cHostContext = I2C_HOST_CONTEXT_FROM_PROTOCOL (This);
+
+ if (Event == NULL) {
+ //
+ // For synchronous transaction, register an event used to wait for finishing synchronous transaction
+ //
+ Status = gBS->CreateEvent (
+ 0,
+ TPL_I2C_SYNC,
+ NULL,
+ NULL,
+ &SyncEvent
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // TPL should be at or below TPL_NOTIFY.
+ // For synchronous requests this routine must be called at or below TPL_CALLBACK.
+ //
+ TplPrevious = EfiGetCurrentTpl ();
+ if ((TplPrevious > TPL_I2C_SYNC) || ((Event == NULL) && (TplPrevious > TPL_CALLBACK))) {
+ DEBUG ((EFI_D_ERROR, "ERROR - TPL %d is too high!\n", TplPrevious));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate the request structure
+ //
+ I2cRequest = AllocateZeroPool (sizeof (I2C_REQUEST));
+ if (I2cRequest == NULL) {
+ DEBUG ((EFI_D_ERROR, "WARNING - Failed to allocate I2C_REQUEST!\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the request
+ //
+ I2cRequest->Signature = I2C_REQUEST_SIGNATURE;
+ I2cRequest->I2cBusConfiguration = I2cBusConfiguration;
+ I2cRequest->SlaveAddress = SlaveAddress;
+ I2cRequest->Event = (Event == NULL) ? SyncEvent : Event;
+ I2cRequest->Status = I2cStatus;
+
+ //
+ // Copy request packet into private buffer, as RequestPacket may be freed during asynchronous transaction
+ //
+ RequestPacketSize = sizeof (UINTN) + RequestPacket->OperationCount * sizeof (EFI_I2C_OPERATION);
+ I2cRequest->RequestPacket = AllocateZeroPool (RequestPacketSize);
+ CopyMem (I2cRequest->RequestPacket, RequestPacket, RequestPacketSize);
+
+ //
+ // Synchronize with the other threads
+ //
+ gBS->RaiseTPL ( TPL_I2C_SYNC );
+
+ FirstRequest = IsListEmpty (&I2cHostContext->RequestList);
+
+ //
+ // Insert new I2C request in the list
+ //
+ InsertTailList (&I2cHostContext->RequestList, &I2cRequest->Link);
+
+ //
+ // Release the thread synchronization
+ //
+ gBS->RestoreTPL (TplPrevious);
+
+ if (FirstRequest) {
+ //
+ // Start the first I2C request, then the subsequent of I2C request will continue
+ //
+ Status = I2cHostRequestEnable (I2cHostContext);
+ }
+
+ if (Event != NULL) {
+ //
+ // For asynchronous, return EFI_SUCCESS indicating that the asynchronously I2C transaction was queued.
+ // No real I2C operation status in I2cStatus
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // For synchronous transaction, wait for the operation completion
+ //
+ do {
+ Status = gBS->CheckEvent (SyncEvent);
+ } while (Status == EFI_NOT_READY);
+
+ //
+ // Get the I2C operation status
+ //
+ Status = I2cHostContext->Status;
+
+ //
+ // Return the I2C operation status
+ //
+ if (I2cStatus != NULL) {
+ *I2cStatus = Status;
+ }
+
+ //
+ // Close the event if necessary
+ //
+ if (SyncEvent != NULL) {
+ gBS->CloseEvent (SyncEvent);
+ }
+
+ return Status;
+}
+
+/**
+ The user Entry Point for I2C host module. The user code starts with this function.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval other Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeI2cHost(
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Install driver model protocol(s).
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gI2cHostDriverBinding,
+ ImageHandle,
+ &gI2cHostComponentName,
+ &gI2cHostComponentName2
+ );
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/**
+ This is the unload handle for I2C host module.
+
+ Disconnect the driver specified by ImageHandle from all the devices in the handle database.
+ Uninstall all the protocols installed in the driver entry point.
+
+ @param[in] ImageHandle The drivers' driver image.
+
+ @retval EFI_SUCCESS The image is unloaded.
+ @retval Others Failed to unload the image.
+
+**/
+EFI_STATUS
+EFIAPI
+I2cHostUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *DeviceHandleBuffer;
+ UINTN DeviceHandleCount;
+ UINTN Index;
+
+ //
+ // Get the list of all I2C Controller handles in the handle database.
+ // If there is an error getting the list, then the unload
+ // operation fails.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiI2cHostProtocolGuid,
+ NULL,
+ &DeviceHandleCount,
+ &DeviceHandleBuffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Disconnect the driver specified by ImageHandle from all
+ // the devices in the handle database.
+ //
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index],
+ ImageHandle,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+ }
+
+ //
+ // Uninstall all the protocols installed in the driver entry point
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ gI2cHostDriverBinding.DriverBindingHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gI2cHostDriverBinding,
+ &gEfiComponentNameProtocolGuid,
+ &gI2cHostComponentName,
+ &gEfiComponentName2ProtocolGuid,
+ &gI2cHostComponentName2,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+Done:
+ //
+ // Free the buffer containing the list of handles from the handle database
+ //
+ if (DeviceHandleBuffer != NULL) {
+ gBS->FreePool (DeviceHandleBuffer);
+ }
+
+ return Status;
+}
|