soc/intel/elkhartlake: Introduce Intel PSE

The IntelĀ® Programmable Services Engine (IntelĀ® PSE) is a
dedicated offload engine for IoT functions powered by an ARM
Cortex-M7 microcontroller. It provides independent, low-DMIPS
computing and low-speed I/Os for IoT applications, plus
dedicated services for real-time computing and time-sensitive
synchronization.

The PSE hosts new functions, including remote out-of-band
device management, network proxy, embedded controller lite
and sensor hub.

This CL enables the user to provide the base address of the
PSE FW blob which will then be loaded by the FSP-S onto the
ARM controller. PSE FW will do the initialization work of
PSE controller and its peripherals. The loading of PSE FW
should have negligible impact on boot time unless PSE
controller could not locate the PSE FW and FSP will attempt to
redo PSE FW loading and wait for PSE handshake until it times
out. Once PSE controller locate the PSE FW, it will do
initialization concurrently by itself with coreboot booting.

It also adds PSE related FSP-S UPD settings which enable the
setup of peripheral ownership (assigned to the PSE or x86
subsystem) and interrupts. These assignments need to take
place at a given point in the boot process and cannot be
changed later.

To verify if PSE FW is loaded properly, the user could enable
PchPseShellEnabled flag and the log will be printed at PSE UART
2.

For further info please refer to doc #611825 (for HW overview)
and #614110 (for PSE EDS).

Signed-off-by: Lean Sheng Tan <lean.sheng.tan@intel.com>
Change-Id: Ifea08fb82fea18ef66bab04b3ce378e79a0afbf7
Reviewed-on: https://review.coreboot.org/c/coreboot/+/55367
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Werner Zeh <werner.zeh@siemens.com>
diff --git a/src/soc/intel/elkhartlake/Kconfig b/src/soc/intel/elkhartlake/Kconfig
index fce2f90..3780789 100644
--- a/src/soc/intel/elkhartlake/Kconfig
+++ b/src/soc/intel/elkhartlake/Kconfig
@@ -194,6 +194,44 @@
 	depends on FSP_USE_REPO
 	default "3rdparty/fsp/ElkhartLakeFspBinPkg/FspBin/FSPRel.bin"
 
+config PSE_ENABLE
+	bool "Enable PSE ARM controller"
+	help
+	  Enable PSE IP. The PSE describes the integrated programmable
+	  service engine that is designed together with x86 Atom cores
+	  as an Asymmetric Multi-Processing (AMP) system.
+
+config ADD_PSE_IMAGE_TO_CBFS
+	bool "Add PSE Firmware to CBFS"
+	depends on PSE_ENABLE
+	default n
+	help
+	  PSE FW binary is required to use PSE dedicated peripherals from
+	  x86 subsystem. Once PSE is enabled, the FW will be loaded from
+	  CBFS by FSP and executed.
+
+config PSE_IMAGE_FILE
+	string "PSE binary path and filename"
+	depends on ADD_PSE_IMAGE_TO_CBFS
+	help
+	  The path and filename of the PSE binary.
+
+config PSE_FW_FILE_SIZE_KIB
+	hex "Memory buffer (KiB) for PSE FW image"
+	depends on ADD_PSE_IMAGE_TO_CBFS
+	default 0x200
+	help
+	  It is recommended to allocate at least 512 KiB for PSE FW.
+
+config PSE_CONFIG_BUFFER_SIZE_KIB
+	hex "Memory buffer (KiB) for PSE config data"
+	depends on ADD_PSE_IMAGE_TO_CBFS
+	default 0x100
+	help
+	  It is recommended to allocate at least 256 KiB for PSE config
+	  data (FSP will append PSE config data to memory region right
+	  after PSE FW memory region).
+
 config SOC_INTEL_ELKHARTLAKE_DEBUG_CONSENT
 	int "Debug Consent for EHL"
 	# USB DBC is more common for developers so make this default to 3 if
diff --git a/src/soc/intel/elkhartlake/Makefile.inc b/src/soc/intel/elkhartlake/Makefile.inc
index 6a5d5c7..ce93e46 100644
--- a/src/soc/intel/elkhartlake/Makefile.inc
+++ b/src/soc/intel/elkhartlake/Makefile.inc
@@ -51,5 +51,18 @@
 
 # B0 stepping
 cpu_microcode_bins += 3rdparty/intel-microcode/intel-ucode/06-96-01
+cbfs-files-$(CONFIG_ADD_PSE_IMAGE_TO_CBFS) += pse.bin
+pse.bin-file := $(CONFIG_PSE_IMAGE_FILE)
+pse.bin-type := raw
+pse.bin-compression := lzma
+
+# Add a build time check if the PSE file size fits
+ifeq ($(CONFIG_ADD_PSE_IMAGE_TO_CBFS),y)
+ifeq ($(call int-gt,\
+	$(call file-size,$(CONFIG_PSE_IMAGE_FILE))\
+	$(shell printf "%d" $(call int-shift-left, $(CONFIG_PSE_FW_FILE_SIZE_KIB) 10))), 1)
+$(error PSE binary larger than CONFIG_PSE_FW_FILE_SIZE_KIB.)
+endif
+endif # CONFIG_ADD_PSE_IMAGE_TO_CBFS
 
 endif
