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 #include #include +#include #include +#include +#include /** * 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 #include #include +#include #include #include #include @@ -21,10 +22,11 @@ #include #include #include +#include #include #include +#include #include -#include #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; + struct udevice *dev; + int ret; - p2sb_set_hide_bit(dev, 0); - - pci_x86_read_config(dev, PCI_VENDOR_ID, &val, PCI_SIZE_16); - - 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 #include +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 #include #include +#include #include #include #include @@ -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 #include +#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 within the GPIO p2sb region + * @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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include #include #include +#include /* * 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 #include #include +#include #include #include #include @@ -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); + if (ret) { + if (ret != -ENOENT) + return log_msg_ret("Could not setup config", ret); + } else { + delay = 0; + } - debug("SDRAM init..."); + 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 +#include #include #include #include @@ -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 - 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 = ; + pci,no-autoconfig; + + n { + compatible = "intel,apl-pinctrl"; + u-boot,dm-pre-reloc; + intel,p2sb-port-id = ; + 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 = <®_audio>; + VDDMIC-supply = <®_audio>; + VDDIO-supply = <®_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 #include #include +#include +#include #include #include #include +#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 -ENOSYS; + return 0; +} + +int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen) +{ + const char *path; + int ret; + + 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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -19,12 +20,15 @@ #include #include #include +#include #include 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 #include #include +#include #include +#include #include #include #include @@ -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 #include #include +#include +#include #include +#include +#include +#include #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 #include #include +#include #include #include 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 #include #include +#include #include /** @@ -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 #include #include +#include #include #include #include -#include +#include +#include +#include +#include +#include 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 #include -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 #include #include +#include #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 +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_X86 +#include +#endif +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_X86 +#include +#endif +#include +#include + +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 #include #include +#include #include #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 +#include +#include + +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 + +/* + * 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 + +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 #include +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 + /** *struct binman_entry - information about a binman entry * @@ -20,6 +22,26 @@ struct binman_entry { u32 size; }; +/** + * binman_entry_map() - Look up the address of an entry in memory + * + * @parent: Parent binman node + * @name: Name of entry + * @bufp: Returns a pointer to the entry + * @sizep: Returns the size of the entry + * @return 0 on success, -EPERM if the ROM offset is not set, -ENOENT if the + * entry cannot be found, other error code other error + */ +int binman_entry_map(ofnode parent, const char *name, void **bufp, int *sizep); + +/** + * binman_set_rom_offset() - Set the ROM memory-map offset + * + * @rom_offset: Offset from an image_pos to the memory-mapped address. This + * tells binman that ROM image_pos x can be addressed at rom_offset + x + */ +void binman_set_rom_offset(int rom_offset); + /** * binman_entry_find() - Find a binman symbol * @@ -33,6 +55,14 @@ struct binman_entry { */ int binman_entry_find(const char *name, struct binman_entry *entry); +/** + * binman_section_find_node() - Find a binman node + * + * @name: Name of node to look for + * @return Node that was found, ofnode_null() if not found + */ +ofnode binman_section_find_node(const char *name); + /** * binman_init() - Set up the binman symbol information * 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,10 +124,36 @@ struct irq_ops { * @return 0 if OK, or a negative error code. */ int (*free)(struct irq *irq); + +#if CONFIG_IS_ENABLED(ACPIGEN) + /** + * get_acpi() - Get the ACPI info for an irq + * + * This converts a irq to an ACPI structure for adding to the ACPI + * tables. + * + * @irq: irq to convert + * @acpi_irq: Output ACPI interrupt information + * @return ACPI pin number or -ve on error + */ + int (*get_acpi)(const struct irq *irq, struct acpi_irq *acpi_irq); +#endif }; #define irq_get_ops(dev) ((struct irq_ops *)(dev)->driver->ops) +/** + * irq_is_valid() - Check if an IRQ is valid + * + * @irq: IRQ description containing device and ID, e.g. previously + * returned by irq_get_by_index() + * @return true if valid, false if not + */ +static inline bool irq_is_valid(const struct irq *irq) +{ + return irq->dev != NULL; +} + /** * irq_route_pmc_gpio_gpe() - Get the GPIO for an event * @@ -223,4 +254,16 @@ int irq_free(struct irq *irq); */ int irq_first_device_type(enum irq_dev_t type, struct udevice **devp); +/** + * irq_get_acpi() - Get the ACPI info for an irq + * + * This converts a irq to an ACPI structure for adding to the ACPI + * tables. + * + * @irq: irq to convert + * @acpi_irq: Output ACPI interrupt information + * @return ACPI pin number or -ve on error + */ +int irq_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq); + #endif diff --git a/include/p2sb.h b/include/p2sb.h index 60c7f70773..93e1155dca 100644 --- a/include/p2sb.h +++ b/include/p2sb.h @@ -31,13 +31,36 @@ struct p2sb_uc_priv { }; /** - * struct p2sb_ops - Operations for the P2SB (none at present) + * struct p2sb_ops - Operations for the P2SB */ struct p2sb_ops { + /** + * set_hide() - Set/clear the 'hide' bit on the p2sb + * + * This device can be hidden from the PCI bus if needed. This method + * can be called before the p2sb is probed. + * + * @dev: P2SB device + * @hide: true to hide the device, false to show it + * @return 0 if OK, -ve on error + */ + int (*set_hide)(struct udevice *dev, bool hide); }; #define p2sb_get_ops(dev) ((struct p2sb_ops *)(dev)->driver->ops) +/** + * p2sb_set_hide() - Set/clear the 'hide' bit on the p2sb + * + * This device can be hidden from the PCI bus if needed. This method + * can be called before the p2sb is probed. + * + * @dev: P2SB device + * @hide: true to hide the device, false to show it + * @return 0 if OK, -ve on error + */ +int p2sb_set_hide(struct udevice *dev, bool hide); + /** * pcr_read32/16/8() - Read from a PCR device * @@ -132,4 +155,13 @@ int p2sb_set_port_id(struct udevice *dev, int portid); */ int p2sb_get_port_id(struct udevice *dev); +/** + * pcr_reg_address() Convert an offset in p2sb space to an absolute address + * + * @dev: Child device (whose parent is UCLASS_P2SB) + * @offset: Offset within that child's address space + * @return pointer to that offset within the child's address space + */ +void *pcr_reg_address(struct udevice *dev, uint offset); + #endif 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 /* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * 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 +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +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 + * 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 #include #include +#include +/** + * 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 #include #include +#include +#include #include #include #include #include +#include "acpi.h" -#define ACPI_TEST_DEV_NAME "ABCD" #define BUF_SIZE 4096 +/** + * struct testacpi_platdata - Platform data for the test ACPI device + * + * @no_name: true to emit an empty ACPI name from testacpi_get_name() + * @return_error: true to return an error instead of a name + */ +struct testacpi_platdata { + bool return_error; + bool no_name; +}; + static int testacpi_write_tables(const struct udevice *dev, struct acpi_ctx *ctx) { @@ -40,12 +53,51 @@ static int testacpi_write_tables(const struct udevice *dev, static int testacpi_get_name(const struct udevice *dev, char *out_name) { - return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME); + struct testacpi_platdata *plat = dev_get_platdata(dev); + + if (plat->return_error) + return -EINVAL; + if (plat->no_name) { + *out_name = '\0'; + return 0; + } + if (device_get_uclass_id(dev->parent) == UCLASS_TEST_ACPI) + return acpi_copy_name(out_name, ACPI_TEST_CHILD_NAME); + else + return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME); +} + +static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + const char *data; + + data = dev_read_string(dev, "acpi-ssdt-test-data"); + if (data) { + while (*data) + acpigen_emit_byte(ctx, *data++); + } + + return 0; +} + +static int testacpi_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + const char *data; + + data = dev_read_string(dev, "acpi-dsdt-test-data"); + if (data) { + while (*data) + acpigen_emit_byte(ctx, *data++); + } + + return 0; } struct acpi_ops testacpi_ops = { .get_name = testacpi_get_name, .write_tables = testacpi_write_tables, + .fill_ssdt = testacpi_fill_ssdt, + .inject_dsdt = testacpi_inject_dsdt, }; static const struct udevice_id testacpi_ids[] = { @@ -57,6 +109,8 @@ U_BOOT_DRIVER(testacpi_drv) = { .name = "testacpi_drv", .of_match = testacpi_ids, .id = UCLASS_TEST_ACPI, + .bind = dm_scan_fdt_dev, + .platdata_auto_alloc_size = sizeof(struct testacpi_platdata), ACPI_OPS_PTR(&testacpi_ops) }; @@ -69,12 +123,52 @@ UCLASS_DRIVER(testacpi) = { static int dm_test_acpi_get_name(struct unit_test_state *uts) { char name[ACPI_NAME_MAX]; - struct udevice *dev; + struct udevice *dev, *dev2, *i2c, *spi, *serial, *timer, *sound; + struct udevice *pci, *root; + /* Test getting the name from the driver */ ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); ut_assertok(acpi_get_name(dev, name)); ut_asserteq_str(ACPI_TEST_DEV_NAME, name); + /* Test getting the name from the device tree */ + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev2)); + ut_assertok(acpi_get_name(dev2, name)); + ut_asserteq_str("GHIJ", name); + + /* Test getting the name from acpi_device_get_name() */ + ut_assertok(uclass_first_device(UCLASS_I2C, &i2c)); + ut_assertok(acpi_get_name(i2c, name)); + ut_asserteq_str("I2C0", name); + + ut_assertok(uclass_first_device(UCLASS_SPI, &spi)); + ut_assertok(acpi_get_name(spi, name)); + ut_asserteq_str("SPI0", name); + + /* The uart has no sequence number, so this should fail */ + ut_assertok(uclass_first_device(UCLASS_SERIAL, &serial)); + ut_asserteq(-ENXIO, acpi_get_name(serial, name)); + + /* ACPI doesn't know about the timer */ + ut_assertok(uclass_first_device(UCLASS_TIMER, &timer)); + ut_asserteq(-ENOENT, acpi_get_name(timer, name)); + + /* May as well test the rest of the cases */ + ut_assertok(uclass_first_device(UCLASS_SOUND, &sound)); + ut_assertok(acpi_get_name(sound, name)); + ut_asserteq_str("HDAS", name); + + ut_assertok(uclass_first_device(UCLASS_PCI, &pci)); + ut_assertok(acpi_get_name(pci, name)); + ut_asserteq_str("PCI0", name); + + ut_assertok(uclass_first_device(UCLASS_ROOT, &root)); + ut_assertok(acpi_get_name(root, name)); + ut_asserteq_str("\\_SB", name); + + /* Note that we don't have tests for acpi_name_from_id() */ + return 0; } DM_TEST(dm_test_acpi_get_name, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); @@ -138,6 +232,7 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) struct acpi_dmar *dmar; struct acpi_ctx ctx; void *buf; + int i; buf = malloc(BUF_SIZE); ut_assertnonnull(buf); @@ -147,24 +242,26 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) ut_assertok(acpi_write_dev_tables(&ctx)); /* - * We should have two dmar tables, one for each "denx,u-boot-acpi-test" - * device + * We should have three dmar tables, one for each + * "denx,u-boot-acpi-test" device */ - ut_asserteq_ptr(dmar + 2, ctx.current); + ut_asserteq_ptr(dmar + 3, ctx.current); ut_asserteq(DMAR_INTR_REMAP, dmar->flags); ut_asserteq(32 - 1, dmar->host_address_width); ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags); ut_asserteq(32 - 1, dmar[1].host_address_width); - /* Check that the pointers were added correctly */ - ut_asserteq(map_to_sysmem(dmar), ctx.rsdt->entry[0]); - ut_asserteq(map_to_sysmem(dmar + 1), ctx.rsdt->entry[1]); - ut_asserteq(0, ctx.rsdt->entry[2]); + ut_asserteq(DMAR_INTR_REMAP, dmar[2].flags); + ut_asserteq(32 - 1, dmar[2].host_address_width); - ut_asserteq(map_to_sysmem(dmar), ctx.xsdt->entry[0]); - ut_asserteq(map_to_sysmem(dmar + 1), ctx.xsdt->entry[1]); - ut_asserteq(0, ctx.xsdt->entry[2]); + /* Check that the pointers were added correctly */ + for (i = 0; i < 3; i++) { + ut_asserteq(map_to_sysmem(dmar + i), ctx.rsdt->entry[i]); + ut_asserteq(map_to_sysmem(dmar + i), ctx.xsdt->entry[i]); + } + ut_asserteq(0, ctx.rsdt->entry[3]); + ut_asserteq(0, ctx.xsdt->entry[3]); return 0; } @@ -268,15 +365,18 @@ static int dm_test_acpi_cmd_list(struct unit_test_state *uts) addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16); ut_assert_nextline("RSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", addr, sizeof(struct acpi_table_header) + - 2 * sizeof(u32), U_BOOT_BUILD_DATE); + 3 * sizeof(u32), U_BOOT_BUILD_DATE); addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16); ut_assert_nextline("XSDT %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", addr, sizeof(struct acpi_table_header) + - 2 * sizeof(u64), U_BOOT_BUILD_DATE); + 3 * sizeof(u64), U_BOOT_BUILD_DATE); addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64); ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); + ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", + addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); + addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); ut_assert_nextline("DMAR %08lx %06lx (v01 U-BOOT U-BOOTBL %u INTL 0)", addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); ut_assert_console_end(); @@ -315,3 +415,151 @@ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_cmd_dump, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_device_path() */ +static int dm_test_acpi_device_path(struct unit_test_state *uts) +{ + struct testacpi_platdata *plat; + char buf[ACPI_PATH_MAX]; + struct udevice *dev, *child; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); + ut_assertok(acpi_device_path(dev, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME, buf); + + /* Test running out of space */ + buf[5] = '\0'; + ut_asserteq(-ENOSPC, acpi_device_path(dev, buf, 5)); + ut_asserteq('\0', buf[5]); + + /* Test a three-component name */ + ut_assertok(device_first_child_err(dev, &child)); + ut_assertok(acpi_device_path(child, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME "." ACPI_TEST_CHILD_NAME, + buf); + + /* Test handling of a device which doesn't produce a name */ + plat = dev_get_platdata(dev); + plat->no_name = true; + ut_assertok(acpi_device_path(child, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_CHILD_NAME, buf); + + /* Test handling of a device which returns an error */ + plat = dev_get_platdata(dev); + plat->return_error = true; + ut_asserteq(-EINVAL, acpi_device_path(child, buf, sizeof(buf))); + + return 0; +} +DM_TEST(dm_test_acpi_device_path, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_device_status() */ +static int dm_test_acpi_device_status(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); + ut_asserteq(ACPI_DSTATUS_ALL_ON, acpi_device_status(dev)); + + return 0; +} +DM_TEST(dm_test_acpi_device_status, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_fill_ssdt() */ +static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + u8 *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + ctx.current = buf; + buf[4] = 'z'; /* sentinel */ + ut_assertok(acpi_fill_ssdt(&ctx)); + + /* + * These values come from acpi-test2's acpi-ssdt-test-data property. + * This device comes first because of u-boot,acpi-ssdt-order + */ + ut_asserteq('c', buf[0]); + ut_asserteq('d', buf[1]); + + /* These values come from acpi-test's acpi-ssdt-test-data property */ + ut_asserteq('a', buf[2]); + ut_asserteq('b', buf[3]); + + ut_asserteq('z', buf[4]); + + return 0; +} +DM_TEST(dm_test_acpi_fill_ssdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_inject_dsdt() */ +static int dm_test_acpi_inject_dsdt(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + u8 *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + ctx.current = buf; + buf[4] = 'z'; /* sentinel */ + ut_assertok(acpi_inject_dsdt(&ctx)); + + /* + * These values come from acpi-test's acpi-dsdt-test-data property. + * There is no u-boot,acpi-dsdt-order so device-tree order is used. + */ + ut_asserteq('h', buf[0]); + ut_asserteq('i', buf[1]); + + /* These values come from acpi-test's acpi-dsdt-test-data property */ + ut_asserteq('j', buf[2]); + ut_asserteq('k', buf[3]); + + ut_asserteq('z', buf[4]); + + return 0; +} +DM_TEST(dm_test_acpi_inject_dsdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test 'acpi items' command */ +static int dm_test_acpi_cmd_items(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + void *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + ctx.current = buf; + ut_assertok(acpi_fill_ssdt(&ctx)); + console_record_reset(); + run_command("acpi items", 0); + ut_assert_nextline("dev 'acpi-test', type 1, size 2"); + ut_assert_nextline("dev 'acpi-test2', type 1, size 2"); + ut_assert_console_end(); + + ctx.current = buf; + ut_assertok(acpi_inject_dsdt(&ctx)); + console_record_reset(); + run_command("acpi items", 0); + ut_assert_nextline("dev 'acpi-test', type 2, size 2"); + ut_assert_nextline("dev 'acpi-test2', type 2, size 2"); + ut_assert_console_end(); + + console_record_reset(); + run_command("acpi items -d", 0); + ut_assert_nextline("dev 'acpi-test', type 2, size 2"); + ut_assert_nextlines_are_dump(2); + ut_assert_nextline("%s", ""); + ut_assert_nextline("dev 'acpi-test2', type 2, size 2"); + ut_assert_nextlines_are_dump(2); + ut_assert_nextline("%s", ""); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_items, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); 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 + */ + +#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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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},