/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2011 Advanced Micro Devices, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "agesawrapper.h"
#include "amdlib.h"
#include "dimmSpd.h"
#include "BiosCallOuts.h"
#include "heapManager.h"
#include "SB800.h"

STATIC BIOS_CALLOUT_STRUCT BiosCallouts[] =
{
	{AGESA_ALLOCATE_BUFFER,
	 BiosAllocateBuffer
	},

	{AGESA_DEALLOCATE_BUFFER,
	 BiosDeallocateBuffer
	},

	{AGESA_DO_RESET,
	 BiosReset
	},

	{AGESA_LOCATE_BUFFER,
	 BiosLocateBuffer
	},

	{AGESA_READ_SPD,
	 BiosReadSpd
	},

	{AGESA_READ_SPD_RECOVERY,
	 BiosDefaultRet
	},

	{AGESA_RUNFUNC_ONAP,
	 BiosRunFuncOnAp
	},

	{AGESA_GNB_PCIE_SLOT_RESET,
	 BiosGnbPcieSlotReset
	},

	{AGESA_HOOKBEFORE_DRAM_INIT,
	 BiosHookBeforeDramInit
	},

	{AGESA_HOOKBEFORE_DRAM_INIT_RECOVERY,
	 BiosHookBeforeDramInitRecovery
	},

	{AGESA_HOOKBEFORE_DQS_TRAINING,
	 BiosHookBeforeDQSTraining
	},

	{AGESA_HOOKBEFORE_EXIT_SELF_REF,
	 BiosHookBeforeExitSelfRefresh
	},
};

AGESA_STATUS GetBiosCallout (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	UINTN i;
	AGESA_STATUS CalloutStatus;
	UINTN CallOutCount = sizeof (BiosCallouts) / sizeof (BiosCallouts [0]);

	CalloutStatus = AGESA_UNSUPPORTED;

	for (i = 0; i < CallOutCount; i++) {
		if (BiosCallouts[i].CalloutName == Func) {
			CalloutStatus = BiosCallouts[i].CalloutPtr (Func, Data, ConfigPtr);
			return CalloutStatus;
		}
	}

	return CalloutStatus;
}

