soc/intel/common/block/acpi: Add LPM requirements support to PEPD _DSM

This patch adds support for the S0ix UUID in the Intel Power Engine _DSM
method. This allows the ACPI tables to expose device/IP power states
requirements for different system low power states

BUG=b:185437326
TEST=Along with following patch on brya0 after resume from s0ix,
cat /sys/kernel/debug/pmc_core/substate_requirements
                       Element |    S0i2.0 |    S0i3.0 |    Status |
               USB2PLL_OFF_STS |  Required |  Required |       Yes |
   PCIe/USB3.1_Gen2PLL_OFF_STS |  Required |  Required |       Yes |
          PCIe_Gen3PLL_OFF_STS |  Required |  Required |       Yes |
               OPIOPLL_OFF_STS |  Required |  Required |       Yes |
                 OCPLL_OFF_STS |  Required |  Required |       Yes |
               MainPLL_OFF_STS |           |  Required |           |
               MIPIPLL_OFF_STS |  Required |  Required |       Yes |
         Fast_XTAL_Osc_OFF_STS |           |  Required |           |
           AC_Ring_Osc_OFF_STS |  Required |  Required |       Yes |
               SATAPLL_OFF_STS |  Required |  Required |       Yes |
          XTAL_USB2PLL_OFF_STS |           |  Required |       Yes |
                   CSME_PG_STS |  Required |  Required |       Yes |
                   SATA_PG_STS |  Required |  Required |       Yes |
                   xHCI_PG_STS |  Required |  Required |       Yes |
                  UFSX2_PG_STS |  Required |  Required |       Yes |
                    OTG_PG_STS |  Required |  Required |       Yes |
                    SPA_PG_STS |  Required |  Required |       Yes |
                    SPB_PG_STS |  Required |  Required |       Yes |
                    SPC_PG_STS |  Required |  Required |       Yes |
                   THC0_PG_STS |  Required |  Required |       Yes |
                   THC1_PG_STS |  Required |  Required |       Yes |
                 GBETSN_PG_STS |  Required |  Required |       Yes |
                    GBE_PG_STS |  Required |  Required |       Yes |
                   LPSS_PG_STS |  Required |  Required |       Yes |
                   ADSP_D3_STS |           |  Required |       Yes |
                  xHCI0_D3_STS |  Required |  Required |       Yes |
                  xDCI1_D3_STS |  Required |  Required |       Yes |
                     IS_D3_STS |  Required |  Required |       Yes |
                GBE_TSN_D3_STS |  Required |  Required |       Yes |
             CPU_C10_REQ_STS_0 |  Required |  Required |       Yes |
                CNVI_REQ_STS_6 |           |  Required |       Yes |
                 ISH_REQ_STS_7 |           |  Required |       Yes |
       MPHY_Core_DL_REQ_STS_16 |  Required |  Required |       Yes |
      Break-even_En_REQ_STS_17 |  Required |  Required |       Yes |
       Auto-demo_En_REQ_STS_18 |  Required |  Required |       Yes |
    Int_Timer_SS_Wake0_Pol_STS |  Required |  Required |           |
    Int_Timer_SS_Wake1_Pol_STS |  Required |  Required |           |
    Int_Timer_SS_Wake2_Pol_STS |  Required |  Required |           |
    Int_Timer_SS_Wake3_Pol_STS |  Required |  Required |           |
    Int_Timer_SS_Wake4_Pol_STS |  Required |  Required |           |
    Int_Timer_SS_Wake5_Pol_STS |  Required |  Required |           |

Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Change-Id: I542290bd5490aa6580a5ae2b266da3d78bc17e6b
Reviewed-on: https://review.coreboot.org/c/coreboot/+/56005
Reviewed-by: Furquan Shaikh <furquan@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/soc/intel/common/block/acpi/pep.c b/src/soc/intel/common/block/acpi/pep.c
index 9b15bf3..b8e7e0d 100644
--- a/src/soc/intel/common/block/acpi/pep.c
+++ b/src/soc/intel/common/block/acpi/pep.c
@@ -3,9 +3,12 @@
 #include <acpi/acpigen.h>
 #include <console/console.h>
 #include <intelblocks/acpi.h>
+#include <intelblocks/pmc_ipc.h>
+#include <stdlib.h>
 #include <types.h>
 
 #define LPI_S0_HELPER_UUID		"c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
+#define PEP_S0IX_UUID			"57a6512e-3979-4e9d-9708-ff13b2508972"
 #define SYSTEM_POWER_MANAGEMENT_HID	"INT33A1"
 #define SYSTEM_POWER_MANAGEMENT_CID	"PNP0D80"
 #define EC_S0IX_HOOK			"\\_SB.PCI0.LPCB.EC0.S0IX"
