/******************************************************************************** Copyright (C) 2021 Rockchip Electronics Co., Ltd SPDX-License-Identifier: BSD-2-Clause-Patent *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "I2cDxe.h" #define RK_I2C_DUMPREG 1 STATIC CONST I2C_SPEC_VALUES StandardModeSpec = { .MinLowNs = 4700, .MinHighNs = 4000, .MaxRiseNs = 1000, .MaxFallNs = 300, }; STATIC CONST I2C_SPEC_VALUES FastModeSpec = { .MinLowNs = 1300, .MinHighNs = 600, .MaxRiseNs = 300, .MaxFallNs = 300, }; STATIC CONST I2C_SPEC_VALUES FastModeplusSpec = { .MinLowNs = 500, .MinHighNs = 260, .MaxRiseNs = 120, .MaxFallNs = 120, }; STATIC I2C_DEVICE_PATH I2cDevicePathProtocol = { { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { (UINT8)(OFFSET_OF (I2C_DEVICE_PATH, End)), (UINT8)(OFFSET_OF (I2C_DEVICE_PATH, End) >> 8), }, }, EFI_CALLER_ID_GUID }, 0, // Instance { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof(EFI_DEVICE_PATH_PROTOCOL), 0 } } }; STATIC CONST I2C_SPEC_VALUES * I2cGetSpec ( IN I2C_MASTER_CONTEXT *I2cMasterContext, IN UINT32 Speed ) { if (Speed == 1000) return &FastModeplusSpec; else if (Speed == 400) return &FastModeSpec; else return &StandardModeSpec; } STATIC UINT32 I2cRead ( IN I2C_MASTER_CONTEXT *I2cMasterContext, IN UINTN Off ) { ASSERT (I2cMasterContext != NULL); return MmioRead32 (I2cMasterContext->BaseAddress + Off); } STATIC EFI_STATUS I2cWrite ( IN I2C_MASTER_CONTEXT *I2cMasterContext, IN UINTN Off, IN UINT32 Value ) { ASSERT (I2cMasterContext != NULL); return MmioWrite32 (I2cMasterContext->BaseAddress + Off, Value); } STATIC VOID I2cShowRegs ( IN I2C_MASTER_CONTEXT *I2cMasterContex ) { #if RK_I2C_DUMPREG UINTN i; DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_con: 0x%08x\n", I2cRead(I2cMasterContex, I2C_CON))); DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_clkdiv: 0x%08x\n", I2cRead(I2cMasterContex, I2C_CLKDIV))); DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_mrxaddr: 0x%08x\n", I2cRead(I2cMasterContex, I2C_MRXADDR))); DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_mrxraddR: 0x%08x\n", I2cRead(I2cMasterContex, I2C_MRXRADDR))); DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_mtxcnt: 0x%08x\n", I2cRead(I2cMasterContex, I2C_MTXCNT))); DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_mrxcnt: 0x%08x\n", I2cRead(I2cMasterContex, I2C_MRXCNT))); DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_ien: 0x%08x\n", I2cRead(I2cMasterContex, I2C_IEN))); DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_ipd: 0x%08x\n", I2cRead(I2cMasterContex, I2C_IPD))); DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_fcnt: 0x%08x\n", I2cRead(I2cMasterContex, I2C_FCNT))); for (i = 0; i < 8; i++) DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_txdata%d: 0x%08x\n", i, I2cRead(I2cMasterContex, I2C_TXDATA_BASE + 4 * i))); for (i = 0; i < 8; i++) DEBUG((DEBUG_INFO | DEBUG_LOAD, "i2c_rxdata%d: 0x%08x\n", i, I2cRead(I2cMasterContex,I2C_RXDATA_BASE + 4 * i))); #endif } STATIC I2C_VERSION I2cGetVersion( IN I2C_MASTER_CONTEXT *I2cMasterContext ) { I2C_VERSION Version; Version = I2cRead(I2cMasterContext, I2C_CON) & I2C_CON_VERSION; return Version >>= I2C_CON_VERSION_SHIFT; } EFI_STATUS EFIAPI I2cInitialiseController ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable, IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT32 BusId ) { EFI_STATUS Status; I2C_MASTER_CONTEXT *I2cMasterContext; I2C_DEVICE_PATH *DevicePath; DEBUG ((EFI_D_VERBOSE, "I2cInitialiseController\n")); DevicePath = AllocateCopyPool (sizeof(I2cDevicePathProtocol), &I2cDevicePathProtocol); if (DevicePath == NULL) { DEBUG((DEBUG_ERROR, "I2cDxe: I2C device path allocation failed\n")); return EFI_OUT_OF_RESOURCES; } DevicePath->Instance = BusId; /* if attachment succeeds, this gets freed at ExitBootServices */ I2cMasterContext = AllocateZeroPool (sizeof (I2C_MASTER_CONTEXT)); if (I2cMasterContext == NULL) { DEBUG((DEBUG_ERROR, "I2cDxe: I2C master context allocation failed\n")); return EFI_OUT_OF_RESOURCES; } I2cMasterContext->Signature = I2C_MASTER_SIGNATURE; I2cMasterContext->I2cMaster.Reset = I2cReset; I2cMasterContext->I2cMaster.StartRequest = I2cStartRequest; I2cMasterContext->I2cEnumerate.Enumerate = I2cEnumerate; I2cMasterContext->I2cBusConf.EnableI2cBusConfiguration = I2cEnableConf; I2cMasterContext->TclkFrequency = PcdGet32 (PcdI2cClockFrequency); I2cMasterContext->BaseAddress = BaseAddress; I2cMasterContext->Bus = BusId; EfiInitializeLock(&I2cMasterContext->Lock, TPL_NOTIFY); if (I2cGetVersion(I2cMasterContext) >= RkI2cVersion1) { I2cAdapterBaudRate(I2cMasterContext, PcdGet32 (PcdI2cBaudRate), I2cMasterContext->TclkFrequency ); } else { I2cCalBaudRate(I2cMasterContext, PcdGet32 (PcdI2cBaudRate), I2cMasterContext->TclkFrequency ); } Status = gBS->InstallMultipleProtocolInterfaces( &I2cMasterContext->Controller, &gEfiI2cMasterProtocolGuid, &I2cMasterContext->I2cMaster, &gEfiI2cEnumerateProtocolGuid, &I2cMasterContext->I2cEnumerate, &gEfiI2cBusConfigurationManagementProtocolGuid, &I2cMasterContext->I2cBusConf, &gEfiDevicePathProtocolGuid, (EFI_DEVICE_PATH_PROTOCOL *) DevicePath, NULL); if (EFI_ERROR(Status)) { DEBUG((DEBUG_ERROR, "I2cDxe: Installing protocol interfaces failed!\n")); goto fail; } DEBUG ((DEBUG_INFO, "Succesfully installed controller %d at 0x%llx\n", BusId, I2cMasterContext->BaseAddress)); return EFI_SUCCESS; fail: FreePool(I2cMasterContext); return Status; } STATIC VOID EFIAPI OnEndOfDxe ( IN EFI_EVENT Event, IN VOID *Context ) { I2C_DEVICE_PATH *DevicePath; EFI_DEVICE_PATH_PROTOCOL *DevicePathPointer; EFI_HANDLE DeviceHandle; EFI_STATUS Status; gBS->CloseEvent (Event); DevicePath = AllocateCopyPool (sizeof (I2cDevicePathProtocol), &I2cDevicePathProtocol); if (DevicePath == NULL) { DEBUG ((DEBUG_ERROR, "I2cDxe: I2C device path allocation failed\n")); return; } do { DevicePathPointer = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath; Status = gBS->LocateDevicePath (&gEfiI2cMasterProtocolGuid, &DevicePathPointer, &DeviceHandle); if (EFI_ERROR (Status)) { break; } Status = gBS->ConnectController (DeviceHandle, NULL, NULL, TRUE); DEBUG ((DEBUG_INFO, "%a: ConnectController () returned %r\n", __FUNCTION__, Status)); DevicePath->Instance++; } while (TRUE); gBS->FreePool (DevicePath); } EFI_STATUS EFIAPI I2cInitialise ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_PHYSICAL_ADDRESS BaseAddress; EFI_EVENT EndOfDxeEvent; UINT8 *DeviceBusPcd; EFI_STATUS Status; UINTN Index; DEBUG ((EFI_D_VERBOSE, "I2cInitialise count: %d\n", PcdGet32 (PcdI2cBusCount))); DeviceBusPcd = PcdGetPtr (PcdI2cSlaveBuses); /* Initialize enabled chips */ for (Index = 0; Index < PcdGet32 (PcdI2cBusCount); Index++) { BaseAddress = I2cGetBase(DeviceBusPcd[Index]); if (!BaseAddress) continue; Status = I2cInitialiseController( ImageHandle, SystemTable, BaseAddress, DeviceBusPcd[Index] ); if (EFI_ERROR(Status)) return Status; } Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, OnEndOfDxe, NULL, &gEfiEndOfDxeEventGroupGuid, &EndOfDxeEvent); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; } STATIC VOID I2cCalBaudRate ( IN I2C_MASTER_CONTEXT *I2cMasterContext, IN UINT32 Target, IN UINT32 ClkRate ) { int Div, Divl, Divh; Div = DIV_ROUND_UP(ClkRate, Target * 8) - 2; Divh = 0; Divl = 0; if (Div >= 0) { Divl = Div / 2; if (Div % 2 == 0) Divh = Div / 2; else Divh = DIV_ROUND_UP(Div, 2); } I2cWrite(I2cMasterContext, I2C_CLKDIV, I2C_CLKDIV_VAL(Divl, Divh)); } STATIC EFI_STATUS I2cAdapterBaudRate ( IN I2C_MASTER_CONTEXT *I2cMasterContext, IN UINT32 Target, IN UINT32 ClkRate ) { CONST I2C_SPEC_VALUES *Spec; UINT32 MinTotalDiv, MinLowDiv, MinHighDiv, MinHoldDiv; UINT32 LowDiv, HighDiv, ExtraDiv, ExtraLowDiv; UINT32 MinLowNs, MinHighNs; UINT32 StartSetup = 0; UINT32 Speed; if (Target <= 100000 && Target >= 1000) { StartSetup = 1; Speed = 100; } else if (Target <= 400000 && Target >= 100000) { Speed = 400; } else if (Target <= 1000000 && Target > 400000) { Speed = 1000; } else { DEBUG ((EFI_D_ERROR, "invalid i2c speed : %d\n", Target)); return EFI_INVALID_PARAMETER; } Spec = I2cGetSpec(I2cMasterContext, Speed); ClkRate = DIV_ROUND_UP(ClkRate, 1000); Speed = DIV_ROUND_UP(Target, 1000); MinTotalDiv = DIV_ROUND_UP(ClkRate, Speed * 8); MinHighNs = Spec->MaxRiseNs + Spec->MinHighNs; MinHighDiv = DIV_ROUND_UP(ClkRate * MinHighNs, 8 * 1000000); MinLowNs = Spec->MaxFallNs + Spec->MinLowNs; MinLowDiv = DIV_ROUND_UP(ClkRate * MinLowNs, 8 * 1000000); MinHighDiv = (MinHighDiv < 1) ? 2 : MinHighDiv; MinLowDiv = (MinLowDiv < 1) ? 2 : MinLowDiv; MinHoldDiv = MinHighDiv + MinLowDiv; if (MinHoldDiv >= MinTotalDiv) { HighDiv = MinHighDiv; LowDiv = MinLowDiv; } else { ExtraDiv = MinTotalDiv - MinHoldDiv; ExtraLowDiv = DIV_ROUND_UP(MinLowDiv * ExtraDiv, MinHoldDiv); LowDiv = MinLowDiv + ExtraLowDiv; HighDiv = MinHighDiv + (ExtraDiv - ExtraLowDiv); } HighDiv--; LowDiv--; if (HighDiv > 0xffff || LowDiv > 0xffff) return EFI_UNSUPPORTED; /* 1 for data hold/setup time is enough */ I2cMasterContext->Config = I2C_CON_SDA_CFG(1) | I2C_CON_STA_CFG(StartSetup); I2cWrite(I2cMasterContext, I2C_CLKDIV, (HighDiv << I2C_CLK_DIV_HIGH_SHIFT) | LowDiv); return EFI_SUCCESS; } EFI_STATUS EFIAPI I2cReset ( IN CONST EFI_I2C_MASTER_PROTOCOL *This ) { DEBUG((EFI_D_VERBOSE, "I2cReset\n")); return EFI_SUCCESS; } STATIC VOID I2cDisable ( IN I2C_MASTER_CONTEXT *I2cMasterContext ) { DEBUG ((EFI_D_VERBOSE, "I2c I2cDisable.\n")); EfiAcquireLock (&I2cMasterContext->Lock); I2cWrite(I2cMasterContext, I2C_IEN, 0); I2cWrite(I2cMasterContext, I2C_IPD, I2C_IPD_ALL_CLEAN); I2cWrite(I2cMasterContext, I2C_CON, 0); EfiReleaseLock (&I2cMasterContext->Lock); } /* * enable and start at same time, Timeout is given in us. */ STATIC EFI_STATUS I2cStartEnable ( IN I2C_MASTER_CONTEXT *I2cMasterContext, IN UINT32 Con, IN UINTN Timeout ) { DEBUG ((EFI_D_VERBOSE, "I2cStartEnable\n")); I2cWrite(I2cMasterContext, I2C_IPD, I2C_IPD_ALL_CLEAN); I2cWrite(I2cMasterContext, I2C_IEN, I2C_STARTIEN); I2cWrite(I2cMasterContext, I2C_CON, I2C_CON_EN | I2C_CON_START | Con |I2cMasterContext->Config); while (Timeout--) { if (I2cRead(I2cMasterContext, I2C_IPD) & I2C_STARTIPD) { I2cWrite(I2cMasterContext, I2C_IPD, I2C_STARTIPD); break; } MicroSecondDelay(1); } if (Timeout <= 0) { DEBUG ((EFI_D_ERROR, "I2C Send Start Bit Timeout\n")); I2cShowRegs(I2cMasterContext); EfiReleaseLock (&I2cMasterContext->Lock); return EFI_TIMEOUT; } return EFI_SUCCESS; } STATIC EFI_STATUS I2cStop ( IN I2C_MASTER_CONTEXT *I2cMasterContext ) { int TimeOut = I2C_READY_TIMEOUT; DEBUG ((EFI_D_VERBOSE, "I2c Send Stop bit.\n")); EfiAcquireLock (&I2cMasterContext->Lock); I2cWrite(I2cMasterContext, I2C_IPD, I2C_IPD_ALL_CLEAN); I2cWrite(I2cMasterContext, I2C_CON, I2C_CON_EN | I2C_CON_STOP |I2cMasterContext->Config); I2cWrite(I2cMasterContext, I2C_IEN, I2C_CON_STOP); while (TimeOut--) { if (I2cRead(I2cMasterContext, I2C_IPD) & I2C_STOPIPD) { I2cWrite(I2cMasterContext, I2C_IPD, I2C_STOPIPD); break; } MicroSecondDelay(1); } if (TimeOut <= 0) { DEBUG ((EFI_D_ERROR, "I2C Send Stop Bit Timeout\n")); I2cShowRegs(I2cMasterContext); EfiReleaseLock (&I2cMasterContext->Lock); return EFI_TIMEOUT; } EfiReleaseLock (&I2cMasterContext->Lock); return EFI_SUCCESS; } STATIC EFI_STATUS I2cReadOperation ( IN I2C_MASTER_CONTEXT *I2cMasterContext, IN UINTN SlaveAddress, IN OUT UINT8 *Buf, IN UINTN Length, IN OUT UINTN *Read, IN UINTN Snd, IN UINTN Timeout ) { EFI_STATUS Status = EFI_SUCCESS; int TimeOut = I2C_TIMEOUT_US; UINT8 *PBuf = Buf; UINT32 BytesRemainLen = Length; UINT32 BytesTranferedLen = 0; UINT32 WordsTranferedLen = 0; UINT32 Con = 0; UINT32 RxData; UINT32 i, j; UINTN SndChunk = 0; DEBUG ((EFI_D_VERBOSE, "I2cRead: base_addr = 0x%x buf = %p, Length = %d\n", I2cMasterContext->BaseAddress, Buf, Length)); EfiAcquireLock (&I2cMasterContext->Lock); /* If the second message for TRX read, resetting internal state. */ if (Snd) { I2cWrite(I2cMasterContext, I2C_CON, 0); } I2cWrite(I2cMasterContext, I2C_MRXADDR, I2C_MRXADDR_SET(1, SlaveAddress << 1 | 1)); (*Read) = 0; while (BytesRemainLen) { if (BytesRemainLen > RK_I2C_FIFO_SIZE) { Con = I2C_CON_EN; BytesTranferedLen = 32; } else { /* * The hw can read up to 32 bytes at a time. If we need * more than one chunk, send an ACK after the last byte. */ Con = I2C_CON_EN | I2C_CON_LASTACK; BytesTranferedLen = BytesRemainLen; } WordsTranferedLen = DIV_ROUND_UP(BytesTranferedLen, 4); /* * make sure we are in plain RX mode if we read a second chunk; * and first rx read need to send start bit. */ if (SndChunk) { Con |= I2C_CON_MOD(I2C_MODE_RX); I2cWrite(I2cMasterContext, I2C_CON, Con | I2cMasterContext->Config); } else { Con |= I2C_CON_MOD(I2C_MODE_TRX); Status = I2cStartEnable(I2cMasterContext, Con, I2C_TIMEOUT_US); if (EFI_ERROR(Status)) goto out; } I2cWrite(I2cMasterContext, I2C_IEN, I2C_MBRFIEN | I2C_NAKRCVIEN); I2cWrite(I2cMasterContext, I2C_MRXCNT, BytesTranferedLen); while (TimeOut--) { if (I2cRead(I2cMasterContext, I2C_IPD) & I2C_NAKRCVIPD) { I2cWrite(I2cMasterContext, I2C_IPD, I2C_NAKRCVIPD); Status = EFI_NO_RESPONSE; goto out; } if (I2cRead(I2cMasterContext, I2C_IPD) & I2C_MBRFIPD) { I2cWrite(I2cMasterContext, I2C_IPD, I2C_MBRFIPD); break; } MicroSecondDelay(1); } if (TimeOut <= 0) { DEBUG ((EFI_D_ERROR, "I2C Read Data Timeout\n")); I2cShowRegs(I2cMasterContext); Status = EFI_TIMEOUT; goto out; } for (i = 0; i < WordsTranferedLen; i++) { RxData = I2cRead(I2cMasterContext, I2C_RXDATA_BASE + i * 4); DEBUG ((EFI_D_VERBOSE, "I2c Read RXDATA[%d] = 0x%x\n", i, RxData)); for (j = 0; j < 4; j++) { if ((i * 4 + j) == BytesTranferedLen) { break; } *PBuf++ = (RxData >> (j * 8)) & 0xff; } } BytesRemainLen -= BytesTranferedLen; SndChunk = 1; (*Read) = BytesTranferedLen; } Status = EFI_SUCCESS; (*Read) = Length; out: EfiReleaseLock (&I2cMasterContext->Lock); return (Status); } STATIC EFI_STATUS I2cWriteOperation ( IN I2C_MASTER_CONTEXT *I2cMasterContext, IN UINTN SlaveAddress, IN OUT CONST UINT8 *Buf, IN UINTN Length, IN OUT UINTN *Sent, IN UINTN Timeout ) { EFI_STATUS Status = EFI_SUCCESS; int TimeOut = I2C_TIMEOUT_US; CONST UINT8 *PBuf = Buf; UINT32 BytesRemainLen = Length + 1; UINT32 BytesTranferedLen = 0; UINT32 WordsTranferedLen = 0; UINT32 TxData; UINT32 i, j; UINTN Next = 0; DEBUG ((EFI_D_VERBOSE, "I2cWrite: base_addr = 0x%x buf = %p, Length = %d\n", I2cMasterContext->BaseAddress, Buf, Length)); EfiAcquireLock (&I2cMasterContext->Lock); (*Sent) = 0; while (BytesRemainLen) { if (BytesRemainLen > RK_I2C_FIFO_SIZE) { BytesTranferedLen = 32; } else { BytesTranferedLen = BytesRemainLen; } WordsTranferedLen = DIV_ROUND_UP(BytesTranferedLen, 4); for (i = 0; i < WordsTranferedLen; i++) { TxData = 0; for (j = 0; j < 4; j++) { if ((i * 4 + j) == BytesTranferedLen) { break; } if (i == 0 && j == 0 && PBuf == Buf) { TxData |= (SlaveAddress << 1); } else { TxData |= (*PBuf++)<<(j * 8); } } I2cWrite(I2cMasterContext, I2C_TXDATA_BASE + i * 4, TxData); DEBUG ((EFI_D_VERBOSE, "I2c Write TXDATA[%d] = 0x%x\n", i, TxData)); } /* If the write is the first, need to send start bit */ if (!Next) { Status = I2cStartEnable(I2cMasterContext, I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX), I2C_TIMEOUT_US); if (EFI_ERROR(Status)) { goto out; } Next = 1; } else { I2cWrite(I2cMasterContext, I2C_CON, I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX) | I2cMasterContext->Config); } I2cWrite(I2cMasterContext, I2C_IEN, I2C_MBTFIEN | I2C_NAKRCVIEN); I2cWrite(I2cMasterContext, I2C_MTXCNT, BytesTranferedLen); while (TimeOut--) { if (I2cRead(I2cMasterContext, I2C_IPD) & I2C_NAKRCVIPD) { I2cWrite(I2cMasterContext, I2C_IPD, I2C_NAKRCVIPD); Status = EFI_NO_RESPONSE; goto out; } if (I2cRead(I2cMasterContext, I2C_IPD) & I2C_MBTFIPD) { I2cWrite(I2cMasterContext, I2C_IPD, I2C_MBTFIPD); break; } MicroSecondDelay(1); } if (TimeOut <= 0) { DEBUG ((EFI_D_ERROR, "I2C Write Data Timeout\n")); I2cShowRegs(I2cMasterContext); Status = EFI_TIMEOUT; goto out; } BytesRemainLen -= BytesTranferedLen; (*Sent) = BytesTranferedLen; } (*Sent) = Length; Status = EFI_SUCCESS; out: EfiReleaseLock (&I2cMasterContext->Lock); return (Status); } /* * I2cStartRequest should be called only by I2cHost. * I2C device drivers ought to use EFI_I2C_IO_PROTOCOL instead. */ STATIC EFI_STATUS I2cStartRequest ( 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 Count = RequestPacket->OperationCount; UINTN ReadMode; UINTN Transmitted; I2C_MASTER_CONTEXT *I2cMasterContext = I2C_SC_FROM_MASTER(This); EFI_I2C_OPERATION *Operation; EFI_STATUS Status = EFI_SUCCESS; UINTN i; ASSERT (RequestPacket != NULL); ASSERT (I2cMasterContext != NULL); DEBUG ((EFI_D_VERBOSE, "I2cStartRequest.\n")); if (Count > 2 || ((Count == 2) && (RequestPacket->Operation->Flags & I2C_FLAG_READ))) { DEBUG ((EFI_D_VERBOSE, "Not support more messages now, split them\n")); return EFI_INVALID_PARAMETER; } for (i = 0; i < Count; i++) { Operation = &RequestPacket->Operation[i]; ReadMode = Operation->Flags & I2C_FLAG_READ; if (Operation->Flags & I2C_FLAG_READ) { /* If snd is true, it is TRX mode. */ Status = I2cReadOperation ( I2cMasterContext, SlaveAddress, Operation->Buffer, Operation->LengthInBytes, &Transmitted, i == 1, I2C_TRANSFER_TIMEOUT); Operation->LengthInBytes = Transmitted; } else { Status = I2cWriteOperation ( I2cMasterContext, SlaveAddress, Operation->Buffer, Operation->LengthInBytes, &Transmitted, I2C_TRANSFER_TIMEOUT); Operation->LengthInBytes = Transmitted; } /* I2C transaction was aborted, so stop further transactions */ if (EFI_ERROR (Status)) { DEBUG ((EFI_D_VERBOSE, "I2cStartRequest: Failed at Count = %d\n", Count)); break; } } I2cStop (I2cMasterContext); I2cDisable (I2cMasterContext); if (I2cStatus != NULL) *I2cStatus = EFI_SUCCESS; if (Event != NULL) gBS->SignalEvent(Event); return EFI_SUCCESS; } STATIC CONST EFI_GUID DevGuid = I2C_GUID; STATIC EFI_STATUS I2cAllocDevice ( IN UINT8 SlaveAddress, IN UINT8 Bus, IN OUT CONST EFI_I2C_DEVICE **Device ) { EFI_STATUS Status; EFI_I2C_DEVICE *Dev; UINT32 *TmpSlaveArray; EFI_GUID *TmpGuidP; Status = gBS->AllocatePool ( EfiBootServicesData, sizeof(EFI_I2C_DEVICE), (VOID **) &Dev ); if (EFI_ERROR(Status)) { DEBUG((DEBUG_ERROR, "I2cDxe: I2C device memory allocation failed\n")); return Status; } *Device = Dev; Dev->DeviceIndex = SlaveAddress; Dev->DeviceIndex = I2C_DEVICE_INDEX(Bus, SlaveAddress); Dev->SlaveAddressCount = 1; Dev->I2cBusConfiguration = 0; Status = gBS->AllocatePool ( EfiBootServicesData, sizeof(UINT32), (VOID **) &TmpSlaveArray); if (EFI_ERROR(Status)) { goto fail1; } TmpSlaveArray[0] = SlaveAddress; Dev->SlaveAddressArray = TmpSlaveArray; Status = gBS->AllocatePool ( EfiBootServicesData, sizeof(EFI_GUID), (VOID **) &TmpGuidP); if (EFI_ERROR(Status)) { goto fail2; } *TmpGuidP = DevGuid; Dev->DeviceGuid = TmpGuidP; return EFI_SUCCESS; fail2: FreePool(TmpSlaveArray); fail1: FreePool(Dev); return Status; } /* * It is called by I2cBus to enumerate devices on I2C bus. In this case, * enumeration is based on PCD configuration - all Slave addresses specified * in PCD get their corresponding EFI_I2C_DEVICE structures here. * * After enumeration succeeds, Supported() function of drivers that installed * DriverBinding protocol is called. */ STATIC EFI_STATUS EFIAPI I2cEnumerate ( IN CONST EFI_I2C_ENUMERATE_PROTOCOL *This, IN OUT CONST EFI_I2C_DEVICE **Device ) { UINT8 *DevicesPcd; UINT8 *DeviceBusPcd; UINTN Index, NextIndex, DevCount; UINT8 NextDeviceAddress; I2C_MASTER_CONTEXT *I2cMasterContext = I2C_SC_FROM_ENUMERATE(This); DevCount = PcdGetSize (PcdI2cSlaveAddresses); DevicesPcd = PcdGetPtr (PcdI2cSlaveAddresses); DeviceBusPcd = PcdGetPtr (PcdI2cSlaveBuses); if (*Device == NULL) { for (Index = 0; Index < DevCount ; Index++) { if (DeviceBusPcd[Index] != I2cMasterContext->Bus) continue; if (Index < DevCount) I2cAllocDevice (DevicesPcd[Index], I2cMasterContext->Bus, Device); return EFI_SUCCESS; } } else { /* Device is not NULL, so something was already allocated */ for (Index = 0; Index < DevCount; Index++) { if (DeviceBusPcd[Index] != I2cMasterContext->Bus) continue; if (DevicesPcd[Index] == I2C_DEVICE_ADDRESS((*Device)->DeviceIndex)) { for (NextIndex = Index + 1; NextIndex < DevCount; NextIndex++) { if (DeviceBusPcd[NextIndex] != I2cMasterContext->Bus) continue; NextDeviceAddress = DevicesPcd[NextIndex]; if (NextIndex < DevCount) I2cAllocDevice(NextDeviceAddress, I2cMasterContext->Bus, Device); return EFI_SUCCESS; } } } *Device = NULL; return EFI_SUCCESS; } return EFI_SUCCESS; } STATIC EFI_STATUS EFIAPI I2cEnableConf ( IN CONST EFI_I2C_BUS_CONFIGURATION_MANAGEMENT_PROTOCOL *This, IN UINTN I2cBusConfiguration, IN EFI_EVENT Event OPTIONAL, IN EFI_STATUS *I2cStatus OPTIONAL ) { /* do nothing */ if (I2cStatus != NULL) I2cStatus = EFI_SUCCESS; if (Event != NULL) gBS->SignalEvent(Event); return EFI_SUCCESS; }