/** @file For I2C Master Driver ;****************************************************************************** ;* Copyright (c) 2012 - 2020, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include "I2cMasterDxe.h" // // Globals // VOID *mI2cContext = NULL; EFI_HANDLE mImageHandle; I2C_PCI_HC_DEV_PATH mI2cPciHcDevPath = { {{ HARDWARE_DEVICE_PATH, HW_PCI_DP, sizeof(PCI_DEVICE_PATH)}, 0, 0 }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, sizeof(EFI_DEVICE_PATH) } }; I2C_MEM_HC_DEV_PATH mI2cMemHcDevPath = { {{ HARDWARE_DEVICE_PATH, HW_MEMMAP_DP, sizeof(MEMMAP_DEVICE_PATH)}, 0, 0, 0 }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, sizeof(EFI_DEVICE_PATH) } }; /** Set the I2C controller bus clock frequency. This routine must be called at or below TPL_NOTIFY. The software and controller do a best case effort of using the specified frequency for the I2C bus. If the frequency does not match exactly then the controller will use a slightly lower frequency for the I2C to avoid exceeding the operating conditions for any of the I2C devices on the bus. For example if 400 KHz was specified and the controller's divide network only supports 402 KHz or 398 KHz then the controller would be set to 398 KHz. However if the desired frequency is 400 KHz and the controller only supports 1 MHz and 100 KHz then this routine would return EFI_UNSUPPORTED. @param[in] This Address of an EFI_I2C_MASTER_PROTOCOL structure @param[in] BusClockHertz New I2C bus clock frequency in Hertz @retval EFI_SUCCESS The bus frequency was set successfully. @retval EFI_UNSUPPORTED The controller does not support this frequency. **/ EFI_STATUS EFIAPI I2cPortSetBusFrequency ( IN CONST EFI_I2C_MASTER_PROTOCOL *This, IN OUT UINTN *BusClockHertz ) { I2C_BUS_INSTANCE *I2cContext; EFI_STATUS Status; Status = EFI_SUCCESS; DEBUG (( DEBUG_INFO, "I2cPortSetBusFrequency entered\r\n" )); // // Locate the configuration // I2cContext = I2C_PORT_CONTEXT_FROM_MASTER_PROTOCOL ( This ); switch (*BusClockHertz) { case 100000: I2cContext->I2cBusSpeed = V_SPEED_STANDARD; break; case 400000: I2cContext->I2cBusSpeed = V_SPEED_FAST; break; case 1000000: I2cContext->I2cBusSpeed = V_SPEED_HIGH; break; default: *BusClockHertz = 400000; I2cContext->I2cBusSpeed = V_SPEED_STANDARD; return EFI_UNSUPPORTED; } // // Display the operation // DEBUG (( DEBUG_INFO, "Setting I2C bus frequency to %d KHz\r\n", (*BusClockHertz) / 1000 )); return Status; } /** Reset the I2C controller and configure it for use This routine must be called at or below TPL_NOTIFY. The controller's I2C bus frequency is set to 100 KHz. @param[in] This Address of an EFI_I2C_MASTER_PROTOCOL structure @retval EFI_SUCCESS The bus reset was set successfully. **/ EFI_STATUS I2cPortReset ( IN CONST EFI_I2C_MASTER_PROTOCOL *This ) { EFI_STATUS Status; I2C_BUS_INSTANCE *I2cPort; UINT32 PciCmdBackUp; BOOLEAN IsSwitched; EFI_PHYSICAL_ADDRESS Base0BackUp; Status = EFI_SUCCESS; DEBUG (( DEBUG_INFO, "I2cPortReset entered\r\n" )); // // Locate the configuration // I2cPort = I2C_PORT_CONTEXT_FROM_MASTER_PROTOCOL ( This ); IsSwitched = FALSE; if ( I2cPort->Info.GetDeviceMode (I2cPort) == I2C_DEVICE_MODE_ACPI) { I2cPort->Info.SwitcDevhMode (I2cPort, I2C_DEVICE_MODE_PCI); IsSwitched = TRUE; } LocateI2cBase( I2cPort, &Base0BackUp, &PciCmdBackUp); if (I2cPort->I2cBase == 0 || I2cPort->I2cHcRead8(I2cPort, R_IC_CON) == 0xFF) { FreeI2cBase(I2cPort, Base0BackUp, PciCmdBackUp); return EFI_DEVICE_ERROR; } // // Reset the host // I2cReset (I2cPort); FreeI2cBase(I2cPort, Base0BackUp, PciCmdBackUp); if (IsSwitched ) { I2cPort->Info.SwitcDevhMode (I2cPort, I2C_DEVICE_MODE_ACPI); } DEBUG (( DEBUG_INFO, "I2cPortReset exiting\r\n" )); return Status; } /** Start an I2C operation on the 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 consumer of this API is the I2C host driver. 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 function initiates an I2C operation on the controller. N.B. This API supports only one operation, no queuing support exists at this layer. The operation is performed by selecting the I2C device with its slave address and then sending all write data to the I2C device. If read data is requested, a restart is sent followed by the slave address and then the read data is clocked into the I2C controller and placed in the read buffer. When the operation completes, the status value is returned and then the event is set. @param[in] This Address of an EFI_I2C_MASTER_PROTOCOL structure @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_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_NOT_FOUND SlaveAddress 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_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_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 I2cPortMasterStartRequest ( IN CONST EFI_I2C_MASTER_PROTOCOL *This, IN UINTN SlaveAddress, IN EFI_I2C_REQUEST_PACKET *RequestPacket, IN EFI_EVENT Event OPTIONAL, OUT EFI_STATUS *I2cStatus OPTIONAL ) { UINTN Index; UINTN WriteBytes; UINTN ReadBytes; UINT8 *WriteBuffer; UINT8 *ReadBuffer; BOOLEAN Repeat; BOOLEAN IsSwitched; EFI_STATUS Status; I2C_BUS_INSTANCE *I2cPort; EFI_I2C_OPERATION *Operation; UINT32 PciCmdBackUp; EFI_PHYSICAL_ADDRESS Base0BackUp; DEBUG (( DEBUG_INFO, "I2cPortMasterStartRequest entered\r\n" )); // // Locate the configuration // I2cPort = I2C_PORT_CONTEXT_FROM_MASTER_PROTOCOL ( This ); if ( NULL != Event ) { return I2cPusAsyncQueue (I2cPort, SlaveAddress, RequestPacket, Event, I2cStatus); } // // Display the request // DEBUG (( DEBUG_INFO, "0x%016Lx: Starting I2C request packet 0x%016Lx, Slave: 0x%03x\r\n", (UINT64)(UINTN)I2cPort, (UINT64)(UINTN)RequestPacket, SlaveAddress )); if (SlaveAddress > 1023) { return RETURN_INVALID_PARAMETER; } if (RequestPacket == NULL) { return EFI_INVALID_PARAMETER; } Operation = RequestPacket->Operation; if ((Operation[0].LengthInBytes > This->I2cControllerCapabilities->MaximumTransmitBytes) || (RequestPacket->OperationCount == 2 && (Operation[1].LengthInBytes > This->I2cControllerCapabilities->MaximumTransmitBytes)) ) { return EFI_BAD_BUFFER_SIZE; } // // There doesn't support the SMBus PEC // if (Operation[0].Flags & I2C_FLAG_SMBUS_PEC) { Operation[0].Flags &= ~I2C_FLAG_SMBUS_PEC; } else if (Operation[1].Flags & I2C_FLAG_SMBUS_PEC) { Operation[1].LengthInBytes -=1 ; } // // QuickRead/QuickWrite // if (RequestPacket->OperationCount == 1 && Operation[0].LengthInBytes == 0) { return EFI_UNSUPPORTED; } if ((Operation[0].Flags & I2C_FLAG_SMBUS_OPERATION) == I2C_FLAG_SMBUS_OPERATION) { // // Used common init // // ReadDataByte // ReadDataWord // ReadBlock // ProcessCall // BlkProcessCall Repeat = FALSE; WriteBytes = Operation[0].LengthInBytes; WriteBuffer = Operation[0].Buffer; ReadBytes = Operation[1].LengthInBytes; ReadBuffer = Operation[1].Buffer; // // For Writ data below // SendByte // WriteDataByte // WriteDataWord // WriteBlock // if ((Operation[0].Flags & I2C_FLAG_READ) == 0 && (RequestPacket->OperationCount == 1) ) { ReadBytes = 0; ReadBuffer = NULL; } // ReceiveByte if ((Operation[0].Flags & I2C_FLAG_READ) && (Operation[0].LengthInBytes == 1) && (RequestPacket->OperationCount == 1) ) { WriteBytes = 0; WriteBuffer = NULL; } } else { if (RequestPacket->OperationCount > 2) { return EFI_INVALID_PARAMETER; } Repeat = FALSE; if (RequestPacket->OperationCount == 2) { if (((Operation[0].Flags & I2C_FLAG_READ) == I2C_FLAG_READ) && (Operation[1].Flags & I2C_FLAG_READ) == 0) { return EFI_INVALID_PARAMETER; } Repeat = TRUE; } ReadBytes = 0; WriteBytes = 0; ReadBuffer = NULL; WriteBuffer = NULL; for (Index = 0;Index < RequestPacket->OperationCount; Index++) { if (Operation[Index].Flags & I2C_FLAG_READ) { ReadBytes = Operation[Index].LengthInBytes; ReadBuffer = Operation[Index].Buffer; } else { WriteBytes = Operation[Index].LengthInBytes; WriteBuffer = Operation[Index].Buffer; } } } IsSwitched = FALSE; if ( I2cPort->Info.GetDeviceMode(I2cPort) == I2C_DEVICE_MODE_ACPI) { I2cPort->Info.SwitcDevhMode (I2cPort, I2C_DEVICE_MODE_PCI); IsSwitched = TRUE; } LocateI2cBase( I2cPort, &Base0BackUp, &PciCmdBackUp); if (I2cPort->I2cBase == 0 || I2cPort->I2cHcRead8(I2cPort, R_IC_CON) == 0xFF) { FreeI2cBase(I2cPort, Base0BackUp, PciCmdBackUp); return EFI_DEVICE_ERROR; } I2cReset (I2cPort); // // Start an I2C operation on the host // Status = I2cStartRequest ( I2cPort, SlaveAddress, &WriteBytes, WriteBuffer, &ReadBytes, ReadBuffer, Repeat ); FreeI2cBase(I2cPort, Base0BackUp, PciCmdBackUp); if (IsSwitched) { I2cPort->Info.SwitcDevhMode (I2cPort, I2C_DEVICE_MODE_ACPI); } if ((Operation[0].Flags & I2C_FLAG_SMBUS_BLOCK) == I2C_FLAG_SMBUS_BLOCK) { Operation[0].LengthInBytes = (UINT32)WriteBytes; Operation[1].LengthInBytes = (UINT32)ReadBytes; } // // Return the status // if ( NULL != I2cStatus ) { DEBUG (( DEBUG_INFO, "0x%016Lx: Returning status for I2C request packet 0x%016Lx, Status: %r\r\n", (UINT64)(UINTN)I2cPort, (UINT64)(UINTN)RequestPacket, Status )); *I2cStatus = Status; } DEBUG (( DEBUG_INFO, "I2cPortMasterStartRequest exiting, Status: %r\r\n", Status )); return Status; } EFI_STATUS EFIAPI InitializeI2cMasterEntry ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) /*++ Routine Description: Initialize the I2C bus Protocol --*/ { UINT8 I2cHcType; UINTN MaxI2cControllerNum; EFI_STATUS Status; UINTN DevPatLen; UINTN Index; EFI_I2C_CONTROLLER_CAPABILITIES *I2cControllerCapabilities; I2C_BUS_INSTANCE *I2cBusInstance; EFI_I2C_PLATFORM_SPECIFIC_PROTOCOL *I2cPlatformSpecific; I2cBusInstance = NULL; Status = EFI_SUCCESS; MaxI2cControllerNum = 0; DevPatLen = 0; DEBUG ((DEBUG_INFO, "InitializeI2cMasterEntry Start. \n")); Status = gBS->LocateProtocol ( &gEfiI2cPlatformSpecificProtocolGuid, NULL, (VOID **) &I2cPlatformSpecific ); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_INFO, "ERROR !!! LocateProtocol I2cPlatformSpecificProtocol Status:%x \n" , Status)); return Status; } Status = I2cPlatformSpecific->GetMaxI2cControllerNumber((UINT8*)&MaxI2cControllerNum); if (MaxI2cControllerNum == 0) { return EFI_NOT_STARTED; } DevPatLen = MAX(sizeof(I2C_PCI_HC_DEV_PATH), sizeof (I2C_MEM_HC_DEV_PATH)); mI2cContext = AllocateZeroPool((sizeof(I2C_BUS_INSTANCE) + DevPatLen) * MaxI2cControllerNum); if (mI2cContext == NULL){ Status = EFI_OUT_OF_RESOURCES; ASSERT_EFI_ERROR (Status); return Status; } for(Index = 0; Index < MaxI2cControllerNum; Index++){ I2cBusInstance = (I2C_BUS_INSTANCE*)(((UINT8*)(UINTN)mI2cContext) + ((sizeof(I2C_BUS_INSTANCE) + DevPatLen) * Index)); I2cBusInstance->Signature = I2C_PRIVATE_DATA_SIGNATURE; I2cBusInstance->Handle = NULL; I2cBusInstance->IntervalTime = 2; I2cBusInstance->I2cBusSpeed = V_SPEED_FAST; I2cBusInstance->I2cHcRead8 = I2cHcRead8; I2cBusInstance->I2cHcRead16 = I2cHcRead16; I2cBusInstance->I2cHcRead32 = I2cHcRead32; I2cBusInstance->I2cHcWrite8 = I2cHcWrite8; I2cBusInstance->I2cHcWrite16 = I2cHcWrite16; I2cBusInstance->I2cHcWrite32 = I2cHcWrite32; I2cControllerCapabilities = AllocateZeroPool (sizeof(EFI_I2C_CONTROLLER_CAPABILITIES)); if (I2cControllerCapabilities == NULL) { Status = EFI_OUT_OF_RESOURCES; DEBUG ((EFI_D_INFO, "InitializeI2cMasterEntry Fail at Index:%d of MaxI2cControllerNum:%d Status:%x \n" , Index, MaxI2cControllerNum, Status)); ASSERT_EFI_ERROR (Status); return Status; } I2cControllerCapabilities->MaximumReceiveBytes = FIFO_SIZE_IN_BYTES; I2cControllerCapabilities->MaximumTransmitBytes = FIFO_SIZE_IN_BYTES; I2cControllerCapabilities->MaximumTotalBytes = FIFO_SIZE_IN_BYTES; // // Build the I2C Master protocol // I2cBusInstance->MasterApi.SetBusFrequency = I2cPortSetBusFrequency; I2cBusInstance->MasterApi.Reset = I2cPortReset; I2cBusInstance->MasterApi.StartRequest = I2cPortMasterStartRequest; I2cBusInstance->MasterApi.I2cControllerCapabilities = I2cControllerCapabilities; I2cBusInstance->DevicePath = (EFI_DEVICE_PATH_PROTOCOL*)(I2cBusInstance + 1); // // Build the I2C Configuration Protocol // I2cBusInstance->ConfigApi.EnableI2cBusConfiguration = I2cBusConfiguration; // // Initialize the cache buffer // I2cBusInstance->CacheSize = DEFAULT_CACHE_SIZE; I2cBusInstance->Cache = AllocateZeroPool(I2cBusInstance->CacheSize); Status = I2cPlatformSpecific->GetI2cConfigData(I2cPlatformSpecific, I2cBusInstance, Index, &I2cHcType); if (EFI_ERROR(Status)) { continue; } switch (I2cHcType) { case I2C_HC_TYPE_PCI: mI2cPciHcDevPath.Pci.Device = (UINT8)I2cBusInstance->PciI2c.Dev; mI2cPciHcDevPath.Pci.Function = (UINT8)I2cBusInstance->PciI2c.Func; CopyMem (I2cBusInstance->DevicePath, &mI2cPciHcDevPath, sizeof(I2C_PCI_HC_DEV_PATH)); break; case I2C_HC_TYPE_MMIO: mI2cMemHcDevPath.Mmio.StartingAddress = I2cBusInstance->I2cBase; mI2cMemHcDevPath.Mmio.EndingAddress = mI2cMemHcDevPath.Mmio.StartingAddress + I2cBusInstance->I2cBaseLen; mI2cMemHcDevPath.Mmio.MemoryType = EfiMemoryMappedIO; CopyMem (I2cBusInstance->DevicePath, &mI2cMemHcDevPath, sizeof(I2C_MEM_HC_DEV_PATH)); break; } // // Initialize the NotifyFunctionList // InitializeListHead (&I2cBusInstance->AsyncQueue); // // Install the I2C interface // Status = gBS->InstallMultipleProtocolInterfaces ( &I2cBusInstance->Handle, &gEfiI2cMasterProtocolGuid, &I2cBusInstance->MasterApi, &gEfiI2cBusConfigurationManagementProtocolGuid, &I2cBusInstance->ConfigApi, &gEfiDevicePathProtocolGuid, I2cBusInstance->DevicePath, NULL ); ASSERT_EFI_ERROR(Status); } DEBUG ((DEBUG_INFO, "InitializeI2cMasterEntry End. \n")); return EFI_SUCCESS; }