summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-07-17 08:04:48 -0400
committerTom Rini <trini@konsulko.com>2020-07-17 08:04:48 -0400
commit7c3cc6f106ed1ca13b0ff6eea9f8e1473240aef3 (patch)
tree8c67a8ed3ab24b1421161960103d8614cbde659a
parent42e7659db0ac7089d3a2f80ee1c3b8eb64d84706 (diff)
parentd40d2c570600396b54dece16429727ef50cfeef0 (diff)
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
-rw-r--r--arch/Kconfig1
-rw-r--r--arch/sandbox/dts/test.dts14
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/cpu/acpi_gpe.c26
-rw-r--r--arch/x86/cpu/apollolake/cpu_spl.c13
-rw-r--r--arch/x86/cpu/apollolake/fsp_m.c18
-rw-r--r--arch/x86/cpu/apollolake/fsp_s.c66
-rw-r--r--arch/x86/cpu/baytrail/acpi.c6
-rw-r--r--arch/x86/cpu/broadwell/power_state.c5
-rw-r--r--arch/x86/cpu/coreboot/coreboot.c4
-rw-r--r--arch/x86/cpu/coreboot/tables.c8
-rw-r--r--arch/x86/cpu/cpu.c64
-rw-r--r--arch/x86/cpu/efi/app.c2
-rw-r--r--arch/x86/cpu/i386/cpu.c7
-rw-r--r--arch/x86/cpu/intel_common/itss.c25
-rw-r--r--arch/x86/cpu/intel_common/p2sb.c44
-rw-r--r--arch/x86/cpu/quark/acpi.c4
-rw-r--r--arch/x86/cpu/quark/quark.c2
-rw-r--r--arch/x86/cpu/start.S1
-rw-r--r--arch/x86/cpu/tangier/acpi.c4
-rw-r--r--arch/x86/dts/chromebook_coral.dts1
-rw-r--r--arch/x86/include/asm/acpi_nhlt.h314
-rw-r--r--arch/x86/include/asm/acpi_table.h10
-rw-r--r--arch/x86/include/asm/fsp2/fsp_internal.h3
-rw-r--r--arch/x86/include/asm/global_data.h4
-rw-r--r--arch/x86/include/asm/intel_pinctrl.h19
-rw-r--r--arch/x86/include/asm/itss.h2
-rw-r--r--arch/x86/include/asm/u-boot-x86.h2
-rw-r--r--arch/x86/lib/Makefile1
-rw-r--r--arch/x86/lib/acpi_nhlt.c482
-rw-r--r--arch/x86/lib/acpi_table.c87
-rw-r--r--arch/x86/lib/coreboot_table.c6
-rw-r--r--arch/x86/lib/fsp/fsp_common.c4
-rw-r--r--arch/x86/lib/fsp/fsp_dram.c26
-rw-r--r--arch/x86/lib/fsp1/fsp_common.c16
-rw-r--r--arch/x86/lib/fsp2/fsp_dram.c7
-rw-r--r--arch/x86/lib/fsp2/fsp_meminit.c24
-rw-r--r--arch/x86/lib/fsp2/fsp_silicon_init.c1
-rw-r--r--arch/x86/lib/tables.c38
-rw-r--r--arch/x86/lib/zimage.c10
-rw-r--r--cmd/acpi.c15
-rw-r--r--configs/chromebook_coral_defconfig1
-rw-r--r--configs/sandbox_defconfig4
-rw-r--r--doc/device-tree-bindings/chosen.txt9
-rw-r--r--doc/device-tree-bindings/device.txt36
-rw-r--r--doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-m.txt4
-rw-r--r--doc/device-tree-bindings/sound/da7219.txt113
-rw-r--r--doc/device-tree-bindings/sound/max98357a.txt22
-rw-r--r--drivers/core/Kconfig2
-rw-r--r--drivers/core/acpi.c263
-rw-r--r--drivers/core/root.c13
-rw-r--r--drivers/gpio/gpio-uclass.c22
-rw-r--r--drivers/gpio/intel_gpio.c49
-rw-r--r--drivers/gpio/sandbox.c77
-rw-r--r--drivers/i2c/designware_i2c.c36
-rw-r--r--drivers/i2c/designware_i2c.h15
-rw-r--r--drivers/i2c/designware_i2c_pci.c96
-rw-r--r--drivers/i2c/i2c-uclass.c4
-rw-r--r--drivers/i2c/sandbox_i2c.c1
-rw-r--r--drivers/misc/Kconfig12
-rw-r--r--drivers/misc/irq-uclass.c20
-rw-r--r--drivers/misc/irq_sandbox.c16
-rw-r--r--drivers/misc/p2sb-uclass.c26
-rw-r--r--drivers/mmc/pci_mmc.c78
-rw-r--r--drivers/pinctrl/intel/Kconfig12
-rw-r--r--drivers/pinctrl/intel/pinctrl.c21
-rw-r--r--drivers/pinctrl/intel/pinctrl_apl.c4
-rw-r--r--drivers/power/acpi_pmc/acpi-pmc-uclass.c9
-rw-r--r--drivers/rtc/sandbox_rtc.c13
-rw-r--r--drivers/sound/Kconfig18
-rw-r--r--drivers/sound/Makefile2
-rw-r--r--drivers/sound/da7219.c190
-rw-r--r--drivers/sound/max98357a.c161
-rw-r--r--drivers/spi/sandbox_spi.c1
-rw-r--r--include/acpi/acpi_device.h406
-rw-r--r--include/acpi/acpi_dp.h287
-rw-r--r--include/acpi/acpigen.h566
-rw-r--r--include/asm-generic/gpio.h27
-rw-r--r--include/binman.h30
-rw-r--r--include/dm/acpi.h119
-rw-r--r--include/dm/device.h2
-rw-r--r--include/dt-bindings/interrupt-controller/x86-irq.h14
-rw-r--r--include/dt-bindings/sound/nhlt.h24
-rw-r--r--include/irq.h43
-rw-r--r--include/p2sb.h34
-rw-r--r--include/power/acpi_pmc.h14
-rw-r--r--include/spi.h4
-rw-r--r--include/test/ut.h17
-rw-r--r--include/time.h11
-rw-r--r--lib/acpi/Makefile3
-rw-r--r--lib/acpi/acpi_device.c823
-rw-r--r--lib/acpi/acpi_dp.c402
-rw-r--r--lib/acpi/acpi_table.c1
-rw-r--r--lib/acpi/acpigen.c616
-rw-r--r--lib/binman.c59
-rw-r--r--lib/time.c5
-rw-r--r--test/dm/Makefile2
-rw-r--r--test/dm/acpi.c278
-rw-r--r--test/dm/acpi.h32
-rw-r--r--test/dm/acpi_dp.c492
-rw-r--r--test/dm/acpigen.c1099
-rw-r--r--test/dm/gpio.c62
-rw-r--r--test/dm/irq.c23
-rw-r--r--test/dm/pci.c14
-rw-r--r--tools/dtoc/dtb_platdata.py4
-rw-r--r--tools/dtoc/dtoc_test_simple.dts1
-rwxr-xr-xtools/dtoc/test_dtoc.py3
107 files changed, 7998 insertions, 256 deletions
diff --git a/arch/Kconfig b/arch/Kconfig
index a11f872938..9be02d1319 100644
--- a/arch/Kconfig
+++ b/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
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 07535a6b7d..3744a46603 100644
--- a/arch/sandbox/dts/test.dts
+++ b/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>;
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index c8eae24c07..27295ef384 100644
--- a/arch/x86/Kconfig
+++ b/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
diff --git a/arch/x86/cpu/acpi_gpe.c b/arch/x86/cpu/acpi_gpe.c
index 8aa2009bd6..70badb15a3 100644
--- a/arch/x86/cpu/acpi_gpe.c
+++ b/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[] = {
diff --git a/arch/x86/cpu/apollolake/cpu_spl.c b/arch/x86/cpu/apollolake/cpu_spl.c
index 707ceb3e64..9f32f2e27e 100644
--- a/arch/x86/cpu/apollolake/cpu_spl.c
+++ b/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;
}
diff --git a/arch/x86/cpu/apollolake/fsp_m.c b/arch/x86/cpu/apollolake/fsp_m.c
index 1301100cd5..cef937573b 100644
--- a/arch/x86/cpu/apollolake/fsp_m.c
+++ b/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;
}
/*
diff --git a/arch/x86/cpu/apollolake/fsp_s.c b/arch/x86/cpu/apollolake/fsp_s.c
index 767ddfe680..e54b0ac104 100644
--- a/arch/x86/cpu/apollolake/fsp_s.c
+++ b/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() */
diff --git a/arch/x86/cpu/baytrail/acpi.c b/arch/x86/cpu/baytrail/acpi.c
index 65f2006a0a..07757b88a3 100644
--- a/arch/x86/cpu/baytrail/acpi.c
+++ b/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
diff --git a/arch/x86/cpu/broadwell/power_state.c b/arch/x86/cpu/broadwell/power_state.c
index 99d6f72cf6..62fd2e8d2c 100644
--- a/arch/x86/cpu/broadwell/power_state.c
+++ b/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;
diff --git a/arch/x86/cpu/coreboot/coreboot.c b/arch/x86/cpu/coreboot/coreboot.c
index d44db1347b..22a93254a9 100644
--- a/arch/x86/cpu/coreboot/coreboot.c
+++ b/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;
}
diff --git a/arch/x86/cpu/coreboot/tables.c b/arch/x86/cpu/coreboot/tables.c
index a5d31d1dea..1594b4a8b2 100644
--- a/arch/x86/cpu/coreboot/tables.c
+++ b/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;
}
diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
index a814e7d7a6..98ed66e67d 100644
--- a/arch/x86/cpu/cpu.c
+++ b/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;
}
diff --git a/arch/x86/cpu/efi/app.c b/arch/x86/cpu/efi/app.c
index 10677ecbc2..f754489784 100644
--- a/arch/x86/cpu/efi/app.c
+++ b/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)
{
}
diff --git a/arch/x86/cpu/i386/cpu.c b/arch/x86/cpu/i386/cpu.c
index d27324cb4e..a6a6afec8c 100644
--- a/arch/x86/cpu/i386/cpu.c
+++ b/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;
}
diff --git a/arch/x86/cpu/intel_common/itss.c b/arch/x86/cpu/intel_common/itss.c
index 963afa8f5b..fe84ebe29f 100644
--- a/arch/x86/cpu/intel_common/itss.c
+++ b/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]);
}
diff --git a/arch/x86/cpu/intel_common/p2sb.c b/arch/x86/cpu/intel_common/p2sb.c
index ec35d04ae5..361d4c90cb 100644
--- a/arch/x86/cpu/intel_common/p2sb.c
+++ b/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,
};
diff --git a/arch/x86/cpu/quark/acpi.c b/arch/x86/cpu/quark/acpi.c
index 26cda3b337..b0406a04e9 100644
--- a/arch/x86/cpu/quark/acpi.c
+++ b/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;
}
diff --git a/arch/x86/cpu/quark/quark.c b/arch/x86/cpu/quark/quark.c
index ddad02e375..30b4711b9a 100644
--- a/arch/x86/cpu/quark/quark.c
+++ b/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;
diff --git a/arch/x86/cpu/start.S b/arch/x86/cpu/start.S
index 01524635e9..4ad515ce08 100644
--- a/arch/x86/cpu/start.S
+++ b/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()
diff --git a/arch/x86/cpu/tangier/acpi.c b/arch/x86/cpu/tangier/acpi.c
index 4ec8fdd6f8..41bd177e09 100644
--- a/arch/x86/cpu/tangier/acpi.c
+++ b/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;
}
diff --git a/arch/x86/dts/chromebook_coral.dts b/arch/x86/dts/chromebook_coral.dts
index 965d9f387d..a17a9c2800 100644
--- a/arch/x86/dts/chromebook_coral.dts
+++ b/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
diff --git a/arch/x86/include/asm/acpi_nhlt.h b/arch/x86/include/asm/acpi_nhlt.h
new file mode 100644
index 0000000000..4720321381
--- /dev/null
+++ b/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
diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h
index 928475cef4..733085c178 100644
--- a/arch/x86/include/asm/acpi_table.h
+++ b/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);
/**
diff --git a/arch/x86/include/asm/fsp2/fsp_internal.h b/arch/x86/include/asm/fsp2/fsp_internal.h
index f751fbf961..b4a4fbbd84 100644
--- a/arch/x86/include/asm/fsp2/fsp_internal.h
+++ b/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);
diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h
index 4aee2f3e8c..3e4044593c 100644
--- a/arch/x86/include/asm/global_data.h
+++ b/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
diff --git a/arch/x86/include/asm/intel_pinctrl.h b/arch/x86/include/asm/intel_pinctrl.h
index e2524b089d..00868d1725 100644
--- a/arch/x86/include/asm/intel_pinctrl.h
+++ b/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);
diff --git a/arch/x86/include/asm/itss.h b/arch/x86/include/asm/itss.h
index c75d8fe8c2..f7d3240384 100644
--- a/arch/x86/include/asm/itss.h
+++ b/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)
diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h
index bd3f44014c..d732661f6d 100644
--- a/arch/x86/include/asm/u-boot-x86.h
+++ b/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);
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 1079bf2022..1185a88c27 100644
--- a/arch/x86/lib/Makefile
+++ b/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
diff --git a/arch/x86/lib/acpi_nhlt.c b/arch/x86/lib/acpi_nhlt.c
new file mode 100644
index 0000000000..c64dd9c008
--- /dev/null
+++ b/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;
+}
diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c
index 6985ef4ba5..3a93fedfc3 100644
--- a/arch/x86/lib/acpi_table.c
+++ b/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;
diff --git a/arch/x86/lib/coreboot_table.c b/arch/x86/lib/coreboot_table.c
index 331c1b7e5a..6cd3244301 100644
--- a/arch/x86/lib/coreboot_table.c
+++ b/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;
diff --git a/arch/x86/lib/fsp/fsp_common.c b/arch/x86/lib/fsp/fsp_common.c
index cf32b3e512..ea52954725 100644
--- a/arch/x86/lib/fsp/fsp_common.c
+++ b/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
diff --git a/arch/x86/lib/fsp/fsp_dram.c b/arch/x86/lib/fsp/fsp_dram.c
index ad5a0f79ad..01d498c21e 100644
--- a/arch/x86/lib/fsp/fsp_dram.c
+++ b/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;
}
diff --git a/arch/x86/lib/fsp1/fsp_common.c b/arch/x86/lib/fsp1/fsp_common.c
index 43d32b7abe..da351cf097 100644
--- a/arch/x86/lib/fsp1/fsp_common.c
+++ b/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,
diff --git a/arch/x86/lib/fsp2/fsp_dram.c b/arch/x86/lib/fsp2/fsp_dram.c
index 1c82b81831..c9f6402e6a 100644
--- a/arch/x86/lib/fsp2/fsp_dram.c
+++ b/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));
diff --git a/arch/x86/lib/fsp2/fsp_meminit.c b/arch/x86/lib/fsp2/fsp_meminit.c
index faf9c29aef..ce0b0aff76 100644
--- a/arch/x86/lib/fsp2/fsp_meminit.c
+++ b/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)
diff --git a/arch/x86/lib/fsp2/fsp_silicon_init.c b/arch/x86/lib/fsp2/fsp_silicon_init.c
index 45c0c7d90b..0f221a864f 100644
--- a/arch/x86/lib/fsp2/fsp_silicon_init.c
+++ b/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 */
diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
index 574d331d76..7bad5dd303 100644
--- a/arch/x86/lib/tables.c
+++ b/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");
}
diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c
index 64d14e8911..d2b6002008 100644
--- a/arch/x86/lib/zimage.c
+++ b/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];
diff --git a/cmd/acpi.c b/cmd/acpi.c
index e9a9161a91..085a3a650d 100644
--- a/cmd/acpi.c
+++ b/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));
diff --git a/configs/chromebook_coral_defconfig b/configs/chromebook_coral_defconfig
index ef3a03ba5a..d06fa9aa48 100644
--- a/configs/chromebook_coral_defconfig
+++ b/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
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index d43d78df6f..6059d668af 100644
--- a/configs/sandbox_defconfig
+++ b/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
diff --git a/doc/device-tree-bindings/chosen.txt b/doc/device-tree-bindings/chosen.txt
index 395c9501e3..d4dfc05847 100644
--- a/doc/device-tree-bindings/chosen.txt
+++ b/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.
diff --git a/doc/device-tree-bindings/device.txt b/doc/device-tree-bindings/device.txt
index 27bd3978d9..2a5736c598 100644
--- a/doc/device-tree-bindings/device.txt
+++ b/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";
+ };
+ };
diff --git a/doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-m.txt b/doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-m.txt
index 647a0862d4..5311938f43 100644
--- a/doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-m.txt
+++ b/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
diff --git a/doc/device-tree-bindings/sound/da7219.txt b/doc/device-tree-bindings/sound/da7219.txt
new file mode 100644
index 0000000000..5fd8a9f1e7
--- /dev/null
+++ b/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>;
+ };
+ };
diff --git a/doc/device-tree-bindings/sound/max98357a.txt b/doc/device-tree-bindings/sound/max98357a.txt
new file mode 100644
index 0000000000..4bce14ce80
--- /dev/null
+++ b/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>;
+};
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig
index a594899f37..00d1d80dc3 100644
--- a/drivers/core/Kconfig
+++ b/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
diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c
index 8ae61575dd..cdbc2c5cf5 100644
--- a/drivers/core/acpi.c
+++ b/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;
+}
diff --git a/drivers/core/root.c b/drivers/core/root.c
index 0de5d7c70d..0726be6b79 100644
--- a/drivers/core/root.c
+++ b/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 */
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index ab17fa8a5d..9c53299b6a 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/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;
diff --git a/drivers/gpio/intel_gpio.c b/drivers/gpio/intel_gpio.c
index 711fea1b58..6a3a8c4cfa 100644
--- a/drivers/gpio/intel_gpio.c
+++ b/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[] = {
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
index b9a1d65acc..c2f80472b8 100644
--- a/drivers/gpio/sandbox.c
+++ b/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)
};
diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
index 3616e2105f..cf892c69d9 100644
--- a/drivers/i2c/designware_i2c.c
+++ b/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
diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h
index dc9a6ccb63..18acf4e841 100644
--- a/drivers/i2c/designware_i2c.h
+++ b/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_ */
diff --git a/drivers/i2c/designware_i2c_pci.c b/drivers/i2c/designware_i2c_pci.c
index bd34ec0b47..d0d869c81a 100644
--- a/drivers/i2c/designware_i2c_pci.c
+++ b/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[] = {
diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c
index 8bc69e870f..2373aa2ea4 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/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;
diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c
index f4ae2397a0..57b1c60fde 100644
--- a/drivers/i2c/sandbox_i2c.c
+++ b/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>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 6bb5bc77e9..b67e906a76 100644
--- a/drivers/misc/Kconfig
+++ b/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
diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c
index ec70866cc3..94fa233f19 100644
--- a/drivers/misc/irq-uclass.c
+++ b/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",
diff --git a/drivers/misc/irq_sandbox.c b/drivers/misc/irq_sandbox.c
index 54bc47c8d8..a2511b32fc 100644
--- a/drivers/misc/irq_sandbox.c
+++ b/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[] = {
diff --git a/drivers/misc/p2sb-uclass.c b/drivers/misc/p2sb-uclass.c
index 06b1e8d9ad..b5219df46b 100644
--- a/drivers/misc/p2sb-uclass.c
+++ b/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);
}
diff --git a/drivers/mmc/pci_mmc.c b/drivers/mmc/pci_mmc.c
index 404264a697..0c45e1b893 100644
--- a/drivers/mmc/pci_mmc.c
+++ b/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[] = {
diff --git a/drivers/pinctrl/intel/Kconfig b/drivers/pinctrl/intel/Kconfig
index e62a2e0349..1acc5dabb0 100644
--- a/drivers/pinctrl/intel/Kconfig
+++ b/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
diff --git a/drivers/pinctrl/intel/pinctrl.c b/drivers/pinctrl/intel/pinctrl.c
index ba8206350e..ba21c9dcc2 100644
--- a/drivers/pinctrl/intel/pinctrl.c
+++ b/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;
}
diff --git a/drivers/pinctrl/intel/pinctrl_apl.c b/drivers/pinctrl/intel/pinctrl_apl.c
index c14176d4a7..7624a9974f 100644
--- a/drivers/pinctrl/intel/pinctrl_apl.c
+++ b/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,
diff --git a/drivers/power/acpi_pmc/acpi-pmc-uclass.c b/drivers/power/acpi_pmc/acpi-pmc-uclass.c
index 1c79f835c6..828963d8a0 100644
--- a/drivers/power/acpi_pmc/acpi-pmc-uclass.c
+++ b/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;
diff --git a/drivers/rtc/sandbox_rtc.c b/drivers/rtc/sandbox_rtc.c
index 77065e49c7..852770a49c 100644
--- a/drivers/rtc/sandbox_rtc.c
+++ b/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)
};
diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig
index 4ebc719be2..0948d8caab 100644
--- a/drivers/sound/Kconfig
+++ b/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
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 73ed7fe53c..9b40c8012f 100644
--- a/drivers/sound/Makefile
+++ b/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
diff --git a/drivers/sound/da7219.c b/drivers/sound/da7219.c
new file mode 100644
index 0000000000..6bc1ad0036
--- /dev/null
+++ b/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)
+};
diff --git a/drivers/sound/max98357a.c b/drivers/sound/max98357a.c
new file mode 100644
index 0000000000..841bc6ef68
--- /dev/null
+++ b/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)
+};
diff --git a/drivers/spi/sandbox_spi.c b/drivers/spi/sandbox_spi.c
index 570ae285f2..755f176861 100644
--- a/drivers/spi/sandbox_spi.c
+++ b/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
diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h
new file mode 100644
index 0000000000..11461e168d
--- /dev/null
+++ b/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
diff --git a/include/acpi/acpi_dp.h b/include/acpi/acpi_dp.h
new file mode 100644
index 0000000000..0b514bce59
--- /dev/null
+++ b/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
diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h
new file mode 100644
index 0000000000..228ac9c404
--- /dev/null
+++ b/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
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index e16c2f31d9..a57dd2665c 100644
--- a/include/asm-generic/gpio.h
+++ b/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_ */
diff --git a/include/binman.h b/include/binman.h
index b462dc8542..e0b92075e2 100644
--- a/include/binman.h
+++ b/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
*
@@ -21,6 +23,26 @@ struct binman_entry {
};
/**
+ * 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
*
* This searches the binman information in the device tree for a symbol of the
@@ -34,6 +56,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
*
* This locates the binary symbol information in the device tree ready for use
diff --git a/include/dm/acpi.h b/include/dm/acpi.h
index 7563a4c60a..e8b0336f6d 100644
--- a/include/dm/acpi.h
+++ b/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
diff --git a/include/dm/device.h b/include/dm/device.h
index f5738a0cee..953706cf52 100644
--- a/include/dm/device.h
+++ b/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;
}
/**
diff --git a/include/dt-bindings/interrupt-controller/x86-irq.h b/include/dt-bindings/interrupt-controller/x86-irq.h
new file mode 100644
index 0000000000..9e0b4612e1
--- /dev/null
+++ b/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
diff --git a/include/dt-bindings/sound/nhlt.h b/include/dt-bindings/sound/nhlt.h
new file mode 100644
index 0000000000..dad69c24b4
--- /dev/null
+++ b/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 */
diff --git a/include/irq.h b/include/irq.h
index b71afe9bee..8527e4dd79 100644
--- a/include/irq.h
+++ b/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,11 +124,37 @@ 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
*
* @dev: IRQ device
@@ -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
diff --git a/include/p2sb.h b/include/p2sb.h
index 60c7f70773..93e1155dca 100644
--- a/include/p2sb.h
+++ b/include/p2sb.h
@@ -31,14 +31,37 @@ 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
*
* Reads data from a PCR device within the P2SB
@@ -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
diff --git a/include/power/acpi_pmc.h b/include/power/acpi_pmc.h
index 1f50c23f5f..5fbf745136 100644
--- a/include/power/acpi_pmc.h
+++ b/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
diff --git a/include/spi.h b/include/spi.h
index a37900b2fd..98ba9e796d 100644
--- a/include/spi.h
+++ b/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)
diff --git a/include/test/ut.h b/include/test/ut.h
index 7ddd6e8872..99bbb1230c 100644
--- a/include/test/ut.h
+++ b/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); \
diff --git a/include/time.h b/include/time.h
index e99f9c8012..3f00e68713 100644
--- a/include/time.h
+++ b/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()
*
diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile
index 660491ef71..5c2f793701 100644
--- a/lib/acpi/Makefile
+++ b/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
diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c
new file mode 100644
index 0000000000..3c75b6d962
--- /dev/null
+++ b/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;
+}
diff --git a/lib/acpi/acpi_dp.c b/lib/acpi/acpi_dp.c
new file mode 100644
index 0000000000..579cab4771
--- /dev/null
+++ b/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;
+}
diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c
index 431776666e..acc55e7fad 100644
--- a/lib/acpi/acpi_table.c
+++ b/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 */
diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c
new file mode 100644
index 0000000000..c609ef4daa
--- /dev/null
+++ b/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;
+}
diff --git a/lib/binman.c b/lib/binman.c
index fd7de24bd2..7a8ad62c4a 100644
--- a/lib/binman.c
+++ b/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;
}
diff --git a/lib/time.c b/lib/time.c
index 65db0f6cda..47f8c84327 100644
--- a/lib/time.c
+++ b/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);
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 0d1c66fa1e..b03c96da06 100644
--- a/test/dm/Makefile
+++ b/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
diff --git a/test/dm/acpi.c b/test/dm/acpi.c
index 4c46dd83a6..b94c4ba4d1 100644
--- a/test/dm/acpi.c
+++ b/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,17 +365,20 @@ 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();
return 0;
@@ -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);
diff --git a/test/dm/acpi.h b/test/dm/acpi.h
new file mode 100644
index 0000000000..535db56b51
--- /dev/null
+++ b/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 */
diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c
new file mode 100644
index 0000000000..93604b87e1
--- /dev/null
+++ b/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);
diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c
new file mode 100644
index 0000000000..14a758d08a
--- /dev/null
+++ b/test/dm/acpigen.c
@@ -0,0 +1,1099 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for ACPI code generation
+ *
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <irq.h>
+#include <malloc.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_table.h>
+#include <asm/gpio.h>
+#include <asm/unaligned.h>
+#include <dm/acpi.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/ut.h>
+#include "acpi.h"
+
+/* Maximum size of the ACPI context needed for most tests */
+#define ACPI_CONTEXT_SIZE 150
+
+#define TEST_STRING "frogmore"
+#define TEST_STRING2 "ranch"
+#define TEST_STREAM2 "\xfa\xde"
+
+#define TEST_INT8 0x7d
+#define TEST_INT16 0x2345
+#define TEST_INT32 0x12345678
+#define TEST_INT64 0x4567890123456
+
+int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size)
+{
+ struct acpi_ctx *ctx;
+
+ *ctxp = NULL;
+ ctx = malloc(sizeof(*ctx));
+ if (!ctx)
+ return -ENOMEM;
+ ctx->base = malloc(size);
+ if (!ctx->base) {
+ free(ctx);
+ return -ENOMEM;
+ }
+ ctx->ltop = 0;
+ ctx->current = ctx->base;
+ *ctxp = ctx;
+
+ return 0;
+}
+
+int acpi_test_get_length(u8 *ptr)
+{
+ if (!(*ptr & 0x80))
+ return -EINVAL;
+
+ return (*ptr & 0xf) | ptr[1] << 4 | ptr[2] << 12;
+}
+
+static int alloc_context(struct acpi_ctx **ctxp)
+{
+ return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE);
+}
+
+static void free_context(struct acpi_ctx **ctxp)
+{
+ free((*ctxp)->base);
+ free(*ctxp);
+ *ctxp = NULL;
+}
+
+/* Test emitting simple types and acpigen_get_current() */
+static int dm_test_acpi_emit_simple(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_emit_byte(ctx, 0x23);
+ ut_asserteq(1, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(0x23, *(u8 *)ptr);
+
+ acpigen_emit_word(ctx, 0x1234);
+ ut_asserteq(3, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(0x1234, get_unaligned((u16 *)(ptr + 1)));
+
+ acpigen_emit_dword(ctx, 0x87654321);
+ ut_asserteq(7, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(0x87654321, get_unaligned((u32 *)(ptr + 3)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_emit_simple, 0);
+
+/* Test emitting a stream */
+static int dm_test_acpi_emit_stream(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_emit_stream(ctx, TEST_STREAM2, 2);
+ ut_asserteq(2, acpigen_get_current(ctx) - ptr);
+ ut_asserteq((u8)TEST_STREAM2[0], ptr[0]);
+ ut_asserteq((u8)TEST_STREAM2[1], ptr[1]);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_emit_stream, 0);
+
+/* Test emitting a string */
+static int dm_test_acpi_emit_string(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_emit_string(ctx, TEST_STRING);
+ ut_asserteq(sizeof(TEST_STRING), acpigen_get_current(ctx) - ptr);
+ ut_asserteq_str(TEST_STRING, (char *)ptr);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_emit_string, 0);
+
+/* Test emitting an interrupt descriptor */
+static int dm_test_acpi_interrupt(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ struct irq irq;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_assertok(irq_get_by_index(dev, 0, &irq));
+
+ /* See a-test, property interrupts-extended in the device tree */
+ ut_asserteq(3, acpi_device_write_interrupt_irq(ctx, &irq));
+ ut_asserteq(9, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_INTERRUPT, ptr[0]);
+ ut_asserteq(6, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(0x19, ptr[3]);
+ ut_asserteq(1, ptr[4]);
+ ut_asserteq(3, get_unaligned((u32 *)(ptr + 5)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_interrupt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test emitting a GPIO descriptor */
+static int dm_test_acpi_gpio(struct unit_test_state *uts)
+{
+ struct gpio_desc desc;
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
+
+ /* This should write GPIO pin 4 (see device tree test.dts ) */
+ ut_asserteq(4, acpi_device_write_gpio_desc(ctx, &desc));
+ ut_asserteq(35, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]);
+ ut_asserteq(32, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]);
+ ut_asserteq(ACPI_GPIO_TYPE_IO, ptr[4]);
+ ut_asserteq(1, get_unaligned((u16 *)(ptr + 5)));
+ ut_asserteq(9, get_unaligned((u16 *)(ptr + 7)));
+ ut_asserteq(ACPI_GPIO_PULL_UP, ptr[9]);
+ ut_asserteq(1234, get_unaligned((u16 *)(ptr + 10)));
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 12)));
+ ut_asserteq(23, get_unaligned((u16 *)(ptr + 14)));
+ ut_asserteq(0, ptr[16]);
+ ut_asserteq(25, get_unaligned((u16 *)(ptr + 17)));
+ ut_asserteq(35, get_unaligned((u16 *)(ptr + 19)));
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 21)));
+
+ /* pin0 */
+ ut_asserteq(4, get_unaligned((u16 *)(ptr + 23)));
+
+ ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test emitting a GPIO descriptor with an interrupt */
+static int dm_test_acpi_gpio_irq(struct unit_test_state *uts)
+{
+ struct gpio_desc desc;
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0));
+
+ /* This should write GPIO pin 6 (see device tree test.dts ) */
+ ut_asserteq(6, acpi_device_write_gpio_desc(ctx, &desc));
+ ut_asserteq(35, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]);
+ ut_asserteq(32, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]);
+ ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, ptr[4]);
+ ut_asserteq(1, get_unaligned((u16 *)(ptr + 5)));
+ ut_asserteq(29, get_unaligned((u16 *)(ptr + 7)));
+ ut_asserteq(ACPI_GPIO_PULL_DOWN, ptr[9]);
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 10)));
+ ut_asserteq(4321, get_unaligned((u16 *)(ptr + 12)));
+ ut_asserteq(23, get_unaligned((u16 *)(ptr + 14)));
+ ut_asserteq(0, ptr[16]);
+ ut_asserteq(25, get_unaligned((u16 *)(ptr + 17)));
+ ut_asserteq(35, get_unaligned((u16 *)(ptr + 19)));
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 21)));
+
+ /* pin0 */
+ ut_asserteq(6, get_unaligned((u16 *)(ptr + 23)));
+
+ ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_gpio_irq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test emitting either a GPIO or interrupt descriptor */
+static int dm_test_acpi_interrupt_or_gpio(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ /* This should produce an interrupt, even though it also has a GPIO */
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_asserteq(3, acpi_device_write_interrupt_or_gpio(ctx, dev,
+ "test2-gpios"));
+ ut_asserteq(ACPI_DESCRIPTOR_INTERRUPT, ptr[0]);
+
+ /* This has no interrupt so should produce a GPIO */
+ ptr = ctx->current;
+ ut_assertok(uclass_find_first_device(UCLASS_PANEL_BACKLIGHT, &dev));
+ ut_asserteq(1, acpi_device_write_interrupt_or_gpio(ctx, dev,
+ "enable-gpios"));
+ ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]);
+
+ /* This one has neither */
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 3, &dev));
+ ut_asserteq_str("b-test", dev->name);
+ ut_asserteq(-ENOENT,
+ acpi_device_write_interrupt_or_gpio(ctx, dev,
+ "enable-gpios"));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_interrupt_or_gpio,
+ DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test emitting an I2C descriptor */
+static int dm_test_acpi_i2c(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev));
+ ut_asserteq(0x43, acpi_device_write_i2c_dev(ctx, dev));
+ ut_asserteq(28, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_SERIAL_BUS, ptr[0]);
+ ut_asserteq(25, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(ACPI_I2C_SERIAL_BUS_REVISION_ID, ptr[3]);
+ ut_asserteq(0, ptr[4]);
+ ut_asserteq(ACPI_SERIAL_BUS_TYPE_I2C, ptr[5]);
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 7)));
+ ut_asserteq(ACPI_I2C_TYPE_SPECIFIC_REVISION_ID, ptr[9]);
+ ut_asserteq(6, get_unaligned((u16 *)(ptr + 10)));
+ ut_asserteq(100000, get_unaligned((u32 *)(ptr + 12)));
+ ut_asserteq(0x43, get_unaligned((u16 *)(ptr + 16)));
+ ut_asserteq_str("\\_SB.I2C0", (char *)ptr + 18);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_i2c, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test emitting a SPI descriptor */
+static int dm_test_acpi_spi(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_first_device_err(UCLASS_SPI_FLASH, &dev));
+ ut_assertok(acpi_device_write_spi_dev(ctx, dev));
+ ut_asserteq(31, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_SERIAL_BUS, ptr[0]);
+ ut_asserteq(28, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(ACPI_SPI_SERIAL_BUS_REVISION_ID, ptr[3]);
+ ut_asserteq(0, ptr[4]);
+ ut_asserteq(ACPI_SERIAL_BUS_TYPE_SPI, ptr[5]);
+ ut_asserteq(2, ptr[6]);
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 7)));
+ ut_asserteq(ACPI_SPI_TYPE_SPECIFIC_REVISION_ID, ptr[9]);
+ ut_asserteq(9, get_unaligned((u16 *)(ptr + 10)));
+ ut_asserteq(40000000, get_unaligned((u32 *)(ptr + 12)));
+ ut_asserteq(8, ptr[16]);
+ ut_asserteq(0, ptr[17]);
+ ut_asserteq(0, ptr[18]);
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 19)));
+ ut_asserteq_str("\\_SB.SPI0", (char *)ptr + 21);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_spi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test emitting a length */
+static int dm_test_acpi_len(struct unit_test_state *uts)
+{
+ const int size = 0xc0000;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+ int i;
+
+ ut_assertok(acpi_test_alloc_context_size(&ctx, size));
+
+ ptr = acpigen_get_current(ctx);
+
+ /* Write a byte and a 3-byte length */
+ acpigen_write_len_f(ctx);
+ acpigen_emit_byte(ctx, 0x23);
+ acpigen_pop_len(ctx);
+ ut_asserteq(1 + 3, acpi_test_get_length(ptr));
+
+ /* Write 200 bytes so we need two length bytes */
+ ptr = ctx->current;
+ acpigen_write_len_f(ctx);
+ for (i = 0; i < 200; i++)
+ acpigen_emit_byte(ctx, 0x23);
+ acpigen_pop_len(ctx);
+ ut_asserteq(200 + 3, acpi_test_get_length(ptr));
+
+ /* Write 40KB so we need three length bytes */
+ ptr = ctx->current;
+ acpigen_write_len_f(ctx);
+ for (i = 0; i < 40000; i++)
+ acpigen_emit_byte(ctx, 0x23);
+ acpigen_pop_len(ctx);
+ ut_asserteq(40000 + 3, acpi_test_get_length(ptr));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_len, 0);
+
+/* Test writing a package */
+static int dm_test_acpi_package(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ char *num_elements;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ num_elements = acpigen_write_package(ctx, 3);
+ ut_asserteq_ptr(num_elements, ptr + 4);
+
+ /* For ease of testing, just emit a byte, not valid package contents */
+ acpigen_emit_byte(ctx, 0x23);
+ acpigen_pop_len(ctx);
+ ut_asserteq(PACKAGE_OP, ptr[0]);
+ ut_asserteq(5, acpi_test_get_length(ptr + 1));
+ ut_asserteq(3, ptr[4]);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_package, 0);
+
+/* Test writing an integer */
+static int dm_test_acpi_integer(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_integer(ctx, 0);
+ acpigen_write_integer(ctx, 1);
+ acpigen_write_integer(ctx, TEST_INT8);
+ acpigen_write_integer(ctx, TEST_INT16);
+ acpigen_write_integer(ctx, TEST_INT32);
+ acpigen_write_integer(ctx, TEST_INT64);
+
+ ut_asserteq(6 + 1 + 2 + 4 + 8, acpigen_get_current(ctx) - ptr);
+
+ ut_asserteq(ZERO_OP, ptr[0]);
+
+ ut_asserteq(ONE_OP, ptr[1]);
+
+ ut_asserteq(BYTE_PREFIX, ptr[2]);
+ ut_asserteq(TEST_INT8, ptr[3]);
+
+ ut_asserteq(WORD_PREFIX, ptr[4]);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 5)));
+
+ ut_asserteq(DWORD_PREFIX, ptr[7]);
+ ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 8)));
+
+ ut_asserteq(QWORD_PREFIX, ptr[12]);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 13)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_integer, 0);
+
+/* Test writing a string */
+static int dm_test_acpi_string(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_string(ctx, TEST_STRING);
+ acpigen_write_string(ctx, TEST_STRING2);
+
+ ut_asserteq(2 + sizeof(TEST_STRING) + sizeof(TEST_STRING2),
+ acpigen_get_current(ctx) - ptr);
+ ut_asserteq(STRING_PREFIX, ptr[0]);
+ ut_asserteq_str(TEST_STRING, (char *)ptr + 1);
+ ptr += 1 + sizeof(TEST_STRING);
+ ut_asserteq(STRING_PREFIX, ptr[0]);
+ ut_asserteq_str(TEST_STRING2, (char *)ptr + 1);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_string, 0);
+
+/* Test writing a name */
+static int dm_test_acpi_name(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ /*
+ * The names here are made up for testing the various cases. The
+ * grammar is in the ACPI spec 6.3 section 19.2.2
+ */
+ acpigen_write_name(ctx, "\\_SB");
+ acpigen_write_name(ctx, "\\_SB.I2C0");
+ acpigen_write_name(ctx, "\\_SB.I2C0.TPM2");
+ acpigen_write_name(ctx, "\\_SB.I2C0.TPM2.LONG");
+ acpigen_write_name(ctx, "^^^^SPI0.FLAS");
+ acpigen_write_name(ctx, "NN");
+ acpigen_write_name(ctx, "^AB.CD.D.EFG");
+ acpigen_write_name(ctx, "^^^^");
+ acpigen_write_name(ctx, "\\");
+ acpigen_write_name(ctx, "\\ABCD");
+
+ ut_asserteq(107, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq_strn("\\_SB_", (char *)ptr + 1);
+ ptr += 6;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('\\', ptr[1]);
+ ut_asserteq(DUAL_NAME_PREFIX, ptr[2]);
+ ut_asserteq_strn("_SB_I2C0", (char *)ptr + 3);
+ ptr += 11;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('\\', ptr[1]);
+ ut_asserteq(MULTI_NAME_PREFIX, ptr[2]);
+ ut_asserteq(3, ptr[3]);
+ ut_asserteq_strn("_SB_I2C0TPM2", (char *)ptr + 4);
+ ptr += 16;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('\\', ptr[1]);
+ ut_asserteq(MULTI_NAME_PREFIX, ptr[2]);
+ ut_asserteq(4, ptr[3]);
+ ut_asserteq_strn("_SB_I2C0TPM2LONG", (char *)ptr + 4);
+ ptr += 20;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('^', ptr[1]);
+ ut_asserteq('^', ptr[2]);
+ ut_asserteq('^', ptr[3]);
+ ut_asserteq('^', ptr[4]);
+ ut_asserteq(DUAL_NAME_PREFIX, ptr[5]);
+ ut_asserteq_strn("SPI0FLAS", (char *)ptr + 6);
+ ptr += 14;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq_strn("NN__", (char *)ptr + 1);
+ ptr += 5;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('^', ptr[1]);
+ ut_asserteq(MULTI_NAME_PREFIX, ptr[2]);
+ ut_asserteq(4, ptr[3]);
+ ut_asserteq_strn("AB__CD__D___EFG_", (char *)ptr + 4);
+ ptr += 20;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('^', ptr[1]);
+ ut_asserteq('^', ptr[2]);
+ ut_asserteq('^', ptr[3]);
+ ut_asserteq('^', ptr[4]);
+ ut_asserteq(ZERO_OP, ptr[5]);
+ ptr += 6;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('\\', ptr[1]);
+ ut_asserteq(ZERO_OP, ptr[2]);
+ ptr += 3;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq_strn("\\ABCD", (char *)ptr + 1);
+ ptr += 5;
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_name, 0);
+
+/* Test writing a UUID */
+static int dm_test_acpi_uuid(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(acpigen_write_uuid(ctx,
+ "dbb8e3e6-5886-4ba6-8795-1319f52a966b"));
+ ut_asserteq(23, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(BUFFER_OP, ptr[0]);
+ ut_asserteq(22, acpi_test_get_length(ptr + 1));
+ ut_asserteq(0xdbb8e3e6, get_unaligned((u32 *)(ptr + 7)));
+ ut_asserteq(0x5886, get_unaligned((u16 *)(ptr + 11)));
+ ut_asserteq(0x4ba6, get_unaligned((u16 *)(ptr + 13)));
+ ut_asserteq(0x9587, get_unaligned((u16 *)(ptr + 15)));
+ ut_asserteq(0x2af51913, get_unaligned((u32 *)(ptr + 17)));
+ ut_asserteq(0x6b96, get_unaligned((u16 *)(ptr + 21)));
+
+ /* Try a bad UUID */
+ ut_asserteq(-EINVAL,
+ acpigen_write_uuid(ctx,
+ "dbb8e3e6-5886-4ba6x8795-1319f52a966b"));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_uuid, 0);
+
+/* Test writing misc ACPI codes */
+static int dm_test_acpi_misc(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ const int flags = 3;
+ const int nargs = 4;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_sleep(ctx, TEST_INT64);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 3)));
+ ptr += 11;
+
+ acpigen_write_store(ctx);
+ ut_asserteq(STORE_OP, *ptr);
+ ptr++;
+
+ acpigen_write_debug_string(ctx, TEST_STRING);
+ ut_asserteq_str(TEST_STRING, (char *)ptr + 2);
+ ptr += 2 + sizeof(TEST_STRING);
+ ut_asserteq(EXT_OP_PREFIX, ptr[0]);
+ ut_asserteq(DEBUG_OP, ptr[1]);
+ ptr += 2;
+
+ acpigen_write_sta(ctx, flags);
+ ut_asserteq(METHOD_OP, ptr[0]);
+ ut_asserteq(11, acpi_test_get_length(ptr + 1));
+ ut_asserteq_strn("_STA", (char *)ptr + 4);
+ ut_asserteq(0, ptr[8]);
+ ut_asserteq(RETURN_OP, ptr[9]);
+ ut_asserteq(BYTE_PREFIX, ptr[10]);
+ ut_asserteq(flags, ptr[11]);
+ ptr += 12;
+
+ acpigen_write_sleep(ctx, TEST_INT16);
+ ut_asserteq(SLEEP_OP, ptr[1]);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 3)));
+ ptr += 5;
+
+ acpigen_write_method_serialized(ctx, "FRED", nargs);
+ ut_asserteq(METHOD_OP, ptr[0]);
+ ut_asserteq_strn("FRED", (char *)ptr + 4);
+ ut_asserteq(1 << 3 | nargs, ptr[8]);
+ ut_asserteq(1, ctx->ltop); /* method is unfinished */
+
+ ptr += 9;
+ acpigen_write_or(ctx, LOCAL0_OP, LOCAL1_OP, LOCAL2_OP);
+ acpigen_write_and(ctx, LOCAL3_OP, LOCAL4_OP, LOCAL5_OP);
+ acpigen_write_not(ctx, LOCAL6_OP, LOCAL7_OP);
+ ut_asserteq(OR_OP, ptr[0]);
+ ut_asserteq(LOCAL0_OP, ptr[1]);
+ ut_asserteq(LOCAL1_OP, ptr[2]);
+ ut_asserteq(LOCAL2_OP, ptr[3]);
+
+ ptr += 4;
+ ut_asserteq(AND_OP, ptr[0]);
+ ut_asserteq(LOCAL3_OP, ptr[1]);
+ ut_asserteq(LOCAL4_OP, ptr[2]);
+ ut_asserteq(LOCAL5_OP, ptr[3]);
+
+ ptr += 4;
+ ut_asserteq(NOT_OP, ptr[0]);
+ ut_asserteq(LOCAL6_OP, ptr[1]);
+ ut_asserteq(LOCAL7_OP, ptr[2]);
+ ptr += 3;
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_misc, 0);
+
+/* Test writing an ACPI power resource */
+static int dm_test_acpi_power_res(struct unit_test_state *uts)
+{
+ const char *const states[] = { "_PR0", "_PR3" };
+ const char *name = "PRIC";
+ const int level = 3;
+ const int order = 2;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ /* PowerResource (PRIC, 0, 0) */
+ acpigen_write_power_res(ctx, name, level, order, states,
+ ARRAY_SIZE(states));
+ ut_asserteq(0x28, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq_strn(states[0], (char *)ptr + 1);
+ ut_asserteq(8, acpi_test_get_length(ptr + 6));
+ ut_asserteq_strn(name, (char *)ptr + 0xa);
+
+ ut_asserteq_strn(states[1], (char *)ptr + 0xf);
+ ut_asserteq(8, acpi_test_get_length(ptr + 0x14));
+ ut_asserteq_strn(name, (char *)ptr + 0x18);
+
+ ut_asserteq(POWER_RES_OP, ptr[0x1d]);
+ ut_asserteq_strn(name, (char *)ptr + 0x21);
+ ut_asserteq(level, ptr[0x25]);
+ ut_asserteq(order, get_unaligned((u16 *)(ptr + 0x26)));
+
+ /* The length is not set - caller must use acpigen_pop_len() */
+ ut_asserteq(1, ctx->ltop);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_power_res, 0);
+
+/* Test writing ACPI code to toggle a GPIO */
+static int dm_test_acpi_gpio_toggle(struct unit_test_state *uts)
+{
+ const uint addr = 0x80012;
+ const int txbit = BIT(2);
+ struct gpio_desc desc;
+ struct acpi_gpio gpio;
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0));
+ ut_assertok(gpio_get_acpi(&desc, &gpio));
+
+ /* Spot-check the results - see sb_gpio_get_acpi() */
+ ptr = acpigen_get_current(ctx);
+ acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", "\\_SB.SPC0",
+ &gpio, true);
+ acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", "\\_SB.SPC0",
+ &gpio, false);
+
+ /* Since this GPIO is active low, we expect it to be cleared here */
+ ut_asserteq(STORE_OP, *ptr);
+ ut_asserteq_strn("_SB_GPC0", (char *)ptr + 3);
+ ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0xc)));
+ ut_asserteq(LOCAL5_OP, ptr[0x10]);
+
+ ut_asserteq(STORE_OP, ptr[0x11]);
+ ut_asserteq(BYTE_PREFIX, ptr[0x12]);
+ ut_asserteq(txbit, ptr[0x13]);
+ ut_asserteq(LOCAL0_OP, ptr[0x14]);
+
+ ut_asserteq(NOT_OP, ptr[0x15]);
+ ut_asserteq(LOCAL0_OP, ptr[0x16]);
+ ut_asserteq(LOCAL6_OP, ptr[0x17]);
+ ut_asserteq(AND_OP, ptr[0x18]);
+ ut_asserteq_strn("_SB_SPC0", (char *)ptr + 0x1e);
+ ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x27)));
+ ut_asserteq(LOCAL5_OP, ptr[0x2b]);
+
+ /* Now the second one, which should be set */
+ ut_asserteq_strn("_SB_GPC0", (char *)ptr + 0x2f);
+ ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x38)));
+ ut_asserteq(LOCAL5_OP, ptr[0x3c]);
+
+ ut_asserteq(STORE_OP, ptr[0x3d]);
+
+ ut_asserteq(OR_OP, ptr[0x41]);
+ ut_asserteq(LOCAL0_OP, ptr[0x43]);
+ ut_asserteq_strn("_SB_SPC0", (char *)ptr + 0x47);
+ ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x50)));
+ ut_asserteq(LOCAL5_OP, ptr[0x54]);
+ ut_asserteq(0x55, acpigen_get_current(ctx) - ptr);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_gpio_toggle, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test writing ACPI code to output power-sequence info */
+static int dm_test_acpi_power_seq(struct unit_test_state *uts)
+{
+ struct gpio_desc reset, enable, stop;
+ const uint addr = 0xc00dc, addr_act_low = 0x80012;
+ const int txbit = BIT(2);
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(acpi_test_alloc_context_size(&ctx, 400));
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 0, &reset, 0));
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 1, &enable, 0));
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &stop, 0));
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(acpi_device_add_power_res(ctx, txbit, "\\_SB.GPC0",
+ "\\_SB.SPC0", &reset, 2, 3,
+ &enable, 4, 5, &stop, 6, 7));
+ ut_asserteq(0x186, acpigen_get_current(ctx) - ptr);
+ ut_asserteq_strn("PRIC", (char *)ptr + 0x18);
+
+ /* First the 'ON' sequence - spot check */
+ ut_asserteq_strn("_ON_", (char *)ptr + 0x38);
+
+ /* reset set */
+ ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x49)));
+ ut_asserteq(OR_OP, ptr[0x52]);
+
+ /* enable set */
+ ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x72)));
+ ut_asserteq(OR_OP, ptr[0x7b]);
+
+ /* reset clear */
+ ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x9f)));
+ ut_asserteq(NOT_OP, ptr[0xa8]);
+
+ /* stop set (disable, active low) */
+ ut_asserteq(addr_act_low + stop.offset,
+ get_unaligned((u32 *)(ptr + 0xcf)));
+ ut_asserteq(OR_OP, ptr[0xd8]);
+
+ /* Now the 'OFF' sequence */
+ ut_asserteq_strn("_OFF", (char *)ptr + 0xf4);
+
+ /* stop clear (enable, active low) */
+ ut_asserteq(addr_act_low + stop.offset,
+ get_unaligned((u32 *)(ptr + 0x105)));
+ ut_asserteq(NOT_OP, ptr[0x10e]);
+
+ /* reset clear */
+ ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x135)));
+ ut_asserteq(OR_OP, ptr[0x13e]);
+
+ /* enable clear */
+ ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x162)));
+ ut_asserteq(NOT_OP, ptr[0x16b]);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_power_seq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test writing values */
+static int dm_test_acpi_write_values(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_zero(ctx);
+ acpigen_write_one(ctx);
+ acpigen_write_byte(ctx, TEST_INT8);
+ acpigen_write_word(ctx, TEST_INT16);
+ acpigen_write_dword(ctx, TEST_INT32);
+ acpigen_write_qword(ctx, TEST_INT64);
+
+ ut_asserteq(ZERO_OP, *ptr++);
+
+ ut_asserteq(ONE_OP, *ptr++);
+
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT8, *ptr++);
+
+ ut_asserteq(WORD_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)ptr));
+ ptr += 2;
+
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT32, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ ut_asserteq(QWORD_PREFIX, *ptr++);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)ptr));
+ ptr += 8;
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_values, 0);
+
+/* Test writing a scope */
+static int dm_test_acpi_scope(struct unit_test_state *uts)
+{
+ char buf[ACPI_PATH_MAX];
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
+ ut_assertok(acpi_device_path(dev, buf, sizeof(buf)));
+ acpigen_write_scope(ctx, buf);
+ acpigen_pop_len(ctx);
+
+ ut_asserteq(SCOPE_OP, *ptr++);
+ ut_asserteq(13, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(ROOT_PREFIX, *ptr++);
+ ut_asserteq(DUAL_NAME_PREFIX, *ptr++);
+ ut_asserteq_strn("_SB_" ACPI_TEST_DEV_NAME, (char *)ptr);
+ ptr += 8;
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_scope, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test writing a resource template */
+static int dm_test_acpi_resource_template(struct unit_test_state *uts)
+{
+ struct acpi_gen_regaddr addr;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ addr.space_id = ACPI_ADDRESS_SPACE_EC;
+ addr.bit_width = 32;
+ addr.bit_offset = 8;
+ addr.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+ addr.addrl = TEST_INT64 & 0xffffffff;
+ addr.addrh = TEST_INT64 >> 32;
+ acpigen_write_register_resource(ctx, &addr);
+
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ut_asserteq(0x17, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(WORD_PREFIX, *ptr++);
+ ut_asserteq(0x11, get_unaligned((u16 *)ptr));
+ ptr += 2;
+ ut_asserteq(ACPI_DESCRIPTOR_REGISTER, *ptr++);
+ ut_asserteq(0xc, *ptr++);
+ ut_asserteq(0, *ptr++);
+ ut_asserteq(ACPI_ADDRESS_SPACE_EC, *ptr++);
+ ut_asserteq(32, *ptr++);
+ ut_asserteq(8, *ptr++);
+ ut_asserteq(ACPI_ACCESS_SIZE_DWORD_ACCESS, *ptr++);
+ ut_asserteq(TEST_INT64 & 0xffffffff, get_unaligned((u32 *)ptr));
+ ptr += 4;
+ ut_asserteq(TEST_INT64 >> 32, get_unaligned((u32 *)ptr));
+ ptr += 4;
+ ut_asserteq(ACPI_END_TAG, *ptr++);
+ ut_asserteq(0x00, *ptr++);
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_resource_template, 0);
+
+/* Test writing a device */
+static int dm_test_acpi_device(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_device(ctx, "\\_SB." ACPI_TEST_DEV_NAME);
+ acpigen_pop_len(ctx);
+
+ ut_asserteq(EXT_OP_PREFIX, *ptr++);
+ ut_asserteq(DEVICE_OP, *ptr++);
+ ut_asserteq(0xd, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(ROOT_PREFIX, *ptr++);
+ ut_asserteq(DUAL_NAME_PREFIX, *ptr++);
+ ptr += 8;
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_device, 0);
+
+/* Test writing named values */
+static int dm_test_acpi_write_name(struct unit_test_state *uts)
+{
+ const char *name = "\\_SB." ACPI_TEST_DEV_NAME;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_name_zero(ctx, name);
+ acpigen_write_name_one(ctx, name);
+ acpigen_write_name_byte(ctx, name, TEST_INT8);
+ acpigen_write_name_word(ctx, name, TEST_INT16);
+ acpigen_write_name_dword(ctx, name, TEST_INT32);
+ acpigen_write_name_qword(ctx, name, TEST_INT64);
+ acpigen_write_name_integer(ctx, name, TEST_INT64 + 1);
+ acpigen_write_name_string(ctx, name, "baldrick");
+ acpigen_write_name_string(ctx, name, NULL);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ut_asserteq_strn("\\._SB_ABCD", (char *)ptr);
+ ptr += 10;
+ ut_asserteq(ZERO_OP, *ptr++);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(ONE_OP, *ptr++);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT8, *ptr++);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(WORD_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)ptr));
+ ptr += 2;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT32, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(QWORD_PREFIX, *ptr++);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)ptr));
+ ptr += 8;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(QWORD_PREFIX, *ptr++);
+ ut_asserteq_64(TEST_INT64 + 1, get_unaligned((u64 *)ptr));
+ ptr += 8;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(STRING_PREFIX, *ptr++);
+ ut_asserteq_str("baldrick", (char *)ptr)
+ ptr += 9;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(STRING_PREFIX, *ptr++);
+ ut_asserteq('\0', *ptr++);
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_name, 0);
diff --git a/test/dm/gpio.c b/test/dm/gpio.c
index e3be57b602..29701389fc 100644
--- a/test/dm/gpio.c
+++ b/test/dm/gpio.c
@@ -8,6 +8,7 @@
#include <dm.h>
#include <log.h>
#include <malloc.h>
+#include <acpi/acpi_device.h>
#include <dm/root.h>
#include <dm/test.h>
#include <dm/util.h>
@@ -417,3 +418,64 @@ static int dm_test_gpio_get_dir_flags(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_gpio_get_dir_flags, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of gpio_get_acpi() */
+static int dm_test_gpio_get_acpi(struct unit_test_state *uts)
+{
+ struct acpi_gpio agpio;
+ struct udevice *dev;
+ struct gpio_desc desc;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
+
+ /* See sb_gpio_get_acpi() */
+ ut_assertok(gpio_get_acpi(&desc, &agpio));
+ ut_asserteq(1, agpio.pin_count);
+ ut_asserteq(4, agpio.pins[0]);
+ ut_asserteq(ACPI_GPIO_TYPE_IO, agpio.type);
+ ut_asserteq(ACPI_GPIO_PULL_UP, agpio.pull);
+ ut_asserteq_str("\\_SB.PINC", agpio.resource);
+ ut_asserteq(0, agpio.interrupt_debounce_timeout);
+ ut_asserteq(0, agpio.irq.pin);
+ ut_asserteq(1234, agpio.output_drive_strength);
+ ut_asserteq(true, agpio.io_shared);
+ ut_asserteq(ACPI_GPIO_IO_RESTRICT_INPUT, agpio.io_restrict);
+ ut_asserteq(ACPI_GPIO_ACTIVE_HIGH, agpio.polarity);
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_get_acpi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of gpio_get_acpi() with an interrupt GPIO */
+static int dm_test_gpio_get_acpi_irq(struct unit_test_state *uts)
+{
+ struct acpi_gpio agpio;
+ struct udevice *dev;
+ struct gpio_desc desc;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0));
+
+ /* See sb_gpio_get_acpi() */
+ ut_assertok(gpio_get_acpi(&desc, &agpio));
+ ut_asserteq(1, agpio.pin_count);
+ ut_asserteq(6, agpio.pins[0]);
+ ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, agpio.type);
+ ut_asserteq(ACPI_GPIO_PULL_DOWN, agpio.pull);
+ ut_asserteq_str("\\_SB.PINC", agpio.resource);
+ ut_asserteq(4321, agpio.interrupt_debounce_timeout);
+ ut_asserteq(6, agpio.irq.pin);
+ ut_asserteq(ACPI_IRQ_ACTIVE_BOTH, agpio.irq.polarity);
+ ut_asserteq(ACPI_IRQ_SHARED, agpio.irq.shared);
+ ut_asserteq(true, agpio.irq.wake);
+ ut_asserteq(0, agpio.output_drive_strength);
+ ut_asserteq(false, agpio.io_shared);
+ ut_asserteq(0, agpio.io_restrict);
+ ut_asserteq(ACPI_GPIO_ACTIVE_LOW, agpio.polarity);
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_get_acpi_irq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/test/dm/irq.c b/test/dm/irq.c
index 192d80d7e1..51bae31b0f 100644
--- a/test/dm/irq.c
+++ b/test/dm/irq.c
@@ -8,6 +8,7 @@
#include <common.h>
#include <dm.h>
#include <irq.h>
+#include <acpi/acpi_device.h>
#include <asm/test.h>
#include <dm/test.h>
#include <test/ut.h>
@@ -75,3 +76,25 @@ static int dm_test_request(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_request, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of irq_get_acpi() */
+static int dm_test_irq_get_acpi(struct unit_test_state *uts)
+{
+ struct acpi_irq airq;
+ struct udevice *dev;
+ struct irq irq;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_assertok(irq_get_by_index(dev, 0, &irq));
+
+ /* see sandbox_get_acpi() */
+ ut_assertok(irq_get_acpi(&irq, &airq));
+ ut_asserteq(3, airq.pin);
+ ut_asserteq(ACPI_IRQ_LEVEL_TRIGGERED, airq.mode);
+ ut_asserteq(ACPI_IRQ_ACTIVE_HIGH, airq.polarity);
+ ut_asserteq(ACPI_IRQ_SHARED, airq.shared);
+ ut_asserteq(ACPI_IRQ_WAKE, airq.wake);
+
+ return 0;
+}
+DM_TEST(dm_test_irq_get_acpi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/test/dm/pci.c b/test/dm/pci.c
index fb93e4c78a..39e82b3699 100644
--- a/test/dm/pci.c
+++ b/test/dm/pci.c
@@ -339,3 +339,17 @@ static int dm_test_pci_addr_live(struct unit_test_state *uts)
}
DM_TEST(dm_test_pci_addr_live, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT |
DM_TESTF_LIVE_TREE);
+
+/* Test device_is_on_pci_bus() */
+static int dm_test_pci_on_bus(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &dev));
+ ut_asserteq(true, device_is_on_pci_bus(dev));
+ ut_asserteq(false, device_is_on_pci_bus(dev_get_parent(dev)));
+ ut_asserteq(true, device_is_on_pci_bus(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_pci_on_bus, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py
index c148c49625..8ba8f16369 100644
--- a/tools/dtoc/dtb_platdata.py
+++ b/tools/dtoc/dtb_platdata.py
@@ -104,7 +104,9 @@ def get_value(ftype, value):
elif ftype == fdt.TYPE_BYTE:
return '%#x' % tools.ToByte(value[0])
elif ftype == fdt.TYPE_STRING:
- return '"%s"' % value
+ # Handle evil ACPI backslashes by adding another backslash before them.
+ # So "\\_SB.GPO0" in the device tree effectively stays like that in C
+ return '"%s"' % value.replace('\\', '\\\\')
elif ftype == fdt.TYPE_BOOL:
return 'true'
elif ftype == fdt.TYPE_INT64:
diff --git a/tools/dtoc/dtoc_test_simple.dts b/tools/dtoc/dtoc_test_simple.dts
index 165680bd4b..11bfc4c47a 100644
--- a/tools/dtoc/dtoc_test_simple.dts
+++ b/tools/dtoc/dtoc_test_simple.dts
@@ -34,6 +34,7 @@
longbytearray = [09 0a 0b 0c];
stringval = "message2";
stringarray = "another", "multi-word", "message";
+ acpi-name = "\\_SB.GPO0";
};
spl-test3 {
diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py
index 3c8e343b1f..08b02d4843 100755
--- a/tools/dtoc/test_dtoc.py
+++ b/tools/dtoc/test_dtoc.py
@@ -72,6 +72,7 @@ class TestDtoc(unittest.TestCase):
@classmethod
def setUpClass(cls):
tools.PrepareOutputDir(None)
+ cls.maxDiff = None
@classmethod
def tearDownClass(cls):
@@ -188,6 +189,7 @@ struct dtd_sandbox_pmic_test {
\tfdt64_t\t\treg[2];
};
struct dtd_sandbox_spl_test {
+\tconst char * acpi_name;
\tbool\t\tboolval;
\tunsigned char\tbytearray[3];
\tunsigned char\tbyteval;
@@ -225,6 +227,7 @@ U_BOOT_DEVICE(spl_test) = {
};
static struct dtd_sandbox_spl_test dtv_spl_test2 = {
+\t.acpi_name\t\t= "\\\\_SB.GPO0",
\t.bytearray\t\t= {0x1, 0x23, 0x34},
\t.byteval\t\t= 0x8,
\t.intarray\t\t= {0x5, 0x0, 0x0, 0x0},