2 Implementation for SMBus DXE driver entry point and SMBus Host
5 Copyright (c) 2013-2015 Intel Corporation.
7 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "CommonHeader.h"
12 #include "DxeQNCSmbus.h"
15 // Interface defintion of SMBUS Host Controller Protocol.
17 EFI_SMBUS_HC_PROTOCOL mSmbusHc
= {
25 // Handle to install SMBus Host Controller protocol.
27 EFI_HANDLE mSmbusHcHandle
= NULL
;
28 UINT8 mDeviceMapEntries
= 0;
29 EFI_SMBUS_DEVICE_MAP mDeviceMap
[MAX_SMBUS_DEVICES
];
30 UINT8 mPlatformNumRsvd
= 0;
31 UINT8
*mPlatformAddrRsvd
= NULL
;
34 // These addresses are reserved by the SMBus 2.0 specification
36 UINT8 mReservedAddress
[SMBUS_NUM_RESERVED
] = {
37 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x18, 0x50, 0x6E, 0xC2,
38 0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE
43 Gets Io port base address of Smbus Host Controller.
45 This internal function depends on a feature flag named PcdIchSmbusFixedIoPortBaseAddress
46 to retrieve Smbus Io port base. If that feature flag is true, it will get Smbus Io port base
47 address from a preset Pcd entry named PcdIchSmbusIoPortBaseAddress; otherwise, it will always
48 read Pci configuration space to get that value in each Smbus bus transaction.
50 @return The Io port base address of Smbus host controller.
54 GetSmbusIoPortBaseAddress (
58 UINTN IoPortBaseAddress
;
60 if (FeaturePcdGet (PcdSmbaIoBaseAddressFixed
)) {
61 IoPortBaseAddress
= (UINTN
) PcdGet16 (PcdSmbaIoBaseAddress
);
63 IoPortBaseAddress
= (UINTN
) LpcPciCfg32 (R_QNC_LPC_SMBUS_BASE
) & B_QNC_LPC_SMBUS_BASE_MASK
;
67 // Make sure that the IO port base address has been properly set.
69 ASSERT (IoPortBaseAddress
!= 0);
71 return IoPortBaseAddress
;
79 UINTN IoPortBaseAddress
;
81 IoPortBaseAddress
= GetSmbusIoPortBaseAddress ();
84 // Step1: Enable QNC SMBUS I/O space.
86 LpcPciCfg32Or(R_QNC_LPC_SMBUS_BASE
, B_QNC_LPC_SMBUS_BASE_EN
);
89 // Step2: Clear Status Register before anyone uses the interfaces.
91 IoWrite8 (IoPortBaseAddress
+ R_QNC_SMBUS_HSTS
, B_QNC_SMBUS_HSTS_ALL
);
94 // Step3: Program the correct smbus clock
96 IoWrite8 (IoPortBaseAddress
+ R_QNC_SMBUS_HCLK
, V_QNC_SMBUS_HCLK_100KHZ
);
104 IN EFI_SMBUS_DEVICE_ADDRESS SlaveAddress
110 // See if we have already assigned this address to a device
112 for (Index
= 0; Index
< mDeviceMapEntries
; Index
++) {
113 if (SlaveAddress
.SmbusDeviceAddress
==
114 mDeviceMap
[Index
].SmbusDeviceAddress
.SmbusDeviceAddress
) {
120 // See if this address is claimed by a platform non-ARP-capable device
122 for (Index
= 0; Index
< mPlatformNumRsvd
; Index
++) {
123 if ((SlaveAddress
.SmbusDeviceAddress
<< 1) == mPlatformAddrRsvd
[Index
]) {
129 // See if this is a reserved address
131 for (Index
= 0; Index
< SMBUS_NUM_RESERVED
; Index
++) {
132 if (SlaveAddress
.SmbusDeviceAddress
== (UINTN
) mReservedAddress
[Index
]) {
142 GetNextAvailableAddress (
143 IN EFI_SMBUS_DEVICE_ADDRESS
*SlaveAddress
146 for (SlaveAddress
->SmbusDeviceAddress
= 0x03;
147 SlaveAddress
->SmbusDeviceAddress
< 0x7F;
148 SlaveAddress
->SmbusDeviceAddress
++
150 if (IsAddressAvailable (*SlaveAddress
)) {
155 return EFI_OUT_OF_RESOURCES
;
162 EFI_SMBUS_DEVICE_ADDRESS SlaveAddress
;
167 SlaveAddress
.SmbusDeviceAddress
= SMBUS_ADDRESS_ARP
;
169 Buffer
= SMBUS_DATA_PREPARE_TO_ARP
;
183 SmbusGetUdidGeneral (
184 IN OUT EFI_SMBUS_DEVICE_MAP
*DeviceMap
187 EFI_SMBUS_DEVICE_ADDRESS SlaveAddress
;
190 UINT8 Buffer
[SMBUS_GET_UDID_LENGTH
];
192 SlaveAddress
.SmbusDeviceAddress
= SMBUS_ADDRESS_ARP
;
193 Length
= SMBUS_GET_UDID_LENGTH
;
197 SMBUS_DATA_GET_UDID_GENERAL
,
204 if (!EFI_ERROR(Status
)) {
205 if (Length
== SMBUS_GET_UDID_LENGTH
) {
206 DeviceMap
->SmbusDeviceUdid
.DeviceCapabilities
= Buffer
[0];
207 DeviceMap
->SmbusDeviceUdid
.VendorRevision
= Buffer
[1];
208 DeviceMap
->SmbusDeviceUdid
.VendorId
= (UINT16
)((Buffer
[2] << 8) + Buffer
[3]);
209 DeviceMap
->SmbusDeviceUdid
.DeviceId
= (UINT16
)((Buffer
[4] << 8) + Buffer
[5]);
210 DeviceMap
->SmbusDeviceUdid
.Interface
= (UINT16
)((Buffer
[6] << 8) + Buffer
[7]);
211 DeviceMap
->SmbusDeviceUdid
.SubsystemVendorId
= (UINT16
)((Buffer
[8] << 8) + Buffer
[9]);
212 DeviceMap
->SmbusDeviceUdid
.SubsystemDeviceId
= (UINT16
)((Buffer
[10] << 8) + Buffer
[11]);
213 DeviceMap
->SmbusDeviceUdid
.VendorSpecificId
= (UINT32
)((Buffer
[12] << 24) + (Buffer
[13] << 16) + (Buffer
[14] << 8) + Buffer
[15]);
214 DeviceMap
->SmbusDeviceAddress
.SmbusDeviceAddress
= (UINT8
)(Buffer
[16] >> 1);
216 Status
= EFI_DEVICE_ERROR
;
225 IN OUT EFI_SMBUS_DEVICE_MAP
*DeviceMap
228 EFI_SMBUS_DEVICE_ADDRESS SlaveAddress
;
231 UINT8 Buffer
[SMBUS_GET_UDID_LENGTH
];
233 Buffer
[0] = DeviceMap
->SmbusDeviceUdid
.DeviceCapabilities
;
234 Buffer
[1] = DeviceMap
->SmbusDeviceUdid
.VendorRevision
;
235 Buffer
[2] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.VendorId
>> 8);
236 Buffer
[3] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.VendorId
);
237 Buffer
[4] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.DeviceId
>> 8);
238 Buffer
[5] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.DeviceId
);
239 Buffer
[6] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.Interface
>> 8);
240 Buffer
[7] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.Interface
);
241 Buffer
[8] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.SubsystemVendorId
>> 8);
242 Buffer
[9] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.SubsystemVendorId
);
243 Buffer
[10] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.SubsystemDeviceId
>> 8);
244 Buffer
[11] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.SubsystemDeviceId
);
245 Buffer
[12] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.VendorSpecificId
>> 24);
246 Buffer
[13] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.VendorSpecificId
>> 16);
247 Buffer
[14] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.VendorSpecificId
>> 8);
248 Buffer
[15] = (UINT8
)(DeviceMap
->SmbusDeviceUdid
.VendorSpecificId
);
249 Buffer
[16] = (UINT8
)(DeviceMap
->SmbusDeviceAddress
.SmbusDeviceAddress
<< 1);
251 SlaveAddress
.SmbusDeviceAddress
= SMBUS_ADDRESS_ARP
;
252 Length
= SMBUS_GET_UDID_LENGTH
;
256 SMBUS_DATA_ASSIGN_ADDRESS
,
271 EFI_SMBUS_DEVICE_MAP
*CurrentDeviceMap
;
273 Status
= SmbusPrepareToArp ();
274 if (EFI_ERROR(Status
)) {
275 if (Status
== EFI_DEVICE_ERROR
) {
286 // Main loop to ARP all ARP-capable devices
289 CurrentDeviceMap
= &mDeviceMap
[mDeviceMapEntries
];
290 Status
= SmbusGetUdidGeneral (CurrentDeviceMap
);
291 if (EFI_ERROR(Status
)) {
295 if (CurrentDeviceMap
->SmbusDeviceAddress
.SmbusDeviceAddress
== (0xFF >> 1)) {
297 // If address is unassigned, assign it
299 Status
= GetNextAvailableAddress (
300 &CurrentDeviceMap
->SmbusDeviceAddress
302 if (EFI_ERROR(Status
)) {
303 return EFI_OUT_OF_RESOURCES
;
305 } else if (((CurrentDeviceMap
->SmbusDeviceUdid
.DeviceCapabilities
) & 0xC0) != 0) {
307 // if address is not fixed, check if the current address is available
309 if (!IsAddressAvailable (
310 CurrentDeviceMap
->SmbusDeviceAddress
313 // if currently assigned address is already used, get a new one
315 Status
= GetNextAvailableAddress (
316 &CurrentDeviceMap
->SmbusDeviceAddress
318 if (EFI_ERROR(Status
)) {
319 return EFI_OUT_OF_RESOURCES
;
324 Status
= SmbusAssignAddress (CurrentDeviceMap
);
325 if (EFI_ERROR(Status
)) {
327 // If there was a device error, just continue on and try again.
328 // Other errors should be reported.
330 if (Status
!= EFI_DEVICE_ERROR
) {
335 // If there was no error, the address was assigned and we must update our
341 } while (mDeviceMapEntries
< MAX_SMBUS_DEVICES
);
349 IN EFI_SMBUS_UDID
*SmbusUdid
,
350 IN OUT EFI_SMBUS_DEVICE_ADDRESS
*SlaveAddress
354 EFI_SMBUS_DEVICE_MAP
*CurrentDeviceMap
;
356 if (mDeviceMapEntries
>= MAX_SMBUS_DEVICES
) {
357 return EFI_OUT_OF_RESOURCES
;
360 CurrentDeviceMap
= &mDeviceMap
[mDeviceMapEntries
];
363 // Find an available address to assign
365 Status
= GetNextAvailableAddress (
366 &CurrentDeviceMap
->SmbusDeviceAddress
368 if (EFI_ERROR(Status
)) {
369 return EFI_OUT_OF_RESOURCES
;
372 CurrentDeviceMap
->SmbusDeviceUdid
.DeviceCapabilities
= SmbusUdid
->DeviceCapabilities
;
373 CurrentDeviceMap
->SmbusDeviceUdid
.DeviceId
= SmbusUdid
->DeviceId
;
374 CurrentDeviceMap
->SmbusDeviceUdid
.Interface
= SmbusUdid
->Interface
;
375 CurrentDeviceMap
->SmbusDeviceUdid
.SubsystemDeviceId
= SmbusUdid
->SubsystemDeviceId
;
376 CurrentDeviceMap
->SmbusDeviceUdid
.SubsystemVendorId
= SmbusUdid
->SubsystemVendorId
;
377 CurrentDeviceMap
->SmbusDeviceUdid
.VendorId
= SmbusUdid
->VendorId
;
378 CurrentDeviceMap
->SmbusDeviceUdid
.VendorRevision
= SmbusUdid
->VendorRevision
;
379 CurrentDeviceMap
->SmbusDeviceUdid
.VendorSpecificId
= SmbusUdid
->VendorSpecificId
;
381 Status
= SmbusAssignAddress (CurrentDeviceMap
);
382 if (EFI_ERROR(Status
)) {
387 SlaveAddress
->SmbusDeviceAddress
= CurrentDeviceMap
->SmbusDeviceAddress
.SmbusDeviceAddress
;
395 Executes an SMBus operation to an SMBus controller. Returns when either the command has been
396 executed or an error is encountered in doing the operation.
398 The Execute() function provides a standard way to execute an operation as defined in the System
399 Management Bus (SMBus) Specification. The resulting transaction will be either that the SMBus
400 slave devices accept this transaction or that this function returns with error.
402 @param This A pointer to the EFI_SMBUS_HC_PROTOCOL instance.
403 @param SlaveAddress The SMBus slave address of the device with which to communicate.
404 @param Command This command is transmitted by the SMBus host controller to the
405 SMBus slave device and the interpretation is SMBus slave device
406 specific. It can mean the offset to a list of functions inside an
407 SMBus slave device. Not all operations or slave devices support
408 this command's registers.
409 @param Operation Signifies which particular SMBus hardware protocol instance that
410 it will use to execute the SMBus transactions. This SMBus
411 hardware protocol is defined by the SMBus Specification and is
413 @param PecCheck Defines if Packet Error Code (PEC) checking is required for this
415 @param Length Signifies the number of bytes that this operation will do. The
416 maximum number of bytes can be revision specific and operation
417 specific. This field will contain the actual number of bytes that
418 are executed for this operation. Not all operations require this
420 @param Buffer Contains the value of data to execute to the SMBus slave device.
421 Not all operations require this argument. The length of this
422 buffer is identified by Length.
424 @retval EFI_SUCCESS The last data that was returned from the access matched the poll
426 @retval EFI_CRC_ERROR Checksum is not correct (PEC is incorrect).
427 @retval EFI_TIMEOUT Timeout expired before the operation was completed. Timeout is
428 determined by the SMBus host controller device.
429 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
430 @retval EFI_DEVICE_ERROR The request was not completed because a failure that was
431 reflected in the Host Status Register bit. Device errors are a
432 result of a transaction collision, illegal command field,
433 unclaimed cycle (host initiated), or bus errors (collisions).
434 @retval EFI_INVALID_PARAMETER Operation is not defined in EFI_SMBUS_OPERATION.
435 @retval EFI_INVALID_PARAMETER Length/Buffer is NULL for operations except for EfiSmbusQuickRead
436 and EfiSmbusQuickWrite. Length is outside the range of valid
438 @retval EFI_UNSUPPORTED The SMBus operation or PEC is not supported.
439 @retval EFI_BUFFER_TOO_SMALL Buffer is not sufficient for this operation.
445 IN CONST EFI_SMBUS_HC_PROTOCOL
*This
,
446 IN CONST EFI_SMBUS_DEVICE_ADDRESS SlaveAddress
,
447 IN CONST EFI_SMBUS_DEVICE_COMMAND Command
,
448 IN CONST EFI_SMBUS_OPERATION Operation
,
449 IN CONST BOOLEAN PecCheck
,
450 IN OUT UINTN
*Length
,
454 InitializeInternal ();
466 Sets the SMBus slave device addresses for the device with a given unique ID or enumerates the
469 The ArpDevice() function provides a standard way for a device driver to enumerate the entire
470 SMBus or specific devices on the bus.
472 @param This A pointer to the EFI_SMBUS_HC_PROTOCOL instance.
473 @param ArpAll A Boolean expression that indicates if the host drivers need to
474 enumerate all the devices or enumerate only the device that is
475 identified by SmbusUdid. If ArpAll is TRUE, SmbusUdid and
476 SlaveAddress are optional. If ArpAll is FALSE, ArpDevice will
477 enumerate SmbusUdid and the address will be at SlaveAddress.
478 @param SmbusUdid The Unique Device Identifier (UDID) that is associated with this
480 @param SlaveAddress The SMBus slave address that is associated with an SMBus UDID.
482 @retval EFI_SUCCESS The last data that was returned from the access matched the poll
484 @retval EFI_CRC_ERROR Checksum is not correct (PEC is incorrect).
485 @retval EFI_TIMEOUT Timeout expired before the operation was completed. Timeout is
486 determined by the SMBus host controller device.
487 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
488 @retval EFI_DEVICE_ERROR The request was not completed because a failure that was
489 reflected in the Host Status Register bit. Device errors are a
490 result of a transaction collision, illegal command field,
491 unclaimed cycle (host initiated), or bus errors (collisions).
492 @retval EFI_UNSUPPORTED The corresponding SMBus operation is not supported.
498 IN CONST EFI_SMBUS_HC_PROTOCOL
*This
,
500 IN EFI_SMBUS_UDID
*SmbusUdid
, OPTIONAL
501 IN OUT EFI_SMBUS_DEVICE_ADDRESS
*SlaveAddress OPTIONAL
504 InitializeInternal ();
507 return SmbusFullArp ();
509 if ((SmbusUdid
== NULL
) || (SlaveAddress
== NULL
)) {
510 return EFI_INVALID_PARAMETER
;
512 return SmbusDirectedArp ((EFI_SMBUS_UDID
*)SmbusUdid
, SlaveAddress
);
517 Returns a pointer to the Address Resolution Protocol (ARP) map that contains the ID/address pair
518 of the slave devices that were enumerated by the SMBus host controller driver.
520 The GetArpMap() function returns the mapping of all the SMBus devices that were enumerated by the
523 @param This A pointer to the EFI_SMBUS_HC_PROTOCOL instance.
524 @param Length Size of the buffer that contains the SMBus device map.
525 @param SmbusDeviceMap The pointer to the device map as enumerated by the SMBus
528 @retval EFI_SUCCESS The SMBus returned the current device map.
529 @retval EFI_UNSUPPORTED The corresponding operation is not supported.
535 IN CONST EFI_SMBUS_HC_PROTOCOL
*This
,
536 IN OUT UINTN
*Length
,
537 IN OUT EFI_SMBUS_DEVICE_MAP
**SmbusDeviceMap
540 *Length
= mDeviceMapEntries
;
541 *SmbusDeviceMap
= mDeviceMap
;
547 Allows a device driver to register for a callback when the bus driver detects a state that it
548 needs to propagate to other drivers that are registered for a callback.
550 The Notify() function registers all the callback functions to allow the bus driver to call these
551 functions when the SlaveAddress/Data pair happens.
552 If NotifyFunction is NULL, then ASSERT ().
554 @param This A pointer to the EFI_SMBUS_HC_PROTOCOL instance.
555 @param SlaveAddress The SMBUS hardware address to which the SMBUS device is
556 preassigned or allocated.
557 @param Data Data of the SMBus host notify command that the caller wants to be
559 @param NotifyFunction The function to call when the bus driver detects the SlaveAddress
562 @retval EFI_SUCCESS NotifyFunction was registered.
563 @retval EFI_UNSUPPORTED The corresponding operation is not supported.
569 IN CONST EFI_SMBUS_HC_PROTOCOL
*This
,
570 IN CONST EFI_SMBUS_DEVICE_ADDRESS SlaveAddress
,
572 IN CONST EFI_SMBUS_NOTIFY_FUNCTION NotifyFunction
575 return EFI_UNSUPPORTED
;
579 Entry point to the DXE Driver that produces the SMBus Host Controller Protocol.
581 @param ImageHandle ImageHandle of the loaded driver.
582 @param SystemTable Pointer to the EFI System Table.
584 @retval EFI_SUCCESS The entry point of SMBus DXE driver is executed successfully.
585 @retval !EFI_SUCESS Some error occurs in the entry point of SMBus DXE driver.
591 IN EFI_HANDLE ImageHandle
,
592 IN EFI_SYSTEM_TABLE
*SystemTable
597 mPlatformNumRsvd
= (UINT8
)PcdGet32 (PcdPlatformSmbusAddrNum
);
598 mPlatformAddrRsvd
= (UINT8
*)(UINTN
) PcdGet64 (PcdPlatformSmbusAddrTable
);
601 // Install SMBus Host Controller protocol interface.
603 Status
= gBS
->InstallMultipleProtocolInterfaces (
605 &gEfiSmbusHcProtocolGuid
,
609 ASSERT_EFI_ERROR (Status
);