diff --git a/src/soc/intel/elkhartlake/chip.h b/src/soc/intel/elkhartlake/chip.h
index 210e92c..a4e8cad 100644
--- a/src/soc/intel/elkhartlake/chip.h
+++ b/src/soc/intel/elkhartlake/chip.h
@@ -49,6 +49,18 @@
 };
 
 /*
+ * PSE native pins and ownership assignment:-
+ * 0: Disable/pins are not owned by PSE/host
+ * 1: Pins are muxed to PSE IP, the IO is owned by PSE
+ * 2: Pins are muxed to PSE IP, the IO is owned by host
+ */
+enum pse_device_ownership {
+	Device_Disabled,
+	PSE_Owned,
+	Host_Owned,
+};
+
+/*
  * Enable external V1P05 Rail in: BIT0:S0i1/S0i2,
  * BIT1:S0i3, BIT2:S3, BIT3:S4, BIT4:S5
  * However, EHL does not support S0i1 and S0i2,
@@ -453,6 +465,46 @@
 	bool PchTsnGbeSgmiiEnable;
 	/* PCH TSN GBE Multiple Virtual Channel: Disable (0) / Enable (1) */
 	bool PchTsnGbeMultiVcEnable;
+
+	/* PSE related */
+	/*
+	 * PSE (Intel Programmable Services Engine) native pins and ownership
+	 * assignment. If the device is configured as 'PSE owned', PSE will have
+	 * full control of specific device and it will be hidden from coreboot
+	 * and OS. If the device is configured as 'Host owned', the device will
+	 * be visible to coreboot and OS as a PCI device, while PSE will still
+	 * do some IP initialization and pin assignment works.
+	 *
+	 * PSE is still required during runtime to ensure any of PSE devices
+	 * works properly.
+	 */
+	enum pse_device_ownership PseDmaOwn[3];
+	enum pse_device_ownership PseUartOwn[6];
+	enum pse_device_ownership PseHsuartOwn[4];
+	enum pse_device_ownership PseQepOwn[4];
+	enum pse_device_ownership PseI2cOwn[8];
+	enum pse_device_ownership PseI2sOwn[2];
+	enum pse_device_ownership PseSpiOwn[4];
+	enum pse_device_ownership PseSpiCs0Own[4];
+	enum pse_device_ownership PseSpiCs1Own[4];
+	enum pse_device_ownership PseCanOwn[2];
+	enum pse_device_ownership PsePwmOwn;
+	enum pse_device_ownership PseAdcOwn;
+	/* PSE devices sideband interrupt: Disable (0) / Enable (1) */
+	bool PseDmaSbIntEn[3];
+	bool PseUartSbIntEn[6];
+	bool PseQepSbIntEn[4];
+	bool PseI2cSbIntEn[8];
+	bool PseI2sSbIntEn[2];
+	bool PseSpiSbIntEn[4];
+	bool PseCanSbIntEn[2];
+	bool PseLh2PseSbIntEn;
+	bool PsePwmSbIntEn;
+	bool PseAdcSbIntEn;
+	/* PSE PWM native function: Disable (0) / Enable (1) */
+	bool PsePwmPinEn[16];
+	/* PSE Console Shell */
+	bool PseShellEn;
 };
 
 typedef struct soc_intel_elkhartlake_config config_t;
diff --git a/src/soc/intel/elkhartlake/fsp_params.c b/src/soc/intel/elkhartlake/fsp_params.c
index ff7222d..fc5ea1c 100644
--- a/src/soc/intel/elkhartlake/fsp_params.c
+++ b/src/soc/intel/elkhartlake/fsp_params.c
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 #include <assert.h>
+#include <cbfs.h>
 #include <console/console.h>
 #include <device/device.h>
 #include <fsp/api.h>
@@ -78,6 +79,70 @@
 	s_cfg->FivrSpreadSpectrum = config->fivr.spread_spectrum;
 }
 