AGESA_STATUS BiosAllocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	UINT32				AvailableHeapSize;
	UINT8				*BiosHeapBaseAddr;
	UINT32				CurrNodeOffset;
	UINT32				PrevNodeOffset;
	UINT32				FreedNodeOffset;
	UINT32				BestFitNodeOffset;
	UINT32				BestFitPrevNodeOffset;
	UINT32				NextFreeOffset;
	BIOS_BUFFER_NODE	*CurrNodePtr;
	BIOS_BUFFER_NODE	*FreedNodePtr;
	BIOS_BUFFER_NODE	*BestFitNodePtr;
	BIOS_BUFFER_NODE	*BestFitPrevNodePtr;
	BIOS_BUFFER_NODE	*NextFreePtr;
	BIOS_HEAP_MANAGER	*BiosHeapBasePtr;
	AGESA_BUFFER_PARAMS *AllocParams;

	AllocParams = ((AGESA_BUFFER_PARAMS *) ConfigPtr);
	AllocParams->BufferPointer = NULL;

	AvailableHeapSize = BIOS_HEAP_SIZE - sizeof (BIOS_HEAP_MANAGER);
	BiosHeapBaseAddr = (UINT8 *) BIOS_HEAP_START_ADDRESS;
	BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BIOS_HEAP_START_ADDRESS;

	if (BiosHeapBasePtr->StartOfAllocatedNodes == 0) {
		/* First allocation */
		CurrNodeOffset = sizeof (BIOS_HEAP_MANAGER);
		CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
		CurrNodePtr->BufferHandle = AllocParams->BufferHandle;
		CurrNodePtr->BufferSize = AllocParams->BufferLength;
		CurrNodePtr->NextNodeOffset = 0;
		AllocParams->BufferPointer = (UINT8 *) CurrNodePtr + sizeof (BIOS_BUFFER_NODE);

		/* Update the remaining free space */
		FreedNodeOffset = CurrNodeOffset + CurrNodePtr->BufferSize + sizeof (BIOS_BUFFER_NODE);
		FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
		FreedNodePtr->BufferSize = AvailableHeapSize - sizeof (BIOS_BUFFER_NODE) - CurrNodePtr->BufferSize;
		FreedNodePtr->NextNodeOffset = 0;

		/* Update the offsets for Allocated and Freed nodes */
		BiosHeapBasePtr->StartOfAllocatedNodes = CurrNodeOffset;
		BiosHeapBasePtr->StartOfFreedNodes = FreedNodeOffset;
	} else {
		/* Find out whether BufferHandle has been allocated on the heap. */
		/* If it has, return AGESA_BOUNDS_CHK */
		CurrNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
		CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);

		while (CurrNodeOffset != 0) {
			CurrNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + CurrNodeOffset);
			if (CurrNodePtr->BufferHandle == AllocParams->BufferHandle) {
				return AGESA_BOUNDS_CHK;
			}
			CurrNodeOffset = CurrNodePtr->NextNodeOffset;
			/* If BufferHandle has not been allocated on the heap, CurrNodePtr here points
			 to the end of the allocated nodes list.
			*/
		}
		/* Find the node that best fits the requested buffer size */
		FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
		PrevNodeOffset = FreedNodeOffset;
		BestFitNodeOffset = 0;
		BestFitPrevNodeOffset = 0;
		while (FreedNodeOffset != 0) {
			FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);
			if (FreedNodePtr->BufferSize >= (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE))) {
				if (BestFitNodeOffset == 0) {
					/* First node that fits the requested buffer size */
					BestFitNodeOffset = FreedNodeOffset;
					BestFitPrevNodeOffset = PrevNodeOffset;
				} else {
					/* Find out whether current node is a better fit than the previous nodes */
					BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
					if (BestFitNodePtr->BufferSize > FreedNodePtr->BufferSize) {
						BestFitNodeOffset = FreedNodeOffset;
						BestFitPrevNodeOffset = PrevNodeOffset;
					}
				}
			}
			PrevNodeOffset = FreedNodeOffset;
			FreedNodeOffset = FreedNodePtr->NextNodeOffset;
		} /* end of while loop */


		if (BestFitNodeOffset == 0) {
			/* If we could not find a node that fits the requested buffer */
			/* size, return AGESA_BOUNDS_CHK */
			return AGESA_BOUNDS_CHK;
		} else {
			BestFitNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitNodeOffset);
			BestFitPrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + BestFitPrevNodeOffset);

			/* If BestFitNode is larger than the requested buffer, fragment the node further */
			if (BestFitNodePtr->BufferSize > (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE))) {
				NextFreeOffset = BestFitNodeOffset + AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE);

				NextFreePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextFreeOffset);
				NextFreePtr->BufferSize = BestFitNodePtr->BufferSize - (AllocParams->BufferLength + sizeof (BIOS_BUFFER_NODE));
				NextFreePtr->NextNodeOffset = BestFitNodePtr->NextNodeOffset;
			} else {
				/* Otherwise, next free node is NextNodeOffset of BestFitNode */
				NextFreeOffset = BestFitNodePtr->NextNodeOffset;
			}

			/* If BestFitNode is the first buffer in the list, then update
			 StartOfFreedNodes to reflect the new free node
			*/
			if (BestFitNodeOffset == BiosHeapBasePtr->StartOfFreedNodes) {
				BiosHeapBasePtr->StartOfFreedNodes = NextFreeOffset;
			} else {
				BestFitPrevNodePtr->NextNodeOffset = NextFreeOffset;
			}

			/* Add BestFitNode to the list of Allocated nodes */
			CurrNodePtr->NextNodeOffset = BestFitNodeOffset;
			BestFitNodePtr->BufferSize = AllocParams->BufferLength;
			BestFitNodePtr->BufferHandle = AllocParams->BufferHandle;
			BestFitNodePtr->NextNodeOffset = 0;

			/* Remove BestFitNode from list of Freed nodes */
			AllocParams->BufferPointer = (UINT8 *) BestFitNodePtr + sizeof (BIOS_BUFFER_NODE);
		}
	}

	return AGESA_SUCCESS;
}

