acpi,soc/intel/common: add support for Intel Low Power Idle Table

Add support for the Intel LPIT table to support reading Low Power Idle
Residency counters by the OS. On platforms supporting S0ix sleep states
there can be two types of residencies:

  * CPU package PC10 residency counter (read from MSR via FFH interface)
  * PCH SLP_S0 assertion residency counter (read via memory mapped
    interface)

With presence of one or both of these counters in the LPIT table, Linux
dynamically adds the corresponding attributes to the cpuidle sysfs
interface, that can be used to read the residency timers:

  * /sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us
  * /sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us

The code in src/acpi implements generic LPIT support. Each SoC or
platform has to implement `acpi_fill_lpit` to fill the table with
platform-specific LPI state entries. This is done in this change for
soc/intel/common, while being added as its own compilation unit, so SoCs
not yet using common acpi code (like Skylake) can use it, too.

Reference:
https://uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf

Test: Linux adds the cpuidle sysfs interface; Windows with s0ix_enable=1
      boots without crashing with an INTERNAL_POWER_ERROR.

- Windows and Linux tested on google/akemi together with CB:49046
- Linux tested on clevo/cml-u, supermicro/x11ssmf together with CB:49046

Change-Id: I816888e8788e2f04c89f20d6ea1654d2f35cf18e
Tested-by: Matt DeVillier <matt.devillier@gmail.com>
Tested-by: Michael Niewöhner <foss@mniewoehner.de>
Signed-off-by: Shaunak Saha <shaunak.saha@intel.com>
Signed-off-by: Michael Niewöhner <foss@mniewoehner.de>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/49045
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c
index a16800b..ee30391 100644
--- a/src/acpi/acpi.c
+++ b/src/acpi/acpi.c
@@ -1293,6 +1293,44 @@
 	    acpi_checksum((void *) fadt, header->length);
 }
 
+void acpi_create_lpit(acpi_lpit_t *lpit)
+{
+	acpi_header_t *header = &(lpit->header);
+	unsigned long current = (unsigned long)lpit + sizeof(acpi_lpit_t);
+
+	memset((void *)lpit, 0, sizeof(acpi_lpit_t));
+
+	if (!header)
+		return;
+
+	/* Fill out header fields. */
+	memcpy(header->signature, "LPIT", 4);
+	memcpy(header->oem_id, OEM_ID, 6);
+	memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8);
+	memcpy(header->asl_compiler_id, ASLC, 4);
+
+	header->asl_compiler_revision = asl_revision;
+	header->revision = get_acpi_table_revision(LPIT);
+	header->oem_revision = 42;
+	header->length = sizeof(acpi_lpit_t);
+
+	current = acpi_fill_lpit(current);
+
+	/* (Re)calculate length and checksum. */
+	header->length = current - (unsigned long)lpit;
+	header->checksum = acpi_checksum((void *)lpit, header->length);
+}
+
+unsigned long acpi_create_lpi_desc_ncst(acpi_lpi_desc_ncst_t *lpi_desc, uint16_t uid)
+{
+	memset(lpi_desc, 0, sizeof(acpi_lpi_desc_ncst_t));
+	lpi_desc->header.length = sizeof(acpi_lpi_desc_ncst_t);
+	lpi_desc->header.type = ACPI_LPI_DESC_TYPE_NATIVE_CSTATE;
+	lpi_desc->header.uid = uid;
+
+	return lpi_desc->header.length;
+}
+
 unsigned long __weak fw_cfg_acpi_tables(unsigned long start)
 {
 	return 0;
@@ -1313,6 +1351,7 @@
 	acpi_tcpa_t *tcpa;
 	acpi_tpm2_t *tpm2;
 	acpi_madt_t *madt;
+	acpi_lpit_t *lpit;
 	struct device *dev;
 	unsigned long fw;
 	size_t slic_size, dsdt_size;
@@ -1507,6 +1546,18 @@
 		}
 	}
 
+	if (CONFIG(ACPI_LPIT)) {
+		printk(BIOS_DEBUG, "ACPI:     * LPIT\n");
+
+		lpit = (acpi_lpit_t *)current;
+		acpi_create_lpit(lpit);
+		if (lpit->header.length >= sizeof(acpi_lpit_t)) {
+			current += lpit->header.length;
+			current = acpi_align_current(current);
+			acpi_add_table(rsdp, lpit);
+		}
+	}
+
 	printk(BIOS_DEBUG, "ACPI:    * MADT\n");
 
 	madt = (acpi_madt_t *) current;
@@ -1515,6 +1566,7 @@
 		current += madt->header.length;
 		acpi_add_table(rsdp, madt);
 	}
+
 	current = acpi_align_current(current);
 
 	printk(BIOS_DEBUG, "current = %lx\n", current);
@@ -1665,6 +1717,8 @@
 		return 1;
 	case CRAT:
 		return 1;
+	case LPIT: /* ACPI 5.1 up to 6.3: 0 */
+		return 0;
 	default:
 		return -1;
 	}