Browse Source

Merge https://gitlab.denx.de/u-boot/custodians/u-boot-x86

- New timer API to allow delays with a 32-bit microsecond timer
- Add dynamic ACPI structs (DSDT/SSDT) generations to the DM core
- x86: Enable ACPI table generation by default
- x86: Enable the copy framebuffer on Coral
- x86: A few fixes to FSP2 with ApolloLake
- x86: Drop setup_pcat_compatibility()
- x86: Primary-to-Sideband Bus minor fixes
Tom Rini 3 years ago
parent
commit
7c3cc6f106
100 changed files with 6793 additions and 255 deletions
  1. 1 0
      arch/Kconfig
  2. 12 2
      arch/sandbox/dts/test.dts
  3. 1 0
      arch/x86/Kconfig
  4. 26 0
      arch/x86/cpu/acpi_gpe.c
  5. 7 6
      arch/x86/cpu/apollolake/cpu_spl.c
  6. 14 4
      arch/x86/cpu/apollolake/fsp_m.c
  7. 26 40
      arch/x86/cpu/apollolake/fsp_s.c
  8. 3 3
      arch/x86/cpu/baytrail/acpi.c
  9. 2 3
      arch/x86/cpu/broadwell/power_state.c
  10. 2 2
      arch/x86/cpu/coreboot/coreboot.c
  11. 7 1
      arch/x86/cpu/coreboot/tables.c
  12. 38 26
      arch/x86/cpu/cpu.c
  13. 1 1
      arch/x86/cpu/efi/app.c
  14. 6 1
      arch/x86/cpu/i386/cpu.c
  15. 23 2
      arch/x86/cpu/intel_common/itss.c
  16. 44 0
      arch/x86/cpu/intel_common/p2sb.c
  17. 3 1
      arch/x86/cpu/quark/acpi.c
  18. 1 1
      arch/x86/cpu/quark/quark.c
  19. 1 0
      arch/x86/cpu/start.S
  20. 3 1
      arch/x86/cpu/tangier/acpi.c
  21. 1 0
      arch/x86/dts/chromebook_coral.dts
  22. 314 0
      arch/x86/include/asm/acpi_nhlt.h
  23. 9 1
      arch/x86/include/asm/acpi_table.h
  24. 2 1
      arch/x86/include/asm/fsp2/fsp_internal.h
  25. 2 2
      arch/x86/include/asm/global_data.h
  26. 15 4
      arch/x86/include/asm/intel_pinctrl.h
  27. 1 1
      arch/x86/include/asm/itss.h
  28. 0 2
      arch/x86/include/asm/u-boot-x86.h
  29. 1 0
      arch/x86/lib/Makefile
  30. 482 0
      arch/x86/lib/acpi_nhlt.c
  31. 72 15
      arch/x86/lib/acpi_table.c
  32. 3 3
      arch/x86/lib/coreboot_table.c
  33. 1 3
      arch/x86/lib/fsp/fsp_common.c
  34. 15 11
      arch/x86/lib/fsp/fsp_dram.c
  35. 9 7
      arch/x86/lib/fsp1/fsp_common.c
  36. 3 4
      arch/x86/lib/fsp2/fsp_dram.c
  37. 19 5
      arch/x86/lib/fsp2/fsp_meminit.c
  38. 1 0
      arch/x86/lib/fsp2/fsp_silicon_init.c
  39. 28 10
      arch/x86/lib/tables.c
  40. 0 10
      arch/x86/lib/zimage.c
  41. 13 2
      cmd/acpi.c
  42. 1 0
      configs/chromebook_coral_defconfig
  43. 4 0
      configs/sandbox_defconfig
  44. 9 0
      doc/device-tree-bindings/chosen.txt
  45. 36 0
      doc/device-tree-bindings/device.txt
  46. 4 0
      doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-m.txt
  47. 113 0
      doc/device-tree-bindings/sound/da7219.txt
  48. 22 0
      doc/device-tree-bindings/sound/max98357a.txt
  49. 1 1
      drivers/core/Kconfig
  50. 259 4
      drivers/core/acpi.c
  51. 13 0
      drivers/core/root.c
  52. 22 0
      drivers/gpio/gpio-uclass.c
  53. 45 4
      drivers/gpio/intel_gpio.c
  54. 77 0
      drivers/gpio/sandbox.c
  55. 31 5
      drivers/i2c/designware_i2c.c
  56. 15 0
      drivers/i2c/designware_i2c.h
  57. 95 1
      drivers/i2c/designware_i2c_pci.c
  58. 2 2
      drivers/i2c/i2c-uclass.c
  59. 1 0
      drivers/i2c/sandbox_i2c.c
  60. 6 6
      drivers/misc/Kconfig
  61. 17 3
      drivers/misc/irq-uclass.c
  62. 16 0
      drivers/misc/irq_sandbox.c
  63. 18 8
      drivers/misc/p2sb-uclass.c
  64. 77 1
      drivers/mmc/pci_mmc.c
  65. 12 0
      drivers/pinctrl/intel/Kconfig
  66. 15 6
      drivers/pinctrl/intel/pinctrl.c
  67. 0 4
      drivers/pinctrl/intel/pinctrl_apl.c
  68. 0 9
      drivers/power/acpi_pmc/acpi-pmc-uclass.c
  69. 13 0
      drivers/rtc/sandbox_rtc.c
  70. 18 0
      drivers/sound/Kconfig
  71. 2 0
      drivers/sound/Makefile
  72. 190 0
      drivers/sound/da7219.c
  73. 161 0
      drivers/sound/max98357a.c
  74. 1 0
      drivers/spi/sandbox_spi.c
  75. 406 0
      include/acpi/acpi_device.h
  76. 287 0
      include/acpi/acpi_dp.h
  77. 566 0
      include/acpi/acpigen.h
  78. 27 0
      include/asm-generic/gpio.h
  79. 30 0
      include/binman.h
  80. 118 1
      include/dm/acpi.h
  81. 1 1
      include/dm/device.h
  82. 14 0
      include/dt-bindings/interrupt-controller/x86-irq.h
  83. 24 0
      include/dt-bindings/sound/nhlt.h
  84. 43 0
      include/irq.h
  85. 33 1
      include/p2sb.h
  86. 14 0
      include/power/acpi_pmc.h
  87. 2 2
      include/spi.h
  88. 17 0
      include/test/ut.h
  89. 11 0
      include/time.h
  90. 3 0
      lib/acpi/Makefile
  91. 823 0
      lib/acpi/acpi_device.c
  92. 402 0
      lib/acpi/acpi_dp.c
  93. 1 0
      lib/acpi/acpi_table.c
  94. 616 0
      lib/acpi/acpigen.c
  95. 53 6
      lib/binman.c
  96. 5 0
      lib/time.c
  97. 2 0
      test/dm/Makefile
  98. 263 15
      test/dm/acpi.c
  99. 32 0
      test/dm/acpi.h
  100. 492 0
      test/dm/acpi_dp.c

+ 1 - 0
arch/Kconfig

@@ -190,6 +190,7 @@ config X86
 	imply PCH
 	imply RTC_MC146818
 	imply IRQ
+	imply ACPIGEN if !QEMU
 
 	# Thing to enable for when SPL/TPL are enabled: SPL
 	imply SPL_DM

+ 12 - 2
arch/sandbox/dts/test.dts

@@ -111,7 +111,9 @@
 		uint-value = <(-1234)>;
 		int64-value = /bits/ 64 <0x1111222233334444>;
 		int-array = <5678 9123 4567>;
+		str-value = "test string";
 		interrupts-extended = <&irq 3 0>;
+		acpi,name = "GHIJ";
 	};
 
 	junk {
@@ -253,12 +255,19 @@
 		compatible = "denx,u-boot-devres-test";
 	};
 
-	acpi-test {
+	acpi_test1: acpi-test {
 		compatible = "denx,u-boot-acpi-test";
+		acpi-ssdt-test-data = "ab";
+		acpi-dsdt-test-data = "hi";
+		child {
+			compatible = "denx,u-boot-acpi-test";
+		};
 	};
 
-	acpi-test2 {
+	acpi_test2: acpi-test2 {
 		compatible = "denx,u-boot-acpi-test";
+		acpi-ssdt-test-data = "cd";
+		acpi-dsdt-test-data = "jk";
 	};
 
 	clocks {
@@ -918,6 +927,7 @@
 		setting = "sunrise ohoka";
 		other-node = "/some-bus/c-test@5";
 		int-values = <0x1937 72993>;
+		u-boot,acpi-ssdt-order = <&acpi_test2 &acpi_test1>;
 		chosen-test {
 			compatible = "denx,u-boot-fdt-test";
 			reg = <9 1>;

+ 1 - 0
arch/x86/Kconfig

@@ -717,6 +717,7 @@ config HAVE_ITSS
 
 config HAVE_P2SB
 	bool "Enable P2SB"
+	depends on P2SB
 	help
 	  Select this to include the driver for the Primary to
 	  Sideband Bridge (P2SB) which is found on several Intel

+ 26 - 0
arch/x86/cpu/acpi_gpe.c

@@ -8,7 +8,10 @@
 #include <dm.h>
 #include <irq.h>
 #include <log.h>
+#include <acpi/acpi_device.h>
 #include <asm/io.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/x86-irq.h>
 
 /**
  * struct acpi_gpe_priv - private driver information
@@ -62,13 +65,36 @@ static int acpi_gpe_ofdata_to_platdata(struct udevice *dev)
 static int acpi_gpe_of_xlate(struct irq *irq, struct ofnode_phandle_args *args)
 {
 	irq->id = args->args[0];
+	irq->flags = args->args[1];
 
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int acpi_gpe_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq)
+{
+	memset(acpi_irq, '\0', sizeof(*acpi_irq));
+	acpi_irq->pin = irq->id;
+	acpi_irq->mode = irq->flags & IRQ_TYPE_EDGE_BOTH ?
+		ACPI_IRQ_EDGE_TRIGGERED : ACPI_IRQ_LEVEL_TRIGGERED;
+	acpi_irq->polarity = irq->flags &
+		 (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW) ?
+		 ACPI_IRQ_ACTIVE_LOW : ACPI_IRQ_ACTIVE_HIGH;
+	acpi_irq->shared = irq->flags & X86_IRQ_TYPE_SHARED ?
+		ACPI_IRQ_SHARED : ACPI_IRQ_EXCLUSIVE;
+	acpi_irq->wake = irq->flags & X86_IRQ_TYPE_WAKE ? ACPI_IRQ_WAKE :
+		ACPI_IRQ_NO_WAKE;
+
+	return 0;
+}
+#endif
+
 static const struct irq_ops acpi_gpe_ops = {
 	.read_and_clear		= acpi_gpe_read_and_clear,
 	.of_xlate		= acpi_gpe_of_xlate,
+#if CONFIG_IS_ENABLED(ACPIGEN)
+	.get_acpi		= acpi_gpe_get_acpi,
+#endif
 };
 
 static const struct udevice_id acpi_gpe_ids[] = {

+ 7 - 6
arch/x86/cpu/apollolake/cpu_spl.c

@@ -247,12 +247,13 @@ static int arch_cpu_init_spl(void)
 	ret = pmc_init(pmc);
 	if (ret < 0)
 		return log_msg_ret("Could not init PMC", ret);
-#ifdef CONFIG_HAVE_ACPI_RESUME
-	ret = pmc_prev_sleep_state(pmc);
-	if (ret < 0)
-		return log_msg_ret("Could not get PMC sleep state", ret);
-	gd->arch.prev_sleep_state = ret;
-#endif
+	if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
+		ret = pmc_prev_sleep_state(pmc);
+		if (ret < 0)
+			return log_msg_ret("Could not get PMC sleep state",
+					   ret);
+		gd->arch.prev_sleep_state = ret;
+	}
 
 	return 0;
 }

+ 14 - 4
arch/x86/cpu/apollolake/fsp_m.c

@@ -16,19 +16,29 @@ int fspm_update_config(struct udevice *dev, struct fspm_upd *upd)
 {
 	struct fsp_m_config *cfg = &upd->config;
 	struct fspm_arch_upd *arch = &upd->arch;
+	int cache_ret = 0;
 	ofnode node;
+	int ret;
 
 	arch->nvs_buffer_ptr = NULL;
-	prepare_mrc_cache(upd);
-	arch->stack_base = (void *)0xfef96000;
+	cache_ret = prepare_mrc_cache(upd);
+	if (cache_ret && cache_ret != -ENOENT)
+		return log_msg_ret("mrc", cache_ret);
+	arch->stack_base = (void *)(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE -
+		 arch->stack_size);
 	arch->boot_loader_tolum_size = 0;
-	arch->boot_mode = FSP_BOOT_WITH_FULL_CONFIGURATION;
+	arch->boot_mode = cache_ret ? FSP_BOOT_WITH_FULL_CONFIGURATION :
+		FSP_BOOT_ASSUMING_NO_CONFIGURATION_CHANGES;
 
 	node = dev_ofnode(dev);
 	if (!ofnode_valid(node))
 		return log_msg_ret("fsp-m settings", -ENOENT);
 
-	return fsp_m_update_config_from_dtb(node, cfg);
+	ret = fsp_m_update_config_from_dtb(node, cfg);
+	if (ret)
+		return log_msg_ret("dtb", cache_ret);
+
+	return cache_ret;
 }
 
 /*

+ 26 - 40
arch/x86/cpu/apollolake/fsp_s.c

@@ -12,6 +12,7 @@
 #include <irq.h>
 #include <log.h>
 #include <malloc.h>
+#include <p2sb.h>
 #include <acpi/acpi_s3.h>
 #include <asm/intel_pinctrl.h>
 #include <asm/io.h>
@@ -21,10 +22,11 @@
 #include <asm/pci.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/systemagent.h>
+#include <asm/arch/fsp_bindings.h>
 #include <asm/arch/fsp/fsp_configs.h>
 #include <asm/arch/fsp/fsp_s_upd.h>
+#include <dm/uclass-internal.h>
 #include <linux/bitops.h>
-#include <asm/arch/fsp_bindings.h>
 
 #define PCH_P2SB_E0		0xe0
 #define HIDE_BIT		BIT(0)
@@ -36,29 +38,20 @@ int fsps_update_config(struct udevice *dev, ulong rom_offset,
 	ofnode node;
 
 	if (IS_ENABLED(CONFIG_HAVE_VBT)) {
-		struct binman_entry vbt;
-		void *vbt_buf;
+		void *buf;
 		int ret;
 
-		ret = binman_entry_find("intel-vbt", &vbt);
+		ret = binman_entry_map(ofnode_null(), "intel-vbt", &buf, NULL);
 		if (ret)
 			return log_msg_ret("Cannot find VBT", ret);
-		vbt.image_pos += rom_offset;
-		vbt_buf = malloc(vbt.size);
-		if (!vbt_buf)
-			return log_msg_ret("Alloc VBT", -ENOMEM);
+		if (*(u32 *)buf != VBT_SIGNATURE)
+			return log_msg_ret("VBT signature", -EINVAL);
 
 		/*
 		 * Load VBT before devicetree-specific config. This only
 		 * supports memory-mapped SPI at present.
 		 */
-		bootstage_start(BOOTSTAGE_ID_ACCUM_MMAP_SPI, "mmap_spi");
-		memcpy(vbt_buf, (void *)vbt.image_pos, vbt.size);
-		bootstage_accum(BOOTSTAGE_ID_ACCUM_MMAP_SPI);
-		if (*(u32 *)vbt_buf != VBT_SIGNATURE)
-			return log_msg_ret("VBT signature", -EINVAL);
-
-		cfg->graphics_config_ptr = (ulong)vbt_buf;
+		cfg->graphics_config_ptr = (ulong)buf;
 	}
 
 	node = dev_read_subnode(dev, "fsp-s");
@@ -68,12 +61,6 @@ int fsps_update_config(struct udevice *dev, ulong rom_offset,
 	return fsp_s_update_config_from_dtb(node, cfg);
 }
 
-static void p2sb_set_hide_bit(pci_dev_t dev, int hide)
-{
-	pci_x86_clrset_config(dev, PCH_P2SB_E0 + 1, HIDE_BIT,
-			      hide ? HIDE_BIT : 0, PCI_SIZE_8);
-}
-
 /* Configure package power limits */
 static int set_power_limits(struct udevice *dev)
 {
@@ -146,15 +133,15 @@ static int set_power_limits(struct udevice *dev)
 
 int p2sb_unhide(void)
 {
-	pci_dev_t dev = PCI_BDF(0, 0xd, 0);
-	ulong val;
-
-	p2sb_set_hide_bit(dev, 0);
-
-	pci_x86_read_config(dev, PCI_VENDOR_ID, &val, PCI_SIZE_16);
+	struct udevice *dev;
+	int ret;
 
-	if (val != PCI_VENDOR_ID_INTEL)
-		return log_msg_ret("p2sb unhide", -EIO);
+	ret = uclass_find_first_device(UCLASS_P2SB, &dev);
+	if (ret)
+		return log_msg_ret("p2sb", ret);
+	ret = p2sb_set_hide(dev, false);
+	if (ret)
+		return log_msg_ret("hide", ret);
 
 	return 0;
 }
@@ -173,11 +160,6 @@ int arch_fsps_preinit(void)
 	ret = irq_first_device_type(X86_IRQT_ITSS, &itss);
 	if (ret)
 		return log_msg_ret("no itss", ret);
-	/*
-	 * Snapshot the current GPIO IRQ polarities. FSP is setting a default
-	 * policy that doesn't honour boards' requirements
-	 */
-	irq_snapshot_polarities(itss);
 
 	/*
 	 * Clear the GPI interrupt status and enable registers. These
@@ -192,16 +174,16 @@ int arch_fsps_preinit(void)
 
 int arch_fsp_init_r(void)
 {
-#ifdef CONFIG_HAVE_ACPI_RESUME
-	bool s3wake = gd->arch.prev_sleep_state == ACPI_S3;
-#else
-	bool s3wake = false;
-#endif
+	bool s3wake;
 	struct udevice *dev, *itss;
 	int ret;
 
 	if (!ll_boot_init())
 		return 0;
+
+	s3wake = IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) &&
+		gd->arch.prev_sleep_state == ACPI_S3;
+
 	/*
 	 * This must be called before any devices are probed. Put any probing
 	 * into arch_fsps_preinit() above.
@@ -216,7 +198,11 @@ int arch_fsp_init_r(void)
 	ret = irq_first_device_type(X86_IRQT_ITSS, &itss);
 	if (ret)
 		return log_msg_ret("no itss", ret);
-	/* Restore GPIO IRQ polarities back to previous settings */
+
+	/*
+	 * Restore GPIO IRQ polarities back to previous settings. This was
+	 * stored in reserve_arch() - see X86_IRQT_ITSS
+	 */
 	irq_restore_polarities(itss);
 
 	/* soc_init() */

+ 3 - 3
arch/x86/cpu/baytrail/acpi.c

@@ -139,7 +139,7 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs,
 	header->checksum = table_compute_checksum(fadt, header->length);
 }
 
-void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
+int acpi_create_gnvs(struct acpi_global_nvs *gnvs)
 {
 	struct udevice *dev;
 	int ret;
@@ -159,9 +159,10 @@ void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
 		gnvs->iuart_en = 1;
 	else
 		gnvs->iuart_en = 0;
+
+	return 0;
 }
 
-#ifdef CONFIG_HAVE_ACPI_RESUME
 /*
  * The following two routines are called at a very early stage, even before
  * FSP 2nd phase API fsp_init() is called. Registers off ACPI_BASE_ADDRESS
@@ -204,4 +205,3 @@ void chipset_clear_sleep_state(void)
 	pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
 	outl(pm1_cnt & ~(SLP_TYP), ACPI_BASE_ADDRESS + PM1_CNT);
 }
-#endif

+ 2 - 3
arch/x86/cpu/broadwell/power_state.c

@@ -23,11 +23,10 @@ static int prev_sleep_state(struct chipset_power_state *ps)
 
 	if (ps->pm1_sts & WAK_STS) {
 		switch ((ps->pm1_cnt & SLP_TYP) >> SLP_TYP_SHIFT) {
-#if CONFIG_HAVE_ACPI_RESUME
 		case SLP_TYP_S3:
-			prev_sleep_state = SLEEP_STATE_S3;
+			if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME))
+				prev_sleep_state = SLEEP_STATE_S3;
 			break;
-#endif
 		case SLP_TYP_S5:
 			prev_sleep_state = SLEEP_STATE_S5;
 			break;

+ 2 - 2
arch/x86/cpu/coreboot/coreboot.c

@@ -42,7 +42,7 @@ int print_cpuinfo(void)
 	return default_print_cpuinfo();
 }
 
-static void board_final_cleanup(void)
+static void board_final_init(void)
 {
 	/*
 	 * Un-cache the ROM so the kernel has one
@@ -80,7 +80,7 @@ int last_stage_init(void)
 	if (CONFIG_IS_ENABLED(USB_KEYBOARD))
 		usb_init();
 
-	board_final_cleanup();
+	board_final_init();
 
 	return 0;
 }

+ 7 - 1
arch/x86/cpu/coreboot/tables.c

@@ -10,6 +10,8 @@
 #include <net.h>
 #include <asm/arch/sysinfo.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 /*
  * This needs to be in the .data section so that it's copied over during
  * relocation. By default it's put in the .bss section which is simply filled
@@ -243,6 +245,10 @@ int get_coreboot_info(struct sysinfo_t *info)
 	if (addr < 0)
 		return addr;
 	ret = cb_parse_header((void *)addr, 0x1000, info);
+	if (!ret)
+		return -ENOENT;
+	gd->arch.coreboot_table = addr;
+	gd->flags |= GD_FLG_SKIP_LL_INIT;
 
-	return ret == 1 ? 0 : -ENOENT;
+	return 0;
 }

+ 38 - 26
arch/x86/cpu/cpu.c

@@ -25,6 +25,7 @@
 #include <dm.h>
 #include <errno.h>
 #include <init.h>
+#include <irq.h>
 #include <log.h>
 #include <malloc.h>
 #include <syscon.h>
@@ -163,10 +164,10 @@ int default_print_cpuinfo(void)
 	       cpu_has_64bit() ? "x86_64" : "x86",
 	       cpu_vendor_name(gd->arch.x86_vendor), gd->arch.x86_device);
 
-#ifdef CONFIG_HAVE_ACPI_RESUME
-	debug("ACPI previous sleep state: %s\n",
-	      acpi_ss_string(gd->arch.prev_sleep_state));
-#endif
+	if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
+		debug("ACPI previous sleep state: %s\n",
+		      acpi_ss_string(gd->arch.prev_sleep_state));
+	}
 
 	return 0;
 }
@@ -178,10 +179,10 @@ void show_boot_progress(int val)
 
 #if !defined(CONFIG_SYS_COREBOOT) && !defined(CONFIG_EFI_STUB)
 /*
- * Implement a weak default function for boards that optionally
- * need to clean up the system before jumping to the kernel.
+ * Implement a weak default function for boards that need to do some final init
+ * before the system is ready.
  */
-__weak void board_final_cleanup(void)
+__weak void board_final_init(void)
 {
 }
 
@@ -189,14 +190,14 @@ int last_stage_init(void)
 {
 	struct acpi_fadt __maybe_unused *fadt;
 
-	board_final_cleanup();
+	board_final_init();
 
-#ifdef CONFIG_HAVE_ACPI_RESUME
-	fadt = acpi_find_fadt();
+	if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
+		fadt = acpi_find_fadt();
 
-	if (fadt && gd->arch.prev_sleep_state == ACPI_S3)
-		acpi_resume(fadt);
-#endif
+		if (fadt && gd->arch.prev_sleep_state == ACPI_S3)
+			acpi_resume(fadt);
+	}
 
 	write_tables();
 
@@ -269,25 +270,36 @@ int cpu_init_r(void)
 #ifndef CONFIG_EFI_STUB
 int reserve_arch(void)
 {
-#ifdef CONFIG_ENABLE_MRC_CACHE
-	mrccache_reserve();
-#endif
+	struct udevice *itss;
+	int ret;
+
+	if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
+		mrccache_reserve();
 
 #ifdef CONFIG_SEABIOS
 	high_table_reserve();
 #endif
 
-#ifdef CONFIG_HAVE_ACPI_RESUME
-	acpi_s3_reserve();
+	if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
+		acpi_s3_reserve();
 
-#ifdef CONFIG_HAVE_FSP
-	/*
-	 * Save stack address to CMOS so that at next S3 boot,
-	 * we can use it as the stack address for fsp_contiue()
-	 */
-	fsp_save_s3_stack();
-#endif /* CONFIG_HAVE_FSP */
-#endif /* CONFIG_HAVE_ACPI_RESUME */
+		if (IS_ENABLED(CONFIG_HAVE_FSP)) {
+			/*
+			 * Save stack address to CMOS so that at next S3 boot,
+			 * we can use it as the stack address for fsp_contiue()
+			 */
+			fsp_save_s3_stack();
+		}
+	}
+	ret = irq_first_device_type(X86_IRQT_ITSS, &itss);
+	if (!ret) {
+		/*
+		 * Snapshot the current GPIO IRQ polarities. FSP-S is about to
+		 * run and will set a default policy that doesn't honour boards'
+		 * requirements
+		 */
+		irq_snapshot_polarities(itss);
+	}
 
 	return 0;
 }

+ 1 - 1
arch/x86/cpu/efi/app.c

@@ -24,7 +24,7 @@ int print_cpuinfo(void)
 	return default_print_cpuinfo();
 }
 
-void board_final_cleanup(void)
+void board_final_init(void)
 {
 }
 

+ 6 - 1
arch/x86/cpu/i386/cpu.c

@@ -455,10 +455,15 @@ int x86_cpu_init_f(void)
 
 int x86_cpu_reinit_f(void)
 {
+	long addr;
+
 	setup_identity();
 	setup_pci_ram_top();
-	if (locate_coreboot_table() >= 0)
+	addr = locate_coreboot_table();
+	if (addr >= 0) {
+		gd->arch.coreboot_table = addr;
 		gd->flags |= GD_FLG_SKIP_LL_INIT;
+	}
 
 	return 0;
 }

+ 23 - 2
arch/x86/cpu/intel_common/itss.c

@@ -65,14 +65,23 @@ static int snapshot_polarities(struct udevice *dev)
 	int i;
 
 	reg_start = start / IRQS_PER_IPC;
-	reg_end = (end + IRQS_PER_IPC - 1) / IRQS_PER_IPC;
+	reg_end = DIV_ROUND_UP(end, IRQS_PER_IPC);
 
+	log_info("ITSS IRQ Polarities snapshot %p\n", priv->irq_snapshot);
 	for (i = reg_start; i < reg_end; i++) {
 		uint reg = PCR_ITSS_IPC0_CONF + sizeof(u32) * i;
 
 		priv->irq_snapshot[i] = pcr_read32(dev, reg);
+		log_debug("   - %d, reg %x: irq_snapshot[i] %x\n", i, reg,
+			  priv->irq_snapshot[i]);
 	}
 
+	/* Save the snapshot for use after relocation */
+	gd->start_addr_sp -= sizeof(*priv);
+	gd->start_addr_sp &= ~0xf;
+	gd->arch.itss_priv = (void *)gd->start_addr_sp;
+	memcpy(gd->arch.itss_priv, priv, sizeof(*priv));
+
 	return 0;
 }
 
@@ -91,16 +100,26 @@ static void show_polarities(struct udevice *dev, const char *msg)
 static int restore_polarities(struct udevice *dev)
 {
 	struct itss_priv *priv = dev_get_priv(dev);
+	struct itss_priv *old_priv;
 	const int start = GPIO_IRQ_START;
 	const int end = GPIO_IRQ_END;
 	int reg_start;
 	int reg_end;
 	int i;
 
+	/* Get the snapshot which was stored by the pre-reloc device */
+	old_priv = gd->arch.itss_priv;
+	if (!old_priv)
+		return log_msg_ret("priv", -EFAULT);
+	memcpy(priv->irq_snapshot, old_priv->irq_snapshot,
+	       sizeof(priv->irq_snapshot));
+
 	show_polarities(dev, "Before");
+	log_info("priv->irq_snapshot %p\n", priv->irq_snapshot);
 
 	reg_start = start / IRQS_PER_IPC;
-	reg_end = (end + IRQS_PER_IPC - 1) / IRQS_PER_IPC;
+	reg_end = DIV_ROUND_UP(end, IRQS_PER_IPC);
+
 
 	for (i = reg_start; i < reg_end; i++) {
 		u32 mask;
@@ -125,6 +144,8 @@ static int restore_polarities(struct udevice *dev)
 		mask &= ~((1U << irq_start) - 1);
 
 		reg = PCR_ITSS_IPC0_CONF + sizeof(u32) * i;
+		log_debug("   - %d, reg %x: mask %x, irq_snapshot[i] %x\n",
+			  i, reg, mask, priv->irq_snapshot[i]);
 		pcr_clrsetbits32(dev, reg, mask, mask & priv->irq_snapshot[i]);
 	}
 

+ 44 - 0
arch/x86/cpu/intel_common/p2sb.c

@@ -16,6 +16,9 @@
 #include <asm/pci.h>
 #include <linux/bitops.h>
 
+#define PCH_P2SB_E0		0xe0
+#define HIDE_BIT		BIT(0)
+
 struct p2sb_platdata {
 #if CONFIG_IS_ENABLED(OF_PLATDATA)
 	struct dtd_intel_p2sb dtplat;
@@ -127,6 +130,40 @@ static int p2sb_probe(struct udevice *dev)
 	return 0;
 }
 
+static void p2sb_set_hide_bit(struct udevice *dev, bool hide)
+{
+	dm_pci_clrset_config8(dev, PCH_P2SB_E0 + 1, HIDE_BIT,
+			      hide ? HIDE_BIT : 0);
+}
+
+static int intel_p2sb_set_hide(struct udevice *dev, bool hide)
+{
+	u16 vendor;
+
+	if (!CONFIG_IS_ENABLED(PCI))
+		return -EPERM;
+	p2sb_set_hide_bit(dev, hide);
+
+	dm_pci_read_config16(dev, PCI_VENDOR_ID, &vendor);
+	if (hide && vendor != 0xffff)
+		return log_msg_ret("hide", -EEXIST);
+	else if (!hide && vendor != PCI_VENDOR_ID_INTEL)
+		return log_msg_ret("unhide", -ENOMEDIUM);
+
+	return 0;
+}
+
+static int p2sb_remove(struct udevice *dev)
+{
+	int ret;
+
+	ret = intel_p2sb_set_hide(dev, true);
+	if (ret)
+		return log_msg_ret("hide", ret);
+
+	return 0;
+}
+
 static int p2sb_child_post_bind(struct udevice *dev)
 {
 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
@@ -143,6 +180,10 @@ static int p2sb_child_post_bind(struct udevice *dev)
 	return 0;
 }
 
+struct p2sb_ops p2sb_ops = {
+	.set_hide	= intel_p2sb_set_hide,
+};
+
 static const struct udevice_id p2sb_ids[] = {
 	{ .compatible = "intel,p2sb" },
 	{ }
@@ -153,9 +194,12 @@ U_BOOT_DRIVER(p2sb_drv) = {
 	.id		= UCLASS_P2SB,
 	.of_match	= p2sb_ids,
 	.probe		= p2sb_probe,
+	.remove		= p2sb_remove,
+	.ops		= &p2sb_ops,
 	.ofdata_to_platdata = p2sb_ofdata_to_platdata,
 	.platdata_auto_alloc_size = sizeof(struct p2sb_platdata),
 	.per_child_platdata_auto_alloc_size =
 		sizeof(struct p2sb_child_platdata),
 	.child_post_bind = p2sb_child_post_bind,
+	.flags		= DM_FLAG_OS_PREPARE,
 };

+ 3 - 1
arch/x86/cpu/quark/acpi.c

@@ -133,8 +133,10 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs,
 	header->checksum = table_compute_checksum(fadt, header->length);
 }
 
-void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
+int acpi_create_gnvs(struct acpi_global_nvs *gnvs)
 {
 	/* quark is a uni-processor */
 	gnvs->pcnt = 1;
+
+	return 0;
 }

+ 1 - 1
arch/x86/cpu/quark/quark.c

@@ -363,7 +363,7 @@ int arch_misc_init(void)
 	return 0;
 }
 
-void board_final_cleanup(void)
+void board_final_init(void)
 {
 	struct quark_rcba *rcba;
 	u32 base, val;

+ 1 - 0
arch/x86/cpu/start.S

@@ -124,6 +124,7 @@ car_init_ret:
 #endif
 #else
 	/*
+	 * Instructions for FSP1, but not FSP2:
 	 * U-Boot enters here twice. For the first time it comes from
 	 * car_init_done() with esp points to a temporary stack and esi
 	 * set to zero. For the second time it comes from fsp_init_done()

+ 3 - 1
arch/x86/cpu/tangier/acpi.c

@@ -107,7 +107,7 @@ u32 acpi_fill_csrt(u32 current)
 	return current;
 }
 
-void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
+int acpi_create_gnvs(struct acpi_global_nvs *gnvs)
 {
 	struct udevice *dev;
 	int ret;
@@ -122,4 +122,6 @@ void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
 		if (ret > 0)
 			gnvs->pcnt = ret;
 	}
+
+	return 0;
 }

+ 1 - 0
arch/x86/dts/chromebook_coral.dts

@@ -117,6 +117,7 @@
 			reg = <0x00000000 0 0 0 0>;
 			compatible = "intel,apl-hostbridge";
 			pciex-region-size = <0x10000000>;
+			fspm,training-delay = <21>;
 			/*
 			 * Parameters used by the FSP-S binary blob. This is
 			 * really unfortunate since these parameters mostly

+ 314 - 0
arch/x86/include/asm/acpi_nhlt.h

@@ -0,0 +1,314 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Modified from coreboot nhlt.h
+ */
+
+#ifndef _NHLT_H_
+#define _NHLT_H_
+
+struct acpi_ctx;
+struct nhlt;
+struct nhlt_endpoint;
+struct nhlt_format;
+struct nhlt_format_config;
+
+/*
+ * Non HD Audio ACPI support. This table is typically used for Intel Smart
+ * Sound Technology DSP. It provides a way to encode opaque settings in
+ * the ACPI tables.
+ *
+ * While the structure fields of the NHLT structs are exposed below
+ * the SoC/chipset code should be the only other user manipulating the
+ * fields directly aside from the library itself.
+ *
+ * The NHLT table consists of endpoints which in turn contain different
+ * supporting stream formats. Each endpoint may contain a device specific
+ * configuration payload as well as each stream format.
+ *
+ * Most code should use the SoC variants of the functions because
+ * there is required logic needed to be performed by the SoC. The SoC
+ * code should be abstracting the inner details of these functions that
+ * specically apply to NHLT objects for that SoC.
+ *
+ * An example sequence:
+ *
+ * nhlt = nhlt_init()
+ * ep = nhlt_add_endpoint()
+ * nhlt_endpoint_append_config(ep)
+ * nhlt_endpoint_add_formats(ep)
+ * nhlt_soc_serialise()
+ */
+
+/* Obtain an nhlt object for adding endpoints. Returns NULL on error. */
+struct nhlt *nhlt_init(void);
+
+/* Return the size of the NHLT table including ACPI header. */
+size_t nhlt_current_size(struct nhlt *nhlt);
+
+/*
+ * Helper functions for adding NHLT devices utilizing an nhlt_endp_descriptor
+ * to drive the logic.
+ */
+
+struct nhlt_endp_descriptor {
+	/* NHLT endpoint types. */
+	int link;
+	int device;
+	int direction;
+	u16 vid;
+	u16 did;
+	/* Optional endpoint specific configuration data. */
+	const void *cfg;
+	size_t cfg_size;
+	/* Formats supported for endpoint. */
+	const struct nhlt_format_config *formats;
+	size_t num_formats;
+};
+
+/*
+ * Add the number of endpoints described by each descriptor. The virtual bus
+ * id for each descriptor is the default value of 0.
+ * Returns < 0 on error, 0 on success.
+ */
+int nhlt_add_endpoints(struct nhlt *nhlt,
+		       const struct nhlt_endp_descriptor *epds,
+		       size_t num_epds);
+
+/*
+ * Add the number of endpoints associated with a single NHLT SSP instance id.
+ * Each endpoint described in the endpoint descriptor array uses the provided
+ * virtual bus id. Returns < 0 on error, 0 on success.
+ */
+int nhlt_add_ssp_endpoints(struct nhlt *nhlt, int virtual_bus_id,
+			   const struct nhlt_endp_descriptor *epds,
+			   size_t num_epds);
+
+/*
+ * Add endpoint to NHLT object. Returns NULL on error.
+ *
+ * generic nhlt_add_endpoint() is called by the SoC code to provide
+ * the specific assumptions/uses for NHLT for that platform. All fields
+ * are the NHLT enumerations found within this header file.
+ */
+struct nhlt_endpoint *nhlt_add_endpoint(struct nhlt *nhlt, int link_type,
+					int device_type, int dir,
+					u16 vid, u16 did);
+
+/*
+ * Append blob of configuration to the endpoint proper. Returns 0 on
+ * success, < 0 on error. A copy of the configuration is made so any
+ * resources pointed to by config can be freed after the call.
+ */
+int nhlt_endpoint_append_config(struct nhlt_endpoint *endpoint,
+				const void *config, size_t config_sz);
+
+/* Add a format type to the provided endpoint. Returns NULL on error. */
+struct nhlt_format *nhlt_add_format(struct nhlt_endpoint *endpoint,
+				    int num_channels, int sample_freq_khz,
+				    int container_bits_per_sample,
+				    int valid_bits_per_sample,
+				    u32 speaker_mask);
+
+/*
+ * Append blob of configuration to the format proper. Returns 0 on
+ * success, < 0 on error. A copy of the configuration is made so any
+ * resources pointed to by config can be freed after the call.
+ */
+int nhlt_format_append_config(struct nhlt_format *format, const void *config,
+			      size_t config_sz);
+
+/*
+ * Add num_formats described by formats to the endpoint. This function
+ * effectively wraps nhlt_add_format() and nhlt_format_config() using the
+ * data found in each nhlt_format_config object. Returns 0 on success, < 0
+ * on error.
+ */
+int nhlt_endpoint_add_formats(struct nhlt_endpoint *endpoint,
+			      const struct nhlt_format_config *formats,
+			      size_t num_formats);
+
+/*
+ * Increment the instance id for a given link type. This function is
+ * used for marking a device being completely added to the NHLT object.
+ * Subsequent endpoints added to the nhlt object with the same link type
+ * will use incremented instance id.
+ */
+void nhlt_next_instance(struct nhlt *nhlt, int link_type);
+
+/*
+ * Serialize NHLT object to ACPI table. Take in the beginning address of where
+ * the table will reside and return the address of the next ACPI table. On
+ * error 0 will be returned. The NHLT object is no longer valid after this
+ * function is called.
+ */
+uintptr_t nhlt_serialise(struct nhlt *nhlt, uintptr_t acpi_addr);
+
+/*
+ * Serialize NHLT object to ACPI table. Take in the beginning address of where
+ * the table will reside oem_id and oem_table_id and return the address of the
+ * next ACPI table. On error 0 will be returned. The NHLT object is no longer
+ * valid after this function is called.
+ */
+int nhlt_serialise_oem_overrides(struct acpi_ctx *ctx, struct nhlt *nhlt,
+				 const char *oem_id, const char *oem_table_id,
+				 u32 oem_revision);
+
+int nhlt_setup(struct nhlt *nhlt, ofnode node);
+
+/* Link and device types. */
+enum {
+	NHLT_LINK_HDA,
+	NHLT_LINK_DSP,
+	NHLT_LINK_PDM,
+	NHLT_LINK_SSP,
+	NHLT_MAX_LINK_TYPES,
+};
+
+enum {
+	NHLT_SSP_DEV_BT, /* Bluetooth */
+	NHLT_SSP_DEV_MODEM,
+	NHLT_SSP_DEV_FM,
+	NHLT_SSP_DEV_RESERVED,
+	NHLT_SSP_DEV_I2S = 4,
+};
+
+enum {
+	NHLT_PDM_DEV,
+};
+
+/* Endpoint direction. */
+enum {
+	NHLT_DIR_RENDER,
+	NHLT_DIR_CAPTURE,
+	NHLT_DIR_BIDIRECTIONAL,
+};
+
+/*
+ * Channel mask for an endpoint. While they are prefixed with 'SPEAKER' the
+ * channel masks are also used for capture devices
+ */
+enum {
+	SPEAKER_FRONT_LEFT =		BIT(0),
+	SPEAKER_FRONT_RIGHT =		BIT(1),
+	SPEAKER_FRONT_CENTER =		BIT(2),
+	SPEAKER_LOW_FREQUENCY =		BIT(3),
+	SPEAKER_BACK_LEFT =		BIT(4),
+	SPEAKER_BACK_RIGHT =		BIT(5),
+	SPEAKER_FRONT_LEFT_OF_CENTER =	BIT(6),
+	SPEAKER_FRONT_RIGHT_OF_CENTER =	BIT(7),
+	SPEAKER_BACK_CENTER =		BIT(8),
+	SPEAKER_SIDE_LEFT =		BIT(9),
+	SPEAKER_SIDE_RIGHT =		BIT(10),
+	SPEAKER_TOP_CENTER =		BIT(11),
+	SPEAKER_TOP_FRONT_LEFT =	BIT(12),
+	SPEAKER_TOP_FRONT_CENTER =	BIT(13),
+	SPEAKER_TOP_FRONT_RIGHT =	BIT(14),
+	SPEAKER_TOP_BACK_LEFT =		BIT(15),
+	SPEAKER_TOP_BACK_CENTER =	BIT(16),
+	SPEAKER_TOP_BACK_RIGHT =	BIT(17),
+};
+
+/*
+ * Supporting structures. Only SoC/chipset and the library code directly should
+ * be manipulating these structures
+ */
+struct sub_format {
+	u32 data1;
+	u16 data2;
+	u16 data3;
+	u8 data4[8];
+};
+
+struct nhlt_specific_config {
+	u32 size;
+	void *capabilities;
+};
+
+struct nhlt_waveform {
+	u16 tag;
+	u16 num_channels;
+	u32 samples_per_second;
+	u32 bytes_per_second;
+	u16 block_align;
+	u16 bits_per_sample;
+	u16 extra_size;
+	u16 valid_bits_per_sample;
+	u32 channel_mask;
+	struct sub_format sub_format;
+};
+
+struct nhlt_format {
+	struct nhlt_waveform waveform;
+	struct nhlt_specific_config config;
+};
+
+/*
+ * This struct is used by nhlt_endpoint_add_formats() for easily adding
+ * waveform formats with associated settings file.
+ */
+struct nhlt_format_config {
+	int num_channels;
+	int sample_freq_khz;
+	int container_bits_per_sample;
+	int valid_bits_per_sample;
+	u32 speaker_mask;
+	const char *settings_file;
+};
+
+/* Arbitrary max number of formats per endpoint. */
+#define MAX_FORMATS 2
+struct nhlt_endpoint {
+	u32 length;
+	u8 link_type;
+	u8 instance_id;
+	u16 vendor_id;
+	u16 device_id;
+	u16 revision_id;
+	u32 subsystem_id;
+	u8 device_type;
+	u8 direction;
+	u8 virtual_bus_id;
+	struct nhlt_specific_config config;
+	u8 num_formats;
+	struct nhlt_format formats[MAX_FORMATS];
+};
+
+#define MAX_ENDPOINTS 8
+struct nhlt {
+	u32 subsystem_id;
+	u8 num_endpoints;
+	struct nhlt_endpoint endpoints[MAX_ENDPOINTS];
+	u8 current_instance_id[NHLT_MAX_LINK_TYPES];
+};
+
+struct nhlt_tdm_config {
+	u8 virtual_slot;
+	u8 config_type;
+};
+
+enum {
+	NHLT_TDM_BASIC,
+	NHLT_TDM_MIC_ARRAY,
+};
+
+struct nhlt_dmic_array_config {
+	struct nhlt_tdm_config tdm_config;
+	u8 array_type;
+};
+
+/*
+ * Microphone array definitions may be found here:
+ * https://msdn.microsoft.com/en-us/library/windows/hardware/dn613960%28v=vs.85%29.aspx
+ */
+enum {
+	NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
+	NHLT_MIC_ARRAY_2CH_BIG = 0xb,
+	NHLT_MIC_ARRAY_4CH_1ST_GEOM = 0xc,
+	NHLT_MIC_ARRAY_4CH_L_SHAPED = 0xd,
+	NHLT_MIC_ARRAY_4CH_2ND_GEOM = 0xe,
+	NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
+};
+
+#endif

+ 9 - 1
arch/x86/include/asm/acpi_table.h

@@ -35,7 +35,15 @@ int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base,
 			      u16 seg_nr, u8 start, u8 end);
 u32 acpi_fill_mcfg(u32 current);
 u32 acpi_fill_csrt(u32 current);
