416 lines
13 KiB
C
416 lines
13 KiB
C
/**@file
|
|
Power Loss Notification
|
|
|
|
@copyright
|
|
INTEL CONFIDENTIAL
|
|
Copyright 2020 - 2021 Intel Corporation.
|
|
|
|
The source code contained or described herein and all documents related to the
|
|
source code ("Material") are owned by Intel Corporation or its suppliers or
|
|
licensors. Title to the Material remains with Intel Corporation or its suppliers
|
|
and licensors. The Material may contain trade secrets and proprietary and
|
|
confidential information of Intel Corporation and its suppliers and licensors,
|
|
and is protected by worldwide copyright and trade secret laws and treaty
|
|
provisions. No part of the Material may be used, copied, reproduced, modified,
|
|
published, uploaded, posted, transmitted, distributed, or disclosed in any way
|
|
without Intel's prior express written permission.
|
|
|
|
No license under any patent, copyright, trade secret or other intellectual
|
|
property right is granted to or conferred upon you by disclosure or delivery
|
|
of the Materials, either expressly, by implication, inducement, estoppel or
|
|
otherwise. Any license under such intellectual property rights must be
|
|
express and approved by Intel in writing.
|
|
|
|
Unless otherwise agreed by Intel in writing, you may not remove or alter
|
|
this notice or any other notice embedded in Materials by Intel or
|
|
Intel's suppliers or licensors in any way.
|
|
|
|
This file contains a 'Sample Driver' and is licensed as such under the terms
|
|
of your license agreement with Intel or your vendor. This file may be modified
|
|
by the user, subject to the additional terms of the license agreement.
|
|
|
|
@par Specification Reference:
|
|
**/
|
|
|
|
#include "PowerLossNotify.h"
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED VOID *mNvmePassthruRegistration = NULL;
|
|
GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mIsPlnConfigured = FALSE;
|
|
|
|
/**
|
|
Set feature command.
|
|
|
|
@param[in] NvmeDevice The pointer to the NVME_PASS_THRU_DEVICE data structure.
|
|
@param[in] NameSpaceId Namespace ID
|
|
@param[in] EnablePLN Indicate if PLN is enabled or not.
|
|
|
|
@return EFI_SUCCESS Successfully send command to device.
|
|
@return EFI_DEVICE_ERROR Fail to send command to device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
NvmeSetFeature (
|
|
IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmeDevice,
|
|
IN UINT32 NameSpaceId,
|
|
IN UINT8 EnablePln
|
|
)
|
|
{
|
|
EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
|
|
EFI_NVM_EXPRESS_COMMAND Command;
|
|
EFI_NVM_EXPRESS_COMPLETION Completion;
|
|
EFI_STATUS Status;
|
|
SAVE_FEATURE_DW3 *ResultDw3;
|
|
SAVE_FEATURE_DW0 *ResultDw0;
|
|
NVME_ADMIN_SET_FEATURES SetFeatureCdw10;
|
|
|
|
if (NvmeDevice == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
|
|
ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND));
|
|
ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION));
|
|
ZeroMem (&SetFeatureCdw10, sizeof (UINT32));
|
|
|
|
CommandPacket.NvmeCmd = &Command;
|
|
CommandPacket.NvmeCompletion = &Completion;
|
|
|
|
SetFeatureCdw10.Fid = PLN_FEATURE_ID;
|
|
SetFeatureCdw10.Sv = BIT0;
|
|
|
|
Command.Cdw0.Opcode = NVME_ADMIN_SET_FEATURES_CMD;
|
|
CommandPacket.NvmeCmd->Cdw10 = *((UINT32*) &SetFeatureCdw10);
|
|
CommandPacket.NvmeCmd->Cdw11 = (UINT32)(EnablePln & BIT0);
|
|
CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;
|
|
CommandPacket.NvmeCmd->Nsid = NVME_CONTROLLER_ID;
|
|
CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5);
|
|
CommandPacket.QueueType = NVME_ADMIN_QUEUE;
|
|
|
|
Status = NvmeDevice->PassThru (
|
|
NvmeDevice,
|
|
NameSpaceId,
|
|
&CommandPacket,
|
|
NULL
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
ResultDw3 = (SAVE_FEATURE_DW3*) &Completion.DW3;
|
|
ResultDw0 = (SAVE_FEATURE_DW0*) &Completion.DW0;
|
|
if ((ResultDw3->StatusCode == 0) && (ResultDw3->StatusCodeType == 0) && ((UINT8)ResultDw0->PlnEnable == EnablePln)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get feature command.
|
|
|
|
@param[in] NvmeDevice The pointer to the NVME_PASS_THRU_DEVICE data structure.
|
|
@param[in] Sel Select field for get feature command.
|
|
@param[in] NameSpaceId Namespace ID
|
|
@param[out] Result capability status
|
|
|
|
@return EFI_SUCCESS Successfully send command to device.
|
|
@return EFI_DEVICE_ERROR Fail to send command to device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
NvmeGetFeature (
|
|
IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmeDevice,
|
|
IN UINT8 Sel,
|
|
IN UINT32 NameSpaceId,
|
|
OUT UINT32 *Result
|
|
)
|
|
{
|
|
EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
|
|
EFI_NVM_EXPRESS_COMMAND Command;
|
|
EFI_NVM_EXPRESS_COMPLETION Completion;
|
|
EFI_STATUS Status;
|
|
NVME_ADMIN_GET_FEATURES GetFeatureCdw10;
|
|
|
|
if (Result == NULL || NvmeDevice == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
|
|
ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND));
|
|
ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION));
|
|
ZeroMem (&GetFeatureCdw10, sizeof (UINT32));
|
|
|
|
CommandPacket.NvmeCmd = &Command;
|
|
CommandPacket.NvmeCompletion = &Completion;
|
|
|
|
GetFeatureCdw10.Fid = PLN_FEATURE_ID;
|
|
GetFeatureCdw10.Sel = Sel;
|
|
|
|
Command.Cdw0.Opcode = NVME_ADMIN_GET_FEATURES_CMD;
|
|
CommandPacket.NvmeCmd->Cdw10 = *((UINT32*) &GetFeatureCdw10);
|
|
CommandPacket.NvmeCmd->Flags = CDW10_VALID;
|
|
CommandPacket.NvmeCmd->Nsid = NVME_CONTROLLER_ID;
|
|
CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5);
|
|
CommandPacket.QueueType = NVME_ADMIN_QUEUE;
|
|
|
|
Status = NvmeDevice->PassThru (
|
|
NvmeDevice,
|
|
NameSpaceId,
|
|
&CommandPacket,
|
|
NULL
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
CopyMem (Result, &Completion.DW0, sizeof (UINT32));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Enable or disable PLN with EC command on CS or non-CS mode.
|
|
|
|
@param[in] EnablePln Enable / Disable PLN feature.
|
|
@param[in] Cs Connected Standby, 1: enabled, 0: disabled.
|
|
|
|
**/
|
|
VOID
|
|
EcControlPln (
|
|
IN UINT8 EnablePln,
|
|
IN UINT8 Cs
|
|
)
|
|
{
|
|
SendEcCommand (EC_C_PLN_ENABLE);
|
|
|
|
if (EnablePln == 0) {
|
|
SendEcData(EC_PLN_DISABLED); // feature is not enabled
|
|
} else if (Cs == 0) {
|
|
SendEcData(EC_PLN_PB_OVR_NO_CS); // 4 sec
|
|
} else {
|
|
SendEcData(EC_PLN_PB_OVR_CS); // 10 sec
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
Enable or disable PLN with GPIO on CS or non-CS mode.
|
|
|
|
@param[in] EnablePln Enable / Disable PLN feature.
|
|
@param[in] Cs Connected Standby, 1: enabled, 0: disabled.
|
|
|
|
**/
|
|
VOID
|
|
GpioControlPln (
|
|
IN UINT8 EnablePln,
|
|
IN UINT8 Cs
|
|
)
|
|
{
|
|
UINT32 PlnDelaySelGpio;
|
|
UINT32 PlnDelayEnGpio;
|
|
|
|
PlnDelayEnGpio = PcdGet32 (PcdPlnDelayEnableGpio);
|
|
PlnDelaySelGpio = PcdGet32 (PcdPlnDelaySelectionGpio);
|
|
|
|
//
|
|
// There is no discrete logic to control PLN.
|
|
//
|
|
if (PlnDelayEnGpio == 0 || PlnDelaySelGpio == 0) {
|
|
return;
|
|
}
|
|
|
|
if (EnablePln == 0) {
|
|
GpioSetOutputValue (PlnDelayEnGpio, GPIO_PLN_DISABLED);
|
|
} else {
|
|
GpioSetOutputValue (PlnDelayEnGpio, GPIO_PLN_ENABLED);
|
|
if (Cs == 0) {
|
|
GpioSetOutputValue (PlnDelaySelGpio, GPIO_PLN_PB_OVR_NO_CS);
|
|
} else {
|
|
GpioSetOutputValue (PlnDelaySelGpio, GPIO_PLN_PB_OVR_CS);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Enable or disable PLN on device if device support PLN
|
|
|
|
@param[in] NvmePassthru The pointer to the NVME_PASS_THRU_DEVICE data structure.
|
|
@param[in] NameSpaceId Name space ID.
|
|
@param[in] EnablePln Enable / Disable PLN feature.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ConfigureDevicePln (
|
|
IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru,
|
|
IN UINT32 NameSpaceId,
|
|
IN UINT8 EnablePln
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 PlnVal;
|
|
GET_FEATURE_ATTR_CUR CurrentAttr;
|
|
|
|
//
|
|
// Test if PLN is supported
|
|
//
|
|
Status = NvmeGetFeature (NvmePassthru, SEL_CURRENT, NameSpaceId, (UINT32*) &CurrentAttr);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
PlnVal = (UINT8) CurrentAttr.PlnEnable;
|
|
DEBUG ((DEBUG_INFO, "PLN - Device support PLN, namespaceid= %d, status: %d -> %d.\n", NameSpaceId, PlnVal, EnablePln));
|
|
|
|
if (EnablePln != PlnVal) {
|
|
Status = NvmeSetFeature (NvmePassthru, NameSpaceId, EnablePln);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, " Configured PLN to value: %d was failed. Status: %r \n", Status));
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Nvme Pass Thru Protocol notification event handler.
|
|
|
|
@param[in] Event Event whose notification function is being invoked.
|
|
@param[in] Context Pointer to the notification function's context.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NvmePassthruNotificationEvent (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BufferSize;
|
|
EFI_HANDLE Handle;
|
|
EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru;
|
|
UINTN VarDataSize;
|
|
SETUP_DATA SystemConfiguration;
|
|
UINT32 NamespaceId;
|
|
|
|
BufferSize = sizeof (EFI_HANDLE);
|
|
Status = gBS->LocateHandle (
|
|
ByRegisterNotify,
|
|
NULL,
|
|
mNvmePassthruRegistration,
|
|
&BufferSize,
|
|
&Handle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Get NvmePassThruProtocol interface
|
|
//
|
|
Status = gBS->HandleProtocol (
|
|
Handle,
|
|
&gEfiNvmExpressPassThruProtocolGuid,
|
|
(VOID **) &NvmePassthru
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check PLN SETUP option
|
|
//
|
|
VarDataSize = sizeof (SETUP_DATA);
|
|
Status = gRT->GetVariable (
|
|
L"Setup",
|
|
&gSetupVariableGuid,
|
|
NULL,
|
|
&VarDataSize,
|
|
&SystemConfiguration
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Configured device with desired PLN setting if device support PLN, follow the below flow
|
|
// 1. send command to controller, if it is failed then
|
|
// 2. send command to the rest name space
|
|
//
|
|
Status = ConfigureDevicePln (NvmePassthru, NVME_CONTROLLER_ID, SystemConfiguration.PlnEnable);
|
|
if (EFI_ERROR (Status)) {
|
|
NamespaceId = NVME_ALL_NAMESPACES;
|
|
while (TRUE) {
|
|
Status = NvmePassthru->GetNextNamespace (
|
|
NvmePassthru,
|
|
(UINT32 *)&NamespaceId
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
|
|
Status = ConfigureDevicePln (NvmePassthru, NamespaceId, SystemConfiguration.PlnEnable);
|
|
}
|
|
}
|
|
|
|
if (!mIsPlnConfigured) {
|
|
|
|
DEBUG ((DEBUG_INFO, "PLN - Configure EC and GPIO.\n"));
|
|
|
|
//
|
|
// 1. Send command to EC to notify the power button override time if go with EC path
|
|
// 2. Set GPIO
|
|
//
|
|
EcControlPln (SystemConfiguration.PlnEnable, SystemConfiguration.LowPowerS0Idle);
|
|
GpioControlPln (SystemConfiguration.PlnEnable, SystemConfiguration.LowPowerS0Idle);
|
|
mIsPlnConfigured = TRUE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Driver entry point to register Nvme PassThru Protocol callback.
|
|
|
|
@param[in] ImageHandle The firmware allocated handle for the EFI image.
|
|
@param[in] SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS Notify event has been created
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PowerLostNotifyEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN VarDataSize;
|
|
SETUP_DATA SystemConfiguration;
|
|
|
|
DEBUG ((DEBUG_INFO, "PLN - PowerLostNotifyEntryPoint\n"));
|
|
|
|
//
|
|
// Check PLN SETUP option
|
|
//
|
|
VarDataSize = sizeof (SETUP_DATA);
|
|
Status = gRT->GetVariable (
|
|
L"Setup",
|
|
&gSetupVariableGuid,
|
|
NULL,
|
|
&VarDataSize,
|
|
&SystemConfiguration
|
|
);
|
|
if (!EFI_ERROR (Status) && SystemConfiguration.PlnEnable != CTL_PLN_DEFAULT) {
|
|
//
|
|
// Register NvmePassthruNotificationEvent() notify function.
|
|
//
|
|
EfiCreateProtocolNotifyEvent (
|
|
&gEfiNvmExpressPassThruProtocolGuid,
|
|
TPL_CALLBACK,
|
|
NvmePassthruNotificationEvent,
|
|
NULL,
|
|
&mNvmePassthruRegistration
|
|
);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|