/** @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) 2014, Hewlett-Packard Development Company, L.P.<BR> | |
Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#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 failed 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 | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "I2cHostI2cBusConfigurationAvailable: Error starting I2C operation, %r\n", 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_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; | |
UINTN StartBit; | |
SyncEvent = NULL; | |
FirstRequest = FALSE; | |
Status = EFI_SUCCESS; | |
if (RequestPacket == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((SlaveAddress & I2C_ADDRESSING_10_BIT) != 0) { | |
// | |
// 10-bit address, bits 0-9 are used for 10-bit I2C slave addresses, | |
// bits 10-30 are reserved bits and must be zero | |
// | |
StartBit = 10; | |
} else { | |
// | |
// 7-bit address, Bits 0-6 are used for 7-bit I2C slave addresses, | |
// bits 7-30 are reserved bits and must be zero | |
// | |
StartBit = 7; | |
} | |
if (BitFieldRead32 ((UINT32)SlaveAddress, StartBit, 30) != 0) { | |
// | |
// Reserved bit set in the SlaveAddress parameter | |
// | |
return EFI_NOT_FOUND; | |
} | |
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); | |
ASSERT (I2cRequest->RequestPacket != NULL); | |
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; | |
EFI_COMPONENT_NAME_PROTOCOL *ComponentName; | |
EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2; | |
// | |
// 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)) { | |
// | |
// 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, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Note we have to one by one uninstall the following protocols. | |
// It's because some of them are optionally installed based on | |
// the following PCD settings. | |
// gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnosticsDisable | |
// gEfiMdePkgTokenSpaceGuid.PcdComponentNameDisable | |
// gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable | |
// gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable | |
// | |
Status = gBS->HandleProtocol ( | |
gI2cHostDriverBinding.DriverBindingHandle, | |
&gEfiComponentNameProtocolGuid, | |
(VOID **) &ComponentName | |
); | |
if (!EFI_ERROR (Status)) { | |
gBS->UninstallProtocolInterface ( | |
gI2cHostDriverBinding.DriverBindingHandle, | |
&gEfiComponentNameProtocolGuid, | |
ComponentName | |
); | |
} | |
Status = gBS->HandleProtocol ( | |
gI2cHostDriverBinding.DriverBindingHandle, | |
&gEfiComponentName2ProtocolGuid, | |
(VOID **) &ComponentName2 | |
); | |
if (!EFI_ERROR (Status)) { | |
gBS->UninstallProtocolInterface ( | |
gI2cHostDriverBinding.DriverBindingHandle, | |
&gEfiComponentName2ProtocolGuid, | |
ComponentName2 | |
); | |
} | |
Status = EFI_SUCCESS; | |
Done: | |
// | |
// Free the buffer containing the list of handles from the handle database | |
// | |
if (DeviceHandleBuffer != NULL) { | |
gBS->FreePool (DeviceHandleBuffer); | |
} | |
return Status; | |
} |