-void acpi_create_gnvs(struct acpi_global_nvs *gnvs);
+
+/**
+ * acpi_create_gnvs() - Create a GNVS (Global Non Volatile Storage) table
+ *
+ * @gnvs: Table to fill in
+ * @return 0 if OK, -ve on error
+ */
+int acpi_create_gnvs(struct acpi_global_nvs *gnvs);
+
 ulong write_acpi_tables(ulong start);
 
 /**

+ 2 - 1
arch/x86/include/asm/fsp2/fsp_internal.h

@@ -57,7 +57,8 @@ int arch_fsps_preinit(void);
  *
  * @dev: Hostbridge device containing config
  * @upd: Config data to fill in
- * @return 0 if OK, -ve on error
+ * @return 0 if OK, -ENOENT if OK but no MRC-cache data was found, other -ve on
+ *	error
  */
 int fspm_update_config(struct udevice *dev, struct fspm_upd *upd);
 

+ 2 - 2
arch/x86/include/asm/global_data.h

@@ -116,14 +116,14 @@ struct arch_global_data {
 	u32 high_table_ptr;
 	u32 high_table_limit;
 #endif
-#ifdef CONFIG_HAVE_ACPI_RESUME
 	int prev_sleep_state;		/* Previous sleep state ACPI_S0/1../5 */
 	ulong backup_mem;		/* Backup memory address for S3 */
-#endif
 #ifdef CONFIG_FSP_VERSION2
 	struct fsp_header *fsp_s_hdr;	/* Pointer to FSP-S header */
 #endif
+	void *itss_priv;		/* Private ITSS data pointer */
 	ulong acpi_start;		/* Start address of ACPI tables */
+	ulong coreboot_table;		/* Address of coreboot table */
 };
 
 #endif

+ 15 - 4
arch/x86/include/asm/intel_pinctrl.h

@@ -99,7 +99,6 @@ struct pad_group {
  * groups exist inside a community
  *
  * @name: Community name
- * @acpi_path: ACPI path
  * @num_gpi_regs: number of gpi registers in community
  * @max_pads_per_group: number of pads in each group; number of pads bit-mapped
  *	in each GPI status/en and Host Own Reg
@@ -120,7 +119,6 @@ struct pad_group {
  */
 struct pad_community {
 	const char *name;
-	const char *acpi_path;
 	size_t num_gpi_regs;
 	size_t max_pads_per_group;
 	uint first_pad;
@@ -263,11 +261,23 @@ int pinctrl_read_pads(struct udevice *dev, ofnode node, const char *prop,
 int pinctrl_count_pads(struct udevice *dev, u32 *pads, int size);
 
 /**
- * intel_pinctrl_get_config_reg_addr() - Get address of the pin config registers
+ * intel_pinctrl_get_config_reg_offset() - Get offset of pin config registers
  *
+ * This works out the register offset of a pin within the p2sb region.
+ *
+ * @dev: Pinctrl device
+ * @offset: GPIO offset within this device
+ * @return register offset of first register within the GPIO p2sb region
+ */
+u32 intel_pinctrl_get_config_reg_offset(struct udevice *dev, uint offset);
+
+/**
+ * intel_pinctrl_get_config_reg_addr() - Get address of pin config registers
+ *
+ * This works out the absolute address of the registers for a pin
  * @dev: Pinctrl device
  * @offset: GPIO offset within this device
- * @return register offset within the GPIO p2sb region
+ * @return register address of first register within the GPIO p2sb region
  */
 u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset);
 
@@ -288,6 +298,7 @@ u32 intel_pinctrl_get_config_reg(struct udevice *dev, uint offset);
  * @pad: Pad to check
  * @devp: Returns pinctrl device containing that pad
  * @offsetp: Returns offset of pad within that pinctrl device
+ * @return 0 if OK, -ENOTBLK if pad number is invalid
  */
 int intel_pinctrl_get_pad(uint pad, struct udevice **devp, uint *offsetp);
 

+ 1 - 1
arch/x86/include/asm/itss.h

@@ -16,7 +16,7 @@
 
 #define ITSS_MAX_IRQ	119
 #define IRQS_PER_IPC	32
-#define NUM_IPC_REGS	((ITSS_MAX_IRQ + IRQS_PER_IPC - 1) / IRQS_PER_IPC)
+#define NUM_IPC_REGS	DIV_ROUND_UP(ITSS_MAX_IRQ, IRQS_PER_IPC)
 
 /* Max PXRC registers in ITSS */
 #define MAX_PXRC_CONFIG	(PCR_ITSS_PIRQH_ROUT - PCR_ITSS_PIRQA_ROUT + 1)

+ 0 - 2
arch/x86/include/asm/u-boot-x86.h

@@ -83,8 +83,6 @@ int default_print_cpuinfo(void);
 /* Set up a UART which can be used with printch(), printhex8(), etc. */
 int setup_internal_uart(int enable);
 
-void setup_pcat_compatibility(void);
-
 void isa_unmap_rom(u32 addr);
 u32 isa_map_rom(u32 bus_addr, int size);
 

+ 1 - 0
arch/x86/lib/Makefile

@@ -22,6 +22,7 @@ obj-y	+= init_helpers.o
 obj-y	+= interrupts.o
 obj-y	+= lpc-uclass.o
 obj-y	+= mpspec.o
+obj-$(CONFIG_$(SPL_TPL_)ACPIGEN) += acpi_nhlt.o
 obj-y	+= northbridge-uclass.o
 obj-$(CONFIG_I8259_PIC) += i8259.o
 obj-$(CONFIG_I8254_TIMER) += i8254.o

+ 482 - 0
arch/x86/lib/acpi_nhlt.c

@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Modified from coreboot nhlt.c
+ */
+
+#define LOG_CATEGORY	LOGC_ACPI
+
+#include <common.h>
+#include <binman.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <tables_csum.h>
+#include <acpi/acpi_table.h>
+#include <asm/acpi_nhlt.h>
+#include <asm/unaligned.h>
+#include <dm/acpi.h>
+
+#define NHLT_RID		1
+#define NHLT_SSID		1
+#define WAVEFORMAT_TAG		0xfffe
+#define DEFAULT_VIRTUAL_BUS_ID	0
+
+static const struct sub_format pcm_subformat = {
+	.data1 = 0x00000001,
+	.data2 = 0x0000,
+	.data3 = 0x0010,
+	.data4 = { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 },
+};
+
+struct nhlt *nhlt_init(void)
+{
+	struct nhlt *nhlt;
+
+	nhlt = malloc(sizeof(*nhlt));
+
+	if (!nhlt)
+		return NULL;
+
+	memset(nhlt, 0, sizeof(*nhlt));
+	nhlt->subsystem_id = NHLT_SSID;
+
+	return nhlt;
+}
+
+struct nhlt_endpoint *nhlt_add_endpoint(struct nhlt *nhlt, int link_type,
+					int device_type, int dir,
+					u16 vid, u16 did)
+{
+	struct nhlt_endpoint *endp;
+
+	if (link_type < NHLT_LINK_HDA || link_type >= NHLT_MAX_LINK_TYPES)
+		return NULL;
+
+	if (nhlt->num_endpoints >= MAX_ENDPOINTS)
+		return NULL;
+
+	endp = &nhlt->endpoints[nhlt->num_endpoints];
+
+	endp->link_type = link_type;
+	endp->instance_id = nhlt->current_instance_id[link_type];
+	endp->vendor_id = vid;
+	endp->device_id = did;
+	endp->revision_id = NHLT_RID;
+	endp->subsystem_id = nhlt->subsystem_id;
+	endp->device_type = device_type;
+	endp->direction = dir;
+	endp->virtual_bus_id = DEFAULT_VIRTUAL_BUS_ID;
+
+	nhlt->num_endpoints++;
+
+	return endp;
+}
+
+static int append_specific_config(struct nhlt_specific_config *spec_cfg,
+				  const void *config, size_t config_sz)
+{
+	size_t new_sz;
+	void *new_cfg;
+
+	new_sz = spec_cfg->size + config_sz;
+	new_cfg = malloc(new_sz);
+	if (!new_cfg)
+		return -ENOMEM;
+
+	/* Append new config */
+	memcpy(new_cfg, spec_cfg->capabilities, spec_cfg->size);
+	memcpy(new_cfg + spec_cfg->size, config, config_sz);
+
+	free(spec_cfg->capabilities);
+
+	/* Update with new config data */
+	spec_cfg->size = new_sz;
+	spec_cfg->capabilities = new_cfg;
+
+	return 0;
+}
+
+int nhlt_endpoint_append_config(struct nhlt_endpoint *endp, const void *config,
+				size_t config_sz)
+{
+	return append_specific_config(&endp->config, config, config_sz);
+}
+
+struct nhlt_format *nhlt_add_format(struct nhlt_endpoint *endp,
+				    int num_channels, int sample_freq_khz,
+				    int container_bits_per_sample,
+				    int valid_bits_per_sample,
+				    uint32_t speaker_mask)
+{
+	struct nhlt_format *fmt;
+	struct nhlt_waveform *wave;
+
+	if (endp->num_formats >= MAX_FORMATS)
+		return NULL;
+
+	fmt = &endp->formats[endp->num_formats];
+	wave = &fmt->waveform;
+
+	wave->tag = WAVEFORMAT_TAG;
+	wave->num_channels = num_channels;
+	wave->samples_per_second = sample_freq_khz * 1000;
+	wave->bits_per_sample = container_bits_per_sample;
+	wave->extra_size = sizeof(wave->valid_bits_per_sample);
+	wave->extra_size += sizeof(wave->channel_mask);
+	wave->extra_size += sizeof(wave->sub_format);
+	wave->valid_bits_per_sample = valid_bits_per_sample;
+	wave->channel_mask = speaker_mask;
+	memcpy(&wave->sub_format, &pcm_subformat, sizeof(wave->sub_format));
+
+	/* Calculate the dervied fields */
+	wave->block_align = wave->num_channels * wave->bits_per_sample / 8;
+	wave->bytes_per_second = wave->block_align * wave->samples_per_second;
+
+	endp->num_formats++;
+
+	return fmt;
+}
+
+int nhlt_format_append_config(struct nhlt_format *fmt, const void *config,
+			      size_t config_sz)
+{
+	return append_specific_config(&fmt->config, config, config_sz);
+}
+
+int nhlt_endpoint_add_formats(struct nhlt_endpoint *endp,
+			      const struct nhlt_format_config *formats,
+			      size_t num_formats)
+{
+	ofnode node;
+	size_t i;
+
+	node = binman_section_find_node("private-files");
+
+	for (i = 0; i < num_formats; i++) {
+		const struct nhlt_format_config *cfg = &formats[i];
+		struct nhlt_format *fmt;
+		void *data;
+		int size;
+		int ret;
+
+		fmt = nhlt_add_format(endp, cfg->num_channels,
+				      cfg->sample_freq_khz,
+				      cfg->container_bits_per_sample,
+				      cfg->valid_bits_per_sample,
+				      cfg->speaker_mask);
+		if (!fmt)
+			return -ENOSPC;
+
+		if (!cfg->settings_file)
+			continue;
+
+		ret = binman_entry_map(node, cfg->settings_file, &data, &size);
+		if (ret) {
+			log_warning("Failed to find settings file %s\n",
+				    cfg->settings_file);
+			return log_msg_ret("settings", ret);
+		}
+
+		ret = nhlt_format_append_config(fmt, data, size);
+		if (ret)
+			return log_msg_ret("append", ret);
+	}
+
+	return 0;
+}
+
+void nhlt_next_instance(struct nhlt *nhlt, int link_type)
+{
+	if (link_type < NHLT_LINK_HDA || link_type >= NHLT_MAX_LINK_TYPES)
+		return;
+
+	nhlt->current_instance_id[link_type]++;
+}
+
+static size_t calc_specific_config_size(struct nhlt_specific_config *cfg)
+{
+	return sizeof(cfg->size) + cfg->size;
+}
+
+static size_t calc_format_size(struct nhlt_format *fmt)
+{
+	size_t sz = 0;
+
+	/* Wave format first */
+	sz += sizeof(fmt->waveform.tag);
+	sz += sizeof(fmt->waveform.num_channels);
+	sz += sizeof(fmt->waveform.samples_per_second);
+	sz += sizeof(fmt->waveform.bytes_per_second);
+	sz += sizeof(fmt->waveform.block_align);
+	sz += sizeof(fmt->waveform.bits_per_sample);
+	sz += sizeof(fmt->waveform.extra_size);
+	sz += sizeof(fmt->waveform.valid_bits_per_sample);
+	sz += sizeof(fmt->waveform.channel_mask);
+	sz += sizeof(fmt->waveform.sub_format);
+
+	sz += calc_specific_config_size(&fmt->config);
+
+	return sz;
+}
+
+static size_t calc_endpoint_size(struct nhlt_endpoint *endp)
+{
+	int i;
+	size_t sz = 0;
+
+	sz += sizeof(endp->length) + sizeof(endp->link_type);
+	sz += sizeof(endp->instance_id) + sizeof(endp->vendor_id);
+	sz += sizeof(endp->device_id) + sizeof(endp->revision_id);
+	sz += sizeof(endp->subsystem_id) + sizeof(endp->device_type);
+	sz += sizeof(endp->direction) + sizeof(endp->virtual_bus_id);
+	sz += calc_specific_config_size(&endp->config);
+	sz += sizeof(endp->num_formats);
+
+	for (i = 0; i < endp->num_formats; i++)
+		sz += calc_format_size(&endp->formats[i]);
+
+	/* Adjust endpoint length to reflect current configuration */
+	endp->length = sz;
+
+	return sz;
+}
+
+static size_t calc_endpoints_size(struct nhlt *nhlt)
+{
+	size_t sz = 0;
+	int i;
+
+	for (i = 0; i < nhlt->num_endpoints; i++)
+		sz += calc_endpoint_size(&nhlt->endpoints[i]);
+
+	return sz;
+}
+
+static size_t calc_size(struct nhlt *nhlt)
+{
+	return sizeof(nhlt->num_endpoints) + calc_endpoints_size(nhlt);
+}
+
+size_t nhlt_current_size(struct nhlt *nhlt)
+{
+	return calc_size(nhlt) + sizeof(struct acpi_table_header);
+}
+
+static void nhlt_free_resources(struct nhlt *nhlt)
+{
+	int i, j;
+
+	/* Free all specific configs */
+	for (i = 0; i < nhlt->num_endpoints; i++) {
+		struct nhlt_endpoint *endp = &nhlt->endpoints[i];
+
+		free(endp->config.capabilities);
+		for (j = 0; j < endp->num_formats; j++) {
+			struct nhlt_format *fmt = &endp->formats[j];
+
+			free(fmt->config.capabilities);
+		}
+	}
+
+	/* Free nhlt object proper */
+	free(nhlt);
+}
+
+struct cursor {
+	u8 *buf;
+};
+
+static void ser8(struct cursor *cur, uint val)
+{
+	*cur->buf = val;
+	cur->buf += sizeof(val);
+}
+
+static void ser16(struct cursor *cur, uint val)
+{
+	put_unaligned_le16(val, cur->buf);
+	cur->buf += sizeof(val);
+}
+
+static void ser32(struct cursor *cur, uint val)
+{
+	put_unaligned_le32(val, cur->buf);
+	cur->buf += sizeof(val);
+}
+
+static void serblob(struct cursor *cur, void *from, size_t sz)
+{
+	memcpy(cur->buf, from, sz);
+	cur->buf += sz;
+}
+
+static void serialise_specific_config(struct nhlt_specific_config *cfg,
+				      struct cursor *cur)
+{
+	ser32(cur, cfg->size);
+	serblob(cur, cfg->capabilities, cfg->size);
+}
+
+static void serialise_waveform(struct nhlt_waveform *wave, struct cursor *cur)
+{
+	ser16(cur, wave->tag);
+	ser16(cur, wave->num_channels);
+	ser32(cur, wave->samples_per_second);
+	ser32(cur, wave->bytes_per_second);
+	ser16(cur, wave->block_align);
+	ser16(cur, wave->bits_per_sample);
+	ser16(cur, wave->extra_size);
+	ser16(cur, wave->valid_bits_per_sample);
+	ser32(cur, wave->channel_mask);
+	ser32(cur, wave->sub_format.data1);
+	ser16(cur, wave->sub_format.data2);
+	ser16(cur, wave->sub_format.data3);
+	serblob(cur, wave->sub_format.data4, sizeof(wave->sub_format.data4));
+}
+
+static void serialise_format(struct nhlt_format *fmt, struct cursor *cur)
+{
+	serialise_waveform(&fmt->waveform, cur);
+	serialise_specific_config(&fmt->config, cur);
+}
+
+static void serialise_endpoint(struct nhlt_endpoint *endp, struct cursor *cur)
+{
+	int i;
+
+	ser32(cur, endp->length);
+	ser8(cur, endp->link_type);
+	ser8(cur, endp->instance_id);
+	ser16(cur, endp->vendor_id);
+	ser16(cur, endp->device_id);
+	ser16(cur, endp->revision_id);
+	ser32(cur, endp->subsystem_id);
+	ser8(cur, endp->device_type);
+	ser8(cur, endp->direction);
+	ser8(cur, endp->virtual_bus_id);
+	serialise_specific_config(&endp->config, cur);
+	ser8(cur, endp->num_formats);
+
+	for (i = 0; i < endp->num_formats; i++)
+		serialise_format(&endp->formats[i], cur);
+}
+
+static void nhlt_serialise_endpoints(struct nhlt *nhlt, struct cursor *cur)
+{
+	int i;
+
+	ser8(cur, nhlt->num_endpoints);
+
+	for (i = 0; i < nhlt->num_endpoints; i++)
+		serialise_endpoint(&nhlt->endpoints[i], cur);
+}
+
+int nhlt_serialise_oem_overrides(struct acpi_ctx *ctx, struct nhlt *nhlt,
+				 const char *oem_id, const char *oem_table_id,
+				 uint32_t oem_revision)
+{
+	struct cursor cur;
+	struct acpi_table_header *header;
+	size_t sz;
+	size_t oem_id_len;
+	size_t oem_table_id_len;
+	int ret;
+
+	log_info("ACPI:    * NHLT\n");
+	sz = nhlt_current_size(nhlt);
+
+	/* Create header */
+	header = (void *)ctx->current;
+	memset(header, '\0', sizeof(struct acpi_table_header));
+	acpi_fill_header(header, "NHLT");
+	header->length = sz;
+	header->revision = acpi_get_table_revision(ACPITAB_NHLT);
+
+	if (oem_id) {
+		oem_id_len = min((int)strlen(oem_id), 6);
+		memcpy(header->oem_id, oem_id, oem_id_len);
+	}
+	if (oem_table_id) {
+		oem_table_id_len = min((int)strlen(oem_table_id), 8);
+		memcpy(header->oem_table_id, oem_table_id, oem_table_id_len);
+	}
+	header->oem_revision = oem_revision;
+
+	cur.buf = (void *)(header + 1);
+	nhlt_serialise_endpoints(nhlt, &cur);
+
+	header->checksum = table_compute_checksum(header, sz);
+	nhlt_free_resources(nhlt);
+
+	ret = acpi_add_table(ctx, ctx->current);
+	if (ret)
+		return log_msg_ret("add", ret);
+	acpi_inc_align(ctx, sz);
+
+	return 0;
+}
+
+static int _nhlt_add_single_endpoint(struct nhlt *nhlt, int virtual_bus_id,
+				     const struct nhlt_endp_descriptor *epd)
+{
+	struct nhlt_endpoint *endp;
+	int ret;
+
+	endp = nhlt_add_endpoint(nhlt, epd->link, epd->device, epd->direction,
+				 epd->vid, epd->did);
+	if (!endp)
+		return -EINVAL;
+
+	endp->virtual_bus_id = virtual_bus_id;
+
+	ret = nhlt_endpoint_append_config(endp, epd->cfg, epd->cfg_size);
+	if (ret)
+		return ret;
+
+	ret = nhlt_endpoint_add_formats(endp, epd->formats, epd->num_formats);
+	if (ret)
+		return log_msg_ret("formats", ret);
+
+	return 0;
+}
+
+static int _nhlt_add_endpoints(struct nhlt *nhlt, int virtual_bus_id,
+			       const struct nhlt_endp_descriptor *epds,
+			       size_t num_epds)
+{
+	size_t i;
+	int ret;
+
+	for (i = 0; i < num_epds; i++) {
+		ret = _nhlt_add_single_endpoint(nhlt, virtual_bus_id, &epds[i]);
+		if (ret)
+			return log_ret(ret);
+	}
+
+	return 0;
+}
+
+int nhlt_add_endpoints(struct nhlt *nhlt,
+		       const struct nhlt_endp_descriptor *epds, size_t num_epds)
+{
+	int ret;
+
+	ret = _nhlt_add_endpoints(nhlt, DEFAULT_VIRTUAL_BUS_ID, epds, num_epds);
+
+	return ret;
+}
+
+int nhlt_add_ssp_endpoints(struct nhlt *nhlt, int virtual_bus_id,
+			   const struct nhlt_endp_descriptor *epds,
+			   size_t num_epds)
+{
+	int ret;
+
+	ret = _nhlt_add_endpoints(nhlt, virtual_bus_id, epds, num_epds);
+	if (!ret)
+		nhlt_next_instance(nhlt, NHLT_LINK_SSP);
+
+	return ret;
+}

+ 72 - 15
arch/x86/lib/acpi_table.c

@@ -14,6 +14,7 @@
 #include <mapmem.h>
 #include <serial.h>
 #include <version.h>
+#include <acpi/acpigen.h>
 #include <acpi/acpi_table.h>
 #include <asm/acpi/global_nvs.h>
 #include <asm/ioapic.h>
@@ -22,6 +23,7 @@
 #include <asm/tables.h>
 #include <asm/arch/global_nvs.h>
 #include <dm/acpi.h>
+#include <linux/err.h>
 
 /*
  * IASL compiles the dsdt entries and writes the hex values
@@ -153,7 +155,7 @@ static void acpi_create_madt(struct acpi_madt *madt)
 	/* Fill out header fields */
 	acpi_fill_header(header, "APIC");
 	header->length = sizeof(struct acpi_madt);
-	header->revision = 4;
+	header->revision = ACPI_MADT_REV_ACPI_3_0;
 
 	madt->lapic_addr = LAPIC_DEFAULT_BASE;
 	madt->flags = ACPI_MADT_PCAT_COMPAT;
@@ -210,13 +212,14 @@ static void acpi_create_mcfg(struct acpi_mcfg *mcfg)
 
 __weak u32 acpi_fill_csrt(u32 current)
 {
-	return current;
+	return 0;
 }
 
-static void acpi_create_csrt(struct acpi_csrt *csrt)
+static int acpi_create_csrt(struct acpi_csrt *csrt)
 {
 	struct acpi_table_header *header = &(csrt->header);
 	u32 current = (u32)csrt + sizeof(struct acpi_csrt);
+	uint ptr;
 
 	memset((void *)csrt, 0, sizeof(struct acpi_csrt));
 
@@ -225,11 +228,16 @@ static void acpi_create_csrt(struct acpi_csrt *csrt)
 	header->length = sizeof(struct acpi_csrt);
 	header->revision = 0;
 
-	current = acpi_fill_csrt(current);
+	ptr = acpi_fill_csrt(current);
+	if (!ptr)
+		return -ENOENT;
+	current = ptr;
 
 	/* (Re)calculate length and checksum */
 	header->length = current - (u32)csrt;
 	header->checksum = table_compute_checksum((void *)csrt, header->length);
+
+	return 0;
 }
 
 static void acpi_create_spcr(struct acpi_spcr *spcr)
@@ -354,6 +362,25 @@ static void acpi_create_spcr(struct acpi_spcr *spcr)
 	header->checksum = table_compute_checksum((void *)spcr, header->length);
 }
 
+void acpi_create_ssdt(struct acpi_ctx *ctx, struct acpi_table_header *ssdt,
+		      const char *oem_table_id)
+{
+	memset((void *)ssdt, '\0', sizeof(struct acpi_table_header));
+
+	acpi_fill_header(ssdt, "SSDT");
+	ssdt->revision = acpi_get_table_revision(ACPITAB_SSDT);
+	ssdt->aslc_revision = 1;
+	ssdt->length = sizeof(struct acpi_table_header);
+
+	acpi_inc(ctx, sizeof(struct acpi_table_header));
+
+	acpi_fill_ssdt(ctx);
+
+	/* (Re)calculate length and checksum. */
+	ssdt->length = ctx->current - (void *)ssdt;
+	ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length);
+}
+
 /*
  * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
  */
@@ -363,6 +390,7 @@ ulong write_acpi_tables(ulong start_addr)
 	struct acpi_facs *facs;
 	struct acpi_table_header *dsdt;
 	struct acpi_fadt *fadt;
+	struct acpi_table_header *ssdt;
 	struct acpi_mcfg *mcfg;
 	struct acpi_madt *madt;
 	struct acpi_csrt *csrt;
@@ -385,11 +413,20 @@ ulong write_acpi_tables(ulong start_addr)
 
 	debug("ACPI:    * DSDT\n");
 	dsdt = ctx->current;
+
+	/* Put the table header first */
 	memcpy(dsdt, &AmlCode, sizeof(struct acpi_table_header));
 	acpi_inc(ctx, sizeof(struct acpi_table_header));
+
+	/* If the table is not empty, allow devices to inject things */
+	if (dsdt->length >= sizeof(struct acpi_table_header))
+		acpi_inject_dsdt(ctx);
+
+	/* Copy in the AML code itself if any (after the header) */
 	memcpy(ctx->current,
 	       (char *)&AmlCode + sizeof(struct acpi_table_header),
 	       dsdt->length - sizeof(struct acpi_table_header));
+
 	acpi_inc_align(ctx, dsdt->length - sizeof(struct acpi_table_header));
 
 	/* Pack GNVS into the ACPI table area */
@@ -404,12 +441,23 @@ ulong write_acpi_tables(ulong start_addr)
 		}
 	}
 
-	/* Update DSDT checksum since we patched the GNVS address */
+	/*
+	 * Recalculate the length and update the DSDT checksum since we patched
+	 * the GNVS address. Set the checksum to zero since it is part of the
+	 * region being checksummed.
+	 */
+	dsdt->length = ctx->current - (void *)dsdt;
 	dsdt->checksum = 0;
 	dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length);
 
-	/* Fill in platform-specific global NVS variables */
-	acpi_create_gnvs(ctx->current);
+	/*
+	 * Fill in platform-specific global NVS variables. If this fails we
+	 * cannot return the error but this should only happen while debugging.
+	 */
+	addr = acpi_create_gnvs(ctx->current);
+	if (IS_ERR_VALUE(addr))
+		printf("Error: Failed to create GNVS\n");
+
 	acpi_inc_align(ctx, sizeof(struct acpi_global_nvs));
 
 	debug("ACPI:    * FADT\n");
@@ -418,11 +466,13 @@ ulong write_acpi_tables(ulong start_addr)
 	acpi_create_fadt(fadt, facs, dsdt);
 	acpi_add_table(ctx, fadt);
 
-	debug("ACPI:    * MADT\n");
-	madt = ctx->current;
-	acpi_create_madt(madt);
-	acpi_inc_align(ctx, madt->header.length);
-	acpi_add_table(ctx, madt);
+	debug("ACPI:     * SSDT\n");
+	ssdt = (struct acpi_table_header *)ctx->current;
+	acpi_create_ssdt(ctx, ssdt, OEM_TABLE_ID);
+	if (ssdt->length > sizeof(struct acpi_table_header)) {
+		acpi_inc_align(ctx, ssdt->length);
+		acpi_add_table(ctx, ssdt);
+	}
 
 	debug("ACPI:    * MCFG\n");
 	mcfg = ctx->current;
@@ -430,11 +480,18 @@ ulong write_acpi_tables(ulong start_addr)
 	acpi_inc_align(ctx, mcfg->header.length);
 	acpi_add_table(ctx, mcfg);
 
+	debug("ACPI:    * MADT\n");
+	madt = ctx->current;
+	acpi_create_madt(madt);
+	acpi_inc_align(ctx, madt->header.length);
+	acpi_add_table(ctx, madt);
+
 	debug("ACPI:    * CSRT\n");
 	csrt = ctx->current;
-	acpi_create_csrt(csrt);
-	acpi_inc_align(ctx, csrt->header.length);
-	acpi_add_table(ctx, csrt);
+	if (!acpi_create_csrt(csrt)) {
+		acpi_inc_align(ctx, csrt->header.length);
+		acpi_add_table(ctx, csrt);
+	}
 
 	debug("ACPI:    * SPCR\n");
 	spcr = ctx->current;

+ 3 - 3
arch/x86/lib/coreboot_table.c

@@ -21,11 +21,11 @@ int high_table_reserve(void)
 	gd->arch.high_table_ptr = gd->start_addr_sp;
 
 	/* clear the memory */
-#ifdef CONFIG_HAVE_ACPI_RESUME
-	if (gd->arch.prev_sleep_state != ACPI_S3)
-#endif
+	if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) &&
+	    gd->arch.prev_sleep_state != ACPI_S3) {
 		memset((void *)gd->arch.high_table_ptr, 0,
 		       CONFIG_HIGH_TABLE_SIZE);
+	}
 
 	gd->start_addr_sp &= ~0xf;
 

+ 1 - 3
arch/x86/lib/fsp/fsp_common.c

@@ -47,7 +47,7 @@ int fsp_init_phase_pci(void)
 	return status ? -EPERM : 0;
 }
 
-void board_final_cleanup(void)
+void board_final_init(void)
 {
 	u32 status;
 
@@ -60,7 +60,6 @@ void board_final_cleanup(void)
 		debug("OK\n");
 }
 
-#ifdef CONFIG_HAVE_ACPI_RESUME
 int fsp_save_s3_stack(void)
 {
 	struct udevice *dev;
@@ -84,4 +83,3 @@ int fsp_save_s3_stack(void)
 
 	return 0;
 }
-#endif

+ 15 - 11
arch/x86/lib/fsp/fsp_dram.c

@@ -117,17 +117,21 @@ unsigned int install_e820_map(unsigned int max_entries,
 	entries[num_entries].type = E820_RESERVED;
 	num_entries++;
 
-#ifdef CONFIG_HAVE_ACPI_RESUME
-	/*
-	 * Everything between U-Boot's stack and ram top needs to be
-	 * reserved in order for ACPI S3 resume to work.
-	 */
-	entries[num_entries].addr = gd->start_addr_sp - CONFIG_STACK_SIZE;
-	entries[num_entries].size = gd->ram_top - gd->start_addr_sp +
-		CONFIG_STACK_SIZE;
-	entries[num_entries].type = E820_RESERVED;
-	num_entries++;
-#endif
+	if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
+		ulong stack_size;
+
+		stack_size = CONFIG_IS_ENABLED(HAVE_ACPI_RESUME,
+					       (CONFIG_STACK_SIZE), (0));
+		/*
+		 * Everything between U-Boot's stack and ram top needs to be
+		 * reserved in order for ACPI S3 resume to work.
+		 */
+		entries[num_entries].addr = gd->start_addr_sp - stack_size;
+		entries[num_entries].size = gd->ram_top - gd->start_addr_sp +
+			stack_size;
+		entries[num_entries].type = E820_RESERVED;
+		num_entries++;
+	}
 
 	return num_entries;
 }

+ 9 - 7
arch/x86/lib/fsp1/fsp_common.c

@@ -46,10 +46,12 @@ int arch_fsp_init(void)
 	void *nvs;
 	int stack = CONFIG_FSP_TEMP_RAM_ADDR;
 	int boot_mode = BOOT_FULL_CONFIG;