AGESA_STATUS BiosDeallocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{

	UINT8				 *BiosHeapBaseAddr;
	UINT32				AllocNodeOffset;
	UINT32				PrevNodeOffset;
	UINT32				NextNodeOffset;
	UINT32				FreedNodeOffset;
	UINT32				EndNodeOffset;
	BIOS_BUFFER_NODE	 *AllocNodePtr;
	BIOS_BUFFER_NODE	 *PrevNodePtr;
	BIOS_BUFFER_NODE	 *FreedNodePtr;
	BIOS_BUFFER_NODE	 *NextNodePtr;
	BIOS_HEAP_MANAGER	*BiosHeapBasePtr;
	AGESA_BUFFER_PARAMS *AllocParams;

	BiosHeapBaseAddr = (UINT8 *) BIOS_HEAP_START_ADDRESS;
	BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BIOS_HEAP_START_ADDRESS;

	AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;

	/* Find target node to deallocate in list of allocated nodes.
	 Return AGESA_BOUNDS_CHK if the BufferHandle is not found
	*/
	AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
	AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
	PrevNodeOffset = AllocNodeOffset;

	while (AllocNodePtr->BufferHandle !=	AllocParams->BufferHandle) {
		if (AllocNodePtr->NextNodeOffset == 0) {
			return AGESA_BOUNDS_CHK;
		}
		PrevNodeOffset = AllocNodeOffset;
		AllocNodeOffset = AllocNodePtr->NextNodeOffset;
		AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
	}

	/* Remove target node from list of allocated nodes */
	PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
	PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;

	/* Zero out the buffer, and clear the BufferHandle */
	LibAmdMemFill ((UINT8 *)AllocNodePtr + sizeof (BIOS_BUFFER_NODE), 0, AllocNodePtr->BufferSize, &(AllocParams->StdHeader));
	AllocNodePtr->BufferHandle = 0;
	AllocNodePtr->BufferSize += sizeof (BIOS_BUFFER_NODE);

	/* Add deallocated node in order to the list of freed nodes */
	FreedNodeOffset = BiosHeapBasePtr->StartOfFreedNodes;
	FreedNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + FreedNodeOffset);

	EndNodeOffset = AllocNodeOffset + AllocNodePtr->BufferSize;

	if (AllocNodeOffset < FreedNodeOffset) {
		/* Add to the start of the freed list */
		if (EndNodeOffset == FreedNodeOffset) {
			/* If the freed node is adjacent to the first node in the list, concatenate both nodes */
			AllocNodePtr->BufferSize += FreedNodePtr->BufferSize;
			AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset;

			/* Clear the BufferSize and NextNodeOffset of the previous first node */
			FreedNodePtr->BufferSize = 0;
			FreedNodePtr->NextNodeOffset = 0;
		} else {
			/* Otherwise, add freed node to the start of the list
			 Update NextNodeOffset and BufferSize to include the
			 size of BIOS_BUFFER_NODE
			*/
			AllocNodePtr->NextNodeOffset = FreedNodeOffset;
		}
		/* Update StartOfFreedNodes to the new first node */
		BiosHeapBasePtr->StartOfFreedNodes = AllocNodeOffset;
	} else {
		/* Traverse list of freed nodes to find where the deallocated node
			 should be place
		*/
		NextNodeOffset = FreedNodeOffset;
		NextNodePtr = FreedNodePtr;
		while (AllocNodeOffset > NextNodeOffset) {
			PrevNodeOffset = NextNodeOffset;
			if (NextNodePtr->NextNodeOffset == 0) {
			break;
			}
			NextNodeOffset = NextNodePtr->NextNodeOffset;
			NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
		}

		/* If deallocated node is adjacent to the next node,
			 concatenate both nodes
		*/
		if (NextNodeOffset == EndNodeOffset) {
			NextNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + NextNodeOffset);
			AllocNodePtr->BufferSize += NextNodePtr->BufferSize;
			AllocNodePtr->NextNodeOffset = NextNodePtr->NextNodeOffset;

			NextNodePtr->BufferSize = 0;
			NextNodePtr->NextNodeOffset = 0;
		} else {
			/*AllocNodePtr->NextNodeOffset = FreedNodePtr->NextNodeOffset; */
			AllocNodePtr->NextNodeOffset = NextNodeOffset;
		}
		/* If deallocated node is adjacent to the previous node,
			 concatenate both nodes
		*/
		PrevNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + PrevNodeOffset);
		EndNodeOffset = PrevNodeOffset + PrevNodePtr->BufferSize;
		if (AllocNodeOffset == EndNodeOffset) {
			PrevNodePtr->NextNodeOffset = AllocNodePtr->NextNodeOffset;
			PrevNodePtr->BufferSize += AllocNodePtr->BufferSize;
			AllocNodePtr->BufferSize = 0;
			AllocNodePtr->NextNodeOffset = 0;
		} else {
			PrevNodePtr->NextNodeOffset = AllocNodeOffset;
		}
	}
	return AGESA_SUCCESS;
}