+static void fill_fsps_pse_params(FSP_S_CONFIG *params,
+		const struct soc_intel_elkhartlake_config *config)
+{
+	static char psefwbuf[(CONFIG_PSE_FW_FILE_SIZE_KIB +
+		CONFIG_PSE_CONFIG_BUFFER_SIZE_KIB) * KiB];
+	uint32_t pse_fw_base;
+	size_t psefwsize = cbfs_load("pse.bin", psefwbuf, sizeof(psefwbuf));
+	if (psefwsize > 0) {
+		pse_fw_base = (uintptr_t)&psefwbuf;
+		params->SiipRegionBase = pse_fw_base;
+		params->SiipRegionSize = psefwsize;
+		printk(BIOS_DEBUG, "PSE base: %08x size: %08zx\n", pse_fw_base, psefwsize);
+
+		/* Configure PSE peripherals */
+		FSP_ARRAY_LOAD(params->PchPseDmaEnable, config->PseDmaOwn);
+		FSP_ARRAY_LOAD(params->PchPseDmaSbInterruptEnable, config->PseDmaSbIntEn);
+		FSP_ARRAY_LOAD(params->PchPseUartEnable, config->PseUartOwn);
+		FSP_ARRAY_LOAD(params->PchPseUartSbInterruptEnable, config->PseUartSbIntEn);
+		FSP_ARRAY_LOAD(params->PchPseHsuartEnable, config->PseHsuartOwn);
+		FSP_ARRAY_LOAD(params->PchPseQepEnable, config->PseQepOwn);
+		FSP_ARRAY_LOAD(params->PchPseQepSbInterruptEnable, config->PseQepSbIntEn);
+		FSP_ARRAY_LOAD(params->PchPseI2cEnable, config->PseI2cOwn);
+		FSP_ARRAY_LOAD(params->PchPseI2cSbInterruptEnable, config->PseI2cSbIntEn);
+		FSP_ARRAY_LOAD(params->PchPseI2sEnable, config->PseI2sOwn);
+		FSP_ARRAY_LOAD(params->PchPseI2sSbInterruptEnable, config->PseI2sSbIntEn);
+		FSP_ARRAY_LOAD(params->PchPseSpiEnable, config->PseSpiOwn);
+		FSP_ARRAY_LOAD(params->PchPseSpiSbInterruptEnable, config->PseSpiSbIntEn);
+		FSP_ARRAY_LOAD(params->PchPseSpiCs0Enable, config->PseSpiCs0Own);
+		FSP_ARRAY_LOAD(params->PchPseSpiCs1Enable, config->PseSpiCs1Own);
+		FSP_ARRAY_LOAD(params->PchPseCanEnable, config->PseCanOwn);
+		FSP_ARRAY_LOAD(params->PchPseCanSbInterruptEnable, config->PseCanSbIntEn);
+		params->PchPsePwmEnable = config->PsePwmOwn;
+		params->PchPsePwmSbInterruptEnable = config->PsePwmSbIntEn;
+		FSP_ARRAY_LOAD(params->PchPsePwmPinEnable, config->PsePwmPinEn);
+		params->PchPseAdcEnable = config->PseAdcOwn;
+		params->PchPseAdcSbInterruptEnable = config->PseAdcSbIntEn;
+		params->PchPseLh2PseSbInterruptEnable = config->PseLh2PseSbIntEn;
+		params->PchPseShellEnabled = config->PseShellEn;
+
+		/*
+		 * As a minimum requirement for PSE initialization, the configuration
+		 * of devices below are required as shown.
+		 * TODO: Help needed to find a better way to handle this part of code
+		 * as the settings from devicetree are overwritten here.
+		 *
+		 * Set the ownership of these devices to PSE. These are hardcoded for now,
+		 * if the PSE should be opened one day (hopefully), this can be handled
+		 * much better.
+		 */
+		params->PchPseDmaEnable[0] = PSE_Owned;
+		params->PchPseUartEnable[2] = PSE_Owned;
+		params->PchPseHsuartEnable[2] = PSE_Owned;
+		params->PchPseI2cEnable[2] = PSE_Owned;
+		params->PchPseTimedGpioEnable[0] = PSE_Owned;
+		params->PchPseTimedGpioEnable[1] = PSE_Owned;
+		/* Disable PSE DMA Sideband Interrupt for DMA 0 */
+		params->PchPseDmaSbInterruptEnable[0] = 0;
+		/* Set the log output to PSE UART 2 */
+		params->PchPseLogOutputChannel = 3;
+	} else {
+		die("PSE enabled but PSE FW not available!\n");
+	}
+}
+
 static void parse_devicetree(FSP_S_CONFIG *params)
 {
 	const struct soc_intel_elkhartlake_config *config = config_of_soc();
@@ -366,6 +431,10 @@
 		}
 	}
 
+	/* PSE (Intel Programmable Services Engine) config */
+	if (CONFIG(PSE_ENABLE) && cbfs_file_exists("pse.bin"))
+		fill_fsps_pse_params(params, config);
+
 	/* Override/Fill FSP Silicon Param for mainboard */
 	mainboard_silicon_init_params(params);
 }
diff --git a/src/soc/intel/elkhartlake/romstage/fsp_params.c b/src/soc/intel/elkhartlake/romstage/fsp_params.c
index 402b134..b8aaca7 100644
--- a/src/soc/intel/elkhartlake/romstage/fsp_params.c
+++ b/src/soc/intel/elkhartlake/romstage/fsp_params.c
@@ -123,7 +123,7 @@
 		}
 	}
 	/* PSE (Intel Programmable Services Engine) switch */
-	m_cfg->PchPseEnable = 0;
+	m_cfg->PchPseEnable = CONFIG(PSE_ENABLE) && cbfs_file_exists("pse.bin");
 }
 
 void platform_fsp_memory_init_params_cb(FSPM_UPD *mupd, uint32_t version)