-#ifdef CONFIG_HAVE_ACPI_RESUME
-	int prev_sleep_state = chipset_prev_sleep_state();
-	gd->arch.prev_sleep_state = prev_sleep_state;
-#endif
+	int prev_sleep_state;
+
+	if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) {
+		prev_sleep_state = chipset_prev_sleep_state();
+		gd->arch.prev_sleep_state = prev_sleep_state;
+	}
 
 	if (!gd->arch.hob_list) {
 		if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
@@ -57,8 +59,8 @@ int arch_fsp_init(void)
 		else
 			nvs = NULL;
 
-#ifdef CONFIG_HAVE_ACPI_RESUME
-		if (prev_sleep_state == ACPI_S3) {
+		if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) &&
+		    prev_sleep_state == ACPI_S3) {
 			if (nvs == NULL) {
 				/* If waking from S3 and no cache then */
 				debug("No MRC cache found in S3 resume path\n");
@@ -79,7 +81,7 @@ int arch_fsp_init(void)
 			stack = cmos_read32(CMOS_FSP_STACK_ADDR);
 			boot_mode = BOOT_ON_S3_RESUME;
 		}
-#endif
+
 		/*
 		 * The first time we enter here, call fsp_init().
 		 * Note the execution does not return to this function,

+ 3 - 4
arch/x86/lib/fsp2/fsp_dram.c

@@ -27,11 +27,10 @@ int dram_init(void)
 		return 0;
 	}
 	if (spl_phase() == PHASE_SPL) {
-#ifdef CONFIG_HAVE_ACPI_RESUME
-		bool s3wake = gd->arch.prev_sleep_state == ACPI_S3;
-#else
 		bool s3wake = false;
-#endif
+
+		s3wake = IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) &&
+			gd->arch.prev_sleep_state == ACPI_S3;
 
 		ret = fsp_memory_init(s3wake,
 			      IS_ENABLED(CONFIG_APL_BOOT_FROM_FAST_SPI_FLASH));

+ 19 - 5
arch/x86/lib/fsp2/fsp_meminit.c

@@ -9,6 +9,7 @@
 #include <common.h>
 #include <binman.h>
 #include <bootstage.h>
+#include <dm.h>
 #include <log.h>
 #include <asm/mrccache.h>
 #include <asm/fsp/fsp_infoheader.h>
@@ -63,8 +64,10 @@ int fsp_memory_init(bool s3wake, bool use_spi_flash)
 	struct fsp_header *hdr;
 	struct hob_header *hob;
 	struct udevice *dev;
+	int delay;
 	int ret;
 
+	log_debug("Locating FSP\n");
 	ret = fsp_locate_fsp(FSP_M, &entry, use_spi_flash, &dev, &hdr, NULL);
 	if (ret)
 		return log_msg_ret("locate FSP", ret);
@@ -76,21 +79,32 @@ int fsp_memory_init(bool s3wake, bool use_spi_flash)
 		return log_msg_ret("Bad UPD signature", -EPERM);
 	memcpy(&upd, fsp_upd, sizeof(upd));
 
+	delay = dev_read_u32_default(dev, "fspm,training-delay", 0);
 	ret = fspm_update_config(dev, &upd);
-	if (ret)
-		return log_msg_ret("Could not setup config", ret);
-
-	debug("SDRAM init...");
+	if (ret) {
+		if (ret != -ENOENT)
+			return log_msg_ret("Could not setup config", ret);
+	} else {
+		delay = 0;
+	}
+
+	if (delay)
+		printf("SDRAM training (%d seconds)...", delay);
+	else
+		log_debug("SDRAM init...");
 	bootstage_start(BOOTSTAGE_ID_ACCUM_FSP_M, "fsp-m");
 	func = (fsp_memory_init_func)(hdr->img_base + hdr->fsp_mem_init);
 	ret = func(&upd, &hob);
 	bootstage_accum(BOOTSTAGE_ID_ACCUM_FSP_M);
 	cpu_reinit_fpu();
+	if (delay)
+		printf("done\n");
+	else
+		log_debug("done\n");
 	if (ret)
 		return log_msg_ret("SDRAM init fail\n", ret);
 
 	gd->arch.hob_list = hob;
-	debug("done\n");
 
 	ret = fspm_done(dev);
 	if (ret)

+ 1 - 0
arch/x86/lib/fsp2/fsp_silicon_init.c

@@ -32,6 +32,7 @@ int fsp_silicon_init(bool s3wake, bool use_spi_flash)
 			     &rom_offset);
 	if (ret)
 		return log_msg_ret("locate FSP", ret);
+	binman_set_rom_offset(rom_offset);
 	gd->arch.fsp_s_hdr = hdr;
 
 	/* Copy over the default config */

+ 28 - 10
arch/x86/lib/tables.c

@@ -4,6 +4,7 @@
  */
 
 #include <common.h>
+#include <log.h>
 #include <malloc.h>
 #include <smbios.h>
 #include <acpi/acpi_table.h>
@@ -20,21 +21,32 @@
  */
 typedef ulong (*table_write)(ulong addr);
 
-static table_write table_write_funcs[] = {
+/**
+ * struct table_info - Information about each table to write
+ *
+ * @name: Name of table (for debugging)
+ * @write: Function to call to write this table
+ */
+struct table_info {
+	const char *name;
+	table_write write;
+};
+
+static struct table_info table_list[] = {
 #ifdef CONFIG_GENERATE_PIRQ_TABLE
-	write_pirq_routing_table,
+	{ "pirq", write_pirq_routing_table },
 #endif
 #ifdef CONFIG_GENERATE_SFI_TABLE
-	write_sfi_table,
+	{ "sfi", write_sfi_table, },
 #endif
 #ifdef CONFIG_GENERATE_MP_TABLE
-	write_mp_table,
+	{ "mp", write_mp_table, },
 #endif
 #ifdef CONFIG_GENERATE_ACPI_TABLE
-	write_acpi_tables,
+	{ "acpi", write_acpi_tables, },
 #endif
 #ifdef CONFIG_GENERATE_SMBIOS_TABLE
-	write_smbios_table,
+	{ "smbios", write_smbios_table, },
 #endif
 };
 
@@ -58,19 +70,22 @@ void write_tables(void)
 	u32 rom_table_end;
 #ifdef CONFIG_SEABIOS
 	u32 high_table, table_size;
-	struct memory_area cfg_tables[ARRAY_SIZE(table_write_funcs) + 1];
+	struct memory_area cfg_tables[ARRAY_SIZE(table_list) + 1];
 #endif
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(table_write_funcs); i++) {
-		rom_table_end = table_write_funcs[i](rom_table_start);
+	debug("Writing tables to %x:\n", rom_table_start);
+	for (i = 0; i < ARRAY_SIZE(table_list); i++) {
+		const struct table_info *table = &table_list[i];
+
+		rom_table_end = table->write(rom_table_start);
 		rom_table_end = ALIGN(rom_table_end, ROM_TABLE_ALIGN);
 
 #ifdef CONFIG_SEABIOS
 		table_size = rom_table_end - rom_table_start;
 		high_table = (u32)high_table_malloc(table_size);
 		if (high_table) {
-			table_write_funcs[i](high_table);
+			table->write(high_table);
 
 			cfg_tables[i].start = high_table;
 			cfg_tables[i].size = table_size;
@@ -79,6 +94,8 @@ void write_tables(void)
 		}
 #endif
 
+		debug("- wrote '%s' to %x, end %x\n", table->name,
+		      rom_table_start, rom_table_end);
 		rom_table_start = rom_table_end;
 	}
 
@@ -87,4 +104,5 @@ void write_tables(void)
 	cfg_tables[i].size = 0;
 	write_coreboot_table(CB_TABLE_ADDR, cfg_tables);
 #endif
+	debug("- done writing tables\n");
 }

+ 0 - 10
arch/x86/lib/zimage.c

@@ -304,13 +304,6 @@ int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot,
 	return 0;
 }
 
-void setup_pcat_compatibility(void)
-	__attribute__((weak, alias("__setup_pcat_compatibility")));
-
-void __setup_pcat_compatibility(void)
-{
-}
-
 int do_zboot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	struct boot_params *base_ptr;
@@ -323,9 +316,6 @@ int do_zboot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 
 	disable_interrupts();
 
-	/* Setup board for maximum PC/AT Compatibility */
-	setup_pcat_compatibility();
-
 	if (argc >= 2) {
 		/* argv[1] holds the address of the bzImage */
 		s = argv[1];

+ 13 - 2
cmd/acpi.c

@@ -153,6 +153,17 @@ static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc,
 	return 0;
 }
 
+static int do_acpi_items(struct cmd_tbl *cmdtp, int flag, int argc,
+			 char *const argv[])
+{
+	bool dump_contents;
+
+	dump_contents = argc >= 2 && !strcmp("-d", argv[1]);
+	acpi_dump_items(dump_contents ? ACPI_DUMP_CONTENTS : ACPI_DUMP_LIST);
+
+	return 0;
+}
+
 static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc,
 			char *const argv[])
 {
@@ -160,8 +171,6 @@ static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc,
 	char sig[ACPI_NAME_LEN];
 	int ret;
 
-	if (argc < 2)
-		return CMD_RET_USAGE;
 	name = argv[1];
 	if (strlen(name) != ACPI_NAME_LEN) {
 		printf("Table name '%s' must be four characters\n", name);
@@ -179,8 +188,10 @@ static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc,
 
 static char acpi_help_text[] =
 	"list - list ACPI tables\n"
+	"acpi items [-d]  - List/dump each piece of ACPI data from devices\n"
 	"acpi dump <name> - Dump ACPI table";
 
 U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text,
 	U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list),
+	U_BOOT_SUBCMD_MKENT(items, 2, 1, do_acpi_items),
 	U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump));

+ 1 - 0
configs/chromebook_coral_defconfig

@@ -95,6 +95,7 @@ CONFIG_TPM2_CR50_I2C=y
 CONFIG_USB_XHCI_HCD=y
 CONFIG_USB_STORAGE=y
 CONFIG_USB_KEYBOARD=y
+CONFIG_VIDEO_COPY=y
 CONFIG_SPL_FS_CBFS=y
 # CONFIG_SPL_USE_TINY_PRINTF is not set
 CONFIG_TPL_USE_TINY_PRINTF=y

+ 4 - 0
configs/sandbox_defconfig

@@ -149,7 +149,9 @@ CONFIG_P2SB=y
 CONFIG_PWRSEQ=y
 CONFIG_SPL_PWRSEQ=y
 CONFIG_I2C_EEPROM=y
+CONFIG_MMC_PCI=y
 CONFIG_MMC_SANDBOX=y
+CONFIG_MMC_SDHCI=y
 CONFIG_MTD=y
 CONFIG_SPI_FLASH_SANDBOX=y
 CONFIG_SPI_FLASH_ATMEL=y
@@ -208,6 +210,8 @@ CONFIG_SANDBOX_SERIAL=y
 CONFIG_SMEM=y
 CONFIG_SANDBOX_SMEM=y
 CONFIG_SOUND=y
+CONFIG_SOUND_DA7219=y
+CONFIG_SOUND_MAX98357A=y
 CONFIG_SOUND_SANDBOX=y
 CONFIG_SANDBOX_SPI=y
 CONFIG_SPMI=y

+ 9 - 0
doc/device-tree-bindings/chosen.txt

@@ -134,3 +134,12 @@ Example
 		phandlepart = <&mmc 1>;
 	};
 };
+
+u-boot,acpi-ssdt-order
+----------------------
+
+This provides the ordering to use when writing device data to the ACPI SSDT
+(Secondary System Descriptor Table). Each cell is a phandle pointer to a device
+node to add. The ACPI information is written in this order.
+
+If the ordering does not include all nodes, an error is generated.

+ 36 - 0
doc/device-tree-bindings/device.txt

@@ -17,6 +17,10 @@ the acpi,compatible property.
     System) Device Name)
  - acpi,hid : Contains the string to use as the HID (Hardware ID)
     identifier _HID
+ - acpi,path : Specifies the full ACPI path for a device. This overrides the
+    normal path built from the driver-model hierarchy
+ - acpi,name : Provides the ACPI name for a device, which is a string consisting
+   of four alphanumeric character (upper case)
  - acpi,uid : _UID value for device
  - linux,probed : Tells U-Boot to add 'linux,probed' to the ACPI tables so that
     Linux will only load the driver if the device can be detected (e.g. on I2C
@@ -34,3 +38,35 @@ elan_touchscreen: elan-touchscreen@10 {
 	interrupts-extended = <&acpi_gpe GPIO_21_IRQ IRQ_TYPE_EDGE_FALLING>;
 	linux,probed;
 };
+
+pcie-a0@14,0 {
+	reg = <0x0000a000 0 0 0 0>;
+	acpi,name = "RP01";
+	wifi: wifi {
+		compatible = "intel,generic-wifi";
+		acpi,ddn = "Intel WiFi";
+		acpi,name = "WF00";
+		interrupts-extended = <&acpi_gpe 0x3c 0>;
+	};
+};
+
+p2sb: p2sb@d,0 {
+	u-boot,dm-pre-reloc;
+	reg = <0x02006810 0 0 0 0>;
+	compatible = "intel,apl-p2sb";
+	early-regs = <IOMAP_P2SB_BAR 0x100000>;
+	pci,no-autoconfig;
+
+	n {
+		compatible = "intel,apl-pinctrl";
+		u-boot,dm-pre-reloc;
+		intel,p2sb-port-id = <PID_GPIO_N>;
+		acpi,path = "\\_SB.GPO0";
+		gpio_n: gpio-n {
+			compatible = "intel,gpio";
+			u-boot,dm-pre-reloc;
+			gpio-controller;
+			#gpio-cells = <2>;
+			linux-name = "INT3452:00";
+		};
+	};

+ 4 - 0
doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-m.txt

@@ -17,6 +17,10 @@ values of the FSP-M are used.
 [2] https://github.com/IntelFsp/FSP/tree/master/ApolloLakeFspBinPkg/Docs
 
 Optional properties:
+- fspm,training-delay: Time taken to train DDR memory if there is no cached MRC
+    data, in seconds. This is used to show a message if possible. For Chromebook
+    Coral this is typically 21 seconds. For an APL board with 1GB of RAM, it may
+    be only 6 seconds.
 - fspm,serial-debug-port-address: Debug Serial Port Base address
 - fspm,serial-debug-port-type: Debug Serial Port Type
   0: NONE

+ 113 - 0
doc/device-tree-bindings/sound/da7219.txt

@@ -0,0 +1,113 @@
+Dialog Semiconductor DA7219 Audio Codec bindings
+
+DA7219 is an audio codec with advanced accessory detect features.
+
+======
+
+Required properties:
+- compatible : Should be "dlg,da7219"
+- reg: Specifies the I2C slave address
+
+- interrupts : IRQ line info for DA7219.
+  (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
+   further information relating to interrupt properties)
+
+- VDD-supply: VDD power supply for the device
+- VDDMIC-supply: VDDMIC power supply for the device
+- VDDIO-supply: VDDIO power supply for the device
+  (See Documentation/devicetree/bindings/regulator/regulator.txt for further
+   information relating to regulators)
+
+Optional properties:
+- interrupt-names : Name associated with interrupt line. Should be "wakeup" if
+  interrupt is to be used to wake system, otherwise "irq" should be used.
+- wakeup-source: Flag to indicate this device can wake system (suspend/resume).
+
+- #clock-cells :  Should be set to '<0>', only one clock source provided;
+- clock-output-names : Name given for DAI clocks output;
+
+- clocks : phandle and clock specifier for codec MCLK.
+- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
+
+- dlg,micbias-lvl : Voltage (mV) for Mic Bias
+	[<1600>, <1800>, <2000>, <2200>, <2400>, <2600>]
+- dlg,mic-amp-in-sel : Mic input source type
+	["diff", "se_p", "se_n"]
+- dlg,mclk-name : String name of MCLK for ACPI
+
+Deprecated properties:
+- dlg,ldo-lvl : Required internal LDO voltage (mV) level for digital engine
+  (LDO unavailable in production HW so property no longer required).
+
+======
+
+Child node - 'da7219_aad':
+
+Optional properties:
+- dlg,micbias-pulse-lvl : Mic bias higher voltage pulse level (mV).
+	[<2800>, <2900>]
+- dlg,micbias-pulse-time : Mic bias higher voltage pulse duration (ms)
+- dlg,btn-cfg : Periodic button press measurements for 4-pole jack (ms)
+	[<2>, <5>, <10>, <50>, <100>, <200>, <500>]
+- dlg,mic-det-thr : Impedance threshold for mic detection measurement (Ohms)
+	[<200>, <500>, <750>, <1000>]
+- dlg,jack-ins-deb : Debounce time for jack insertion (ms)
+	[<5>, <10>, <20>, <50>, <100>, <200>, <500>, <1000>]
+- dlg,jack-det-rate: Jack type detection latency (3/4 pole)
+	["32ms_64ms", "64ms_128ms", "128ms_256ms", "256ms_512ms"]
+- dlg,jack-rem-deb : Debounce time for jack removal (ms)
+	[<1>, <5>, <10>, <20>]
+- dlg,a-d-btn-thr : Impedance threshold between buttons A and D
+	[0x0 - 0xFF]
+- dlg,d-b-btn-thr : Impedance threshold between buttons D and B
+	[0x0 - 0xFF]
+- dlg,b-c-btn-thr : Impedance threshold between buttons B and C
+	[0x0 - 0xFF]
+- dlg,c-mic-btn-thr : Impedance threshold between button C and Mic
+	[0x0 - 0xFF]
+- dlg,btn-avg : Number of 8-bit readings for averaged button measurement
+	[<1>, <2>, <4>, <8>]
+- dlg,adc-1bit-rpt : Repeat count for 1-bit button measurement
+	[<1>, <2>, <4>, <8>]
+
+======
+
+Example:
+
+	codec: da7219@1a {
+		compatible = "dlg,da7219";
+		reg = <0x1a>;
+
+		interrupt-parent = <&gpio6>;
+		interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+
+		VDD-supply = <&reg_audio>;
+		VDDMIC-supply = <&reg_audio>;
+		VDDIO-supply = <&reg_audio>;
+
+		#clock-cells = <0>;
+		clock-output-names = "dai-clks";
+
+		clocks = <&clks 201>;
+		clock-names = "mclk";
+
+		dlg,ldo-lvl = <1200>;
+		dlg,micbias-lvl = <2600>;
+		dlg,mic-amp-in-sel = "diff";
+
+		da7219_aad {
+			dlg,btn-cfg = <50>;
+			dlg,mic-det-thr = <500>;
+			dlg,jack-ins-deb = <20>;
+			dlg,jack-det-rate = "32ms_64ms";
+			dlg,jack-rem-deb = <1>;
+
+			dlg,a-d-btn-thr = <0xa>;
+			dlg,d-b-btn-thr = <0x16>;
+			dlg,b-c-btn-thr = <0x21>;
+			dlg,c-mic-btn-thr = <0x3E>;
+
+			dlg,btn-avg = <4>;
+			dlg,adc-1bit-rpt = <1>;
+		};
+	};

+ 22 - 0
doc/device-tree-bindings/sound/max98357a.txt

@@ -0,0 +1,22 @@
+Maxim MAX98357A audio DAC
+
+This node models the Maxim MAX98357A DAC.
+
+Required properties:
+- compatible   : "maxim,max98357a"
+
+Optional properties:
+- sdmode-gpios : GPIO specifier for the chip's SD_MODE pin.
+        If this option is not specified then driver does not manage
+        the pin state (e.g. chip is always on).
+- sdmode-delay : specify delay time for SD_MODE pin.
+        If this option is specified, which means it's required i2s clocks
+        ready before SD_MODE is unmuted in order to avoid the speaker pop noise.
+        It's observed that 5ms is sufficient.
+
+Example:
+
+max98357a {
+	compatible = "maxim,max98357a";
+	sdmode-gpios = <&qcom_pinmux 25 0>;
+};

+ 1 - 1
drivers/core/Kconfig

@@ -270,7 +270,7 @@ config DM_DEV_READ_INLINE
 
 config ACPIGEN
 	bool "Support ACPI table generation in driver model"
-	default y if SANDBOX || GENERATE_ACPI_TABLE
+	default y if SANDBOX || (GENERATE_ACPI_TABLE && !QEMU)
 	help
 	  This option enables generation of ACPI tables using driver-model
 	  devices. It adds a new operation struct to each driver, to support

+ 259 - 4
drivers/core/acpi.c

@@ -11,18 +11,51 @@
 #include <common.h>
 #include <dm.h>
 #include <log.h>
+#include <malloc.h>
+#include <acpi/acpi_device.h>
 #include <dm/acpi.h>
 #include <dm/device-internal.h>
 #include <dm/root.h>
 
+#define MAX_ACPI_ITEMS	100
+
+/* Type of table that we collected */
+enum gen_type_t {
+	TYPE_NONE,
+	TYPE_SSDT,
+	TYPE_DSDT,
+};
+
 /* Type of method to call */
 enum method_t {
 	METHOD_WRITE_TABLES,
+	METHOD_FILL_SSDT,
+	METHOD_INJECT_DSDT,
+	METHOD_SETUP_NHLT,
 };
 
 /* Prototype for all methods */
 typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx);
 
+/**
+ * struct acpi_item - Holds info about ACPI data generated by a driver method
+ *
+ * @dev: Device that generated this data
+ * @type: Table type it refers to
+ * @buf: Buffer containing the data
+ * @size: Size of the data in bytes
+ */
+struct acpi_item {
+	struct udevice *dev;
+	enum gen_type_t type;
+	char *buf;
+	int size;
+};
+
+/* List of ACPI items collected */
+static struct acpi_item acpi_item[MAX_ACPI_ITEMS];
+static int item_count;
+
 int acpi_copy_name(char *out_name, const char *name)
 {
 	strncpy(out_name, name, ACPI_NAME_LEN);
@@ -34,12 +67,173 @@ int acpi_copy_name(char *out_name, const char *name)
 int acpi_get_name(const struct udevice *dev, char *out_name)
 {
 	struct acpi_ops *aops;
+	const char *name;
+	int ret;
 
 	aops = device_get_acpi_ops(dev);
 	if (aops && aops->get_name)
 		return aops->get_name(dev, out_name);
+	name = dev_read_string(dev, "acpi,name");
+	if (name)
+		return acpi_copy_name(out_name, name);
+	ret = acpi_device_infer_name(dev, out_name);
+	if (ret)
+		return log_msg_ret("dev", ret);
+
+	return 0;
+}
+
+int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen)
+{
+	const char *path;
+	int ret;
 
-	return -ENOSYS;
+	path = dev_read_string(dev, "acpi,path");
+	if (path) {
+		if (strlen(path) >= maxlen)
+			return -E2BIG;
+		strcpy(out_path, path);
+		return 0;
+	}
+	ret = acpi_device_path(dev, out_path, maxlen);
+	if (ret)
+		return log_msg_ret("dev", ret);
+
+	return 0;
+}
+
+/**
+ * acpi_add_item() - Add a new item to the list of data collected
+ *
+ * @ctx: ACPI context
+ * @dev: Device that generated the data
+ * @type: Table type it refers to
+ * @start: The start of the data (the end is obtained from ctx->current)
+ * @return 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory
+ */
+static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev,
+			 enum gen_type_t type, void *start)
+{
+	struct acpi_item *item;
+	void *end = ctx->current;
+
+	if (item_count == MAX_ACPI_ITEMS) {
+		log_err("Too many items\n");
+		return log_msg_ret("mem", -ENOSPC);
+	}
+
+	item = &acpi_item[item_count];
+	item->dev = dev;
+	item->type = type;
+	item->size = end - start;
+	if (!item->size)
+		return 0;
+	item->buf = malloc(item->size);
+	if (!item->buf)
+		return log_msg_ret("mem", -ENOMEM);
+	memcpy(item->buf, start, item->size);
+	item_count++;
+	log_debug("* %s: Added type %d, %p, size %x\n", dev->name, type, start,
+		  item->size);
+
+	return 0;
+}
+
+void acpi_dump_items(enum acpi_dump_option option)
+{
+	int i;
+
+	for (i = 0; i < item_count; i++) {
+		struct acpi_item *item = &acpi_item[i];
+
+		printf("dev '%s', type %d, size %x\n", item->dev->name,
+		       item->type, item->size);
+		if (option == ACPI_DUMP_CONTENTS) {
+			print_buffer(0, item->buf, 1, item->size, 0);
+			printf("\n");
+		}
+	}
+}
+
+static struct acpi_item *find_acpi_item(const char *devname)
+{
+	int i;
+
+	for (i = 0; i < item_count; i++) {
+		struct acpi_item *item = &acpi_item[i];
+
+		if (!strcmp(devname, item->dev->name))
+			return item;
+	}
+
+	return NULL;
+}
+
+/**
+ * sort_acpi_item_type - Sort the ACPI items into the desired order
+ *
+ * This looks up the ordering in the device tree and then adds each item one by
+ * one into the supplied buffer
+ *
+ * @ctx: ACPI context
+ * @start: Start position to put the sorted items. The items will follow each
+ *	other in sorted order
+ * @type: Type of items to sort
+ * @return 0 if OK, -ve on error
+ */
+static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start,
+			       enum gen_type_t type)
+{
+	const u32 *order;
+	int size;
+	int count;
+	void *ptr;
+	void *end = ctx->current;
+
+	ptr = start;
+	order = ofnode_read_chosen_prop(type == TYPE_DSDT ?
+					"u-boot,acpi-dsdt-order" :
+					"u-boot,acpi-ssdt-order", &size);
+	if (!order) {
+		log_warning("Failed to find ordering, leaving as is\n");
+		return 0;
+	}
+
+	/*
+	 * This algorithm rewrites the context buffer without changing its
+	 * length. So there is no need to update ctx-current
+	 */
+	count = size / sizeof(u32);
+	while (count--) {
+		struct acpi_item *item;
+		const char *name;
+		ofnode node;
+
+		node = ofnode_get_by_phandle(fdt32_to_cpu(*order++));
+		name = ofnode_get_name(node);
+		item = find_acpi_item(name);
+		if (!item) {
+			log_err("Failed to find item '%s'\n", name);
+			return log_msg_ret("find", -ENOENT);
+		}
+		if (item->type == type) {
+			log_debug("   - add %s\n", item->dev->name);
+			memcpy(ptr, item->buf, item->size);
+			ptr += item->size;
+		}
+	}
+
+	/*
+	 * If the sort order is missing an item then the output will be too
+	 * small. Report this error since the item needs to be added to the
+	 * ordering for the ACPI tables to be complete.
+	 */
+	if (ptr != end) {
+		log_warning("*** Missing bytes: ptr=%p, end=%p\n", ptr, end);
+		return -ENXIO;
+	}
+
+	return 0;
 }
 
 acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
@@ -51,6 +245,12 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
 		switch (method) {
 		case METHOD_WRITE_TABLES:
 			return aops->write_tables;
+		case METHOD_FILL_SSDT:
+			return aops->fill_ssdt;
+		case METHOD_INJECT_DSDT:
+			return aops->inject_dsdt;
+		case METHOD_SETUP_NHLT:
+			return aops->setup_nhlt;
 		}
 	}
 
@@ -58,7 +258,7 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
 }
 
 int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
-			enum method_t method)
+			enum method_t method, enum gen_type_t type)
 {
 	struct udevice *dev;
 	acpi_method func;
@@ -66,6 +266,8 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
 
 	func = acpi_get_method(parent, method);
 	if (func) {
+		void *start = ctx->current;
+
 		log_debug("\n");
 		log_debug("- %s %p\n", parent->name, func);
 		ret = device_ofdata_to_platdata(parent);
@@ -74,9 +276,16 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
 		ret = func(parent, ctx);
 		if (ret)
 			return log_msg_ret("func", ret);
+
+		/* Add the item to the internal list */
+		if (type != TYPE_NONE) {
+			ret = acpi_add_item(ctx, parent, type, start);
+			if (ret)
+				return log_msg_ret("add", ret);
+		}
 	}
 	device_foreach_child(dev, parent) {
-		ret = acpi_recurse_method(ctx, dev, method);
+		ret = acpi_recurse_method(ctx, dev, method, type);
 		if (ret)
 			return log_msg_ret("recurse", ret);
 	}
@@ -84,13 +293,59 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
 	return 0;
 }
 
+int acpi_fill_ssdt(struct acpi_ctx *ctx)
+{
+	void *start = ctx->current;
+	int ret;
+
+	log_debug("Writing SSDT tables\n");
+	item_count = 0;
+	ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT);
+	log_debug("Writing SSDT finished, err=%d\n", ret);
+	ret = sort_acpi_item_type(ctx, start, TYPE_SSDT);
+	if (ret)
+		return log_msg_ret("build", ret);
+
+	return ret;
+}
+
+int acpi_inject_dsdt(struct acpi_ctx *ctx)
+{
+	void *start = ctx->current;
+	int ret;
+
+	log_debug("Writing DSDT tables\n");
+	item_count = 0;
+	ret = acpi_recurse_method(ctx, dm_root(), METHOD_INJECT_DSDT,
+				  TYPE_DSDT);
+	log_debug("Writing DSDT finished, err=%d\n", ret);
+	ret = sort_acpi_item_type(ctx, start, TYPE_DSDT);
+	if (ret)
+		return log_msg_ret("build", ret);
+
+	return ret;
+}
+
 int acpi_write_dev_tables(struct acpi_ctx *ctx)
 {
 	int ret;
 
 	log_debug("Writing device tables\n");
-	ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES);
+	ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES,
+				  TYPE_NONE);
 	log_debug("Writing finished, err=%d\n", ret);
 
 	return ret;
 }
+
+int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt)
+{
+	int ret;
+
+	log_debug("Setup NHLT\n");
+	ctx->nhlt = nhlt;
+	ret = acpi_recurse_method(ctx, dm_root(), METHOD_SETUP_NHLT, TYPE_NONE);
+	log_debug("Setup finished, err=%d\n", ret);
+
+	return ret;
+}

+ 13 - 0
drivers/core/root.c

@@ -12,6 +12,7 @@
 #include <log.h>
 #include <malloc.h>
 #include <linux/libfdt.h>
+#include <dm/acpi.h>
 #include <dm/device.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
@@ -377,10 +378,22 @@ int dm_init_and_scan(bool pre_reloc_only)
 	return 0;
 }
 
+#ifdef CONFIG_ACPIGEN
+static int root_acpi_get_name(const struct udevice *dev, char *out_name)
+{
+	return acpi_copy_name(out_name, "\\_SB");
+}
+
+struct acpi_ops root_acpi_ops = {
+	.get_name	= root_acpi_get_name,
+};
+#endif
+
 /* This is the root driver - all drivers are children of this */
 U_BOOT_DRIVER(root_driver) = {
 	.name	= "root_driver",
 	.id	= UCLASS_ROOT,
+	ACPI_OPS_PTR(&root_acpi_ops)
 };
 
 /* This is the root uclass */

+ 22 - 0
drivers/gpio/gpio-uclass.c

@@ -13,6 +13,7 @@
 #include <errno.h>
 #include <fdtdec.h>
 #include <malloc.h>
+#include <acpi/acpi_device.h>
 #include <asm/gpio.h>
 #include <dm/device_compat.h>
 #include <linux/bug.h>