AGESA_STATUS BiosLocateBuffer (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	UINT32				AllocNodeOffset;
	UINT8				*BiosHeapBaseAddr;
	BIOS_BUFFER_NODE	*AllocNodePtr;
	BIOS_HEAP_MANAGER	*BiosHeapBasePtr;
	AGESA_BUFFER_PARAMS	*AllocParams;

	AllocParams = (AGESA_BUFFER_PARAMS *) ConfigPtr;

	BiosHeapBaseAddr = (UINT8 *) BIOS_HEAP_START_ADDRESS;
	BiosHeapBasePtr = (BIOS_HEAP_MANAGER *) BIOS_HEAP_START_ADDRESS;

	AllocNodeOffset = BiosHeapBasePtr->StartOfAllocatedNodes;
	AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);

	while (AllocParams->BufferHandle != AllocNodePtr->BufferHandle) {
		if (AllocNodePtr->NextNodeOffset == 0) {
			AllocParams->BufferPointer = NULL;
			AllocParams->BufferLength = 0;
			return AGESA_BOUNDS_CHK;
		} else {
			AllocNodeOffset = AllocNodePtr->NextNodeOffset;
			AllocNodePtr = (BIOS_BUFFER_NODE *) (BiosHeapBaseAddr + AllocNodeOffset);
		}
	}

	AllocParams->BufferPointer = (UINT8 *) ((UINT8 *) AllocNodePtr + sizeof (BIOS_BUFFER_NODE));
	AllocParams->BufferLength = AllocNodePtr->BufferSize;

	return AGESA_SUCCESS;

}

AGESA_STATUS BiosRunFuncOnAp (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	AGESA_STATUS		Status;

	Status = agesawrapper_amdlaterunaptask (Func, Data, ConfigPtr);
	return Status;
}

AGESA_STATUS BiosReset (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	AGESA_STATUS		Status;
	UINT8				 Value;
	UINTN				 ResetType;
	AMD_CONFIG_PARAMS	 *StdHeader;

	ResetType = Data;
	StdHeader = ConfigPtr;

	//
	// Perform the RESET based upon the ResetType. In case of
	// WARM_RESET_WHENVER and COLD_RESET_WHENEVER, the request will go to
	// AmdResetManager. During the critical condition, where reset is required
	// immediately, the reset will be invoked directly by writing 0x04 to port
	// 0xCF9 (Reset Port).
	//
	switch (ResetType) {
		case WARM_RESET_WHENEVER:
		case COLD_RESET_WHENEVER:
		break;

		case WARM_RESET_IMMEDIATELY:
		case COLD_RESET_IMMEDIATELY:
			Value = 0x06;
			LibAmdIoWrite (AccessWidth8, 0xCf9, &Value, StdHeader);
		break;

		default:
		break;
	}

	Status = 0;
	return Status;
}

AGESA_STATUS BiosReadSpd (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	AGESA_STATUS Status;
	Status = AmdMemoryReadSPD (Func, Data, (AGESA_READ_SPD_PARAMS *)ConfigPtr);

	return Status;
}