@@ -16,6 +19,54 @@
 #define MIN_DEVICE_STATE		ACPI_DEVICE_SLEEP_D0
 #define PEPD_SCOPE			"\\_SB.PCI0"
 
+struct reg_info {
+	uint8_t *addr;
+	size_t buffer_size;
+};
+
+static void read_pmc_lpm_requirements(const struct soc_pmc_lpm *lpm,
+				      struct reg_info *info)
+{
+	if (!CONFIG(SOC_INTEL_COMMON_BLOCK_ACPI_PEP_LPM_REQ) || !lpm) {
+		memset(info, 0, sizeof(*info));
+		return;
+	}
+
+	const size_t register_count = lpm->num_substates * lpm->num_req_regs;
+	uint32_t *reg = calloc(register_count, sizeof(uint32_t));
+
+	/* Read the various LPM state requirement registers from the PMC */
+	for (size_t i = 0; i < lpm->num_substates; i++) {
+		if (!(lpm->lpm_enable_mask & BIT(i)))
+			continue;
+
+		for (size_t j = 0; j < lpm->num_req_regs; j++) {
+			const uint32_t offset = lpm->lpm_ipc_offset +
+				i * lpm->req_reg_stride +
+				j * sizeof(uint32_t);
+			const uint32_t cmd_reg = pmc_make_ipc_cmd(PMC_IPC_CMD_RD_PMC_REG, 0, 0);
+			struct pmc_ipc_buffer req = {.buf[0] = offset};
+			struct pmc_ipc_buffer res = {};
+
+			enum cb_err result = pmc_send_ipc_cmd(cmd_reg, &req, &res);
+			if (result != CB_SUCCESS) {
+				printk(BIOS_ERR, "Failed to retrieve LPM substate registers"
+				       "from LPM, substate %lu, reg %lu\n", i, j);
+				free(reg);
+				return;
+			}
+
+			uint32_t *ptr = reg + i * lpm->num_req_regs + j;
+			*ptr = res.buf[0];
+		}
+	}
+
+	if (info) {
+		info->addr = (uint8_t *)reg;
+		info->buffer_size = register_count * sizeof(uint32_t);
+	}
+}
+
 /*
  * For now there is only one disabled non-existent device, because Windows
  * expects at least one device and crashes without it with a bluescreen
@@ -104,8 +155,31 @@
 	lpi_s0ix_exit,		/* s0ix exit */
 };
 
-void generate_acpi_power_engine(void)
+static void pep_s0ix_return_lpm_requirements(void *arg)
 {
+	if (!CONFIG(SOC_INTEL_COMMON_BLOCK_ACPI_PEP_LPM_REQ)) {
+		acpigen_write_return_singleton_buffer(0x0);
+		return;
+	}
+
+	struct reg_info *info = (struct reg_info *)arg;
+	acpigen_write_return_byte_buffer(info->addr, info->buffer_size);
+}
+
+static void (*pep_s0ix[])(void *) = {
+	NULL,					/* enumerate functions (autogenerated) */
+	pep_s0ix_return_lpm_requirements,	/* Return LPM requirements */
+};
+
+void generate_acpi_power_engine_with_lpm(const struct soc_pmc_lpm *lpm)
+{
+	struct reg_info info;
+	size_t uuid_count = 1;
+	struct dsm_uuid ids[] = {
+		DSM_UUID(LPI_S0_HELPER_UUID, lpi_s0_helpers, ARRAY_SIZE(lpi_s0_helpers), NULL),
+		DSM_UUID(PEP_S0IX_UUID, pep_s0ix, ARRAY_SIZE(pep_s0ix), &info),
+	};
+
 	acpigen_write_scope(PEPD_SCOPE);
 	acpigen_write_device("PEPD");
 
@@ -113,10 +187,19 @@
 	acpigen_write_name("_CID");
 	acpigen_emit_eisaid(SYSTEM_POWER_MANAGEMENT_CID);
 
-	acpigen_write_dsm(LPI_S0_HELPER_UUID, lpi_s0_helpers, ARRAY_SIZE(lpi_s0_helpers), NULL);
+	read_pmc_lpm_requirements(lpm, &info);
+	if (info.buffer_size)
+		uuid_count++;
 
+	acpigen_write_dsm_uuid_arr(ids, uuid_count);
 	acpigen_write_device_end();
 	acpigen_write_scope_end();
 
+	free(info.addr);
 	printk(BIOS_INFO, PEPD_SCOPE ".PEPD: Intel Power Engine Plug-in\n");
 }
+
+void generate_acpi_power_engine(void)
+{
+	generate_acpi_power_engine_with_lpm(NULL);
+}