@@ -855,6 +856,27 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio)
+{
+	struct dm_gpio_ops *ops;
+
+	memset(gpio, '\0', sizeof(*gpio));
+	if (!dm_gpio_is_valid(desc)) {
+		/* Indicate that the GPIO is not valid */
+		gpio->pin_count = 0;
+		gpio->pins[0] = 0;
+		return -EINVAL;
+	}
+
+	ops = gpio_get_ops(desc->dev);
+	if (!ops->get_acpi)
+		return -ENOSYS;
+
+	return ops->get_acpi(desc, gpio);
+}
+#endif
+
 int gpio_claim_vector(const int *gpio_num_array, const char *fmt)
 {
 	int i, ret;

+ 45 - 4
drivers/gpio/intel_gpio.c

@@ -12,6 +12,7 @@
 #include <pch.h>
 #include <pci.h>
 #include <syscon.h>
+#include <acpi/acpi_device.h>
 #include <asm/cpu.h>
 #include <asm/gpio.h>
 #include <asm/intel_pinctrl.h>
@@ -19,12 +20,15 @@
 #include <asm/io.h>
 #include <asm/pci.h>
 #include <asm/arch/gpio.h>
+#include <dm/acpi.h>
 #include <dt-bindings/gpio/x86-gpio.h>
 
 static int intel_gpio_direction_input(struct udevice *dev, uint offset)
 {
 	struct udevice *pinctrl = dev_get_parent(dev);
-	uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset);
+	uint config_offset;
+
+	config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
 
 	pcr_clrsetbits32(pinctrl, config_offset,
 			 PAD_CFG0_MODE_MASK | PAD_CFG0_TX_STATE |
@@ -38,7 +42,9 @@ static int intel_gpio_direction_output(struct udevice *dev, uint offset,
 				       int value)
 {
 	struct udevice *pinctrl = dev_get_parent(dev);
-	uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset);
+	uint config_offset;
+
+	config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
 
 	pcr_clrsetbits32(pinctrl, config_offset,
 			 PAD_CFG0_MODE_MASK | PAD_CFG0_RX_STATE |
@@ -68,10 +74,13 @@ static int intel_gpio_get_value(struct udevice *dev, uint offset)
 	return 0;
 }
 
-static int intel_gpio_set_value(struct udevice *dev, unsigned offset, int value)
+static int intel_gpio_set_value(struct udevice *dev, unsigned int offset,
+				int value)
 {
 	struct udevice *pinctrl = dev_get_parent(dev);
-	uint config_offset = intel_pinctrl_get_config_reg_addr(pinctrl, offset);
+	uint config_offset;
+
+	config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
 
 	pcr_clrsetbits32(pinctrl, config_offset, PAD_CFG0_TX_STATE,
 			 value ? PAD_CFG0_TX_STATE : 0);
@@ -121,6 +130,35 @@ static int intel_gpio_xlate(struct udevice *orig_dev, struct gpio_desc *desc,
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int intel_gpio_get_acpi(const struct gpio_desc *desc,
+			       struct acpi_gpio *gpio)
+{
+	struct udevice *pinctrl;
+	int ret;
+
+	if (!dm_gpio_is_valid(desc))
+		return -ENOENT;
+	pinctrl = dev_get_parent(desc->dev);
+
+	memset(gpio, '\0', sizeof(*gpio));
+
+	gpio->type = ACPI_GPIO_TYPE_IO;
+	gpio->pull = ACPI_GPIO_PULL_DEFAULT;
+	gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_OUTPUT;
+	gpio->polarity = ACPI_GPIO_ACTIVE_HIGH;
+	gpio->pin_count = 1;
+	gpio->pins[0] = intel_pinctrl_get_acpi_pin(pinctrl, desc->offset);
+	gpio->pin0_addr = intel_pinctrl_get_config_reg_addr(pinctrl,
+							    desc->offset);
+	ret = acpi_get_path(pinctrl, gpio->resource, sizeof(gpio->resource));
+	if (ret)
+		return log_msg_ret("resource", ret);
+
+	return 0;
+}
+#endif
+
 static int intel_gpio_probe(struct udevice *dev)
 {
 	return 0;
@@ -145,6 +183,9 @@ static const struct dm_gpio_ops gpio_intel_ops = {
 	.set_value		= intel_gpio_set_value,
 	.get_function		= intel_gpio_get_function,
 	.xlate			= intel_gpio_xlate,
+#if CONFIG_IS_ENABLED(ACPIGEN)
+	.get_acpi		= intel_gpio_get_acpi,
+#endif
 };
 
 static const struct udevice_id intel_intel_gpio_ids[] = {

+ 77 - 0
drivers/gpio/sandbox.c

@@ -8,7 +8,9 @@
 #include <fdtdec.h>
 #include <log.h>
 #include <malloc.h>
+#include <acpi/acpi_device.h>
 #include <asm/gpio.h>
+#include <dm/acpi.h>
 #include <dm/device_compat.h>
 #include <dm/lists.h>
 #include <dm/of.h>
@@ -197,6 +199,63 @@ static int sb_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int sb_gpio_get_acpi(const struct gpio_desc *desc,
+			    struct acpi_gpio *gpio)
+{
+	int ret;
+
+	/* Note that gpio_get_acpi() zeroes *gpio before calling here */
+	gpio->pin_count = 1;
+	gpio->pins[0] = desc->offset;
+	ret = acpi_device_scope(desc->dev, gpio->resource,
+				sizeof(gpio->resource));
+	if (ret)
+		return log_ret(ret);
+
+	/* All of these values are just used for testing */
+	if (desc->flags & GPIOD_ACTIVE_LOW) {
+		gpio->pin0_addr = 0x80012 + desc->offset;
+		gpio->type = ACPI_GPIO_TYPE_INTERRUPT;
+		gpio->pull = ACPI_GPIO_PULL_DOWN;
+		gpio->interrupt_debounce_timeout = 4321;
+
+		/* We use the GpioInt part */
+		gpio->irq.pin = desc->offset;
+		gpio->irq.polarity = ACPI_IRQ_ACTIVE_BOTH;
+		gpio->irq.shared = ACPI_IRQ_SHARED;
+		gpio->irq.wake = ACPI_IRQ_WAKE;
+
+		/* The GpioIo part is only used for testing */
+		gpio->polarity = ACPI_GPIO_ACTIVE_LOW;
+	} else {
+		gpio->pin0_addr = 0xc00dc + desc->offset;
+		gpio->type = ACPI_GPIO_TYPE_IO;
+		gpio->pull = ACPI_GPIO_PULL_UP;
+		gpio->interrupt_debounce_timeout = 0;
+
+		/* The GpioInt part is not used */
+
+		/* We use the GpioIo part */
+		gpio->output_drive_strength = 1234;
+		gpio->io_shared = true;
+		gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_INPUT;
+		gpio->polarity = 0;
+	}
+
+	return 0;
+}
+
+static int sb_gpio_get_name(const struct udevice *dev, char *out_name)
+{
+	return acpi_copy_name(out_name, "GPIO");
+}
+
+struct acpi_ops gpio_sandbox_acpi_ops = {
+	.get_name	= sb_gpio_get_name,
+};
+#endif /* ACPIGEN */
+
 static const struct dm_gpio_ops gpio_sandbox_ops = {
 	.direction_input	= sb_gpio_direction_input,
 	.direction_output	= sb_gpio_direction_output,
@@ -206,6 +265,9 @@ static const struct dm_gpio_ops gpio_sandbox_ops = {
 	.xlate			= sb_gpio_xlate,
 	.set_dir_flags		= sb_gpio_set_dir_flags,
 	.get_dir_flags		= sb_gpio_get_dir_flags,
+#if CONFIG_IS_ENABLED(ACPIGEN)
+	.get_acpi		= sb_gpio_get_acpi,
+#endif
 };
 
 static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev)
@@ -252,6 +314,7 @@ U_BOOT_DRIVER(sandbox_gpio) = {
 	.probe	= gpio_sandbox_probe,
 	.remove	= gpio_sandbox_remove,
 	.ops	= &gpio_sandbox_ops,
+	ACPI_OPS_PTR(&gpio_sandbox_acpi_ops)
 };
 
 U_BOOT_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias)
@@ -421,6 +484,13 @@ static int sb_pinctrl_get_pin_muxing(struct udevice *dev,
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int sb_pinctrl_get_name(const struct udevice *dev, char *out_name)
+{
+	return acpi_copy_name(out_name, "PINC");
+}
+#endif
+
 static int sandbox_pinctrl_probe(struct udevice *dev)
 {
 	struct sb_pinctrl_priv *priv = dev_get_priv(dev);
@@ -436,6 +506,12 @@ static struct pinctrl_ops sandbox_pinctrl_gpio_ops = {
 	.get_pin_muxing		= sb_pinctrl_get_pin_muxing,
 };
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+struct acpi_ops pinctrl_sandbox_acpi_ops = {
+	.get_name	= sb_pinctrl_get_name,
+};
+#endif
+
 static const struct udevice_id sandbox_pinctrl_gpio_match[] = {
 	{ .compatible = "sandbox,pinctrl-gpio" },
 	{ /* sentinel */ }
@@ -449,4 +525,5 @@ U_BOOT_DRIVER(sandbox_pinctrl_gpio) = {
 	.bind = dm_scan_fdt_dev,
 	.probe = sandbox_pinctrl_probe,
 	.priv_auto_alloc_size	= sizeof(struct sb_pinctrl_priv),
+	ACPI_OPS_PTR(&pinctrl_sandbox_acpi_ops)
 };

+ 31 - 5
drivers/i2c/designware_i2c.c

@@ -160,9 +160,9 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
 	min_tlow_cnt = calc_counts(ic_clk, info->min_scl_lowtime_ns);
 	min_thigh_cnt = calc_counts(ic_clk, info->min_scl_hightime_ns);
 
-	debug("dw_i2c: period %d rise %d fall %d tlow %d thigh %d spk %d\n",
-	      period_cnt, rise_cnt, fall_cnt, min_tlow_cnt, min_thigh_cnt,
-	      spk_cnt);
+	debug("dw_i2c: mode %d, ic_clk %d, speed %d, period %d rise %d fall %d tlow %d thigh %d spk %d\n",
+	      mode, ic_clk, info->speed, period_cnt, rise_cnt, fall_cnt,
+	      min_tlow_cnt, min_thigh_cnt, spk_cnt);
 
 	/*
 	 * Back-solve for hcnt and lcnt according to the following equations:
@@ -174,7 +174,7 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
 
 	if (hcnt < 0 || lcnt < 0) {
 		debug("dw_i2c: bad counts. hcnt = %d lcnt = %d\n", hcnt, lcnt);
-		return -EINVAL;
+		return log_msg_ret("counts", -EINVAL);
 	}
 
 	/*
@@ -333,6 +333,32 @@ static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base,
 	/* Restore back i2c now speed set */
 	if (ena == IC_ENABLE_0B)
 		dw_i2c_enable(i2c_base, true);
+	if (priv)
+		priv->config = config;
+
+	return 0;
+}
+
+int dw_i2c_gen_speed_config(const struct udevice *dev, int speed_hz,
+			    struct dw_i2c_speed_config *config)
+{
+	struct dw_i2c *priv = dev_get_priv(dev);
+	ulong rate;
+	int ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+	rate = clk_get_rate(&priv->clk);
+	if (IS_ERR_VALUE(rate))
+		return log_msg_ret("clk", -EINVAL);
+#else
+	rate = IC_CLK;
+#endif
+
+	ret = calc_bus_speed(priv, priv->regs, speed_hz, rate, config);
+	if (ret)
+		printf("%s: ret=%d\n", __func__, ret);
+	if (ret)
+		return log_msg_ret("calc_bus_speed", ret);
 
 	return 0;
 }
@@ -713,7 +739,7 @@ static int designware_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
 #if CONFIG_IS_ENABLED(CLK)
 	rate = clk_get_rate(&i2c->clk);
 	if (IS_ERR_VALUE(rate))
-		return -EINVAL;
+		return log_ret(-EINVAL);
 #else
 	rate = IC_CLK;
 #endif

+ 15 - 0
drivers/i2c/designware_i2c.h

@@ -205,6 +205,7 @@ struct dw_i2c {
 #if CONFIG_IS_ENABLED(CLK)
 	struct clk clk;
 #endif
+	struct dw_i2c_speed_config config;
 };
 
 extern const struct dm_i2c_ops designware_i2c_ops;
@@ -213,4 +214,18 @@ int designware_i2c_probe(struct udevice *bus);
 int designware_i2c_remove(struct udevice *dev);
 int designware_i2c_ofdata_to_platdata(struct udevice *bus);
 
+/**
+ * dw_i2c_gen_speed_config() - Calculate config info from requested speed
+ *
+ * Calculate the speed config from the given @speed_hz and return it so that
+ * it can be incorporated in ACPI tables
+ *
+ * @dev: I2C bus to check
+ * @speed_hz: Requested speed in Hz
+ * @config: Returns config to use for that speed
+ * @return 0 if OK, -ve on error
+ */
+int dw_i2c_gen_speed_config(const struct udevice *dev, int speed_hz,
+			    struct dw_i2c_speed_config *config);
+
 #endif /* __DW_I2C_H_ */

+ 95 - 1
drivers/i2c/designware_i2c_pci.c

@@ -9,7 +9,12 @@
 #include <dm.h>
 #include <log.h>
 #include <spl.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
 #include <asm/lpss.h>
+#include <dm/acpi.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
 #include "designware_i2c.h"
 
 enum {
@@ -87,6 +92,9 @@ static int designware_i2c_pci_bind(struct udevice *dev)
 {
 	char name[20];
 
+	if (dev_of_valid(dev))
+		return 0;
+
 	/*
 	 * Create a unique device name for PCI type devices
 	 * ToDo:
@@ -100,13 +108,98 @@ static int designware_i2c_pci_bind(struct udevice *dev)
 	 * be possible. We cannot use static data in drivers since they may be
 	 * used in SPL or before relocation.
 	 */
-	dev->req_seq = gd->arch.dw_i2c_num_cards++;
+	dev->req_seq = uclass_find_next_free_req_seq(UCLASS_I2C);
 	sprintf(name, "i2c_designware#%u", dev->req_seq);
 	device_set_name(dev, name);
 
 	return 0;
 }
 
+/*
+ * Write ACPI object to describe speed configuration.
+ *
+ * ACPI Object: Name ("xxxx", Package () { scl_lcnt, scl_hcnt, sda_hold }
+ *
+ * SSCN: I2C_SPEED_STANDARD
+ * FMCN: I2C_SPEED_FAST
+ * FPCN: I2C_SPEED_FAST_PLUS
+ * HSCN: I2C_SPEED_HIGH
+ */
+static void dw_i2c_acpi_write_speed_config(struct acpi_ctx *ctx,
+					   struct dw_i2c_speed_config *config)
+{
+	switch (config->speed_mode) {
+	case IC_SPEED_MODE_HIGH:
+		acpigen_write_name(ctx, "HSCN");
+		break;
+	case IC_SPEED_MODE_FAST_PLUS:
+		acpigen_write_name(ctx, "FPCN");
+		break;
+	case IC_SPEED_MODE_FAST:
+		acpigen_write_name(ctx, "FMCN");
+		break;
+	case IC_SPEED_MODE_STANDARD:
+	default:
+		acpigen_write_name(ctx, "SSCN");
+	}
+
+	/* Package () { scl_lcnt, scl_hcnt, sda_hold } */
+	acpigen_write_package(ctx, 3);
+	acpigen_write_word(ctx, config->scl_hcnt);
+	acpigen_write_word(ctx, config->scl_lcnt);
+	acpigen_write_dword(ctx, config->sda_hold);
+	acpigen_pop_len(ctx);
+}
+
+/*
+ * Generate I2C timing information into the SSDT for the OS driver to consume,
+ * optionally applying override values provided by the caller.
+ */
+static int dw_i2c_acpi_fill_ssdt(const struct udevice *dev,
+				 struct acpi_ctx *ctx)
+{
+	struct dw_i2c_speed_config config;
+	char path[ACPI_PATH_MAX];
+	u32 speeds[4];
+	uint speed;
+	int size;
+	int ret;
+
+	/* If no device-tree node, ignore this since we assume it isn't used */
+	if (!dev_of_valid(dev))
+		return 0;
+
+	ret = acpi_device_path(dev, path, sizeof(path));
+	if (ret)
+		return log_msg_ret("path", ret);
+
+	size = dev_read_size(dev, "i2c,speeds");
+	if (size < 0)
+		return log_msg_ret("i2c,speeds", -EINVAL);
+
+	size /= sizeof(u32);
+	if (size > ARRAY_SIZE(speeds))
+		return log_msg_ret("array", -E2BIG);
+
+	ret = dev_read_u32_array(dev, "i2c,speeds", speeds, size);
+	if (ret)
+		return log_msg_ret("read", -E2BIG);
+
+	speed = dev_read_u32_default(dev, "clock-frequency", 100000);
+	acpigen_write_scope(ctx, path);
+	ret = dw_i2c_gen_speed_config(dev, speed, &config);
+	if (ret)
+		return log_msg_ret("config", ret);
+	dw_i2c_acpi_write_speed_config(ctx, &config);
+	acpigen_pop_len(ctx);
+
+	return 0;
+}
+
+struct acpi_ops dw_i2c_acpi_ops = {
+	.fill_ssdt	= dw_i2c_acpi_fill_ssdt,
+};
+
 static const struct udevice_id designware_i2c_pci_ids[] = {
 	{ .compatible = "snps,designware-i2c-pci" },
 	{ .compatible = "intel,apl-i2c", .data = INTEL_APL },
@@ -124,6 +217,7 @@ U_BOOT_DRIVER(i2c_designware_pci) = {
 	.remove = designware_i2c_remove,
 	.flags = DM_FLAG_OS_PREPARE,
 	.ops	= &designware_i2c_ops,
+	ACPI_OPS_PTR(&dw_i2c_acpi_ops)
 };
 
 static struct pci_device_id designware_pci_supported[] = {

+ 2 - 2
drivers/i2c/i2c-uclass.c

@@ -458,7 +458,7 @@ int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len)
 	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
 
 	if (offset_len > I2C_MAX_OFFSET_LEN)
-		return -EINVAL;
+		return log_ret(-EINVAL);
 	chip->offset_len = offset_len;
 
 	return 0;
@@ -625,7 +625,7 @@ int i2c_chip_ofdata_to_platdata(struct udevice *dev, struct dm_i2c_chip *chip)
 	if (addr == -1) {
 		debug("%s: I2C Node '%s' has no 'reg' property %s\n", __func__,
 		      dev_read_name(dev), dev->name);
-		return -EINVAL;
+		return log_ret(-EINVAL);
 	}
 	chip->chip_addr = addr;
 

+ 1 - 0
drivers/i2c/sandbox_i2c.c

@@ -11,6 +11,7 @@
 #include <i2c.h>
 #include <log.h>
 #include <asm/test.h>
+#include <dm/acpi.h>
 #include <dm/lists.h>
 #include <dm/device-internal.h>
 

+ 6 - 6
drivers/misc/Kconfig

@@ -243,10 +243,10 @@ config NUVOTON_NCT6102D
 	  in the Nuvoton Super IO chips on X86 platforms.
 
 config P2SB
-	bool "Intel Primary-to-Sideband Bus"
+	bool "Intel Primary to Sideband Bridge"
 	depends on X86 || SANDBOX
 	help
-	  This enables support for the Intel Primary-to-Sideband bus,
+	  This enables support for the Intel Primary to Sideband Bridge,
 	  abbreviated to P2SB. The P2SB is used to access various peripherals
 	  such as eSPI, GPIO, through memory-mapped I/O in a large chunk of PCI
 	  space. The space is segmented into different channels and peripherals
@@ -256,20 +256,20 @@ config P2SB
 	  devices - see pcr_readl(), etc.
 
 config SPL_P2SB
-	bool "Intel Primary-to-Sideband Bus in SPL"
+	bool "Intel Primary to Sideband Bridge in SPL"
 	depends on SPL && (X86 || SANDBOX)
 	help
-	  The Primary-to-Sideband bus is used to access various peripherals
+	  The Primary to Sideband Bridge is used to access various peripherals
 	  through memory-mapped I/O in a large chunk of PCI space. The space is
 	  segmented into different channels and peripherals are accessed by
 	  device-specific means within those channels. Devices should be added
 	  in the device tree as subnodes of the p2sb.
 
 config TPL_P2SB
-	bool "Intel Primary-to-Sideband Bus in TPL"
+	bool "Intel Primary to Sideband Bridge in TPL"
 	depends on TPL && (X86 || SANDBOX)
 	help
-	  The Primary-to-Sideband bus is used to access various peripherals
+	  The Primary to Sideband Bridge is used to access various peripherals
 	  through memory-mapped I/O in a large chunk of PCI space. The space is
 	  segmented into different channels and peripherals are accessed by
 	  device-specific means within those channels. Devices should be added

+ 17 - 3
drivers/misc/irq-uclass.c

@@ -152,8 +152,6 @@ int irq_request(struct udevice *dev, struct irq *irq)
 	const struct irq_ops *ops;
 
 	log_debug("(dev=%p, irq=%p)\n", dev, irq);
-	if (!irq)
-		return 0;
 	ops = irq_get_ops(dev);
 
 	irq->dev = dev;
@@ -170,11 +168,27 @@ int irq_first_device_type(enum irq_dev_t type, struct udevice **devp)
 
 	ret = uclass_first_device_drvdata(UCLASS_IRQ, type, devp);
 	if (ret)
-		return log_msg_ret("find", ret);
+		return ret;
 
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+int irq_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq)
+{
+	struct irq_ops *ops;
+
+	if (!irq_is_valid(irq))
+		return -EINVAL;
+
+	ops = irq_get_ops(irq->dev);
+	if (!ops->get_acpi)
+		return -ENOSYS;
+
+	return ops->get_acpi(irq, acpi_irq);
+}
+#endif
+
 UCLASS_DRIVER(irq) = {
 	.id		= UCLASS_IRQ,
 	.name		= "irq",

+ 16 - 0
drivers/misc/irq_sandbox.c

@@ -8,6 +8,7 @@
 #include <common.h>
 #include <dm.h>
 #include <irq.h>
+#include <acpi/acpi_device.h>
 #include <asm/test.h>
 
 /**
@@ -73,6 +74,18 @@ static int sandbox_irq_of_xlate(struct irq *irq,
 	return 0;
 }
 
+static __maybe_unused int sandbox_get_acpi(const struct irq *irq,
+					   struct acpi_irq *acpi_irq)
+{
+	acpi_irq->pin = irq->id;
+	acpi_irq->mode = ACPI_IRQ_LEVEL_TRIGGERED;
+	acpi_irq->polarity = ACPI_IRQ_ACTIVE_HIGH;
+	acpi_irq->shared = ACPI_IRQ_SHARED;
+	acpi_irq->wake = ACPI_IRQ_WAKE;
+
+	return 0;
+}
+
 static const struct irq_ops sandbox_irq_ops = {
 	.route_pmc_gpio_gpe	= sandbox_route_pmc_gpio_gpe,
 	.set_polarity		= sandbox_set_polarity,
@@ -80,6 +93,9 @@ static const struct irq_ops sandbox_irq_ops = {
 	.restore_polarities	= sandbox_restore_polarities,
 	.read_and_clear		= sandbox_irq_read_and_clear,
 	.of_xlate		= sandbox_irq_of_xlate,
+#if CONFIG_IS_ENABLED(ACPIGEN)
+	.get_acpi		= sandbox_get_acpi,
+#endif
 };
 
 static const struct udevice_id sandbox_irq_ids[] = {

+ 18 - 8
drivers/misc/p2sb-uclass.c

@@ -18,7 +18,17 @@
 
 #define PCR_COMMON_IOSF_1_0	1
 
-static void *_pcr_reg_address(struct udevice *dev, uint offset)
+int p2sb_set_hide(struct udevice *dev, bool hide)
+{
+	struct p2sb_ops *ops = p2sb_get_ops(dev);
+
+	if (!ops->set_hide)
+		return -ENOSYS;
+
+	return ops->set_hide(dev, hide);
+}
+
+void *pcr_reg_address(struct udevice *dev, uint offset)
 {
 	struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
 	struct udevice *p2sb = dev_get_parent(dev);
@@ -55,7 +65,7 @@ uint pcr_read32(struct udevice *dev, uint offset)
 	/* Ensure the PCR offset is correctly aligned */
 	assert(IS_ALIGNED(offset, sizeof(uint32_t)));
 
-	ptr = _pcr_reg_address(dev, offset);
+	ptr = pcr_reg_address(dev, offset);
 	val = readl(ptr);
 	unmap_sysmem(ptr);
 
@@ -67,7 +77,7 @@ uint pcr_read16(struct udevice *dev, uint offset)
 	/* Ensure the PCR offset is correctly aligned */
 	check_pcr_offset_align(offset, sizeof(uint16_t));
 
-	return readw(_pcr_reg_address(dev, offset));
+	return readw(pcr_reg_address(dev, offset));
 }
 
 uint pcr_read8(struct udevice *dev, uint offset)
@@ -75,7 +85,7 @@ uint pcr_read8(struct udevice *dev, uint offset)
 	/* Ensure the PCR offset is correctly aligned */
 	check_pcr_offset_align(offset, sizeof(uint8_t));
 
-	return readb(_pcr_reg_address(dev, offset));
+	return readb(pcr_reg_address(dev, offset));
 }
 
 /*
@@ -86,7 +96,7 @@ uint pcr_read8(struct udevice *dev, uint offset)
  */
 static void write_completion(struct udevice *dev, uint offset)
 {
-	readl(_pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t))));
+	readl(pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t))));
 }
 
 void pcr_write32(struct udevice *dev, uint offset, uint indata)
@@ -94,7 +104,7 @@ void pcr_write32(struct udevice *dev, uint offset, uint indata)
 	/* Ensure the PCR offset is correctly aligned */
 	assert(IS_ALIGNED(offset, sizeof(indata)));
 
-	writel(indata, _pcr_reg_address(dev, offset));
+	writel(indata, pcr_reg_address(dev, offset));
 	/* Ensure the writes complete */
 	write_completion(dev, offset);
 }
@@ -104,7 +114,7 @@ void pcr_write16(struct udevice *dev, uint offset, uint indata)
 	/* Ensure the PCR offset is correctly aligned */
 	check_pcr_offset_align(offset, sizeof(uint16_t));
 
-	writew(indata, _pcr_reg_address(dev, offset));
+	writew(indata, pcr_reg_address(dev, offset));
 	/* Ensure the writes complete */
 	write_completion(dev, offset);
 }
@@ -114,7 +124,7 @@ void pcr_write8(struct udevice *dev, uint offset, uint indata)
 	/* Ensure the PCR offset is correctly aligned */
 	check_pcr_offset_align(offset, sizeof(uint8_t));
 
-	writeb(indata, _pcr_reg_address(dev, offset));
+	writeb(indata, pcr_reg_address(dev, offset));
 	/* Ensure the writes complete */
 	write_completion(dev, offset);
 }

+ 77 - 1
drivers/mmc/pci_mmc.c

@@ -7,10 +7,15 @@
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
+#include <log.h>
 #include <malloc.h>
 #include <mapmem.h>
 #include <sdhci.h>
-#include <asm/pci.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_dp.h>
+#include <asm-generic/gpio.h>
+#include <dm/acpi.h>
 
 struct pci_mmc_plat {
 	struct mmc_config cfg;
@@ -20,6 +25,7 @@ struct pci_mmc_plat {
 struct pci_mmc_priv {
 	struct sdhci_host host;
 	void *base;
+	struct gpio_desc cd_gpio;
 };
 
 static int pci_mmc_probe(struct udevice *dev)
@@ -44,6 +50,15 @@ static int pci_mmc_probe(struct udevice *dev)
 	return sdhci_probe(dev);
 }
 
+static int pci_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+	struct pci_mmc_priv *priv = dev_get_priv(dev);
+
+	gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
+
+	return 0;
+}
+
 static int pci_mmc_bind(struct udevice *dev)
 {
 	struct pci_mmc_plat *plat = dev_get_platdata(dev);
@@ -51,14 +66,75 @@ static int pci_mmc_bind(struct udevice *dev)
 	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
 }
 
+static int pci_mmc_acpi_fill_ssdt(const struct udevice *dev,
+				  struct acpi_ctx *ctx)
+{
+	struct pci_mmc_priv *priv = dev_get_priv(dev);
+	char path[ACPI_PATH_MAX];
+	struct acpi_gpio gpio;
+	struct acpi_dp *dp;
+	int ret;
+
+	if (!dev_of_valid(dev))
+		return 0;
+
+	ret = gpio_get_acpi(&priv->cd_gpio, &gpio);
+	if (ret)
+		return log_msg_ret("gpio", ret);
+	gpio.type = ACPI_GPIO_TYPE_INTERRUPT;
+	gpio.pull = ACPI_GPIO_PULL_NONE;
+	gpio.irq.mode = ACPI_IRQ_EDGE_TRIGGERED;
+	gpio.irq.polarity = ACPI_IRQ_ACTIVE_BOTH;
+	gpio.irq.shared = ACPI_IRQ_SHARED;
+	gpio.irq.wake = ACPI_IRQ_WAKE;
+	gpio.interrupt_debounce_timeout = 10000; /* 100ms */
+
+	/* Use device path as the Scope for the SSDT */
+	ret = acpi_device_path(dev, path, sizeof(path));
+	if (ret)
+		return log_msg_ret("path", ret);
+	acpigen_write_scope(ctx, path);
+	acpigen_write_name(ctx, "_CRS");
+
+	/* Write GpioInt() as default (if set) or custom from devicetree */
+	acpigen_write_resourcetemplate_header(ctx);
+	acpi_device_write_gpio(ctx, &gpio);
+	acpigen_write_resourcetemplate_footer(ctx);
+
+	/* Bind the cd-gpio name to the GpioInt() resource */
+	dp = acpi_dp_new_table("_DSD");
+	if (!dp)
+		return -ENOMEM;
+	acpi_dp_add_gpio(dp, "cd-gpio", path, 0, 0, 1);
+	ret = acpi_dp_write(ctx, dp);
+	if (ret)
+		return log_msg_ret("cd", ret);
+
+	acpigen_pop_len(ctx);
+
+	return 0;
+}
+
+struct acpi_ops pci_mmc_acpi_ops = {
+	.fill_ssdt	= pci_mmc_acpi_fill_ssdt,
+};
+
+static const struct udevice_id pci_mmc_match[] = {
+	{ .compatible = "intel,apl-sd" },
+	{ }
+};
+
 U_BOOT_DRIVER(pci_mmc) = {
 	.name	= "pci_mmc",
 	.id	= UCLASS_MMC,
+	.of_match = pci_mmc_match,
 	.bind	= pci_mmc_bind,
+	.ofdata_to_platdata	= pci_mmc_ofdata_to_platdata,
 	.probe	= pci_mmc_probe,
 	.ops	= &sdhci_ops,
 	.priv_auto_alloc_size = sizeof(struct pci_mmc_priv),
 	.platdata_auto_alloc_size = sizeof(struct pci_mmc_plat),
+	ACPI_OPS_PTR(&pci_mmc_acpi_ops)
 };
 
 static struct pci_device_id mmc_supported[] = {

+ 12 - 0
drivers/pinctrl/intel/Kconfig

@@ -15,6 +15,18 @@ config INTEL_PINCTRL_IOSTANDBY
 	bool
 	default y
 
+config INTEL_PINCTRL_MULTI_ACPI_DEVICES
+	bool
+	default y
+	help
+	  Enable this if the pinctrl devices are modelled as multiple,
+	  separate ACPI devices in the ACPI tables. If enabled, the ACPI
+	  devices match the U-Boot pinctrl devices and the pin 'offset' is
+	  relatove to a particular pinctrl device. If disabled, there is a
+	  single ACPI pinctrl device which includes all U-Boot pinctrl devices
+	  and the pin 'offset' is in effect a global pin number.
+
+
 config PINCTRL_INTEL_APL
 	bool "Support Intel Apollo Lake (APL)"
 	help

+ 15 - 6
drivers/pinctrl/intel/pinctrl.c

@@ -394,7 +394,7 @@ static int pinctrl_configure_pad(struct udevice *dev,
 	return 0;
 }
 
-u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset)
+u32 intel_pinctrl_get_config_reg_offset(struct udevice *dev, uint offset)
 {
 	struct intel_pinctrl_priv *priv = dev_get_priv(dev);
 	const struct pad_community *comm = priv->comm;
@@ -407,9 +407,16 @@ u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset)
 	return config_offset;
 }
 
+u32 intel_pinctrl_get_config_reg_addr(struct udevice *dev, uint offset)
+{
+	uint config_offset = intel_pinctrl_get_config_reg_offset(dev, offset);
+
+	return (u32)(ulong)pcr_reg_address(dev, config_offset);
+}
+
 u32 intel_pinctrl_get_config_reg(struct udevice *dev, uint offset)
 {
-	uint config_offset = intel_pinctrl_get_config_reg_addr(dev, offset);
+	uint config_offset = intel_pinctrl_get_config_reg_offset(dev, offset);
 
 	return pcr_read32(dev, config_offset);
 }
@@ -420,6 +427,8 @@ int intel_pinctrl_get_acpi_pin(struct udevice *dev, uint offset)
 	const struct pad_community *comm = priv->comm;
 	int group;
 
+	if (IS_ENABLED(CONFIG_INTEL_PINCTRL_MULTI_ACPI_DEVICES))
+		return offset;
 	group = pinctrl_group_index(comm, offset);
 
 	/* If pad base is not set then use GPIO number as ACPI pin number */