AGESA_STATUS BiosDefaultRet (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	return AGESA_UNSUPPORTED;
}
/*	Call the host environment interface to provide a user hook opportunity. */
AGESA_STATUS BiosHookBeforeDQSTraining (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	return AGESA_SUCCESS;
}
/*	Call the host environment interface to provide a user hook opportunity. */
AGESA_STATUS BiosHookBeforeDramInit (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	AGESA_STATUS		Status;
	UINTN			 FcnData;
	MEM_DATA_STRUCT	 *MemData;
	UINT32			AcpiMmioAddr;
	UINT32			GpioMmioAddr;
	UINT8			 Data8;
	UINT16			Data16;
	UINT8			 TempData8;

	FcnData = Data;
	MemData = ConfigPtr;

	Status	= AGESA_SUCCESS;
	/* Get SB MMIO Base (AcpiMmioAddr) */
	WriteIo8 (0xCD6, 0x27);
	Data8	 = ReadIo8(0xCD7);
	Data16	= Data8<<8;
	WriteIo8 (0xCD6, 0x26);
	Data8	 = ReadIo8(0xCD7);
	Data16	|= Data8;
	AcpiMmioAddr = (UINT32)Data16 << 16;
	GpioMmioAddr = AcpiMmioAddr + GPIO_BASE;

	Data8 = Read64Mem8(GpioMmioAddr+SB_GPIO_REG178);
	Data8 &= ~BIT5;
	TempData8	= Read64Mem8 (GpioMmioAddr+SB_GPIO_REG178);
	TempData8 &= 0x03;
	TempData8 |= Data8;
	Write64Mem8(GpioMmioAddr+SB_GPIO_REG178, TempData8);

	Data8 |= BIT2+BIT3;
	Data8 &= ~BIT4;
	TempData8	= Read64Mem8 (GpioMmioAddr+SB_GPIO_REG178);
	TempData8 &= 0x23;
	TempData8 |= Data8;
	Write64Mem8(GpioMmioAddr+SB_GPIO_REG178, TempData8);

	Data8 = Read64Mem8(GpioMmioAddr+SB_GPIO_REG179);
	Data8 &= ~BIT5;
	TempData8	= Read64Mem8 (GpioMmioAddr+SB_GPIO_REG179);
	TempData8 &= 0x03;
	TempData8 |= Data8;
	Write64Mem8(GpioMmioAddr+SB_GPIO_REG179, TempData8);

	Data8 |= BIT2+BIT3;
	Data8 &= ~BIT4;
	TempData8	= Read64Mem8 (GpioMmioAddr+SB_GPIO_REG179);
	TempData8 &= 0x23;
	TempData8 |= Data8;
	Write64Mem8(GpioMmioAddr+SB_GPIO_REG179, TempData8);

	switch(MemData->ParameterListPtr->DDR3Voltage){
	case VOLT1_35:
		Data8 =	Read64Mem8 (GpioMmioAddr+SB_GPIO_REG178);
		Data8 &= ~(UINT8)BIT6;
		Write64Mem8(GpioMmioAddr+SB_GPIO_REG178, Data8);
		Data8 =	Read64Mem8 (GpioMmioAddr+SB_GPIO_REG179);
		Data8 |= (UINT8)BIT6;
		Write64Mem8(GpioMmioAddr+SB_GPIO_REG179, Data8);
		break;
	case VOLT1_25:
		Data8 =	Read64Mem8 (GpioMmioAddr+SB_GPIO_REG178);
		Data8 &= ~(UINT8)BIT6;
		Write64Mem8(GpioMmioAddr+SB_GPIO_REG178, Data8);
		Data8 =	Read64Mem8 (GpioMmioAddr+SB_GPIO_REG179);
		Data8 &= ~(UINT8)BIT6;
		Write64Mem8(GpioMmioAddr+SB_GPIO_REG179, Data8);
		break;
	case VOLT1_5:
	default:
		Data8 =	Read64Mem8 (GpioMmioAddr+SB_GPIO_REG178);
		Data8 |= (UINT8)BIT6;
		Write64Mem8(GpioMmioAddr+SB_GPIO_REG178, Data8);
		Data8 =	Read64Mem8 (GpioMmioAddr+SB_GPIO_REG179);
		Data8 &= ~(UINT8)BIT6;
		Write64Mem8(GpioMmioAddr+SB_GPIO_REG179, Data8);
	}
	return Status;
}

