/* SPDX-License-Identifier: BSD-2-Clause */

#include <stdint.h>
#include <security/intel/stm/StmApi.h>
#include <security/intel/stm/SmmStm.h>
#include <security/intel/stm/StmPlatformResource.h>

#if CONFIG(SOUTHBRIDGE_INTEL_COMMON_PMCLIB)
#include <southbridge/intel/common/pmutil.h>
#else
#include <soc/pm.h>
#endif
#include <cpu/x86/msr.h>
#include <console/console.h>

#define RDWR_ACCS 3
#define FULL_ACCS 7

// Fixed memory ranges
//
// TSEG memory!
static STM_RSC_MEM_DESC rsc_tseg_memory = {{MEM_RANGE, sizeof(STM_RSC_MEM_DESC)},
				    0,
				    0,
				    FULL_ACCS};

// Flash part
static STM_RSC_MEM_DESC rsc_spi_memory = {
				{MEM_RANGE, sizeof(STM_RSC_MEM_DESC)},
				0xFE000000,
				0x01000000,
				FULL_ACCS};

// ACPI
static STM_RSC_IO_DESC rsc_pm_io = {{IO_RANGE, sizeof(STM_RSC_IO_DESC)}, 0, 128};

// PCIE MMIO
static STM_RSC_MMIO_DESC rsc_pcie_mmio = {{MMIO_RANGE, sizeof(STM_RSC_MMIO_DESC)},
				   0,
				   0, // Length
				   RDWR_ACCS};

// Local APIC
static STM_RSC_MMIO_DESC rsc_apic_mmio = {{MMIO_RANGE, sizeof(STM_RSC_MMIO_DESC)},
				   0,
				   0x400,
				   RDWR_ACCS};

// Software SMI
static STM_RSC_TRAPPED_IO_DESC rsc_sw_smi_trap_io = {
				{TRAPPED_IO_RANGE, sizeof(STM_RSC_TRAPPED_IO_DESC)},
				0xB2,
				2};

// End of list
static STM_RSC_END rsc_list_end __attribute__((used)) = {
			{END_OF_RESOURCES, sizeof(STM_RSC_END)}, 0};

// Common PCI devices
//
// LPC bridge
STM_RSC_PCI_CFG_DESC rsc_lpc_bridge_pci = {
	{PCI_CFG_RANGE, sizeof(STM_RSC_PCI_CFG_DESC)},
	RDWR_ACCS,
	0,
	0,
	0x1000,
	0,
	0,
	{
		{1, 1, sizeof(STM_PCI_DEVICE_PATH_NODE), LPC_FUNCTION,
		 LPC_DEVICE},
	},
};

// Template for MSR resources.
STM_RSC_MSR_DESC rsc_msr_tpl = {
	{MACHINE_SPECIFIC_REG, sizeof(STM_RSC_MSR_DESC)},
};

// MSR indices to register
typedef struct {
	uint32_t msr_index;
	uint64_t read_mask;
	uint64_t write_mask;
} MSR_TABLE_ENTRY;

MSR_TABLE_ENTRY msr_table[] = {
	// Index Read Write
	// MASK64 means need access, MASK0 means no need access.
	{SMRR_PHYSBASE_MSR, MASK64, MASK0},
	{SMRR_PHYSMASK_MSR, MASK64, MASK0},
};

/*
 *  Fix up PCIE resource.
 */
static void fixup_pciex_resource(void)
{
	// Find max bus number and PCIEX length
	rsc_pcie_mmio.length = CONFIG_MMCONF_LENGTH; // 0x10000000;// 256 MB
	rsc_pcie_mmio.base = CONFIG_MMCONF_BASE_ADDRESS;
}

/*
 *  Add basic resources to BIOS resource database.
 */
static void add_simple_resources(void)
{
	int Status = 0;
	msr_t ReadMsr;

	ReadMsr = rdmsr(SMRR_PHYSBASE_MSR);
	rsc_tseg_memory.base = ReadMsr.lo & 0xFFFFF000;

	ReadMsr = rdmsr(SMRR_PHYSMASK_MSR);
	rsc_tseg_memory.length = (~(ReadMsr.lo & 0xFFFFF000) + 1);

	rsc_pm_io.base = (uint16_t)get_pmbase();

	// Local APIC. We assume that all thteads are programmed identically
	// despite that it is possible to have individual APIC address for
	// each of the threads. If this is the case this programming should
	// be corrected.
	ReadMsr = rdmsr(IA32_APIC_BASE_MSR_INDEX);
	rsc_apic_mmio.base = ((uint64_t)ReadMsr.lo & 0xFFFFF000) |
				((uint64_t)(ReadMsr.hi & 0x0000000F) << 32);

	// PCIEX BAR
	fixup_pciex_resource();

	Status |= add_pi_resource((void *)&rsc_tseg_memory, 1);
	Status |= add_pi_resource((void *)&rsc_spi_memory, 1);

	Status |= add_pi_resource((void *)&rsc_pm_io, 1);
	Status |= add_pi_resource((void *)&rsc_pcie_mmio, 1);
	Status |= add_pi_resource((void *)&rsc_apic_mmio, 1);
	Status |= add_pi_resource((void *)&rsc_sw_smi_trap_io, 1);

	Status |= add_pi_resource((void *)&rsc_lpc_bridge_pci, 1);

	if (Status != 0)
		printk(BIOS_DEBUG, "STM - Error in adding simple resources\n");
}

/*
 * Add MSR resources to BIOS resource database.
 */
static void add_msr_resources(void)
{
	uint32_t Status = 0;
	uint32_t Index;

	for (Index = 0; Index < ARRAY_SIZE(msr_table); Index++) {

		rsc_msr_tpl.msr_index = (uint32_t)msr_table[Index].msr_index;
		rsc_msr_tpl.read_mask = (uint64_t)msr_table[Index].read_mask;
		rsc_msr_tpl.write_mask = (uint64_t)msr_table[Index].write_mask;

		Status |= add_pi_resource((void *)&rsc_msr_tpl, 1);
	}

	if (Status != 0)
		printk(BIOS_DEBUG, "STM - Error in adding MSR resources\n");
}

/*
 * Add resources to BIOS resource database.
 */
void add_resources_cmd(void)
{

	add_simple_resources();

	add_msr_resources();
}