@@ -610,15 +619,11 @@ int intel_pinctrl_ofdata_to_platdata(struct udevice *dev,
 {
 	struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev);
 	struct intel_pinctrl_priv *priv = dev_get_priv(dev);
-	int ret;
 
 	if (!comm) {
 		log_err("Cannot find community for pid %d\n", pplat->pid);
 		return -EDOM;
 	}
-	ret = irq_first_device_type(X86_IRQT_ITSS, &priv->itss);
-	if (ret)
-		return log_msg_ret("Cannot find ITSS", ret);
 	priv->comm = comm;
 	priv->num_cfgs = num_cfgs;
 
@@ -628,8 +633,12 @@ int intel_pinctrl_ofdata_to_platdata(struct udevice *dev,
 int intel_pinctrl_probe(struct udevice *dev)
 {
 	struct intel_pinctrl_priv *priv = dev_get_priv(dev);
+	int ret;
 
 	priv->itss_pol_cfg = true;
+	ret = irq_first_device_type(X86_IRQT_ITSS, &priv->itss);
+	if (ret)
+		return log_msg_ret("Cannot find ITSS", ret);
 
 	return 0;
 }

+ 0 - 4
drivers/pinctrl/intel/pinctrl_apl.c

@@ -75,7 +75,6 @@ static const struct pad_community apl_gpio_communities[] = {
 		.gpi_smi_en_reg_0 = GPI_SMI_EN_0,
 		.max_pads_per_group = GPIO_MAX_NUM_PER_GROUP,
 		.name = "GPIO_GPE_N",
-		.acpi_path = "\\_SB.GPO0",
 		.reset_map = rst_map,
 		.num_reset_vals = ARRAY_SIZE(rst_map),
 		.groups = apl_community_n_groups,
@@ -94,7 +93,6 @@ static const struct pad_community apl_gpio_communities[] = {
 		.gpi_smi_en_reg_0 = GPI_SMI_EN_0,
 		.max_pads_per_group = GPIO_MAX_NUM_PER_GROUP,
 		.name = "GPIO_GPE_NW",
-		.acpi_path = "\\_SB.GPO1",
 		.reset_map = rst_map,
 		.num_reset_vals = ARRAY_SIZE(rst_map),
 		.groups = apl_community_nw_groups,
@@ -113,7 +111,6 @@ static const struct pad_community apl_gpio_communities[] = {
 		.gpi_smi_en_reg_0 = GPI_SMI_EN_0,
 		.max_pads_per_group = GPIO_MAX_NUM_PER_GROUP,
 		.name = "GPIO_GPE_W",
-		.acpi_path = "\\_SB.GPO2",
 		.reset_map = rst_map,
 		.num_reset_vals = ARRAY_SIZE(rst_map),
 		.groups = apl_community_w_groups,
@@ -132,7 +129,6 @@ static const struct pad_community apl_gpio_communities[] = {
 		.gpi_smi_en_reg_0 = GPI_SMI_EN_0,
 		.max_pads_per_group = GPIO_MAX_NUM_PER_GROUP,
 		.name = "GPIO_GPE_SW",
-		.acpi_path = "\\_SB.GPO3",
 		.reset_map = rst_map,
 		.num_reset_vals = ARRAY_SIZE(rst_map),
 		.groups = apl_community_sw_groups,

+ 0 - 9
drivers/power/acpi_pmc/acpi-pmc-uclass.c

@@ -15,15 +15,6 @@
 #include <asm/io.h>
 #include <power/acpi_pmc.h>
 
-enum {
-	PM1_STS		= 0x00,
-	PM1_EN		= 0x02,
-	PM1_CNT		= 0x04,
-
-	GPE0_STS	= 0x20,
-	GPE0_EN		= 0x30,
-};
-
 struct tco_regs {
 	u32 tco_rld;
 	u32 tco_sts;

+ 13 - 0
drivers/rtc/sandbox_rtc.c

@@ -9,6 +9,7 @@
 #include <i2c.h>
 #include <rtc.h>
 #include <asm/rtc.h>
+#include <dm/acpi.h>
 
 #define REG_COUNT 0x80
 
@@ -67,6 +68,17 @@ static int sandbox_rtc_write8(struct udevice *dev, unsigned int reg, int val)
 	return dm_i2c_reg_write(dev, reg, val);
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int sandbox_rtc_get_name(const struct udevice *dev, char *out_name)
+{
+	return acpi_copy_name(out_name, "RTCC");
+}
+
+struct acpi_ops sandbox_rtc_acpi_ops = {
+	.get_name	= sandbox_rtc_get_name,
+};
+#endif
+
 static const struct rtc_ops sandbox_rtc_ops = {
 	.get = sandbox_rtc_get,
 	.set = sandbox_rtc_set,
@@ -85,4 +97,5 @@ U_BOOT_DRIVER(rtc_sandbox) = {
 	.id	= UCLASS_RTC,
 	.of_match = sandbox_rtc_ids,
 	.ops	= &sandbox_rtc_ops,
+	ACPI_OPS_PTR(&sandbox_rtc_acpi_ops)
 };

+ 18 - 0
drivers/sound/Kconfig

@@ -40,6 +40,15 @@ config I2S_SAMSUNG
 	  option provides an implementation for sound_init() and
 	  sound_play().
 
+config SOUND_DA7219
+	bool "Dialog Semiconductor audio codec"
+	depends on SOUND
+	help
+	  The DA7219 is an ultra-low-power audio codec with Advanced Accessory
+	  Detection (AAD). This driver only supports generation of ACPI tables.
+	  It does not support sound output or any of the other codec
+	  features.
+
 config SOUND_I8254
 	bool "Intel i8254 timer / beeper"
 	depends on SOUND
@@ -104,6 +113,15 @@ config SOUND_MAX98095
 	  audio data and I2C for codec control. At present it only works
 	  with the Samsung I2S driver.
 
+config SOUND_MAX98357A
+	bool "Support Maxim max98357a audio codec"
+	depends on PCI
+	help
+	  Enable the max98357a audio codec. This is connected on PCI for
+	  audio data codec control. This is currently only capable of providing
+	  ACPI information. A full driver (with sound in U-Boot) is currently
+	  not available.
+
 config SOUND_RT5677
 	bool "Support Realtek RT5677 audio codec"
 	depends on SOUND

+ 2 - 0
drivers/sound/Makefile

@@ -7,6 +7,7 @@ obj-$(CONFIG_SOUND)	+= sound.o
 obj-$(CONFIG_SOUND)	+= codec-uclass.o
 obj-$(CONFIG_SOUND)	+= i2s-uclass.o
 obj-$(CONFIG_SOUND)	+= sound-uclass.o
+obj-$(CONFIG_SOUND_DA7219)	+= da7219.o
 obj-$(CONFIG_I2S_SAMSUNG)	+= samsung-i2s.o
 obj-$(CONFIG_SOUND_SANDBOX)	+= sandbox.o
 obj-$(CONFIG_I2S_ROCKCHIP)	+= rockchip_i2s.o rockchip_sound.o
@@ -16,6 +17,7 @@ obj-$(CONFIG_SOUND_WM8994)	+= wm8994.o
 obj-$(CONFIG_SOUND_MAX98088)	+= max98088.o maxim_codec.o
 obj-$(CONFIG_SOUND_MAX98090)	+= max98090.o maxim_codec.o
 obj-$(CONFIG_SOUND_MAX98095)	+= max98095.o maxim_codec.o
+obj-$(CONFIG_SOUND_MAX98357A)	+= max98357a.o
 obj-$(CONFIG_SOUND_INTEL_HDA)	+= hda_codec.o
 obj-$(CONFIG_SOUND_I8254)	+= i8254_beep.o
 obj-$(CONFIG_SOUND_RT5677)	+= rt5677.o

+ 190 - 0
drivers/sound/da7219.c

@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ACPI driver for DA7219 codec
+ *
+ * Copyright 2019 Google LLC
+ * Parts taken from coreboot
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <irq.h>
+#include <log.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_dp.h>
+#ifdef CONFIG_X86
+#include <asm/acpi_nhlt.h>
+#endif
+#include <asm-generic/gpio.h>
+#include <dt-bindings/sound/nhlt.h>
+#include <dm/acpi.h>
+
+#define DA7219_ACPI_HID		"DLGS7219"
+
+static int da7219_acpi_fill_ssdt(const struct udevice *dev,
+				 struct acpi_ctx *ctx)
+{
+	char scope[ACPI_PATH_MAX];
+	char name[ACPI_NAME_MAX];
+	struct acpi_dp *dsd, *aad;
+	ofnode node;
+	u32 val;
+	int ret;
+
+	ret = acpi_device_scope(dev, scope, sizeof(scope));
+	if (ret)
+		return log_msg_ret("scope", ret);
+	ret = acpi_get_name(dev, name);
+	if (ret)
+		return log_msg_ret("name", ret);
+
+	/* Device */
+	acpigen_write_scope(ctx, scope);
+	acpigen_write_device(ctx, name);
+	acpigen_write_name_string(ctx, "_HID", DA7219_ACPI_HID);
+	acpigen_write_name_integer(ctx, "_UID", 1);
+	acpigen_write_name_string(ctx, "_DDN",
+				  dev_read_string(dev, "acpi,ddn"));
+	acpigen_write_name_integer(ctx, "_S0W", 4);
+	acpigen_write_sta(ctx, acpi_device_status(dev));
+
+	/* Resources */
+	acpigen_write_name(ctx, "_CRS");
+	acpigen_write_resourcetemplate_header(ctx);
+	ret = acpi_device_write_i2c_dev(ctx, dev);
+	if (ret)
+		return log_msg_ret("i2c", ret);
+
+	/* Use either Interrupt() or GpioInt() */
+	ret = acpi_device_write_interrupt_or_gpio(ctx, (struct udevice *)dev,
+						  "req-gpios");
+	if (ret)
+		return log_msg_ret("irq_gpio", ret);
+	acpigen_write_resourcetemplate_footer(ctx);
+
+	/* AAD Child Device Properties */
+	aad = acpi_dp_new_table("DAAD");
+	if (!aad)
+		return log_msg_ret("aad", -ENOMEM);
+
+	node = ofnode_find_subnode(dev_ofnode(dev), "da7219_aad");
+	if (!ofnode_valid(node))
+		return log_msg_ret("da7219_aad", -EINVAL);
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,btn-cfg");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,mic-det-thr");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,jack-ins-deb");
+	acpi_dp_ofnode_copy_str(node, aad, "dlg,jack-det-rate");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,jack-rem-deb");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,a-d-btn-thr");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,d-b-btn-thr");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,b-c-btn-thr");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,c-mic-btn-thr");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,btn-avg");
+	acpi_dp_ofnode_copy_int(node, aad, "dlg,adc-1bit-rpt");
+	if (!ofnode_read_u32(node, "dlg,micbias-pulse-lvl", &val)) {
+		acpi_dp_ofnode_copy_int(node, aad, "dlg,micbias-pulse-lvl");
+		acpi_dp_ofnode_copy_int(node, aad, "dlg,micbias-pulse-time");
+	}
+
+	/* DA7219 Properties */
+	dsd = acpi_dp_new_table("_DSD");
+	if (!dsd)
+		return log_msg_ret("dsd", -ENOMEM);
+	acpi_dp_dev_copy_int(dev, dsd, "dlg,micbias-lvl");
+	acpi_dp_dev_copy_str(dev, dsd, "dlg,mic-amp-in-sel");
+	acpi_dp_dev_copy_str(dev, dsd, "dlg,mclk-name");
+	acpi_dp_add_child(dsd, "da7219_aad", aad);
+
+	/* Write Device Property Hierarchy */
+	acpi_dp_write(ctx, dsd);
+
+	acpigen_pop_len(ctx); /* Device */
+	acpigen_pop_len(ctx); /* Scope */
+
+	return 0;
+}
+
+/* For now only X86 boards support NHLT */
+#ifdef CONFIG_X86
+static const struct nhlt_format_config da7219_formats[] = {
+	/* 48 KHz 24-bits per sample. */
+	{
+		.num_channels = 2,
+		.sample_freq_khz = 48,
+		.container_bits_per_sample = 32,
+		.valid_bits_per_sample = 24,
+		.settings_file = "dialog-2ch-48khz-24b.dat",
+	},
+};
+
+static const struct nhlt_tdm_config tdm_config = {
+	.virtual_slot = 0,
+	.config_type = NHLT_TDM_BASIC,
+};
+
+static const struct nhlt_endp_descriptor da7219_descriptors[] = {
+	/* Render Endpoint */
+	{
+		.link = NHLT_LINK_SSP,
+		.device = NHLT_SSP_DEV_I2S,
+		.direction = NHLT_DIR_RENDER,
+		.vid = NHLT_VID,
+		.did = NHLT_DID_SSP,
+		.cfg = &tdm_config,
+		.cfg_size = sizeof(tdm_config),
+		.formats = da7219_formats,
+		.num_formats = ARRAY_SIZE(da7219_formats),
+	},
+	/* Capture Endpoint */
+	{
+		.link = NHLT_LINK_SSP,
+		.device = NHLT_SSP_DEV_I2S,
+		.direction = NHLT_DIR_CAPTURE,
+		.vid = NHLT_VID,
+		.did = NHLT_DID_SSP,
+		.cfg = &tdm_config,
+		.cfg_size = sizeof(tdm_config),
+		.formats = da7219_formats,
+		.num_formats = ARRAY_SIZE(da7219_formats),
+	},
+};
+
+static int da7219_acpi_setup_nhlt(const struct udevice *dev,
+				  struct acpi_ctx *ctx)
+{
+	u32 hwlink;
+	int ret;
+
+	if (dev_read_u32(dev, "acpi,audio-link", &hwlink))
+		return log_msg_ret("link", -EINVAL);
+
+	/* Virtual bus id of SSP links are the hardware port ids proper. */
+	ret = nhlt_add_ssp_endpoints(ctx->nhlt, hwlink, da7219_descriptors,
+				     ARRAY_SIZE(da7219_descriptors));
+	if (ret)
+		return log_msg_ret("add", ret);
+
+	return 0;
+}
+#endif
+
+struct acpi_ops da7219_acpi_ops = {
+	.fill_ssdt	= da7219_acpi_fill_ssdt,
+#ifdef CONFIG_X86
+	.setup_nhlt	= da7219_acpi_setup_nhlt,
+#endif
+};
+
+static const struct udevice_id da7219_ids[] = {
+	{ .compatible = "dlg,da7219" },
+	{ }
+};
+
+U_BOOT_DRIVER(da7219) = {
+	.name		= "da7219",
+	.id		= UCLASS_MISC,
+	.of_match	= da7219_ids,
+	ACPI_OPS_PTR(&da7219_acpi_ops)
+};

+ 161 - 0
drivers/sound/max98357a.c

@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * max98357a.c -- MAX98357A Audio driver
+ *
+ * Copyright 2019 Google LLC
+ * Parts taken from coreboot
+ */
+
+#include <common.h>
+#include <audio_codec.h>
+#include <dm.h>
+#include <log.h>
+#include <sound.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_dp.h>
+#include <asm-generic/gpio.h>
+#ifdef CONFIG_X86
+#include <asm/acpi_nhlt.h>
+#endif
+#include <dt-bindings/sound/nhlt.h>
+#include <dm/acpi.h>
+
+struct max98357a_priv {
+	struct gpio_desc sdmode_gpio;
+};
+
+static int max98357a_ofdata_to_platdata(struct udevice *dev)
+{
+	struct max98357a_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = gpio_request_by_name(dev, "sdmode-gpios", 0, &priv->sdmode_gpio,
+				   GPIOD_IS_IN);
+	if (ret)
+		return log_msg_ret("gpio", ret);
+
+	return 0;
+}
+
+static int max98357a_acpi_fill_ssdt(const struct udevice *dev,
+				    struct acpi_ctx *ctx)
+{
+	struct max98357a_priv *priv = dev_get_priv(dev);
+	char scope[ACPI_PATH_MAX];
+	char name[ACPI_NAME_MAX];
+	char path[ACPI_PATH_MAX];
+	struct acpi_dp *dp;
+	int ret;
+
+	ret = acpi_device_scope(dev, scope, sizeof(scope));
+	if (ret)
+		return log_msg_ret("scope", ret);
+	ret = acpi_get_name(dev, name);
+	if (ret)
+		return log_msg_ret("name", ret);
+
+	/* Device */
+	acpigen_write_scope(ctx, scope);
+	acpigen_write_device(ctx, name);
+	acpigen_write_name_string(ctx, "_HID",
+				  dev_read_string(dev, "acpi,hid"));
+	acpigen_write_name_integer(ctx, "_UID", 0);
+	acpigen_write_name_string(ctx, "_DDN",
+				  dev_read_string(dev, "acpi,ddn"));
+	acpigen_write_sta(ctx, acpi_device_status(dev));
+
+	/* Resources */
+	acpigen_write_name(ctx, "_CRS");
+	acpigen_write_resourcetemplate_header(ctx);
+	ret = acpi_device_write_gpio_desc(ctx, &priv->sdmode_gpio);
+	if (ret)
+		return log_msg_ret("gpio", ret);
+	acpigen_write_resourcetemplate_footer(ctx);
+
+	/* _DSD for devicetree properties */
+	/* This points to the first pin in the first gpio entry in _CRS */
+	ret = acpi_device_path(dev, path, sizeof(path));
+	if (ret)
+		return log_msg_ret("path", ret);
+	dp = acpi_dp_new_table("_DSD");
+	acpi_dp_add_gpio(dp, "sdmode-gpio", path, 0, 0,
+			 priv->sdmode_gpio.flags & GPIOD_ACTIVE_LOW ?
+			 ACPI_IRQ_ACTIVE_LOW : ACPI_IRQ_ACTIVE_HIGH);
+	acpi_dp_add_integer(dp, "sdmode-delay",
+			    dev_read_u32_default(dev, "sdmode-delay", 0));
+	acpi_dp_write(ctx, dp);
+
+	acpigen_pop_len(ctx); /* Device */
+	acpigen_pop_len(ctx); /* Scope */
+
+	return 0;
+}
+
+/* For now only X86 boards support NHLT */
+#ifdef CONFIG_X86
+static const struct nhlt_format_config max98357a_formats[] = {
+	/* 48 KHz 24-bits per sample. */
+	{
+		.num_channels = 2,
+		.sample_freq_khz = 48,
+		.container_bits_per_sample = 32,
+		.valid_bits_per_sample = 24,
+		.settings_file = "max98357-render-2ch-48khz-24b.dat",
+	},
+};
+
+static const struct nhlt_endp_descriptor max98357a_descriptors[] = {
+	{
+		.link = NHLT_LINK_SSP,
+		.device = NHLT_SSP_DEV_I2S,
+		.direction = NHLT_DIR_RENDER,
+		.vid = NHLT_VID,
+		.did = NHLT_DID_SSP,
+		.formats = max98357a_formats,
+		.num_formats = ARRAY_SIZE(max98357a_formats),
+	},
+};
+
+static int max98357a_acpi_setup_nhlt(const struct udevice *dev,
+				     struct acpi_ctx *ctx)
+{
+	u32 hwlink;
+	int ret;
+
+	if (dev_read_u32(dev, "acpi,audio-link", &hwlink))
+		return log_msg_ret("link", -EINVAL);
+
+	/* Virtual bus id of SSP links are the hardware port ids proper. */
+	ret = nhlt_add_ssp_endpoints(ctx->nhlt, hwlink, max98357a_descriptors,
+				     ARRAY_SIZE(max98357a_descriptors));
+	if (ret)
+		return log_msg_ret("add", ret);
+
+	return 0;
+}
+#endif
+
+struct acpi_ops max98357a_acpi_ops = {
+	.fill_ssdt	= max98357a_acpi_fill_ssdt,
+#ifdef CONFIG_X86
+	.setup_nhlt	= max98357a_acpi_setup_nhlt,
+#endif
+};
+
+static const struct audio_codec_ops max98357a_ops = {
+};
+
+static const struct udevice_id max98357a_ids[] = {
+	{ .compatible = "maxim,max98357a" },
+	{ }
+};
+
+U_BOOT_DRIVER(max98357a) = {
+	.name		= "max98357a",
+	.id		= UCLASS_AUDIO_CODEC,
+	.of_match	= max98357a_ids,
+	.ofdata_to_platdata	= max98357a_ofdata_to_platdata,
+	.ops		= &max98357a_ops,
+	ACPI_OPS_PTR(&max98357a_acpi_ops)
+};

+ 1 - 0
drivers/spi/sandbox_spi.c

@@ -21,6 +21,7 @@
 #include <linux/errno.h>
 #include <asm/spi.h>
 #include <asm/state.h>
+#include <dm/acpi.h>
 #include <dm/device-internal.h>
 
 #ifndef CONFIG_SPI_IDLE_VAL

+ 406 - 0
include/acpi/acpi_device.h

@@ -0,0 +1,406 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Generation of tables for particular device types
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot file of the same name
+ */
+
+#ifndef __ACPI_DEVICE_H
+#define __ACPI_DEVICE_H
+
+#include <i2c.h>
+#include <spi.h>
+#include <linux/bitops.h>
+
+struct acpi_ctx;
+struct gpio_desc;
+struct irq;
+struct udevice;
+
+/* ACPI descriptor values for common descriptors: SERIAL_BUS means I2C */
+#define ACPI_DESCRIPTOR_LARGE		BIT(7)
+#define ACPI_DESCRIPTOR_REGISTER	(ACPI_DESCRIPTOR_LARGE | 2)
+#define ACPI_DESCRIPTOR_INTERRUPT	(ACPI_DESCRIPTOR_LARGE | 9)
+#define ACPI_DESCRIPTOR_GPIO		(ACPI_DESCRIPTOR_LARGE | 12)
+#define ACPI_DESCRIPTOR_SERIAL_BUS	(ACPI_DESCRIPTOR_LARGE | 14)
+
+/* Length of a full path to an ACPI device */
+#define ACPI_PATH_MAX		30
+
+/* Values that can be returned for ACPI device _STA method */
+enum acpi_dev_status {
+	ACPI_DSTATUS_PRESENT		= BIT(0),
+	ACPI_DSTATUS_ENABLED		= BIT(1),
+	ACPI_DSTATUS_SHOW_IN_UI		= BIT(2),
+	ACPI_DSTATUS_OK			= BIT(3),
+	ACPI_DSTATUS_HAS_BATTERY	= BIT(4),
+
+	ACPI_DSTATUS_ALL_OFF	= 0,
+	ACPI_DSTATUS_HIDDEN_ON	= ACPI_DSTATUS_PRESENT | ACPI_DSTATUS_ENABLED |
+		ACPI_DSTATUS_OK,
+	ACPI_DSTATUS_ALL_ON	= ACPI_DSTATUS_HIDDEN_ON |
+		ACPI_DSTATUS_SHOW_IN_UI,
+};
+
+/** enum acpi_irq_mode - edge/level trigger mode */
+enum acpi_irq_mode {
+	ACPI_IRQ_EDGE_TRIGGERED,
+	ACPI_IRQ_LEVEL_TRIGGERED,
+};
+
+/**
+ * enum acpi_irq_polarity - polarity of interrupt
+ *
+ * @ACPI_IRQ_ACTIVE_LOW - for ACPI_IRQ_EDGE_TRIGGERED this means falling edge
+ * @ACPI_IRQ_ACTIVE_HIGH - for ACPI_IRQ_EDGE_TRIGGERED this means rising edge
+ * @ACPI_IRQ_ACTIVE_BOTH - not meaningful for ACPI_IRQ_EDGE_TRIGGERED
+ */
+enum acpi_irq_polarity {
+	ACPI_IRQ_ACTIVE_LOW,
+	ACPI_IRQ_ACTIVE_HIGH,
+	ACPI_IRQ_ACTIVE_BOTH,
+};
+
+/**
+ * enum acpi_irq_shared - whether interrupt is shared or not
+ *
+ * @ACPI_IRQ_EXCLUSIVE: only this device uses the interrupt
+ * @ACPI_IRQ_SHARED: other devices may use this interrupt
+ */
+enum acpi_irq_shared {
+	ACPI_IRQ_EXCLUSIVE,
+	ACPI_IRQ_SHARED,
+};
+
+/** enum acpi_irq_wake - indicates whether this interrupt can wake the device */
+enum acpi_irq_wake {
+	ACPI_IRQ_NO_WAKE,
+	ACPI_IRQ_WAKE,
+};
+
+/**
+ * struct acpi_irq - representation of an ACPI interrupt
+ *
+ * @pin: ACPI pin that is monitored for the interrupt
+ * @mode: Edge/level triggering
+ * @polarity: Interrupt polarity
+ * @shared: Whether interrupt is shared or not
+ * @wake: Whether interrupt can wake the device from sleep
+ */
+struct acpi_irq {
+	unsigned int pin;
+	enum acpi_irq_mode mode;
+	enum acpi_irq_polarity polarity;
+	enum acpi_irq_shared shared;
+	enum acpi_irq_wake wake;
+};
+
+/**
+ * enum acpi_gpio_type - type of the descriptor
+ *
+ * @ACPI_GPIO_TYPE_INTERRUPT: GpioInterrupt
+ * @ACPI_GPIO_TYPE_IO: GpioIo
+ */
+enum acpi_gpio_type {
+	ACPI_GPIO_TYPE_INTERRUPT,
+	ACPI_GPIO_TYPE_IO,
+};
+
+/**
+ * enum acpi_gpio_pull - pull direction
+ *
+ * @ACPI_GPIO_PULL_DEFAULT: Use default value for pin
+ * @ACPI_GPIO_PULL_UP: Pull up
+ * @ACPI_GPIO_PULL_DOWN: Pull down
+ * @ACPI_GPIO_PULL_NONE: No pullup/pulldown
+ */
+enum acpi_gpio_pull {
+	ACPI_GPIO_PULL_DEFAULT,
+	ACPI_GPIO_PULL_UP,
+	ACPI_GPIO_PULL_DOWN,
+	ACPI_GPIO_PULL_NONE,
+};
+
+/**
+ * enum acpi_gpio_io_restrict - controls input/output of pin
+ *
+ * @ACPI_GPIO_IO_RESTRICT_NONE: no restrictions
+ * @ACPI_GPIO_IO_RESTRICT_INPUT: input only (no output)
+ * @ACPI_GPIO_IO_RESTRICT_OUTPUT: output only (no input)
+ * @ACPI_GPIO_IO_RESTRICT_PRESERVE: preserve settings when driver not active
+ */
+enum acpi_gpio_io_restrict {
+	ACPI_GPIO_IO_RESTRICT_NONE,
+	ACPI_GPIO_IO_RESTRICT_INPUT,
+	ACPI_GPIO_IO_RESTRICT_OUTPUT,
+	ACPI_GPIO_IO_RESTRICT_PRESERVE,
+};
+
+/** enum acpi_gpio_polarity - controls the GPIO polarity */
+enum acpi_gpio_polarity {
+	ACPI_GPIO_ACTIVE_HIGH = 0,
+	ACPI_GPIO_ACTIVE_LOW = 1,
+};
+
+#define ACPI_GPIO_REVISION_ID		1
+#define ACPI_GPIO_MAX_PINS		2
+
+/**
+ * struct acpi_gpio - representation of an ACPI GPIO
+ *
+ * @pin_count: Number of pins represented
+ * @pins: List of pins
+ * @pin0_addr: Address in memory of the control registers for pin 0. This is
+ *   used when generating ACPI tables
+ * @type: GPIO type
+ * @pull: Pullup/pulldown setting
+ * @resource: Resource name for this GPIO controller
+ * For GpioInt:
+ * @interrupt_debounce_timeout: Debounce timeout in units of 10us
+ * @irq: Interrupt
+ *
+ * For GpioIo:
+ * @output_drive_strength: Drive strength in units of 10uA
+ * @io_shared; true if GPIO is shared
+ * @io_restrict: I/O restriction setting
+ * @polarity: GPIO polarity
+ */
+struct acpi_gpio {
+	int pin_count;
+	u16 pins[ACPI_GPIO_MAX_PINS];
+	ulong pin0_addr;
+
+	enum acpi_gpio_type type;
+	enum acpi_gpio_pull pull;
+	char resource[ACPI_PATH_MAX];
+
+	/* GpioInt */
+	u16 interrupt_debounce_timeout;
+	struct acpi_irq irq;
+
+	/* GpioIo */
+	u16 output_drive_strength;
+	bool io_shared;
+	enum acpi_gpio_io_restrict io_restrict;
+	enum acpi_gpio_polarity polarity;
+};
+
+/* ACPI Descriptors for Serial Bus interfaces */
+#define ACPI_SERIAL_BUS_TYPE_I2C		1
+#define ACPI_SERIAL_BUS_TYPE_SPI		2
+#define ACPI_I2C_SERIAL_BUS_REVISION_ID		1 /* TODO: upgrade to 2 */
+#define ACPI_I2C_TYPE_SPECIFIC_REVISION_ID	1
+#define ACPI_SPI_SERIAL_BUS_REVISION_ID		1
+#define ACPI_SPI_TYPE_SPECIFIC_REVISION_ID	1
+
+/**
+ * struct acpi_i2c - representation of an ACPI I2C device
+ *
+ * @address: 7-bit or 10-bit I2C address
+ * @mode_10bit: Which address size is used
+ * @speed: Bus speed in Hz
+ * @resource: Resource name for the I2C controller
+ */
+struct acpi_i2c {
+	u16 address;
+	enum i2c_address_mode mode_10bit;
+	enum i2c_speed_rate speed;
+	const char *resource;
+};
+
+/**
+ * struct acpi_spi - representation of an ACPI SPI device
+ *
+ * @device_select: Chip select used by this device (typically 0)
+ * @device_select_polarity: Polarity for the device
+ * @wire_mode: Number of wires used for SPI
+ * @speed: Bus speed in Hz
+ * @data_bit_length: Word length for SPI (typically 8)
+ * @clock_phase: Clock phase to capture data
+ * @clock_polarity: Bus polarity
+ * @resource: Resource name for the SPI controller
+ */
+struct acpi_spi {
+	u16 device_select;
+	enum spi_polarity device_select_polarity;
+	enum spi_wire_mode wire_mode;
+	unsigned int speed;
+	u8 data_bit_length;
+	enum spi_clock_phase clock_phase;
+	enum spi_polarity clock_polarity;
+	const char *resource;
+};
+
+/**
+ * acpi_device_path() - Get the full path to an ACPI device
+ *
+ * This gets the full path in the form XXXX.YYYY.ZZZZ where XXXX is the root
+ * and ZZZZ is the device. All parent devices are added to the path.
+ *
+ * @dev: Device to check
+ * @buf: Buffer to place the path in (should be ACPI_PATH_MAX long)
+ * @maxlen: Size of buffer (typically ACPI_PATH_MAX)
+ * @return 0 if OK, -ve on error
+ */
+int acpi_device_path(const struct udevice *dev, char *buf, int maxlen);
+
+/**
+ * acpi_device_scope() - Get the scope of an ACPI device
+ *
+ * This gets the scope which is the full path of the parent device, as per
+ * acpi_device_path().
+ *
+ * @dev: Device to check
+ * @buf: Buffer to place the path in (should be ACPI_PATH_MAX long)
+ * @maxlen: Size of buffer (typically ACPI_PATH_MAX)
+ * @return 0 if OK, -EINVAL if the device has no parent, other -ve on other
+ *	error
+ */
+int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen);
+
+/**
+ * acpi_device_status() - Get the status of a device
+ *
+ * This currently just returns ACPI_DSTATUS_ALL_ON. It does not support
+ * inactive or hidden devices.
+ *
+ * @dev: Device to check
+ * @return device status, as ACPI_DSTATUS_...
+ */
+enum acpi_dev_status acpi_device_status(const struct udevice *dev);
+
+/**
+ * acpi_device_write_interrupt_irq() - Write an interrupt descriptor
+ *
+ * This writes an ACPI interrupt descriptor for the given interrupt, converting
+ * fields as needed.
+ *
+ * @ctx: ACPI context pointer
+ * @req_irq: Interrupt to output
+ * @return IRQ pin number if OK, -ve on error
+ */
+int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx,
+				    const struct irq *req_irq);
+
+/**
+ * acpi_device_write_gpio() - Write GpioIo() or GpioInt() descriptor
+ *
+ * @gpio: GPIO information to write
+ * @return GPIO pin number of first GPIO if OK, -ve on error
+ */
+int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio);
+
+/**
+ * acpi_device_write_gpio_desc() - Write a GPIO to ACPI
+ *
+ * This creates a GPIO descriptor for a GPIO, including information ACPI needs
+ * to use it.
+ *
+ * @ctx: ACPI context pointer
+ * @desc: GPIO to write
+ * @return 0 if OK, -ve on error
+ */
+int acpi_device_write_gpio_desc(struct acpi_ctx *ctx,
+				const struct gpio_desc *desc);
+
+/**
+ * acpi_device_write_interrupt_or_gpio() - Write interrupt or GPIO to ACPI
+ *
+ * This reads an interrupt from the device tree "interrupts-extended" property,
+ * if available. If not it reads the first GPIO with the name @prop.
+ *
+ * If an interrupt is found, an ACPI interrupt descriptor is written to the ACPI
+ * output. If not, but if a GPIO is found, a GPIO descriptor is written.
+ *
+ * @return irq or GPIO pin number if OK, -ve if neither an interrupt nor a GPIO
+ *	could be found, or some other error occurred
+ */
+int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx,
+					struct udevice *dev, const char *prop);
+
+/**
+ * acpi_device_write_i2c_dev() - Write an I2C device to ACPI
+ *
+ * This creates a I2cSerialBus descriptor for an I2C device, including
+ * information ACPI needs to use it.
+ *
+ * @ctx: ACPI context pointer
+ * @dev: I2C device to write
+ * @return I2C address of device if OK, -ve on error
+ */
+int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev);
+
+/**
+ * acpi_device_write_spi_dev() - Write a SPI device to ACPI
+ *
+ * This writes a serial bus descriptor for the SPI device so that ACPI can use
+ * it
+ *
+ * @ctx: ACPI context pointer
+ * @dev: SPI device to write
+ * @return 0 if OK, -ve on error
+ */
+int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev);
+
+/**
+ * acpi_device_add_power_res() - Add a basic PowerResource block for a device
+ *
+ * This includes GPIOs to control enable, reset and stop operation of the
+ * device. Each GPIO is optional, but at least one must be provided.
+ * This can be applied to any device that has power control, so is fairly
+ * generic.
+ *
+ * Reset - Put the device into / take the device out of reset.
+ * Enable - Enable / disable power to device.
+ * Stop - Stop / start operation of device.
+ *
+ * @ctx: ACPI context pointer
+ * @tx_state_val: Mask to use to toggle the TX state on the GPIO pin, e,g.
+ *	PAD_CFG0_TX_STATE
+ * @dw0_read: Name to use to read dw0, e.g. "\\_SB.GPC0"
+ * @dw0_write: Name to use to read dw0, e.g. "\\_SB.SPC0"
+ * @reset_gpio: GPIO used to take device out of reset or to put it into reset
+ * @reset_delay_ms: Delay to be inserted after device is taken out of reset
+ *	(_ON method delay)
+ * @reset_off_delay_ms: Delay to be inserted after device is put into reset
+ *	(_OFF method delay)
+ * @enable_gpio: GPIO used to enable device
+ * @enable_delay_ms: Delay to be inserted after device is enabled
+ * @enable_off_delay_ms: Delay to be inserted after device is disabled
+ *	(_OFF method delay)
+ * @stop_gpio: GPIO used to stop operation of device
+ * @stop_delay_ms: Delay to be inserted after disabling stop (_ON method delay)
+ * @stop_off_delay_ms: Delay to be inserted after enabling stop.
+ *	(_OFF method delay)
+ *
+ * @return 0 if OK, -ve if at least one GPIO is not provided
+ */
+int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val,
+			      const char *dw0_read, const char *dw0_write,
+			      const struct gpio_desc *reset_gpio,
+			      uint reset_delay_ms, uint reset_off_delay_ms,
+			      const struct gpio_desc *enable_gpio,
+			      uint enable_delay_ms, uint enable_off_delay_ms,
+			      const struct gpio_desc *stop_gpio,
+			      uint stop_delay_ms, uint stop_off_delay_ms);
+
+/**
+ * acpi_device_infer_name() - Infer the name from its uclass or parent
+ *
+ * Many ACPI devices have a standard name that can be inferred from the uclass
+ * they are in, or the uclass of their parent. These rules are implemented in
+ * this function. It attempts to produce a name for a device based on these
+ * rules.
+ *
+ * NOTE: This currently supports only x86 devices. Feel free to enhance it for
+ * other architectures as needed.
+ *
+ * @dev: Device to check
+ * @out_name: Place to put the name (must hold ACPI_NAME_MAX bytes)
+ * @return 0 if a name was found, -ENOENT if not found, -ENXIO if the device
+ *	sequence number could not be determined
+ */
+int acpi_device_infer_name(const struct udevice *dev, char *out_name);
+
+#endif

+ 287 - 0
include/acpi/acpi_dp.h

@@ -0,0 +1,287 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Device properties, a temporary data structure for adding to ACPI code
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot file acpi_device.h
+ */
+
+#ifndef __ACPI_DP_H
+#define __ACPI_DP_H
+
+struct acpi_ctx;
+
+#include <acpi/acpi_device.h>
+
+/*
+ * Writing Device Properties objects via _DSD
+ *
+ * This is described in ACPI 6.3 section 6.2.5
+ *
+ * This provides a structure to handle nested device-specific data which ends
+ * up in a _DSD table.
+ *
+ * https://www.kernel.org/doc/html/latest/firmware-guide/acpi/DSD-properties-rules.html
+ * https://uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf
+ * https://uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf
+ *
+ * The Device Property Hierarchy can be multiple levels deep with multiple
+ * children possible in each level.  In order to support this flexibility
+ * the device property hierarchy must be built up before being written out.
+ *
+ * For example:
+ *
+ * Child table with string and integer:
+ * struct acpi_dp *child = acpi_dp_new_table("CHLD");
+ * acpi_dp_add_string(child, "childstring", "CHILD");
+ * acpi_dp_add_integer(child, "childint", 100);
+ *
+ * _DSD table with integer and gpio and child pointer:
+ * struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
+ * acpi_dp_add_integer(dsd, "number1", 1);
+ * acpi_dp_add_gpio(dsd, "gpio", "\_SB.PCI0.GPIO", 0, 0, 1);
+ * acpi_dp_add_child(dsd, "child", child);
+ *
+ * Write entries into SSDT and clean up resources:
+ * acpi_dp_write(dsd);
+ *
+ * Name(_DSD, Package() {
+ *   ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301")
+ *   Package() {
+ *     Package() { "gpio", Package() { \_SB.PCI0.GPIO, 0, 0, 0 } }
+ *     Package() { "number1", 1 }
+ *   }
+ *   ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b")
+ *   Package() {
+ *     Package() { "child", CHLD }
+ *   }
+ * }
+ * Name(CHLD, Package() {
+ *   ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301")
+ *   Package() {
+ *     Package() { "childstring", "CHILD" }
+ *     Package() { "childint", 100 }
+ *   }
+ * }
+ */
+
+#define ACPI_DP_UUID		"daffd814-6eba-4d8c-8a91-bc9bbf4aa301"
+#define ACPI_DP_CHILD_UUID	"dbb8e3e6-5886-4ba6-8795-1319f52a966b"
+
+/**
+ * enum acpi_dp_type - types of device property objects
+ *
+ * These refer to the types defined by struct acpi_dp below
+ *
+ * @ACPI_DP_TYPE_UNKNOWN: Unknown / do not use
+ * @ACPI_DP_TYPE_INTEGER: Integer value (u64) in @integer
+ * @ACPI_DP_TYPE_STRING: String value in @string
+ * @ACPI_DP_TYPE_REFERENCE: Reference to another object, with value in @string
+ * @ACPI_DP_TYPE_TABLE: Type for a top-level table which may have children
+ * @ACPI_DP_TYPE_ARRAY: Array of items with first item in @array and following
+ *	items linked from that item's @next
+ * @ACPI_DP_TYPE_CHILD: Child object, with siblings in that child's @next
+ */
+enum acpi_dp_type {
+	ACPI_DP_TYPE_UNKNOWN,
+	ACPI_DP_TYPE_INTEGER,
+	ACPI_DP_TYPE_STRING,
+	ACPI_DP_TYPE_REFERENCE,
+	ACPI_DP_TYPE_TABLE,
+	ACPI_DP_TYPE_ARRAY,
+	ACPI_DP_TYPE_CHILD,
+};
+
+/**
+ * struct acpi_dp - ACPI device properties
+ *
+ * @type: Table type
+ * @name: Name of object, typically _DSD but could be CHLD for a child object.
+ *	This can be NULL if there is no name
+ * @next: Next object in list (next array element or next sibling)
+ * @child: Pointer to first child, if @type == ACPI_DP_TYPE_CHILD, else NULL
+ * @array: First array element, if @type == ACPI_DP_TYPE_ARRAY, else NULL
+ * @integer: Integer value of the property, if @type == ACPI_DP_TYPE_INTEGER
+ * @string: String value of the property, if @type == ACPI_DP_TYPE_STRING;
+ *	child name if @type == ACPI_DP_TYPE_CHILD;
+ *	reference name if @type == ACPI_DP_TYPE_REFERENCE;
+ */
+struct acpi_dp {
+	enum acpi_dp_type type;
+	const char *name;
+	struct acpi_dp *next;
+	union {
+		struct acpi_dp *child;
+		struct acpi_dp *array;
+	};
+	union {
+		u64 integer;
+		const char *string;
+	};
+};
+
+/**
+ * acpi_dp_new_table() - Start a new Device Property table
+ *
+ * @ref: ACPI reference (e.g. "_DSD")
+ * @return pointer to table, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_new_table(const char *ref);
+
+/**
+ * acpi_dp_add_integer() - Add integer Device Property
+ *
+ * A new node is added to the end of the property list of @dp
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @value: Integer value
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name,
+				    u64 value);
+
+/**
+ * acpi_dp_add_string() - Add string Device Property
+ *
+ * A new node is added to the end of the property list of @dp
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @string: String value
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name,
+				   const char *string);
+
+/**
+ * acpi_dp_add_reference() - Add reference Device Property
+ *
+ * A new node is added to the end of the property list of @dp
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @reference: Reference value
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name,
+				      const char *reference);
+
+/**
+ * acpi_dp_add_array() - Add array Device Property
+ *
+ * A new node is added to the end of the property list of @dp, with the array
+ * attached to that.
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array);
+
+/**
+ * acpi_dp_add_integer_array() - Add an array of integers
+ *
+ * A new node is added to the end of the property list of @dp, with the array
+ * attached to that. Each element of the array becomes a new node.
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property, or NULL for none
+ * @return pointer to new array node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name,
+					  u64 *array, int len);
+
+/**
+ * acpi_dp_add_child() - Add a child table of Device Properties
+ *
+ * A new node is added as a child of @dp
+ *
+ * @dp: Table to add this child to
+ * @name: Name of child, or NULL for none
+ * @child: Child node to add
+ * @return pointer to new child node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
+				  struct acpi_dp *child);
+
+/**
+ * acpi_dp_add_gpio() - Add a GPIO to a list of Device Properties
+ *
+ * A new node is added to the end of the property list of @dp, with the
+ * GPIO properties added to the the new node
+ *
+ * @dp: Table to add this property to
+ * @name: Name of property
+ * @ref: Reference to device with a _CRS containing GpioIO or GpioInt
+ * @index: Index of the GPIO resource in _CRS starting from zero
+ * @pin: Pin in the GPIO resource, typically zero
+ * @polarity: GPIO polarity. Note that ACPI_IRQ_ACTIVE_BOTH is not supported
+ * @return pointer to new node, or NULL if out of memory
+ */
+struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name,
+				 const char *ref, int index, int pin,
+				 enum acpi_irq_polarity polarity);
+
+/**
+ * acpi_dp_write() - Write Device Property hierarchy and clean up resources
+ *
+ * This writes the table using acpigen and then frees it
+ *
+ * @ctx: ACPI context
+ * @table: Table to write
+ * @return 0 if OK, -ve on error
+ */
+int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table);
+
+/**
+ * acpi_dp_ofnode_copy_int() - Copy a property from device tree to DP
+ *
+ * This copies an integer property from the device tree to the ACPI DP table.
+ *
+ * @node: Node to copy from
+ * @dp: DP to copy to
+ * @prop: Property name to copy
+ * @return 0 if OK, -ve on error
+ */
+int acpi_dp_ofnode_copy_int(ofnode node, struct acpi_dp *dp, const char *prop);
+
+/**
+ * acpi_dp_ofnode_copy_str() - Copy a property from device tree to DP
+ *
+ * This copies a string property from the device tree to the ACPI DP table.
+ *
+ * @node: Node to copy from
+ * @dp: DP to copy to
+ * @prop: Property name to copy
+ * @return 0 if OK, -ve on error
+ */
+int acpi_dp_ofnode_copy_str(ofnode node, struct acpi_dp *dp, const char *prop);
+
+/**
+ * acpi_dp_dev_copy_int() - Copy a property from device tree to DP
+ *
+ * This copies an integer property from the device tree to the ACPI DP table.
+ *
+ * @dev: Device to copy from
+ * @dp: DP to copy to
+ * @prop: Property name to copy
+ * @return 0 if OK, -ve on error
+ */
+int acpi_dp_dev_copy_int(const struct udevice *dev, struct acpi_dp *dp,
+			 const char *prop);
+
+/**
+ * acpi_dp_dev_copy_str() - Copy a property from device tree to DP
+ *
+ * This copies a string property from the device tree to the ACPI DP table.
+ *
+ * @dev: Device to copy from
+ * @dp: DP to copy to
+ * @prop: Property name to copy
+ * @return 0 if OK, -ve on error
+ */
+int acpi_dp_dev_copy_str(const struct udevice *dev, struct acpi_dp *dp,
+			 const char *prop);
+
+#endif

+ 566 - 0
include/acpi/acpigen.h

