From 43e543bcaa235955a93547a3e06cefcaf9ee605c Mon Sep 17 00:00:00 2001 From: Elvin Li Date: Wed, 14 Aug 2013 01:16:06 +0000 Subject: [PATCH] Add I2C bus DXE driver and I2C host DXE driver following PI 1.3 spec. Signed-off-by: Elvin Li Reviewed-by: Leahy Leroy P git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14549 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/I2c/I2cDxe/I2cBus.c | 1468 ++++++++++++++++++++ MdeModulePkg/Bus/I2c/I2cDxe/I2cBusDxe.inf | 54 + MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.c | 75 + MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.h | 1097 +++++++++++++++ MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf | 56 + MdeModulePkg/Bus/I2c/I2cDxe/I2cHost.c | 1169 ++++++++++++++++ MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf | 53 + MdeModulePkg/MdeModulePkg.dsc | 3 + 8 files changed, 3975 insertions(+) create mode 100644 MdeModulePkg/Bus/I2c/I2cDxe/I2cBus.c create mode 100644 MdeModulePkg/Bus/I2c/I2cDxe/I2cBusDxe.inf create mode 100644 MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.c create mode 100644 MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.h create mode 100644 MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf create mode 100644 MdeModulePkg/Bus/I2c/I2cDxe/I2cHost.c create mode 100644 MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf diff --git a/MdeModulePkg/Bus/I2c/I2cDxe/I2cBus.c b/MdeModulePkg/Bus/I2c/I2cDxe/I2cBus.c new file mode 100644 index 0000000000..3da1474edc --- /dev/null +++ b/MdeModulePkg/Bus/I2c/I2cDxe/I2cBus.c @@ -0,0 +1,1468 @@ +/** @file + This file implements I2C IO Protocol which enables the user to manipulate a single + I2C device independent of the host controller and I2C design. + + Copyright (c) 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "I2cDxe.h" + +// +// EFI_DRIVER_BINDING_PROTOCOL instance +// +EFI_DRIVER_BINDING_PROTOCOL gI2cBusDriverBinding = { + I2cBusDriverSupported, + I2cBusDriverStart, + I2cBusDriverStop, + 0x10, + NULL, + NULL +}; + +// +// Template for I2C Bus Child Device. +// +I2C_DEVICE_CONTEXT gI2cDeviceContextTemplate = { + I2C_DEVICE_SIGNATURE, + NULL, + { // I2cIo Protocol + I2cBusQueueRequest, // QueueRequest + NULL, // DeviceGuid + 0, // DeviceIndex + 0, // HardwareRevision + NULL // I2cControllerCapabilities + }, + NULL, // DevicePath + NULL, // I2cDevice + NULL, // I2cBusContext +}; + +// +// Template for controller device path node. +// +CONTROLLER_DEVICE_PATH gControllerDevicePathTemplate = { + { + HARDWARE_DEVICE_PATH, + HW_CONTROLLER_DP, + { + (UINT8) (sizeof (CONTROLLER_DEVICE_PATH)), + (UINT8) ((sizeof (CONTROLLER_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +// +// Template for vendor device path node. +// +VENDOR_DEVICE_PATH gVendorDevicePathTemplate = { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }} +}; + +// +// Driver name table +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mI2cBusDriverNameTable[] = { + { "eng;en", (CHAR16 *) L"I2C Bus Driver" }, + { NULL , NULL } +}; + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gI2cBusComponentName = { + (EFI_COMPONENT_NAME_GET_DRIVER_NAME) I2cBusComponentNameGetDriverName, + (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) I2cBusComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gI2cBusComponentName2 = { + I2cBusComponentNameGetDriverName, + I2cBusComponentNameGetControllerName, + "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 +I2cBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mI2cBusDriverNameTable, + DriverName, + (BOOLEAN)(This != &gI2cBusComponentName2) + ); +} + +/** + 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 +I2cBusComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Check if the child of I2C controller has been created. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] Controller I2C controller handle. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. + @param[in] RemainingHasControllerNode Indicate if RemainingDevicePath contains CONTROLLER_DEVICE_PATH. + @param[in] RemainingControllerNumber Controller number in CONTROLLER_DEVICE_PATH. + + @retval EFI_SUCCESS The child of I2C controller is not created. + @retval Others The child of I2C controller has been created or other errors happen. + +**/ +EFI_STATUS +CheckRemainingDevicePath ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath, + IN BOOLEAN RemainingHasControllerNode, + IN UINT32 RemainingControllerNumber + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *SystemDevicePath; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + UINTN Index; + BOOLEAN SystemHasControllerNode; + UINT32 SystemControllerNumber; + + SystemHasControllerNode = FALSE; + SystemControllerNumber = 0; + + Status = gBS->OpenProtocolInformation ( + Controller, + &gEfiI2cHostProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &SystemDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // Find vendor device path node and compare + // + while (!IsDevicePathEnd (SystemDevicePath)) { + if ((DevicePathType (SystemDevicePath) == HARDWARE_DEVICE_PATH) && + (DevicePathSubType (SystemDevicePath) == HW_VENDOR_DP)) { + // + // Check if vendor device path is same between system device path and remaining device path + // + if (CompareMem (SystemDevicePath, RemainingDevicePath, sizeof (VENDOR_DEVICE_PATH)) == 0) { + // + // Get controller node appended after vendor node + // + SystemDevicePath = NextDevicePathNode (SystemDevicePath); + if ((DevicePathType (SystemDevicePath) == HARDWARE_DEVICE_PATH) && + (DevicePathSubType (SystemDevicePath) == HW_CONTROLLER_DP)) { + SystemHasControllerNode = TRUE; + SystemControllerNumber = ((CONTROLLER_DEVICE_PATH *) SystemDevicePath)->ControllerNumber; + } else { + SystemHasControllerNode = FALSE; + SystemControllerNumber = 0; + } + if (((SystemHasControllerNode) && (!RemainingHasControllerNode) && (SystemControllerNumber == 0)) || + ((!SystemHasControllerNode) && (RemainingHasControllerNode) && (RemainingControllerNumber == 0)) || + ((SystemHasControllerNode) && (RemainingHasControllerNode) && (SystemControllerNumber == RemainingControllerNumber)) || + ((!SystemHasControllerNode) && (!RemainingHasControllerNode))) { + DEBUG ((EFI_D_ERROR, "This I2C device has been already started.\n")); + Status = EFI_UNSUPPORTED; + break; + } + } + } + SystemDevicePath = NextDevicePathNode (SystemDevicePath); + } + if (EFI_ERROR (Status)) { + break; + } + } + } + } + FreePool (OpenInfoBuffer); + return Status; +} + +/** + 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 +I2cBusDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_I2C_ENUMERATE_PROTOCOL *I2cEnumerate; + EFI_I2C_HOST_PROTOCOL *I2cHost; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevPathNode; + BOOLEAN RemainingHasControllerNode; + UINT32 RemainingControllerNumber; + + RemainingHasControllerNode = FALSE; + RemainingControllerNumber = 0; + + // + // Determine if the I2c Enumerate Protocol is available + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiI2cEnumerateProtocolGuid, + (VOID **) &I2cEnumerate, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiI2cEnumerateProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) { + // + // Check if the first node of RemainingDevicePath is a hardware vendor device path + // + if ((DevicePathType (RemainingDevicePath) != HARDWARE_DEVICE_PATH) || + (DevicePathSubType (RemainingDevicePath) != HW_VENDOR_DP)) { + return EFI_UNSUPPORTED; + } + // + // Check if the second node of RemainingDevicePath is a controller node + // + DevPathNode = NextDevicePathNode (RemainingDevicePath); + if (!IsDevicePathEnd (DevPathNode)) { + if ((DevicePathType (DevPathNode) != HARDWARE_DEVICE_PATH) || + (DevicePathSubType (DevPathNode) != HW_CONTROLLER_DP)) { + return EFI_UNSUPPORTED; + } else { + RemainingHasControllerNode = TRUE; + RemainingControllerNumber = ((CONTROLLER_DEVICE_PATH *) DevPathNode)->ControllerNumber; + } + } + } + + // + // Determine if the I2C Host Protocol is available + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiI2cHostProtocolGuid, + (VOID **) &I2cHost, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiI2cHostProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + + if (Status == EFI_ALREADY_STARTED) { + if ((RemainingDevicePath == NULL) || + ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath))) { + // + // If RemainingDevicePath is NULL or is the End of Device Path Node, return EFI_SUCCESS. + // + Status = EFI_SUCCESS; + } else { + // + // Test if the child with the RemainingDevicePath has already been created. + // + Status = CheckRemainingDevicePath ( + This, + Controller, + RemainingDevicePath, + RemainingHasControllerNode, + RemainingControllerNumber + ); + } + } + + return Status; +} + +/** + 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 +I2cBusDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_I2C_ENUMERATE_PROTOCOL *I2cEnumerate; + EFI_I2C_HOST_PROTOCOL *I2cHost; + I2C_BUS_CONTEXT *I2cBusContext; + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + I2cBusContext = NULL; + ParentDevicePath = NULL; + I2cEnumerate = NULL; + I2cHost = NULL; + + // + // Determine if the I2C controller is available + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiI2cHostProtocolGuid, + (VOID**)&I2cHost, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + DEBUG ((EFI_D_ERROR, "I2cBus: open I2C host error, Status = %r\n", Status)); + return Status; + } + + if (Status == EFI_ALREADY_STARTED) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &I2cBusContext, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "I2cBus: open private protocol error, Status = %r.\n", Status)); + return Status; + } + } + + // + // Get the I2C bus enumeration API + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiI2cEnumerateProtocolGuid, + (VOID**)&I2cEnumerate, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + DEBUG ((EFI_D_ERROR, "I2cBus: open I2C enumerate error, Status = %r\n", Status)); + goto Error; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + DEBUG ((EFI_D_ERROR, "I2cBus: open device path error, Status = %r\n", Status)); + goto Error; + } + + if ((RemainingDevicePath != NULL) && IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath is the End of Device Path Node, + // don't create any child device and return EFI_SUCESS + // + return EFI_SUCCESS; + } + + // + // Allocate the buffer for I2C_BUS_CONTEXT if it is not allocated before. + // + if (I2cBusContext == NULL) { + // + // Allocate the I2C context structure for the current I2C controller + // + I2cBusContext = AllocateZeroPool (sizeof (I2C_BUS_CONTEXT)); + if (I2cBusContext == NULL) { + DEBUG ((EFI_D_ERROR, "I2cBus: there is no enough memory to allocate.\n")); + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + /* + +----------------+ + .->| I2C_BUS_CONTEXT|<----- This file Protocol (gEfiCallerIdGuid) installed on I2C Controller handle + | +----------------+ + | + | +----------------------------+ + | | I2C_DEVICE_CONTEXT | + `--| | + | | + | I2C IO Protocol Structure | <----- I2C IO Protocol + | | + +----------------------------+ + + */ + I2cBusContext->I2cHost = I2cHost; + I2cBusContext->I2cEnumerate = I2cEnumerate; + // + // Parent controller used to create children + // + I2cBusContext->Controller = Controller; + // + // Parent controller device path used to create children device path + // + I2cBusContext->ParentDevicePath = ParentDevicePath; + + I2cBusContext->DriverBindingHandle = This->DriverBindingHandle; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiCallerIdGuid, + I2cBusContext, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "I2cBus: install private protocol error, Status = %r.\n", Status)); + goto Error; + } + } + + // + // Start the driver + // + Status = RegisterI2cDevice (I2cBusContext, Controller, RemainingDevicePath); + + return Status; + +Error: + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "I2cBus: Start() function failed, Status = %r\n", Status)); + if (ParentDevicePath != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + if (I2cHost != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiI2cHostProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + if (I2cEnumerate != NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiI2cEnumerateProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + if (I2cBusContext != NULL) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + &Controller, + gEfiCallerIdGuid, + I2cBusContext, + NULL + ); + FreePool (I2cBusContext); + } + } + + // + // 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 +I2cBusDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + I2C_BUS_CONTEXT *I2cBusContext; + EFI_STATUS Status; + BOOLEAN AllChildrenStopped; + UINTN Index; + + if (NumberOfChildren == 0) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiI2cHostProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiI2cEnumerateProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &I2cBusContext, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiCallerIdGuid, + I2cBusContext, + NULL + ); + // + // No more child now, free bus context data. + // + FreePool (I2cBusContext); + } + return Status; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + Status = UnRegisterI2cDevice (This, Controller, ChildHandleBuffer[Index]); + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; +} + +/** + Enumerate the I2C bus + + This routine walks the platform specific data describing the + I2C bus to create the I2C devices where driver GUIDs were + specified. + + @param[in] I2cBusContext Address of an I2C_BUS_CONTEXT structure + @param[in] Controller Handle to the controller + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. + + @retval EFI_SUCCESS The bus is successfully configured + +**/ +EFI_STATUS +RegisterI2cDevice ( + IN I2C_BUS_CONTEXT *I2cBusContext, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + I2C_DEVICE_CONTEXT *I2cDeviceContext; + EFI_STATUS Status; + CONST EFI_I2C_DEVICE *Device; + CONST EFI_I2C_DEVICE *TempDevice; + UINT32 RemainingPathDeviceIndex; + EFI_DEVICE_PATH_PROTOCOL *DevPathNode; + BOOLEAN BuildControllerNode; + UINTN Count; + + Status = EFI_SUCCESS; + BuildControllerNode = TRUE; + + // + // Default DeviceIndex + // + RemainingPathDeviceIndex = 0; + + // + // Determine the controller number in Controller Node Device Path when RemainingDevicePath is not NULL. + // + if (RemainingDevicePath != NULL) { + // + // Check if there is a controller node appended after vendor node + // + DevPathNode = NextDevicePathNode (RemainingDevicePath); + if ((DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH) && + (DevicePathSubType(DevPathNode) == HW_CONTROLLER_DP)) { + // + // RemainingDevicePath != NULL and RemainingDevicePath contains Controller Node, + // add Controller Node to Device Path on child handle. + // + RemainingPathDeviceIndex = ((CONTROLLER_DEVICE_PATH *) DevPathNode)->ControllerNumber; + } else { + // + // RemainingDevicePath != NULL and RemainingDevicePath does not contain Controller Node, + // do not add controller node to Device Path on child handle. + // + BuildControllerNode = FALSE; + } + } + + // + // Walk the list of I2C devices on this bus + // + Device = NULL; + while (TRUE) { + // + // Get the next I2C device + // + Status = I2cBusContext->I2cEnumerate->Enumerate (I2cBusContext->I2cEnumerate, &Device); + if (EFI_ERROR (Status) || Device == NULL) { + if (RemainingDevicePath != NULL) { + Status = EFI_NOT_FOUND; + } else { + Status = EFI_SUCCESS; + } + break; + } + + // + // Determine if the device info is valid + // + if ((Device->DeviceGuid == NULL) || (Device->SlaveAddressCount == 0) || (Device->SlaveAddressArray == NULL)) { + DEBUG ((EFI_D_ERROR, "Invalid EFI_I2C_DEVICE reported by I2c Enumerate protocol.\n")); + continue; + } + + if (RemainingDevicePath == NULL) { + if (Device->DeviceIndex == 0) { + // + // Determine if the controller node is necessary when controller number is zero in I2C device + // + TempDevice = NULL; + Count = 0; + while (TRUE) { + // + // Get the next I2C device + // + Status = I2cBusContext->I2cEnumerate->Enumerate (I2cBusContext->I2cEnumerate, &TempDevice); + if (EFI_ERROR (Status) || TempDevice == NULL) { + Status = EFI_SUCCESS; + break; + } + if (CompareGuid (Device->DeviceGuid, TempDevice->DeviceGuid)) { + Count++; + } + } + if (Count == 1) { + // + // RemainingDevicePath == NULL and only DeviceIndex 0 is present on the I2C bus, + // do not add Controller Node to Device Path on child handle. + // + BuildControllerNode = FALSE; + } + } + } else { + // + // Find I2C device reported in Remaining Device Path + // + if ((!CompareGuid (&((VENDOR_DEVICE_PATH *)RemainingDevicePath)->Guid, Device->DeviceGuid)) || + (RemainingPathDeviceIndex != Device->DeviceIndex)) { + continue; + } + } + + // + // Build the device context for current I2C device. + // + I2cDeviceContext = NULL; + I2cDeviceContext = AllocateCopyPool (sizeof (I2C_DEVICE_CONTEXT), &gI2cDeviceContextTemplate); + ASSERT (I2cDeviceContext != NULL); + if (I2cDeviceContext == NULL) { + continue; + } + + // + // Initialize the specific device context + // + I2cDeviceContext->I2cBusContext = I2cBusContext; + I2cDeviceContext->I2cDevice = Device; + I2cDeviceContext->I2cIo.DeviceGuid = Device->DeviceGuid; + I2cDeviceContext->I2cIo.DeviceIndex = Device->DeviceIndex; + I2cDeviceContext->I2cIo.HardwareRevision = Device->HardwareRevision; + I2cDeviceContext->I2cIo.I2cControllerCapabilities = I2cBusContext->I2cHost->I2cControllerCapabilities; + + // + // Build the device path + // + Status = I2cBusDevicePathAppend (I2cDeviceContext, BuildControllerNode); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Install the protocol + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &I2cDeviceContext->Handle, + &gEfiI2cIoProtocolGuid, + &I2cDeviceContext->I2cIo, + &gEfiDevicePathProtocolGuid, + I2cDeviceContext->DevicePath, + NULL ); + if (EFI_ERROR (Status)) { + // + // Free resources for this I2C device + // + ReleaseI2cDeviceContext (I2cDeviceContext); + continue; + } + + // + // Create the child handle + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiI2cHostProtocolGuid, + (VOID **) &I2cBusContext->I2cHost, + I2cBusContext->DriverBindingHandle, + I2cDeviceContext->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + I2cDeviceContext->Handle, + &gEfiDevicePathProtocolGuid, + I2cDeviceContext->DevicePath, + &gEfiI2cIoProtocolGuid, + &I2cDeviceContext->I2cIo, + NULL + ); + // + // Free resources for this I2C device + // + ReleaseI2cDeviceContext (I2cDeviceContext); + continue; + } + + if (RemainingDevicePath != NULL) { + // + // Child has been created successfully + // + break; + } + } + + return Status; +} + + +/** + Queue an I2C transaction for execution on the I2C device. + + This routine must be called at or below TPL_NOTIFY. For synchronous + requests this routine must be called at or below TPL_CALLBACK. + + This routine queues an I2C transaction to the I2C controller for + execution on the I2C bus. + + When Event is NULL, QueueRequest() operates synchronously and returns + the I2C completion status as its return value. + + When Event is not NULL, QueueRequest() synchronously returns EFI_SUCCESS + indicating that the asynchronous I2C transaction was queued. The values + above are returned in the buffer pointed to by I2cStatus upon the + completion of the I2C transaction when I2cStatus is not NULL. + + The upper layer driver writer provides the following to the platform + vendor: + + 1. Vendor specific GUID for the I2C part + 2. Guidance on proper construction of the slave address array when the + I2C device uses more than one slave address. The I2C bus protocol + uses the SlaveAddressIndex to perform relative to physical address + translation to access the blocks of hardware within the I2C device. + + @param[in] This Pointer to an EFI_I2C_IO_PROTOCOL structure. + @param[in] SlaveAddressIndex Index value into an array of slave addresses + for the I2C device. The values in the array + are specified by the board designer, with the + third party I2C device driver writer providing + the slave address order. + + For devices that have a single slave address, + this value must be zero. If the I2C device + uses more than one slave address then the + third party (upper level) I2C driver writer + needs to specify the order of entries in the + slave address array. + + \ref ThirdPartyI2cDrivers "Third Party I2C + Drivers" section in I2cMaster.h. + @param[in] Event Event to signal for asynchronous transactions, + NULL for synchronous transactions + @param[in] RequestPacket Pointer to an EFI_I2C_REQUEST_PACKET structure + describing the I2C transaction + @param[out] I2cStatus Optional buffer to receive the I2C transaction + completion status + + @retval EFI_SUCCESS The asynchronous transaction was successfully + queued when Event is not NULL. + @retval EFI_SUCCESS The transaction completed successfully when + Event is NULL. + @retval EFI_ABORTED The request did not complete because the driver + binding Stop() routine was called. + @retval EFI_BAD_BUFFER_SIZE The RequestPacket->LengthInBytes value is too + large. + @retval EFI_DEVICE_ERROR There was an I2C error (NACK) during the + transaction. + @retval EFI_INVALID_PARAMETER RequestPacket is NULL + @retval EFI_NOT_FOUND Reserved bit set in the SlaveAddress parameter + @retval EFI_NO_MAPPING The EFI_I2C_HOST_PROTOCOL could not set the + bus configuration required to access this I2C + device. + @retval EFI_NO_RESPONSE The I2C device is not responding to the slave + address selected by SlaveAddressIndex. + EFI_DEVICE_ERROR will be returned if the + controller cannot distinguish when the NACK + occurred. + @retval EFI_OUT_OF_RESOURCES Insufficient memory for I2C transaction + @retval EFI_UNSUPPORTED The controller does not support the requested + transaction. + +**/ +EFI_STATUS +EFIAPI +I2cBusQueueRequest ( + IN CONST EFI_I2C_IO_PROTOCOL *This, + IN UINTN SlaveAddressIndex, + IN EFI_EVENT Event OPTIONAL, + IN EFI_I2C_REQUEST_PACKET *RequestPacket, + OUT EFI_STATUS *I2cStatus OPTIONAL + ) +{ + CONST EFI_I2C_DEVICE *I2cDevice; + I2C_BUS_CONTEXT *I2cBusContext; + CONST EFI_I2C_HOST_PROTOCOL *I2cHost; + I2C_DEVICE_CONTEXT *I2cDeviceContext; + EFI_STATUS Status; + + if (RequestPacket == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Validate the I2C slave index + // + I2cDeviceContext = I2C_DEVICE_CONTEXT_FROM_PROTOCOL (This); + I2cDevice = I2cDeviceContext->I2cDevice; + if ( SlaveAddressIndex >= I2cDevice->SlaveAddressCount ) { + return EFI_INVALID_PARAMETER; + } + + // + // Locate the I2c Host Protocol to queue request + // + I2cBusContext = I2cDeviceContext->I2cBusContext; + I2cHost = I2cBusContext->I2cHost; + + // + // Start the I2C operation + // + Status = I2cHost->QueueRequest ( + I2cHost, + I2cDevice->I2cBusConfiguration, + I2cDevice->SlaveAddressArray [SlaveAddressIndex], + Event, + RequestPacket, + I2cStatus + ); + + return Status; +} + +/** + Release all the resources allocated for the I2C device. + + This function releases all the resources allocated for the I2C device. + + @param I2cDeviceContext The I2C child device involved for the operation. + +**/ +VOID +ReleaseI2cDeviceContext ( + IN I2C_DEVICE_CONTEXT *I2cDeviceContext + ) +{ + if (I2cDeviceContext == NULL) { + return; + } + + if (I2cDeviceContext->DevicePath != NULL) { + FreePool (I2cDeviceContext->DevicePath); + } + + FreePool (I2cDeviceContext); +} + +/** + Unregister an I2C device. + + This function removes the protocols installed on the controller handle and + frees the resources allocated for the I2C device. + + @param This The pointer to EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The controller handle of the I2C device. + @param Handle The child handle. + + @retval EFI_SUCCESS The I2C device is successfully unregistered. + @return Others Some error occurs when unregistering the I2C device. + +**/ +EFI_STATUS +UnRegisterI2cDevice ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + I2C_DEVICE_CONTEXT *I2cDeviceContext; + EFI_I2C_IO_PROTOCOL *I2cIo; + EFI_I2C_HOST_PROTOCOL *I2cHost; + + I2cIo = NULL; + + Status = gBS->OpenProtocol ( + Handle, + &gEfiI2cIoProtocolGuid, + (VOID **) &I2cIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get I2c device context data. + // + I2cDeviceContext = I2C_DEVICE_CONTEXT_FROM_PROTOCOL (I2cIo); + + // + // Close the child handle + // + gBS->CloseProtocol ( + Controller, + &gEfiI2cHostProtocolGuid, + This->DriverBindingHandle, + Handle + ); + + // + // The I2C Bus driver installs the I2C Io and Device Path Protocol in the DriverBindingStart(). + // Here should uninstall them. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + Handle, + &gEfiDevicePathProtocolGuid, + I2cDeviceContext->DevicePath, + &gEfiI2cIoProtocolGuid, + &I2cDeviceContext->I2cIo, + NULL + ); + + if (EFI_ERROR (Status)) { + // + // Keep parent and child relationship + // + gBS->OpenProtocol ( + Controller, + &gEfiI2cHostProtocolGuid, + (VOID **) &I2cHost, + This->DriverBindingHandle, + Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + return Status; + } + + // + // Free resources for this I2C device + // + ReleaseI2cDeviceContext (I2cDeviceContext); + + return EFI_SUCCESS; +} + +/** + Create a path for the I2C device + + Append the I2C slave path to the I2C master controller path. + + @param[in] I2cDeviceContext Address of an I2C_DEVICE_CONTEXT structure. + @param[in] BuildControllerNode Flag to build controller node in device path. + + @retval EFI_SUCCESS The I2C device path is built successfully. + @return Others It is failed to built device path. + +**/ +EFI_STATUS +I2cBusDevicePathAppend ( + IN I2C_DEVICE_CONTEXT *I2cDeviceContext, + IN BOOLEAN BuildControllerNode + ) +{ + EFI_DEVICE_PATH_PROTOCOL *PreviousDevicePath; + + PreviousDevicePath = NULL; + + // + // Build vendor device path + // + CopyMem (&gVendorDevicePathTemplate.Guid, I2cDeviceContext->I2cDevice->DeviceGuid, sizeof (EFI_GUID)); + I2cDeviceContext->DevicePath = AppendDevicePathNode ( + I2cDeviceContext->I2cBusContext->ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &gVendorDevicePathTemplate + ); + ASSERT (I2cDeviceContext->DevicePath != NULL); + if (I2cDeviceContext->DevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if ((BuildControllerNode) && (I2cDeviceContext->DevicePath != NULL)) { + // + // Build the final I2C device path with controller node + // + PreviousDevicePath = I2cDeviceContext->DevicePath; + gControllerDevicePathTemplate.ControllerNumber = I2cDeviceContext->I2cDevice->DeviceIndex; + I2cDeviceContext->DevicePath = AppendDevicePathNode ( + I2cDeviceContext->DevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &gControllerDevicePathTemplate + ); + gBS->FreePool (PreviousDevicePath); + ASSERT (I2cDeviceContext->DevicePath != NULL); + if (I2cDeviceContext->DevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + return EFI_SUCCESS; +} + +/** + The user entry point for the I2C bus 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 +InitializeI2cBus( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gI2cBusDriverBinding, + NULL, + &gI2cBusComponentName, + &gI2cBusComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + + return Status; +} + +/** + This is the unload handle for I2C bus 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 +I2cBusUnload ( + 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 Driver BindingHandle from all + // the devices in the handle database. + // + for (Index = 0; Index < DeviceHandleCount; Index++) { + Status = gBS->DisconnectController ( + DeviceHandleBuffer[Index], + gI2cBusDriverBinding.DriverBindingHandle, + NULL + ); + if (EFI_ERROR (Status)) { + goto Done; + } + } + + // + // Uninstall all the protocols installed in the driver entry point + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + gI2cBusDriverBinding.DriverBindingHandle, + &gEfiDriverBindingProtocolGuid, + &gI2cBusDriverBinding, + &gEfiComponentNameProtocolGuid, + &gI2cBusComponentName, + &gEfiComponentName2ProtocolGuid, + &gI2cBusComponentName2, + 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; +} diff --git a/MdeModulePkg/Bus/I2c/I2cDxe/I2cBusDxe.inf b/MdeModulePkg/Bus/I2c/I2cDxe/I2cBusDxe.inf new file mode 100644 index 0000000000..12b42ab417 --- /dev/null +++ b/MdeModulePkg/Bus/I2c/I2cDxe/I2cBusDxe.inf @@ -0,0 +1,54 @@ +## @file +# This driver enumerates I2C devices on I2C bus and produce I2C +# IO Protocol on I2C devices. +# +# Copyright (c) 2013, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = I2cBusDxe + FILE_GUID = 0C34B372-2622-4A13-A46E-BFD0DEB48BFF + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeI2cBus + UNLOAD_IMAGE = I2cBusUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + I2cDxe.h + I2cBus.c + +[LibraryClasses] + BaseMemoryLib + DebugLib + DevicePathLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Packages] + MdePkg/MdePkg.dec + +[Protocols] + gEfiI2cIoProtocolGuid ## BY_START + gEfiI2cHostProtocolGuid ## BY_START + gEfiI2cMasterProtocolGuid ## TO_START + gEfiI2cEnumerateProtocolGuid ## TO_START + gEfiI2cBusConfigurationManagementProtocolGuid ## TO_START + gEfiI2cHostProtocolGuid ## TO_START diff --git a/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.c b/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.c new file mode 100644 index 0000000000..fb985f96e2 --- /dev/null +++ b/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.c @@ -0,0 +1,75 @@ +/** @file + This file implements the entrypoint and unload function for I2C DXE module. + + Copyright (c) 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "I2cDxe.h" + +/** + The user Entry Point for I2C 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 +InitializeI2c( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = InitializeI2cHost ( ImageHandle, SystemTable ); + if ( !EFI_ERROR ( Status )) + { + Status = InitializeI2cBus ( ImageHandle, SystemTable ); + } + return Status; +} + +/** + This is the unload handle for I2C 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 +I2cUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + + // + // Disconnect the drivers + // + Status = I2cBusUnload ( ImageHandle ); + if ( !EFI_ERROR ( Status )) { + Status = I2cHostUnload ( ImageHandle ); + } + return Status; +} diff --git a/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.h b/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.h new file mode 100644 index 0000000000..1490d4283a --- /dev/null +++ b/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.h @@ -0,0 +1,1097 @@ +/** @file + Private data structures for the I2C DXE driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2013, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __I2C_DXE_H__ +#define __I2C_DXE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define I2C_DEVICE_SIGNATURE SIGNATURE_32 ('I', '2', 'C', 'D') +#define I2C_HOST_SIGNATURE SIGNATURE_32 ('I', '2', 'C', 'H') +#define I2C_REQUEST_SIGNATURE SIGNATURE_32 ('I', '2', 'C', 'R') + +// +// Synchronize access to the list of requests +// +#define TPL_I2C_SYNC TPL_NOTIFY + +// +// I2C bus context +// +typedef struct { + EFI_I2C_ENUMERATE_PROTOCOL *I2cEnumerate; + EFI_I2C_HOST_PROTOCOL *I2cHost; + EFI_HANDLE Controller; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_HANDLE DriverBindingHandle; +} I2C_BUS_CONTEXT; + +// +// I2C device context +// +typedef struct { + // + // Structure identification + // + UINT32 Signature; + + // + // I2c device handle + // + EFI_HANDLE Handle; + + // + // Upper level API to support the I2C device I/O + // + EFI_I2C_IO_PROTOCOL I2cIo; + + // + // Device path for this device + // + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + // + // Platform specific data for this device + // + CONST EFI_I2C_DEVICE *I2cDevice; + + // + // Context for the common I/O support including the + // lower level API to the host controller. + // + I2C_BUS_CONTEXT *I2cBusContext; +} I2C_DEVICE_CONTEXT; + +#define I2C_DEVICE_CONTEXT_FROM_PROTOCOL(a) CR (a, I2C_DEVICE_CONTEXT, I2cIo, I2C_DEVICE_SIGNATURE) + +// +// I2C Request +// +typedef struct { + // + // Signature + // + UINT32 Signature; + + // + // Next request in the pending request list + // + LIST_ENTRY Link; + + // + // I2C bus configuration for the operation + // + UINTN I2cBusConfiguration; + + // + // I2C slave address for the operation + // + UINTN SlaveAddress; + + // + // Event to set for asynchronous operations, NULL for + // synchronous operations + // + EFI_EVENT Event; + + // + // I2C operation description + // + EFI_I2C_REQUEST_PACKET *RequestPacket; + + // + // Optional buffer to receive the I2C operation completion status + // + EFI_STATUS *Status; +} I2C_REQUEST; + +#define I2C_REQUEST_FROM_ENTRY(a) CR (a, I2C_REQUEST, Link, I2C_REQUEST_SIGNATURE); + +// +// I2C host context +// +typedef struct { + // + // Structure identification + // + UINTN Signature; + + // + // Current I2C bus configuration + // + UINTN I2cBusConfiguration; + + // + // I2C bus configuration management event + // + EFI_EVENT I2cBusConfigurationEvent; + + // + // I2C operation completion event + // + EFI_EVENT I2cEvent; + + // + // I2C operation and I2C bus configuration management status + // + EFI_STATUS Status; + + // + // I2C bus configuration management operation pending + // + BOOLEAN I2cBusConfigurationManagementPending; + + // + // I2C request list maintained by I2C Host + // + LIST_ENTRY RequestList; + + // + // Upper level API + // + EFI_I2C_HOST_PROTOCOL I2cHost; + + // + // I2C bus configuration management protocol + // + EFI_I2C_BUS_CONFIGURATION_MANAGEMENT_PROTOCOL *I2cBusConfigurationManagement; + + // + // Lower level API for I2C master (controller) + // + EFI_I2C_MASTER_PROTOCOL *I2cMaster; +} I2C_HOST_CONTEXT; + +#define I2C_HOST_CONTEXT_FROM_PROTOCOL(a) CR (a, I2C_HOST_CONTEXT, I2cHost, I2C_HOST_SIGNATURE) + +// +// Global Variables +// +extern EFI_COMPONENT_NAME_PROTOCOL gI2cBusComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gI2cBusComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gI2cBusDriverBinding; + +extern EFI_COMPONENT_NAME_PROTOCOL gI2cHostComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gI2cHostComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gI2cHostDriverBinding; + +/** + Start the I2C driver + + This routine allocates the necessary resources for the driver. + + This routine is called by I2cBusDriverStart to complete the driver + initialization. + + @param[in] I2cBus Address of an I2C_BUS_CONTEXT structure + @param[in] Controller Handle to the controller + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. + + @retval EFI_SUCCESS Driver API properly initialized + +**/ +EFI_STATUS +RegisterI2cDevice ( + IN I2C_BUS_CONTEXT *I2cBus, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Unregister an I2C device. + + This function removes the protocols installed on the controller handle and + frees the resources allocated for the I2C device. + + @param This The pointer to EFI_DRIVER_BINDING_PROTOCOL instance. + @param Controller The controller handle of the I2C device. + @param Handle The child handle. + + @retval EFI_SUCCESS The I2C device is successfully unregistered. + @return Others Some error occurs when unregistering the I2C device. + +**/ +EFI_STATUS +UnRegisterI2cDevice ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_HANDLE Handle + ); + +/** + Create a path for the I2C device + + Append the I2C slave path to the I2C master controller path. + + @param[in] I2cDeviceContext Address of an I2C_DEVICE_CONTEXT structure. + @param[in] BuildControllerNode Flag to build controller node in device path. + + @retval EFI_SUCCESS The I2C device path is built successfully. + @return Others It is failed to built device path. + +**/ +EFI_STATUS +I2cBusDevicePathAppend ( + IN I2C_DEVICE_CONTEXT *I2cDeviceContext, + IN BOOLEAN BuildControllerNode + ); + +/** + Queue an I2C transaction for execution on the I2C device. + + This routine must be called at or below TPL_NOTIFY. For synchronous + requests this routine must be called at or below TPL_CALLBACK. + + This routine queues an I2C transaction to the I2C controller for + execution on the I2C bus. + + When Event is NULL, QueueRequest() operates synchronously and returns + the I2C completion status as its return value. + + When Event is not NULL, QueueRequest() synchronously returns EFI_SUCCESS + indicating that the asynchronous I2C transaction was queued. The values + above are returned in the buffer pointed to by I2cStatus upon the + completion of the I2C transaction when I2cStatus is not NULL. + + The upper layer driver writer provides the following to the platform + vendor: + + 1. Vendor specific GUID for the I2C part + 2. Guidance on proper construction of the slave address array when the + I2C device uses more than one slave address. The I2C bus protocol + uses the SlaveAddressIndex to perform relative to physical address + translation to access the blocks of hardware within the I2C device. + + @param[in] This Pointer to an EFI_I2C_IO_PROTOCOL structure. + @param[in] SlaveAddressIndex Index value into an array of slave addresses + for the I2C device. The values in the array + are specified by the board designer, with the + third party I2C device driver writer providing + the slave address order. + + For devices that have a single slave address, + this value must be zero. If the I2C device + uses more than one slave address then the + third party (upper level) I2C driver writer + needs to specify the order of entries in the + slave address array. + + \ref ThirdPartyI2cDrivers "Third Party I2C + Drivers" section in I2cMaster.h. + @param[in] Event Event to signal for asynchronous transactions, + NULL for synchronous transactions + @param[in] RequestPacket Pointer to an EFI_I2C_REQUEST_PACKET structure + describing the I2C transaction + @param[out] I2cStatus Optional buffer to receive the I2C transaction + completion status + + @retval EFI_SUCCESS The asynchronous transaction was successfully + queued when Event is not NULL. + @retval EFI_SUCCESS The transaction completed successfully when + Event is NULL. + @retval EFI_ABORTED The request did not complete because the driver + binding Stop() routine was called. + @retval EFI_BAD_BUFFER_SIZE The RequestPacket->LengthInBytes value is too + large. + @retval EFI_DEVICE_ERROR There was an I2C error (NACK) during the + transaction. + @retval EFI_INVALID_PARAMETER RequestPacket is NULL + @retval EFI_NOT_FOUND Reserved bit set in the SlaveAddress parameter + @retval EFI_NO_MAPPING The EFI_I2C_HOST_PROTOCOL could not set the + bus configuration required to access this I2C + device. + @retval EFI_NO_RESPONSE The I2C device is not responding to the slave + address selected by SlaveAddressIndex. + EFI_DEVICE_ERROR will be returned if the + controller cannot distinguish when the NACK + occurred. + @retval EFI_OUT_OF_RESOURCES Insufficient memory for I2C transaction + @retval EFI_UNSUPPORTED The controller does not support the requested + transaction. + +**/ +EFI_STATUS +EFIAPI +I2cBusQueueRequest ( + IN CONST EFI_I2C_IO_PROTOCOL *This, + IN UINTN SlaveAddressIndex, + IN EFI_EVENT Event OPTIONAL, + IN EFI_I2C_REQUEST_PACKET *RequestPacket, + OUT EFI_STATUS *I2cStatus OPTIONAL + ); + +/** + 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 +I2cBusDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + 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 +I2cBusDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + 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 +I2cBusDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + 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 +I2cBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + 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 +I2cBusComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + The user entry point for the I2C bus 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 +InitializeI2cBus( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + This is the unload handle for I2C bus 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 +I2cBusUnload ( + IN EFI_HANDLE ImageHandle + ); + +/** + Release all the resources allocated for the I2C device. + + This function releases all the resources allocated for the I2C device. + + @param I2cDeviceContext The I2C child device involved for the operation. + +**/ +VOID +ReleaseI2cDeviceContext ( + IN I2C_DEVICE_CONTEXT *I2cDeviceContext + ); + +/** + Complete the current request + + @param[in] I2cHost 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 *I2cHost, + EFI_STATUS 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 *I2cHost + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + I2C + Specification 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +#endif // __I2C_DXE_H__ diff --git a/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf b/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf new file mode 100644 index 0000000000..8600531a94 --- /dev/null +++ b/MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf @@ -0,0 +1,56 @@ +## @file +# This driver produce I2C Host Protocol on I2C controller handle, enumerate I2C +# devices on I2C bus and produce I2C IO Protocol on I2C devices. +# +# Copyright (c) 2013, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = I2cDxe + FILE_GUID = ECA2AE9E-7594-4901-871C-449DA1A11660 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeI2c + UNLOAD_IMAGE = I2cUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + I2cDxe.c + I2cDxe.h + I2cHost.c + I2cBus.c + +[LibraryClasses] + BaseMemoryLib + DebugLib + DevicePathLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Packages] + MdePkg/MdePkg.dec + +[Protocols] + gEfiI2cIoProtocolGuid ## BY_START + gEfiI2cHostProtocolGuid ## BY_START + gEfiI2cMasterProtocolGuid ## TO_START + gEfiI2cEnumerateProtocolGuid ## TO_START + gEfiI2cBusConfigurationManagementProtocolGuid ## TO_START + gEfiI2cHostProtocolGuid ## TO_START 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.
+ 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 + I2C + Specification 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; +} diff --git a/MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf b/MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf new file mode 100644 index 0000000000..ebd9fcac4b --- /dev/null +++ b/MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf @@ -0,0 +1,53 @@ +## @file +# This driver produce I2C Host Protocol on I2C controller handle. +# +# Copyright (c) 2013, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = I2cHostDxe + FILE_GUID = CDEC3671-816E-43DC-A002-DCD645229338 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeI2cHost + UNLOAD_IMAGE = I2cHostUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + I2cDxe.h + I2cHost.c + +[LibraryClasses] + BaseMemoryLib + DebugLib + DevicePathLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Packages] + MdePkg/MdePkg.dec + +[Protocols] + gEfiI2cIoProtocolGuid ## BY_START + gEfiI2cHostProtocolGuid ## BY_START + gEfiI2cMasterProtocolGuid ## TO_START + gEfiI2cEnumerateProtocolGuid ## TO_START + gEfiI2cBusConfigurationManagementProtocolGuid ## TO_START + gEfiI2cHostProtocolGuid ## TO_START diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 2ff9dc5fae..a2248697db 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -214,6 +214,9 @@ MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf MdeModulePkg/Bus/Usb/UsbMouseAbsolutePointerDxe/UsbMouseAbsolutePointerDxe.inf MdeModulePkg/Bus/Usb/UsbMouseDxe/UsbMouseDxe.inf + MdeModulePkg/Bus/I2c/I2cDxe/I2cBusDxe.inf + MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf + MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf MdeModulePkg/Core/Dxe/DxeMain.inf { -- 2.39.2