3 This driver produces Extended SCSI Pass Thru Protocol instances for
4 LSI Fusion MPT SCSI devices.
6 Copyright (C) 2020, Oracle and/or its affiliates.
8 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <IndustryStandard/FusionMptScsi.h>
13 #include <IndustryStandard/Pci.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/DebugLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/PcdLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/UefiLib.h>
20 #include <Protocol/PciIo.h>
21 #include <Protocol/ScsiPassThruExt.h>
22 #include <Uefi/UefiSpec.h>
25 // Higher versions will be used before lower, 0x10-0xffffffef is the version
26 // range for IVH (Indie Hardware Vendors)
28 #define MPT_SCSI_BINDING_VERSION 0x10
34 #define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S')
37 EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru
;
38 EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode
;
40 EFI_PCI_IO_PROTOCOL
*PciIo
;
41 UINT64 OriginalPciAttributes
;
44 #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
45 CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE)
59 return Dev
->PciIo
->Io
.Write (
77 return Dev
->PciIo
->Io
.Read (
91 IN UINT8 DoorbellFunc
,
98 (((UINT32
)DoorbellFunc
) << 24) | (DoorbellArg
<< 16)
113 Status
= MptDoorbell (Dev
, MPT_DOORBELL_RESET
, 0);
114 if (EFI_ERROR (Status
)) {
120 Status
= Out32 (Dev
, MPT_REG_IMASK
, MPT_IMASK_DOORBELL
| MPT_IMASK_REPLY
);
121 if (EFI_ERROR (Status
)) {
125 // Clear interrupt status
127 Status
= Out32 (Dev
, MPT_REG_ISTATUS
, 0);
128 if (EFI_ERROR (Status
)) {
143 MPT_IO_CONTROLLER_INIT_REQUEST Data
;
146 MPT_IO_CONTROLLER_INIT_REQUEST
*Req
;
147 MPT_IO_CONTROLLER_INIT_REPLY Reply
;
151 Req
= &AlignedReq
.Data
;
153 Status
= MptScsiReset (Dev
);
154 if (EFI_ERROR (Status
)) {
158 ZeroMem (Req
, sizeof (*Req
));
159 ZeroMem (&Reply
, sizeof (Reply
));
160 Req
->WhoInit
= MPT_IOC_WHOINIT_ROM_BIOS
;
161 Req
->Function
= MPT_MESSAGE_HDR_FUNCTION_IOC_INIT
;
163 FixedPcdGet8 (PcdMptScsiMaxTargetLimit
) < 255,
164 "Req supports 255 targets only (max target is 254)"
166 Req
->MaxDevices
= Dev
->MaxTarget
+ 1;
170 // Send controller init through doorbell
173 sizeof (*Req
) % sizeof (UINT32
) == 0,
174 "Req must be multiple of UINT32"
177 sizeof (*Req
) / sizeof (UINT32
) <= MAX_UINT8
,
178 "Req must fit in MAX_UINT8 Dwords"
180 Status
= MptDoorbell (
182 MPT_DOORBELL_HANDSHAKE
,
183 (UINT8
)(sizeof (*Req
) / sizeof (UINT32
))
185 if (EFI_ERROR (Status
)) {
188 Status
= Dev
->PciIo
->Io
.Write (
190 EfiPciIoWidthFifoUint32
,
193 sizeof (*Req
) / sizeof (UINT32
),
196 if (EFI_ERROR (Status
)) {
201 // Read reply through doorbell
202 // Each 32bit (Dword) read produces 16bit (Word) of data
204 // The reply is read back to complete the doorbell function but it
205 // isn't useful because it doesn't contain relevant data or status
209 sizeof (Reply
) % sizeof (UINT16
) == 0,
210 "Reply must be multiple of UINT16"
212 ReplyBytes
= (UINT8
*)&Reply
;
213 while (ReplyBytes
!= (UINT8
*)(&Reply
+ 1)) {
214 Status
= In32 (Dev
, MPT_REG_DOORBELL
, &ReplyWord
);
215 if (EFI_ERROR (Status
)) {
218 CopyMem (ReplyBytes
, &ReplyWord
, sizeof (UINT16
));
219 ReplyBytes
+= sizeof (UINT16
);
223 // Clear interrupts generated by doorbell reply
225 Status
= Out32 (Dev
, MPT_REG_ISTATUS
, 0);
226 if (EFI_ERROR (Status
)) {
234 // Ext SCSI Pass Thru
241 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
244 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
245 IN EFI_EVENT Event OPTIONAL
248 return EFI_UNSUPPORTED
;
253 IsTargetInitialized (
259 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
; ++Idx
) {
260 if (Target
[Idx
] != 0xFF) {
270 MptScsiGetNextTargetLun (
271 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
272 IN OUT UINT8
**Target
,
278 Dev
= MPT_SCSI_FROM_PASS_THRU (This
);
280 // Currently support only LUN 0, so hardcode it
282 if (!IsTargetInitialized (*Target
)) {
283 ZeroMem (*Target
, TARGET_MAX_BYTES
);
285 } else if (**Target
> Dev
->MaxTarget
|| *Lun
> 0) {
286 return EFI_INVALID_PARAMETER
;
287 } else if (**Target
< Dev
->MaxTarget
) {
289 // This device interface support 256 targets only, so it's enough to
290 // increment the LSB of Target, as it will never overflow.
294 return EFI_NOT_FOUND
;
303 MptScsiGetNextTarget (
304 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
305 IN OUT UINT8
**Target
310 Dev
= MPT_SCSI_FROM_PASS_THRU (This
);
311 if (!IsTargetInitialized (*Target
)) {
312 ZeroMem (*Target
, TARGET_MAX_BYTES
);
313 } else if (**Target
> Dev
->MaxTarget
) {
314 return EFI_INVALID_PARAMETER
;
315 } else if (**Target
< Dev
->MaxTarget
) {
317 // This device interface support 256 targets only, so it's enough to
318 // increment the LSB of Target, as it will never overflow.
322 return EFI_NOT_FOUND
;
331 MptScsiBuildDevicePath (
332 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
335 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
339 SCSI_DEVICE_PATH
*ScsiDevicePath
;
341 if (DevicePath
== NULL
) {
342 return EFI_INVALID_PARAMETER
;
346 // This device support 256 targets only, so it's enough to dereference
347 // the LSB of Target.
349 Dev
= MPT_SCSI_FROM_PASS_THRU (This
);
350 if (*Target
> Dev
->MaxTarget
|| Lun
> 0) {
351 return EFI_NOT_FOUND
;
354 ScsiDevicePath
= AllocateZeroPool (sizeof (*ScsiDevicePath
));
355 if (ScsiDevicePath
== NULL
) {
356 return EFI_OUT_OF_RESOURCES
;
359 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
360 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
361 ScsiDevicePath
->Header
.Length
[0] = (UINT8
)sizeof (*ScsiDevicePath
);
362 ScsiDevicePath
->Header
.Length
[1] = (UINT8
)(sizeof (*ScsiDevicePath
) >> 8);
363 ScsiDevicePath
->Pun
= *Target
;
364 ScsiDevicePath
->Lun
= (UINT16
)Lun
;
366 *DevicePath
= &ScsiDevicePath
->Header
;
373 MptScsiGetTargetLun (
374 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
375 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
381 SCSI_DEVICE_PATH
*ScsiDevicePath
;
383 if (DevicePath
== NULL
||
384 Target
== NULL
|| *Target
== NULL
|| Lun
== NULL
) {
385 return EFI_INVALID_PARAMETER
;
388 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
389 DevicePath
->SubType
!= MSG_SCSI_DP
) {
390 return EFI_UNSUPPORTED
;
393 Dev
= MPT_SCSI_FROM_PASS_THRU (This
);
394 ScsiDevicePath
= (SCSI_DEVICE_PATH
*)DevicePath
;
395 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
396 ScsiDevicePath
->Lun
> 0) {
397 return EFI_NOT_FOUND
;
400 ZeroMem (*Target
, TARGET_MAX_BYTES
);
402 // This device support 256 targets only, so it's enough to set the LSB
405 **Target
= (UINT8
)ScsiDevicePath
->Pun
;
406 *Lun
= ScsiDevicePath
->Lun
;
414 MptScsiResetChannel (
415 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
418 return EFI_UNSUPPORTED
;
424 MptScsiResetTargetLun (
425 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
430 return EFI_UNSUPPORTED
;
440 MptScsiControllerSupported (
441 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
442 IN EFI_HANDLE ControllerHandle
,
443 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
447 EFI_PCI_IO_PROTOCOL
*PciIo
;
450 Status
= gBS
->OpenProtocol (
452 &gEfiPciIoProtocolGuid
,
454 This
->DriverBindingHandle
,
456 EFI_OPEN_PROTOCOL_BY_DRIVER
458 if (EFI_ERROR (Status
)) {
462 Status
= PciIo
->Pci
.Read (
466 sizeof (Pci
) / sizeof (UINT32
),
469 if (EFI_ERROR (Status
)) {
473 if (Pci
.Hdr
.VendorId
== LSI_LOGIC_PCI_VENDOR_ID
&&
474 (Pci
.Hdr
.DeviceId
== LSI_53C1030_PCI_DEVICE_ID
||
475 Pci
.Hdr
.DeviceId
== LSI_SAS1068_PCI_DEVICE_ID
||
476 Pci
.Hdr
.DeviceId
== LSI_SAS1068E_PCI_DEVICE_ID
)) {
477 Status
= EFI_SUCCESS
;
479 Status
= EFI_UNSUPPORTED
;
485 &gEfiPciIoProtocolGuid
,
486 This
->DriverBindingHandle
,
495 MptScsiControllerStart (
496 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
497 IN EFI_HANDLE ControllerHandle
,
498 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
504 Dev
= AllocateZeroPool (sizeof (*Dev
));
506 return EFI_OUT_OF_RESOURCES
;
509 Dev
->Signature
= MPT_SCSI_DEV_SIGNATURE
;
511 Dev
->MaxTarget
= PcdGet8 (PcdMptScsiMaxTargetLimit
);
513 Status
= gBS
->OpenProtocol (
515 &gEfiPciIoProtocolGuid
,
516 (VOID
**)&Dev
->PciIo
,
517 This
->DriverBindingHandle
,
519 EFI_OPEN_PROTOCOL_BY_DRIVER
521 if (EFI_ERROR (Status
)) {
525 Status
= Dev
->PciIo
->Attributes (
527 EfiPciIoAttributeOperationGet
,
529 &Dev
->OriginalPciAttributes
531 if (EFI_ERROR (Status
)) {
536 // Enable I/O Space & Bus-Mastering
538 Status
= Dev
->PciIo
->Attributes (
540 EfiPciIoAttributeOperationEnable
,
541 (EFI_PCI_IO_ATTRIBUTE_IO
|
542 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER
),
545 if (EFI_ERROR (Status
)) {
550 // Signal device supports 64-bit DMA addresses
552 Status
= Dev
->PciIo
->Attributes (
554 EfiPciIoAttributeOperationEnable
,
555 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE
,
558 if (EFI_ERROR (Status
)) {
560 // Warn user that device will only be using 32-bit DMA addresses.
562 // Note that this does not prevent the device/driver from working
563 // and therefore we only warn and continue as usual.
567 "%a: failed to enable 64-bit DMA addresses\n",
572 Status
= MptScsiInit (Dev
);
573 if (EFI_ERROR (Status
)) {
574 goto RestoreAttributes
;
578 // Host adapter channel, doesn't exist
580 Dev
->PassThruMode
.AdapterId
= MAX_UINT32
;
581 Dev
->PassThruMode
.Attributes
=
582 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
583 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
585 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
586 Dev
->PassThru
.PassThru
= &MptScsiPassThru
;
587 Dev
->PassThru
.GetNextTargetLun
= &MptScsiGetNextTargetLun
;
588 Dev
->PassThru
.BuildDevicePath
= &MptScsiBuildDevicePath
;
589 Dev
->PassThru
.GetTargetLun
= &MptScsiGetTargetLun
;
590 Dev
->PassThru
.ResetChannel
= &MptScsiResetChannel
;
591 Dev
->PassThru
.ResetTargetLun
= &MptScsiResetTargetLun
;
592 Dev
->PassThru
.GetNextTarget
= &MptScsiGetNextTarget
;
594 Status
= gBS
->InstallProtocolInterface (
596 &gEfiExtScsiPassThruProtocolGuid
,
597 EFI_NATIVE_INTERFACE
,
600 if (EFI_ERROR (Status
)) {
610 Dev
->PciIo
->Attributes (
612 EfiPciIoAttributeOperationSet
,
613 Dev
->OriginalPciAttributes
,
620 &gEfiPciIoProtocolGuid
,
621 This
->DriverBindingHandle
,
634 MptScsiControllerStop (
635 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
636 IN EFI_HANDLE ControllerHandle
,
637 IN UINTN NumberOfChildren
,
638 IN EFI_HANDLE
*ChildHandleBuffer
642 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
645 Status
= gBS
->OpenProtocol (
647 &gEfiExtScsiPassThruProtocolGuid
,
649 This
->DriverBindingHandle
,
651 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// Lookup only
653 if (EFI_ERROR (Status
)) {
657 Dev
= MPT_SCSI_FROM_PASS_THRU (PassThru
);
659 Status
= gBS
->UninstallProtocolInterface (
661 &gEfiExtScsiPassThruProtocolGuid
,
664 if (EFI_ERROR (Status
)) {
670 Dev
->PciIo
->Attributes (
672 EfiPciIoAttributeOperationSet
,
673 Dev
->OriginalPciAttributes
,
679 &gEfiPciIoProtocolGuid
,
680 This
->DriverBindingHandle
,
690 EFI_DRIVER_BINDING_PROTOCOL mMptScsiDriverBinding
= {
691 &MptScsiControllerSupported
,
692 &MptScsiControllerStart
,
693 &MptScsiControllerStop
,
694 MPT_SCSI_BINDING_VERSION
,
695 NULL
, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2
696 NULL
, // DriverBindingHandle, filled as well
704 EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
705 { "eng;en", L
"LSI Fusion MPT SCSI Driver" },
710 EFI_COMPONENT_NAME_PROTOCOL mComponentName
;
714 MptScsiGetDriverName (
715 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
717 OUT CHAR16
**DriverName
720 return LookupUnicodeString2 (
722 This
->SupportedLanguages
,
725 (BOOLEAN
)(This
== &mComponentName
) // Iso639Language
731 MptScsiGetDeviceName (
732 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
733 IN EFI_HANDLE DeviceHandle
,
734 IN EFI_HANDLE ChildHandle
,
736 OUT CHAR16
**ControllerName
739 return EFI_UNSUPPORTED
;
743 EFI_COMPONENT_NAME_PROTOCOL mComponentName
= {
744 &MptScsiGetDriverName
,
745 &MptScsiGetDeviceName
,
746 "eng" // SupportedLanguages, ISO 639-2 language codes
750 EFI_COMPONENT_NAME2_PROTOCOL mComponentName2
= {
751 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &MptScsiGetDriverName
,
752 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &MptScsiGetDeviceName
,
753 "en" // SupportedLanguages, RFC 4646 language codes
763 IN EFI_HANDLE ImageHandle
,
764 IN EFI_SYSTEM_TABLE
*SystemTable
767 return EfiLibInstallDriverBindingComponentName2 (
770 &mMptScsiDriverBinding
,
771 ImageHandle
, // The handle to install onto