@@ -0,0 +1,566 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Core ACPI (Advanced Configuration and Power Interface) support
+ *
+ * Copyright 2019 Google LLC
+ *
+ * Modified from coreboot file acpigen.h
+ */
+
+#ifndef __ACPI_ACPIGEN_H
+#define __ACPI_ACPIGEN_H
+
+#include <linux/types.h>
+
+struct acpi_ctx;
+struct acpi_gen_regaddr;
+struct acpi_gpio;
+
+/* Top 4 bits of the value used to indicate a three-byte length value */
+#define ACPI_PKG_LEN_3_BYTES	0x80
+
+#define ACPI_METHOD_NARGS_MASK		0x7
+#define ACPI_METHOD_SERIALIZED_MASK	BIT(3)
+
+#define ACPI_END_TAG			0x79
+
+/* ACPI Op/Prefix codes */
+enum {
+	ZERO_OP			= 0x00,
+	ONE_OP			= 0x01,
+	NAME_OP			= 0x08,
+	BYTE_PREFIX		= 0x0a,
+	WORD_PREFIX		= 0x0b,
+	DWORD_PREFIX		= 0x0c,
+	STRING_PREFIX		= 0x0d,
+	QWORD_PREFIX		= 0x0e,
+	SCOPE_OP		= 0x10,
+	BUFFER_OP		= 0x11,
+	PACKAGE_OP		= 0x12,
+	METHOD_OP		= 0x14,
+	SLEEP_OP		= 0x22,
+	DUAL_NAME_PREFIX	= 0x2e,
+	MULTI_NAME_PREFIX	= 0x2f,
+	DEBUG_OP		= 0x31,
+	EXT_OP_PREFIX		= 0x5b,
+	ROOT_PREFIX		= 0x5c,
+	LOCAL0_OP		= 0x60,
+	LOCAL1_OP		= 0x61,
+	LOCAL2_OP		= 0x62,
+	LOCAL3_OP		= 0x63,
+	LOCAL4_OP		= 0x64,
+	LOCAL5_OP		= 0x65,
+	LOCAL6_OP		= 0x66,
+	LOCAL7_OP		= 0x67,
+	STORE_OP		= 0x70,
+	AND_OP			= 0x7b,
+	OR_OP			= 0x7d,
+	NOT_OP			= 0x80,
+	DEVICE_OP		= 0x82,
+	POWER_RES_OP		= 0x84,
+	RETURN_OP		= 0xa4,
+};
+
+/**
+ * acpigen_get_current() - Get the current ACPI code output pointer
+ *
+ * @ctx: ACPI context pointer
+ * @return output pointer
+ */
+u8 *acpigen_get_current(struct acpi_ctx *ctx);
+
+/**
+ * acpigen_emit_byte() - Emit a byte to the ACPI code
+ *
+ * @ctx: ACPI context pointer
+ * @data: Value to output
+ */
+void acpigen_emit_byte(struct acpi_ctx *ctx, uint data);
+
+/**
+ * acpigen_emit_word() - Emit a 16-bit word to the ACPI code
+ *
+ * @ctx: ACPI context pointer
+ * @data: Value to output
+ */
+void acpigen_emit_word(struct acpi_ctx *ctx, uint data);
+
+/**
+ * acpigen_emit_dword() - Emit a 32-bit 'double word' to the ACPI code
+ *
+ * @ctx: ACPI context pointer
+ * @data: Value to output
+ */
+void acpigen_emit_dword(struct acpi_ctx *ctx, uint data);
+
+/**
+ * acpigen_emit_stream() - Emit a stream of bytes
+ *
+ * @ctx: ACPI context pointer
+ * @data: Data to output
+ * @size: Size of data in bytes
+ */
+void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size);
+
+/**
+ * acpigen_emit_string() - Emit a string
+ *
+ * Emit a string with a null terminator
+ *
+ * @ctx: ACPI context pointer
+ * @str: String to output, or NULL for an empty string
+ */
+void acpigen_emit_string(struct acpi_ctx *ctx, const char *str);
+
+/**
+ * acpigen_write_len_f() - Write a 'forward' length placeholder
+ *
+ * This adds space for a length value in the ACPI stream and pushes the current
+ * position (before the length) on the stack. After calling this you can write
+ * some data and then call acpigen_pop_len() to update the length value.
+ *
+ * Usage:
+ *
+ *    acpigen_write_len_f() ------\
+ *    acpigen_write...()          |
+ *    acpigen_write...()          |
+ *      acpigen_write_len_f() --\ |
+ *      acpigen_write...()      | |
+ *      acpigen_write...()      | |
+ *      acpigen_pop_len() ------/ |
+ *    acpigen_write...()          |
+ *    acpigen_pop_len() ----------/
+ *
+ * See ACPI 6.3 section 20.2.4 Package Length Encoding
+ *
+ * This implementation always uses a 3-byte packet length for simplicity. It
+ * could be adjusted to support other lengths.
+ *
+ * @ctx: ACPI context pointer
+ */
+void acpigen_write_len_f(struct acpi_ctx *ctx);
+
+/**
+ * acpigen_pop_len() - Update the previously stacked length placeholder
+ *
+ * Call this after the data for the block has been written. It updates the
+ * top length value in the stack and pops it off.
+ *
+ * @ctx: ACPI context pointer
+ */
+void acpigen_pop_len(struct acpi_ctx *ctx);
+
+/**
+ * acpigen_write_package() - Start writing a package
+ *
+ * A package collects together a number of elements in the ACPI code. To write
+ * a package use:
+ *
+ * acpigen_write_package(ctx, 3);
+ * ...write things
+ * acpigen_pop_len()
+ *
+ * If you don't know the number of elements in advance, acpigen_write_package()
+ * returns a pointer to the value so you can update it later:
+ *
+ * char *num_elements = acpigen_write_package(ctx, 0);
+ * ...write things
+ * *num_elements += 1;
+ * ...write things
+ * *num_elements += 1;
+ * acpigen_pop_len()
+ *
+ * @ctx: ACPI context pointer
+ * @nr_el: Number of elements (0 if not known)
+ * @returns pointer to the number of elements, which can be updated by the
+ *	caller if needed
+ */
+char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el);
+
+/**
+ * acpigen_write_byte() - Write a byte
+ *
+ * @ctx: ACPI context pointer
+ * @data: Value to write
+ */
+void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data);
+
+/**
+ * acpigen_write_word() - Write a word
+ *
+ * @ctx: ACPI context pointer
+ * @data: Value to write
+ */
+void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data);
+
+/**
+ * acpigen_write_dword() - Write a dword
+ *
+ * @ctx: ACPI context pointer
+ * @data: Value to write
+ */
+void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data);
+
+/**
+ * acpigen_write_qword() - Write a qword
+ *
+ * @ctx: ACPI context pointer
+ * @data: Value to write
+ */
+void acpigen_write_qword(struct acpi_ctx *ctx, u64 data);
+
+/**
+ * acpigen_write_zero() - Write zero
+ *
+ * @ctx: ACPI context pointer
+ */
+void acpigen_write_zero(struct acpi_ctx *ctx);
+
+/**
+ * acpigen_write_one() - Write one
+ *
+ * @ctx: ACPI context pointer
+ */
+void acpigen_write_one(struct acpi_ctx *ctx);
+
+/**
+ * acpigen_write_integer() - Write an integer
+ *
+ * This writes an operation (BYTE_OP, WORD_OP, DWORD_OP, QWORD_OP depending on
+ * the integer size) and an integer value. Note that WORD means 16 bits in ACPI.
+ *
+ * @ctx: ACPI context pointer
+ * @data: Integer to write
+ */
+void acpigen_write_integer(struct acpi_ctx *ctx, u64 data);
+
+/**
+ * acpigen_write_name_zero() - Write a named zero value
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of the value
+ */
+void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name);
+
+/**
+ * acpigen_write_name_one() - Write a named one value
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of the value
+ */
+void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name);
+
+/**
+ * acpigen_write_name_byte() - Write a named byte value
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of the value
+ * @val: Value to write
+ */
+void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, uint val);
+
+/**
+ * acpigen_write_name_word() - Write a named word value
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of the value
+ * @val: Value to write
+ */
+void acpigen_write_name_word(struct acpi_ctx *ctx, const char *name, uint val);
+
+/**
+ * acpigen_write_name_dword() - Write a named dword value
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of the value
+ * @val: Value to write
+ */
+void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, uint val);
+
+/**
+ * acpigen_write_name_qword() - Write a named qword value
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of the value
+ * @val: Value to write
+ */
+void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val);
+
+/**
+ * acpigen_write_name_integer() - Write a named integer value
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of the value
+ * @val: Value to write
+ */
+void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name,
+				u64 val);
+
+/**
+ * acpigen_write_name_string() - Write a named string value
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of the value
+ * @string: String to write
+ */
+void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name,
+			       const char *string);
+
+/**
+ * acpigen_write_string() - Write a string
+ *
+ * This writes a STRING_PREFIX followed by a null-terminated string
+ *
+ * @ctx: ACPI context pointer
+ * @str: String to write
+ */
+void acpigen_write_string(struct acpi_ctx *ctx, const char *str);
+
+/**
+ * acpigen_emit_namestring() - Emit an ACPI name
+ *
+ * This writes out an ACPI name or path in the required special format. It does
+ * not add the NAME_OP prefix.
+ *
+ * @ctx: ACPI context pointer
+ * @namepath: Name / path to emit
+ */
+void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath);
+
+/**
+ * acpigen_write_name() - Write out an ACPI name
+ *
+ * This writes out an ACPI name or path in the required special format with a
+ * NAME_OP prefix.
+ *
+ * @ctx: ACPI context pointer
+ * @namepath: Name / path to emit
+ */
+void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath);
+
+/**
+ * acpigen_write_scope() - Write a scope
+ *
+ * @ctx: ACPI context pointer
+ * @scope: Scope to write (e.g. "\\_SB.ABCD")
+ */
+void acpigen_write_scope(struct acpi_ctx *ctx, const char *scope);
+
+/**
+ * acpigen_write_uuid() - Write a UUID
+ *
+ * This writes out a UUID in the format used by ACPI, with a BUFFER_OP prefix.
+ *
+ * @ctx: ACPI context pointer
+ * @uuid: UUID to write in the form aabbccdd-eeff-gghh-iijj-kkllmmnnoopp
+ * @return 0 if OK, -EINVAL if the format is incorrect
+ */
+int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid);
+
+/**
+ * acpigen_emit_ext_op() - Emit an extended op with the EXT_OP_PREFIX prefix
+ *
+ * @ctx: ACPI context pointer
+ * @op: Operation code (e.g. SLEEP_OP)
+ */
+void acpigen_emit_ext_op(struct acpi_ctx *ctx, uint op);
+
+/**
+ * acpigen_write_method() - Write a method header
+ *
+ * @ctx: ACPI context pointer
+ * @name: Method name (4 characters)
+ * @nargs: Number of method arguments (0 if none)
+ */
+void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs);
+
+/**
+ * acpigen_write_method_serialized() - Write a method header
+ *
+ * This sets the 'serialized' flag so that the method is thread-safe
+ *
+ * @ctx: ACPI context pointer
+ * @name: Method name (4 characters)
+ * @nargs: Number of method arguments (0 if none)
+ */
+void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name,
+				     int nargs);
+
+/**
+ * acpigen_write_device() - Write an ACPI device
+ *
+ * @ctx: ACPI context pointer
+ * @name: Device name to write
+ */
+void acpigen_write_device(struct acpi_ctx *ctx, const char *name);
+
+/**
+ * acpigen_write_sta() - Write a _STA method
+ *
+ * @ctx: ACPI context pointer
+ * @status: Status value to return
+ */
+void acpigen_write_sta(struct acpi_ctx *ctx, uint status);
+
+/**
+ * acpigen_write_resourcetemplate_header() - Write a ResourceTemplate header
+ *
+ * @ctx: ACPI context pointer
+ */
+void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx);
+
+/**
+ * acpigen_write_resourcetemplate_footer() - Write a ResourceTemplate footer
+ *
+ * @ctx: ACPI context pointer
+ */
+void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx);
+
+/**
+ * acpigen_write_register_resource() - Write a register resource
+ *
+ * This writes a header, the address information and a footer
+ *
+ * @ctx: ACPI context pointer
+ * @addr: Address to write
+ */
+void acpigen_write_register_resource(struct acpi_ctx *ctx,
+				     const struct acpi_gen_regaddr *addr);
+
+/**
+ * acpigen_write_sleep() - Write a sleep operation
+ *
+ * @ctx: ACPI context pointer
+ * @sleep_ms: Number of milliseconds to sleep for
+ */
+void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms);
+
+/**
+ * acpigen_write_store() - Write a store operation
+ *
+ * @ctx: ACPI context pointer
+ */
+void acpigen_write_store(struct acpi_ctx *ctx);
+
+/**
+ * acpigen_write_debug_string() - Write a debug string
+ *
+ * This writes a debug operation with an associated string
+ *
+ * @ctx: ACPI context pointer
+ * @str: String to write
+ */
+void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str);
+
+/**
+ * acpigen_write_or() - Write a bitwise OR operation
+ *
+ * res = arg1 | arg2
+ *
+ * @ctx: ACPI context pointer
+ * @arg1: ACPI opcode for operand 1 (e.g. LOCAL0_OP)
+ * @arg2: ACPI opcode for operand 2 (e.g. LOCAL1_OP)
+ * @res: ACPI opcode for result (e.g. LOCAL2_OP)
+ */
+void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res);
+
+/**
+ * acpigen_write_and() - Write a bitwise AND operation
+ *
+ * res = arg1 & arg2
+ *
+ * @ctx: ACPI context pointer
+ * @arg1: ACPI opcode for operand 1 (e.g. LOCAL0_OP)
+ * @arg2: ACPI opcode for operand 2 (e.g. LOCAL1_OP)
+ * @res: ACPI opcode for result (e.g. LOCAL2_OP)
+ */
+void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res);
+
+/**
+ * acpigen_write_not() - Write a bitwise NOT operation
+ *
+ * res = ~arg1
+ *
+ * @ctx: ACPI context pointer
+ * @arg: ACPI opcode for operand (e.g. LOCAL0_OP)
+ * @res: ACPI opcode for result (e.g. LOCAL2_OP)
+ */
+void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res);
+
+/**
+ * acpigen_write_power_res() - Write a power resource
+ *
+ * Name (_PRx, Package(One) { name })
+ * ...
+ * PowerResource (name, level, order)
+ *
+ * The caller should fill in the rest of the power resource and then call
+ * acpigen_pop_len() to close it off
+ *
+ * @ctx: ACPI context pointer
+ * @name: Name of power resource (e.g. "PRIC")
+ * @level: Deepest sleep level that this resource must be kept on (0=S0, 3=S3)
+ * @order: Order that this must be enabled/disabled (e.g. 0)
+ * @dev_stats: List of states to define, e.g. {"_PR0", "_PR3"}
+ * @dev_states_count: Number of dev states
+ */
+void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, uint level,
+			     uint order, const char *const dev_states[],
+			     size_t dev_states_count);
+
+/**
+ * acpigen_set_enable_tx_gpio() - Emit ACPI code to enable/disable a GPIO
+ *
+ * This emits code to either enable to disable a Tx GPIO. It takes account of
+ * the GPIO polarity.
+ *
+ * The code needs access to the DW0 register for the pad being used. This is
+ * provided by gpio->pin0_addr and ACPI methods must be defined for the board
+ * which can read and write the pad's DW0 register given this address:
+ *    @dw0_read: takes a single argument, the DW0 address
+ *		 returns the DW0 value
+ *    @dw0:write: takes two arguments, the DW0 address and the value to write
+ *		 no return value
+ *
+ * Example code (-- means comment):
+ *
+ *	-- Get Pad Configuration DW0 register value
+ *	Method (GPC0, 0x1, Serialized)
+ *	{
+ *		-- Arg0 - GPIO DW0 address
+ *		Store (Arg0, Local0)
+ *		OperationRegion (PDW0, SystemMemory, Local0, 4)
+ *		Field (PDW0, AnyAcc, NoLock, Preserve) {
+ *			TEMP, 32
+ *		}
+ *		Return (TEMP)
+ *	}
+ *
+ *	-- Set Pad Configuration DW0 register value
+ *	Method (SPC0, 0x2, Serialized)
+ *	{
+ *		-- Arg0 - GPIO DW0 address
+ *		-- Arg1 - Value for DW0 register
+ *		Store (Arg0, Local0)
+ *		OperationRegion (PDW0, SystemMemory, Local0, 4)
+ *		Field (PDW0, AnyAcc, NoLock, Preserve) {
+ *			TEMP,32
+ *		}
+ *		Store (Arg1, TEMP)
+ *	}
+ *
+ *
+ * @ctx: ACPI context pointer
+ * @tx_state_val: Mask to use to toggle the TX state on the GPIO pin, e,g.
+ *	PAD_CFG0_TX_STATE
+ * @dw0_read: Method name to use to read dw0, e.g. "\\_SB.GPC0"
+ * @dw0_write: Method name to use to read dw0, e.g. "\\_SB.SPC0"
+ * @gpio: GPIO to change
+ * @enable: true to enable GPIO, false to disable
+ * Returns 0 on success, -ve on error.
+ */
+int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val,
+			       const char *dw0_read, const char *dw0_write,
+			       struct acpi_gpio *gpio, bool enable);
+
+#endif

+ 27 - 0
include/asm-generic/gpio.h

@@ -10,6 +10,7 @@
 #include <dm/ofnode.h>
 #include <linux/bitops.h>
 
+struct acpi_gpio;
 struct ofnode_phandle_args;
 
 /*
@@ -329,6 +330,20 @@ struct dm_gpio_ops {
 	 */
 	int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
 			     ulong *flags);
+
+#if CONFIG_IS_ENABLED(ACPIGEN)
+	/**
+	 * get_acpi() - Get the ACPI info for a GPIO
+	 *
+	 * This converts a GPIO to an ACPI structure for adding to the ACPI
+	 * tables.
+	 *
+	 * @desc:	GPIO description to convert
+	 * @gpio:	Output ACPI GPIO information
+	 * @return ACPI pin number or -ve on error
+	 */
+	int (*get_acpi)(const struct gpio_desc *desc, struct acpi_gpio *gpio);
+#endif
 };
 
 /**
@@ -674,4 +689,16 @@ int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
  */
 int gpio_get_number(const struct gpio_desc *desc);
 
+/**
+ * gpio_get_acpi() - Get the ACPI pin for a GPIO
+ *
+ * This converts a GPIO to an ACPI pin number for adding to the ACPI
+ * tables. If the GPIO is invalid, the pin_count and pins[0] are set to 0
+ *
+ * @desc:	GPIO description to convert
+ * @gpio:	Output ACPI GPIO information
+ * @return ACPI pin number or -ve on error
+ */
+int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio);
+
 #endif	/* _ASM_GENERIC_GPIO_H_ */

+ 30 - 0
include/binman.h

@@ -9,6 +9,8 @@
 #ifndef _BINMAN_H_
 #define _BINMAN_H_
 
+#include <dm/ofnode.h>
+
 /**
  *struct binman_entry - information about a binman entry
  *
@@ -20,6 +22,26 @@ struct binman_entry {
 	u32 size;
 };
 
+/**
+ * binman_entry_map() - Look up the address of an entry in memory
+ *
+ * @parent: Parent binman node
+ * @name: Name of entry
+ * @bufp: Returns a pointer to the entry
+ * @sizep: Returns the size of the entry
+ * @return 0 on success, -EPERM if the ROM offset is not set, -ENOENT if the
+ *	entry cannot be found, other error code other error
+ */
+int binman_entry_map(ofnode parent, const char *name, void **bufp, int *sizep);
+
+/**
+ * binman_set_rom_offset() - Set the ROM memory-map offset
+ *
+ * @rom_offset: Offset from an image_pos to the memory-mapped address. This
+ *	tells binman that ROM image_pos x can be addressed at rom_offset + x
+ */
+void binman_set_rom_offset(int rom_offset);
+
 /**
  * binman_entry_find() - Find a binman symbol
  *
@@ -33,6 +55,14 @@ struct binman_entry {
  */
 int binman_entry_find(const char *name, struct binman_entry *entry);
 
+/**
+ * binman_section_find_node() - Find a binman node
+ *
+ * @name: Name of node to look for
+ * @return Node that was found, ofnode_null() if not found
+ */
+ofnode binman_section_find_node(const char *name);
+
 /**
  * binman_init() - Set up the binman symbol information
  *

+ 118 - 1
include/dm/acpi.h

@@ -16,30 +16,51 @@
 #define ACPI_OPS_PTR(_ptr)
 #endif
 
-/* Length of an ACPI name string, excluding nul terminator */
+/* Length of an ACPI name string, excluding null terminator */
 #define ACPI_NAME_LEN	4
 
 /* Length of an ACPI name string including nul terminator */
 #define ACPI_NAME_MAX	(ACPI_NAME_LEN + 1)
 
+/* Number of nested objects supported */
+#define ACPIGEN_LENSTACK_SIZE 10
+
 #if !defined(__ACPI__)
 
+struct nhlt;
+
+/** enum acpi_dump_option - selects what ACPI information to dump */
+enum acpi_dump_option {
+	ACPI_DUMP_LIST,		/* Just the list of items */
+	ACPI_DUMP_CONTENTS,	/* Include the binary contents also */
+};
+
 /**
  * struct acpi_ctx - Context used for writing ACPI tables
  *
  * This contains a few useful pieces of information used when writing
  *
+ * @base: Base address of ACPI tables
  * @current: Current address for writing
  * @rsdp: Pointer to the Root System Description Pointer, typically used when
  *	adding a new table. The RSDP holds pointers to the RSDT and XSDT.
  * @rsdt: Pointer to the Root System Description Table
  * @xsdt: Pointer to the Extended System Description Table
+ * @nhlt: Intel Non-High-Definition-Audio Link Table (NHLT) pointer, used to
+ *	build up information that audio codecs need to provide in the NHLT ACPI
+ *	table
+ * @len_stack: Stack of 'length' words to fix up later
+ * @ltop: Points to current top of stack (0 = empty)
  */
 struct acpi_ctx {
+	void *base;
 	void *current;
 	struct acpi_rsdp *rsdp;
 	struct acpi_rsdt *rsdt;
 	struct acpi_xsdt *xsdt;
+	struct nhlt *nhlt;
+	char *len_stack[ACPIGEN_LENSTACK_SIZE];
+	int ltop;
 };
 
 /**
@@ -65,6 +86,48 @@ struct acpi_ops {
 	 * @return 0 if OK, -ve on error
 	 */
 	int (*write_tables)(const struct udevice *dev, struct acpi_ctx *ctx);
+
+	/**
+	 * fill_ssdt() - Generate SSDT code for a device
+	 *
+	 * This is called to create the SSDT code. The method should write out
+	 * whatever ACPI code is needed by this device. It will end up in the
+	 * SSDT table.
+	 *
+	 * Note that this is called 'fill' because the entire contents of the
+	 * SSDT is build by calling this method on all devices.
+	 *
+	 * @dev: Device to write
+	 * @ctx: ACPI context to use
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*fill_ssdt)(const struct udevice *dev, struct acpi_ctx *ctx);
+
+	/**
+	 * inject_dsdt() - Generate DSDT code for a device
+	 *
+	 * This is called to create the DSDT code. The method should write out
+	 * whatever ACPI code is needed by this device. It will end up in the
+	 * DSDT table.
+	 *
+	 * Note that this is called 'inject' because the output of calling this
+	 * method on all devices is injected into the DSDT, the bulk of which
+	 * is written in .asl files for the board.
+	 *
+	 * @dev: Device to write
+	 * @ctx: ACPI context to use
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*inject_dsdt)(const struct udevice *dev, struct acpi_ctx *ctx);
+
+	/**
+	 * setup_nhlt() - Set up audio information for this device
+	 *
+	 * The method can add information to ctx->nhlt if it likes
+	 *
+	 * @return 0 if OK, -ENODATA if nothing to add, -ve on error
+	 */
+	int (*setup_nhlt)(const struct udevice *dev, struct acpi_ctx *ctx);
 };
 
 #define device_get_acpi_ops(dev)	((dev)->driver->acpi_ops)
@@ -109,6 +172,60 @@ int acpi_copy_name(char *out_name, const char *name);
  */
 int acpi_write_dev_tables(struct acpi_ctx *ctx);
 
+/**
+ * acpi_fill_ssdt() - Generate ACPI tables for SSDT
+ *
+ * This is called to create the SSDT code for all devices.
+ *
+ * @ctx: ACPI context to use
+ * @return 0 if OK, -ve on error
+ */
+int acpi_fill_ssdt(struct acpi_ctx *ctx);
+
+/**
+ * acpi_inject_dsdt() - Generate ACPI tables for DSDT
+ *
+ * This is called to create the DSDT code for all devices.
+ *
+ * @ctx: ACPI context to use
+ * @return 0 if OK, -ve on error
+ */
+int acpi_inject_dsdt(struct acpi_ctx *ctx);
+
+/**
+ * acpi_setup_nhlt() - Set up audio information
+ *
+ * This is called to set up the nhlt information for all devices.
+ *
+ * @ctx: ACPI context to use
+ * @nhlt: Pointer to nhlt information to add to
+ * @return 0 if OK, -ve on error
+ */
+int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt);
+
+/**
+ * acpi_dump_items() - Dump out the collected ACPI items
+ *
+ * This lists the ACPI DSDT and SSDT items generated by the various U-Boot
+ * drivers.
+ *
+ * @option: Sets what should be dumpyed
+ */
+void acpi_dump_items(enum acpi_dump_option option);
+
+/**
+ * acpi_get_path() - Get the full ACPI path for a device
+ *
+ * This checks for any override in the device tree and calls acpi_device_path()
+ * if not
+ *
+ * @dev: Device to check
+ * @out_path: Buffer to place the path in (should be ACPI_PATH_MAX long)
+ * @maxlen: Size of buffer (typically ACPI_PATH_MAX)
+ * @return 0 if OK, -ve on error
+ */
+int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen);
+
 #endif /* __ACPI__ */
 
 #endif

+ 1 - 1
include/dm/device.h

@@ -764,7 +764,7 @@ int dev_enable_by_path(const char *path);
  */
 static inline bool device_is_on_pci_bus(const struct udevice *dev)
 {
-	return device_get_uclass_id(dev->parent) == UCLASS_PCI;
+	return dev->parent && device_get_uclass_id(dev->parent) == UCLASS_PCI;
 }
 
 /**

+ 14 - 0
include/dt-bindings/interrupt-controller/x86-irq.h

@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2019 Google LLC
+ *
+ * This provides additional flags used by x86.
+ */
+
+#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_X86_IRQ_H
+#define _DT_BINDINGS_INTERRUPT_CONTROLLER_X86_IRQ_H
+
+#define X86_IRQ_TYPE_SHARED	(1 << 4)
+#define X86_IRQ_TYPE_WAKE	(1 << 5)
+
+#endif

+ 24 - 0
include/dt-bindings/sound/nhlt.h

@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2019 Google LLC
+ */
+
+#ifndef _DT_BINDINGS_SOUND_NHLT_H
+#define _DT_BINDINGS_SOUND_NHLT_H
+
+/* See Table 2-1. NHLT Endpoint Descriptor in the NHLT Specification (0.8.1) */
+#define NHLT_VID	0x8086
+#define NHLT_DID_DMIC	0xae20
+#define NHLT_DID_BT	0xae30
+#define NHLT_DID_SSP	0xae34
+
+/* Hardware links available to use for codecs */
+#define AUDIO_LINK_SSP0		0
+#define AUDIO_LINK_SSP1		1
+#define AUDIO_LINK_SSP2		2
+#define AUDIO_LINK_SSP3		3
+#define AUDIO_LINK_SSP4		4
+#define AUDIO_LINK_SSP5		5
+#define AUDIO_LINK_DMIC		6
+
+#endif /* _DT_BINDINGS_SOUND_NHLT_H */

+ 43 - 0
include/irq.h

@@ -8,6 +8,9 @@
 #ifndef __irq_H
 #define __irq_H
 
+struct acpi_irq;
+struct ofnode_phandle_args;
+
 /*
  * Interrupt controller types available. You can find a particular one with
  * irq_first_device_type()
@@ -24,10 +27,12 @@ enum irq_dev_t {
  *
  * @dev: IRQ device that handles this irq
  * @id: ID to identify this irq with the device
+ * @flags: Flags associated with this interrupt (IRQ_TYPE_...)
  */
 struct irq {
 	struct udevice *dev;
 	ulong id;
+	ulong flags;
 };
 
 /**
@@ -119,10 +124,36 @@ struct irq_ops {
 	 * @return 0 if OK, or a negative error code.
 	 */
 	int (*free)(struct irq *irq);
+
+#if CONFIG_IS_ENABLED(ACPIGEN)
+	/**
+	 * get_acpi() - Get the ACPI info for an irq
+	 *
+	 * This converts a irq to an ACPI structure for adding to the ACPI
+	 * tables.
+	 *
+	 * @irq:	irq to convert
+	 * @acpi_irq:	Output ACPI interrupt information
+	 * @return ACPI pin number or -ve on error
+	 */
+	int (*get_acpi)(const struct irq *irq, struct acpi_irq *acpi_irq);
+#endif
 };
 
 #define irq_get_ops(dev)	((struct irq_ops *)(dev)->driver->ops)
 
+/**
+ * irq_is_valid() - Check if an IRQ is valid
+ *
+ * @irq:	IRQ description containing device and ID, e.g. previously
+ *		returned by irq_get_by_index()
+ * @return true if valid, false if not
+ */
+static inline bool irq_is_valid(const struct irq *irq)
+{
+	return irq->dev != NULL;
+}
+
 /**
  * irq_route_pmc_gpio_gpe() - Get the GPIO for an event
  *
@@ -223,4 +254,16 @@ int irq_free(struct irq *irq);
  */
 int irq_first_device_type(enum irq_dev_t type, struct udevice **devp);
 
+/**
+ * irq_get_acpi() - Get the ACPI info for an irq
+ *
+ * This converts a irq to an ACPI structure for adding to the ACPI
+ * tables.
+ *
+ * @irq:	irq to convert
+ * @acpi_irq:	Output ACPI interrupt information
+ * @return ACPI pin number or -ve on error
+ */
+int irq_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq);
+
 #endif

+ 33 - 1
include/p2sb.h

@@ -31,13 +31,36 @@ struct p2sb_uc_priv {
 };
 
 /**
- * struct p2sb_ops - Operations for the P2SB (none at present)
+ * struct p2sb_ops - Operations for the P2SB
  */
 struct p2sb_ops {
+	/**
+	 * set_hide() - Set/clear the 'hide' bit on the p2sb
+	 *
+	 * This device can be hidden from the PCI bus if needed. This method
+	 * can be called before the p2sb is probed.
+	 *
+	 * @dev: P2SB device
+	 * @hide: true to hide the device, false to show it
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*set_hide)(struct udevice *dev, bool hide);
 };
 
 #define p2sb_get_ops(dev)        ((struct p2sb_ops *)(dev)->driver->ops)
 
+/**
+ * p2sb_set_hide() - Set/clear the 'hide' bit on the p2sb
+ *
+ * This device can be hidden from the PCI bus if needed. This method
+ * can be called before the p2sb is probed.
+ *
+ * @dev: P2SB device
+ * @hide: true to hide the device, false to show it
+ * @return 0 if OK, -ve on error
+ */
+int p2sb_set_hide(struct udevice *dev, bool hide);
+
 /**
  * pcr_read32/16/8() - Read from a PCR device
  *
@@ -132,4 +155,13 @@ int p2sb_set_port_id(struct udevice *dev, int portid);
  */
 int p2sb_get_port_id(struct udevice *dev);
 
+/**
+ * pcr_reg_address() Convert an offset in p2sb space to an absolute address
+ *
+ * @dev: Child device (whose parent is UCLASS_P2SB)
+ * @offset: Offset within that child's address space
+ * @return pointer to that offset within the child's address space
+ */
+void *pcr_reg_address(struct udevice *dev, uint offset);
+
 #endif

+ 14 - 0
include/power/acpi_pmc.h

@@ -6,10 +6,22 @@
 #ifndef __ACPI_PMC_H
 #define __ACPI_PMC_H
 
+#ifndef __ACPI__
+
 enum {
 	GPE0_REG_MAX	= 4,
 };
 
+enum {
+	PM1_STS		= 0x00,
+	PM1_EN		= 0x02,
+	PM1_CNT		= 0x04,
+	PM1_TMR		= 0x08,
+
+	GPE0_STS	= 0x20,
+	GPE0_EN		= 0x30,
+};
+
 /**
  * struct acpi_pmc_upriv - holds common data for the x86 PMC
  *
@@ -182,4 +194,6 @@ void pmc_dump_info(struct udevice *dev);
  */
 int pmc_gpe_init(struct udevice *dev);
 
+#endif /* !__ACPI__ */
+
 #endif

+ 2 - 2
include/spi.h

@@ -13,8 +13,8 @@
 #include <linux/bitops.h>
 
 /* SPI mode flags */
-#define SPI_CPHA	BIT(0)			/* clock phase */
-#define SPI_CPOL	BIT(1)			/* clock polarity */
+#define SPI_CPHA	BIT(0)	/* clock phase (1 = SPI_CLOCK_PHASE_SECOND) */
+#define SPI_CPOL	BIT(1)	/* clock polarity (1 = SPI_POLARITY_HIGH) */
 #define SPI_MODE_0	(0|0)			/* (original MicroWire) */
 #define SPI_MODE_1	(0|SPI_CPHA)
 #define SPI_MODE_2	(SPI_CPOL|0)

+ 17 - 0
include/test/ut.h

@@ -134,6 +134,23 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes);
 	}								\
 }
 
+/*
+ * Assert that two string expressions are equal, up to length of the
+ * first
+ */
+#define ut_asserteq_strn(expr1, expr2) {				\
+	const char *_val1 = (expr1), *_val2 = (expr2);			\
+	int _len = strlen(_val1);					\
+									\
+	if (memcmp(_val1, _val2, _len)) {				\
+		ut_failf(uts, __FILE__, __LINE__, __func__,		\
+			 #expr1 " = " #expr2,				\
+			 "Expected \"%.*s\", got \"%.*s\"",		\
+			 _len, _val1, _len, _val2);			\
+		return CMD_RET_FAILURE;					\
+	}								\
+}
+
 /* Assert that two memory areas are equal */
 #define ut_asserteq_mem(expr1, expr2, len) {				\
 	const u8 *_val1 = (u8 *)(expr1), *_val2 = (u8 *)(expr2);	\

+ 11 - 0
include/time.h

@@ -17,6 +17,17 @@ unsigned long get_timer(unsigned long base);
 unsigned long timer_get_us(void);
 uint64_t get_timer_us(uint64_t base);
 