/*	Call the host environment interface to provide a user hook opportunity. */
AGESA_STATUS BiosHookBeforeDramInitRecovery (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	return AGESA_SUCCESS;
}

/*	Call the host environment interface to provide a user hook opportunity. */
AGESA_STATUS BiosHookBeforeExitSelfRefresh (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	return AGESA_SUCCESS;
}
/* PCIE slot reset control */
AGESA_STATUS BiosGnbPcieSlotReset (UINT32 Func, UINT32 Data, VOID *ConfigPtr)
{
	AGESA_STATUS Status;
	UINTN					FcnData;
	PCIe_SLOT_RESET_INFO	*ResetInfo;

	UINT32	GpioMmioAddr;
	UINT32	AcpiMmioAddr;
	UINT8	 Data8;
	UINT16	Data16;

	FcnData = Data;
	ResetInfo = ConfigPtr;
	// Get SB800 MMIO Base (AcpiMmioAddr)
	WriteIo8(0xCD6, 0x27);
	Data8 = ReadIo8(0xCD7);
	Data16=Data8<<8;
	WriteIo8(0xCD6, 0x26);
	Data8 = ReadIo8(0xCD7);
	Data16|=Data8;
	AcpiMmioAddr = (UINT32)Data16 << 16;
	Status = AGESA_UNSUPPORTED;
	GpioMmioAddr = AcpiMmioAddr + GPIO_BASE;
	switch (ResetInfo->ResetId)
	{
	case 4:
		switch (ResetInfo->ResetControl) {
			case AssertSlotReset:
			Data8 = Read64Mem8(GpioMmioAddr+SB_GPIO_REG21);
			Data8 &= ~(UINT8)BIT6 ;
			Write64Mem8(GpioMmioAddr+SB_GPIO_REG21, Data8);	 // MXM_GPIO0. GPIO21
			Status = AGESA_SUCCESS;
			break;
			case DeassertSlotReset:
			Data8 = Read64Mem8(GpioMmioAddr+SB_GPIO_REG21);
			Data8 |= BIT6 ;
			Write64Mem8 (GpioMmioAddr+SB_GPIO_REG21, Data8);		 // MXM_GPIO0. GPIO21
			Status = AGESA_SUCCESS;
			break;
		}
		break;
	case 6:
		switch (ResetInfo->ResetControl) {
			case AssertSlotReset:
			Data8 = Read64Mem8(GpioMmioAddr+SB_GPIO_REG25);
			Data8 &= ~(UINT8)BIT6 ;
			Write64Mem8(GpioMmioAddr+SB_GPIO_REG25, Data8);	 // PCIE_RST#_LAN, GPIO25
			Status = AGESA_SUCCESS;
			break;
			case DeassertSlotReset:
			Data8 = Read64Mem8(GpioMmioAddr+SB_GPIO_REG25);
			Data8 |= BIT6 ;
			Write64Mem8 (GpioMmioAddr+SB_GPIO_REG25, Data8);		 // PCIE_RST#_LAN, GPIO25
			Status = AGESA_SUCCESS;
			break;
		}
		break;
	case 7:
		switch (ResetInfo->ResetControl) {
			case AssertSlotReset:
			Data8 = Read64Mem8(GpioMmioAddr+SB_GPIO_REG02);
			Data8 &= ~(UINT8)BIT6 ;
			Write64Mem8(GpioMmioAddr+SB_GPIO_REG02, Data8);	 // MPCIE_RST0, GPIO02
			Status = AGESA_SUCCESS;
			break;
			case DeassertSlotReset:
			Data8 = Read64Mem8(GpioMmioAddr+SB_GPIO_REG25);
			Data8 |= BIT6 ;
			Write64Mem8 (GpioMmioAddr+SB_GPIO_REG02, Data8);		 // MPCIE_RST0, GPIO02
			Status = AGESA_SUCCESS;
			break;
		}
		break;
	}
	return	Status;
}