+/**
+ * get_timer_us_long() - Get the number of elapsed microseconds
+ *
+ * This uses 32-bit arithmetic on 32-bit machines, which is enough to handle
+ * delays of over an hour. For 64-bit machines it uses a 64-bit value.
+ *
+ *@base: Base time to consider
+ *@return elapsed time since @base
+ */
+unsigned long get_timer_us_long(unsigned long base);
+
 /*
  * timer_test_add_offset()
  *

+ 3 - 0
lib/acpi/Makefile

@@ -1,4 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0+
 #
 
+obj-y += acpigen.o
+obj-y += acpi_device.o
+obj-y += acpi_dp.o
 obj-y += acpi_table.o

+ 823 - 0
lib/acpi/acpi_device.c

@@ -0,0 +1,823 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generation of tables for particular device types
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot file of the same name
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <irq.h>
+#include <log.h>
+#include <usb.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpigen.h>
+#include <asm-generic/gpio.h>
+#include <dm/acpi.h>
+
+/**
+ * acpi_device_path_fill() - Find the root device and build a path from there
+ *
+ * This recursively reaches back to the root device and progressively adds path
+ * elements until the device is reached.
+ *
+ * @dev: Device to return path of
+ * @buf: Buffer to hold the path
+ * @buf_len: Length of buffer
+ * @cur: Current position in the buffer
+ * @return new position in buffer after adding @dev, or -ve on error
+ */
+static int acpi_device_path_fill(const struct udevice *dev, char *buf,
+				 size_t buf_len, int cur)
+{
+	char name[ACPI_NAME_MAX];
+	int next = 0;
+	int ret;
+
+	ret = acpi_get_name(dev, name);
+	if (ret)
+		return ret;
+
+	/*
+	 * Make sure this name segment will fit, including the path segment
+	 * separator and possible NULL terminator, if this is the last segment.
+	 */
+	if (cur + strlen(name) + 2 > buf_len)
+		return -ENOSPC;
+
+	/* Walk up the tree to the root device */
+	if (dev_get_parent(dev)) {
+		next = acpi_device_path_fill(dev_get_parent(dev), buf, buf_len,
+					     cur);
+		if (next < 0)
+			return next;
+	}
+
+	/* Fill in the path from the root device */
+	next += snprintf(buf + next, buf_len - next, "%s%s",
+			 dev_get_parent(dev) && *name ? "." : "", name);
+
+	return next;
+}
+
+int acpi_device_path(const struct udevice *dev, char *buf, int maxlen)
+{
+	int ret;
+
+	ret = acpi_device_path_fill(dev, buf, maxlen, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int acpi_device_scope(const struct udevice *dev, char *scope, int maxlen)
+{
+	int ret;
+
+	if (!dev_get_parent(dev))
+		return log_msg_ret("noparent", -EINVAL);
+
+	ret = acpi_device_path_fill(dev_get_parent(dev), scope, maxlen, 0);
+	if (ret < 0)
+		return log_msg_ret("fill", ret);
+
+	return 0;
+}
+
+enum acpi_dev_status acpi_device_status(const struct udevice *dev)
+{
+	return ACPI_DSTATUS_ALL_ON;
+}
+
+/**
+ * largeres_write_len_f() - Write a placeholder word value
+ *
+ * Write a forward length for a large resource (2 bytes)
+ *
+ * @return pointer to the zero word (for fixing up later)
+ */
+static void *largeres_write_len_f(struct acpi_ctx *ctx)
+{
+	u8 *p = acpigen_get_current(ctx);
+
+	acpigen_emit_word(ctx, 0);
+
+	return p;
+}
+
+/**
+ * largeres_fill_from_len() - Fill in a length value
+ *
+ * This calculated the number of bytes since the provided @start and writes it
+ * to @ptr, which was previous returned by largeres_write_len_f().
+ *
+ * @ptr: Word to update
+ * @start: Start address to count from to calculated the length
+ */
+static void largeres_fill_from_len(struct acpi_ctx *ctx, char *ptr, u8 *start)
+{
+	u16 len = acpigen_get_current(ctx) - start;
+
+	ptr[0] = len & 0xff;
+	ptr[1] = (len >> 8) & 0xff;
+}
+
+/**
+ * largeres_fill_len() - Fill in a length value, excluding the length itself
+ *
+ * Fill in the length field with the value calculated from after the 16bit
+ * field to acpigen current. This is useful since the length value does not
+ * include the length field itself.
+ *
+ * This calls acpi_device_largeres_fill_len() passing @ptr + 2 as @start
+ *
+ * @ptr: Word to update.
+ */
+static void largeres_fill_len(struct acpi_ctx *ctx, void *ptr)
+{
+	largeres_fill_from_len(ctx, ptr, ptr + sizeof(u16));
+}
+
+/* ACPI 6.3 section 6.4.3.6: Extended Interrupt Descriptor */
+static int acpi_device_write_interrupt(struct acpi_ctx *ctx,
+				       const struct acpi_irq *irq)
+{
+	void *desc_length;
+	u8 flags;
+
+	if (!irq->pin)
+		return -ENOENT;
+
+	/* This is supported by GpioInt() but not Interrupt() */
+	if (irq->polarity == ACPI_IRQ_ACTIVE_BOTH)
+		return -EINVAL;
+
+	/* Byte 0: Descriptor Type */
+	acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_INTERRUPT);
+
+	/* Byte 1-2: Length (filled in later) */
+	desc_length = largeres_write_len_f(ctx);
+
+	/*
+	 * Byte 3: Flags
+	 *  [7:5]: Reserved
+	 *    [4]: Wake     (0=NO_WAKE   1=WAKE)
+	 *    [3]: Sharing  (0=EXCLUSIVE 1=SHARED)
+	 *    [2]: Polarity (0=HIGH      1=LOW)
+	 *    [1]: Mode     (0=LEVEL     1=EDGE)
+	 *    [0]: Resource (0=PRODUCER  1=CONSUMER)
+	 */
+	flags = BIT(0); /* ResourceConsumer */
+	if (irq->mode == ACPI_IRQ_EDGE_TRIGGERED)
+		flags |= BIT(1);
+	if (irq->polarity == ACPI_IRQ_ACTIVE_LOW)
+		flags |= BIT(2);
+	if (irq->shared == ACPI_IRQ_SHARED)
+		flags |= BIT(3);
+	if (irq->wake == ACPI_IRQ_WAKE)
+		flags |= BIT(4);
+	acpigen_emit_byte(ctx, flags);
+
+	/* Byte 4: Interrupt Table Entry Count */
+	acpigen_emit_byte(ctx, 1);
+
+	/* Byte 5-8: Interrupt Number */
+	acpigen_emit_dword(ctx, irq->pin);
+
+	/* Fill in Descriptor Length (account for len word) */
+	largeres_fill_len(ctx, desc_length);
+
+	return 0;
+}
+
+int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx,
+				    const struct irq *req_irq)
+{
+	struct acpi_irq irq;
+	int ret;
+
+	ret = irq_get_acpi(req_irq, &irq);
+	if (ret)
+		return log_msg_ret("get", ret);
+	ret = acpi_device_write_interrupt(ctx, &irq);
+	if (ret)
+		return log_msg_ret("write", ret);
+
+	return irq.pin;
+}
+
+/* ACPI 6.3 section 6.4.3.8.1 - GPIO Interrupt or I/O */
+int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio)
+{
+	void *start, *desc_length;
+	void *pin_table_offset, *vendor_data_offset, *resource_offset;
+	u16 flags = 0;
+	int pin;
+
+	if (gpio->type > ACPI_GPIO_TYPE_IO)
+		return -EINVAL;
+
+	start = acpigen_get_current(ctx);
+
+	/* Byte 0: Descriptor Type */
+	acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_GPIO);
+
+	/* Byte 1-2: Length (fill in later) */
+	desc_length = largeres_write_len_f(ctx);
+
+	/* Byte 3: Revision ID */
+	acpigen_emit_byte(ctx, ACPI_GPIO_REVISION_ID);
+
+	/* Byte 4: GpioIo or GpioInt */
+	acpigen_emit_byte(ctx, gpio->type);
+
+	/*
+	 * Byte 5-6: General Flags
+	 *   [15:1]: 0 => Reserved
+	 *      [0]: 1 => ResourceConsumer
+	 */
+	acpigen_emit_word(ctx, 1 << 0);
+
+	switch (gpio->type) {
+	case ACPI_GPIO_TYPE_INTERRUPT:
+		/*
+		 * Byte 7-8: GPIO Interrupt Flags
+		 *   [15:5]: 0 => Reserved
+		 *      [4]: Wake     (0=NO_WAKE   1=WAKE)
+		 *      [3]: Sharing  (0=EXCLUSIVE 1=SHARED)
+		 *    [2:1]: Polarity (0=HIGH      1=LOW     2=BOTH)
+		 *      [0]: Mode     (0=LEVEL     1=EDGE)
+		 */
+		if (gpio->irq.mode == ACPI_IRQ_EDGE_TRIGGERED)
+			flags |= 1 << 0;
+		if (gpio->irq.shared == ACPI_IRQ_SHARED)
+			flags |= 1 << 3;
+		if (gpio->irq.wake == ACPI_IRQ_WAKE)
+			flags |= 1 << 4;
+
+		switch (gpio->irq.polarity) {
+		case ACPI_IRQ_ACTIVE_HIGH:
+			flags |= 0 << 1;
+			break;
+		case ACPI_IRQ_ACTIVE_LOW:
+			flags |= 1 << 1;
+			break;
+		case ACPI_IRQ_ACTIVE_BOTH:
+			flags |= 2 << 1;
+			break;
+		}
+		break;
+
+	case ACPI_GPIO_TYPE_IO:
+		/*
+		 * Byte 7-8: GPIO IO Flags
+		 *   [15:4]: 0 => Reserved
+		 *      [3]: Sharing  (0=EXCLUSIVE 1=SHARED)
+		 *      [2]: 0 => Reserved
+		 *    [1:0]: IO Restriction
+		 *           0 => IoRestrictionNone
+		 *           1 => IoRestrictionInputOnly
+		 *           2 => IoRestrictionOutputOnly
+		 *           3 => IoRestrictionNoneAndPreserve
+		 */
+		flags |= gpio->io_restrict & 3;
+		if (gpio->io_shared)
+			flags |= 1 << 3;
+		break;
+	}
+	acpigen_emit_word(ctx, flags);
+
+	/*
+	 * Byte 9: Pin Configuration
+	 *  0x01 => Default (no configuration applied)
+	 *  0x02 => Pull-up
+	 *  0x03 => Pull-down
+	 *  0x04-0x7F => Reserved
+	 *  0x80-0xff => Vendor defined
+	 */
+	acpigen_emit_byte(ctx, gpio->pull);
+
+	/* Byte 10-11: Output Drive Strength in 1/100 mA */
+	acpigen_emit_word(ctx, gpio->output_drive_strength);
+
+	/* Byte 12-13: Debounce Timeout in 1/100 ms */
+	acpigen_emit_word(ctx, gpio->interrupt_debounce_timeout);
+
+	/* Byte 14-15: Pin Table Offset, relative to start */
+	pin_table_offset = largeres_write_len_f(ctx);
+
+	/* Byte 16: Reserved */
+	acpigen_emit_byte(ctx, 0);
+
+	/* Byte 17-18: Resource Source Name Offset, relative to start */
+	resource_offset = largeres_write_len_f(ctx);
+
+	/* Byte 19-20: Vendor Data Offset, relative to start */
+	vendor_data_offset = largeres_write_len_f(ctx);
+
+	/* Byte 21-22: Vendor Data Length */
+	acpigen_emit_word(ctx, 0);
+
+	/* Fill in Pin Table Offset */
+	largeres_fill_from_len(ctx, pin_table_offset, start);
+
+	/* Pin Table, one word for each pin */
+	for (pin = 0; pin < gpio->pin_count; pin++)
+		acpigen_emit_word(ctx, gpio->pins[pin]);
+
+	/* Fill in Resource Source Name Offset */
+	largeres_fill_from_len(ctx, resource_offset, start);
+
+	/* Resource Source Name String */
+	acpigen_emit_string(ctx, gpio->resource);
+
+	/* Fill in Vendor Data Offset */
+	largeres_fill_from_len(ctx, vendor_data_offset, start);
+
+	/* Fill in GPIO Descriptor Length (account for len word) */
+	largeres_fill_len(ctx, desc_length);
+
+	return gpio->pins[0];
+}
+
+int acpi_device_write_gpio_desc(struct acpi_ctx *ctx,
+				const struct gpio_desc *desc)
+{
+	struct acpi_gpio gpio;
+	int ret;
+
+	ret = gpio_get_acpi(desc, &gpio);
+	if (ret)
+		return log_msg_ret("desc", ret);
+	ret = acpi_device_write_gpio(ctx, &gpio);
+	if (ret < 0)
+		return log_msg_ret("gpio", ret);
+
+	return ret;
+}
+
+int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx,
+					struct udevice *dev, const char *prop)
+{
+	struct irq req_irq;
+	int pin;
+	int ret;
+
+	ret = irq_get_by_index(dev, 0, &req_irq);
+	if (!ret) {
+		ret = acpi_device_write_interrupt_irq(ctx, &req_irq);
+		if (ret < 0)
+			return log_msg_ret("irq", ret);
+		pin = ret;
+	} else {
+		struct gpio_desc req_gpio;
+
+		ret = gpio_request_by_name(dev, prop, 0, &req_gpio,
+					   GPIOD_IS_IN);
+		if (ret)
+			return log_msg_ret("no gpio", ret);
+		ret = acpi_device_write_gpio_desc(ctx, &req_gpio);
+		if (ret < 0)
+			return log_msg_ret("gpio", ret);
+		pin = ret;
+	}
+
+	return pin;
+}
+
+/* PowerResource() with Enable and/or Reset control */
+int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val,
+			      const char *dw0_read, const char *dw0_write,
+			      const struct gpio_desc *reset_gpio,
+			      uint reset_delay_ms, uint reset_off_delay_ms,
+			      const struct gpio_desc *enable_gpio,
+			      uint enable_delay_ms, uint enable_off_delay_ms,
+			      const struct gpio_desc *stop_gpio,
+			      uint stop_delay_ms, uint stop_off_delay_ms)
+{
+	static const char *const power_res_dev_states[] = { "_PR0", "_PR3" };
+	struct acpi_gpio reset, enable, stop;
+	bool has_reset, has_enable, has_stop;
+	int ret;
+
+	gpio_get_acpi(reset_gpio, &reset);
+	gpio_get_acpi(enable_gpio, &enable);
+	gpio_get_acpi(stop_gpio, &stop);
+	has_reset = reset.pins[0];
+	has_enable = enable.pins[0];
+	has_stop = stop.pins[0];
+
+	if (!has_reset && !has_enable && !has_stop)
+		return -EINVAL;
+
+	/* PowerResource (PRIC, 0, 0) */
+	acpigen_write_power_res(ctx, "PRIC", 0, 0, power_res_dev_states,
+				ARRAY_SIZE(power_res_dev_states));
+
+	/* Method (_STA, 0, NotSerialized) { Return (0x1) } */
+	acpigen_write_sta(ctx, 0x1);
+
+	/* Method (_ON, 0, Serialized) */
+	acpigen_write_method_serialized(ctx, "_ON", 0);
+	if (reset_gpio) {
+		ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+						 dw0_write, &reset, true);
+		if (ret)
+			return log_msg_ret("reset1", ret);
+	}
+	if (has_enable) {
+		ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+						 dw0_write, &enable, true);
+		if (ret)
+			return log_msg_ret("enable1", ret);
+		if (enable_delay_ms)
+			acpigen_write_sleep(ctx, enable_delay_ms);
+	}
+	if (has_reset) {
+		ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+						 dw0_write, &reset, false);
+		if (ret)
+			return log_msg_ret("reset2", ret);
+		if (reset_delay_ms)
+			acpigen_write_sleep(ctx, reset_delay_ms);
+	}
+	if (has_stop) {
+		ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+						 dw0_write, &stop, false);
+		if (ret)
+			return log_msg_ret("stop1", ret);
+		if (stop_delay_ms)
+			acpigen_write_sleep(ctx, stop_delay_ms);
+	}
+	acpigen_pop_len(ctx);		/* _ON method */
+
+	/* Method (_OFF, 0, Serialized) */
+	acpigen_write_method_serialized(ctx, "_OFF", 0);
+	if (has_stop) {
+		ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+						 dw0_write, &stop, true);
+		if (ret)
+			return log_msg_ret("stop2", ret);
+		if (stop_off_delay_ms)
+			acpigen_write_sleep(ctx, stop_off_delay_ms);
+	}
+	if (has_reset) {
+		ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+						 dw0_write, &reset, true);
+		if (ret)
+			return log_msg_ret("reset3", ret);
+		if (reset_off_delay_ms)
+			acpigen_write_sleep(ctx, reset_off_delay_ms);
+	}
+	if (has_enable) {
+		ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+						 dw0_write, &enable, false);
+		if (ret)
+			return log_msg_ret("enable2", ret);
+		if (enable_off_delay_ms)
+			acpigen_write_sleep(ctx, enable_off_delay_ms);
+	}
+	acpigen_pop_len(ctx);		/* _OFF method */
+
+	acpigen_pop_len(ctx);		/* PowerResource PRIC */
+
+	return 0;
+}
+
+/* ACPI 6.3 section 6.4.3.8.2.1 - I2cSerialBus() */
+static void acpi_device_write_i2c(struct acpi_ctx *ctx,
+				  const struct acpi_i2c *i2c)
+{
+	void *desc_length, *type_length;
+
+	/* Byte 0: Descriptor Type */
+	acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_SERIAL_BUS);
+
+	/* Byte 1+2: Length (filled in later) */
+	desc_length = largeres_write_len_f(ctx);
+
+	/* Byte 3: Revision ID */
+	acpigen_emit_byte(ctx, ACPI_I2C_SERIAL_BUS_REVISION_ID);
+
+	/* Byte 4: Resource Source Index is Reserved */
+	acpigen_emit_byte(ctx, 0);
+
+	/* Byte 5: Serial Bus Type is I2C */
+	acpigen_emit_byte(ctx, ACPI_SERIAL_BUS_TYPE_I2C);
+
+	/*
+	 * Byte 6: Flags
+	 *  [7:2]: 0 => Reserved
+	 *    [1]: 1 => ResourceConsumer
+	 *    [0]: 0 => ControllerInitiated
+	 */
+	acpigen_emit_byte(ctx, 1 << 1);
+
+	/*
+	 * Byte 7-8: Type Specific Flags
+	 *   [15:1]: 0 => Reserved
+	 *      [0]: 0 => 7bit, 1 => 10bit
+	 */
+	acpigen_emit_word(ctx, i2c->mode_10bit);
+
+	/* Byte 9: Type Specific Revision ID */
+	acpigen_emit_byte(ctx, ACPI_I2C_TYPE_SPECIFIC_REVISION_ID);
+
+	/* Byte 10-11: I2C Type Data Length */
+	type_length = largeres_write_len_f(ctx);
+
+	/* Byte 12-15: I2C Bus Speed */
+	acpigen_emit_dword(ctx, i2c->speed);
+
+	/* Byte 16-17: I2C Slave Address */
+	acpigen_emit_word(ctx, i2c->address);
+
+	/* Fill in Type Data Length */
+	largeres_fill_len(ctx, type_length);
+
+	/* Byte 18+: ResourceSource */
+	acpigen_emit_string(ctx, i2c->resource);
+
+	/* Fill in I2C Descriptor Length */
+	largeres_fill_len(ctx, desc_length);
+}
+
+/**
+ * acpi_device_set_i2c() - Set up an ACPI I2C struct from a device
+ *
+ * The value of @scope is not copied, but only referenced. This implies the
+ * caller has to ensure it stays valid for the lifetime of @i2c.
+ *
+ * @dev: I2C device to convert
+ * @i2c: Place to put the new structure
+ * @scope: Scope of the I2C device (this is the controller path)
+ * @return chip address of device
+ */
+static int acpi_device_set_i2c(const struct udevice *dev, struct acpi_i2c *i2c,
+			       const char *scope)
+{
+	struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+	struct udevice *bus = dev_get_parent(dev);
+
+	memset(i2c, '\0', sizeof(*i2c));
+	i2c->address = chip->chip_addr;
+	i2c->mode_10bit = 0;
+
+	/*
+	 * i2c_bus->speed_hz is set if this device is probed, but if not we
+	 * must use the device tree
+	 */
+	i2c->speed = dev_read_u32_default(bus, "clock-frequency",
+					  I2C_SPEED_STANDARD_RATE);
+	i2c->resource = scope;
+
+	return i2c->address;
+}
+
+int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev)
+{
+	char scope[ACPI_PATH_MAX];
+	struct acpi_i2c i2c;
+	int ret;
+
+	ret = acpi_device_scope(dev, scope, sizeof(scope));
+	if (ret)
+		return log_msg_ret("scope", ret);
+	ret = acpi_device_set_i2c(dev, &i2c, scope);
+	if (ret < 0)
+		return log_msg_ret("set", ret);
+	acpi_device_write_i2c(ctx, &i2c);
+
+	return ret;
+}
+
+#ifdef CONFIG_SPI
+/* ACPI 6.1 section 6.4.3.8.2.2 - SpiSerialBus() */
+static void acpi_device_write_spi(struct acpi_ctx *ctx, const struct acpi_spi *spi)
+{
+	void *desc_length, *type_length;
+	u16 flags = 0;
+
+	/* Byte 0: Descriptor Type */
+	acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_SERIAL_BUS);
+
+	/* Byte 1+2: Length (filled in later) */
+	desc_length = largeres_write_len_f(ctx);
+
+	/* Byte 3: Revision ID */
+	acpigen_emit_byte(ctx, ACPI_SPI_SERIAL_BUS_REVISION_ID);
+
+	/* Byte 4: Resource Source Index is Reserved */
+	acpigen_emit_byte(ctx, 0);
+
+	/* Byte 5: Serial Bus Type is SPI */
+	acpigen_emit_byte(ctx, ACPI_SERIAL_BUS_TYPE_SPI);
+
+	/*
+	 * Byte 6: Flags
+	 *  [7:2]: 0 => Reserved
+	 *    [1]: 1 => ResourceConsumer
+	 *    [0]: 0 => ControllerInitiated
+	 */
+	acpigen_emit_byte(ctx, BIT(1));
+
+	/*
+	 * Byte 7-8: Type Specific Flags
+	 *   [15:2]: 0 => Reserveda
+	 *      [1]: 0 => ActiveLow, 1 => ActiveHigh
+	 *      [0]: 0 => FourWire, 1 => ThreeWire
+	 */
+	if (spi->wire_mode == SPI_3_WIRE_MODE)
+		flags |= BIT(0);
+	if (spi->device_select_polarity == SPI_POLARITY_HIGH)
+		flags |= BIT(1);
+	acpigen_emit_word(ctx, flags);
+
+	/* Byte 9: Type Specific Revision ID */
+	acpigen_emit_byte(ctx, ACPI_SPI_TYPE_SPECIFIC_REVISION_ID);
+
+	/* Byte 10-11: SPI Type Data Length */
+	type_length = largeres_write_len_f(ctx);
+
+	/* Byte 12-15: Connection Speed */
+	acpigen_emit_dword(ctx, spi->speed);
+
+	/* Byte 16: Data Bit Length */
+	acpigen_emit_byte(ctx, spi->data_bit_length);
+
+	/* Byte 17: Clock Phase */
+	acpigen_emit_byte(ctx, spi->clock_phase);
+
+	/* Byte 18: Clock Polarity */
+	acpigen_emit_byte(ctx, spi->clock_polarity);
+
+	/* Byte 19-20: Device Selection */
+	acpigen_emit_word(ctx, spi->device_select);
+
+	/* Fill in Type Data Length */
+	largeres_fill_len(ctx, type_length);
+
+	/* Byte 21+: ResourceSource String */
+	acpigen_emit_string(ctx, spi->resource);
+
+	/* Fill in SPI Descriptor Length */
+	largeres_fill_len(ctx, desc_length);
+}
+
+/**
+ * acpi_device_set_spi() - Set up an ACPI SPI struct from a device
+ *
+ * The value of @scope is not copied, but only referenced. This implies the
+ * caller has to ensure it stays valid for the lifetime of @spi.
+ *
+ * @dev: SPI device to convert
+ * @spi: Place to put the new structure
+ * @scope: Scope of the SPI device (this is the controller path)
+ * @return 0 (always)
+ */
+static int acpi_device_set_spi(const struct udevice *dev, struct acpi_spi *spi,
+			       const char *scope)
+{
+	struct dm_spi_slave_platdata *plat;
+	struct spi_slave *slave = dev_get_parent_priv(dev);
+
+	plat = dev_get_parent_platdata(slave->dev);
+	memset(spi, '\0', sizeof(*spi));
+	spi->device_select = plat->cs;
+	spi->device_select_polarity = SPI_POLARITY_LOW;
+	spi->wire_mode = SPI_4_WIRE_MODE;
+	spi->speed = plat->max_hz;
+	spi->data_bit_length = slave->wordlen;
+	spi->clock_phase = plat->mode & SPI_CPHA ?
+		 SPI_CLOCK_PHASE_SECOND : SPI_CLOCK_PHASE_FIRST;
+	spi->clock_polarity = plat->mode & SPI_CPOL ?
+		 SPI_POLARITY_HIGH : SPI_POLARITY_LOW;
+	spi->resource = scope;
+
+	return 0;
+}
+
+int acpi_device_write_spi_dev(struct acpi_ctx *ctx, const struct udevice *dev)
+{
+	char scope[ACPI_PATH_MAX];
+	struct acpi_spi spi;
+	int ret;
+
+	ret = acpi_device_scope(dev, scope, sizeof(scope));
+	if (ret)
+		return log_msg_ret("scope", ret);
+	ret = acpi_device_set_spi(dev, &spi, scope);
+	if (ret)
+		return log_msg_ret("set", ret);
+	acpi_device_write_spi(ctx, &spi);
+
+	return 0;
+}
+#endif /* CONFIG_SPI */
+
+static const char *acpi_name_from_id(enum uclass_id id)
+{
+	switch (id) {
+	case UCLASS_USB_HUB:
+		/* Root Hub */
+		return "RHUB";
+	/* DSDT: acpi/northbridge.asl */
+	case UCLASS_NORTHBRIDGE:
+		return "MCHC";
+	/* DSDT: acpi/lpc.asl */
+	case UCLASS_LPC:
+		return "LPCB";
+	/* DSDT: acpi/xhci.asl */
+	case UCLASS_USB:
+		/* This only supports USB3.0 controllers at present */
+		return "XHCI";
+	case UCLASS_PWM:
+		return "PWM";
+	default:
+		return NULL;
+	}
+}
+
+static int acpi_check_seq(const struct udevice *dev)
+{
+	if (dev->req_seq == -1) {
+		log_warning("Device '%s' has no seq\n", dev->name);
+		return log_msg_ret("no seq", -ENXIO);
+	}
+
+	return dev->req_seq;
+}
+
+/* If you change this function, add test cases to dm_test_acpi_get_name() */
+int acpi_device_infer_name(const struct udevice *dev, char *out_name)
+{
+	enum uclass_id parent_id = UCLASS_INVALID;
+	enum uclass_id id;
+	const char *name = NULL;
+
+	id = device_get_uclass_id(dev);
+	if (dev_get_parent(dev))
+		parent_id = device_get_uclass_id(dev_get_parent(dev));
+
+	if (id == UCLASS_SOUND)
+		name = "HDAS";
+	else if (id == UCLASS_PCI)
+		name = "PCI0";
+	else if (device_is_on_pci_bus(dev))
+		name = acpi_name_from_id(id);
+	if (!name) {
+		switch (parent_id) {
+		case UCLASS_USB: {
+			struct usb_device *udev = dev_get_parent_priv(dev);
+
+			sprintf(out_name, udev->speed >= USB_SPEED_SUPER ?
+				"HS%02d" : "FS%02d", udev->portnr);
+			name = out_name;
+			break;
+		}
+		default:
+			break;
+		}
+	}
+	if (!name) {
+		int num;
+
+		switch (id) {
+		/* DSDT: acpi/lpss.asl */
+		case UCLASS_SERIAL:
+			num = acpi_check_seq(dev);
+			if (num < 0)
+				return num;
+			sprintf(out_name, "URT%d", num);
+			name = out_name;
+			break;
+		case UCLASS_I2C:
+			num = acpi_check_seq(dev);
+			if (num < 0)
+				return num;
+			sprintf(out_name, "I2C%d", num);
+			name = out_name;
+			break;
+		case UCLASS_SPI:
+			num = acpi_check_seq(dev);
+			if (num < 0)
+				return num;
+			sprintf(out_name, "SPI%d", num);
+			name = out_name;
+			break;
+		default:
+			break;
+		}
+	}
+	if (!name) {
+		log_warning("No name for device '%s'\n", dev->name);
+		return -ENOENT;
+	}
+	if (name != out_name)
+		acpi_copy_name(out_name, name);
+
+	return 0;
+}

+ 402 - 0
lib/acpi/acpi_dp.c

@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generation of tables for particular device types
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot file acpi_device.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <uuid.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_dp.h>
+#include <dm/acpi.h>
+
+static void acpi_dp_write_array(struct acpi_ctx *ctx,
+				const struct acpi_dp *array);
+
+static void acpi_dp_write_value(struct acpi_ctx *ctx,
+				const struct acpi_dp *prop)
+{
+	switch (prop->type) {
+	case ACPI_DP_TYPE_INTEGER:
+		acpigen_write_integer(ctx, prop->integer);
+		break;
+	case ACPI_DP_TYPE_STRING:
+	case ACPI_DP_TYPE_CHILD:
+		acpigen_write_string(ctx, prop->string);
+		break;
+	case ACPI_DP_TYPE_REFERENCE:
+		acpigen_emit_namestring(ctx, prop->string);
+		break;
+	case ACPI_DP_TYPE_ARRAY:
+		acpi_dp_write_array(ctx, prop->array);
+		break;
+	default:
+		break;
+	}
+}
+
+/* Package (2) { "prop->name", VALUE } */
+static void acpi_dp_write_property(struct acpi_ctx *ctx,
+				   const struct acpi_dp *prop)
+{
+	acpigen_write_package(ctx, 2);
+	acpigen_write_string(ctx, prop->name);
+	acpi_dp_write_value(ctx, prop);
+	acpigen_pop_len(ctx);
+}
+
+/* Write array of Device Properties */
+static void acpi_dp_write_array(struct acpi_ctx *ctx,
+				const struct acpi_dp *array)
+{
+	const struct acpi_dp *dp;
+	char *pkg_count;
+
+	/* Package element count determined as it is populated */
+	pkg_count = acpigen_write_package(ctx, 0);
+
+	/*
+	 * Only acpi_dp of type DP_TYPE_TABLE is allowed to be an array.
+	 * DP_TYPE_TABLE does not have a value to be written. Thus, start
+	 * the loop from next type in the array.
+	 */
+	for (dp = array->next; dp; dp = dp->next) {
+		acpi_dp_write_value(ctx, dp);
+		(*pkg_count)++;
+	}
+
+	acpigen_pop_len(ctx);
+}
+
+static void acpi_dp_free(struct acpi_dp *dp)
+{
+	assert(dp);
+	while (dp) {
+		struct acpi_dp *p = dp->next;
+
+		switch (dp->type) {
+		case ACPI_DP_TYPE_CHILD:
+			acpi_dp_free(dp->child);
+			break;
+		case ACPI_DP_TYPE_ARRAY:
+			acpi_dp_free(dp->array);
+			break;
+		default:
+			break;
+		}
+
+		free(dp);
+		dp = p;
+	}
+}
+
+static int acpi_dp_write_internal(struct acpi_ctx *ctx, struct acpi_dp *table)
+{
+	struct acpi_dp *dp, *prop;
+	char *dp_count, *prop_count = NULL;
+	int child_count = 0;
+	int ret;
+
+	assert(table);
+	if (table->type != ACPI_DP_TYPE_TABLE)
+		return 0;
+
+	/* Name (name) */
+	acpigen_write_name(ctx, table->name);
+
+	/* Device Property list starts with the next entry */
+	prop = table->next;
+
+	/* Package (DP), default to assuming no properties or children */
+	dp_count = acpigen_write_package(ctx, 0);
+
+	/* Print base properties */
+	for (dp = prop; dp; dp = dp->next) {
+		if (dp->type == ACPI_DP_TYPE_CHILD) {
+			child_count++;
+		} else {
+			/*
+			 * The UUID and package is only added when
+			 * we come across the first property.  This
+			 * is to avoid creating a zero-length package
+			 * in situations where there are only children.
+			 */
+			if (!prop_count) {
+				*dp_count += 2;
+				/* ToUUID (ACPI_DP_UUID) */
+				ret = acpigen_write_uuid(ctx, ACPI_DP_UUID);
+				if (ret)
+					return log_msg_ret("touuid", ret);
+				/*
+				 * Package (PROP), element count determined as
+				 * it is populated
+				 */
+				prop_count = acpigen_write_package(ctx, 0);
+			}
+			(*prop_count)++;
+			acpi_dp_write_property(ctx, dp);
+		}
+	}
+
+	if (prop_count) {
+		/* Package (PROP) length, if a package was written */
+		acpigen_pop_len(ctx);
+	}
+
+	if (child_count) {
+		/* Update DP package count to 2 or 4 */
+		*dp_count += 2;
+		/* ToUUID (ACPI_DP_CHILD_UUID) */
+		ret = acpigen_write_uuid(ctx, ACPI_DP_CHILD_UUID);
+		if (ret)
+			return log_msg_ret("child uuid", ret);
+
+		/* Print child pointer properties */
+		acpigen_write_package(ctx, child_count);
+
+		for (dp = prop; dp; dp = dp->next)
+			if (dp->type == ACPI_DP_TYPE_CHILD)
+				acpi_dp_write_property(ctx, dp);
+		/* Package (CHILD) length */
+		acpigen_pop_len(ctx);
+	}
+
+	/* Package (DP) length */
+	acpigen_pop_len(ctx);
+
+	/* Recursively parse children into separate tables */
+	for (dp = prop; dp; dp = dp->next) {
+		if (dp->type == ACPI_DP_TYPE_CHILD) {
+			ret = acpi_dp_write_internal(ctx, dp->child);
+			if (ret)
+				return log_msg_ret("dp child", ret);
+		}
+	}
+
+	return 0;
+}
+
+int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table)
+{
+	int ret;
+
+	ret = acpi_dp_write_internal(ctx, table);
+
+	/* Clean up */
+	acpi_dp_free(table);
+
+	if (ret)
+		return log_msg_ret("write", ret);
+
+	return 0;
+}
+
+static struct acpi_dp *acpi_dp_new(struct acpi_dp *dp, enum acpi_dp_type type,
+				   const char *name)
+{
+	struct acpi_dp *new;
+
+	new = malloc(sizeof(struct acpi_dp));
+	if (!new)
+		return NULL;
+
+	memset(new, '\0', sizeof(*new));
+	new->type = type;
+	new->name = name;
+
+	if (dp) {
+		/* Add to end of property list */
+		while (dp->next)
+			dp = dp->next;
+		dp->next = new;
+	}
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_new_table(const char *name)
+{
+	return acpi_dp_new(NULL, ACPI_DP_TYPE_TABLE, name);
+}
+
+struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name,
+				    u64 value)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_INTEGER, name);
+
+	if (new)
+		new->integer = value;
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name,
+				   const char *string)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_STRING, name);
+	if (new)
+		new->string = string;
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name,
+				      const char *reference)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_REFERENCE, name);
+	if (new)
+		new->string = reference;
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
+				  struct acpi_dp *child)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	if (child->type != ACPI_DP_TYPE_TABLE)
+		return NULL;
+
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_CHILD, name);
+	if (new) {
+		new->child = child;
+		new->string = child->name;
+	}
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array)
+{
+	struct acpi_dp *new;
+
+	assert(dp);
+	assert(array);
+	if (array->type != ACPI_DP_TYPE_TABLE)
+		return NULL;
+
+	new = acpi_dp_new(dp, ACPI_DP_TYPE_ARRAY, array->name);
+	if (new)
+		new->array = array;
+
+	return new;
+}
+
+struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name,
+					  u64 *array, int len)
+{
+	struct acpi_dp *dp_array;
+	int i;
+
+	assert(dp);
+	if (len <= 0)
+		return NULL;
+
+	dp_array = acpi_dp_new_table(name);
+	if (!dp_array)
+		return NULL;
+
+	for (i = 0; i < len; i++)
+		if (!acpi_dp_add_integer(dp_array, NULL, array[i]))
+			break;
+
+	if (!acpi_dp_add_array(dp, dp_array))
+		return NULL;
+
+	return dp_array;
+}
+
+struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name,
+				 const char *ref, int index, int pin,
+				 enum acpi_irq_polarity polarity)
+{
+	struct acpi_dp *gpio;
+
+	assert(dp);
+	gpio = acpi_dp_new_table(name);
+	if (!gpio)
+		return NULL;
+
+	if (!acpi_dp_add_reference(gpio, NULL, ref) ||
+	    !acpi_dp_add_integer(gpio, NULL, index) ||
+	    !acpi_dp_add_integer(gpio, NULL, pin) ||
+	    !acpi_dp_add_integer(gpio, NULL, polarity == ACPI_IRQ_ACTIVE_LOW))
+		return NULL;
+
+	if (!acpi_dp_add_array(dp, gpio))
+		return NULL;
+
+	return gpio;
+}
+
+int acpi_dp_ofnode_copy_int(ofnode node, struct acpi_dp *dp, const char *prop)
+{
+	int ret;
+	u32 val = 0;
+
+	ret = ofnode_read_u32(node, prop, &val);
+	if (ret)
+		return ret;
+	if (!acpi_dp_add_integer(dp, prop, val))
+		return log_ret(-ENOMEM);
+
+	return 0;
+}
+
+int acpi_dp_ofnode_copy_str(ofnode node, struct acpi_dp *dp, const char *prop)
+{
+	const char *val;
+
+	val = ofnode_read_string(node, prop);
+	if (!val)
+		return -EINVAL;
+	if (!acpi_dp_add_string(dp, prop, val))
+		return log_ret(-ENOMEM);
+
+	return 0;
+}
+
+int acpi_dp_dev_copy_int(const struct udevice *dev, struct acpi_dp *dp,
+			 const char *prop)
+{
+	int ret;
+	u32 val = 0;
+
+	ret = dev_read_u32(dev, prop, &val);
+	if (ret)
+		return ret;
+	if (!acpi_dp_add_integer(dp, prop, val))
+		return log_ret(-ENOMEM);
+
+	return ret;
+}
+
+int acpi_dp_dev_copy_str(const struct udevice *dev, struct acpi_dp *dp,
+			 const char *prop)
+{
+	const char *val;
+
+	val = dev_read_string(dev, prop);
+	if (!val)
+		return -EINVAL;
+	if (!acpi_dp_add_string(dp, prop, val))
+		return log_ret(-ENOMEM);
+
+	return 0;
+}

+ 1 - 0
lib/acpi/acpi_table.c

@@ -237,6 +237,7 @@ static void acpi_write_xsdt(struct acpi_xsdt *xsdt)
 
 void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start)
 {
+	ctx->base = start;
 	ctx->current = start;
 
 	/* Align ACPI tables to 16 byte */

+ 616 - 0
lib/acpi/acpigen.c

@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generation of ACPI (Advanced Configuration and Power Interface) tables
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot
+ */
+
+#define LOG_CATEGORY LOGC_ACPI
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <uuid.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_table.h>
+#include <dm/acpi.h>
+
+u8 *acpigen_get_current(struct acpi_ctx *ctx)
+{
+	return ctx->current;
+}
+
+void acpigen_emit_byte(struct acpi_ctx *ctx, uint data)
+{
+	*(u8 *)ctx->current++ = data;
+}
+
+void acpigen_emit_word(struct acpi_ctx *ctx, uint data)
+{
+	acpigen_emit_byte(ctx, data & 0xff);
+	acpigen_emit_byte(ctx, (data >> 8) & 0xff);
+}
+
+void acpigen_emit_dword(struct acpi_ctx *ctx, uint data)
+{
+	/* Output the value in little-endian format */
+	acpigen_emit_byte(ctx, data & 0xff);
+	acpigen_emit_byte(ctx, (data >> 8) & 0xff);
+	acpigen_emit_byte(ctx, (data >> 16) & 0xff);
+	acpigen_emit_byte(ctx, (data >> 24) & 0xff);
+}
+
+/*
+ * Maximum length for an ACPI object generated by this code,
+ *
+ * If you need to change this, change acpigen_write_len_f(ctx) and
+ * acpigen_pop_len(ctx)
+ */
+#define ACPIGEN_MAXLEN 0xfffff
+
+void acpigen_write_len_f(struct acpi_ctx *ctx)
+{
+	assert(ctx->ltop < (ACPIGEN_LENSTACK_SIZE - 1));
+	ctx->len_stack[ctx->ltop++] = ctx->current;
+	acpigen_emit_byte(ctx, 0);
+	acpigen_emit_byte(ctx, 0);
+	acpigen_emit_byte(ctx, 0);
+}
+
+void acpigen_pop_len(struct acpi_ctx *ctx)
+{
+	int len;
+	char *p;
+
+	assert(ctx->ltop > 0);
+	p = ctx->len_stack[--ctx->ltop];
+	len = ctx->current - (void *)p;
+	assert(len <= ACPIGEN_MAXLEN);
+	/* generate store length for 0xfffff max */
+	p[0] = ACPI_PKG_LEN_3_BYTES | (len & 0xf);
+	p[1] = len >> 4 & 0xff;
+	p[2] = len >> 12 & 0xff;
+}
+
+void acpigen_emit_ext_op(struct acpi_ctx *ctx, uint op)
+{
+	acpigen_emit_byte(ctx, EXT_OP_PREFIX);
+	acpigen_emit_byte(ctx, op);
+}
+
+char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el)
+{
+	char *p;
+
+	acpigen_emit_byte(ctx, PACKAGE_OP);
+	acpigen_write_len_f(ctx);
+	p = ctx->current;
+	acpigen_emit_byte(ctx, nr_el);
+
+	return p;
+}
+
+void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data)
+{
+	acpigen_emit_byte(ctx, BYTE_PREFIX);
+	acpigen_emit_byte(ctx, data & 0xff);
+}
+
+void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data)
+{
+	acpigen_emit_byte(ctx, WORD_PREFIX);
+	acpigen_emit_word(ctx, data);
+}
+
+void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data)
+{
+	acpigen_emit_byte(ctx, DWORD_PREFIX);
+	acpigen_emit_dword(ctx, data);
+}
+
+void acpigen_write_qword(struct acpi_ctx *ctx, u64 data)
+{
+	acpigen_emit_byte(ctx, QWORD_PREFIX);
+	acpigen_emit_dword(ctx, data & 0xffffffff);
+	acpigen_emit_dword(ctx, (data >> 32) & 0xffffffff);
+}
+
+void acpigen_write_zero(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, ZERO_OP);
+}
+
+void acpigen_write_one(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, ONE_OP);
+}
+
+void acpigen_write_integer(struct acpi_ctx *ctx, u64 data)
+{
+	if (data == 0)
+		acpigen_write_zero(ctx);
+	else if (data == 1)
+		acpigen_write_one(ctx);
+	else if (data <= 0xff)
+		acpigen_write_byte(ctx, (unsigned char)data);
+	else if (data <= 0xffff)
+		acpigen_write_word(ctx, (unsigned int)data);
+	else if (data <= 0xffffffff)
+		acpigen_write_dword(ctx, (unsigned int)data);
+	else
+		acpigen_write_qword(ctx, data);
+}
+
+void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_zero(ctx);
+}
+
+void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_one(ctx);
+}
+
+void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, uint val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_byte(ctx, val);
+}
+
+void acpigen_write_name_word(struct acpi_ctx *ctx, const char *name, uint val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_word(ctx, val);
+}
+
+void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, uint val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_dword(ctx, val);
+}
+
+void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_qword(ctx, val);
+}
+
+void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name, u64 val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_integer(ctx, val);
+}
+
+void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name,
+			       const char *string)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_string(ctx, string);
+}
+
+void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		acpigen_emit_byte(ctx, data[i]);
+}
+
+void acpigen_emit_string(struct acpi_ctx *ctx, const char *str)
+{
+	acpigen_emit_stream(ctx, str, str ? strlen(str) : 0);
+	acpigen_emit_byte(ctx, '\0');
+}
+
+void acpigen_write_string(struct acpi_ctx *ctx, const char *str)
+{
+	acpigen_emit_byte(ctx, STRING_PREFIX);
+	acpigen_emit_string(ctx, str);
+}
+
+/*
+ * The naming conventions for ACPI namespace names are a bit tricky as
+ * each element has to be 4 chars wide ("All names are a fixed 32 bits.")
+ * and "By convention, when an ASL compiler pads a name shorter than 4
+ * characters, it is done so with trailing underscores ('_')".
+ *
+ * Check sections 5.3, 20.2.2 and 20.4 of ACPI spec 6.3 for details.
+ */
+static void acpigen_emit_simple_namestring(struct acpi_ctx *ctx,
+					   const char *name)
+{
+	const char *ptr;
+	int i;
+
+	for (i = 0, ptr = name; i < 4; i++) {
+		if (!*ptr || *ptr == '.')
+			acpigen_emit_byte(ctx, '_');
+		else
+			acpigen_emit_byte(ctx, *ptr++);
+	}
+}
+
+static void acpigen_emit_double_namestring(struct acpi_ctx *ctx,
+					   const char *name, int dotpos)
+{
+	acpigen_emit_byte(ctx, DUAL_NAME_PREFIX);
+	acpigen_emit_simple_namestring(ctx, name);
+	acpigen_emit_simple_namestring(ctx, &name[dotpos + 1]);
+}
+
+static void acpigen_emit_multi_namestring(struct acpi_ctx *ctx,
+					  const char *name)
+{
+	unsigned char *pathlen;
+	int count = 0;
+
+	acpigen_emit_byte(ctx, MULTI_NAME_PREFIX);
+	pathlen = ctx->current;
+	acpigen_emit_byte(ctx, 0);
+
+	while (*name) {
+		acpigen_emit_simple_namestring(ctx, name);
+		/* find end or next entity */
+		while (*name != '.' && *name)
+			name++;
+		/* forward to next */
+		if (*name == '.')
+			name++;
+		count++;
+	}
+
+	*pathlen = count;
+}
+
+void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath)
+{
+	int dotcount;
+	int dotpos;
+	int i;
+
+	/* We can start with a '\' */
+	if (*namepath == '\\') {
+		acpigen_emit_byte(ctx, '\\');
+		namepath++;
+	}
+
+	/* And there can be any number of '^' */
+	while (*namepath == '^') {
+		acpigen_emit_byte(ctx, '^');
+		namepath++;
+	}
+
+	for (i = 0, dotcount = 0; namepath[i]; i++) {
+		if (namepath[i] == '.') {
+			dotcount++;
+			dotpos = i;
+		}
+	}
+
+	/* If we have only \\ or only ^* then we need to add a null name */
+	if (!*namepath)
+		acpigen_emit_byte(ctx, ZERO_OP);
+	else if (dotcount == 0)
+		acpigen_emit_simple_namestring(ctx, namepath);
+	else if (dotcount == 1)
+		acpigen_emit_double_namestring(ctx, namepath, dotpos);
+	else
+		acpigen_emit_multi_namestring(ctx, namepath);
+}
+
+void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath)
+{
+	acpigen_emit_byte(ctx, NAME_OP);
+	acpigen_emit_namestring(ctx, namepath);
+}
+
+void acpigen_write_scope(struct acpi_ctx *ctx, const char *scope)
+{
+	acpigen_emit_byte(ctx, SCOPE_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_namestring(ctx, scope);
+}
+
+static void acpigen_write_method_internal(struct acpi_ctx *ctx,
+					  const char *name, uint flags)
+{
+	acpigen_emit_byte(ctx, METHOD_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_namestring(ctx, name);
+	acpigen_emit_byte(ctx, flags);
+}
+
+/* Method (name, nargs, NotSerialized) */
+void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs)
+{
+	acpigen_write_method_internal(ctx, name,
+				      nargs & ACPI_METHOD_NARGS_MASK);
+}
+
+/* Method (name, nargs, Serialized) */
+void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name,
+				     int nargs)
+{
+	acpigen_write_method_internal(ctx, name,
+				      (nargs & ACPI_METHOD_NARGS_MASK) |
+				      ACPI_METHOD_SERIALIZED_MASK);
+}
+
+void acpigen_write_device(struct acpi_ctx *ctx, const char *name)
+{
+	acpigen_emit_ext_op(ctx, DEVICE_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_namestring(ctx, name);
+}
+
+void acpigen_write_sta(struct acpi_ctx *ctx, uint status)
+{
+	/* Method (_STA, 0, NotSerialized) { Return (status) } */
+	acpigen_write_method(ctx, "_STA", 0);
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_write_byte(ctx, status);
+	acpigen_pop_len(ctx);
+}
+
+static void acpigen_write_register(struct acpi_ctx *ctx,
+				   const struct acpi_gen_regaddr *addr)
+{
+	/* See ACPI v6.3 section 6.4.3.7: Generic Register Descriptor */
+	acpigen_emit_byte(ctx, ACPI_DESCRIPTOR_REGISTER);
+	acpigen_emit_byte(ctx, 0x0c);		/* Register Length 7:0 */
+	acpigen_emit_byte(ctx, 0x00);		/* Register Length 15:8 */
+	acpigen_emit_byte(ctx, addr->space_id);
+	acpigen_emit_byte(ctx, addr->bit_width);
+	acpigen_emit_byte(ctx, addr->bit_offset);
+	acpigen_emit_byte(ctx, addr->access_size);
+	acpigen_emit_dword(ctx, addr->addrl);
+	acpigen_emit_dword(ctx, addr->addrh);
+}
+
+void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx)
+{
+	/*
+	 * A ResourceTemplate() is a Buffer() with a
+	 * (Byte|Word|DWord) containing the length, followed by one or more
+	 * resource items, terminated by the end tag.
+	 * (small item 0xf, len 1)
+	 */
+	acpigen_emit_byte(ctx, BUFFER_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_byte(ctx, WORD_PREFIX);
+	ctx->len_stack[ctx->ltop++] = ctx->current;
+
+	/*
+	 * Add two dummy bytes for the ACPI word (keep aligned with the
+	 * calculation in acpigen_write_resourcetemplate_footer() below)
+	 */
+	acpigen_emit_byte(ctx, 0x00);
+	acpigen_emit_byte(ctx, 0x00);
+}
+
+void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx)
+{
+	char *p = ctx->len_stack[--ctx->ltop];
+	int len;
+	/*
+	 * See ACPI v6.3 section 6.4.2.9: End Tag
+	 * 0x79 <checksum>
+	 * 0x00 is treated as a good checksum according to the spec
+	 * and is what iasl generates.
+	 */
+	acpigen_emit_byte(ctx, ACPI_END_TAG);
+	acpigen_emit_byte(ctx, 0x00);
+
+	/*
+	 * Start counting past the 2-bytes length added in
+	 * acpigen_write_resourcetemplate_header() above
+	 */
+	len = (char *)ctx->current - (p + 2);
+
+	/* patch len word */
+	p[0] = len & 0xff;
+	p[1] = (len >> 8) & 0xff;
+
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_register_resource(struct acpi_ctx *ctx,
+				     const struct acpi_gen_regaddr *addr)
+{
+	acpigen_write_resourcetemplate_header(ctx);
+	acpigen_write_register(ctx, addr);
+	acpigen_write_resourcetemplate_footer(ctx);
+}
+
+/*
+ * ToUUID(uuid)
+ *
+ * ACPI 6.3 Section 19.6.142 table 19-438 defines a special output order for the
+ * bytes that make up a UUID Buffer object:
+ *
+ * UUID byte order for input to this function:
+ *   aabbccdd-eeff-gghh-iijj-kkllmmnnoopp
+ *
+ * UUID byte order output by this function:
+ *   ddccbbaa-ffee-hhgg-iijj-kkllmmnnoopp
+ */
+int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid)
+{
+	u8 buf[UUID_BIN_LEN];
+	int ret;
+
+	/* Parse UUID string into bytes */
+	ret = uuid_str_to_bin(uuid, buf, UUID_STR_FORMAT_GUID);
+	if (ret)
+		return log_msg_ret("bad hex", -EINVAL);
+
+	/* BufferOp */
+	acpigen_emit_byte(ctx, BUFFER_OP);
+	acpigen_write_len_f(ctx);
+
+	/* Buffer length in bytes */
+	acpigen_write_word(ctx, UUID_BIN_LEN);
+
+	/* Output UUID in expected order */
+	acpigen_emit_stream(ctx, (char *)buf, UUID_BIN_LEN);
+
+	acpigen_pop_len(ctx);
+
+	return 0;
+}
+
+void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, uint level,
+			     uint order, const char *const dev_states[],
+			     size_t dev_states_count)
+{
+	size_t i;
+
+	for (i = 0; i < dev_states_count; i++) {
+		acpigen_write_name(ctx, dev_states[i]);
+		acpigen_write_package(ctx, 1);
+		acpigen_emit_simple_namestring(ctx, name);
+		acpigen_pop_len(ctx);		/* Package */
+	}
+
+	acpigen_emit_ext_op(ctx, POWER_RES_OP);
+
+	acpigen_write_len_f(ctx);
+
+	acpigen_emit_simple_namestring(ctx, name);
+	acpigen_emit_byte(ctx, level);
+	acpigen_emit_word(ctx, order);
+}
+
+/* Sleep (ms) */
+void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms)
+{
+	acpigen_emit_ext_op(ctx, SLEEP_OP);
+	acpigen_write_integer(ctx, sleep_ms);
+}
+
+void acpigen_write_store(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, STORE_OP);
+}
+
+/* Or (arg1, arg2, res) */
+void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res)
+{
+	acpigen_emit_byte(ctx, OR_OP);
+	acpigen_emit_byte(ctx, arg1);
+	acpigen_emit_byte(ctx, arg2);
+	acpigen_emit_byte(ctx, res);
+}
+
+/* And (arg1, arg2, res) */
+void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res)
+{
+	acpigen_emit_byte(ctx, AND_OP);
+	acpigen_emit_byte(ctx, arg1);
+	acpigen_emit_byte(ctx, arg2);
+	acpigen_emit_byte(ctx, res);
+}
+
+/* Not (arg, res) */
+void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res)
+{
+	acpigen_emit_byte(ctx, NOT_OP);
+	acpigen_emit_byte(ctx, arg);
+	acpigen_emit_byte(ctx, res);
+}
+
+/* Store (str, DEBUG) */
+void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str)
+{
+	acpigen_write_store(ctx);
+	acpigen_write_string(ctx, str);
+	acpigen_emit_ext_op(ctx, DEBUG_OP);
+}
+
+/**
+ * acpigen_get_dw0_in_local5() - Generate code to put dw0 cfg0 in local5
+ *
+ * Store (\_SB.GPC0 (addr), Local5)
+ *
+ * \_SB.GPC0 is used to read cfg0 value from dw0. It is typically defined in
+ * the board's gpiolib.asl
+ *
+ * The value needs to be stored in a local variable so that it can be used in
+ * expressions in the ACPI code.
+ *
+ * @ctx: ACPI context pointer
+ * @dw0_read: Name to use to read dw0, e.g. "\\_SB.GPC0"
+ * @addr: GPIO pin configuration register address
+ *
+ */
+static void acpigen_get_dw0_in_local5(struct acpi_ctx *ctx,
+				      const char *dw0_read, ulong addr)
+{
+	acpigen_write_store(ctx);
+	acpigen_emit_namestring(ctx, dw0_read);
+	acpigen_write_integer(ctx, addr);
+	acpigen_emit_byte(ctx, LOCAL5_OP);
+}
+
+/**
+ * acpigen_set_gpio_val() - Emit code to set value of TX GPIO to on/off
+ *
+ * @ctx: ACPI context pointer
+ * @dw0_read: Method name to use to read dw0, e.g. "\\_SB.GPC0"
+ * @dw0_write: Method name to use to read dw0, e.g. "\\_SB.SPC0"
+ * @gpio_num: GPIO number to adjust
+ * @vaL: true to set on, false to set off
+ */
+static int acpigen_set_gpio_val(struct acpi_ctx *ctx, u32 tx_state_val,
+				const char *dw0_read, const char *dw0_write,
+				struct acpi_gpio *gpio, bool val)
+{
+	acpigen_get_dw0_in_local5(ctx, dw0_read, gpio->pin0_addr);
+
+	/* Store (0x40, Local0) */
+	acpigen_write_store(ctx);
+	acpigen_write_integer(ctx, tx_state_val);
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+
+	if (val) {
+		/* Or (Local5, PAD_CFG0_TX_STATE, Local5) */
+		acpigen_write_or(ctx, LOCAL5_OP, LOCAL0_OP, LOCAL5_OP);
+	} else {
+		/* Not (PAD_CFG0_TX_STATE, Local6) */
+		acpigen_write_not(ctx, LOCAL0_OP, LOCAL6_OP);
+
+		/* And (Local5, Local6, Local5) */
+		acpigen_write_and(ctx, LOCAL5_OP, LOCAL6_OP, LOCAL5_OP);
+	}
+
+	/*
+	 * \_SB.SPC0 (addr, Local5)
+	 * \_SB.SPC0 is used to write cfg0 value in dw0. It is defined in
+	 * gpiolib.asl.
+	 */
+	acpigen_emit_namestring(ctx, dw0_write);
+	acpigen_write_integer(ctx, gpio->pin0_addr);
+	acpigen_emit_byte(ctx, LOCAL5_OP);
+
+	return 0;
+}
+
+int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val,
+			       const char *dw0_read, const char *dw0_write,
+			       struct acpi_gpio *gpio, bool enable)
+{
+	bool set;
+	int ret;
+
+	set = gpio->polarity == ACPI_GPIO_ACTIVE_HIGH ? enable : !enable;
+	ret = acpigen_set_gpio_val(ctx, tx_state_val, dw0_read, dw0_write, gpio,
+				   set);
+	if (ret)
+		return log_msg_ret("call", ret);
+
+	return 0;
+}

+ 53 - 6
lib/binman.c

@@ -11,32 +11,78 @@
 #include <dm.h>
 #include <log.h>
 #include <malloc.h>
+#include <mapmem.h>
 
+/**
+ * struct binman_info - Information needed by the binman library
+ *
+ * @image: Node describing the image we are running from
+ * @rom_offset: Offset from an image_pos to the memory-mapped address, or
+ *	ROM_OFFSET_NONE if the ROM is not memory-mapped. Can be positive or
+ *	negative
+ */
 struct binman_info {
 	ofnode image;
+	int rom_offset;
 };
 
+#define ROM_OFFSET_NONE		(-1)
+
 static struct binman_info *binman;
 
-int binman_entry_find(const char *name, struct binman_entry *entry)
+static int binman_entry_find_internal(ofnode node, const char *name,
+				      struct binman_entry *entry)
 {
-	ofnode node;
 	int ret;
 
-	node = ofnode_find_subnode(binman->image, name);
 	if (!ofnode_valid(node))
-		return log_msg_ret("no binman node", -ENOENT);
+		node = binman->image;
+	node = ofnode_find_subnode(node, name);
+	if (!ofnode_valid(node))
+		return log_msg_ret("node", -ENOENT);
 
 	ret = ofnode_read_u32(node, "image-pos", &entry->image_pos);
 	if (ret)
-		return log_msg_ret("bad binman node1", ret);
+		return log_msg_ret("import-pos", ret);
 	ret = ofnode_read_u32(node, "size", &entry->size);
 	if (ret)
-		return log_msg_ret("bad binman node2", ret);
+		return log_msg_ret("size", ret);
 
 	return 0;
 }
 
+int binman_entry_find(const char *name, struct binman_entry *entry)
+{
+	return binman_entry_find_internal(binman->image, name, entry);
+}
+
+int binman_entry_map(ofnode parent, const char *name, void **bufp, int *sizep)
+{
+	struct binman_entry entry;
+	int ret;
+
+	if (binman->rom_offset == ROM_OFFSET_NONE)
+		return -EPERM;
+	ret = binman_entry_find_internal(parent, name, &entry);
+	if (ret)
+		return log_msg_ret("entry", ret);
+	if (sizep)
+		*sizep = entry.size;
+	*bufp = map_sysmem(entry.image_pos + binman->rom_offset, entry.size);
+
+	return 0;
+}
+
+ofnode binman_section_find_node(const char *name)
+{
+	return ofnode_find_subnode(binman->image, name);
+}
+
+void binman_set_rom_offset(int rom_offset)
+{
+	binman->rom_offset = rom_offset;
+}
+
 int binman_init(void)
 {
 	binman = malloc(sizeof(struct binman_info));
@@ -45,6 +91,7 @@ int binman_init(void)
 	binman->image = ofnode_path("/binman");
 	if (!ofnode_valid(binman->image))
 		return log_msg_ret("binman node", -EINVAL);
+	binman->rom_offset = ROM_OFFSET_NONE;
 
 	return 0;
 }

+ 5 - 0
lib/time.c

@@ -152,6 +152,11 @@ uint64_t __weak get_timer_us(uint64_t base)
 	return tick_to_time_us(get_ticks()) - base;
 }
 
+unsigned long __weak get_timer_us_long(unsigned long base)
+{
+	return timer_get_us() - base;
+}
+
 unsigned long __weak notrace timer_get_us(void)
 {
 	return tick_to_time(get_ticks() * 1000);

+ 2 - 0
test/dm/Makefile

@@ -14,6 +14,8 @@ obj-$(CONFIG_UT_DM) += test-uclass.o
 obj-$(CONFIG_UT_DM) += core.o
 ifneq ($(CONFIG_SANDBOX),)
 obj-$(CONFIG_ACPIGEN) += acpi.o
+obj-$(CONFIG_ACPIGEN) += acpigen.o
+obj-$(CONFIG_ACPIGEN) += acpi_dp.o
 obj-$(CONFIG_SOUND) += audio.o
 obj-$(CONFIG_BLK) += blk.o
 obj-$(CONFIG_BOARD) += board.o

+ 263 - 15
test/dm/acpi.c

@@ -14,14 +14,27 @@
 #include <version.h>
 #include <tables_csum.h>
 #include <version.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
 #include <acpi/acpi_table.h>
 #include <dm/acpi.h>
 #include <dm/test.h>
 #include <test/ut.h>
+#include "acpi.h"
 
-#define ACPI_TEST_DEV_NAME	"ABCD"
 #define BUF_SIZE		4096
 
+/**
+ * struct testacpi_platdata - Platform data for the test ACPI device
+ *
+ * @no_name: true to emit an empty ACPI name from testacpi_get_name()
+ * @return_error: true to return an error instead of a name
+ */
+struct testacpi_platdata {
+	bool return_error;
+	bool no_name;
+};
+
 static int testacpi_write_tables(const struct udevice *dev,
 				 struct acpi_ctx *ctx)
 {
@@ -40,12 +53,51 @@ static int testacpi_write_tables(const struct udevice *dev,
 
 static int testacpi_get_name(const struct udevice *dev, char *out_name)
 {
-	return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME);
+	struct testacpi_platdata *plat = dev_get_platdata(dev);
+
+	if (plat->return_error)
+		return -EINVAL;
+	if (plat->no_name) {
+		*out_name = '\0';
+		return 0;
+	}
+	if (device_get_uclass_id(dev->parent) == UCLASS_TEST_ACPI)
+		return acpi_copy_name(out_name, ACPI_TEST_CHILD_NAME);
+	else
+		return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME);
+}
+
+static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+	const char *data;
+
+	data = dev_read_string(dev, "acpi-ssdt-test-data");
+	if (data) {
+		while (*data)
+			acpigen_emit_byte(ctx, *data++);
+	}
+
+	return 0;
+}
+
+static int testacpi_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+	const char *data;
+
+	data = dev_read_string(dev, "acpi-dsdt-test-data");
+	if (data) {
+		while (*data)
+			acpigen_emit_byte(ctx, *data++);
+	}
+
+	return 0;
 }
 
 struct acpi_ops testacpi_ops = {
 	.get_name	= testacpi_get_name,
 	.write_tables	= testacpi_write_tables,
+	.fill_ssdt	= testacpi_fill_ssdt,
+	.inject_dsdt	= testacpi_inject_dsdt,
 };
 
 static const struct udevice_id testacpi_ids[] = {
@@ -57,6 +109,8 @@ U_BOOT_DRIVER(testacpi_drv) = {
 	.name	= "testacpi_drv",
 	.of_match	= testacpi_ids,
 	.id	= UCLASS_TEST_ACPI,
+	.bind	= dm_scan_fdt_dev,
+	.platdata_auto_alloc_size	= sizeof(struct testacpi_platdata),
 	ACPI_OPS_PTR(&testacpi_ops)
 };
 
@@ -69,12 +123,52 @@ UCLASS_DRIVER(testacpi) = {
 static int dm_test_acpi_get_name(struct unit_test_state *uts)
 {
 	char name[ACPI_NAME_MAX];
-	struct udevice *dev;
+	struct udevice *dev, *dev2, *i2c, *spi, *serial, *timer, *sound;
+	struct udevice *pci, *root;
 
+	/* Test getting the name from the driver */
 	ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
 	ut_assertok(acpi_get_name(dev, name));
 	ut_asserteq_str(ACPI_TEST_DEV_NAME, name);
 
+	/* Test getting the name from the device tree */
+	ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+					      &dev2));
+	ut_assertok(acpi_get_name(dev2, name));
+	ut_asserteq_str("GHIJ", name);
+
+	/* Test getting the name from acpi_device_get_name() */
+	ut_assertok(uclass_first_device(UCLASS_I2C, &i2c));
+	ut_assertok(acpi_get_name(i2c, name));
+	ut_asserteq_str("I2C0", name);
+
+	ut_assertok(uclass_first_device(UCLASS_SPI, &spi));
+	ut_assertok(acpi_get_name(spi, name));
+	ut_asserteq_str("SPI0", name);
+
+	/* The uart has no sequence number, so this should fail */
+	ut_assertok(uclass_first_device(UCLASS_SERIAL, &serial));
+	ut_asserteq(-ENXIO, acpi_get_name(serial, name));
+
+	/* ACPI doesn't know about the timer */
+	ut_assertok(uclass_first_device(UCLASS_TIMER, &timer));
+	ut_asserteq(-ENOENT, acpi_get_name(timer, name));
+
+	/* May as well test the rest of the cases */
+	ut_assertok(uclass_first_device(UCLASS_SOUND, &sound));
+	ut_assertok(acpi_get_name(sound, name));
+	ut_asserteq_str("HDAS", name);
+
+	ut_assertok(uclass_first_device(UCLASS_PCI, &pci));
+	ut_assertok(acpi_get_name(pci, name));
+	ut_asserteq_str("PCI0", name);
+
+	ut_assertok(uclass_first_device(UCLASS_ROOT, &root));
+	ut_assertok(acpi_get_name(root, name));
+	ut_asserteq_str("\\_SB", name);
+
+	/* Note that we don't have tests for acpi_name_from_id() */
+
 	return 0;
 }
 DM_TEST(dm_test_acpi_get_name, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
@@ -138,6 +232,7 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts)
 	struct acpi_dmar *dmar;
 	struct acpi_ctx ctx;
 	void *buf;
+	int i;
 
 	buf = malloc(BUF_SIZE);
 	ut_assertnonnull(buf);
@@ -147,24 +242,26 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts)
 	ut_assertok(acpi_write_dev_tables(&ctx));
 
 	/*
-	 * We should have two dmar tables, one for each "denx,u-boot-acpi-test"
-	 * device
+	 * We should have three dmar tables, one for each
+	 * "denx,u-boot-acpi-test" device
 	 */
-	ut_asserteq_ptr(dmar + 2, ctx.current);
+	ut_asserteq_ptr(dmar + 3, ctx.current);
 	ut_asserteq(DMAR_INTR_REMAP, dmar->flags);
 	ut_asserteq(32 - 1, dmar->host_address_width);
 
 	ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags);
 	ut_asserteq(32 - 1, dmar[1].host_address_width);
 
-	/* Check that the pointers were added correctly */
-	ut_asserteq(map_to_sysmem(dmar), ctx.rsdt->entry[0]);
-	ut_asserteq(map_to_sysmem(dmar + 1), ctx.rsdt->entry[1]);
-	ut_asserteq(0, ctx.rsdt->entry[2]);
+	ut_asserteq(DMAR_INTR_REMAP, dmar[2].flags);
+	ut_asserteq(32 - 1, dmar[2].host_address_width);
 
-	ut_asserteq(map_to_sysmem(dmar), ctx.xsdt->entry[0]);
-	ut_asserteq(map_to_sysmem(dmar + 1), ctx.xsdt->entry[1]);
-	ut_asserteq(0, ctx.xsdt->entry[2]);
+	/* Check that the pointers were added correctly */
+	for (i = 0; i < 3; i++) {
+		ut_asserteq(map_to_sysmem(dmar + i), ctx.rsdt->entry[i]);
+		ut_asserteq(map_to_sysmem(dmar + i), ctx.xsdt->entry[i]);
+	}
+	ut_asserteq(0, ctx.rsdt->entry[3]);
+	ut_asserteq(0, ctx.xsdt->entry[3]);
 
 	return 0;
 }
@@ -268,15 +365,18 @@ static int dm_test_acpi_cmd_list(struct unit_test_state *uts)
 	addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16);
 	ut_assert_nextline("RSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
 			   addr, sizeof(struct acpi_table_header) +
-			   2 * sizeof(u32), U_BOOT_BUILD_DATE);
+			   3 * sizeof(u32), U_BOOT_BUILD_DATE);
 	addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16);
 	ut_assert_nextline("XSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
 			   addr, sizeof(struct acpi_table_header) +
-			   2 * sizeof(u64), U_BOOT_BUILD_DATE);
+			   3 * sizeof(u64), U_BOOT_BUILD_DATE);
 	addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64);
 	ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
 			   addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
 	addr = ALIGN(addr + sizeof(struct acpi_dmar), 16);
+	ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
+			   addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
+	addr = ALIGN(addr + sizeof(struct acpi_dmar), 16);
 	ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)",
 			   addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE);
 	ut_assert_console_end();
@@ -315,3 +415,151 @@ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_acpi_cmd_dump, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test acpi_device_path() */
+static int dm_test_acpi_device_path(struct unit_test_state *uts)
+{
+	struct testacpi_platdata *plat;
+	char buf[ACPI_PATH_MAX];
+	struct udevice *dev, *child;
+
+	ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
+	ut_assertok(acpi_device_path(dev, buf, sizeof(buf)));
+	ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME, buf);
+
+	/* Test running out of space */
+	buf[5] = '\0';
+	ut_asserteq(-ENOSPC, acpi_device_path(dev, buf, 5));
+	ut_asserteq('\0', buf[5]);
+
+	/* Test a three-component name */
+	ut_assertok(device_first_child_err(dev, &child));
+	ut_assertok(acpi_device_path(child, buf, sizeof(buf)));
+	ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME "." ACPI_TEST_CHILD_NAME,
+			buf);
+
+	/* Test handling of a device which doesn't produce a name */
+	plat = dev_get_platdata(dev);
+	plat->no_name = true;
+	ut_assertok(acpi_device_path(child, buf, sizeof(buf)));
+	ut_asserteq_str("\\_SB." ACPI_TEST_CHILD_NAME, buf);
+
+	/* Test handling of a device which returns an error */
+	plat = dev_get_platdata(dev);
+	plat->return_error = true;
+	ut_asserteq(-EINVAL, acpi_device_path(child, buf, sizeof(buf)));
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_device_path, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test acpi_device_status() */
+static int dm_test_acpi_device_status(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+
+	ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
+	ut_asserteq(ACPI_DSTATUS_ALL_ON, acpi_device_status(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_device_status, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test acpi_fill_ssdt() */
+static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts)
+{
+	struct acpi_ctx ctx;
+	u8 *buf;
+
+	buf = malloc(BUF_SIZE);
+	ut_assertnonnull(buf);
+
+	ctx.current = buf;
+	buf[4] = 'z';	/* sentinel */
+	ut_assertok(acpi_fill_ssdt(&ctx));
+
+	/*
+	 * These values come from acpi-test2's acpi-ssdt-test-data property.
+	 * This device comes first because of u-boot,acpi-ssdt-order
+	 */
+	ut_asserteq('c', buf[0]);
+	ut_asserteq('d', buf[1]);
+
+	/* These values come from acpi-test's acpi-ssdt-test-data property */
+	ut_asserteq('a', buf[2]);
+	ut_asserteq('b', buf[3]);
+
+	ut_asserteq('z', buf[4]);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_fill_ssdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test acpi_inject_dsdt() */
+static int dm_test_acpi_inject_dsdt(struct unit_test_state *uts)
+{
+	struct acpi_ctx ctx;
+	u8 *buf;
+
+	buf = malloc(BUF_SIZE);
+	ut_assertnonnull(buf);
+
+	ctx.current = buf;
+	buf[4] = 'z';	/* sentinel */
+	ut_assertok(acpi_inject_dsdt(&ctx));
+
+	/*
+	 * These values come from acpi-test's acpi-dsdt-test-data property.
+	 * There is no u-boot,acpi-dsdt-order so device-tree order is used.
+	 */
+	ut_asserteq('h', buf[0]);
+	ut_asserteq('i', buf[1]);
+
+	/* These values come from acpi-test's acpi-dsdt-test-data property */
+	ut_asserteq('j', buf[2]);
+	ut_asserteq('k', buf[3]);
+
+	ut_asserteq('z', buf[4]);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_inject_dsdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test 'acpi items' command */
+static int dm_test_acpi_cmd_items(struct unit_test_state *uts)
+{
+	struct acpi_ctx ctx;
+	void *buf;
+
+	buf = malloc(BUF_SIZE);
+	ut_assertnonnull(buf);
+
+	ctx.current = buf;
+	ut_assertok(acpi_fill_ssdt(&ctx));
+	console_record_reset();
+	run_command("acpi items", 0);
+	ut_assert_nextline("dev 'acpi-test', type 1, size 2");
+	ut_assert_nextline("dev 'acpi-test2', type 1, size 2");
+	ut_assert_console_end();
+
+	ctx.current = buf;
+	ut_assertok(acpi_inject_dsdt(&ctx));
+	console_record_reset();
+	run_command("acpi items", 0);
+	ut_assert_nextline("dev 'acpi-test', type 2, size 2");
+	ut_assert_nextline("dev 'acpi-test2', type 2, size 2");
+	ut_assert_console_end();
+
+	console_record_reset();
+	run_command("acpi items -d", 0);
+	ut_assert_nextline("dev 'acpi-test', type 2, size 2");
+	ut_assert_nextlines_are_dump(2);
+	ut_assert_nextline("%s", "");
+	ut_assert_nextline("dev 'acpi-test2', type 2, size 2");
+	ut_assert_nextlines_are_dump(2);
+	ut_assert_nextline("%s", "");
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_cmd_items, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

+ 32 - 0
test/dm/acpi.h

@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Common functions for ACPI tests
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __TEST_DM_ACPI_H
+#define __TEST_DM_ACPI_H
+
+#define ACPI_TEST_DEV_NAME	"ABCD"
+#define ACPI_TEST_CHILD_NAME	"EFGH"
+
+/**
+ * acpi_test_alloc_context_size() - Allocate an ACPI context of a given size
+ *
+ * @ctxp: Returns allocated context
+ * @size: Size to allocate in bytes
+ * @return 0 if OK, -ENOMEM if out of memory
+ */
+int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size);
+
+/**
+ * acpi_test_get_length() - decode a three-byte length field
+ *
+ * @ptr: Length encoded as per ACPI
+ * @return decoded length, or -EINVAL on error
+ */
+int acpi_test_get_length(u8 *ptr);
+
+#endif /*__TEST_DM_ACPI_H */

+ 492 - 0
test/dm/acpi_dp.c

@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for ACPI code generation via a device-property table
+ *
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <uuid.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_dp.h>
+#include <asm/unaligned.h>
+#include <dm/acpi.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include "acpi.h"
+
+/* Maximum size of the ACPI context needed for most tests */
+#define ACPI_CONTEXT_SIZE	500
+
+#define TEST_INT8	0x7d
+#define TEST_INT16	0x2345
+#define TEST_INT32	0x12345678
+#define TEST_INT64	0x4567890123456
+#define TEST_STR	"testing acpi strings"
+#define TEST_REF	"\\SB.I2C0.TPM2"
+#define EXPECT_REF	"SB__I2C0TPM2"
+
+static int alloc_context(struct acpi_ctx **ctxp)
+{
+	return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE);
+
+	return 0;
+}
+
+static void free_context(struct acpi_ctx **ctxp)
+{
+	free(*ctxp);
+	*ctxp = NULL;
+}
+
+/* Test emitting an empty table */
+static int dm_test_acpi_dp_new_table(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(10, acpigen_get_current(ctx) - ptr);
+	ut_asserteq(NAME_OP, *(u8 *)ptr);
+	ut_asserteq_strn("FRED", (char *)ptr + 1);
+	ut_asserteq(PACKAGE_OP, ptr[5]);
+	ut_asserteq(4, acpi_test_get_length(ptr + 6));
+	ut_asserteq(0, ptr[9]);
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_new_table, 0);
+
+/* Test emitting an integer */
+static int dm_test_acpi_dp_int(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	char uuid[UUID_STR_LEN + 1];
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT32));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(54, acpigen_get_current(ctx) - ptr);
+	ut_asserteq(NAME_OP, *(u8 *)ptr);
+	ut_asserteq_strn("FRED", (char *)ptr + 1);
+	ut_asserteq(PACKAGE_OP, ptr[5]);
+	ut_asserteq(48, acpi_test_get_length(ptr + 6));
+	ut_asserteq(2, ptr[9]);
+
+	/* UUID */
+	ut_asserteq(BUFFER_OP, ptr[10]);
+	ut_asserteq(22, acpi_test_get_length(ptr + 11));
+	ut_asserteq(WORD_PREFIX, ptr[14]);
+	ut_asserteq(16, get_unaligned((u16 *)(ptr + 15)));
+	uuid_bin_to_str(ptr + 17, uuid, 1);
+	ut_asserteq_str(ACPI_DP_UUID, uuid);
+
+	/* Container package */
+	ut_asserteq(PACKAGE_OP, ptr[33]);
+	ut_asserteq(20, acpi_test_get_length(ptr + 34));
+	ut_asserteq(1, ptr[37]);
+
+	/* Package with name and (integer) value */
+	ut_asserteq(PACKAGE_OP, ptr[38]);
+	ut_asserteq(15, acpi_test_get_length(ptr + 39));
+	ut_asserteq(2, ptr[42]);
+	ut_asserteq(STRING_PREFIX, ptr[43]);
+	ut_asserteq_str("MARY", (char *)ptr + 44);
+
+	ut_asserteq(DWORD_PREFIX, ptr[49]);
+	ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 50)));
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_int, 0);
+
+/* Test emitting a 64-bit integer */
+static int dm_test_acpi_dp_int64(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT64));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(58, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(QWORD_PREFIX, ptr[49]);
+	ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 50)));
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_int64, 0);
+
+/* Test emitting a 16-bit integer */
+static int dm_test_acpi_dp_int16(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT16));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(52, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(WORD_PREFIX, ptr[49]);
+	ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 50)));
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_int16, 0);
+
+/* Test emitting a 8-bit integer */
+static int dm_test_acpi_dp_int8(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT8));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(51, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(BYTE_PREFIX, ptr[49]);
+	ut_asserteq(TEST_INT8, ptr[50]);
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_int8, 0);
+
+/* Test emitting multiple values */
+static int dm_test_acpi_dp_multiple(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	ut_assertnonnull(acpi_dp_add_integer(dp, "int16", TEST_INT16));
+	ut_assertnonnull(acpi_dp_add_string(dp, "str", TEST_STR));
+	ut_assertnonnull(acpi_dp_add_reference(dp, "ref", TEST_REF));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(110, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(WORD_PREFIX, ptr[0x32]);
+	ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x33)));
+	ut_asserteq(STRING_PREFIX, ptr[0x3f]);
+	ut_asserteq_str(TEST_STR, (char *)ptr + 0x40);
+	ut_asserteq(ROOT_PREFIX, ptr[0x5f]);
+	ut_asserteq(MULTI_NAME_PREFIX, ptr[0x60]);
+	ut_asserteq(3, ptr[0x61]);
+	ut_asserteq_strn(EXPECT_REF, (char *)ptr + 0x62);
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_multiple, 0);
+
+/* Test emitting an array */
+static int dm_test_acpi_dp_array(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u64 speed[4];
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+	speed[0] = TEST_INT8;
+	speed[1] = TEST_INT16;
+	speed[2] = TEST_INT32;
+	speed[3] = TEST_INT64;
+	ut_assertnonnull(acpi_dp_add_integer_array(dp, "speeds", speed,
+						   ARRAY_SIZE(speed)));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(75, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(BYTE_PREFIX, ptr[0x38]);
+	ut_asserteq(TEST_INT8, ptr[0x39]);
+
+	ut_asserteq(WORD_PREFIX, ptr[0x3a]);
+	ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x3b)));
+
+	ut_asserteq(DWORD_PREFIX, ptr[0x3d]);
+	ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 0x3e)));
+
+	ut_asserteq(QWORD_PREFIX, ptr[0x42]);
+	ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 0x43)));
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_array, 0);
+
+/* Test emitting a child */
+static int dm_test_acpi_dp_child(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp, *child1, *child2;
+	char uuid[UUID_STR_LEN + 1];
+	u8 *ptr, *pptr;
+	int i;
+
+	ut_assertok(alloc_context(&ctx));
+
+	child1 = acpi_dp_new_table("child");
+	ut_assertnonnull(child1);
+	ut_assertnonnull(acpi_dp_add_integer(child1, "height", TEST_INT16));
+
+	child2 = acpi_dp_new_table("child");
+	ut_assertnonnull(child2);
+	ut_assertnonnull(acpi_dp_add_integer(child2, "age", TEST_INT8));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+
+	ut_assertnonnull(acpi_dp_add_child(dp, "anna", child1));
+	ut_assertnonnull(acpi_dp_add_child(dp, "john", child2));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(178, acpigen_get_current(ctx) - ptr);
+
+	/* UUID for child extension using Hierarchical Data Extension UUID */
+	ut_asserteq(BUFFER_OP, ptr[10]);
+	ut_asserteq(22, acpi_test_get_length(ptr + 11));
+	ut_asserteq(WORD_PREFIX, ptr[14]);
+	ut_asserteq(16, get_unaligned((u16 *)(ptr + 15)));
+	uuid_bin_to_str(ptr + 17, uuid, 1);
+	ut_asserteq_str(ACPI_DP_CHILD_UUID, uuid);
+
+	/* Package with two children */
+	ut_asserteq(PACKAGE_OP, ptr[0x21]);
+	ut_asserteq(0x28, acpi_test_get_length(ptr + 0x22));
+	ut_asserteq(2, ptr[0x25]);
+
+	/* First we expect the two children as string/value */
+	pptr = ptr + 0x26;
+	for (i = 0; i < 2; i++) {
+		ut_asserteq(PACKAGE_OP, pptr[0]);
+		ut_asserteq(0x11, acpi_test_get_length(pptr + 1));
+		ut_asserteq(2, pptr[4]);
+		ut_asserteq(STRING_PREFIX, pptr[5]);
+		ut_asserteq_str(i ? "john" : "anna", (char *)pptr + 6);
+		ut_asserteq(STRING_PREFIX, pptr[11]);
+		ut_asserteq_str("child", (char *)pptr + 12);
+		pptr += 0x12;
+	}
+
+	/* Write the two children */
+	ut_asserteq(0x4a, pptr - ptr);
+	for (i = 0; i < 2; i++) {
+		const char *prop = i ? "age" : "height";
+		const int datalen = i ? 1 : 2;
+		int len = strlen(prop) + 1;
+
+		ut_asserteq(NAME_OP, pptr[0]);
+		ut_asserteq_strn("chil", (char *)pptr + 1);
+		ut_asserteq(PACKAGE_OP, pptr[5]);
+		ut_asserteq(0x27 + len + datalen, acpi_test_get_length(pptr + 6));
+		ut_asserteq(2, pptr[9]);
+
+		/* UUID */
+		ut_asserteq(BUFFER_OP, pptr[10]);
+		ut_asserteq(22, acpi_test_get_length(pptr + 11));
+		ut_asserteq(WORD_PREFIX, pptr[14]);
+		ut_asserteq(16, get_unaligned((u16 *)(pptr + 15)));
+		uuid_bin_to_str(pptr + 17, uuid, 1);
+		ut_asserteq_str(ACPI_DP_UUID, uuid);
+		pptr += 33;
+
+		/* Containing package */
+		ut_asserteq(i ? 0xa1 : 0x6b, pptr - ptr);
+		ut_asserteq(PACKAGE_OP, pptr[0]);
+		ut_asserteq(0xb + len + datalen, acpi_test_get_length(pptr + 1));
+		ut_asserteq(1, pptr[4]);
+
+		/* Package containing the property-name string and the value */
+		pptr += 5;
+		ut_asserteq(i ? 0xa6 : 0x70, pptr - ptr);
+		ut_asserteq(PACKAGE_OP, pptr[0]);
+		ut_asserteq(6 + len + datalen, acpi_test_get_length(pptr + 1));
+		ut_asserteq(2, pptr[4]);
+
+		ut_asserteq(STRING_PREFIX, pptr[5]);
+		ut_asserteq_str(i ? "age" : "height", (char *)pptr + 6);
+		pptr += 6 + len;
+		if (i) {
+			ut_asserteq(BYTE_PREFIX, pptr[0]);
+			ut_asserteq(TEST_INT8, pptr[1]);
+		} else {
+			ut_asserteq(WORD_PREFIX, pptr[0]);
+			ut_asserteq(TEST_INT16,
+				    get_unaligned((u16 *)(pptr + 1)));
+		}
+		pptr += 1 + datalen;
+	}
+	ut_asserteq(178, pptr - ptr);
+
+	free_context(&ctx);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_child, 0);
+
+/* Test emitting a GPIO */
+static int dm_test_acpi_dp_gpio(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct acpi_dp *dp;
+	u8 *ptr, *pptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+
+	/* Try a few different parameters */
+	ut_assertnonnull(acpi_dp_add_gpio(dp, "reset", TEST_REF, 0x23, 0x24,
+					  ACPI_IRQ_ACTIVE_HIGH));
+	ut_assertnonnull(acpi_dp_add_gpio(dp, "allow", TEST_REF, 0, 0,
+					  ACPI_IRQ_ACTIVE_LOW));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(0x6e, acpigen_get_current(ctx) - ptr);
+
+	pptr = ptr + 0x2c; //0x3a;
+	ut_asserteq_str("reset", (char *)pptr);
+	ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe);
+	ut_asserteq(0x23, pptr[0x1b]);
+	ut_asserteq(0x24, pptr[0x1d]);
+	ut_asserteq(ZERO_OP, pptr[0x1e]);
+
+	pptr = ptr + 0x51;
+	ut_asserteq_str("allow", (char *)pptr);
+	ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe);
+	ut_asserteq(ZERO_OP, pptr[0x1a]);
+	ut_asserteq(ZERO_OP, pptr[0x1b]);
+	ut_asserteq(ONE_OP, pptr[0x1c]);
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_gpio, 0);
+
+/* Test copying info from the device tree to ACPI tables */
+static int dm_test_acpi_dp_copy(struct unit_test_state *uts)
+{
+	struct acpi_ctx *ctx;
+	struct udevice *dev;
+	struct acpi_dp *dp;
+	ofnode node;
+	u8 *ptr;
+
+	ut_assertok(alloc_context(&ctx));
+
+	dp = acpi_dp_new_table("FRED");
+	ut_assertnonnull(dp);
+
+	ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+	ut_asserteq_str("a-test", dev->name);
+
+	ut_assertok(acpi_dp_dev_copy_int(dev, dp, "int-value"));
+	ut_asserteq(-EINVAL, acpi_dp_dev_copy_int(dev, dp, "missing-value"));
+	ut_assertok(acpi_dp_dev_copy_int(dev, dp, "uint-value"));
+
+	ut_assertok(acpi_dp_dev_copy_str(dev, dp, "str-value"));
+	ut_asserteq(-EINVAL, acpi_dp_dev_copy_str(dev, dp, "missing-value"));
+
+	node = ofnode_path("/chosen");
+	ut_assert(ofnode_valid(node));
+	ut_assertok(acpi_dp_ofnode_copy_int(node, dp, "int-values"));
+	ut_asserteq(-EINVAL,
+		    acpi_dp_ofnode_copy_int(node, dp, "missing-value"));
+
+	ut_assertok(acpi_dp_ofnode_copy_str(node, dp, "setting"));
+	ut_asserteq(-EINVAL,
+		    acpi_dp_ofnode_copy_str(node, dp, "missing-value"));
+
+	ptr = acpigen_get_current(ctx);
+	ut_assertok(acpi_dp_write(ctx, dp));
+	ut_asserteq(0x9d, acpigen_get_current(ctx) - ptr);
+
+	ut_asserteq(STRING_PREFIX, ptr[0x2b]);
+	ut_asserteq_str("int-value", (char *)ptr + 0x2c);
+	ut_asserteq(WORD_PREFIX, ptr[0x36]);
+	ut_asserteq(1234, get_unaligned((u16 *)(ptr + 0x37)));
+
+	ut_asserteq(STRING_PREFIX, ptr[0x3e]);
+	ut_asserteq_str("uint-value", (char *)ptr + 0x3f);
+	ut_asserteq(DWORD_PREFIX, ptr[0x4a]);
+	ut_asserteq(-1234, get_unaligned((u32 *)(ptr + 0x4b)));
+
+	ut_asserteq(STRING_PREFIX, ptr[0x54]);
+	ut_asserteq_str("str-value", (char *)ptr + 0x55);
+	ut_asserteq(STRING_PREFIX, ptr[0x5f]);
+	ut_asserteq_str("test string", (char *)ptr + 0x60);
+
+	ut_asserteq(STRING_PREFIX, ptr[0x71]);
+	ut_asserteq_str("int-values", (char *)ptr + 0x72);
+	ut_asserteq(WORD_PREFIX, ptr[0x7d]);
+	ut_asserteq(0x1937, get_unaligned((u16 *)(ptr + 0x7e)));
+
+	ut_asserteq(STRING_PREFIX, ptr[0x85]);
+	ut_asserteq_str("setting", (char *)ptr + 0x86);
+	ut_asserteq(STRING_PREFIX, ptr[0x8e]);
+	ut_asserteq_str("sunrise ohoka", (char *)(ptr + 0x8f));
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_dp_copy, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

Some files were not shown because too many files changed in this diff