From patchwork Mon Mar 9 03:44:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243375 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:25 -0600 Subject: [PATCH v2 01/39] cpu: Support querying the address width In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.1.I094ecfe5a8a83eabccd2ac9f6dc5f6fccbdcea5f@changeid> Different CPUs may support different address widths, meaning the amount of memory they can address. Add a property for this to the cpu_info struct. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- Changes in v2: None drivers/cpu/cpu_sandbox.c | 1 + include/cpu.h | 2 ++ test/dm/cpu.c | 1 + 3 files changed, 4 insertions(+) diff --git a/drivers/cpu/cpu_sandbox.c b/drivers/cpu/cpu_sandbox.c index ff87e8adca..05b384f6a4 100644 --- a/drivers/cpu/cpu_sandbox.c +++ b/drivers/cpu/cpu_sandbox.c @@ -19,6 +19,7 @@ int cpu_sandbox_get_info(struct udevice *dev, struct cpu_info *info) { info->cpu_freq = 42 * 42 * 42 * 42 * 42; info->features = 0x42424242; + info->address_width = IS_ENABLED(CONFIG_PHYS_64BIT) ? 64 : 32; return 0; } diff --git a/include/cpu.h b/include/cpu.h index 28dd48feb8..6b1b6b37b3 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -44,10 +44,12 @@ enum { * * @cpu_freq: Current CPU frequency in Hz * @features: Flags for supported CPU features + * @address_width: Width of the CPU address space in bits (e.g. 32) */ struct cpu_info { ulong cpu_freq; ulong features; + uint address_width; }; struct cpu_ops { diff --git a/test/dm/cpu.c b/test/dm/cpu.c index f5f1caef71..e6dc576ea3 100644 --- a/test/dm/cpu.c +++ b/test/dm/cpu.c @@ -33,6 +33,7 @@ static int dm_test_cpu(struct unit_test_state *uts) ut_assertok(cpu_get_info(dev, &info)); ut_asserteq(info.cpu_freq, 42 * 42 * 42 * 42 * 42); ut_asserteq(info.features, 0x42424242); + ut_asserteq(info.address_width, 32); ut_asserteq(cpu_get_count(dev), 42); From patchwork Mon Mar 9 03:44:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243376 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:26 -0600 Subject: [PATCH v2 02/39] spi: Add SPI mode enums In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.2.I83a459a3914c56ca81dcdfa7e01d83c54f7ab914@changeid> With ACPI we need to describe the settings of the SPI bus. Add enums to handle this. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- Changes in v2: - Don't bracket the definitions with DM_SPI include/spi.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/include/spi.h b/include/spi.h index 852f570eaa..2092940f4c 100644 --- a/include/spi.h +++ b/include/spi.h @@ -66,6 +66,39 @@ struct dm_spi_slave_platdata { #endif /* CONFIG_DM_SPI */ +/** + * enum spi_clock_phase - indicates the clock phase to use for SPI (CPHA) + * + * @SPI_CLOCK_PHASE_FIRST: Data sampled on the first phase + * @SPI_CLOCK_PHASE_SECOND: Data sampled on the second phase + */ +enum spi_clock_phase { + SPI_CLOCK_PHASE_FIRST, + SPI_CLOCK_PHASE_SECOND +}; + +/** + * enum spi_wire_mode - indicates the number of wires used for SPI + * + * @SPI_4_WIRE_MODE: Normal bidirectional mode with MOSI and MISO + * @SPI_3_WIRE_MODE: Unidirectional version with a single data line SISO + */ +enum spi_wire_mode { + SPI_4_WIRE_MODE, + SPI_3_WIRE_MODE +}; + +/** + * enum spi_polarity - indicates the polarity of the SPI bus (CPOL) + * + * @SPI_POLARITY_LOW: Clock is low in idle state + * @SPI_POLARITY_HIGH: Clock is high in idle state + */ +enum spi_polarity { + SPI_POLARITY_LOW, + SPI_POLARITY_HIGH +}; + /** * struct spi_slave - Representation of a SPI slave * From patchwork Mon Mar 9 03:44:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243377 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:27 -0600 Subject: [PATCH v2 03/39] tpm: cr50: Release locality on exit In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.3.Ic7bc756f1622775532c4536a2dfd4ee783ee4a35@changeid> At present the cr50 driver claims the locality and does not release it for Linux. This causes problems. Fix this by tracking what is claimed, and adding a 'remove' method. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- Changes in v2: None drivers/tpm/cr50_i2c.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index b904a7d426..b30f55b40d 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -206,7 +206,7 @@ static int release_locality(struct udevice *dev, int force) cr50_i2c_write(dev, addr, &buf, 1); } - priv->locality = 0; + priv->locality = -1; return 0; } @@ -499,6 +499,7 @@ static int process_reset(struct udevice *dev) static int claim_locality(struct udevice *dev, int loc) { const u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY; + struct cr50_priv *priv = dev_get_priv(dev); u8 access; int ret; @@ -525,6 +526,7 @@ static int claim_locality(struct udevice *dev, int loc) return -EPERM; } log_info("Claimed locality %d\n", loc); + priv->locality = loc; return 0; } @@ -559,7 +561,11 @@ static int cr50_i2c_open(struct udevice *dev) static int cr50_i2c_cleanup(struct udevice *dev) { - release_locality(dev, 1); + struct cr50_priv *priv = dev_get_priv(dev); + + printf("%s: cleanup %d\n", __func__, priv->locality); + if (priv->locality != -1) + release_locality(dev, 1); return 0; } @@ -631,6 +637,7 @@ static int cr50_i2c_probe(struct udevice *dev) return log_msg_ret("vendor-id", -EXDEV); } priv->vendor = vendor; + priv->locality = -1; return 0; } @@ -655,5 +662,7 @@ U_BOOT_DRIVER(cr50_i2c) = { .ops = &cr50_i2c_ops, .ofdata_to_platdata = cr50_i2c_ofdata_to_platdata, .probe = cr50_i2c_probe, + .remove = cr50_i2c_cleanup, .priv_auto_alloc_size = sizeof(struct cr50_priv), + .flags = DM_FLAG_OS_PREPARE, }; From patchwork Mon Mar 9 03:44:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243378 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:28 -0600 Subject: [PATCH v2 04/39] tpm: cr50: Add a comment for cr50_priv In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.4.I405998b384e2bb25cbfb3179a8375c586bf0ce7a@changeid> Add a comment for the private structure Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- Changes in v2: - Drop the other comment change since it is already applied drivers/tpm/cr50_i2c.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index b30f55b40d..c1d2d2fa38 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -34,6 +34,15 @@ enum { CR50_MAX_BUF_SIZE = 63, }; +/** + * struct cr50_priv - Private driver data + * + * @ready_gpio: GPIO to use to check if the TPM is ready + * @irq: IRQ to use check if the TPM is ready (has priority over @ready_gpio) + * @locality: Currenttly claimed locality (-1 if none) + * @vendor: vendor: Vendor ID for TPM + * @use_irq: true to use @irq, false to use @ready if available + */ struct cr50_priv { struct gpio_desc ready_gpio; struct irq irq; From patchwork Mon Mar 9 03:44:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243379 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:29 -0600 Subject: [PATCH v2 05/39] tpm: cr50: Use the correct GPIO binding In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.5.Iffff549a1b63fedb9e8ec87cdc5e9e28478a4587@changeid> This device should use ready-gpios rather than ready-gpio. Fix it. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- Changes in v2: None arch/x86/dts/chromebook_coral.dts | 2 +- doc/device-tree-bindings/gpio/intel,apl-gpio.txt | 2 +- .../interrupt-controller/intel,acpi-gpe.txt | 2 +- drivers/tpm/cr50_i2c.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/dts/chromebook_coral.dts b/arch/x86/dts/chromebook_coral.dts index af52e11c89..d48ef3573e 100644 --- a/arch/x86/dts/chromebook_coral.dts +++ b/arch/x86/dts/chromebook_coral.dts @@ -292,7 +292,7 @@ reg = <0x50>; compatible = "google,cr50"; u-boot,i2c-offset-len = <0>; - ready-gpio = <&gpio_n 28 GPIO_ACTIVE_LOW>; + ready-gpios = <&gpio_n 28 GPIO_ACTIVE_LOW>; interrupts-extended = <&acpi_gpe 0x3c 0>; }; }; diff --git a/doc/device-tree-bindings/gpio/intel,apl-gpio.txt b/doc/device-tree-bindings/gpio/intel,apl-gpio.txt index cf0659b70e..8422ff63ab 100644 --- a/doc/device-tree-bindings/gpio/intel,apl-gpio.txt +++ b/doc/device-tree-bindings/gpio/intel,apl-gpio.txt @@ -47,7 +47,7 @@ Example: reg = <0x50>; compatible = "google,cr50"; u-boot,i2c-offset-len = <0>; - ready-gpio = <&gpio_n GPIO_28 GPIO_ACTIVE_LOW>; + ready-gpios = <&gpio_n GPIO_28 GPIO_ACTIVE_LOW>; }; }; diff --git a/doc/device-tree-bindings/interrupt-controller/intel,acpi-gpe.txt b/doc/device-tree-bindings/interrupt-controller/intel,acpi-gpe.txt index d9252bf29f..2fe02d8a22 100644 --- a/doc/device-tree-bindings/interrupt-controller/intel,acpi-gpe.txt +++ b/doc/device-tree-bindings/interrupt-controller/intel,acpi-gpe.txt @@ -25,6 +25,6 @@ Example: tpm at 50 { reg = <0x50>; compatible = "google,cr50"; - ready-gpio = <&gpio_n 0x1c GPIO_ACTIVE_LOW>; + ready-gpios = <&gpio_n 0x1c GPIO_ACTIVE_LOW>; interrupts-extended = <&acpi_gpe 0x3c 0>; }; diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c index c1d2d2fa38..b67051af26 100644 --- a/drivers/tpm/cr50_i2c.c +++ b/drivers/tpm/cr50_i2c.c @@ -607,7 +607,7 @@ static int cr50_i2c_ofdata_to_platdata(struct udevice *dev) priv->irq = irq; priv->use_irq = true; } else { - ret = gpio_request_by_name(dev, "ready-gpio", 0, + ret = gpio_request_by_name(dev, "ready-gpios", 0, &priv->ready_gpio, GPIOD_IS_IN); if (ret) { log_warning("Cr50 does not have an ready GPIO/interrupt (err=%d)\n", From patchwork Mon Mar 9 03:44:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243380 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:30 -0600 Subject: [PATCH v2 06/39] tpm: Don't cleanup unless an error happens In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.6.Ic7e63c1f960c3adab3a1c03a61a56c4199fc7074@changeid> At present the cleanup() method is called on every transfer. It should only be called on failing transfers. Fix this and tidy up the error handling a little. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- Changes in v2: None drivers/tpm/tpm-uclass.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/tpm/tpm-uclass.c b/drivers/tpm/tpm-uclass.c index 1b11c93194..71d5807006 100644 --- a/drivers/tpm/tpm-uclass.c +++ b/drivers/tpm/tpm-uclass.c @@ -72,7 +72,7 @@ int tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, size_t send_size, struct tpm_ops *ops = tpm_get_ops(dev); ulong start, stop; uint count, ordinal; - int ret, ret2; + int ret, ret2 = 0; if (ops->xfer) return ops->xfer(dev, sendbuf, send_size, recvbuf, recv_size); @@ -120,9 +120,16 @@ int tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, size_t send_size, } } while (ret); - ret2 = ops->cleanup ? ops->cleanup(dev) : 0; + if (ret) { + if (ops->cleanup) { + ret2 = ops->cleanup(dev); + if (ret2) + return log_msg_ret("cleanup", ret2); + } + return log_msg_ret("xfer", ret); + } - return ret2 ? ret2 : ret; + return 0; } UCLASS_DRIVER(tpm) = { From patchwork Mon Mar 9 03:44:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243381 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:31 -0600 Subject: [PATCH v2 07/39] dm: pci: Allow disabling auto-config for a device In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.7.I66a7bcb7db931e196e8319d2dbca4e418274f39b@changeid> Add a means to avoid configuring a device when needed. Add an explanation of why this is useful to the binding file. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- Changes in v2: None doc/device-tree-bindings/pci/x86-pci.txt | 24 ++++++++++++++++++++++++ drivers/pci/pci-uclass.c | 2 ++ 2 files changed, 26 insertions(+) diff --git a/doc/device-tree-bindings/pci/x86-pci.txt b/doc/device-tree-bindings/pci/x86-pci.txt index 3aa5bd9a46..62b29a4e36 100644 --- a/doc/device-tree-bindings/pci/x86-pci.txt +++ b/doc/device-tree-bindings/pci/x86-pci.txt @@ -10,6 +10,17 @@ Optional properties: configuration in TPL/SPL to reduce code size and boot time, since these phases only know about a small subset of PCI devices. +For PCI devices the following optional property is available: + +- pci,no-autoconfig : Don't automatically configure this PCI device at all. + This is used when the device is statically configured and must maintain + this same config throughout the boot process. An example is a serial + UART being used to debug PCI configuration, since reconfiguring it stops + the UART from working until the driver is re-probed, and this can cause + output to be lost. This should not generally be used in production code, + although it is often harmless. + + Example: pci { @@ -21,4 +32,17 @@ pci { 0x42000000 0x0 0xb0000000 0xb0000000 0 0x10000000 0x01000000 0x0 0x1000 0x1000 0 0xefff>; u-boot,skip-auto-config-until-reloc; + + + serial: serial at 18,2 { + reg = <0x0200c210 0 0 0 0>; + u-boot,dm-pre-reloc; + compatible = "intel,apl-ns16550"; + early-regs = <0xde000000 0x20>; + reg-shift = <2>; + clock-frequency = <1843200>; + current-speed = <115200>; + acpi,name = "URT3"; + pci,no-autoconfig; + }; }; diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 94733662b1..213381da6b 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -536,6 +536,8 @@ int pci_auto_config_devices(struct udevice *bus) int ret; debug("%s: device %s\n", __func__, dev->name); + if (dev_read_bool(dev, "pci,no-autoconfig")) + continue; ret = dm_pciauto_config_device(dev); if (ret < 0) return ret; From patchwork Mon Mar 9 03:44:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243382 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:32 -0600 Subject: [PATCH v2 08/39] x86: Correct wording of coreboot source code In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.8.I0e67688e96b62650b8218c3d536f9fc4de9d13b4@changeid> Some files are taken or modified from coreboot, but the files are no-longer part of the coreboot project. Fix the wording in a few places. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- Changes in v2: None arch/x86/cpu/coreboot/timestamp.c | 4 ++-- arch/x86/include/asm/arch-coreboot/timestamp.h | 4 ++-- arch/x86/include/asm/intel_pinctrl_defs.h | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/x86/cpu/coreboot/timestamp.c b/arch/x86/cpu/coreboot/timestamp.c index e698200d70..e8ccaf2212 100644 --- a/arch/x86/cpu/coreboot/timestamp.c +++ b/arch/x86/cpu/coreboot/timestamp.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * This file is part of the coreboot project. - * * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * Modified from the coreboot version */ #include diff --git a/arch/x86/include/asm/arch-coreboot/timestamp.h b/arch/x86/include/asm/arch-coreboot/timestamp.h index 9320afba56..85d42c02c4 100644 --- a/arch/x86/include/asm/arch-coreboot/timestamp.h +++ b/arch/x86/include/asm/arch-coreboot/timestamp.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * This file is part of the coreboot project. - * * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * Taken from the coreboot version */ #ifndef __COREBOOT_TIMESTAMP_H__ diff --git a/arch/x86/include/asm/intel_pinctrl_defs.h b/arch/x86/include/asm/intel_pinctrl_defs.h index 6da06bb52b..1ea141f082 100644 --- a/arch/x86/include/asm/intel_pinctrl_defs.h +++ b/arch/x86/include/asm/intel_pinctrl_defs.h @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * This file is part of the coreboot project. - * * Copyright (C) 2015-2016 Intel Corp. * Copyright 2019 Google LLC * From patchwork Mon Mar 9 03:44:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243383 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:33 -0600 Subject: [PATCH v2 09/39] x86: apl: Move p2sb ofdata reading to the correct method In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.9.Iff8dfbeef5f76f776cf3a84807b0ff3fd5a491ac@changeid> With P2SB the initial BAR (base-address register) is set up by TPL and this is used unchanged right through U-Boot. At present the reading of this address is split between the ofdata() and probe() methods. There are a few problems that are unique to the p2sb. One is that its children need to call pcr_read32(), etc. which needs to have the p2sb address correct. Also some of its children are pinctrl devices and pinctrl is used when any device is probed. So p2sb really needs to get its base address set up in ofdata_to_platdata(), before it is probed. Another point is that reading the p2sb BAR will not work if the p2sb is hidden. The FSP-S seems to hide it, presumably to avoid confusing PCI enumeration. Reading ofdata in ofdata_to_platdata() is the correct place anyway, so this is easy to fix. Move the code into one place and use the early-regs property in all cases for simplicity and to avoid needing to probe any PCI devices just to read the BAR. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner Tested-by: Wolfgang Wallner --- Changes in v2: None arch/x86/cpu/intel_common/p2sb.c | 35 +++++++++++--------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/arch/x86/cpu/intel_common/p2sb.c b/arch/x86/cpu/intel_common/p2sb.c index d5b4846e0a..4af55786e6 100644 --- a/arch/x86/cpu/intel_common/p2sb.c +++ b/arch/x86/cpu/intel_common/p2sb.c @@ -92,46 +92,35 @@ int p2sb_ofdata_to_platdata(struct udevice *dev) #if !CONFIG_IS_ENABLED(OF_PLATDATA) int ret; + u32 base[2]; + ret = dev_read_u32_array(dev, "early-regs", base, ARRAY_SIZE(base)); + if (ret) + return log_msg_ret("Missing/short early-regs", ret); + plat->mmio_base = base[0]; + /* TPL sets up the initial BAR */ if (spl_phase() == PHASE_TPL) { - u32 base[2]; - - /* TPL sets up the initial BAR */ - ret = dev_read_u32_array(dev, "early-regs", base, - ARRAY_SIZE(base)); - if (ret) - return log_msg_ret("Missing/short early-regs", ret); - plat->mmio_base = base[0]; plat->bdf = pci_get_devfn(dev); if (plat->bdf < 0) return log_msg_ret("Cannot get p2sb PCI address", - plat->bdf); + plat->bdf); } + upriv->mmio_base = plat->mmio_base; #else plat->mmio_base = plat->dtplat.early_regs[0]; plat->bdf = pci_ofplat_get_devfn(plat->dtplat.reg[0]); -#endif upriv->mmio_base = plat->mmio_base; - debug("p2sb: mmio_base=%x\n", (uint)plat->mmio_base); +#endif return 0; } static int p2sb_probe(struct udevice *dev) { - if (spl_phase() == PHASE_TPL) { + if (spl_phase() == PHASE_TPL) return p2sb_early_init(dev); - } else { - struct p2sb_platdata *plat = dev_get_platdata(dev); - - plat->mmio_base = dev_read_addr_pci(dev); - /* Don't set BDF since it should not be used */ - if (!plat->mmio_base || plat->mmio_base == FDT_ADDR_T_NONE) - return -EINVAL; - - if (spl_phase() == PHASE_SPL) - return p2sb_spl_init(dev); - } + else if (spl_phase() == PHASE_SPL) + return p2sb_spl_init(dev); return 0; } From patchwork Mon Mar 9 03:44:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243384 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:34 -0600 Subject: [PATCH v2 10/39] pci: Adjust dm_pci_read_bar32() to return errors correctly In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.10.I36321d5e30daf051900f01f6289dfc58439871ea@changeid> At present if reading a BAR returns 0xffffffff (e.g. the device is not present) then the value is masked and a different value is returned. This makes it harder to detect the problem when debugging. Update the function to avoid masking in this case. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- Changes in v2: None drivers/pci/pci-uclass.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 213381da6b..7f46e901fb 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -1213,7 +1213,14 @@ u32 dm_pci_read_bar32(const struct udevice *dev, int barnum) bar = PCI_BASE_ADDRESS_0 + barnum * 4; dm_pci_read_config32(dev, bar, &addr); - if (addr & PCI_BASE_ADDRESS_SPACE_IO) + + /* + * If we get an invalid address, return this so that comparisons with + * FDT_ADDR_T_NONE work correctly + */ + if (addr == 0xffffffff) + return addr; + else if (addr & PCI_BASE_ADDRESS_SPACE_IO) return addr & PCI_BASE_ADDRESS_IO_MASK; else return addr & PCI_BASE_ADDRESS_MEM_MASK; From patchwork Mon Mar 9 03:44:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243385 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:35 -0600 Subject: [PATCH v2 11/39] x86: apl: Add Global NVS table header In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.11.I4aa2fe09882dcf5a040603469bac136e4342d350@changeid> Add the C version of this header. It includes a few Chrome OS bits which are disabled for a normal build. Signed-off-by: Simon Glass --- Changes in v2: - Drop the Chrome OS pieces - Rename the 'coreboot' console to 'U-Boot' .../include/asm/arch-apollolake/global_nvs.h | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 arch/x86/include/asm/arch-apollolake/global_nvs.h diff --git a/arch/x86/include/asm/arch-apollolake/global_nvs.h b/arch/x86/include/asm/arch-apollolake/global_nvs.h new file mode 100644 index 0000000000..344a853fe9 --- /dev/null +++ b/arch/x86/include/asm/arch-apollolake/global_nvs.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2015-2017 Intel Corp. + * (Written by Lance Zhao for Intel Corp.) + * Copyright Google LLC 2019 + * + * Modified from coreboot apollolake/include/soc/nvs.h + */ + +#ifndef _GLOBAL_NVS_H_ +#define _GLOBAL_NVS_H_ + +struct __packed acpi_global_nvs { + /* Miscellaneous */ + u8 pcnt; /* 0x00 - Processor Count */ + u8 ppcm; /* 0x01 - Max PPC State */ + u8 lids; /* 0x02 - LID State */ + u8 pwrs; /* 0x03 - AC Power State */ + u8 dpte; /* 0x04 - Enable DPTF */ + u32 cbmc; /* 0x05 - 0x08 - U-Boot Console */ + u64 pm1i; /* 0x09 - 0x10 - System Wake Source - PM1 Index */ + u64 gpei; /* 0x11 - 0x18 - GPE Wake Source */ + u64 nhla; /* 0x19 - 0x20 - NHLT Address */ + u32 nhll; /* 0x21 - 0x24 - NHLT Length */ + u32 prt0; /* 0x25 - 0x28 - PERST_0 Address */ + u8 scdp; /* 0x29 - SD_CD GPIO portid */ + u8 scdo; /* 0x2A - GPIO pad offset relative to the community */ + u8 uior; /* 0x2B - UART debug controller init on S3 resume */ + u8 ecps; /* 0x2C - SGX Enabled status */ + u64 emna; /* 0x2D - 0x34 EPC base address */ + u64 elng; /* 0x35 - 0x3C EPC Length */ + u8 unused[195]; + u8 unused2[0xf00]; +#endif +}; + +#endif /* _GLOBAL_NVS_H_ */ From patchwork Mon Mar 9 03:44:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243386 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:36 -0600 Subject: [PATCH v2 12/39] dm: core: Add basic ACPI support In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.12.Ia5e9ba1f146567b18e9183395484bf04d2e5ba6a@changeid> ACPI (Advanced Configuration and Power Interface) is an Intel standard for specifying information about a platform. It is a little like device tree but considerably more complicated and with more backslashes. A primary difference is that it supports an interpreted bytecode language. Driver model does not use ACPI for U-Boot's configuration, but it is convenient to have it support generation of ACPI tables for passing to Linux, etc. As a starting point, add an optional set of ACPI operations to each device. Initially only a single operation is available, to obtain the ACPI name for the device. More operations are added later. Enable ACPI for sandbox to ensure build coverage and so that we can add tests. Reviewed-by: Bin Meng Signed-off-by: Simon Glass --- Changes in v2: - Move LOGC_ACPI definition to this patch drivers/core/Kconfig | 9 ++++++ drivers/core/Makefile | 1 + drivers/core/acpi.c | 32 +++++++++++++++++++ include/dm/acpi.h | 73 +++++++++++++++++++++++++++++++++++++++++++ include/dm/device.h | 5 +++ include/log.h | 2 ++ 6 files changed, 122 insertions(+) create mode 100644 drivers/core/acpi.c create mode 100644 include/dm/acpi.h diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 3b95b5387b..a3b0399342 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -261,4 +261,13 @@ config DM_DEV_READ_INLINE bool default y if !OF_LIVE +config ACPIGEN + bool "Support ACPI table generation in driver model" + default y if SANDBOX || GENERATE_ACPI_TABLE + help + This option enables generation of ACPI tables using driver-model + devices. It adds a new operation struct to each driver, to support + things like generating device-specific tables and returning the ACPI + name of a device. + endmenu diff --git a/drivers/core/Makefile b/drivers/core/Makefile index bce7467da1..c707026a3a 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -3,6 +3,7 @@ # Copyright (c) 2013 Google, Inc obj-y += device.o fdtaddr.o lists.o root.o uclass.o util.o +obj-$(CONFIG_$(SPL_TPL_)ACPIGEN) += acpi.o obj-$(CONFIG_DEVRES) += devres.o obj-$(CONFIG_$(SPL_)DM_DEVICE_REMOVE) += device-remove.o obj-$(CONFIG_$(SPL_)SIMPLE_BUS) += simple-bus.o diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c new file mode 100644 index 0000000000..45542199f5 --- /dev/null +++ b/drivers/core/acpi.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Core driver model support for ACPI table generation + * + * Copyright 2019 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEOGRY LOGC_ACPI + +#include +#include +#include +#include + +int acpi_return_name(char *out_name, const char *name) +{ + strcpy(out_name, name); + + return 0; +} + +int acpi_get_name(const struct udevice *dev, char *out_name) +{ + struct acpi_ops *aops; + + aops = device_get_acpi_ops(dev); + if (aops && aops->get_name) + return aops->get_name(dev, out_name); + + return -ENOSYS; +} diff --git a/include/dm/acpi.h b/include/dm/acpi.h new file mode 100644 index 0000000000..120576adc0 --- /dev/null +++ b/include/dm/acpi.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Core ACPI (Advanced Configuration and Power Interface) support + * + * Copyright 2019 Google LLC + * Written by Simon Glass + */ + +#ifndef __DM_ACPI_H__ +#define __DM_ACPI_H__ + +/* Allow operations to be optional for ACPI */ +#if CONFIG_IS_ENABLED(ACPIGEN) +#define acpi_ops_ptr(_ptr) .acpi_ops = _ptr, +#else +#define acpi_ops_ptr(_ptr) +#endif + +/* Length of an ACPI name string, excluding nul terminator */ +#define ACPI_NAME_LEN 4 + +/* Length of an ACPI name string including nul terminator */ +#define ACPI_NAME_MAX 5 + +/** + * struct acpi_ops - ACPI operations supported by driver model + */ +struct acpi_ops { + /** + * get_name() - Obtain the ACPI name of a device + * + * @dev: Device to check + * @out_name: Place to put the name, must hold at least ACPI_NAME_MAX + * bytes + * @return 0 if OK, -ENOENT if no name is available, other -ve value on + * other error + */ + int (*get_name)(const struct udevice *dev, char *out_name); +}; + +#define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops) + +/** + * acpi_get_name() - Obtain the ACPI name of a device + * + * @dev: Device to check + * @out_name: Place to put the name, must hold at least ACPI_NAME_MAX + * bytes + * @return 0 if OK, -ENOENT if no name is available, other -ve value on + * other error + */ +int acpi_get_name(const struct udevice *dev, char *out_name); + +/** + * acpi_return_name() - Copy an ACPI name to an output buffer + * + * This convenience function can be used to return a literal string as a name + * in functions that implement the get_name() method. + * + * For example: + * + * static int mydev_get_name(const struct udevice *dev, char *out_name) + * { + * return acpi_return_name(out_name, "WIBB"); + * } + * + * @out_name: Place to put the name + * @name: Name to copy + * @return 0 (always) + */ +int acpi_return_name(char *out_name, const char *name); + +#endif diff --git a/include/dm/device.h b/include/dm/device.h index 3517fc1926..9e77a0cd7b 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -245,6 +245,8 @@ struct udevice_id { * pointers defined by the driver, to implement driver functions required by * the uclass. * @flags: driver flags - see DM_FLAGS_... + * @acpi_ops: Advanced Configuration and Power Interface (ACPI) operations, + * allowing the device to add things to the ACPI tables passed to Linux */ struct driver { char *name; @@ -264,6 +266,9 @@ struct driver { int per_child_platdata_auto_alloc_size; const void *ops; /* driver-specific operations */ uint32_t flags; +#if CONFIG_IS_ENABLED(ACPIGEN) + struct acpi_ops *acpi_ops; +#endif }; /* Declare a new U-Boot driver */ diff --git a/include/log.h b/include/log.h index 62fb8afbd0..d706a1a464 100644 --- a/include/log.h +++ b/include/log.h @@ -51,6 +51,8 @@ enum log_category_t { LOGC_SANDBOX, /* Related to the sandbox board */ LOGC_BLOBLIST, /* Bloblist */ LOGC_DEVRES, /* Device resources (devres_... functions) */ + /* Intel Advanced Configuration and Power Interface (ACPI) */ + LOGC_ACPI, LOGC_COUNT, /* Number of log categories */ LOGC_END, /* Sentinel value for a list of log categories */ From patchwork Mon Mar 9 03:44:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243388 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:37 -0600 Subject: [PATCH v2 13/39] acpi: Add a binding for ACPI settings in the device tree In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.13.I7842b2dd0d6b475301fc044c6640d8089873053f@changeid> Devices need to report various identifiers in the ACPI tables. Rather than hard-coding these in drivers it is typically better to put them in the device tree. Add a binding file to describe this. Signed-off-by: Simon Glass --- Changes in v2: - Fix definition of HID - Infer hid-over-i2c CID value - Add the hid-over-i2c binding document doc/device-tree-bindings/device.txt | 36 +++++++++++++++ .../input/hid-over-i2c.txt | 44 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 doc/device-tree-bindings/device.txt create mode 100644 doc/device-tree-bindings/input/hid-over-i2c.txt diff --git a/doc/device-tree-bindings/device.txt b/doc/device-tree-bindings/device.txt new file mode 100644 index 0000000000..31ec2fa31b --- /dev/null +++ b/doc/device-tree-bindings/device.txt @@ -0,0 +1,36 @@ +Devices +======= + +Device bindings are described by their own individual binding files. + +U-Boot provides for some optional properties which are documented here. See +also hid-over-i2c.txt which describes HID devices. + + - acpi,has-power-resource : (boolean) true if this device has a power resource. + This causes a PRIC (ACPI PowerResource) to be written containing the + properties provided by this binding, to describe how to handle powering the + device up and down using GPIOs + - acpi,compatible : compatible string to report + - acpi,desc : Contains the string to use as the _DDN (DOS (Disk Operating + System) Device Name) + - acpi,hid : Contains the string to use as the HID (Hardware ID) + identifier _HID + - hid-descr-addr : HID register offset (for Human Interface Devices) + - acpi,probed : Tells U-Boot to add 'linux,probed' to the ACPI tables so that + Linux will not re-init the device + - acpi,uid : _UID value for device + + +Example +------- + +synaptics_touchpad: synaptics-touchpad at 2c { + compatible = "hid-over-i2c"; + reg = <0x2c>; + acpi,hid = "PNP0C50"; + acpi,desc = "Synaptics Touchpad"; + interrupts-extended = <&acpi_gpe GPIO_18_IRQ + IRQ_TYPE_EDGE_FALLING>; + acpi,probed; + hid-descr-addr = <0x20>; +}; diff --git a/doc/device-tree-bindings/input/hid-over-i2c.txt b/doc/device-tree-bindings/input/hid-over-i2c.txt new file mode 100644 index 0000000000..c76bafaf98 --- /dev/null +++ b/doc/device-tree-bindings/input/hid-over-i2c.txt @@ -0,0 +1,44 @@ +* HID over I2C Device-Tree bindings + +HID over I2C provides support for various Human Interface Devices over the +I2C bus. These devices can be for example touchpads, keyboards, touch screens +or sensors. + +The specification has been written by Microsoft and is currently available here: +http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx + +If this binding is used, the kernel module i2c-hid will handle the communication +with the device and the generic hid core layer will handle the protocol. + +Required properties: +- compatible: must be "hid-over-i2c" +- reg: i2c slave address +- hid-descr-addr: HID descriptor address +- interrupts: interrupt line + +Additional optional properties: + +Some devices may support additional optional properties to help with, e.g., +power sequencing. The following properties can be supported by one or more +device-specific compatible properties, which should be used in addition to the +"hid-over-i2c" string. + +- compatible: + * "wacom,w9013" (Wacom W9013 digitizer). Supports: + - vdd-supply (3.3V) + - vddl-supply (1.8V) + - post-power-on-delay-ms + +- vdd-supply: phandle of the regulator that provides the supply voltage. +- post-power-on-delay-ms: time required by the device after enabling its regulators + or powering it on, before it is ready for communication. + +Example: + + i2c-hid-dev at 2c { + compatible = "hid-over-i2c"; + reg = <0x2c>; + hid-descr-addr = <0x0020>; + interrupt-parent = <&gpx3>; + interrupts = <3 2>; + }; From patchwork Mon Mar 9 03:44:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243387 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:38 -0600 Subject: [PATCH v2 14/39] acpi: Add a simple sandbox test In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.14.Ife90d6e6cfa90e642e73328168a00804b1dc6fd4@changeid> Add a sandbox test for the basic ACPI functionality we have so far. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- Changes in v2: - Add in the acpi_table.h header file to this patch arch/sandbox/dts/test.dts | 4 ++ arch/sandbox/include/asm/acpi_table.h | 9 +++++ include/dm/uclass-id.h | 1 + test/dm/Makefile | 1 + test/dm/acpi.c | 55 +++++++++++++++++++++++++++ 5 files changed, 70 insertions(+) create mode 100644 arch/sandbox/include/asm/acpi_table.h create mode 100644 test/dm/acpi.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 4a277934a7..5fa951ad4b 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -206,6 +206,10 @@ compatible = "denx,u-boot-devres-test"; }; + acpi-test { + compatible = "denx,u-boot-acpi-test"; + }; + clocks { clk_fixed: clk-fixed { compatible = "fixed-clock"; diff --git a/arch/sandbox/include/asm/acpi_table.h b/arch/sandbox/include/asm/acpi_table.h new file mode 100644 index 0000000000..921c7f4201 --- /dev/null +++ b/arch/sandbox/include/asm/acpi_table.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 Google LLC + */ + +#ifndef __ASM_ACPI_TABLE_H__ +#define __ASM_ACPI_TABLE_H__ + +#endif /* __ASM_ACPI_TABLE_H__ */ diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 598f65ea7a..37ada51f9f 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -20,6 +20,7 @@ enum uclass_id { UCLASS_TEST_PROBE, UCLASS_TEST_DUMMY, UCLASS_TEST_DEVRES, + UCLASS_TEST_ACPI, UCLASS_SPI_EMUL, /* sandbox SPI device emulator */ UCLASS_I2C_EMUL, /* sandbox I2C device emulator */ UCLASS_I2C_EMUL_PARENT, /* parent for I2C device emulators */ diff --git a/test/dm/Makefile b/test/dm/Makefile index dd1ceff86c..3daf8a544e 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_UT_DM) += test-uclass.o # subsystem you must add sandbox tests here. obj-$(CONFIG_UT_DM) += core.o ifneq ($(CONFIG_SANDBOX),) +obj-$(CONFIG_ACPIGEN) += acpi.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 new file mode 100644 index 0000000000..e3519d4689 --- /dev/null +++ b/test/dm/acpi.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for ACPI table generation + * + * Copyright 2019 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include + +#define ACPI_TEST_DEV_NAME "ABCD" + +static int testacpi_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_return_name(out_name, ACPI_TEST_DEV_NAME); +} + +struct acpi_ops testacpi_ops = { + .get_name = testacpi_get_name, +}; + +static const struct udevice_id testacpi_ids[] = { + { .compatible = "denx,u-boot-acpi-test" }, + { } +}; + +U_BOOT_DRIVER(testacpi_drv) = { + .name = "testacpi_drv", + .of_match = testacpi_ids, + .id = UCLASS_TEST_ACPI, + acpi_ops_ptr(&testacpi_ops) +}; + +UCLASS_DRIVER(testacpi) = { + .name = "testacpi", + .id = UCLASS_TEST_ACPI, +}; + +/* Test ACPI get_name() */ +static int dm_test_acpi_get_name(struct unit_test_state *uts) +{ + char name[ACPI_NAME_MAX]; + struct udevice *dev; + + 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); + + return 0; +} +DM_TEST(dm_test_acpi_get_name, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243389 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:39 -0600 Subject: [PATCH v2 15/39] x86: Move acpi_table header to main include/ directory In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.15.If8795289f9cb41804659b2b37b17185f588b3175@changeid> This file is potentially useful to other architectures saddled with ACPI so move it into a common location. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- Changes in v2: None arch/x86/cpu/baytrail/acpi.c | 2 +- arch/x86/cpu/cpu.c | 2 +- arch/x86/cpu/quark/acpi.c | 2 +- arch/x86/cpu/tangier/acpi.c | 2 +- arch/x86/include/asm/acpi_table.h | 376 ---------------------------- arch/x86/lib/acpi.c | 2 +- arch/x86/lib/acpi_s3.c | 2 +- arch/x86/lib/acpi_table.c | 2 +- arch/x86/lib/tables.c | 2 +- arch/x86/lib/zimage.c | 2 +- include/acpi_table.h | 394 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_acpi.c | 2 +- 12 files changed, 404 insertions(+), 386 deletions(-) create mode 100644 include/acpi_table.h diff --git a/arch/x86/cpu/baytrail/acpi.c b/arch/x86/cpu/baytrail/acpi.c index f44228e693..856af95556 100644 --- a/arch/x86/cpu/baytrail/acpi.c +++ b/arch/x86/cpu/baytrail/acpi.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index 3db035c2c0..b878fd4b1f 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/x86/cpu/quark/acpi.c b/arch/x86/cpu/quark/acpi.c index 7b6fc2f4a5..dd562a6e2f 100644 --- a/arch/x86/cpu/quark/acpi.c +++ b/arch/x86/cpu/quark/acpi.c @@ -4,7 +4,7 @@ */ #include -#include +#include #include #include #include diff --git a/arch/x86/cpu/tangier/acpi.c b/arch/x86/cpu/tangier/acpi.c index 8b128138b0..768c4dcbc8 100644 --- a/arch/x86/cpu/tangier/acpi.c +++ b/arch/x86/cpu/tangier/acpi.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index 7588913f93..22d54057f1 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -9,382 +9,6 @@ #ifndef __ASM_ACPI_TABLE_H__ #define __ASM_ACPI_TABLE_H__ -#define RSDP_SIG "RSD PTR " /* RSDP pointer signature */ -#define OEM_ID "U-BOOT" /* U-Boot */ -#define OEM_TABLE_ID "U-BOOTBL" /* U-Boot Table */ -#define ASLC_ID "INTL" /* Intel ASL Compiler */ - -#define ACPI_RSDP_REV_ACPI_1_0 0 -#define ACPI_RSDP_REV_ACPI_2_0 2 - -/* - * RSDP (Root System Description Pointer) - * Note: ACPI 1.0 didn't have length, xsdt_address, and ext_checksum - */ -struct acpi_rsdp { - char signature[8]; /* RSDP signature */ - u8 checksum; /* Checksum of the first 20 bytes */ - char oem_id[6]; /* OEM ID */ - u8 revision; /* 0 for ACPI 1.0, others 2 */ - u32 rsdt_address; /* Physical address of RSDT (32 bits) */ - u32 length; /* Total RSDP length (incl. extended part) */ - u64 xsdt_address; /* Physical address of XSDT (64 bits) */ - u8 ext_checksum; /* Checksum of the whole table */ - u8 reserved[3]; -}; - -/* Generic ACPI header, provided by (almost) all tables */ -struct __packed acpi_table_header { - char signature[4]; /* ACPI signature (4 ASCII characters) */ - u32 length; /* Table length in bytes (incl. header) */ - u8 revision; /* Table version (not ACPI version!) */ - volatile u8 checksum; /* To make sum of entire table == 0 */ - char oem_id[6]; /* OEM identification */ - char oem_table_id[8]; /* OEM table identification */ - u32 oem_revision; /* OEM revision number */ - char aslc_id[4]; /* ASL compiler vendor ID */ - u32 aslc_revision; /* ASL compiler revision number */ -}; - -/* A maximum number of 32 ACPI tables ought to be enough for now */ -#define MAX_ACPI_TABLES 32 - -/* RSDT (Root System Description Table) */ -struct acpi_rsdt { - struct acpi_table_header header; - u32 entry[MAX_ACPI_TABLES]; -}; - -/* XSDT (Extended System Description Table) */ -struct acpi_xsdt { - struct acpi_table_header header; - u64 entry[MAX_ACPI_TABLES]; -}; - -/* FADT Preferred Power Management Profile */ -enum acpi_pm_profile { - ACPI_PM_UNSPECIFIED = 0, - ACPI_PM_DESKTOP, - ACPI_PM_MOBILE, - ACPI_PM_WORKSTATION, - ACPI_PM_ENTERPRISE_SERVER, - ACPI_PM_SOHO_SERVER, - ACPI_PM_APPLIANCE_PC, - ACPI_PM_PERFORMANCE_SERVER, - ACPI_PM_TABLET -}; - -/* FADT flags for p_lvl2_lat and p_lvl3_lat */ -#define ACPI_FADT_C2_NOT_SUPPORTED 101 -#define ACPI_FADT_C3_NOT_SUPPORTED 1001 - -/* FADT Boot Architecture Flags */ -#define ACPI_FADT_LEGACY_FREE 0x00 -#define ACPI_FADT_LEGACY_DEVICES (1 << 0) -#define ACPI_FADT_8042 (1 << 1) -#define ACPI_FADT_VGA_NOT_PRESENT (1 << 2) -#define ACPI_FADT_MSI_NOT_SUPPORTED (1 << 3) -#define ACPI_FADT_NO_PCIE_ASPM_CONTROL (1 << 4) - -/* FADT Feature Flags */ -#define ACPI_FADT_WBINVD (1 << 0) -#define ACPI_FADT_WBINVD_FLUSH (1 << 1) -#define ACPI_FADT_C1_SUPPORTED (1 << 2) -#define ACPI_FADT_C2_MP_SUPPORTED (1 << 3) -#define ACPI_FADT_POWER_BUTTON (1 << 4) -#define ACPI_FADT_SLEEP_BUTTON (1 << 5) -#define ACPI_FADT_FIXED_RTC (1 << 6) -#define ACPI_FADT_S4_RTC_WAKE (1 << 7) -#define ACPI_FADT_32BIT_TIMER (1 << 8) -#define ACPI_FADT_DOCKING_SUPPORTED (1 << 9) -#define ACPI_FADT_RESET_REGISTER (1 << 10) -#define ACPI_FADT_SEALED_CASE (1 << 11) -#define ACPI_FADT_HEADLESS (1 << 12) -#define ACPI_FADT_SLEEP_TYPE (1 << 13) -#define ACPI_FADT_PCI_EXPRESS_WAKE (1 << 14) -#define ACPI_FADT_PLATFORM_CLOCK (1 << 15) -#define ACPI_FADT_S4_RTC_VALID (1 << 16) -#define ACPI_FADT_REMOTE_POWER_ON (1 << 17) -#define ACPI_FADT_APIC_CLUSTER (1 << 18) -#define ACPI_FADT_APIC_PHYSICAL (1 << 19) -#define ACPI_FADT_HW_REDUCED_ACPI (1 << 20) -#define ACPI_FADT_LOW_PWR_IDLE_S0 (1 << 21) - -enum acpi_address_space_type { - ACPI_ADDRESS_SPACE_MEMORY = 0, /* System memory */ - ACPI_ADDRESS_SPACE_IO, /* System I/O */ - ACPI_ADDRESS_SPACE_PCI, /* PCI config space */ - ACPI_ADDRESS_SPACE_EC, /* Embedded controller */ - ACPI_ADDRESS_SPACE_SMBUS, /* SMBus */ - ACPI_ADDRESS_SPACE_PCC = 0x0a, /* Platform Comm. Channel */ - ACPI_ADDRESS_SPACE_FIXED = 0x7f /* Functional fixed hardware */ -}; - -enum acpi_address_space_size { - ACPI_ACCESS_SIZE_UNDEFINED = 0, - ACPI_ACCESS_SIZE_BYTE_ACCESS, - ACPI_ACCESS_SIZE_WORD_ACCESS, - ACPI_ACCESS_SIZE_DWORD_ACCESS, - ACPI_ACCESS_SIZE_QWORD_ACCESS -}; - -struct acpi_gen_regaddr { - u8 space_id; /* Address space ID */ - u8 bit_width; /* Register size in bits */ - u8 bit_offset; /* Register bit offset */ - u8 access_size; /* Access size */ - u32 addrl; /* Register address, low 32 bits */ - u32 addrh; /* Register address, high 32 bits */ -}; - -/* FADT (Fixed ACPI Description Table) */ -struct __packed acpi_fadt { - struct acpi_table_header header; - u32 firmware_ctrl; - u32 dsdt; - u8 res1; - u8 preferred_pm_profile; - u16 sci_int; - u32 smi_cmd; - u8 acpi_enable; - u8 acpi_disable; - u8 s4bios_req; - u8 pstate_cnt; - u32 pm1a_evt_blk; - u32 pm1b_evt_blk; - u32 pm1a_cnt_blk; - u32 pm1b_cnt_blk; - u32 pm2_cnt_blk; - u32 pm_tmr_blk; - u32 gpe0_blk; - u32 gpe1_blk; - u8 pm1_evt_len; - u8 pm1_cnt_len; - u8 pm2_cnt_len; - u8 pm_tmr_len; - u8 gpe0_blk_len; - u8 gpe1_blk_len; - u8 gpe1_base; - u8 cst_cnt; - u16 p_lvl2_lat; - u16 p_lvl3_lat; - u16 flush_size; - u16 flush_stride; - u8 duty_offset; - u8 duty_width; - u8 day_alrm; - u8 mon_alrm; - u8 century; - u16 iapc_boot_arch; - u8 res2; - u32 flags; - struct acpi_gen_regaddr reset_reg; - u8 reset_value; - u16 arm_boot_arch; - u8 minor_revision; - u32 x_firmware_ctl_l; - u32 x_firmware_ctl_h; - u32 x_dsdt_l; - u32 x_dsdt_h; - struct acpi_gen_regaddr x_pm1a_evt_blk; - struct acpi_gen_regaddr x_pm1b_evt_blk; - struct acpi_gen_regaddr x_pm1a_cnt_blk; - struct acpi_gen_regaddr x_pm1b_cnt_blk; - struct acpi_gen_regaddr x_pm2_cnt_blk; - struct acpi_gen_regaddr x_pm_tmr_blk; - struct acpi_gen_regaddr x_gpe0_blk; - struct acpi_gen_regaddr x_gpe1_blk; -}; - -/* FACS flags */ -#define ACPI_FACS_S4BIOS_F (1 << 0) -#define ACPI_FACS_64BIT_WAKE_F (1 << 1) - -/* FACS (Firmware ACPI Control Structure) */ -struct acpi_facs { - char signature[4]; /* "FACS" */ - u32 length; /* Length in bytes (>= 64) */ - u32 hardware_signature; /* Hardware signature */ - u32 firmware_waking_vector; /* Firmware waking vector */ - u32 global_lock; /* Global lock */ - u32 flags; /* FACS flags */ - u32 x_firmware_waking_vector_l; /* X FW waking vector, low */ - u32 x_firmware_waking_vector_h; /* X FW waking vector, high */ - u8 version; /* Version 2 */ - u8 res1[3]; - u32 ospm_flags; /* OSPM enabled flags */ - u8 res2[24]; -}; - -/* MADT flags */ -#define ACPI_MADT_PCAT_COMPAT (1 << 0) - -/* MADT (Multiple APIC Description Table) */ -struct acpi_madt { - struct acpi_table_header header; - u32 lapic_addr; /* Local APIC address */ - u32 flags; /* Multiple APIC flags */ -}; - -/* MADT: APIC Structure Type*/ -enum acpi_apic_types { - ACPI_APIC_LAPIC = 0, /* Processor local APIC */ - ACPI_APIC_IOAPIC, /* I/O APIC */ - ACPI_APIC_IRQ_SRC_OVERRIDE, /* Interrupt source override */ - ACPI_APIC_NMI_SRC, /* NMI source */ - ACPI_APIC_LAPIC_NMI, /* Local APIC NMI */ - ACPI_APIC_LAPIC_ADDR_OVERRIDE, /* Local APIC address override */ - ACPI_APIC_IOSAPIC, /* I/O SAPIC */ - ACPI_APIC_LSAPIC, /* Local SAPIC */ - ACPI_APIC_PLATFORM_IRQ_SRC, /* Platform interrupt sources */ - ACPI_APIC_LX2APIC, /* Processor local x2APIC */ - ACPI_APIC_LX2APIC_NMI, /* Local x2APIC NMI */ -}; - -/* MADT: Processor Local APIC Structure */ - -#define LOCAL_APIC_FLAG_ENABLED (1 << 0) - -struct acpi_madt_lapic { - u8 type; /* Type (0) */ - u8 length; /* Length in bytes (8) */ - u8 processor_id; /* ACPI processor ID */ - u8 apic_id; /* Local APIC ID */ - u32 flags; /* Local APIC flags */ -}; - -/* MADT: I/O APIC Structure */ -struct acpi_madt_ioapic { - u8 type; /* Type (1) */ - u8 length; /* Length in bytes (12) */ - u8 ioapic_id; /* I/O APIC ID */ - u8 reserved; - u32 ioapic_addr; /* I/O APIC address */ - u32 gsi_base; /* Global system interrupt base */ -}; - -/* MADT: Interrupt Source Override Structure */ -struct __packed acpi_madt_irqoverride { - u8 type; /* Type (2) */ - u8 length; /* Length in bytes (10) */ - u8 bus; /* ISA (0) */ - u8 source; /* Bus-relative int. source (IRQ) */ - u32 gsirq; /* Global system interrupt */ - u16 flags; /* MPS INTI flags */ -}; - -/* MADT: Local APIC NMI Structure */ -struct __packed acpi_madt_lapic_nmi { - u8 type; /* Type (4) */ - u8 length; /* Length in bytes (6) */ - u8 processor_id; /* ACPI processor ID */ - u16 flags; /* MPS INTI flags */ - u8 lint; /* Local APIC LINT# */ -}; - -/* MCFG (PCI Express MMIO config space BAR description table) */ -struct acpi_mcfg { - struct acpi_table_header header; - u8 reserved[8]; -}; - -struct acpi_mcfg_mmconfig { - u32 base_address_l; - u32 base_address_h; - u16 pci_segment_group_number; - u8 start_bus_number; - u8 end_bus_number; - u8 reserved[4]; -}; - -/* PM1_CNT bit defines */ -#define PM1_CNT_SCI_EN (1 << 0) - -/* ACPI global NVS structure */ -struct acpi_global_nvs; - -/* CSRT (Core System Resource Table) */ -struct acpi_csrt { - struct acpi_table_header header; -}; - -struct acpi_csrt_group { - u32 length; - u32 vendor_id; - u32 subvendor_id; - u16 device_id; - u16 subdevice_id; - u16 revision; - u16 reserved; - u32 shared_info_length; -}; - -struct acpi_csrt_shared_info { - u16 major_version; - u16 minor_version; - u32 mmio_base_low; - u32 mmio_base_high; - u32 gsi_interrupt; - u8 interrupt_polarity; - u8 interrupt_mode; - u8 num_channels; - u8 dma_address_width; - u16 base_request_line; - u16 num_handshake_signals; - u32 max_block_size; -}; - -/* DBG2 definitions are partially used for SPCR interface_type */ - -/* Types for port_type field */ - -#define ACPI_DBG2_SERIAL_PORT 0x8000 -#define ACPI_DBG2_1394_PORT 0x8001 -#define ACPI_DBG2_USB_PORT 0x8002 -#define ACPI_DBG2_NET_PORT 0x8003 - -/* Subtypes for port_subtype field */ - -#define ACPI_DBG2_16550_COMPATIBLE 0x0000 -#define ACPI_DBG2_16550_SUBSET 0x0001 -#define ACPI_DBG2_ARM_PL011 0x0003 -#define ACPI_DBG2_ARM_SBSA_32BIT 0x000D -#define ACPI_DBG2_ARM_SBSA_GENERIC 0x000E -#define ACPI_DBG2_ARM_DCC 0x000F -#define ACPI_DBG2_BCM2835 0x0010 - -#define ACPI_DBG2_1394_STANDARD 0x0000 - -#define ACPI_DBG2_USB_XHCI 0x0000 -#define ACPI_DBG2_USB_EHCI 0x0001 - -#define ACPI_DBG2_UNKNOWN 0x00FF - -/* SPCR (Serial Port Console Redirection table) */ -struct __packed acpi_spcr { - struct acpi_table_header header; - u8 interface_type; - u8 reserved[3]; - struct acpi_gen_regaddr serial_port; - u8 interrupt_type; - u8 pc_interrupt; - u32 interrupt; /* Global system interrupt */ - u8 baud_rate; - u8 parity; - u8 stop_bits; - u8 flow_control; - u8 terminal_type; - u8 reserved1; - u16 pci_device_id; /* Must be 0xffff if not PCI device */ - u16 pci_vendor_id; /* Must be 0xffff if not PCI device */ - u8 pci_bus; - u8 pci_device; - u8 pci_function; - u32 pci_flags; - u8 pci_segment; - u32 reserved2; -}; - /* These can be used by the target port */ void acpi_fill_header(struct acpi_table_header *header, char *signature); diff --git a/arch/x86/lib/acpi.c b/arch/x86/lib/acpi.c index cba9c24dd4..2f4cd736f7 100644 --- a/arch/x86/lib/acpi.c +++ b/arch/x86/lib/acpi.c @@ -4,7 +4,7 @@ */ #include -#include +#include #include #include diff --git a/arch/x86/lib/acpi_s3.c b/arch/x86/lib/acpi_s3.c index 197636c4b5..74a78fd193 100644 --- a/arch/x86/lib/acpi_s3.c +++ b/arch/x86/lib/acpi_s3.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 0d69cf271f..71913b6f65 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c index 7aea722d0b..b718a5c3dc 100644 --- a/arch/x86/lib/tables.c +++ b/arch/x86/lib/tables.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include /** diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c index 9b5e767ccc..180b18349d 100644 --- a/arch/x86/lib/zimage.c +++ b/arch/x86/lib/zimage.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/acpi_table.h b/include/acpi_table.h new file mode 100644 index 0000000000..b4404a632c --- /dev/null +++ b/include/acpi_table.h @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Helpers for ACPI table generation + * + * Based on acpi.c from coreboot + * + * Copyright 2019 Google LLC + * + * Copyright (C) 2015, Saket Sinha + * Copyright (C) 2016, Bin Meng + */ + +#ifndef __ACPI_TABLE_H__ +#define __ACPI_TABLE_H__ + +#define RSDP_SIG "RSD PTR " /* RSDP pointer signature */ +#define OEM_ID "U-BOOT" /* U-Boot */ +#define OEM_TABLE_ID "U-BOOTBL" /* U-Boot Table */ +#define ASLC_ID "INTL" /* Intel ASL Compiler */ + +#define ACPI_RSDP_REV_ACPI_1_0 0 +#define ACPI_RSDP_REV_ACPI_2_0 2 + +/* + * RSDP (Root System Description Pointer) + * Note: ACPI 1.0 didn't have length, xsdt_address, and ext_checksum + */ +struct acpi_rsdp { + char signature[8]; /* RSDP signature */ + u8 checksum; /* Checksum of the first 20 bytes */ + char oem_id[6]; /* OEM ID */ + u8 revision; /* 0 for ACPI 1.0, others 2 */ + u32 rsdt_address; /* Physical address of RSDT (32 bits) */ + u32 length; /* Total RSDP length (incl. extended part) */ + u64 xsdt_address; /* Physical address of XSDT (64 bits) */ + u8 ext_checksum; /* Checksum of the whole table */ + u8 reserved[3]; +}; + +/* Generic ACPI header, provided by (almost) all tables */ +struct __packed acpi_table_header { + char signature[4]; /* ACPI signature (4 ASCII characters) */ + u32 length; /* Table length in bytes (incl. header) */ + u8 revision; /* Table version (not ACPI version!) */ + volatile u8 checksum; /* To make sum of entire table == 0 */ + char oem_id[6]; /* OEM identification */ + char oem_table_id[8]; /* OEM table identification */ + u32 oem_revision; /* OEM revision number */ + char aslc_id[4]; /* ASL compiler vendor ID */ + u32 aslc_revision; /* ASL compiler revision number */ +}; + +/* A maximum number of 32 ACPI tables ought to be enough for now */ +#define MAX_ACPI_TABLES 32 + +/* RSDT (Root System Description Table) */ +struct acpi_rsdt { + struct acpi_table_header header; + u32 entry[MAX_ACPI_TABLES]; +}; + +/* XSDT (Extended System Description Table) */ +struct acpi_xsdt { + struct acpi_table_header header; + u64 entry[MAX_ACPI_TABLES]; +}; + +/* FADT Preferred Power Management Profile */ +enum acpi_pm_profile { + ACPI_PM_UNSPECIFIED = 0, + ACPI_PM_DESKTOP, + ACPI_PM_MOBILE, + ACPI_PM_WORKSTATION, + ACPI_PM_ENTERPRISE_SERVER, + ACPI_PM_SOHO_SERVER, + ACPI_PM_APPLIANCE_PC, + ACPI_PM_PERFORMANCE_SERVER, + ACPI_PM_TABLET +}; + +/* FADT flags for p_lvl2_lat and p_lvl3_lat */ +#define ACPI_FADT_C2_NOT_SUPPORTED 101 +#define ACPI_FADT_C3_NOT_SUPPORTED 1001 + +/* FADT Boot Architecture Flags */ +#define ACPI_FADT_LEGACY_FREE 0x00 +#define ACPI_FADT_LEGACY_DEVICES BIT(0) +#define ACPI_FADT_8042 BIT(1) +#define ACPI_FADT_VGA_NOT_PRESENT BIT(2) +#define ACPI_FADT_MSI_NOT_SUPPORTED BIT(3) +#define ACPI_FADT_NO_PCIE_ASPM_CONTROL BIT(4) + +/* FADT Feature Flags */ +#define ACPI_FADT_WBINVD BIT(0) +#define ACPI_FADT_WBINVD_FLUSH BIT(1) +#define ACPI_FADT_C1_SUPPORTED BIT(2) +#define ACPI_FADT_C2_MP_SUPPORTED BIT(3) +#define ACPI_FADT_POWER_BUTTON BIT(4) +#define ACPI_FADT_SLEEP_BUTTON BIT(5) +#define ACPI_FADT_FIXED_RTC BIT(6) +#define ACPI_FADT_S4_RTC_WAKE BIT(7) +#define ACPI_FADT_32BIT_TIMER BIT(8) +#define ACPI_FADT_DOCKING_SUPPORTED BIT(9) +#define ACPI_FADT_RESET_REGISTER BIT(10) +#define ACPI_FADT_SEALED_CASE BIT(11) +#define ACPI_FADT_HEADLESS BIT(12) +#define ACPI_FADT_SLEEP_TYPE BIT(13) +#define ACPI_FADT_PCI_EXPRESS_WAKE BIT(14) +#define ACPI_FADT_PLATFORM_CLOCK BIT(15) +#define ACPI_FADT_S4_RTC_VALID BIT(16) +#define ACPI_FADT_REMOTE_POWER_ON BIT(17) +#define ACPI_FADT_APIC_CLUSTER BIT(18) +#define ACPI_FADT_APIC_PHYSICAL BIT(19) +#define ACPI_FADT_HW_REDUCED_ACPI BIT(20) +#define ACPI_FADT_LOW_PWR_IDLE_S0 BIT(21) + +enum acpi_address_space_type { + ACPI_ADDRESS_SPACE_MEMORY = 0, /* System memory */ + ACPI_ADDRESS_SPACE_IO, /* System I/O */ + ACPI_ADDRESS_SPACE_PCI, /* PCI config space */ + ACPI_ADDRESS_SPACE_EC, /* Embedded controller */ + ACPI_ADDRESS_SPACE_SMBUS, /* SMBus */ + ACPI_ADDRESS_SPACE_PCC = 0x0a, /* Platform Comm. Channel */ + ACPI_ADDRESS_SPACE_FIXED = 0x7f /* Functional fixed hardware */ +}; + +enum acpi_address_space_size { + ACPI_ACCESS_SIZE_UNDEFINED = 0, + ACPI_ACCESS_SIZE_BYTE_ACCESS, + ACPI_ACCESS_SIZE_WORD_ACCESS, + ACPI_ACCESS_SIZE_DWORD_ACCESS, + ACPI_ACCESS_SIZE_QWORD_ACCESS +}; + +struct acpi_gen_regaddr { + u8 space_id; /* Address space ID */ + u8 bit_width; /* Register size in bits */ + u8 bit_offset; /* Register bit offset */ + u8 access_size; /* Access size */ + u32 addrl; /* Register address, low 32 bits */ + u32 addrh; /* Register address, high 32 bits */ +}; + +/* FADT (Fixed ACPI Description Table) */ +struct __packed acpi_fadt { + struct acpi_table_header header; + u32 firmware_ctrl; + u32 dsdt; + u8 res1; + u8 preferred_pm_profile; + u16 sci_int; + u32 smi_cmd; + u8 acpi_enable; + u8 acpi_disable; + u8 s4bios_req; + u8 pstate_cnt; + u32 pm1a_evt_blk; + u32 pm1b_evt_blk; + u32 pm1a_cnt_blk; + u32 pm1b_cnt_blk; + u32 pm2_cnt_blk; + u32 pm_tmr_blk; + u32 gpe0_blk; + u32 gpe1_blk; + u8 pm1_evt_len; + u8 pm1_cnt_len; + u8 pm2_cnt_len; + u8 pm_tmr_len; + u8 gpe0_blk_len; + u8 gpe1_blk_len; + u8 gpe1_base; + u8 cst_cnt; + u16 p_lvl2_lat; + u16 p_lvl3_lat; + u16 flush_size; + u16 flush_stride; + u8 duty_offset; + u8 duty_width; + u8 day_alrm; + u8 mon_alrm; + u8 century; + u16 iapc_boot_arch; + u8 res2; + u32 flags; + struct acpi_gen_regaddr reset_reg; + u8 reset_value; + u16 arm_boot_arch; + u8 minor_revision; + u32 x_firmware_ctl_l; + u32 x_firmware_ctl_h; + u32 x_dsdt_l; + u32 x_dsdt_h; + struct acpi_gen_regaddr x_pm1a_evt_blk; + struct acpi_gen_regaddr x_pm1b_evt_blk; + struct acpi_gen_regaddr x_pm1a_cnt_blk; + struct acpi_gen_regaddr x_pm1b_cnt_blk; + struct acpi_gen_regaddr x_pm2_cnt_blk; + struct acpi_gen_regaddr x_pm_tmr_blk; + struct acpi_gen_regaddr x_gpe0_blk; + struct acpi_gen_regaddr x_gpe1_blk; +}; + +/* FACS flags */ +#define ACPI_FACS_S4BIOS_F BIT(0) +#define ACPI_FACS_64BIT_WAKE_F BIT(1) + +/* FACS (Firmware ACPI Control Structure) */ +struct acpi_facs { + char signature[4]; /* "FACS" */ + u32 length; /* Length in bytes (>= 64) */ + u32 hardware_signature; /* Hardware signature */ + u32 firmware_waking_vector; /* Firmware waking vector */ + u32 global_lock; /* Global lock */ + u32 flags; /* FACS flags */ + u32 x_firmware_waking_vector_l; /* X FW waking vector, low */ + u32 x_firmware_waking_vector_h; /* X FW waking vector, high */ + u8 version; /* Version 2 */ + u8 res1[3]; + u32 ospm_flags; /* OSPM enabled flags */ + u8 res2[24]; +}; + +/* MADT flags */ +#define ACPI_MADT_PCAT_COMPAT BIT(0) + +/* MADT (Multiple APIC Description Table) */ +struct acpi_madt { + struct acpi_table_header header; + u32 lapic_addr; /* Local APIC address */ + u32 flags; /* Multiple APIC flags */ +}; + +/* MADT: APIC Structure Type*/ +enum acpi_apic_types { + ACPI_APIC_LAPIC = 0, /* Processor local APIC */ + ACPI_APIC_IOAPIC, /* I/O APIC */ + ACPI_APIC_IRQ_SRC_OVERRIDE, /* Interrupt source override */ + ACPI_APIC_NMI_SRC, /* NMI source */ + ACPI_APIC_LAPIC_NMI, /* Local APIC NMI */ + ACPI_APIC_LAPIC_ADDR_OVERRIDE, /* Local APIC address override */ + ACPI_APIC_IOSAPIC, /* I/O SAPIC */ + ACPI_APIC_LSAPIC, /* Local SAPIC */ + ACPI_APIC_PLATFORM_IRQ_SRC, /* Platform interrupt sources */ + ACPI_APIC_LX2APIC, /* Processor local x2APIC */ + ACPI_APIC_LX2APIC_NMI, /* Local x2APIC NMI */ +}; + +/* MADT: Processor Local APIC Structure */ + +#define LOCAL_APIC_FLAG_ENABLED BIT(0) + +struct acpi_madt_lapic { + u8 type; /* Type (0) */ + u8 length; /* Length in bytes (8) */ + u8 processor_id; /* ACPI processor ID */ + u8 apic_id; /* Local APIC ID */ + u32 flags; /* Local APIC flags */ +}; + +/* MADT: I/O APIC Structure */ +struct acpi_madt_ioapic { + u8 type; /* Type (1) */ + u8 length; /* Length in bytes (12) */ + u8 ioapic_id; /* I/O APIC ID */ + u8 reserved; + u32 ioapic_addr; /* I/O APIC address */ + u32 gsi_base; /* Global system interrupt base */ +}; + +/* MADT: Interrupt Source Override Structure */ +struct __packed acpi_madt_irqoverride { + u8 type; /* Type (2) */ + u8 length; /* Length in bytes (10) */ + u8 bus; /* ISA (0) */ + u8 source; /* Bus-relative int. source (IRQ) */ + u32 gsirq; /* Global system interrupt */ + u16 flags; /* MPS INTI flags */ +}; + +/* MADT: Local APIC NMI Structure */ +struct __packed acpi_madt_lapic_nmi { + u8 type; /* Type (4) */ + u8 length; /* Length in bytes (6) */ + u8 processor_id; /* ACPI processor ID */ + u16 flags; /* MPS INTI flags */ + u8 lint; /* Local APIC LINT# */ +}; + +/* MCFG (PCI Express MMIO config space BAR description table) */ +struct acpi_mcfg { + struct acpi_table_header header; + u8 reserved[8]; +}; + +struct acpi_mcfg_mmconfig { + u32 base_address_l; + u32 base_address_h; + u16 pci_segment_group_number; + u8 start_bus_number; + u8 end_bus_number; + u8 reserved[4]; +}; + +/* PM1_CNT bit defines */ +#define PM1_CNT_SCI_EN BIT(0) + +/* ACPI global NVS structure */ +struct acpi_global_nvs; + +/* CSRT (Core System Resource Table) */ +struct acpi_csrt { + struct acpi_table_header header; +}; + +struct acpi_csrt_group { + u32 length; + u32 vendor_id; + u32 subvendor_id; + u16 device_id; + u16 subdevice_id; + u16 revision; + u16 reserved; + u32 shared_info_length; +}; + +struct acpi_csrt_shared_info { + u16 major_version; + u16 minor_version; + u32 mmio_base_low; + u32 mmio_base_high; + u32 gsi_interrupt; + u8 interrupt_polarity; + u8 interrupt_mode; + u8 num_channels; + u8 dma_address_width; + u16 base_request_line; + u16 num_handshake_signals; + u32 max_block_size; +}; + +/* DBG2 definitions are partially used for SPCR interface_type */ + +/* Types for port_type field */ + +#define ACPI_DBG2_SERIAL_PORT 0x8000 +#define ACPI_DBG2_1394_PORT 0x8001 +#define ACPI_DBG2_USB_PORT 0x8002 +#define ACPI_DBG2_NET_PORT 0x8003 + +/* Subtypes for port_subtype field */ + +#define ACPI_DBG2_16550_COMPATIBLE 0x0000 +#define ACPI_DBG2_16550_SUBSET 0x0001 +#define ACPI_DBG2_ARM_PL011 0x0003 +#define ACPI_DBG2_ARM_SBSA_32BIT 0x000D +#define ACPI_DBG2_ARM_SBSA_GENERIC 0x000E +#define ACPI_DBG2_ARM_DCC 0x000F +#define ACPI_DBG2_BCM2835 0x0010 + +#define ACPI_DBG2_1394_STANDARD 0x0000 + +#define ACPI_DBG2_USB_XHCI 0x0000 +#define ACPI_DBG2_USB_EHCI 0x0001 + +#define ACPI_DBG2_UNKNOWN 0x00FF + +/* SPCR (Serial Port Console Redirection table) */ +struct __packed acpi_spcr { + struct acpi_table_header header; + u8 interface_type; + u8 reserved[3]; + struct acpi_gen_regaddr serial_port; + u8 interrupt_type; + u8 pc_interrupt; + u32 interrupt; /* Global system interrupt */ + u8 baud_rate; + u8 parity; + u8 stop_bits; + u8 flow_control; + u8 terminal_type; + u8 reserved1; + u16 pci_device_id; /* Must be 0xffff if not PCI device */ + u16 pci_vendor_id; /* Must be 0xffff if not PCI device */ + u8 pci_bus; + u8 pci_device; + u8 pci_function; + u32 pci_flags; + u8 pci_segment; + u32 reserved2; +}; + +#include + +#endif /* __ACPI_TABLE_H__ */ diff --git a/lib/efi_loader/efi_acpi.c b/lib/efi_loader/efi_acpi.c index a4e5e53d15..74d8f5567f 100644 --- a/lib/efi_loader/efi_acpi.c +++ b/lib/efi_loader/efi_acpi.c @@ -7,7 +7,7 @@ #include #include -#include +#include static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID; From patchwork Mon Mar 9 03:44:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243390 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:40 -0600 Subject: [PATCH v2 16/39] acpi: Add an __ACPI__ preprocessor symbol In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.16.I6e674b2388c5a74a6912deb2dcc9c8acc8875da0@changeid> The ASL compiler cannot handle C structures and the like so needs some sort of header guard around these. We already have an __ASSEMBLY__ #define but it seems best to create a new one for ACPI since the rules may be different. Add the check to a few files that ACPI always includes. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- Changes in v2: None include/acpi_table.h | 4 ++++ include/dm/acpi.h | 4 ++++ scripts/Makefile.lib | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/acpi_table.h b/include/acpi_table.h index b4404a632c..dd74895813 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -21,6 +21,8 @@ #define ACPI_RSDP_REV_ACPI_1_0 0 #define ACPI_RSDP_REV_ACPI_2_0 2 +#if !defined(__ACPI__) + /* * RSDP (Root System Description Pointer) * Note: ACPI 1.0 didn't have length, xsdt_address, and ext_checksum @@ -389,6 +391,8 @@ struct __packed acpi_spcr { u32 reserved2; }; +#endif /* !__ACPI__*/ + #include #endif /* __ACPI_TABLE_H__ */ diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 120576adc0..8d6c3fd424 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -22,6 +22,8 @@ /* Length of an ACPI name string including nul terminator */ #define ACPI_NAME_MAX 5 +#if !defined(__ACPI__) + /** * struct acpi_ops - ACPI operations supported by driver model */ @@ -70,4 +72,6 @@ int acpi_get_name(const struct udevice *dev, char *out_name); */ int acpi_return_name(char *out_name, const char *name); +#endif /* __ACPI__ */ + #endif diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 30f392fdfb..aebdb38d0d 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -413,8 +413,8 @@ ASL_TMP = $(patsubst %.c,%.asl.tmp,$@) quiet_cmd_acpi_c_asl= ASL $< cmd_acpi_c_asl= \ - $(CPP) -x assembler-with-cpp -D__ASSEMBLY__ -P $(UBOOTINCLUDE) \ - -o $(ASL_TMP) $< && \ + $(CPP) -x assembler-with-cpp -D__ASSEMBLY__ -D__ACPI__ \ + -P $(UBOOTINCLUDE) -o $(ASL_TMP) $< && \ iasl -p $@ -tc $(ASL_TMP) $(if $(KBUILD_VERBOSE:1=), >/dev/null) && \ mv $(patsubst %.c,%.hex,$@) $@ From patchwork Mon Mar 9 03:44:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243392 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:41 -0600 Subject: [PATCH v2 17/39] acpi: Add a central location for table version numbers In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.17.I19e38d6d49d8a7ac5fbe8a724e5ab3e71f857208@changeid> Each ACPI table has its own version number. Add the version numbers in a single function so we can keep them consistent and easily see what versions are supported. Start a new acpi_table file in a generic directory to house this function. We can move things over to this file from x86 as needed. Signed-off-by: Simon Glass --- Changes in v2: - Move the sandbox acpi_table.h header file to an earlier patch - Use #defines for MADT and MCFG version numbers include/acpi_table.h | 61 +++++++++++++++++++++++++++++++++++++++++++ lib/Makefile | 1 + lib/acpi/Makefile | 4 +++ lib/acpi/acpi_table.c | 60 ++++++++++++++++++++++++++++++++++++++++++ test/dm/acpi.c | 14 ++++++++++ 5 files changed, 140 insertions(+) create mode 100644 lib/acpi/Makefile create mode 100644 lib/acpi/acpi_table.c diff --git a/include/acpi_table.h b/include/acpi_table.h index dd74895813..ccf6fa04db 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -202,6 +202,26 @@ struct __packed acpi_fadt { struct acpi_gen_regaddr x_gpe1_blk; }; +/* FADT TABLE Revision values */ +#define ACPI_FADT_REV_ACPI_1_0 1 +#define ACPI_FADT_REV_ACPI_2_0 3 +#define ACPI_FADT_REV_ACPI_3_0 4 +#define ACPI_FADT_REV_ACPI_4_0 4 +#define ACPI_FADT_REV_ACPI_5_0 5 +#define ACPI_FADT_REV_ACPI_6_0 6 + +/* MADT TABLE Revision values */ +#define ACPI_MADT_REV_ACPI_3_0 2 +#define ACPI_MADT_REV_ACPI_4_0 3 +#define ACPI_MADT_REV_ACPI_5_0 3 +#define ACPI_MADT_REV_ACPI_6_0 5 + +#define ACPI_MCFG_REV_ACPI_3_0 1 + +/* IVRS Revision Field */ +#define IVRS_FORMAT_FIXED 0x01 /* Type 10h & 11h only */ +#define IVRS_FORMAT_MIXED 0x02 /* Type 10h, 11h, & 40h */ + /* FACS flags */ #define ACPI_FACS_S4BIOS_F BIT(0) #define ACPI_FACS_64BIT_WAKE_F BIT(1) @@ -391,6 +411,47 @@ struct __packed acpi_spcr { u32 reserved2; }; +/* Tables defined by ACPI and generated by U-Boot */ +enum acpi_tables { + ACPITAB_BERT, + ACPITAB_DBG2, + ACPITAB_DMAR, + ACPITAB_DSDT, + ACPITAB_FACS, + ACPITAB_FADT, + ACPITAB_HEST, + ACPITAB_HPET, + ACPITAB_IVRS, + ACPITAB_MADT, + ACPITAB_MCFG, + ACPITAB_RSDP, + ACPITAB_RSDT, + ACPITAB_SLIT, + ACPITAB_SRAT, + ACPITAB_SSDT, + ACPITAB_TCPA, + ACPITAB_TPM2, + ACPITAB_XSDT, + ACPITAB_ECDT, + + /* Additional proprietary tables */ + ACPITAB_VFCT, + ACPITAB_NHLT, + ACPITAB_SPMI, + + ACPITAB_COUNT, +}; + +/** + * acpi_get_table_revision() - Get the revision number generated for a table + * + * This keeps the version-number information in one place + * + * @table: ACPI table to check + * @return version number that U-Boot generates + */ +int acpi_get_table_revision(enum acpi_tables table); + #endif /* !__ACPI__*/ #include diff --git a/lib/Makefile b/lib/Makefile index 15259d0473..9df834c2fd 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -5,6 +5,7 @@ ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_$(SPL_TPL_)ACPIGEN) += acpi/ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile new file mode 100644 index 0000000000..660491ef71 --- /dev/null +++ b/lib/acpi/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += acpi_table.o diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c new file mode 100644 index 0000000000..197f965c08 --- /dev/null +++ b/lib/acpi/acpi_table.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for ACPI table generation + * + * Copyright 2019 Google LLC + */ + +#include +#include + +int acpi_get_table_revision(enum acpi_tables table) +{ + switch (table) { + case ACPITAB_FADT: + return ACPI_FADT_REV_ACPI_3_0; + case ACPITAB_MADT: + return ACPI_MADT_REV_ACPI_3_0; + case ACPITAB_MCFG: + return ACPI_MCFG_REV_ACPI_3_0; + case ACPITAB_TCPA: + /* THis version and the rest are open-coded */ + return 2; + case ACPITAB_TPM2: + return 4; + case ACPITAB_SSDT: /* ACPI 3.0 upto 6.3: 2 */ + return 2; + case ACPITAB_SRAT: /* ACPI 2.0: 1, ACPI 3.0: 2, ACPI 4.0 upto 6.3: 3 */ + return 1; /* TODO Should probably be upgraded to 2 */ + case ACPITAB_DMAR: + return 1; + case ACPITAB_SLIT: /* ACPI 2.0 upto 6.3: 1 */ + return 1; + case ACPITAB_SPMI: /* IMPI 2.0 */ + return 5; + case ACPITAB_HPET: /* Currently 1. Table added in ACPI 2.0 */ + return 1; + case ACPITAB_VFCT: /* ACPI 2.0/3.0/4.0: 1 */ + return 1; + case ACPITAB_IVRS: + return IVRS_FORMAT_FIXED; + case ACPITAB_DBG2: + return 0; + case ACPITAB_FACS: /* ACPI 2.0/3.0: 1, ACPI 4.0 upto 6.3: 2 */ + return 1; + case ACPITAB_RSDT: /* ACPI 1.0 upto 6.3: 1 */ + return 1; + case ACPITAB_XSDT: /* ACPI 2.0 upto 6.3: 1 */ + return 1; + case ACPITAB_RSDP: /* ACPI 2.0 upto 6.3: 2 */ + return 2; + case ACPITAB_HEST: + return 1; + case ACPITAB_NHLT: + return 5; + case ACPITAB_BERT: + return 1; + default: + return -EINVAL; + } +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index e3519d4689..e65295b7ca 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -53,3 +54,16 @@ static int dm_test_acpi_get_name(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_get_name, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_get_table_revision() */ +static int dm_test_acpi_get_table_revision(struct unit_test_state *uts) +{ + ut_asserteq(1, acpi_get_table_revision(ACPITAB_MCFG)); + ut_asserteq(2, acpi_get_table_revision(ACPITAB_RSDP)); + ut_asserteq(4, acpi_get_table_revision(ACPITAB_TPM2)); + ut_asserteq(-EINVAL, acpi_get_table_revision(ACPITAB_COUNT)); + + return 0; +} +DM_TEST(dm_test_acpi_get_table_revision, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243391 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:42 -0600 Subject: [PATCH v2 18/39] acpi: Add support for DMAR In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.18.Iffd9fd365ee53abc99a8f1e85c40d30c9368c19d@changeid> The DMA Remapping Reporting (DMAR) table contains information about DMA remapping. Add a version simple version of this table with only the minimum fields filled out. i.e. no entries. Reviewed-by: Bin Meng Signed-off-by: Simon Glass --- Changes in v2: - Drop two unnecessary __packed - Move __packed to after struct include/acpi_table.h | 57 +++++++++++++++++++++++++++++++++++++++++++ lib/acpi/acpi_table.c | 26 ++++++++++++++++++++ test/dm/acpi.c | 14 +++++++++++ 3 files changed, 97 insertions(+) diff --git a/include/acpi_table.h b/include/acpi_table.h index ccf6fa04db..db84b79be5 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -21,6 +21,9 @@ #define ACPI_RSDP_REV_ACPI_1_0 0 #define ACPI_RSDP_REV_ACPI_2_0 2 +/* TODO(sjg at chromium.org): Figure out how to get compiler revision */ +#define ASL_REVISION 0 + #if !defined(__ACPI__) /* @@ -360,6 +363,51 @@ struct acpi_csrt_shared_info { u32 max_block_size; }; +enum dmar_type { + DMAR_DRHD = 0, + DMAR_RMRR = 1, + DMAR_ATSR = 2, + DMAR_RHSA = 3, + DMAR_ANDD = 4 +}; + +enum { + DRHD_INCLUDE_PCI_ALL = 1 +}; + +enum dmar_flags { + DMAR_INTR_REMAP = 1 << 0, + DMAR_X2APIC_OPT_OUT = 1 << 1, + DMA_CTRL_PLATFORM_OPT_IN_FLAG = 1 << 2, +}; + +struct dmar_entry { + u16 type; + u16 length; + u8 flags; + u8 reserved; + u16 segment; + u64 bar; +}; + +struct dmar_rmrr_entry { + u16 type; + u16 length; + u16 reserved; + u16 segment; + u64 bar; + u64 limit; +}; + +/* DMAR (DMA Remapping Reporting Structure) */ +struct __packed acpi_dmar { + struct acpi_table_header header; + u8 host_address_width; + u8 flags; + u8 reserved[10]; + struct dmar_entry structure[0]; +}; + /* DBG2 definitions are partially used for SPCR interface_type */ /* Types for port_type field */ @@ -452,6 +500,15 @@ enum acpi_tables { */ int acpi_get_table_revision(enum acpi_tables table); +/** + * acpi_create_dmar() - Create a DMA Remapping Reporting (DMAR) table + * + * @dmar: Place to put the table + * @flags: DMAR flags to use + * @return 0 if OK, -ve on error + */ +int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags); + #endif /* !__ACPI__*/ #include diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 197f965c08..ed312ac663 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -6,7 +6,33 @@ */ #include +#include #include +#include + +int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags) +{ + struct acpi_table_header *header = &dmar->header; + struct cpu_info info; + struct udevice *cpu; + int ret; + + ret = uclass_first_device(UCLASS_CPU, &cpu); + if (ret) + return log_msg_ret("cpu", ret); + ret = cpu_get_info(cpu, &info); + memset((void *)dmar, 0, sizeof(struct acpi_dmar)); + + /* Fill out header fields. */ + acpi_fill_header(&dmar->header, "DMAR"); + header->length = sizeof(struct acpi_dmar); + header->revision = acpi_get_table_revision(ACPITAB_DMAR); + + dmar->host_address_width = info.address_width - 1; + dmar->flags = flags; + + return 0; +} int acpi_get_table_revision(enum acpi_tables table) { diff --git a/test/dm/acpi.c b/test/dm/acpi.c index e65295b7ca..2737896643 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -67,3 +67,17 @@ static int dm_test_acpi_get_table_revision(struct unit_test_state *uts) } DM_TEST(dm_test_acpi_get_table_revision, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_create_dmar() */ +static int dm_test_acpi_create_dmar(struct unit_test_state *uts) +{ + struct acpi_dmar dmar; + + ut_assertok(acpi_create_dmar(&dmar, DMAR_INTR_REMAP)); + ut_asserteq(DMAR_INTR_REMAP, dmar.flags); + ut_asserteq(DMAR_INTR_REMAP, dmar.flags); + ut_asserteq(32 - 1, dmar.host_address_width); + + return 0; +} +DM_TEST(dm_test_acpi_create_dmar, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243395 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:43 -0600 Subject: [PATCH v2 19/39] acpi: Move acpi_fill_header() to generic code In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.19.Iadbc6a54ae1387847aabfdb8e2c10e040fb1bf77@changeid> This function needs to be used by sandbox for tests. Move it into the generic directory. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Wolfgang Wallner --- Changes in v2: None arch/x86/lib/acpi_table.c | 9 --------- include/acpi_table.h | 10 ++++++++++ lib/acpi/acpi_table.c | 10 ++++++++++ test/dm/acpi.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 71913b6f65..487fef87f2 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -60,15 +60,6 @@ static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, sizeof(struct acpi_rsdp)); } -void acpi_fill_header(struct acpi_table_header *header, char *signature) -{ - memcpy(header->signature, signature, 4); - memcpy(header->oem_id, OEM_ID, 6); - memcpy(header->oem_table_id, OEM_TABLE_ID, 8); - header->oem_revision = U_BOOT_BUILD_DATE; - memcpy(header->aslc_id, ASLC_ID, 4); -} - static void acpi_write_rsdt(struct acpi_rsdt *rsdt) { struct acpi_table_header *header = &(rsdt->header); diff --git a/include/acpi_table.h b/include/acpi_table.h index db84b79be5..3fd2ef16b0 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -509,6 +509,16 @@ int acpi_get_table_revision(enum acpi_tables table); */ int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags); +/** + * acpi_fill_header() - Set up a new table header + * + * This sets all fields except length, revision, checksum and aslc_revision + * + * @header: ACPI header to update + * @signature: Table signature to use (4 characters) + */ +void acpi_fill_header(struct acpi_table_header *header, char *signature); + #endif /* !__ACPI__*/ #include diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index ed312ac663..a86bfa6187 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -9,6 +9,7 @@ #include #include #include +#include int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags) { @@ -84,3 +85,12 @@ int acpi_get_table_revision(enum acpi_tables table) return -EINVAL; } } + +void acpi_fill_header(struct acpi_table_header *header, char *signature) +{ + memcpy(header->signature, signature, 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, OEM_TABLE_ID, 8); + header->oem_revision = U_BOOT_BUILD_DATE; + memcpy(header->aslc_id, ASLC_ID, 4); +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 2737896643..e28ebf4f90 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -81,3 +82,30 @@ static int dm_test_acpi_create_dmar(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_create_dmar, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_fill_header() */ +static int dm_test_acpi_fill_header(struct unit_test_state *uts) +{ + struct acpi_table_header hdr; + + /* Make sure these 5 fields are not changed */ + hdr.length = 0x11; + hdr.revision = 0x22; + hdr.checksum = 0x33; + hdr.aslc_revision = 0x44; + acpi_fill_header(&hdr, "ABCD"); + + ut_assertok(memcmp("ABCD", hdr.signature, sizeof(hdr.signature))); + ut_asserteq(0x11, hdr.length); + ut_asserteq(0x22, hdr.revision); + ut_asserteq(0x33, hdr.checksum); + ut_assertok(memcmp(OEM_ID, hdr.oem_id, sizeof(hdr.oem_id))); + ut_assertok(memcmp(OEM_TABLE_ID, hdr.oem_table_id, + sizeof(hdr.oem_table_id))); + ut_asserteq(U_BOOT_BUILD_DATE, hdr.oem_revision); + ut_assertok(memcmp(ASLC_ID, hdr.aslc_id, sizeof(hdr.aslc_id))); + ut_asserteq(0x44, hdr.aslc_revision); + + return 0; +} +DM_TEST(dm_test_acpi_fill_header, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243394 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:44 -0600 Subject: [PATCH v2 20/39] acpi: Add a method to write tables for a device In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.20.I04c496e973a0d5eb09ed6f6e4674c300fc34b605@changeid> A device may want to write out ACPI tables to describe itself to Linux. Add a method to permit this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: - Drop definition of ACPI_TABLE_CREATOR - Make _acpi_write_dev_tables() static and switch argument order - Generalise the ACPI function recursion with acpi_recurse_method() arch/sandbox/dts/test.dts | 4 +++ drivers/core/acpi.c | 61 +++++++++++++++++++++++++++++++++++++++ include/dm/acpi.h | 30 +++++++++++++++++++ test/dm/acpi.c | 43 +++++++++++++++++++++++++++ 4 files changed, 138 insertions(+) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 5fa951ad4b..1204c14b07 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -210,6 +210,10 @@ compatible = "denx,u-boot-acpi-test"; }; + acpi-test2 { + compatible = "denx,u-boot-acpi-test"; + }; + clocks { clk_fixed: clk-fixed { compatible = "fixed-clock"; diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 45542199f5..10bac38c1d 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -11,8 +11,17 @@ #include #include #include +#include #include +/* Type of method to call */ +enum method_t { + METHOD_WRITE_TABLES, +}; + +/* Prototype for all methods */ +typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx); + int acpi_return_name(char *out_name, const char *name) { strcpy(out_name, name); @@ -30,3 +39,55 @@ int acpi_get_name(const struct udevice *dev, char *out_name) return -ENOSYS; } + +acpi_method acpi_get_method(struct udevice *dev, enum method_t method) +{ + struct acpi_ops *aops; + + aops = device_get_acpi_ops(dev); + if (aops) { + switch (method) { + case METHOD_WRITE_TABLES: + return aops->write_tables; + } + } + + return NULL; +} + +int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, + enum method_t method) +{ + struct udevice *dev; + acpi_method func; + int ret; + + func = acpi_get_method(parent, method); + if (func) { + log_debug("\n- %s %p\n", parent->name, func); + ret = device_ofdata_to_platdata(parent); + if (ret) + return log_msg_ret("ofdata", ret); + ret = func(parent, ctx); + if (ret) + return log_msg_ret("func", ret); + } + device_foreach_child(dev, parent) { + ret = acpi_recurse_method(ctx, dev, method); + if (ret) + return log_msg_ret("recurse", ret); + } + + return 0; +} + +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); + log_debug("Writing finished, err=%d\n", ret); + + return ret; +} diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 8d6c3fd424..dcfcf5c347 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -24,6 +24,17 @@ #if !defined(__ACPI__) +/** + * struct acpi_ctx - Context used for writing ACPI tables + * + * This contains a few useful pieces of information used when writing + * + * @current: Current address for writing + */ +struct acpi_ctx { + void *current; +}; + /** * struct acpi_ops - ACPI operations supported by driver model */ @@ -38,6 +49,15 @@ struct acpi_ops { * other error */ int (*get_name)(const struct udevice *dev, char *out_name); + + /** + * write_tables() - Write out any tables required by this device + * + * @dev: Device to write + * @ctx: ACPI context to use + * @return 0 if OK, -ve on error + */ + int (*write_tables)(const struct udevice *dev, struct acpi_ctx *ctx); }; #define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops) @@ -72,6 +92,16 @@ int acpi_get_name(const struct udevice *dev, char *out_name); */ int acpi_return_name(char *out_name, const char *name); +/** + * acpi_write_dev_tables() - Write ACPI tables required by devices + * + * This scans through all devices and tells them to write any tables they want + * to write. + * + * @return 0 if OK, -ve if any device returned an error + */ +int acpi_write_dev_tables(struct acpi_ctx *ctx); + #endif /* __ACPI__ */ #endif diff --git a/test/dm/acpi.c b/test/dm/acpi.c index e28ebf4f90..b87fbd16b0 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -15,6 +15,19 @@ #include #define ACPI_TEST_DEV_NAME "ABCD" +#define BUF_SIZE 4096 + +static int testacpi_write_tables(const struct udevice *dev, + struct acpi_ctx *ctx) +{ + struct acpi_dmar *dmar; + + dmar = (struct acpi_dmar *)ctx->current; + acpi_create_dmar(dmar, DMAR_INTR_REMAP); + ctx->current += sizeof(struct acpi_dmar); + + return 0; +} static int testacpi_get_name(const struct udevice *dev, char *out_name) { @@ -23,6 +36,7 @@ static int testacpi_get_name(const struct udevice *dev, char *out_name) struct acpi_ops testacpi_ops = { .get_name = testacpi_get_name, + .write_tables = testacpi_write_tables, }; static const struct udevice_id testacpi_ids[] = { @@ -109,3 +123,32 @@ static int dm_test_acpi_fill_header(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_fill_header, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test ACPI write_tables() */ +static int dm_test_acpi_write_tables(struct unit_test_state *uts) +{ + struct acpi_dmar *dmar; + struct acpi_ctx ctx; + void *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + ctx.current = buf; + ut_assertok(acpi_write_dev_tables(&ctx)); + dmar = buf; + + /* + * We should have two dmar tables, one for each "denx,u-boot-acpi-test" + * device + */ + ut_asserteq_ptr(dmar + 2, 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); + + return 0; +} +DM_TEST(dm_test_acpi_write_tables, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243397 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:45 -0600 Subject: [PATCH v2 21/39] acpi: Convert part of acpi_table to use acpi_ctx In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.21.I93e1e33891714417335e1dd517982b18bf9f882f@changeid> The current code uses an address but a pointer would result in fewer casts. Also it repeats the alignment code in a lot of places so this would be better done in a helper function. Update write_acpi_tables() to make use of the new acpi_ctx structure, adding a few helpers to clean things up. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: None arch/x86/lib/acpi_table.c | 88 +++++++++++++++++++-------------------- include/acpi_table.h | 36 ++++++++++++++++ lib/acpi/acpi_table.c | 22 ++++++++++ test/dm/acpi.c | 28 +++++++++++++ 4 files changed, 129 insertions(+), 45 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 487fef87f2..8e13d6a3e6 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include /* * IASL compiles the dsdt entries and writes the hex values @@ -468,9 +470,9 @@ static void acpi_create_spcr(struct acpi_spcr *spcr) /* * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c */ -ulong write_acpi_tables(ulong start) +ulong write_acpi_tables(ulong start_addr) { - u32 current; + struct acpi_ctx sctx, *ctx = &sctx; struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; @@ -481,60 +483,61 @@ ulong write_acpi_tables(ulong start) struct acpi_madt *madt; struct acpi_csrt *csrt; struct acpi_spcr *spcr; + void *start; + ulong addr; int i; - current = start; + start = map_sysmem(start_addr, 0); + ctx->current = start; /* Align ACPI tables to 16 byte */ - current = ALIGN(current, 16); + acpi_align(ctx); - debug("ACPI: Writing ACPI tables at %lx\n", start); + debug("ACPI: Writing ACPI tables at %lx\n", start_addr); /* We need at least an RSDP and an RSDT Table */ - rsdp = (struct acpi_rsdp *)current; - current += sizeof(struct acpi_rsdp); - current = ALIGN(current, 16); - rsdt = (struct acpi_rsdt *)current; - current += sizeof(struct acpi_rsdt); - current = ALIGN(current, 16); - xsdt = (struct acpi_xsdt *)current; - current += sizeof(struct acpi_xsdt); + rsdp = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); + rsdt = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_rsdt)); + xsdt = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); /* * Per ACPI spec, the FACS table address must be aligned to a 64 byte * boundary (Windows checks this, but Linux does not). */ - current = ALIGN(current, 64); + acpi_align_large(ctx); /* clear all table memory */ - memset((void *)start, 0, current - start); + memset((void *)start, 0, ctx->current - start); acpi_write_rsdp(rsdp, rsdt, xsdt); acpi_write_rsdt(rsdt); acpi_write_xsdt(xsdt); debug("ACPI: * FACS\n"); - facs = (struct acpi_facs *)current; - current += sizeof(struct acpi_facs); - current = ALIGN(current, 16); + facs = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_facs)); acpi_create_facs(facs); debug("ACPI: * DSDT\n"); - dsdt = (struct acpi_table_header *)current; + dsdt = ctx->current; memcpy(dsdt, &AmlCode, sizeof(struct acpi_table_header)); - current += sizeof(struct acpi_table_header); - memcpy((char *)current, + acpi_inc(ctx, sizeof(struct acpi_table_header)); + memcpy(ctx->current, (char *)&AmlCode + sizeof(struct acpi_table_header), dsdt->length - sizeof(struct acpi_table_header)); - current += dsdt->length - sizeof(struct acpi_table_header); - current = ALIGN(current, 16); + acpi_inc_align(ctx, dsdt->length - sizeof(struct acpi_table_header)); /* Pack GNVS into the ACPI table area */ for (i = 0; i < dsdt->length; i++) { u32 *gnvs = (u32 *)((u32)dsdt + i); if (*gnvs == ACPI_GNVS_ADDR) { - debug("Fix up global NVS in DSDT to 0x%08x\n", current); - *gnvs = current; + ulong addr = (ulong)map_to_sysmem(ctx->current); + + debug("Fix up global NVS in DSDT to %#08lx\n", addr); + *gnvs = addr; break; } } @@ -544,51 +547,46 @@ ulong write_acpi_tables(ulong start) dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length); /* Fill in platform-specific global NVS variables */ - acpi_create_gnvs((struct acpi_global_nvs *)current); - current += sizeof(struct acpi_global_nvs); - current = ALIGN(current, 16); + acpi_create_gnvs(ctx->current); + acpi_inc_align(ctx, sizeof(struct acpi_global_nvs)); debug("ACPI: * FADT\n"); - fadt = (struct acpi_fadt *)current; - current += sizeof(struct acpi_fadt); - current = ALIGN(current, 16); + fadt = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_fadt)); acpi_create_fadt(fadt, facs, dsdt); acpi_add_table(rsdp, fadt); debug("ACPI: * MADT\n"); - madt = (struct acpi_madt *)current; + madt = ctx->current; acpi_create_madt(madt); - current += madt->header.length; + acpi_inc_align(ctx, madt->header.length); acpi_add_table(rsdp, madt); - current = ALIGN(current, 16); debug("ACPI: * MCFG\n"); - mcfg = (struct acpi_mcfg *)current; + mcfg = ctx->current; acpi_create_mcfg(mcfg); - current += mcfg->header.length; + acpi_inc_align(ctx, mcfg->header.length); acpi_add_table(rsdp, mcfg); - current = ALIGN(current, 16); debug("ACPI: * CSRT\n"); - csrt = (struct acpi_csrt *)current; + csrt = ctx->current; acpi_create_csrt(csrt); - current += csrt->header.length; + acpi_inc_align(ctx, csrt->header.length); acpi_add_table(rsdp, csrt); - current = ALIGN(current, 16); debug("ACPI: * SPCR\n"); - spcr = (struct acpi_spcr *)current; + spcr = ctx->current; acpi_create_spcr(spcr); - current += spcr->header.length; + acpi_inc_align(ctx, spcr->header.length); acpi_add_table(rsdp, spcr); - current = ALIGN(current, 16); - debug("current = %x\n", current); + addr = map_to_sysmem(ctx->current); + debug("current = %lx\n", addr); acpi_rsdp_addr = (unsigned long)rsdp; debug("ACPI: done\n"); - return current; + return addr; } ulong acpi_get_rsdp_addr(void) diff --git a/include/acpi_table.h b/include/acpi_table.h index 3fd2ef16b0..5fd0fa71a6 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -26,6 +26,8 @@ #if !defined(__ACPI__) +struct acpi_ctx; + /* * RSDP (Root System Description Pointer) * Note: ACPI 1.0 didn't have length, xsdt_address, and ext_checksum @@ -519,6 +521,40 @@ int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags); */ void acpi_fill_header(struct acpi_table_header *header, char *signature); +/** + * acpi_align() - Align the ACPI output pointer to a 16-byte boundary + * + * @ctx: ACPI context + */ +void acpi_align(struct acpi_ctx *ctx); + +/** + * acpi_align_large() - Align the ACPI output pointer to a 64-byte boundary + * + * @ctx: ACPI context + */ +void acpi_align_large(struct acpi_ctx *ctx); + +/** + * acpi_inc() - Increment the ACPI output pointer by a bit + * + * The pointer is NOT aligned afterwards. + * + * @ctx: ACPI context + * @amount: Amount to increment by + */ +void acpi_inc(struct acpi_ctx *ctx, uint amount); + +/** + * acpi_inc_align() - Increment the ACPI output pointer by a bit and align + * + * The pointer is aligned afterwards. + * + * @ctx: ACPI context + * @amount: Amount to increment by + */ +void acpi_inc_align(struct acpi_ctx *ctx, uint amount); + #endif /* !__ACPI__*/ #include diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index a86bfa6187..3d24cc26b6 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -10,6 +10,7 @@ #include #include #include +#include int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags) { @@ -94,3 +95,24 @@ void acpi_fill_header(struct acpi_table_header *header, char *signature) header->oem_revision = U_BOOT_BUILD_DATE; memcpy(header->aslc_id, ASLC_ID, 4); } + +void acpi_align(struct acpi_ctx *ctx) +{ + ctx->current = (void *)ALIGN((ulong)ctx->current, 16); +} + +void acpi_align_large(struct acpi_ctx *ctx) +{ + ctx->current = (void *)ALIGN((ulong)ctx->current, 64); +} + +void acpi_inc(struct acpi_ctx *ctx, uint amount) +{ + ctx->current += amount; +} + +void acpi_inc_align(struct acpi_ctx *ctx, uint amount) +{ + ctx->current += amount; + acpi_align(ctx); +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index b87fbd16b0..0bd7e51ac9 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -152,3 +152,31 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_write_tables, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test basic ACPI functions */ +static int dm_test_acpi_basic(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + + /* Check align works */ + ctx.current = (void *)5; + acpi_align(&ctx); + ut_asserteq_ptr((void *)16, ctx.current); + + /* Check that align does nothing if already aligned */ + acpi_align(&ctx); + ut_asserteq_ptr((void *)16, ctx.current); + acpi_align_large(&ctx); + ut_asserteq_ptr((void *)64, ctx.current); + acpi_align_large(&ctx); + ut_asserteq_ptr((void *)64, ctx.current); + + /* Check incrementing */ + acpi_inc(&ctx, 3); + ut_asserteq_ptr((void *)67, ctx.current); + acpi_inc_align(&ctx, 3); + ut_asserteq_ptr((void *)80, ctx.current); + + return 0; +} +DM_TEST(dm_test_acpi_basic, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243393 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:46 -0600 Subject: [PATCH v2 22/39] x86: Allow devices to write ACPI tables In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.22.I6f54ae1deb9a3f954441d072e5d591d7cdb4ed5d@changeid> Call the new core function to permit devices to write their own ACPI tables. These tables will appear after all other tables. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: None arch/x86/lib/acpi_table.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 8e13d6a3e6..964f10a7cb 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -580,6 +580,8 @@ ulong write_acpi_tables(ulong start_addr) acpi_inc_align(ctx, spcr->header.length); acpi_add_table(rsdp, spcr); + acpi_write_dev_tables(ctx); + addr = map_to_sysmem(ctx->current); debug("current = %lx\n", addr); From patchwork Mon Mar 9 03:44:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243396 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:47 -0600 Subject: [PATCH v2 23/39] acpi: Drop code for missing XSDT from acpi_write_rsdp() In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.23.I4566efe7db4c618499115f6973d851e0aa502da9@changeid> We don't actually support tables without an XSDT so we can drop this dead code. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: None arch/x86/lib/acpi_table.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 964f10a7cb..0757ac3431 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -42,19 +42,8 @@ static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, rsdp->length = sizeof(struct acpi_rsdp); rsdp->rsdt_address = (u32)rsdt; - /* - * Revision: ACPI 1.0: 0, ACPI 2.0/3.0/4.0: 2 - * - * Some OSes expect an XSDT to be present for RSD PTR revisions >= 2. - * If we don't have an ACPI XSDT, force ACPI 1.0 (and thus RSD PTR - * revision 0) - */ - if (xsdt == NULL) { - rsdp->revision = ACPI_RSDP_REV_ACPI_1_0; - } else { - rsdp->xsdt_address = (u64)(u32)xsdt; - rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; - } + rsdp->xsdt_address = (u64)(u32)xsdt; + rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; /* Calculate checksums */ rsdp->checksum = table_compute_checksum((void *)rsdp, 20); From patchwork Mon Mar 9 03:44:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243398 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:48 -0600 Subject: [PATCH v2 24/39] acpi: Move acpi_add_table() to generic code In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.24.Ie9d79ce7293bd55022f7c6e940ce3583d38353da@changeid> Move this code to a generic location so that we can test it with sandbox. This requires adding a few new fields to acpi_ctx, so drop the local variables used in the original code. Also use mapmem to avoid pointer-to-address casts which don't work on sandbox. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: None arch/x86/lib/acpi_table.c | 82 +++++---------------------------------- include/acpi_table.h | 9 +++++ include/dm/acpi.h | 5 +++ lib/acpi/acpi_table.c | 64 ++++++++++++++++++++++++++++++ test/dm/acpi.c | 4 ++ 5 files changed, 92 insertions(+), 72 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 0757ac3431..9168119547 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -83,66 +83,6 @@ static void acpi_write_xsdt(struct acpi_xsdt *xsdt) sizeof(struct acpi_xsdt)); } -/** - * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length - * and checksum. - */ -static void acpi_add_table(struct acpi_rsdp *rsdp, void *table) -{ - int i, entries_num; - struct acpi_rsdt *rsdt; - struct acpi_xsdt *xsdt = NULL; - - /* The RSDT is mandatory while the XSDT is not */ - rsdt = (struct acpi_rsdt *)rsdp->rsdt_address; - - if (rsdp->xsdt_address) - xsdt = (struct acpi_xsdt *)((u32)rsdp->xsdt_address); - - /* This should always be MAX_ACPI_TABLES */ - entries_num = ARRAY_SIZE(rsdt->entry); - - for (i = 0; i < entries_num; i++) { - if (rsdt->entry[i] == 0) - break; - } - - if (i >= entries_num) { - debug("ACPI: Error: too many tables\n"); - return; - } - - /* Add table to the RSDT */ - rsdt->entry[i] = (u32)table; - - /* Fix RSDT length or the kernel will assume invalid entries */ - rsdt->header.length = sizeof(struct acpi_table_header) + - (sizeof(u32) * (i + 1)); - - /* Re-calculate checksum */ - rsdt->header.checksum = 0; - rsdt->header.checksum = table_compute_checksum((u8 *)rsdt, - rsdt->header.length); - - /* - * And now the same thing for the XSDT. We use the same index as for - * now we want the XSDT and RSDT to always be in sync in U-Boot - */ - if (xsdt) { - /* Add table to the XSDT */ - xsdt->entry[i] = (u64)(u32)table; - - /* Fix XSDT length */ - xsdt->header.length = sizeof(struct acpi_table_header) + - (sizeof(u64) * (i + 1)); - - /* Re-calculate checksum */ - xsdt->header.checksum = 0; - xsdt->header.checksum = table_compute_checksum((u8 *)xsdt, - xsdt->header.length); - } -} - static void acpi_create_facs(struct acpi_facs *facs) { memset((void *)facs, 0, sizeof(struct acpi_facs)); @@ -462,8 +402,6 @@ static void acpi_create_spcr(struct acpi_spcr *spcr) ulong write_acpi_tables(ulong start_addr) { struct acpi_ctx sctx, *ctx = &sctx; - struct acpi_rsdp *rsdp; - struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; struct acpi_facs *facs; struct acpi_table_header *dsdt; @@ -485,9 +423,9 @@ ulong write_acpi_tables(ulong start_addr) debug("ACPI: Writing ACPI tables at %lx\n", start_addr); /* We need at least an RSDP and an RSDT Table */ - rsdp = ctx->current; + ctx->rsdp = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); - rsdt = ctx->current; + ctx->rsdt = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_rsdt)); xsdt = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); @@ -500,8 +438,8 @@ ulong write_acpi_tables(ulong start_addr) /* clear all table memory */ memset((void *)start, 0, ctx->current - start); - acpi_write_rsdp(rsdp, rsdt, xsdt); - acpi_write_rsdt(rsdt); + acpi_write_rsdp(ctx->rsdp, ctx->rsdt, xsdt); + acpi_write_rsdt(ctx->rsdt); acpi_write_xsdt(xsdt); debug("ACPI: * FACS\n"); @@ -543,38 +481,38 @@ ulong write_acpi_tables(ulong start_addr) fadt = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_fadt)); acpi_create_fadt(fadt, facs, dsdt); - acpi_add_table(rsdp, fadt); + 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(rsdp, madt); + acpi_add_table(ctx, madt); debug("ACPI: * MCFG\n"); mcfg = ctx->current; acpi_create_mcfg(mcfg); acpi_inc_align(ctx, mcfg->header.length); - acpi_add_table(rsdp, mcfg); + acpi_add_table(ctx, mcfg); debug("ACPI: * CSRT\n"); csrt = ctx->current; acpi_create_csrt(csrt); acpi_inc_align(ctx, csrt->header.length); - acpi_add_table(rsdp, csrt); + acpi_add_table(ctx, csrt); debug("ACPI: * SPCR\n"); spcr = ctx->current; acpi_create_spcr(spcr); acpi_inc_align(ctx, spcr->header.length); - acpi_add_table(rsdp, spcr); + acpi_add_table(ctx, spcr); acpi_write_dev_tables(ctx); addr = map_to_sysmem(ctx->current); debug("current = %lx\n", addr); - acpi_rsdp_addr = (unsigned long)rsdp; + acpi_rsdp_addr = (unsigned long)ctx->rsdp; debug("ACPI: done\n"); return addr; diff --git a/include/acpi_table.h b/include/acpi_table.h index 5fd0fa71a6..2131484880 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -555,6 +555,15 @@ void acpi_inc(struct acpi_ctx *ctx, uint amount); */ void acpi_inc_align(struct acpi_ctx *ctx, uint amount); +/** + * acpi_add_table() - Add a new table to the RSDP and XSDT + * + * @ctx: ACPI context + * @table: Table to add + * @return 0 if OK, -E2BIG if too many tables + */ +int acpi_add_table(struct acpi_ctx *ctx, void *table); + #endif /* !__ACPI__*/ #include diff --git a/include/dm/acpi.h b/include/dm/acpi.h index dcfcf5c347..4465e62848 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -30,9 +30,14 @@ * This contains a few useful pieces of information used when writing * * @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 RSDP and XSDT. + * @rsdt: Pointer to the Root System Description Table */ struct acpi_ctx { void *current; + struct acpi_rsdp *rsdp; + struct acpi_rsdt *rsdt; }; /** diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 3d24cc26b6..00e80ac39a 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -116,3 +118,65 @@ void acpi_inc_align(struct acpi_ctx *ctx, uint amount) ctx->current += amount; acpi_align(ctx); } + +/** + * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length + * and checksum. + */ +int acpi_add_table(struct acpi_ctx *ctx, void *table) +{ + int i, entries_num; + struct acpi_rsdt *rsdt; + struct acpi_xsdt *xsdt = NULL; + + /* The RSDT is mandatory while the XSDT is not */ + rsdt = ctx->rsdt; + + if (ctx->rsdp->xsdt_address) + xsdt = map_sysmem(ctx->rsdp->xsdt_address, 0); + + /* This should always be MAX_ACPI_TABLES */ + entries_num = ARRAY_SIZE(rsdt->entry); + + for (i = 0; i < entries_num; i++) { + if (rsdt->entry[i] == 0) + break; + } + + if (i >= entries_num) { + debug("ACPI: Error: too many tables\n"); + return -E2BIG; + } + + /* Add table to the RSDT */ + rsdt->entry[i] = map_to_sysmem(table); + + /* Fix RSDT length or the kernel will assume invalid entries */ + rsdt->header.length = sizeof(struct acpi_table_header) + + (sizeof(u32) * (i + 1)); + + /* Re-calculate checksum */ + rsdt->header.checksum = 0; + rsdt->header.checksum = table_compute_checksum((u8 *)rsdt, + rsdt->header.length); + + /* + * And now the same thing for the XSDT. We use the same index as for + * now we want the XSDT and RSDT to always be in sync in U-Boot + */ + if (xsdt) { + /* Add table to the XSDT */ + xsdt->entry[i] = map_to_sysmem(table); + + /* Fix XSDT length */ + xsdt->header.length = sizeof(struct acpi_table_header) + + (sizeof(u64) * (i + 1)); + + /* Re-calculate checksum */ + xsdt->header.checksum = 0; + xsdt->header.checksum = + table_compute_checksum((u8 *)xsdt, xsdt->header.length); + } + + return 0; +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 0bd7e51ac9..a2a57a29a6 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -21,10 +21,14 @@ static int testacpi_write_tables(const struct udevice *dev, struct acpi_ctx *ctx) { struct acpi_dmar *dmar; + int ret; dmar = (struct acpi_dmar *)ctx->current; acpi_create_dmar(dmar, DMAR_INTR_REMAP); ctx->current += sizeof(struct acpi_dmar); + ret = acpi_add_table(ctx, dmar); + if (ret) + return log_msg_ret("add", ret); return 0; } From patchwork Mon Mar 9 03:44:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243399 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:49 -0600 Subject: [PATCH v2 25/39] acpi: Put table-setup code in its own function In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.25.I34e9fcd28119cc2fcb87ad8679efb582a4c611df@changeid> We always write three basic tables to ACPI at the start. Move this into its own function, along with acpi_fill_header(), so we can write a test for this code. Signed-off-by: Simon Glass --- Changes in v2: None arch/x86/lib/acpi_table.c | 77 +---------------------------------- include/acpi_table.h | 10 +++++ lib/acpi/acpi_table.c | 86 ++++++++++++++++++++++++++++++++++++++- test/dm/acpi.c | 55 ++++++++++++++++++++++++- 4 files changed, 148 insertions(+), 80 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 9168119547..83b92e2a4c 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -31,58 +31,6 @@ extern const unsigned char AmlCode[]; /* ACPI RSDP address to be used in boot parameters */ static ulong acpi_rsdp_addr; -static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, - struct acpi_xsdt *xsdt) -{ - memset(rsdp, 0, sizeof(struct acpi_rsdp)); - - memcpy(rsdp->signature, RSDP_SIG, 8); - memcpy(rsdp->oem_id, OEM_ID, 6); - - rsdp->length = sizeof(struct acpi_rsdp); - rsdp->rsdt_address = (u32)rsdt; - - rsdp->xsdt_address = (u64)(u32)xsdt; - rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; - - /* Calculate checksums */ - rsdp->checksum = table_compute_checksum((void *)rsdp, 20); - rsdp->ext_checksum = table_compute_checksum((void *)rsdp, - sizeof(struct acpi_rsdp)); -} - -static void acpi_write_rsdt(struct acpi_rsdt *rsdt) -{ - struct acpi_table_header *header = &(rsdt->header); - - /* Fill out header fields */ - acpi_fill_header(header, "RSDT"); - header->length = sizeof(struct acpi_rsdt); - header->revision = 1; - - /* Entries are filled in later, we come with an empty set */ - - /* Fix checksum */ - header->checksum = table_compute_checksum((void *)rsdt, - sizeof(struct acpi_rsdt)); -} - -static void acpi_write_xsdt(struct acpi_xsdt *xsdt) -{ - struct acpi_table_header *header = &(xsdt->header); - - /* Fill out header fields */ - acpi_fill_header(header, "XSDT"); - header->length = sizeof(struct acpi_xsdt); - header->revision = 1; - - /* Entries are filled in later, we come with an empty set */ - - /* Fix checksum */ - header->checksum = table_compute_checksum((void *)xsdt, - sizeof(struct acpi_xsdt)); -} - static void acpi_create_facs(struct acpi_facs *facs) { memset((void *)facs, 0, sizeof(struct acpi_facs)); @@ -402,7 +350,6 @@ static void acpi_create_spcr(struct acpi_spcr *spcr) ulong write_acpi_tables(ulong start_addr) { struct acpi_ctx sctx, *ctx = &sctx; - struct acpi_xsdt *xsdt; struct acpi_facs *facs; struct acpi_table_header *dsdt; struct acpi_fadt *fadt; @@ -415,32 +362,10 @@ ulong write_acpi_tables(ulong start_addr) int i; start = map_sysmem(start_addr, 0); - ctx->current = start; - - /* Align ACPI tables to 16 byte */ - acpi_align(ctx); debug("ACPI: Writing ACPI tables at %lx\n", start_addr); - /* We need at least an RSDP and an RSDT Table */ - ctx->rsdp = ctx->current; - acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); - ctx->rsdt = ctx->current; - acpi_inc_align(ctx, sizeof(struct acpi_rsdt)); - xsdt = ctx->current; - acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); - /* - * Per ACPI spec, the FACS table address must be aligned to a 64 byte - * boundary (Windows checks this, but Linux does not). - */ - acpi_align_large(ctx); - - /* clear all table memory */ - memset((void *)start, 0, ctx->current - start); - - acpi_write_rsdp(ctx->rsdp, ctx->rsdt, xsdt); - acpi_write_rsdt(ctx->rsdt); - acpi_write_xsdt(xsdt); + acpi_setup_base_tables(ctx, start); debug("ACPI: * FACS\n"); facs = ctx->current; diff --git a/include/acpi_table.h b/include/acpi_table.h index 2131484880..f500f0d3fe 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -564,6 +564,16 @@ void acpi_inc_align(struct acpi_ctx *ctx, uint amount); */ int acpi_add_table(struct acpi_ctx *ctx, void *table); +/** + * acpi_setup_base_tables() - Set up context along with RSDP, RSDT and XDST + * + * Set up the context with the given start position. Some basic tables are + * always needed, so set them up as well. + * + * @ctx: Context to set up + */ +void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start); + #endif /* !__ACPI__*/ #include diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 00e80ac39a..9f452a65ce 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -157,7 +157,7 @@ int acpi_add_table(struct acpi_ctx *ctx, void *table) /* Re-calculate checksum */ rsdt->header.checksum = 0; - rsdt->header.checksum = table_compute_checksum((u8 *)rsdt, + rsdt->header.checksum = table_compute_checksum(rsdt, rsdt->header.length); /* @@ -175,8 +175,90 @@ int acpi_add_table(struct acpi_ctx *ctx, void *table) /* Re-calculate checksum */ xsdt->header.checksum = 0; xsdt->header.checksum = - table_compute_checksum((u8 *)xsdt, xsdt->header.length); + table_compute_checksum(xsdt, xsdt->header.length); } return 0; } + +static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, + struct acpi_xsdt *xsdt) +{ + memset(rsdp, 0, sizeof(struct acpi_rsdp)); + + memcpy(rsdp->signature, RSDP_SIG, 8); + memcpy(rsdp->oem_id, OEM_ID, 6); + + rsdp->length = sizeof(struct acpi_rsdp); + rsdp->rsdt_address = map_to_sysmem(rsdt); + + rsdp->xsdt_address = map_to_sysmem(xsdt); + rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; + + /* Calculate checksums */ + rsdp->checksum = table_compute_checksum(rsdp, 20); + rsdp->ext_checksum = table_compute_checksum(rsdp, + sizeof(struct acpi_rsdp)); +} + +static void acpi_write_rsdt(struct acpi_rsdt *rsdt) +{ + struct acpi_table_header *header = &rsdt->header; + + /* Fill out header fields */ + acpi_fill_header(header, "RSDT"); + header->length = sizeof(struct acpi_rsdt); + header->revision = 1; + + /* Entries are filled in later, we come with an empty set */ + + /* Fix checksum */ + header->checksum = table_compute_checksum(rsdt, + sizeof(struct acpi_rsdt)); +} + +static void acpi_write_xsdt(struct acpi_xsdt *xsdt) +{ + struct acpi_table_header *header = &xsdt->header; + + /* Fill out header fields */ + acpi_fill_header(header, "XSDT"); + header->length = sizeof(struct acpi_xsdt); + header->revision = 1; + + /* Entries are filled in later, we come with an empty set */ + + /* Fix checksum */ + header->checksum = table_compute_checksum(xsdt, + sizeof(struct acpi_xsdt)); +} + +void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) +{ + struct acpi_xsdt *xsdt; + + ctx->current = start; + + /* Align ACPI tables to 16 byte */ + acpi_align(ctx); + + /* We need at least an RSDP and an RSDT Table */ + ctx->rsdp = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); + ctx->rsdt = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_rsdt)); + xsdt = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); + /* + * Per ACPI spec, the FACS table address must be aligned to a 64 byte + * boundary (Windows checks this, but Linux does not). + */ + acpi_align_large(ctx); + + /* clear all table memory */ + memset((void *)start, '\0', ctx->current - start); + + acpi_write_rsdp(ctx->rsdp, ctx->rsdt, xsdt); + acpi_write_rsdt(ctx->rsdt); + acpi_write_xsdt(xsdt); +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index a2a57a29a6..bb66c7229c 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -9,6 +9,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -138,9 +141,9 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) buf = malloc(BUF_SIZE); ut_assertnonnull(buf); - ctx.current = buf; + acpi_setup_base_tables(&ctx, buf); + dmar = ctx.current; ut_assertok(acpi_write_dev_tables(&ctx)); - dmar = buf; /* * We should have two dmar tables, one for each "denx,u-boot-acpi-test" @@ -153,6 +156,11 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) 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]); + return 0; } DM_TEST(dm_test_acpi_write_tables, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); @@ -184,3 +192,46 @@ static int dm_test_acpi_basic(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_basic, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test acpi_setup_base_tables */ +static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) +{ + struct acpi_rsdp *rsdp; + struct acpi_rsdt *rsdt; + struct acpi_xsdt *xsdt; + struct acpi_ctx ctx; + void *buf, *end; + + /* Use an unaligned address deliberately */ + buf = memalign(64, BUF_SIZE); + ut_assertnonnull(buf); + acpi_setup_base_tables(&ctx, buf + 4); + + rsdp = buf + 16; + ut_asserteq_ptr(rsdp, ctx.rsdp); + ut_assertok(memcmp(RSDP_SIG, rsdp->signature, sizeof(rsdp->signature))); + ut_asserteq(sizeof(*rsdp), rsdp->length); + ut_assertok(table_compute_checksum(rsdp, 20)); + ut_assertok(table_compute_checksum(rsdp, sizeof(*rsdp))); + + rsdt = PTR_ALIGN((void *)rsdp + sizeof(*rsdp), 16); + ut_asserteq_ptr(rsdt, ctx.rsdt); + ut_assertok(memcmp("RSDT", rsdt->header.signature, ACPI_NAME_LEN)); + ut_asserteq(sizeof(*rsdt), rsdt->header.length); + ut_assertok(table_compute_checksum(rsdt, sizeof(*rsdt))); + + xsdt = PTR_ALIGN((void *)rsdt + sizeof(*rsdt), 16); + ut_assertok(memcmp("XSDT", xsdt->header.signature, ACPI_NAME_LEN)); + ut_asserteq(sizeof(*xsdt), xsdt->header.length); + ut_assertok(table_compute_checksum(xsdt, sizeof(*xsdt))); + + end = PTR_ALIGN((void *)xsdt + sizeof(*xsdt), 64); + ut_asserteq_ptr(end, ctx.current); + + ut_asserteq(map_to_sysmem(rsdt), rsdp->rsdt_address); + ut_asserteq(map_to_sysmem(xsdt), rsdp->xsdt_address); + + return 0; +} +DM_TEST(dm_test_acpi_setup_base_tables, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243401 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:50 -0600 Subject: [PATCH v2 26/39] acpi: Move the xsdt pointer to acpi_ctx In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.26.Ib392027f44b50a6cfdad4ce529b8479958cb302d@changeid> Put this in the context along with the other important pointers. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: None include/dm/acpi.h | 2 ++ lib/acpi/acpi_table.c | 38 ++++++++++++++++---------------------- test/dm/acpi.c | 5 +++++ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 4465e62848..f3e9d73b78 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -33,11 +33,13 @@ * @rsdp: Pointer to the Root System Description Pointer, typically used when * adding a new table. The RSDP holds pointers to the RSDP and XSDT. * @rsdt: Pointer to the Root System Description Table + * @xsdt: Pointer to the Extended System Description Table */ struct acpi_ctx { void *current; struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; + struct acpi_xsdt *xsdt; }; /** diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 9f452a65ce..be52b3ac8e 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -127,13 +127,11 @@ int acpi_add_table(struct acpi_ctx *ctx, void *table) { int i, entries_num; struct acpi_rsdt *rsdt; - struct acpi_xsdt *xsdt = NULL; + struct acpi_xsdt *xsdt; /* The RSDT is mandatory while the XSDT is not */ rsdt = ctx->rsdt; - - if (ctx->rsdp->xsdt_address) - xsdt = map_sysmem(ctx->rsdp->xsdt_address, 0); + xsdt = ctx->xsdt; /* This should always be MAX_ACPI_TABLES */ entries_num = ARRAY_SIZE(rsdt->entry); @@ -164,19 +162,17 @@ int acpi_add_table(struct acpi_ctx *ctx, void *table) * And now the same thing for the XSDT. We use the same index as for * now we want the XSDT and RSDT to always be in sync in U-Boot */ - if (xsdt) { - /* Add table to the XSDT */ - xsdt->entry[i] = map_to_sysmem(table); - - /* Fix XSDT length */ - xsdt->header.length = sizeof(struct acpi_table_header) + - (sizeof(u64) * (i + 1)); - - /* Re-calculate checksum */ - xsdt->header.checksum = 0; - xsdt->header.checksum = - table_compute_checksum(xsdt, xsdt->header.length); - } + /* Add table to the XSDT */ + xsdt->entry[i] = map_to_sysmem(table); + + /* Fix XSDT length */ + xsdt->header.length = sizeof(struct acpi_table_header) + + (sizeof(u64) * (i + 1)); + + /* Re-calculate checksum */ + xsdt->header.checksum = 0; + xsdt->header.checksum = table_compute_checksum(xsdt, + xsdt->header.length); return 0; } @@ -235,8 +231,6 @@ static void acpi_write_xsdt(struct acpi_xsdt *xsdt) void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) { - struct acpi_xsdt *xsdt; - ctx->current = start; /* Align ACPI tables to 16 byte */ @@ -247,7 +241,7 @@ void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); ctx->rsdt = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_rsdt)); - xsdt = ctx->current; + ctx->xsdt = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); /* * Per ACPI spec, the FACS table address must be aligned to a 64 byte @@ -258,7 +252,7 @@ void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) /* clear all table memory */ memset((void *)start, '\0', ctx->current - start); - acpi_write_rsdp(ctx->rsdp, ctx->rsdt, xsdt); + acpi_write_rsdp(ctx->rsdp, ctx->rsdt, ctx->xsdt); acpi_write_rsdt(ctx->rsdt); - acpi_write_xsdt(xsdt); + acpi_write_xsdt(ctx->xsdt); } diff --git a/test/dm/acpi.c b/test/dm/acpi.c index bb66c7229c..a60f67e73d 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -161,6 +161,10 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) ut_asserteq(map_to_sysmem(dmar + 1), ctx.rsdt->entry[1]); ut_asserteq(0, ctx.rsdt->entry[2]); + 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]); + return 0; } DM_TEST(dm_test_acpi_write_tables, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); @@ -221,6 +225,7 @@ static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) ut_assertok(table_compute_checksum(rsdt, sizeof(*rsdt))); xsdt = PTR_ALIGN((void *)rsdt + sizeof(*rsdt), 16); + ut_asserteq_ptr(xsdt, ctx.xsdt); ut_assertok(memcmp("XSDT", xsdt->header.signature, ACPI_NAME_LEN)); ut_asserteq(sizeof(*xsdt), xsdt->header.length); ut_assertok(table_compute_checksum(xsdt, sizeof(*xsdt))); From patchwork Mon Mar 9 03:44:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243402 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:51 -0600 Subject: [PATCH v2 27/39] acpi: Add an acpi command In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.27.I5894252b8fe4522e409528c497d882226f34ab9b@changeid> It is useful to dump ACPI tables in U-Boot to see what has been generated. Add a command to handle this. To allow the command to find the tables, add a position into the global data. Support subcommands to list and dump the tables. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: None arch/sandbox/include/asm/global_data.h | 1 + arch/x86/include/asm/global_data.h | 1 + cmd/Kconfig | 14 ++ cmd/Makefile | 1 + cmd/acpi.c | 179 +++++++++++++++++++++++++ lib/acpi/acpi_table.c | 1 + test/dm/acpi.c | 73 ++++++++++ 7 files changed, 270 insertions(+) create mode 100644 cmd/acpi.c diff --git a/arch/sandbox/include/asm/global_data.h b/arch/sandbox/include/asm/global_data.h index f4ce72d566..f95ddb058a 100644 --- a/arch/sandbox/include/asm/global_data.h +++ b/arch/sandbox/include/asm/global_data.h @@ -13,6 +13,7 @@ struct arch_global_data { uint8_t *ram_buf; /* emulated RAM buffer */ void *text_base; /* pointer to base of text region */ + ulong acpi_start; /* Start address of ACPI tables */ }; #include diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index f4c1839104..4aee2f3e8c 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -123,6 +123,7 @@ struct arch_global_data { #ifdef CONFIG_FSP_VERSION2 struct fsp_header *fsp_s_hdr; /* Pointer to FSP-S header */ #endif + ulong acpi_start; /* Start address of ACPI tables */ }; #endif diff --git a/cmd/Kconfig b/cmd/Kconfig index 6403bc45a5..2d3bfe0ab9 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -190,6 +190,20 @@ comment "Commands" menu "Info commands" +config CMD_ACPI + bool "acpi" + default y if ACPIGEN + help + List and dump ACPI tables. ACPI (Advanced Configuration and Power + Interface) is used mostly on x86 for providing information to the + Operating System about devices in the system. The tables are set up + by the firmware, typically U-Boot but possibly an earlier firmware + module, if U-Boot is chain-loaded from something else. ACPI tables + can also include code, to perform hardware-specific tasks required + by the Operating Systems. This allows some amount of separation + between the firmware and OS, and is particularly useful when you + want to make hardware changes without the OS needing to be adjusted. + config CMD_BDI bool "bdinfo" default y diff --git a/cmd/Makefile b/cmd/Makefile index f1dd513a4b..15a9693ed0 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -11,6 +11,7 @@ obj-y += help.o obj-y += version.o # command +obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_AES) += aes.o obj-$(CONFIG_CMD_AB_SELECT) += ab_select.o obj-$(CONFIG_CMD_ADC) += adc.o diff --git a/cmd/acpi.c b/cmd/acpi.c new file mode 100644 index 0000000000..b66c26265b --- /dev/null +++ b/cmd/acpi.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + * Written by Simon Glass + */ +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static void dump_hdr(struct acpi_table_header *hdr) +{ + bool has_hdr = memcmp(hdr->signature, "FACS", ACPI_NAME_LEN); + + printf("%.*s %08lx %06x", ACPI_NAME_LEN, hdr->signature, + (ulong)map_to_sysmem(hdr), hdr->length); + if (has_hdr) { + printf(" (v%02d %.6s %.8s %u %.4s %d)\n", hdr->revision, + hdr->oem_id, hdr->oem_table_id, hdr->oem_revision, + hdr->aslc_id, hdr->aslc_revision); + } else { + printf("\n"); + } +} + +/** + * find_table() - Look up an ACPI table + * + * @sig: Signature of table (4 characters, upper case) + * @return pointer to table header, or NULL if not found + */ +struct acpi_table_header *find_table(const char *sig) +{ + struct acpi_rsdp *rsdp; + struct acpi_rsdt *rsdt; + int len, i, count; + + rsdp = map_sysmem(gd->arch.acpi_start, 0); + if (!rsdp) + return NULL; + rsdt = map_sysmem(rsdp->rsdt_address, 0); + len = rsdt->header.length - sizeof(rsdt->header); + count = len / sizeof(u32); + for (i = 0; i < count; i++) { + struct acpi_table_header *hdr; + + hdr = map_sysmem(rsdt->entry[i], 0); + if (!memcmp(hdr->signature, sig, ACPI_NAME_LEN)) + return hdr; + if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) { + struct acpi_fadt *fadt = (struct acpi_fadt *)hdr; + + if (!memcmp(sig, "DSDT", ACPI_NAME_LEN) && fadt->dsdt) + return map_sysmem(fadt->dsdt, 0); + if (!memcmp(sig, "FACS", ACPI_NAME_LEN) && + fadt->firmware_ctrl) + return map_sysmem(fadt->firmware_ctrl, 0); + } + } + + return NULL; +} + +static int dump_table_name(const char *sig) +{ + struct acpi_table_header *hdr; + + hdr = find_table(sig); + if (!hdr) + return -ENOENT; + printf("%.*s @ %08lx\n", ACPI_NAME_LEN, hdr->signature, + (ulong)map_to_sysmem(hdr)); + print_buffer(0, hdr, 1, hdr->length, 0); + + return 0; +} + +static void list_fact(struct acpi_fadt *fadt) +{ + if (fadt->dsdt) + dump_hdr(map_sysmem(fadt->dsdt, 0)); + if (fadt->firmware_ctrl) + dump_hdr(map_sysmem(fadt->firmware_ctrl, 0)); +} + +static int list_rsdt(struct acpi_rsdt *rsdt, struct acpi_xsdt *xsdt) +{ + int len, i, count; + + dump_hdr(&rsdt->header); + if (xsdt) + dump_hdr(&xsdt->header); + len = rsdt->header.length - sizeof(rsdt->header); + count = len / sizeof(u32); + for (i = 0; i < count; i++) { + struct acpi_table_header *hdr; + + if (!rsdt->entry[i]) + break; + hdr = map_sysmem(rsdt->entry[i], 0); + dump_hdr(hdr); + if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) + list_fact((struct acpi_fadt *)hdr); + if (xsdt) { + if (xsdt->entry[i] != rsdt->entry[i]) { + printf(" (xsdt mismatch %llx)\n", + xsdt->entry[i]); + } + } + } + + return 0; +} + +static int list_rsdp(struct acpi_rsdp *rsdp) +{ + struct acpi_rsdt *rsdt; + struct acpi_xsdt *xsdt; + + printf("RSDP %08lx %06x (v%02d %.6s)\n", (ulong)map_to_sysmem(rsdp), + rsdp->length, rsdp->revision, rsdp->oem_id); + rsdt = map_sysmem(rsdp->rsdt_address, 0); + xsdt = map_sysmem(rsdp->xsdt_address, 0); + list_rsdt(rsdt, xsdt); + + return 0; +} + +static int do_acpi_list(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct acpi_rsdp *rsdp; + + rsdp = map_sysmem(gd->arch.acpi_start, 0); + if (!rsdp) { + printf("No ACPI tables present\n"); + return 0; + } + printf("ACPI tables start at %lx\n", gd->arch.acpi_start); + list_rsdp(rsdp); + + return 0; +} + +static int do_acpi_dump(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *name; + 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); + return CMD_RET_FAILURE; + } + str_to_upper(name, sig, -1); + ret = dump_table_name(sig); + if (ret) { + printf("Table '%.*s' not found\n", ACPI_NAME_LEN, sig); + return CMD_RET_FAILURE; + } + + return 0; +} + +static char acpi_help_text[] = + "list - list ACPI tables\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(dump, 2, 1, do_acpi_dump)); diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index be52b3ac8e..06b4d538f5 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -235,6 +235,7 @@ void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) /* Align ACPI tables to 16 byte */ acpi_align(ctx); + gd->arch.acpi_start = map_to_sysmem(ctx->current); /* We need at least an RSDP and an RSDT Table */ ctx->rsdp = ctx->current; diff --git a/test/dm/acpi.c b/test/dm/acpi.c index a60f67e73d..feb380b26c 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -8,9 +8,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -210,6 +212,7 @@ static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) buf = memalign(64, BUF_SIZE); ut_assertnonnull(buf); acpi_setup_base_tables(&ctx, buf + 4); + ut_asserteq(map_to_sysmem(PTR_ALIGN(buf + 4, 16)), gd->arch.acpi_start); rsdp = buf + 16; ut_asserteq_ptr(rsdp, ctx.rsdp); @@ -240,3 +243,73 @@ static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) } DM_TEST(dm_test_acpi_setup_base_tables, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test 'acpi list' command */ +static int dm_test_acpi_cmd_list(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + ulong addr; + void *buf; + + buf = memalign(16, BUF_SIZE); + ut_assertnonnull(buf); + acpi_setup_base_tables(&ctx, buf); + + ut_assertok(acpi_write_dev_tables(&ctx)); + + console_record_reset(); + run_command("acpi list", 0); + addr = (ulong)map_to_sysmem(buf); + ut_assert_nextline("ACPI tables start at %lx", addr); + ut_assert_nextline("RSDP %08lx %06lx (v02 U-BOOT)", addr, + sizeof(struct acpi_rsdp)); + 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); + 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); + 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); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_list, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test 'acpi dump' command */ +static int dm_test_acpi_cmd_dump(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + ulong addr; + void *buf; + + buf = memalign(16, BUF_SIZE); + ut_assertnonnull(buf); + acpi_setup_base_tables(&ctx, buf); + + ut_assertok(acpi_write_dev_tables(&ctx)); + + /* First search for a non-existent table */ + console_record_reset(); + run_command("acpi dump rdst", 0); + ut_assert_nextline("Table 'RDST' not found"); + ut_assert_console_end(); + + /* Now a real table */ + console_record_reset(); + run_command("acpi dump dmar", 0); + addr = ALIGN(map_to_sysmem(ctx.xsdt) + sizeof(struct acpi_xsdt), 64); + ut_assert_nextline("DMAR @ %08lx", addr); + ut_assert_nextlines_are_dump(0x30); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_dump, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243400 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:52 -0600 Subject: [PATCH v2 28/39] acpi: Add some tables required by the generation code In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.28.I0e2a72f2eafe37b48ffe957baa419baab129b5b4@changeid> The code which generates ACPI tables programmatically, add a few ACPI definitions required by that code. Signed-off-by: Simon Glass --- Changes in v2: None include/acpi_table.h | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/include/acpi_table.h b/include/acpi_table.h index f500f0d3fe..5fd2cef5d1 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -28,6 +28,18 @@ struct acpi_ctx; +/* + * The assigned ACPI ID for the coreboot project is 'BOOT' + * http://www.uefi.org/acpi_id_list + */ +#define COREBOOT_ACPI_ID "BOOT" /* ACPI ID for coreboot HIDs */ + +/* List of ACPI HID that use the coreboot ACPI ID */ +enum coreboot_acpi_ids { + COREBOOT_ACPI_ID_CBTABLE = 0x0000, /* BOOT0000 */ + COREBOOT_ACPI_ID_MAX = 0xFFFF, /* BOOTFFFF */ +}; + /* * RSDP (Root System Description Pointer) * Note: ACPI 1.0 didn't have length, xsdt_address, and ext_checksum @@ -365,6 +377,44 @@ struct acpi_csrt_shared_info { u32 max_block_size; }; +struct __packed acpi_cstate { + u8 ctype; + u16 latency; + u32 power; + struct acpi_gen_regaddr resource; +}; + +struct __packed acpi_tstate { + u32 percent; + u32 power; + u32 latency; + u32 control; + u32 status; +}; + +/* Port types for ACPI _UPC object */ +enum acpi_upc_type { + UPC_TYPE_A, + UPC_TYPE_MINI_AB, + UPC_TYPE_EXPRESSCARD, + UPC_TYPE_USB3_A, + UPC_TYPE_USB3_B, + UPC_TYPE_USB3_MICRO_B, + UPC_TYPE_USB3_MICRO_AB, + UPC_TYPE_USB3_POWER_B, + UPC_TYPE_C_USB2_ONLY, + UPC_TYPE_C_USB2_SS_SWITCH, + UPC_TYPE_C_USB2_SS, + UPC_TYPE_PROPRIETARY = 0xff, + /* + * The following types are not directly defined in the ACPI + * spec but are used by coreboot to identify a USB device type. + */ + UPC_TYPE_INTERNAL = 0xff, + UPC_TYPE_UNUSED, + UPC_TYPE_HUB +}; + enum dmar_type { DMAR_DRHD = 0, DMAR_RMRR = 1, From patchwork Mon Mar 9 03:44:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243407 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:53 -0600 Subject: [PATCH v2 29/39] acpi: Add generation code for devices In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.29.I06704ecd3c988c7826720a7f447e0388bfe85808@changeid> Some devices need to create ACPI tables to communcate their parameters to Linux. Add support for this. Signed-off-by: Simon Glass --- Changes in v2: - Drop CID value from i2c struct include/acpi_device.h | 705 ++++++++++++++++++++++++++ lib/acpi/acpi_device.c | 1094 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1799 insertions(+) create mode 100644 include/acpi_device.h create mode 100644 lib/acpi/acpi_device.c diff --git a/include/acpi_device.h b/include/acpi_device.h new file mode 100644 index 0000000000..f97bd075ec --- /dev/null +++ b/include/acpi_device.h @@ -0,0 +1,705 @@ +/* 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 +#include + +struct acpi_ctx; +struct irq; +struct gpio_desc; +struct udevice; + +/** + * 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, +}; + +/* ACPI descriptor values for common descriptors: SERIAL_BUS means I2C */ +#define ACPI_DESCRIPTOR_LARGE BIT(7) +#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) + +/* + * PRP0001 is a special DT namespace link device ID. It provides a means to use + * existing DT-compatible device identification in ACPI. When this _HID is used + * by an ACPI device, the ACPI subsystem in OS looks up "compatible" property in + * the device object's _DSD and will use the value of that property to identify + * the corresponding device in analogy with the original DT device + * identification algorithm. + * More details can be found in Linux kernel documentation: + * Documentation/acpi/enumeration.txt + */ +#define ACPI_DT_NAMESPACE_HID "PRP0001" + +/* Length of a full path to an ACPI device */ +#define ACPI_PATH_MAX 30 + +/** + * acpi_device_name() - Locate and return the ACPI name for this device + * + * @dev: Device to check + * @name: Returns the character name, must be at least ACPI_NAME_MAX long + * @return 0 if OK, -ve on error + */ +int acpi_device_name(const struct udevice *dev, char *name); + +/** + * 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_STATUS_DEVICE_ALL_ON. It does not support + * inactive or hidden devices. + * + * @dev: Device to check + * @return device status, as ACPI_STATUS_DEVICE_... + */ +int acpi_device_status(const struct udevice *dev); + +/** 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 + * @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]; + + 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_gpio - 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; +}; + +/** + * struct acpi_power_res_params - power on/off sequence information + * + * This provides GPIOs and timing information for powering a device on and off. + * This can be applied to any device that has power control, so is fairly + * generic. + * + * @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) + */ +struct acpi_power_res_params { + struct acpi_gpio *reset_gpio; + unsigned int reset_delay_ms; + unsigned int reset_off_delay_ms; + struct acpi_gpio *enable_gpio; + unsigned int enable_delay_ms; + unsigned int enable_off_delay_ms; + struct acpi_gpio *stop_gpio; + unsigned int stop_delay_ms; + unsigned int stop_off_delay_ms; +}; + +/** + * struct acpi_dp - ACPI device properties + * + * Writing Device Properties objects via _DSD + * + * 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 } + * } + * } + * + * @type: Table type + * @name: Name of object, typically _DSD but could be CHLD for a child object + * @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 + * @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 + * @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 + * @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 + * @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 + * @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_gpio() - Add a GPIO binding Device Property + * + * 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 of the device that has _CRS containing GpioIO()/GpioInt() + * @index: Index of the GPIO resource in _CRS starting from zero + * @pin: Pin in the GPIO resource, typically zero + * @active_low: true if pin is active low + * @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, + bool active_low); + +/** + * 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 + * @child: Child node to add + * @return pointer to new array 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_property_list() - Add a list of of Device Properties + * + * This adds a list of properties to @dp. Any properties without a name or of + * type ACPI_DP_TYPE_UNKNOWN are ignored. + * + * @dp: Table to add properties to + * @property_list: List of properties to add + * @property_count: Number of properties in the list + * @return number of properties added + */ +size_t acpi_dp_add_property_list(struct acpi_dp *dp, + const struct acpi_dp *property_list, + size_t property_count); + +/** + * acpi_dp_write() - Write Device Property hierarchy and clean up resources + * + * This writes the table using acpigen and then frees it + * + * @table: Table to write + * @return 0 if OK, -ve on error + */ +int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table); + +/** + * acpi_device_write_i2c_dev() - Write an I2C device to ACPI, including + * information ACPI needs to use it. + * + * This writes a serial bus descriptor for the I2C device so that ACPI can + */ +int acpi_device_write_i2c_dev(struct acpi_ctx *ctx, const struct udevice *dev); + +/** + * struct acpi_i2c_priv - Information read from device tree + * + * This is used by devices which want to specify various pieces of ACPI + * information, including power control. It allows a generic function to + * generate the information for ACPI, based on device-tree properties. + * + * @disable_gpio_export_in_crs: Don't export GPIOs in the CRS + * @reset_gpio: GPIO used to assert reset to the device + * @enable_gpio: GPIO used to enable the device + * @stop_gpio: GPIO used to stop the device + * @irq_gpio: GPIO used for interrupt (if @irq is not used) + * @irq: IRQ used for interrupt (if @irq_gpio is not used) + * @hid: _HID value for device (required) + * @uid: _UID value for device + * @desc: _DDN value for device + * @wake: Wake event, e.g. GPE0_DW1_15; 0 if none + * @property_count: Number of other DSD properties (currently always 0) + * @probed: true set set 'linux,probed' property + * @compat_string: Device tree compatible string to report through ACPI + * @has_power_resource: true if this device has a power resource + * @reset_delay_ms: Delay after de-asserting reset, in ms + * @reset_off_delay_ms: Delay after asserting reset (during power off) + * @enable_delay_ms: Delay after asserting enable + * @enable_off_delay_ms: Delay after de-asserting enable (during power off) + * @stop_delay_ms: Delay after de-aserting stop + * @stop_off_delay_ms: Delay after asserting stop (during power off) + * @hid_desc_reg_offset: HID register offset (for Human Interface Devices) + */ +struct acpi_i2c_priv { + bool disable_gpio_export_in_crs; + struct gpio_desc reset_gpio; + struct gpio_desc enable_gpio; + struct gpio_desc irq_gpio; + struct gpio_desc stop_gpio; + struct irq irq; + const char *hid; + u32 uid; + const char *desc; + u32 wake; + u32 property_count; + bool probed; + const char *compat_string; + bool has_power_resource; + u32 reset_delay_ms; + u32 reset_off_delay_ms; + u32 enable_delay_ms; + u32 enable_off_delay_ms; + u32 stop_delay_ms; + u32 stop_off_delay_ms; + u32 hid_desc_reg_offset; +}; + +/** + * I2C Human-Interface Devices configuration + * + * @hid_desc_reg_offset: HID register offset + */ +struct dsm_i2c_hid_config { + u8 hid_desc_reg_offset; +}; + +/** + * acpi_device_write_gpio() - Write GpioIo() or GpioInt() descriptor + * + * @gpio: GPIO information to write + * @return 0 if OK, -ve on error + */ +int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio); + +/* + * 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. + * + * Reset - Put the device into / take the device out of reset. + * Enable - Enable / disable power to device. + * Stop - Stop / start operation of device. + * + * @return 0 if OK, -ve if at least one GPIO is not provided + */ +int acpi_device_add_power_res(struct acpi_ctx *ctx, + const struct acpi_power_res_params *params); + +/** + * gpio_get_acpi() - Convert a GPIO description into an ACPI GPIO + * + * At present this is fairly limited. It only supports ACPI_GPIO_TYPE_IO and + * has hard-coded settings for type, pull, IO restrict and polarity. These + * could come from pinctrl potentially. + * + * @desc: GPIO description to convert + * @gpio: Place to put ACPI GPIO information + * @return 0 if OK, -ENOENT if @desc is invalid + */ +int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio); + +/** + * acpi_device_write_dsm_i2c_hid() - Write a device-specific method for HID + * + * This writes a DSM for an I2C Human-Interface Device based on the config + * provided + * + * @config: Config information to write + */ +void acpi_device_write_dsm_i2c_hid(struct acpi_ctx *ctx, + struct dsm_i2c_hid_config *config); + +/** + * 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. + * + * @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_ofnode_copy_int(ofnode node, 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_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); + +int acpi_device_get_name(const struct udevice *dev, char *out_name); + +#endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c new file mode 100644 index 0000000000..145de90677 --- /dev/null +++ b/lib/acpi/acpi_device.c @@ -0,0 +1,1094 @@ +// 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 + +#define ACPI_DP_UUID "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" +#define ACPI_DP_CHILD_UUID "dbb8e3e6-5886-4ba6-8795-1319f52a966b" +#define ACPI_DSM_I2C_HID_UUID "3cdff6f7-4267-4555-ad05-b30a3d8938de" + +/** + * acpi_device_write_zero_len() - Write a placeholder word value + * + * @return pointer to the zero word (for fixing up later) + */ +static void *acpi_device_write_zero_len(struct acpi_ctx *ctx) +{ + char *p = acpigen_get_current(ctx); + + acpigen_emit_word(ctx, 0); + + return p; +} + +/** + * acpi_device_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 acpi_device_write_zero_len(). + * + * @ptr: Word to update + * @start: Start address to count from to calculated the length + */ +static void acpi_device_fill_from_len(struct acpi_ctx *ctx, char *ptr, + char *start) +{ + u16 len = acpigen_get_current(ctx) - start; + + ptr[0] = len & 0xff; + ptr[1] = (len >> 8) & 0xff; +} + +/** + * acpi_device_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_fill_from_len() passing @ptr + 2 as @start + * + * @ptr: Word to update. + */ +static void acpi_device_fill_len(struct acpi_ctx *ctx, void *ptr) +{ + acpi_device_fill_from_len(ctx, ptr, ptr + sizeof(u16)); +} + +int acpi_device_name(const struct udevice *dev, char *name) +{ + int ret; + + ret = acpi_get_name(dev, name); + if (ret) + return log_msg_ret("name", ret); + + return 0; +} + +/** + * 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_device_name(dev, name); + if (ret) + return ret; + + /* + * Make sure this name segment will fit, including the path segment + * separator and possible NUL terminator if this is the last segment. + */ + if (!dev || (cur + strlen(name) + 2) > buf_len) + return cur; + + /* 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; +} + +int acpi_device_status(const struct udevice *dev) +{ + return ACPI_STATUS_DEVICE_ALL_ON; +} + +/* ACPI 6.1 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 || 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 = acpi_device_write_zero_len(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 = acpi_device_write_zero_len(ctx); + + /* Byte 16: Reserved */ + acpigen_emit_byte(ctx, 0); + + /* Byte 17-18: Resource Source Name Offset, relative to start */ + resource_offset = acpi_device_write_zero_len(ctx); + + /* Byte 19-20: Vendor Data Offset, relative to start */ + vendor_data_offset = acpi_device_write_zero_len(ctx); + + /* Byte 21-22: Vendor Data Length */ + acpigen_emit_word(ctx, 0); + + /* Fill in Pin Table Offset */ + acpi_device_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 */ + acpi_device_fill_from_len(ctx, resource_offset, start); + + /* Resource Source Name String */ + acpigen_emit_string(ctx, gpio->resource); + + /* Fill in Vendor Data Offset */ + acpi_device_fill_from_len(ctx, vendor_data_offset, start); + + /* Fill in GPIO Descriptor Length (account for len word) */ + acpi_device_fill_len(ctx, desc_length); + + return 0; +} + +/* ACPI 6.1 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 = acpi_device_write_zero_len(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 = acpi_device_write_zero_len(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 */ + acpi_device_fill_len(ctx, type_length); + + /* Byte 18+: ResourceSource */ + acpigen_emit_string(ctx, i2c->resource); + + /* Fill in I2C Descriptor Length */ + acpi_device_fill_len(ctx, desc_length); +} + +/* ACPI 6.1 section 6.4.3.8.2.2 - SpiSerialBus() */ +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 = acpi_device_write_zero_len(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, 1 << 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 |= 1 << 0; + if (spi->device_select_polarity == SPI_POLARITY_HIGH) + flags |= 1 << 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 = acpi_device_write_zero_len(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 */ + acpi_device_fill_len(ctx, type_length); + + /* Byte 21+: ResourceSource String */ + acpigen_emit_string(ctx, spi->resource); + + /* Fill in SPI Descriptor Length */ + acpi_device_fill_len(ctx, desc_length); +} + +/* PowerResource() with Enable and/or Reset control */ +int acpi_device_add_power_res(struct acpi_ctx *ctx, + const struct acpi_power_res_params *params) +{ + static const char *const power_res_dev_states[] = { "_PR0", "_PR3" }; + unsigned int reset_gpio = params->reset_gpio->pins[0]; + unsigned int enable_gpio = params->enable_gpio->pins[0]; + unsigned int stop_gpio = params->stop_gpio->pins[0]; + int ret; + + if (!reset_gpio && !enable_gpio && !stop_gpio) + 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_enable_tx_gpio(ctx, params->reset_gpio); + if (ret) + return log_msg_ret("reset1", ret); + } + if (enable_gpio) { + ret = acpigen_enable_tx_gpio(ctx, params->enable_gpio); + if (ret) + return log_msg_ret("enable1", ret); + if (params->enable_delay_ms) + acpigen_write_sleep(ctx, params->enable_delay_ms); + } + if (reset_gpio) { + ret = acpigen_disable_tx_gpio(ctx, params->reset_gpio); + if (ret) + return log_msg_ret("reset2", ret); + if (params->reset_delay_ms) + acpigen_write_sleep(ctx, params->reset_delay_ms); + } + if (stop_gpio) { + ret = acpigen_disable_tx_gpio(ctx, params->stop_gpio); + if (ret) + return log_msg_ret("stop1", ret); + if (params->stop_delay_ms) + acpigen_write_sleep(ctx, params->stop_delay_ms); + } + acpigen_pop_len(ctx); /* _ON method */ + + /* Method (_OFF, 0, Serialized) */ + acpigen_write_method_serialized(ctx, "_OFF", 0); + if (stop_gpio) { + ret = acpigen_enable_tx_gpio(ctx, params->stop_gpio); + if (ret) + return log_msg_ret("stop2", ret); + if (params->stop_off_delay_ms) + acpigen_write_sleep(ctx, params->stop_off_delay_ms); + } + if (reset_gpio) { + ret = acpigen_enable_tx_gpio(ctx, params->reset_gpio); + if (ret) + return log_msg_ret("reset3", ret); + if (params->reset_off_delay_ms) + acpigen_write_sleep(ctx, params->reset_off_delay_ms); + } + if (enable_gpio) { + ret = acpigen_disable_tx_gpio(ctx, params->enable_gpio); + if (ret) + return log_msg_ret("enable2", ret); + if (params->enable_off_delay_ms) + acpigen_write_sleep(ctx, params->enable_off_delay_ms); + } + acpigen_pop_len(ctx); /* _OFF method */ + + acpigen_pop_len(ctx); /* PowerResource PRIC */ + + return 0; +} + +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) +{ + 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; + } +} + +int acpi_dp_write(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; + + if (!table || 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(ctx, dp->child); + if (ret) + return log_msg_ret("dp child", ret); + } + } + + /* Clean up */ + acpi_dp_free(table); + + 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); +} + +size_t acpi_dp_add_property_list(struct acpi_dp *dp, + const struct acpi_dp *property_list, + size_t property_count) +{ + const struct acpi_dp *prop; + size_t i, properties_added = 0; + + if (!dp || !property_list) + return 0; + + for (i = 0; i < property_count; i++) { + prop = &property_list[i]; + + if (prop->type == ACPI_DP_TYPE_UNKNOWN || !prop->name) + continue; + + switch (prop->type) { + case ACPI_DP_TYPE_INTEGER: + acpi_dp_add_integer(dp, prop->name, prop->integer); + break; + case ACPI_DP_TYPE_STRING: + acpi_dp_add_string(dp, prop->name, prop->string); + break; + case ACPI_DP_TYPE_REFERENCE: + acpi_dp_add_reference(dp, prop->name, prop->string); + break; + case ACPI_DP_TYPE_ARRAY: + acpi_dp_add_array(dp, prop->array); + break; + case ACPI_DP_TYPE_CHILD: + acpi_dp_add_child(dp, prop->name, prop->child); + break; + default: + continue; + } + + ++properties_added; + } + + return properties_added; +} + +struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name, + u64 value) +{ + struct acpi_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 = 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) +{ + if (!dp) + return NULL; + + struct acpi_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; + + if (!dp || !child || 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; + + if (!dp || !array || 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; + + if (!dp || 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; + + acpi_dp_add_array(dp, dp_array); + + return dp_array; +} + +struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, + const char *ref, int index, int pin, + bool active_low) +{ + if (!dp) + return NULL; + + struct acpi_dp *gpio = acpi_dp_new_table(name); + + if (!gpio) + return NULL; + + /* The device that has _CRS containing GpioIO()/GpioInt() */ + acpi_dp_add_reference(gpio, NULL, ref); + + /* Index of the GPIO resource in _CRS starting from zero */ + acpi_dp_add_integer(gpio, NULL, index); + + /* Pin in the GPIO resource, typically zero */ + acpi_dp_add_integer(gpio, NULL, pin); + + /* Set if pin is active low */ + acpi_dp_add_integer(gpio, NULL, active_low); + + acpi_dp_add_array(dp, gpio); + + return gpio; +} + +/** + * acpi_device_set_i2c() - Set up an ACPI I2C struct from a device + * + * @dev: I2C device to convert + * @i2c: Place to put the new structure + * @scope: Scope of the I2C device (this is the controller path) + * @return 0 (always) + */ +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", 100000); + i2c->resource = scope; + + return 0; +} + +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) + return log_msg_ret("set", ret); + acpi_device_write_i2c(ctx, &i2c); + + return 0; +} + +static void i2c_hid_func0_cb(struct acpi_ctx *ctx, void *arg) +{ + /* ToInteger (Arg1, Local2) */ + acpigen_write_to_integer(ctx, ARG1_OP, LOCAL2_OP); + /* If (LEqual (Local2, 0x0)) */ + acpigen_write_if_lequal_op_int(ctx, LOCAL2_OP, 0x0); + /* Return (Buffer (One) { 0x1f }) */ + acpigen_write_return_singleton_buffer(ctx, 0x1f); + acpigen_pop_len(ctx); /* Pop : If */ + /* Else */ + acpigen_write_else(ctx); + /* If (LEqual (Local2, 0x1)) */ + acpigen_write_if_lequal_op_int(ctx, LOCAL2_OP, 0x1); + /* Return (Buffer (One) { 0x3f }) */ + acpigen_write_return_singleton_buffer(ctx, 0x3f); + acpigen_pop_len(ctx); /* Pop : If */ + /* Else */ + acpigen_write_else(ctx); + /* Return (Buffer (One) { 0x0 }) */ + acpigen_write_return_singleton_buffer(ctx, 0x0); + acpigen_pop_len(ctx); /* Pop : Else */ + acpigen_pop_len(ctx); /* Pop : Else */ +} + +static void i2c_hid_func1_cb(struct acpi_ctx *ctx, void *arg) +{ + struct dsm_i2c_hid_config *config = arg; + + acpigen_write_return_byte(ctx, config->hid_desc_reg_offset); +} + +static hid_callback_func i2c_hid_callbacks[2] = { + i2c_hid_func0_cb, + i2c_hid_func1_cb, +}; + +void acpi_device_write_dsm_i2c_hid(struct acpi_ctx *ctx, + struct dsm_i2c_hid_config *config) +{ + acpigen_write_dsm(ctx, ACPI_DSM_I2C_HID_UUID, i2c_hid_callbacks, + ARRAY_SIZE(i2c_hid_callbacks), config); +} + +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: + 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_get_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) + memcpy(out_name, name, ACPI_NAME_MAX); + + return 0; +} + +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); + acpi_dp_add_integer(dp, prop, val); + + return ret; +} + +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 -ENOENT; + acpi_dp_add_string(dp, prop, val); + + 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); + acpi_dp_add_integer(dp, prop, val); + + 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 -ENOENT; + acpi_dp_add_string(dp, prop, val); + + return 0; +} From patchwork Mon Mar 9 03:44:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243408 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:54 -0600 Subject: [PATCH v2 30/39] acpi: Add functions to generate ACPI code In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.30.Ie25c3492416531983761521cbc51e188052e18b4@changeid> Sometimes we need to generate ACPI code on the fly based on things only known at run time. Add a new 'acpigen' library to handle this. This code comes from coreboot and has been modified to support the acpi_ctx struct. Also add acpi_device.c to the build, since these files are co-dependent. Signed-off-by: Simon Glass --- Changes in v2: None include/acpigen.h | 482 +++++++++++++ include/dm/acpi.h | 7 + include/irq.h | 2 + lib/acpi/Makefile | 2 + lib/acpi/acpigen.c | 1683 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 2176 insertions(+) create mode 100644 include/acpigen.h create mode 100644 lib/acpi/acpigen.c diff --git a/include/acpigen.h b/include/acpigen.h new file mode 100644 index 0000000000..08000831b9 --- /dev/null +++ b/include/acpigen.h @@ -0,0 +1,482 @@ +/* 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 _ACPIGEN_H +#define _ACPIGEN_H + +#include + +struct acpi_cstate; +struct acpi_pld; +struct acpi_gpio; +struct acpi_tstate; + +/* Values that can be returned for ACPI Device _STA method */ +#define ACPI_STATUS_DEVICE_PRESENT BIT(0) +#define ACPI_STATUS_DEVICE_ENABLED BIT(1) +#define ACPI_STATUS_DEVICE_SHOW_IN_UI BIT(2) +#define ACPI_STATUS_DEVICE_STATE_OK BIT(3) + +#define ACPI_STATUS_DEVICE_ALL_OFF 0 +#define ACPI_STATUS_DEVICE_ALL_ON (ACPI_STATUS_DEVICE_PRESENT |\ + ACPI_STATUS_DEVICE_ENABLED |\ + ACPI_STATUS_DEVICE_SHOW_IN_UI |\ + ACPI_STATUS_DEVICE_STATE_OK) +#define ACPI_STATUS_DEVICE_HIDDEN_ON (ACPI_STATUS_DEVICE_PRESENT |\ + ACPI_STATUS_DEVICE_ENABLED |\ + ACPI_STATUS_DEVICE_STATE_OK) + +/* ACPI Op/Prefix Codes */ +enum { + ZERO_OP = 0x00, + ONE_OP = 0x01, + ALIAS_OP = 0x06, + 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, + VARIABLE_PACKAGE_OP = 0x13, + METHOD_OP = 0x14, + EXTERNAL_OP = 0x15, + DUAL_NAME_PREFIX = 0x2E, + MULTI_NAME_PREFIX = 0x2F, + EXT_OP_PREFIX = 0x5B, + + MUTEX_OP = 0x01, + EVENT_OP = 0x01, + SF_RIGHT_OP = 0x10, + SF_LEFT_OP = 0x11, + COND_REFOF_OP = 0x12, + CREATEFIELD_OP = 0x13, + LOAD_TABLE_OP = 0x1f, + LOAD_OP = 0x20, + STALL_OP = 0x21, + SLEEP_OP = 0x22, + ACQUIRE_OP = 0x23, + SIGNAL_OP = 0x24, + WAIT_OP = 0x25, + RST_OP = 0x26, + RELEASE_OP = 0x27, + FROM_BCD_OP = 0x28, + TO_BCD_OP = 0x29, + UNLOAD_OP = 0x2A, + REVISON_OP = 0x30, + DEBUG_OP = 0x31, + FATAL_OP = 0x32, + TIMER_OP = 0x33, + OPREGION_OP = 0x80, + FIELD_OP = 0x81, + DEVICE_OP = 0x82, + PROCESSOR_OP = 0x83, + POWER_RES_OP = 0x84, + THERMAL_ZONE_OP = 0x85, + INDEX_FIELD_OP = 0x86, + BANK_FIELD_OP = 0x87, + DATA_REGION_OP = 0x88, + + ROOT_PREFIX = 0x5C, + PARENT_PREFIX = 0x5D, + LOCAL0_OP = 0x60, + LOCAL1_OP = 0x61, + LOCAL2_OP = 0x62, + LOCAL3_OP = 0x63, + LOCAL4_OP = 0x64, + LOCAL5_OP = 0x65, + LOCAL6_OP = 0x66, + LOCAL7_OP = 0x67, + ARG0_OP = 0x68, + ARG1_OP = 0x69, + ARG2_OP = 0x6A, + ARG3_OP = 0x6B, + ARG4_OP = 0x6C, + ARG5_OP = 0x6D, + ARG6_OP = 0x6E, + STORE_OP = 0x70, + REF_OF_OP = 0x71, + ADD_OP = 0x72, + CONCATENATE_OP = 0x73, + SUBTRACT_OP = 0x74, + INCREMENT_OP = 0x75, + DECREMENT_OP = 0x76, + MULTIPLY_OP = 0x77, + DIVIDE_OP = 0x78, + SHIFT_LEFT_OP = 0x79, + SHIFT_RIGHT_OP = 0x7A, + AND_OP = 0x7B, + NAND_OP = 0x7C, + OR_OP = 0x7D, + NOR_OP = 0x7E, + XOR_OP = 0x7F, + NOT_OP = 0x80, + FD_SHIFT_LEFT_BIT_OR = 0x81, + FD_SHIFT_RIGHT_BIT_OR = 0x82, + DEREF_OP = 0x83, + CONCATENATE_TEMP_OP = 0x84, + MOD_OP = 0x85, + NOTIFY_OP = 0x86, + SIZEOF_OP = 0x87, + INDEX_OP = 0x88, + MATCH_OP = 0x89, + CREATE_DWORD_OP = 0x8A, + CREATE_WORD_OP = 0x8B, + CREATE_BYTE_OP = 0x8C, + CREATE_BIT_OP = 0x8D, + OBJ_TYPE_OP = 0x8E, + CREATE_QWORD_OP = 0x8F, + LAND_OP = 0x90, + LOR_OP = 0x91, + LNOT_OP = 0x92, + LEQUAL_OP = 0x93, + LGREATER_OP = 0x94, + LLESS_OP = 0x95, + TO_BUFFER_OP = 0x96, + TO_DEC_STRING_OP = 0x97, + TO_HEX_STRING_OP = 0x98, + TO_INTEGER_OP = 0x99, + TO_STRING_OP = 0x9C, + CP_OBJ_OP = 0x9D, + MID_OP = 0x9E, + CONTINUE_OP = 0x9F, + IF_OP = 0xA0, + ELSE_OP = 0xA1, + WHILE_OP = 0xA2, + NOOP_OP = 0xA3, + RETURN_OP = 0xA4, + BREAK_OP = 0xA5, + COMMENT_OP = 0xA9, + BREAKPIONT_OP = 0xCC, + ONES_OP = 0xFF, +}; + +#define FIELDLIST_OFFSET(_bits) { .type = OFFSET, \ + .name = "", \ + .bits = _bits * 8, \ + } +#define FIELDLIST_NAMESTR(_name, _bits) { .type = NAME_STRING, \ + .name = _name, \ + .bits = _bits, \ + } + +#define FIELD_ANYACC 0 +#define FIELD_BYTEACC 1 +#define FIELD_WORDACC 2 +#define FIELD_DWORDACC 3 +#define FIELD_QWORDACC 4 +#define FIELD_BUFFERACC 5 +#define FIELD_NOLOCK (0 << 4) +#define FIELD_LOCK (1 << 4) +#define FIELD_PRESERVE (0 << 5) +#define FIELD_WRITEASONES (1 << 5) +#define FIELD_WRITEASZEROS (2 << 5) + +enum field_type { + OFFSET, + NAME_STRING, + FIELD_TYPE_MAX, +}; + +struct fieldlist { + enum field_type type; + const char *name; + u32 bits; +}; + +#define OPREGION(rname, space, offset, len) {.name = rname, \ + .regionspace = space, \ + .regionoffset = offset, \ + .regionlen = len, \ + } + +enum region_space { + SYSTEMMEMORY, + SYSTEMIO, + PCI_CONFIG, + EMBEDDEDCONTROL, + SMBUS, + CMOS, + PCIBARTARGET, + IPMI, + GPIO_REGION, + GPSERIALBUS, + PCC, + FIXED_HARDWARE = 0x7F, + REGION_SPACE_MAX, +}; + +struct opregion { + const char *name; + enum region_space regionspace; + unsigned long regionoffset; + unsigned long regionlen; +}; + +#define DSM_UUID(DSM_UUID, DSM_CALLBACKS, DSM_COUNT, DSM_ARG) \ + { .uuid = DSM_UUID, \ + .callbacks = DSM_CALLBACKS, \ + .count = DSM_COUNT, \ + .arg = DSM_ARG, \ + } + +typedef void (*hid_callback_func)(struct acpi_ctx *ctx, void *arg); + +struct dsm_uuid { + const char *uuid; + hid_callback_func *callbacks; + size_t count; + void *arg; +}; + +/* version 1 has 15 fields, version 2 has 19, and version 3 has 21 */ +enum cppc_fields { + CPPC_HIGHEST_PERF, /* can be DWORD */ + CPPC_NOMINAL_PERF, /* can be DWORD */ + CPPC_LOWEST_NONL_PERF, /* can be DWORD */ + CPPC_LOWEST_PERF, /* can be DWORD */ + CPPC_GUARANTEED_PERF, + CPPC_DESIRED_PERF, + CPPC_MIN_PERF, + CPPC_MAX_PERF, + CPPC_PERF_REDUCE_TOLERANCE, + CPPC_TIME_WINDOW, + CPPC_COUNTER_WRAP, /* can be DWORD */ + CPPC_REF_PERF_COUNTER, + CPPC_DELIVERED_PERF_COUNTER, + CPPC_PERF_LIMITED, + CPPC_ENABLE, /* can be System I/O */ + CPPC_MAX_FIELDS_VER_1, + CPPC_AUTO_SELECT = /* can be DWORD */ + CPPC_MAX_FIELDS_VER_1, + CPPC_AUTO_ACTIVITY_WINDOW, + CPPC_PERF_PREF, + CPPC_REF_PERF, /* can be DWORD */ + CPPC_MAX_FIELDS_VER_2, + CPPC_LOWEST_FREQ = /* can be DWORD */ + CPPC_MAX_FIELDS_VER_2, + CPPC_NOMINAL_FREQ, /* can be DWORD */ + CPPC_MAX_FIELDS_VER_3, +}; + +struct cppc_config { + u32 version; /* must be 1, 2, or 3 */ + /* + * The generic struct acpi_gen_regaddr structure is being used, though + * anything besides PPC or FFIXED generally requires checking + * if the OS has advertised support for it (via _OSC). + * + * NOTE: some fields permit DWORDs to be used. If you + * provide a System Memory register with all zeros (which + * represents unsupported) then this will be used as-is. + * Otherwise, a System Memory register with a 32-bit + * width will be converted into a DWORD field (the value + * of which will be the value of 'addrl'. Any other use + * of System Memory register is currently undefined. + * (i.e., if you have an actual need for System Memory + * then you'll need to adjust this kludge). + */ + struct acpi_gen_regaddr regs[CPPC_MAX_FIELDS_VER_3]; +}; + +void acpigen_write_return_integer(struct acpi_ctx *ctx, u64 arg); +void acpigen_write_return_string(struct acpi_ctx *ctx, const char *arg); +void acpigen_write_len_f(struct acpi_ctx *ctx); +void acpigen_pop_len(struct acpi_ctx *ctx); +void acpigen_set_current(struct acpi_ctx *ctx, char *curr); +char *acpigen_get_current(struct acpi_ctx *ctx); +char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el); +void acpigen_write_zero(struct acpi_ctx *ctx); +void acpigen_write_one(struct acpi_ctx *ctx); +void acpigen_write_ones(struct acpi_ctx *ctx); +void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data); +void acpigen_emit_byte(struct acpi_ctx *ctx, unsigned char data); +void acpigen_emit_ext_op(struct acpi_ctx *ctx, u8 op); +void acpigen_emit_word(struct acpi_ctx *ctx, unsigned int data); +void acpigen_emit_dword(struct acpi_ctx *ctx, unsigned int data); +void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size); +void acpigen_emit_string(struct acpi_ctx *ctx, const char *string); +void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath); +void acpigen_emit_eisaid(struct acpi_ctx *ctx, const char *eisaid); +void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data); +void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data); +void acpigen_write_qword(struct acpi_ctx *ctx, u64 data); +void acpigen_write_integer(struct acpi_ctx *ctx, u64 data); +void acpigen_write_string(struct acpi_ctx *ctx, const char *string); +void acpigen_write_coreboot_hid(struct acpi_ctx *ctx, + enum coreboot_acpi_ids id); +void acpigen_write_name(struct acpi_ctx *ctx, const char *name); +void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name); +void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name); +void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name, + const char *string); +void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, u32 val); +void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val); +void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, u8 val); +void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name, + u64 val); +void acpigen_write_scope(struct acpi_ctx *ctx, const char *name); +void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs); +void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name, + int nargs); +void acpigen_write_device(struct acpi_ctx *ctx, const char *name); +void acpigen_write_ppc(struct acpi_ctx *ctx, u8 nr); +void acpigen_write_ppc_nvs(struct acpi_ctx *ctx); +void acpigen_write_empty_pct(struct acpi_ctx *ctx); +void acpigen_write_empty_ptc(struct acpi_ctx *ctx); +void acpigen_write_prw(struct acpi_ctx *ctx, u32 wake, u32 level); +void acpigen_write_sta(struct acpi_ctx *ctx, u8 status); +void acpigen_write_tpc(struct acpi_ctx *ctx, const char *gnvs_tpc_limit); +void acpigen_write_pss_package(struct acpi_ctx *ctx, u32 corefreq, u32 power, + u32 translat, + u32 busmlat, u32 control, u32 status); +enum psd_coord { + SW_ALL = 0xfc, + SW_ANY = 0xfd, + HW_ALL = 0xfe +}; + +void acpigen_write_psd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs, + enum psd_coord coordtype); +void acpigen_write_cst_package_entry(struct acpi_ctx *ctx, + struct acpi_cstate *cstate); +void acpigen_write_cst_package(struct acpi_ctx *ctx, struct acpi_cstate *entry, + int nentries); + +enum csd_coord { + CSD_HW_ALL = 0xfe, +}; + +void acpigen_write_CSD_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs, + enum csd_coord coordtype, u32 index); +void acpigen_write_processor(struct acpi_ctx *ctx, u8 cpuindex, u32 pblock_addr, + u8 pblock_len); +void acpigen_write_processor_package(struct acpi_ctx *ctx, const char *name, + uint first_core, uint core_count); +void acpigen_write_processor_cnot(struct acpi_ctx *ctx, const uint num_cores); +void acpigen_write_tss_package(struct acpi_ctx *ctx, int entries, + struct acpi_tstate *tstate_list); +void acpigen_write_tsd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs, + enum psd_coord coordtype); +void acpigen_write_mem32fixed(struct acpi_ctx *ctx, int readwrite, u32 base, + u32 size); +void acpigen_write_register_resource(struct acpi_ctx *ctx, + const struct acpi_gen_regaddr *addr); +void acpigen_write_irq(struct acpi_ctx *ctx, u16 mask); +void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx); +void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx); +int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid); +void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, u8 level, + u16 order, const char *const dev_states[], + size_t dev_states_count); +void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms); +void acpigen_write_store(struct acpi_ctx *ctx); +void acpigen_write_store_ops(struct acpi_ctx *ctx, u8 src, u8 dst); +void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res); +void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res); +void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res); +void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str); +void acpigen_write_debug_integer(struct acpi_ctx *ctx, u64 val); +void acpigen_write_debug_op(struct acpi_ctx *ctx, u8 op); +void acpigen_write_if(struct acpi_ctx *ctx); +void acpigen_write_if_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2); +void acpigen_write_if_lequal_op_int(struct acpi_ctx *ctx, u8 op, u64 val); +void acpigen_write_else(struct acpi_ctx *ctx); +void acpigen_write_to_buffer(struct acpi_ctx *ctx, u8 src, u8 dst); +void acpigen_write_to_integer(struct acpi_ctx *ctx, u8 src, u8 dst); +void acpigen_write_byte_buffer(struct acpi_ctx *ctx, u8 *arr, size_t size); +void acpigen_write_return_byte_buffer(struct acpi_ctx *ctx, u8 *arr, + size_t size); +void acpigen_write_return_singleton_buffer(struct acpi_ctx *ctx, u8 arg); +void acpigen_write_return_byte(struct acpi_ctx *ctx, u8 arg); +void acpigen_write_upc(struct acpi_ctx *ctx, enum acpi_upc_type type); + +/* + * Generate ACPI AML code for _DSM method. + * This function takes as input uuid for the device, set of callbacks and + * argument to pass into the callbacks. Callbacks should ensure that Local0 and + * Local1 are left untouched. Use of Local2-Local7 is permitted in callbacks. + */ +int acpigen_write_dsm(struct acpi_ctx *ctx, const char *uuid, + hid_callback_func callbacks[], size_t count, void *arg); +int acpigen_write_dsm_uuid_arr(struct acpi_ctx *ctx, struct dsm_uuid *ids, + size_t count); + +/* + * Generate ACPI AML code for _CPC (struct acpi_ctx *ctx, Continuous Perfmance + * Control). Execute the package function once to create a global table, then + * execute the method function within each processor object to + * create a method that points to the global table. + */ +int acpigen_write_cppc_package(struct acpi_ctx *ctx, + const struct cppc_config *config); +void acpigen_write_cppc_method(struct acpi_ctx *ctx); + +/* + * Generate ACPI AML code for _ROM method. + * This function takes as input ROM data and ROM length. + * The ROM length has to be multiple of 4096 and has to be less + * than the current implementation limit of 0x40000. + */ +int acpigen_write_rom(struct acpi_ctx *ctx, void *bios, const size_t length); +/* + * Generate ACPI AML code for OperationRegion + * This function takes input region name, region space, region offset & region + * length. + */ +void acpigen_write_opregion(struct acpi_ctx *ctx, struct opregion *opreg); +/* + * Generate ACPI AML code for Field + * This function takes input region name, fieldlist, count & flags. + */ +int acpigen_write_field(struct acpi_ctx *ctx, const char *name, + struct fieldlist *l, size_t count, uint flags); +/* + * Generate ACPI AML code for IndexField + * This function takes input index name, data name, fieldlist, count & flags. + */ +int acpigen_write_indexfield(struct acpi_ctx *ctx, const char *idx, + const char *data, struct fieldlist *l, + size_t count, uint flags); + +/* + * Soc-implemented functions for generating ACPI AML code for GPIO handling. All + * these functions are expected to use only Local5, Local6 and Local7 + * variables. If the functions call into another ACPI method, then there is no + * restriction on the use of Local variables. In case of get/read functions, + * return value is expected to be stored in Local0 variable. + * + * All functions return 0 on success and -1 on error. + */ + +/* Generate ACPI AML code to return Rx value of GPIO in Local0. */ +int acpigen_soc_read_rx_gpio(struct acpi_ctx *ctx, uint gpio_num); + +/* Generate ACPI AML code to return Tx value of GPIO in Local0. */ +int acpigen_soc_get_tx_gpio(struct acpi_ctx *ctx, uint gpio_num); + +/* Generate ACPI AML code to set Tx value of GPIO to 1. */ +int acpigen_soc_set_tx_gpio(struct acpi_ctx *ctx, uint gpio_num); + +/* Generate ACPI AML code to set Tx value of GPIO to 0. */ +int acpigen_soc_clear_tx_gpio(struct acpi_ctx *ctx, uint gpio_num); + +/* + * Helper functions for enabling/disabling Tx GPIOs based on the GPIO + * polarity. These functions end up calling acpigen_soc_{set,clear}_tx_gpio to + * make callbacks into SoC acpigen code. + * + * Returns 0 on success and -1 on error. + */ +int acpigen_enable_tx_gpio(struct acpi_ctx *ctx, struct acpi_gpio *gpio); +int acpigen_disable_tx_gpio(struct acpi_ctx *ctx, struct acpi_gpio *gpio); + +#endif diff --git a/include/dm/acpi.h b/include/dm/acpi.h index f3e9d73b78..0f78a506da 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -22,6 +22,9 @@ /* Length of an ACPI name string including nul terminator */ #define ACPI_NAME_MAX 5 +/* Number of nested objects supported */ +#define ACPIGEN_LENSTACK_SIZE 10 + #if !defined(__ACPI__) /** @@ -34,12 +37,16 @@ * adding a new table. The RSDP holds pointers to the RSDP and XSDT. * @rsdt: Pointer to the Root System Description Table * @xsdt: Pointer to the Extended System Description Table + * @len_stack: Stack of 'length' words to fix up later + * @ltop: Points to current top of stack (0 = empty) */ struct acpi_ctx { void *current; struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; + char *len_stack[ACPIGEN_LENSTACK_SIZE]; + int ltop; }; /** diff --git a/include/irq.h b/include/irq.h index b71afe9bee..d4948e6dc4 100644 --- a/include/irq.h +++ b/include/irq.h @@ -8,6 +8,8 @@ #ifndef __irq_H #define __irq_H +struct ofnode_phandle_args; + /* * Interrupt controller types available. You can find a particular one with * irq_first_device_type() diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index 660491ef71..85a1f774ad 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-y += acpigen.o +obj-y += acpi_device.o obj-y += acpi_table.o diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c new file mode 100644 index 0000000000..4e7d1ce51e --- /dev/null +++ b/lib/acpi/acpigen.c @@ -0,0 +1,1683 @@ +// 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 +#include + +/* + * 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 + +/* CPU path format */ +#define ACPI_CPU_STRING "\\_PR.CP%02d" + +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] = (0x80 | (len & 0xf)); + p[1] = (len >> 4 & 0xff); + p[2] = (len >> 12 & 0xff); +} + +void acpigen_set_current(struct acpi_ctx *ctx, char *curr) +{ + ctx->current = curr; +} + +char *acpigen_get_current(struct acpi_ctx *ctx) +{ + return ctx->current; +} + +void acpigen_emit_byte(struct acpi_ctx *ctx, unsigned char b) +{ + *(u8 *)ctx->current++ = b; +} + +void acpigen_emit_ext_op(struct acpi_ctx *ctx, u8 op) +{ + acpigen_emit_byte(ctx, EXT_OP_PREFIX); + acpigen_emit_byte(ctx, op); +} + +void acpigen_emit_word(struct acpi_ctx *ctx, unsigned int data) +{ + acpigen_emit_byte(ctx, data & 0xff); + acpigen_emit_byte(ctx, (data >> 8) & 0xff); +} + +void acpigen_emit_dword(struct acpi_ctx *ctx, unsigned int data) +{ + 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); +} + +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_ones(struct acpi_ctx *ctx) +{ + acpigen_emit_byte(ctx, ONES_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_one(ctx); +} + +void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name) +{ + acpigen_write_name(ctx, name); + acpigen_write_zero(ctx); +} + +void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, u8 val) +{ + acpigen_write_name(ctx, name); + acpigen_write_byte(ctx, val); +} + +void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, u32 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 *string) +{ + acpigen_emit_stream(ctx, string, string ? strlen(string) : 0); + acpigen_emit_byte(ctx, '\0'); /* NUL */ +} + +void acpigen_write_string(struct acpi_ctx *ctx, const char *string) +{ + acpigen_emit_byte(ctx, STRING_PREFIX); + acpigen_emit_string(ctx, string); +} + +void acpigen_write_coreboot_hid(struct acpi_ctx *ctx, enum coreboot_acpi_ids id) +{ + char hid[9]; /* BOOTxxxx */ + + snprintf(hid, sizeof(hid), "%.4s%04X", COREBOOT_ACPI_ID, id); + acpigen_write_name_string(ctx, "_HID", hid); +} + +/* + * 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, 18.2.2 and 18.4 of ACPI spec 3.0 for details. + */ + +static void acpigen_emit_simple_namestring(struct acpi_ctx *ctx, + const char *name) +{ + char ud[] = "____"; + int i; + + for (i = 0; i < 4; i++) { + if ((name[i] == '\0') || (name[i] == '.')) { + acpigen_emit_stream(ctx, ud, 4 - i); + break; + } + acpigen_emit_byte(ctx, name[i]); + } +} + +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) +{ + int count = 0; + unsigned char *pathlen; + + acpigen_emit_byte(ctx, MULTI_NAME_PREFIX); + acpigen_emit_byte(ctx, ZERO_OP); + pathlen = ((unsigned char *)ctx->current) - 1; + + while (name[0] != '\0') { + acpigen_emit_simple_namestring(ctx, name); + /* find end or next entity */ + while ((name[0] != '.') && (name[0] != '\0')) + name++; + /* forward to next */ + if (name[0] == '.') + name++; + count++; + } + + pathlen[0] = count; +} + +void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath) +{ + int dotcount = 0, i; + int dotpos = 0; + + /* We can start with a '\'. */ + if (namepath[0] == '\\') { + acpigen_emit_byte(ctx, '\\'); + namepath++; + } + + /* And there can be any number of '^' */ + while (namepath[0] == '^') { + acpigen_emit_byte(ctx, '^'); + namepath++; + } + + /* + * If we have only \\ or only ^...^ then we need to add a null name + * (0x00) + */ + if (namepath[0] == '\0') { + acpigen_emit_byte(ctx, ZERO_OP); + return; + } + + i = 0; + while (namepath[i] != '\0') { + if (namepath[i] == '.') { + dotcount++; + dotpos = i; + } + i++; + } + + 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 *name) +{ + acpigen_emit_byte(ctx, NAME_OP); + acpigen_emit_namestring(ctx, name); +} + +void acpigen_write_scope(struct acpi_ctx *ctx, const char *name) +{ + acpigen_emit_byte(ctx, SCOPE_OP); + acpigen_write_len_f(ctx); + acpigen_emit_namestring(ctx, name); +} + +void acpigen_write_processor(struct acpi_ctx *ctx, u8 cpuindex, u32 pblock_addr, + u8 pblock_len) +{ + /* + * Processor (\_PR.CPcpuindex, cpuindex, pblock_addr, pblock_len) + * { + */ + char pscope[16]; + + acpigen_emit_ext_op(ctx, PROCESSOR_OP); + acpigen_write_len_f(ctx); + + snprintf(pscope, sizeof(pscope), ACPI_CPU_STRING, (uint)cpuindex); + acpigen_emit_namestring(ctx, pscope); + acpigen_emit_byte(ctx, cpuindex); + acpigen_emit_dword(ctx, pblock_addr); + acpigen_emit_byte(ctx, pblock_len); +} + +void acpigen_write_processor_package(struct acpi_ctx *ctx, + const char *const name, + const uint first_core, + const uint core_count) +{ + uint i; + char pscope[16]; + + acpigen_write_name(ctx, name); + acpigen_write_package(ctx, core_count); + for (i = first_core; i < first_core + core_count; ++i) { + snprintf(pscope, sizeof(pscope), ACPI_CPU_STRING, i); + acpigen_emit_namestring(ctx, pscope); + } + acpigen_pop_len(ctx); +} + +/* Method to notify all CPU cores */ +void acpigen_write_processor_cnot(struct acpi_ctx *ctx, const uint num_cores) +{ + int core_id; + + acpigen_write_method(ctx, "\\_PR.CNOT", 1); + for (core_id = 0; core_id < num_cores; core_id++) { + char buffer[30]; + + snprintf(buffer, sizeof(buffer), ACPI_CPU_STRING, core_id); + acpigen_emit_byte(ctx, NOTIFY_OP); + acpigen_emit_namestring(ctx, buffer); + acpigen_emit_byte(ctx, ARG0_OP); + } + acpigen_pop_len(ctx); +} + +/* + * Generate ACPI AML code for OperationRegion + * Arg0: Pointer to struct opregion opreg = OPREGION(rname, space, offset, len) + * where rname is region name, space is region space, offset is region offset & + * len is region length. + * OperationRegion(regionname, regionspace, regionoffset, regionlength) + */ +void acpigen_write_opregion(struct acpi_ctx *ctx, struct opregion *opreg) +{ + /* OpregionOp */ + acpigen_emit_ext_op(ctx, OPREGION_OP); + /* NameString 4 chars only */ + acpigen_emit_simple_namestring(ctx, opreg->name); + /* RegionSpace */ + acpigen_emit_byte(ctx, opreg->regionspace); + /* RegionOffset & RegionLen, it can be byte word or double word */ + acpigen_write_integer(ctx, opreg->regionoffset); + acpigen_write_integer(ctx, opreg->regionlen); +} + +static void acpigen_write_field_length(struct acpi_ctx *ctx, u32 len) +{ + u8 i, j; + u8 emit[4]; + + i = 1; + if (len < 0x40) { + emit[0] = len & 0x3F; + } else { + emit[0] = len & 0xF; + len >>= 4; + while (len) { + emit[i] = len & 0xFF; + i++; + len >>= 8; + } + } + /* Update bit 7:6 : Number of bytes followed by emit[0] */ + emit[0] |= (i - 1) << 6; + + for (j = 0; j < i; j++) + acpigen_emit_byte(ctx, emit[j]); +} + +static int acpigen_write_field_offset(struct acpi_ctx *ctx, u32 offset, + u32 current_bit_pos) +{ + u32 diff_bits; + + if (offset < current_bit_pos) { + log_warning("Cannot move offset backward"); + return -ESPIPE; + } + + diff_bits = offset - current_bit_pos; + /* Upper limit */ + if (diff_bits > 0xFFFFFFF) { + log_warning("Offset very large to encode"); + return E2BIG; + } + + acpigen_emit_byte(ctx, 0); + acpigen_write_field_length(ctx, diff_bits); + + return 0; +} + +static void acpigen_write_field_name(struct acpi_ctx *ctx, const char *name, + u32 size) +{ + acpigen_emit_simple_namestring(ctx, name); + acpigen_write_field_length(ctx, size); +} + +/* + * Generate ACPI AML code for Field + * Arg0: region name + * Arg1: Pointer to struct fieldlist. + * Arg2: no. of entries in Arg1 + * Arg3: flags which indicate filed access type, lock rule & update rule. + * Example with fieldlist + * struct fieldlist l[] = { + * FIELDLIST_OFFSET(0x84), + * FIELDLIST_NAMESTR("PMCS", 2), + * }; + * acpigen_write_field("UART", l, ARRAY_SIZE(l), FIELD_ANYACC | FIELD_NOLOCK | + * FIELD_PRESERVE); + * Output: + * Field (UART, AnyAcc, NoLock, Preserve) + * { + * Offset (0x84), + * PMCS, 2 + * } + */ +int acpigen_write_field(struct acpi_ctx *ctx, const char *name, + struct fieldlist *l, size_t count, uint flags) +{ + int i; + u32 current_bit_pos = 0; + int ret; + + /* FieldOp */ + acpigen_emit_ext_op(ctx, FIELD_OP); + /* Package Length */ + acpigen_write_len_f(ctx); + /* NameString 4 chars only */ + acpigen_emit_simple_namestring(ctx, name); + /* Field Flag */ + acpigen_emit_byte(ctx, flags); + + for (i = 0; i < count; i++) { + switch (l[i].type) { + case NAME_STRING: + acpigen_write_field_name(ctx, l[i].name, l[i].bits); + current_bit_pos += l[i].bits; + break; + case OFFSET: + ret = acpigen_write_field_offset(ctx, l[i].bits, + current_bit_pos); + if (ret) + return log_msg_ret("field", ret); + current_bit_pos = l[i].bits; + break; + default: + log_err("Invalid field type %#x\n", l[i].type); + return -EDOM; + } + } + acpigen_pop_len(ctx); + + return 0; +} + +/* + * Generate ACPI AML code for IndexField + * Arg0: region name + * Arg1: Pointer to struct fieldlist. + * Arg2: no. of entries in Arg1 + * Arg3: flags which indicate filed access type, lock rule & update rule. + * Example with fieldlist + * struct fieldlist l[] = { + * FIELDLIST_OFFSET(0x84), + * FIELDLIST_NAMESTR("PMCS", 2), + * }; + * acpigen_write_field("IDX", "DATA" l, ARRAY_SIZE(l), FIELD_ANYACC | + * FIELD_NOLOCK | + * FIELD_PRESERVE); + * Output: + * IndexField (IDX, DATA, AnyAcc, NoLock, Preserve) + * { + * Offset (0x84), + * PMCS, 2 + * } + */ +int acpigen_write_indexfield(struct acpi_ctx *ctx, const char *idx, + const char *data, struct fieldlist *l, + size_t count, uint flags) +{ + u16 i; + u32 current_bit_pos = 0; + int ret; + + /* FieldOp */ + acpigen_emit_ext_op(ctx, INDEX_FIELD_OP); + /* Package Length */ + acpigen_write_len_f(ctx); + /* NameString 4 chars only */ + acpigen_emit_simple_namestring(ctx, idx); + /* NameString 4 chars only */ + acpigen_emit_simple_namestring(ctx, data); + /* Field Flag */ + acpigen_emit_byte(ctx, flags); + + for (i = 0; i < count; i++) { + switch (l[i].type) { + case NAME_STRING: + acpigen_write_field_name(ctx, l[i].name, l[i].bits); + current_bit_pos += l[i].bits; + break; + case OFFSET: + ret = acpigen_write_field_offset(ctx, l[i].bits, + current_bit_pos); + if (ret) + return log_msg_ret("field", ret); + current_bit_pos = l[i].bits; + break; + default: + log_err("Invalid field type 0x%X\n", l[i].type); + return -EDOM; + } + } + acpigen_pop_len(ctx); + + return 0; +} + +void acpigen_write_empty_pct(struct acpi_ctx *ctx) +{ + /* + * Name (_PCT, Package (0x02) + * { + * ResourceTemplate () + * { + * Register (FFixedHW, + * 0x00, // Bit Width + * 0x00, // Bit Offset + * 0x0000000000000000, // Address + * ,) + * }, + * + * ResourceTemplate () + * { + * Register (FFixedHW, + * 0x00, // Bit Width + * 0x00, // Bit Offset + * 0x0000000000000000, // Address + * ,) + * } + * }) + */ + static char stream[] = { + /* 00000030 "0._PCT.," */ + 0x08, 0x5F, 0x50, 0x43, 0x54, 0x12, 0x2C, + /* 00000038 "........" */ + 0x02, 0x11, 0x14, 0x0A, 0x11, 0x82, 0x0C, 0x00, + /* 00000040 "........" */ + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000048 "....y..." */ + 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x11, 0x14, + /* 00000050 "........" */ + 0x0A, 0x11, 0x82, 0x0C, 0x00, 0x7F, 0x00, 0x00, + /* 00000058 "........" */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x79, 0x00 + }; + acpigen_emit_stream(ctx, stream, ARRAY_SIZE(stream)); +} + +void acpigen_write_empty_ptc(struct acpi_ctx *ctx) +{ + /* + * Name (_PTC, Package (0x02) + * { + * ResourceTemplate () + * { + * Register (FFixedHW, + * 0x00, // Bit Width + * 0x00, // Bit Offset + * 0x0000000000000000, // Address + * ,) + * }, + * + * ResourceTemplate () + * { + * Register (FFixedHW, + * 0x00, // Bit Width + * 0x00, // Bit Offset + * 0x0000000000000000, // Address + * ,) + * } + * }) + */ + struct acpi_gen_regaddr addr = { + .space_id = ACPI_ADDRESS_SPACE_FIXED, + .bit_width = 0, + .bit_offset = 0, + .access_size = 0, + .addrl = 0, + .addrh = 0, + }; + + acpigen_write_name(ctx, "_PTC"); + acpigen_write_package(ctx, 2); + + /* ControlRegister */ + acpigen_write_register_resource(ctx, &addr); + + /* StatusRegister */ + acpigen_write_register_resource(ctx, &addr); + + acpigen_pop_len(ctx); +} + +static void __acpigen_write_method(struct acpi_ctx *ctx, const char *name, + u8 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(ctx, name, (nargs & 7)); +} + +/* Method (name, nargs, Serialized) */ +void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name, + int nargs) +{ + __acpigen_write_method(ctx, name, (nargs & 7) | (1 << 3)); +} + +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, u8 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); +} + +/* + * Generates a func with max supported P-states. + */ +void acpigen_write_ppc(struct acpi_ctx *ctx, u8 nr) +{ + /* + * Method (_PPC, 0, NotSerialized) + * { + * Return (nr) + * } + */ + acpigen_write_method(ctx, "_PPC", 0); + acpigen_emit_byte(ctx, RETURN_OP); + /* arg */ + acpigen_write_byte(ctx, nr); + acpigen_pop_len(ctx); +} + +/* + * Generates a func with max supported P-states saved + * in the variable PPCM. + */ +void acpigen_write_ppc_nvs(struct acpi_ctx *ctx) +{ + /* + * Method (_PPC, 0, NotSerialized) + * { + * Return (PPCM) + * } + */ + acpigen_write_method(ctx, "_PPC", 0); + acpigen_emit_byte(ctx, RETURN_OP); + /* arg */ + acpigen_emit_namestring(ctx, "PPCM"); + acpigen_pop_len(ctx); +} + +void acpigen_write_tpc(struct acpi_ctx *ctx, const char *gnvs_tpc_limit) +{ + /* + * // Sample _TPC method + * Method (_TPC, 0, NotSerialized) + * { + * Return (\TLVL) + * } + */ + acpigen_write_method(ctx, "_TPC", 0); + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_emit_namestring(ctx, gnvs_tpc_limit); + acpigen_pop_len(ctx); +} + +void acpigen_write_prw(struct acpi_ctx *ctx, u32 wake, u32 level) +{ + /* + * Name (_PRW, Package () { wake, level } + */ + acpigen_write_name(ctx, "_PRW"); + acpigen_write_package(ctx, 2); + acpigen_write_integer(ctx, wake); + acpigen_write_integer(ctx, level); + acpigen_pop_len(ctx); +} + +void acpigen_write_pss_package(struct acpi_ctx *ctx, u32 core_freq, u32 power, + u32 trans_lat, u32 busm_lat, u32 control, + u32 status) +{ + acpigen_write_package(ctx, 6); + acpigen_write_dword(ctx, core_freq); + acpigen_write_dword(ctx, power); + acpigen_write_dword(ctx, trans_lat); + acpigen_write_dword(ctx, busm_lat); + acpigen_write_dword(ctx, control); + acpigen_write_dword(ctx, status); + acpigen_pop_len(ctx); + + log_debug("PSS: %uMHz power %u control 0x%x status 0x%x\n", + core_freq, power, control, status); +} + +void acpigen_write_psd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs, + enum psd_coord coordtype) +{ + acpigen_write_name(ctx, "_PSD"); + acpigen_write_package(ctx, 1); + acpigen_write_package(ctx, 5); + acpigen_write_byte(ctx, 5); // 5 values + acpigen_write_byte(ctx, 0); // revision 0 + acpigen_write_dword(ctx, domain); + acpigen_write_dword(ctx, coordtype); + acpigen_write_dword(ctx, numprocs); + acpigen_pop_len(ctx); + acpigen_pop_len(ctx); +} + +void acpigen_write_cst_package_entry(struct acpi_ctx *ctx, + struct acpi_cstate *cstate) +{ + acpigen_write_package(ctx, 4); + acpigen_write_register_resource(ctx, &cstate->resource); + acpigen_write_dword(ctx, cstate->ctype); + acpigen_write_dword(ctx, cstate->latency); + acpigen_write_dword(ctx, cstate->power); + acpigen_pop_len(ctx); +} + +void acpigen_write_cst_package(struct acpi_ctx *ctx, struct acpi_cstate *cstate, + int nentries) +{ + int i; + + acpigen_write_name(ctx, "_CST"); + acpigen_write_package(ctx, nentries + 1); + acpigen_write_dword(ctx, nentries); + + for (i = 0; i < nentries; i++) + acpigen_write_cst_package_entry(ctx, cstate + i); + + acpigen_pop_len(ctx); +} + +void acpigen_write_csd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs, + enum csd_coord coordtype, u32 index) +{ + acpigen_write_name(ctx, "_CSD"); + acpigen_write_package(ctx, 1); + acpigen_write_package(ctx, 6); + acpigen_write_byte(ctx, 6); // 6 values + acpigen_write_byte(ctx, 0); // revision 0 + acpigen_write_dword(ctx, domain); + acpigen_write_dword(ctx, coordtype); + acpigen_write_dword(ctx, numprocs); + acpigen_write_dword(ctx, index); + acpigen_pop_len(ctx); + acpigen_pop_len(ctx); +} + +void acpigen_write_tss_package(struct acpi_ctx *ctx, int entries, + struct acpi_tstate *tstate_list) +{ + /* + * Sample _TSS package with 100% and 50% duty cycles + * Name (_TSS, Package (0x02) + * { + * Package(){100, 1000, 0, 0x00, 0) + * Package(){50, 520, 0, 0x18, 0) + * }) + */ + int i; + struct acpi_tstate *tstate = tstate_list; + + acpigen_write_name(ctx, "_TSS"); + acpigen_write_package(ctx, entries); + + for (i = 0; i < entries; i++) { + acpigen_write_package(ctx, 5); + acpigen_write_dword(ctx, tstate->percent); + acpigen_write_dword(ctx, tstate->power); + acpigen_write_dword(ctx, tstate->latency); + acpigen_write_dword(ctx, tstate->control); + acpigen_write_dword(ctx, tstate->status); + acpigen_pop_len(ctx); + tstate++; + } + + acpigen_pop_len(ctx); +} + +void acpigen_write_tsd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs, + enum psd_coord coordtype) +{ + acpigen_write_name(ctx, "_TSD"); + acpigen_write_package(ctx, 1); + acpigen_write_package(ctx, 5); + acpigen_write_byte(ctx, 5); // 5 values + acpigen_write_byte(ctx, 0); // revision 0 + acpigen_write_dword(ctx, domain); + acpigen_write_dword(ctx, coordtype); + acpigen_write_dword(ctx, numprocs); + acpigen_pop_len(ctx); + acpigen_pop_len(ctx); +} + +void acpigen_write_mem32fixed(struct acpi_ctx *ctx, int readwrite, u32 base, + u32 size) +{ + /* + * ACPI 4.0 section 6.4.3.4: 32-Bit Fixed Memory Range Descriptor + * Byte 0: + * Bit7 : 1 => big item + * Bit6-0: 0000110 (0x6) => 32-bit fixed memory + */ + acpigen_emit_byte(ctx, 0x86); + /* Byte 1+2: length (0x0009) */ + acpigen_emit_byte(ctx, 0x09); + acpigen_emit_byte(ctx, 0x00); + /* bit1-7 are ignored */ + acpigen_emit_byte(ctx, readwrite ? 0x01 : 0x00); + acpigen_emit_dword(ctx, base); + acpigen_emit_dword(ctx, size); +} + +static void acpigen_write_register(struct acpi_ctx *ctx, + const struct acpi_gen_regaddr *addr) +{ + acpigen_emit_byte(ctx, 0x82); /* Register Descriptor */ + 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_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); +} + +void acpigen_write_irq(struct acpi_ctx *ctx, u16 mask) +{ + /* + * ACPI 3.0b section 6.4.2.1: IRQ Descriptor + * Byte 0: + * Bit7 : 0 => small item + * Bit6-3: 0100 (0x4) => IRQ port descriptor + * Bit2-0: 010 (0x2) => 2 Bytes long + */ + acpigen_emit_byte(ctx, 0x22); + acpigen_emit_byte(ctx, mask & 0xff); + acpigen_emit_byte(ctx, (mask >> 8) & 0xff); +} + +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 + * calclulation in acpigen_write_resourcetemplate() 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; + /* + * end tag (acpi 4.0 Section 6.4.2.8) + * 0x79 + * 0x00 is treated as a good checksum according to the spec + * and is what iasl generates. + */ + acpigen_emit_byte(ctx, 0x79); + acpigen_emit_byte(ctx, 0x00); + + /* + * Start counting past the 2-bytes length added in + * acpigen_write_resourcetemplate() above + */ + len = (char *)ctx->current - (p + 2); + + /* patch len word */ + p[0] = len & 0xff; + p[1] = (len >> 8) & 0xff; + /* patch len field */ + acpigen_pop_len(ctx); +} + +/* + * ToUUID(uuid) + * + * ACPI 6.1 Section 19.6.136 table 19-385 defines a special output + * order for the bytes that make up a UUID Buffer object. + * UUID byte order for input: + * aabbccdd-eeff-gghh-iijj-kkllmmnnoopp + * UUID byte order for output: + * ddccbbaa-ffee-hhgg-iijj-kkllmmnnoopp + */ +#define UUID_LEN 16 +int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid) +{ + u8 buf[UUID_LEN]; + size_t i, order[UUID_LEN] = { 3, 2, 1, 0, 5, 4, 7, 6, + 8, 9, 10, 11, 12, 13, 14, 15 }; + int ret; + + /* Parse UUID string into bytes */ + ret = uuid_str_to_bin(uuid, buf, UUID_STR_FORMAT_STD); + 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_LEN); + + /* Output UUID in expected order */ + for (i = 0; i < UUID_LEN; i++) + acpigen_emit_byte(ctx, buf[order[i]]); + + acpigen_pop_len(ctx); + + return 0; +} + +/* + * Name (_PRx, Package(One) { name }) + * ... + * PowerResource (name, level, order) + */ +void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, u8 level, + u16 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); +} + +/* Store (src, dst) */ +void acpigen_write_store_ops(struct acpi_ctx *ctx, u8 src, u8 dst) +{ + acpigen_write_store(ctx); + acpigen_emit_byte(ctx, src); + acpigen_emit_byte(ctx, dst); +} + +/* 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); +} + +/* Store (val, DEBUG) */ +void acpigen_write_debug_integer(struct acpi_ctx *ctx, u64 val) +{ + acpigen_write_store(ctx); + acpigen_write_integer(ctx, val); + acpigen_emit_ext_op(ctx, DEBUG_OP); +} + +/* Store (op, DEBUG) */ +void acpigen_write_debug_op(struct acpi_ctx *ctx, u8 op) +{ + acpigen_write_store(ctx); + acpigen_emit_byte(ctx, op); + acpigen_emit_ext_op(ctx, DEBUG_OP); +} + +void acpigen_write_if(struct acpi_ctx *ctx) +{ + acpigen_emit_byte(ctx, IF_OP); + acpigen_write_len_f(ctx); +} + +/* If (And (arg1, arg2)) */ +void acpigen_write_if_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2) +{ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, AND_OP); + acpigen_emit_byte(ctx, arg1); + acpigen_emit_byte(ctx, arg2); +} + +/* + * Generates ACPI code for checking if operand1 and operand2 are equal, where, + * operand1 is ACPI op and operand2 is an integer. + * + * If (Lequal (op, val)) + */ +void acpigen_write_if_lequal_op_int(struct acpi_ctx *ctx, u8 op, u64 val) +{ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, LEQUAL_OP); + acpigen_emit_byte(ctx, op); + acpigen_write_integer(ctx, val); +} + +void acpigen_write_else(struct acpi_ctx *ctx) +{ + acpigen_emit_byte(ctx, ELSE_OP); + acpigen_write_len_f(ctx); +} + +void acpigen_write_to_buffer(struct acpi_ctx *ctx, u8 src, u8 dst) +{ + acpigen_emit_byte(ctx, TO_BUFFER_OP); + acpigen_emit_byte(ctx, src); + acpigen_emit_byte(ctx, dst); +} + +void acpigen_write_to_integer(struct acpi_ctx *ctx, u8 src, u8 dst) +{ + acpigen_emit_byte(ctx, TO_INTEGER_OP); + acpigen_emit_byte(ctx, src); + acpigen_emit_byte(ctx, dst); +} + +void acpigen_write_byte_buffer(struct acpi_ctx *ctx, u8 *arr, size_t size) +{ + size_t i; + + acpigen_emit_byte(ctx, BUFFER_OP); + acpigen_write_len_f(ctx); + acpigen_write_integer(ctx, size); + + for (i = 0; i < size; i++) + acpigen_emit_byte(ctx, arr[i]); + + acpigen_pop_len(ctx); +} + +void acpigen_write_return_byte_buffer(struct acpi_ctx *ctx, u8 *arr, + size_t size) +{ + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_write_byte_buffer(ctx, arr, size); +} + +void acpigen_write_return_singleton_buffer(struct acpi_ctx *ctx, u8 arg) +{ + acpigen_write_return_byte_buffer(ctx, &arg, 1); +} + +void acpigen_write_return_byte(struct acpi_ctx *ctx, u8 arg) +{ + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_write_byte(ctx, arg); +} + +void acpigen_write_return_integer(struct acpi_ctx *ctx, u64 arg) +{ + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_write_integer(ctx, arg); +} + +void acpigen_write_return_string(struct acpi_ctx *ctx, const char *arg) +{ + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_write_string(ctx, arg); +} + +void acpigen_write_upc(struct acpi_ctx *ctx, enum acpi_upc_type type) +{ + acpigen_write_name(ctx, "_UPC"); + acpigen_write_package(ctx, 4); + /* Connectable */ + acpigen_write_byte(ctx, type == UPC_TYPE_UNUSED ? 0 : 0xff); + /* Type */ + acpigen_write_byte(ctx, type); + /* Reserved0 */ + acpigen_write_zero(ctx); + /* Reserved1 */ + acpigen_write_zero(ctx); + acpigen_pop_len(ctx); +} + +int acpigen_write_dsm(struct acpi_ctx *ctx, const char *uuid, + hid_callback_func callbacks[], size_t count, void *arg) +{ + struct dsm_uuid id = DSM_UUID(uuid, callbacks, count, arg); + int ret; + + ret = acpigen_write_dsm_uuid_arr(ctx, &id, 1); + if (ret) + return log_msg_ret("uuid_arr", ret); + + return 0; +} + +static int acpigen_write_dsm_uuid(struct acpi_ctx *ctx, struct dsm_uuid *id) +{ + size_t i; + int ret; + + /* If (LEqual (Local0, ToUUID(uuid))) */ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, LEQUAL_OP); + acpigen_emit_byte(ctx, LOCAL0_OP); + ret = acpigen_write_uuid(ctx, id->uuid); + if (ret) + return log_msg_ret("uuid", ret); + + /* ToInteger (Arg2, Local1) */ + acpigen_write_to_integer(ctx, ARG2_OP, LOCAL1_OP); + + for (i = 0; i < id->count; i++) { + /* If (LEqual (Local1, i)) */ + acpigen_write_if_lequal_op_int(ctx, LOCAL1_OP, i); + + /* Callback to write if handler. */ + if (id->callbacks[i]) + id->callbacks[i](ctx, id->arg); + + acpigen_pop_len(ctx); /* If */ + } + + /* Default case: Return (Buffer (One) { 0x0 }) */ + acpigen_write_return_singleton_buffer(ctx, 0x0); + + acpigen_pop_len(ctx); /* If (LEqual (Local0, ToUUID(uuid))) */ + + return 0; +} + +/* + * Generate ACPI AML code for _DSM method. + * This function takes as input array of uuid for the device, set of callbacks + * and argument to pass into the callbacks. Callbacks should ensure that Local0 + * and Local1 are left untouched. Use of Local2-Local7 is permitted in + * callbacks. + * + * Arguments passed into _DSM method: + * Arg0 = UUID + * Arg1 = Revision + * Arg2 = Function index + * Arg3 = Function specific arguments + * + * AML code generated would look like: + * Method (_DSM, 4, Serialized) { + * ToBuffer (Arg0, Local0) + * If (LEqual (Local0, ToUUID(uuid))) { + * ToInteger (Arg2, Local1) + * If (LEqual (Local1, 0)) { + * + * } + * ... + * If (LEqual (Local1, n)) { + * + * } + * Return (Buffer (One) { 0x0 }) + * } + * ... + * If (LEqual (Local0, ToUUID(uuidn))) { + * ... + * } + * Return (Buffer (One) { 0x0 }) + * } + */ +int acpigen_write_dsm_uuid_arr(struct acpi_ctx *ctx, struct dsm_uuid *ids, + size_t count) +{ + size_t i; + int ret; + + /* Method (_DSM, 4, Serialized) */ + acpigen_write_method_serialized(ctx, "_DSM", 0x4); + + /* ToBuffer (Arg0, Local0) */ + acpigen_write_to_buffer(ctx, ARG0_OP, LOCAL0_OP); + + for (i = 0; i < count; i++) { + ret = acpigen_write_dsm_uuid(ctx, &ids[i]); + if (ret) + return ret; + } + + /* Return (Buffer (One) { 0x0 }) */ + acpigen_write_return_singleton_buffer(ctx, 0x0); + + acpigen_pop_len(ctx); /* Method _DSM */ + + return 0; +} + +#define CPPC_PACKAGE_NAME "\\GCPC" + +int acpigen_write_cppc_package(struct acpi_ctx *ctx, + const struct cppc_config *config) +{ + u32 i; + u32 max; + + switch (config->version) { + case 1: + max = CPPC_MAX_FIELDS_VER_1; + break; + case 2: + max = CPPC_MAX_FIELDS_VER_2; + break; + case 3: + max = CPPC_MAX_FIELDS_VER_3; + break; + default: + log_err("CPPC version %u is not implemented\n", + config->version); + return -EDOM; + } + acpigen_write_name(ctx, CPPC_PACKAGE_NAME); + + /* Adding 2 to account for length and version fields */ + acpigen_write_package(ctx, max + 2); + acpigen_write_dword(ctx, max + 2); + + acpigen_write_byte(ctx, config->version); + + for (i = 0; i < max; ++i) { + const struct acpi_gen_regaddr *reg = &config->regs[i]; + + if (reg->space_id == ACPI_ADDRESS_SPACE_MEMORY && + reg->bit_width == 32 && reg->access_size == 0) { + acpigen_write_dword(ctx, reg->addrl); + } else { + acpigen_write_register_resource(ctx, reg); + } + } + acpigen_pop_len(ctx); + + return 0; +} + +void acpigen_write_cppc_method(struct acpi_ctx *ctx) +{ + acpigen_write_method(ctx, "_CPC", 0); + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_emit_namestring(ctx, CPPC_PACKAGE_NAME); + acpigen_pop_len(ctx); +} + +/* + * Generate ACPI AML code for _ROM method. + * This function takes as input ROM data and ROM length. + * + * The ACPI spec isn't clear about what should happen at the end of the + * ROM. Tests showed that it shouldn't truncate, but fill the remaining + * bytes in the returned buffer with zeros. + * + * Arguments passed into _DSM method: + * Arg0 = Offset in Bytes + * Arg1 = Bytes to return + * + * Example: + * acpigen_write_rom(0xdeadbeef, 0x10000) + * + * AML code generated would look like: + * Method (_ROM, 2, NotSerialized) { + * + * OperationRegion("ROMS", SYSTEMMEMORY, 0xdeadbeef, 0x10000) + * Field (ROMS, AnyAcc, NoLock, Preserve) + * { + * Offset (0), + * RBF0, 0x80000 + * } + * + * Store (Arg0, Local0) + * Store (Arg1, Local1) + * + * If (LGreater (Local1, 0x1000)) + * { + * Store (0x1000, Local1) + * } + * + * Store (Local1, Local3) + * + * If (LGreater (Local0, 0x10000)) + * { + * Return(Buffer(Local1){0}) + * } + * + * If (LGreater (Local0, 0x0f000)) + * { + * Subtract (0x10000, Local0, Local2) + * If (LGreater (Local1, Local2)) + * { + * Store (Local2, Local1) + * } + * } + * + * Name (ROM1, Buffer (Local3) {0}) + * + * Multiply (Local0, 0x08, Local0) + * Multiply (Local1, 0x08, Local1) + * + * CreateField (RBF0, Local0, Local1, TMPB) + * Store (TMPB, ROM1) + * Return (ROM1) + * } + */ +int acpigen_write_rom(struct acpi_ctx *ctx, void *bios, const size_t length) +{ + int ret; + + assert(bios); + assert(length); + + /* Method (_ROM, 2, Serialized) */ + acpigen_write_method_serialized(ctx, "_ROM", 2); + + /* OperationRegion("ROMS", SYSTEMMEMORY, current, length) */ + struct opregion opreg = OPREGION("ROMS", SYSTEMMEMORY, + (uintptr_t)bios, length); + acpigen_write_opregion(ctx, &opreg); + + struct fieldlist l[] = { + FIELDLIST_OFFSET(0), + FIELDLIST_NAMESTR("RBF0", 8 * length), + }; + + /* + * Field (ROMS, AnyAcc, NoLock, Preserve) + * { + * Offset (0), + * RBF0, 0x80000 + * } + */ + ret = acpigen_write_field(ctx, opreg.name, l, 2, FIELD_ANYACC | + FIELD_NOLOCK | FIELD_PRESERVE); + if (ret) + return log_msg_ret("field", ret); + + /* Store (Arg0, Local0) */ + acpigen_write_store(ctx); + acpigen_emit_byte(ctx, ARG0_OP); + acpigen_emit_byte(ctx, LOCAL0_OP); + + /* Store (Arg1, Local1) */ + acpigen_write_store(ctx); + acpigen_emit_byte(ctx, ARG1_OP); + acpigen_emit_byte(ctx, LOCAL1_OP); + + /* ACPI SPEC requires to return at maximum 4KiB */ + /* If (LGreater (Local1, 0x1000)) */ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, LGREATER_OP); + acpigen_emit_byte(ctx, LOCAL1_OP); + acpigen_write_integer(ctx, 0x1000); + + /* Store (0x1000, Local1) */ + acpigen_write_store(ctx); + acpigen_write_integer(ctx, 0x1000); + acpigen_emit_byte(ctx, LOCAL1_OP); + + /* Pop if */ + acpigen_pop_len(ctx); + + /* Store (Local1, Local3) */ + acpigen_write_store(ctx); + acpigen_emit_byte(ctx, LOCAL1_OP); + acpigen_emit_byte(ctx, LOCAL3_OP); + + /* If (LGreater (Local0, length)) */ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, LGREATER_OP); + acpigen_emit_byte(ctx, LOCAL0_OP); + acpigen_write_integer(ctx, length); + + /* Return(Buffer(Local1){0}) */ + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_emit_byte(ctx, BUFFER_OP); + acpigen_write_len_f(ctx); + acpigen_emit_byte(ctx, LOCAL1_OP); + acpigen_emit_byte(ctx, 0); + acpigen_pop_len(ctx); + + /* Pop if */ + acpigen_pop_len(ctx); + + /* If (LGreater (Local0, length - 4096)) */ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, LGREATER_OP); + acpigen_emit_byte(ctx, LOCAL0_OP); + acpigen_write_integer(ctx, length - 4096); + + /* Subtract (length, Local0, Local2) */ + acpigen_emit_byte(ctx, SUBTRACT_OP); + acpigen_write_integer(ctx, length); + acpigen_emit_byte(ctx, LOCAL0_OP); + acpigen_emit_byte(ctx, LOCAL2_OP); + + /* If (LGreater (Local1, Local2)) */ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, LGREATER_OP); + acpigen_emit_byte(ctx, LOCAL1_OP); + acpigen_emit_byte(ctx, LOCAL2_OP); + + /* Store (Local2, Local1) */ + acpigen_write_store(ctx); + acpigen_emit_byte(ctx, LOCAL2_OP); + acpigen_emit_byte(ctx, LOCAL1_OP); + + /* Pop if */ + acpigen_pop_len(ctx); + + /* Pop if */ + acpigen_pop_len(ctx); + + /* Name (ROM1, Buffer (Local3) {0}) */ + acpigen_write_name(ctx, "ROM1"); + acpigen_emit_byte(ctx, BUFFER_OP); + acpigen_write_len_f(ctx); + acpigen_emit_byte(ctx, LOCAL3_OP); + acpigen_emit_byte(ctx, 0); + acpigen_pop_len(ctx); + + /* Multiply (Local1, 0x08, Local1) */ + acpigen_emit_byte(ctx, MULTIPLY_OP); + acpigen_emit_byte(ctx, LOCAL1_OP); + acpigen_write_integer(ctx, 0x08); + acpigen_emit_byte(ctx, LOCAL1_OP); + + /* Multiply (Local0, 0x08, Local0) */ + acpigen_emit_byte(ctx, MULTIPLY_OP); + acpigen_emit_byte(ctx, LOCAL0_OP); + acpigen_write_integer(ctx, 0x08); + acpigen_emit_byte(ctx, LOCAL0_OP); + + /* CreateField (RBF0, Local0, Local1, TMPB) */ + acpigen_emit_ext_op(ctx, CREATEFIELD_OP); + acpigen_emit_namestring(ctx, "RBF0"); + acpigen_emit_byte(ctx, LOCAL0_OP); + acpigen_emit_byte(ctx, LOCAL1_OP); + acpigen_emit_namestring(ctx, "TMPB"); + + /* Store (TMPB, ROM1) */ + acpigen_write_store(ctx); + acpigen_emit_namestring(ctx, "TMPB"); + acpigen_emit_namestring(ctx, "ROM1"); + + /* Return (ROM1) */ + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_emit_namestring(ctx, "ROM1"); + + /* Pop method */ + acpigen_pop_len(ctx); + + return 0; +} + +/* Soc-implemented functions -- weak definitions. */ +int __weak acpigen_soc_read_rx_gpio(struct acpi_ctx *ctx, unsigned int gpio_num) +{ + log_err("not implemented\n"); + acpigen_write_debug_string(ctx, "read_rx_gpio not available"); + + return -ENOTSUPP; +} + +int __weak acpigen_soc_get_tx_gpio(struct acpi_ctx *ctx, unsigned int gpio_num) +{ + log_err("not implemented\n"); + acpigen_write_debug_string(ctx, "get_tx_gpio not available"); + + return -ENOTSUPP; +} + +int __weak acpigen_soc_set_tx_gpio(struct acpi_ctx *ctx, unsigned int gpio_num) +{ + log_err("not implemented\n"); + acpigen_write_debug_string(ctx, "set_tx_gpio not available"); + + return -ENOTSUPP; +} + +int __weak acpigen_soc_clear_tx_gpio(struct acpi_ctx *ctx, + unsigned int gpio_num) +{ + log_err("not implemented\n"); + acpigen_write_debug_string(ctx, "clear_tx_gpio not available"); + + return -ENOTSUPP; +} + +/* + * Helper functions for enabling/disabling Tx GPIOs based on the GPIO + * polarity. These functions end up calling acpigen_soc_{set,clear}_tx_gpio to + * make callbacks into SoC acpigen code. + * + * Returns 0 on success and -1 on error. + */ +int acpigen_enable_tx_gpio(struct acpi_ctx *ctx, struct acpi_gpio *gpio) +{ + int ret; + + if (gpio->polarity == ACPI_GPIO_ACTIVE_HIGH) + ret = acpigen_soc_set_tx_gpio(ctx, gpio->pins[0]); + else + ret = acpigen_soc_clear_tx_gpio(ctx, gpio->pins[0]); + if (ret) + return log_msg_ret("call", ret); + + return 0; +} + +int acpigen_disable_tx_gpio(struct acpi_ctx *ctx, struct acpi_gpio *gpio) +{ + int ret; + + if (gpio->polarity == ACPI_GPIO_ACTIVE_LOW) + return acpigen_soc_set_tx_gpio(ctx, gpio->pins[0]); + else + return acpigen_soc_clear_tx_gpio(ctx, gpio->pins[0]); + if (ret) + return log_msg_ret("call", ret); + + return 0; +} From patchwork Mon Mar 9 03:44:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243403 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:55 -0600 Subject: [PATCH v2 31/39] gpio: Add a method to convert a GPIO to ACPI In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.31.I29adaf758a9ea81f51ffbecf89b53983968a0d2a@changeid> When generating ACPI tables we need to convert GPIOs in U-Boot to the ACPI structures required by ACPI. This is a SoC-specific conversion and cannot be handled by generic code, so add a new GPIO method to do the conversion. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: None drivers/gpio/gpio-uclass.c | 21 +++++++++++++++++++++ include/acpi_device.h | 12 ++++++++++++ include/asm-generic/gpio.h | 27 +++++++++++++++++++++++++++ lib/acpi/acpi_device.c | 16 ++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 0a22441d38..7bd656ad63 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -697,6 +698,26 @@ 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; + + 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/include/acpi_device.h b/include/acpi_device.h index f97bd075ec..acd26c0f54 100644 --- a/include/acpi_device.h +++ b/include/acpi_device.h @@ -533,6 +533,18 @@ size_t acpi_dp_add_property_list(struct acpi_dp *dp, */ int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table); +/** + * 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. The type is always ACPI_GPIO_TYPE_IO. + * + * @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_i2c_dev() - Write an I2C device to ACPI, including * information ACPI needs to use it. diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 05777e6afe..b594a273fa 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -9,6 +9,7 @@ #include +struct acpi_gpio; struct ofnode_phandle_args; /* @@ -290,6 +291,20 @@ struct dm_gpio_ops { */ int (*xlate)(struct udevice *dev, struct gpio_desc *desc, struct ofnode_phandle_args *args); + +#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 }; /** @@ -657,4 +672,16 @@ int dm_gpio_set_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/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 145de90677..adc32f1216 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -288,6 +288,22 @@ int acpi_device_write_gpio(struct acpi_ctx *ctx, const struct acpi_gpio *gpio) return 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) + return log_msg_ret("gpio", ret); + + return 0; +} + /* ACPI 6.1 section 6.4.3.8.2.1 - I2cSerialBus() */ static void acpi_device_write_i2c(struct acpi_ctx *ctx, const struct acpi_i2c *i2c) From patchwork Mon Mar 9 03:44:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243404 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:56 -0600 Subject: [PATCH v2 32/39] irq: Add a method to convert an interrupt to ACPI In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.32.I29adaf758a9ea81f51ffbecf89b53983968a0d2a@changeid> When generating ACPI tables we need to convert IRQs in U-Boot to the ACPI structures required by ACPI. This is a SoC-specific conversion and cannot be handled by generic code, so add a new IRQ method to do the conversion. Signed-off-by: Simon Glass --- Changes in v2: None drivers/misc/irq-uclass.c | 18 +++++++- include/acpi_device.h | 27 +++++++++++ include/irq.h | 41 +++++++++++++++++ lib/acpi/acpi_device.c | 94 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 2 deletions(-) diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c index 61aa10e465..b4a8b7b429 100644 --- a/drivers/misc/irq-uclass.c +++ b/drivers/misc/irq-uclass.c @@ -153,8 +153,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; @@ -176,6 +174,22 @@ int irq_first_device_type(enum irq_dev_t type, struct udevice **devp) 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/include/acpi_device.h b/include/acpi_device.h index acd26c0f54..50ba9b66aa 100644 --- a/include/acpi_device.h +++ b/include/acpi_device.h @@ -545,6 +545,33 @@ int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table); int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, const struct gpio_desc *desc); +/** + * acpi_device_write_interrupt_irq() - Write an interrupt to ACPI + * + * This creates an interrupt descriptor for an interrupt, including information + * ACPI needs to use it. + * + * @req_irq: Interrupt to write + * @return 0 if OK, -ve on error + */ +int acpi_device_write_interrupt_irq(struct acpi_ctx *ctx, + const struct irq *req_irq); + +/** + * acpi_device_write_interrupt_or_gpio() - Write interrupt or GPIO to ACPI + * + * This reads the an interrupt from the device tree, if available. If not it + * reads the first GPIO with the name @prop. + * + * If an interrupt is found, that is written to ACPI. If not, but an GPIO is + * found, that is written. + * + * @return 0 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, including * information ACPI needs to use it. diff --git a/include/irq.h b/include/irq.h index d4948e6dc4..8527e4dd79 100644 --- a/include/irq.h +++ b/include/irq.h @@ -8,6 +8,7 @@ #ifndef __irq_H #define __irq_H +struct acpi_irq; struct ofnode_phandle_args; /* @@ -26,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; }; /** @@ -121,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 * @@ -225,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/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index adc32f1216..aa5edfe807 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -154,6 +154,58 @@ int acpi_device_status(const struct udevice *dev) return ACPI_STATUS_DEVICE_ALL_ON; } +/* ACPI 6.1 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 || !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 = acpi_device_write_zero_len(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 = 1 << 0; /* ResourceConsumer */ + if (irq->mode == ACPI_IRQ_EDGE_TRIGGERED) + flags |= 1 << 1; + if (irq->polarity == ACPI_IRQ_ACTIVE_LOW) + flags |= 1 << 2; + if (irq->shared == ACPI_IRQ_SHARED) + flags |= 1 << 3; + if (irq->wake == ACPI_IRQ_WAKE) + flags |= 1 << 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) */ + acpi_device_fill_len(ctx, desc_length); + + return 0; +} + /* ACPI 6.1 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) { @@ -304,6 +356,48 @@ int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, 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 0; +} + +int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, + struct udevice *dev, const char *prop) +{ + struct irq req_irq; + int ret; + + ret = irq_get_by_index(dev, 0, &req_irq); + if (!ret) { + ret = acpi_device_write_interrupt_irq(ctx, &req_irq); + if (ret) + return log_msg_ret("irq", 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) + return log_msg_ret("gpio", ret); + } + + return 0; +} + /* ACPI 6.1 section 6.4.3.8.2.1 - I2cSerialBus() */ static void acpi_device_write_i2c(struct acpi_ctx *ctx, const struct acpi_i2c *i2c) From patchwork Mon Mar 9 03:44:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243406 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:57 -0600 Subject: [PATCH v2 33/39] acpi: Add support for SSDT generation In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.33.I5ed669af183c547e044a628e7c670f1ef73bfa3d@changeid> Some devices need to generate code for the Secondary System Descriptor Table (SSDT). Add a method to handle this. Signed-off-by: Simon Glass Reviewed-by: Wolfgang Wallner --- Changes in v2: - Switch parameter order of _acpi_fill_ssdt() and make it static arch/sandbox/dts/test.dts | 2 ++ drivers/core/acpi.c | 14 +++++++++++++ include/dm/acpi.h | 23 ++++++++++++++++++++++ test/dm/acpi.c | 41 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 1204c14b07..fefa3af999 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -208,10 +208,12 @@ acpi-test { compatible = "denx,u-boot-acpi-test"; + acpi-ssdt-test-data = "ab"; }; acpi-test2 { compatible = "denx,u-boot-acpi-test"; + acpi-ssdt-test-data = "cd"; }; clocks { diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 10bac38c1d..d2f6a9a9d1 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -17,6 +17,7 @@ /* Type of method to call */ enum method_t { METHOD_WRITE_TABLES, + METHOD_FILL_SDDT, }; /* Prototype for all methods */ @@ -49,6 +50,8 @@ 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_SDDT: + return aops->fill_ssdt; } } @@ -81,6 +84,17 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, return 0; } +int acpi_fill_ssdt(struct acpi_ctx *ctx) +{ + int ret; + + log_debug("Writing SSDT tables\n"); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SDDT); + log_debug("Writing SSDT finished, err=%d\n", ret); + + return ret; +} + int acpi_write_dev_tables(struct acpi_ctx *ctx) { int ret; diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 0f78a506da..c340c21685 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -72,6 +72,19 @@ 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. + * + * @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); }; #define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops) @@ -116,6 +129,16 @@ int acpi_return_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); + #endif /* __ACPI__ */ #endif diff --git a/test/dm/acpi.c b/test/dm/acpi.c index feb380b26c..305d8395ff 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -14,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -43,9 +43,21 @@ static int testacpi_get_name(const struct udevice *dev, char *out_name) return acpi_return_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"); + 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, }; static const struct udevice_id testacpi_ids[] = { @@ -313,3 +325,30 @@ 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_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'; /* sentinal */ + ut_assertok(acpi_fill_ssdt(&ctx)); + + /* These values come from acpi-test's acpi-ssdt-test-data property */ + ut_asserteq('a', buf[0]); + ut_asserteq('b', buf[1]); + + /* These values come from acpi-test2's acpi-ssdt-test-data property */ + ut_asserteq('c', buf[2]); + ut_asserteq('d', buf[3]); + + ut_asserteq('z', buf[4]); + + return 0; +} +DM_TEST(dm_test_acpi_fill_ssdt, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From patchwork Mon Mar 9 03:44:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243405 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:58 -0600 Subject: [PATCH v2 34/39] x86: acpi: Move MADT up a bit In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.34.Ib6a3a2f7b429bfd6a43f7bea0543862a1a2eced0@changeid> Put this table before MCFG so that it matches the order that coreboot uses when passing tables to Linux. This is a cosmetic change since the order of the tables does not otherwise matter. Signed-off-by: Simon Glass --- Changes in v2: None arch/x86/lib/acpi_table.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 83b92e2a4c..694e92c158 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -408,18 +408,18 @@ 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: * MCFG\n"); mcfg = ctx->current; acpi_create_mcfg(mcfg); 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); From patchwork Mon Mar 9 03:44:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243411 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:44:59 -0600 Subject: [PATCH v2 35/39] acpi: Record the items added to SSDT In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.35.I1f119e7a847da3e0b2b1791e6f48acaeaf25a223@changeid> It is useful to be able to control the order of data written to the SSDT so that we can compare the output against known-good kernel dumps. Add code to record each item that is added along with the device that added it. That allows us to reorder things later if needed. Signed-off-by: Simon Glass --- Changes in v2: - Generalise the ACPI function recursion with acpi_recurse_method() drivers/core/acpi.c | 83 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index d2f6a9a9d1..7b295bf921 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -9,11 +9,20 @@ #define LOG_CATEOGRY LOGC_ACPI #include +#include #include #include #include #include +#define MAX_ITEMS 100 + +/* Type of table that we collected */ +enum gen_type_t { + TYPE_NONE, + TYPE_SSDT, +}; + /* Type of method to call */ enum method_t { METHOD_WRITE_TABLES, @@ -23,6 +32,25 @@ enum method_t { /* 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_ITEMS]; +static int item_count; + int acpi_return_name(char *out_name, const char *name) { strcpy(out_name, name); @@ -41,6 +69,43 @@ int acpi_get_name(const struct udevice *dev, char *out_name) return -ENOSYS; } +/** + * 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_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; +} + acpi_method acpi_get_method(struct udevice *dev, enum method_t method) { struct acpi_ops *aops; @@ -59,7 +124,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; @@ -67,6 +132,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- %s %p\n", parent->name, func); ret = device_ofdata_to_platdata(parent); if (ret) @@ -74,9 +141,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); } @@ -89,7 +163,7 @@ int acpi_fill_ssdt(struct acpi_ctx *ctx) int ret; log_debug("Writing SSDT tables\n"); - ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SDDT); + ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SDDT, TYPE_SSDT); log_debug("Writing SSDT finished, err=%d\n", ret); return ret; @@ -100,7 +174,8 @@ 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; From patchwork Mon Mar 9 03:45:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243409 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:45:00 -0600 Subject: [PATCH v2 36/39] acpi: Support ordering SSDT data by device In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.36.I91d8e58921d27a54f6ecb58ecd5b72d141238f63@changeid> Add a /chosen property to control the order in which the data appears in the SSDT. This allows matching up U-Boot's output from a dump of the known-good data obtained from within Linux. Signed-off-by: Simon Glass --- Changes in v2: - Generalise the ACPI function recursion with acpi_recurse_method() arch/sandbox/dts/test.dts | 5 ++- doc/device-tree-bindings/chosen.txt | 9 +++++ drivers/core/acpi.c | 62 +++++++++++++++++++++++++++++ test/dm/acpi.c | 17 ++++---- 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index fefa3af999..7d55d360f6 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -206,12 +206,12 @@ compatible = "denx,u-boot-devres-test"; }; - acpi-test { + acpi_test1: acpi-test { compatible = "denx,u-boot-acpi-test"; acpi-ssdt-test-data = "ab"; }; - acpi-test2 { + acpi_test2: acpi-test2 { compatible = "denx,u-boot-acpi-test"; acpi-ssdt-test-data = "cd"; }; @@ -829,6 +829,7 @@ setting = "sunrise ohoka"; other-node = "/some-bus/c-test at 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/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/drivers/core/acpi.c b/drivers/core/acpi.c index 7b295bf921..2f57ff9da2 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -106,6 +106,63 @@ static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev, return 0; } +struct acpi_item *find_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; +} + +static int build_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("u-boot,acpi-ssdt-order", &size); + if (!order) { + log_warning("Failed to find ordering, leaving as is\n"); + return 0; + } + + 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_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 (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) { struct acpi_ops *aops; @@ -160,11 +217,16 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, 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_SDDT, TYPE_SSDT); log_debug("Writing SSDT finished, err=%d\n", ret); + ret = build_type(ctx, start, TYPE_SSDT); + if (ret) + return log_msg_ret("build", ret); return ret; } diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 305d8395ff..b0d8d41e49 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -336,16 +336,19 @@ static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts) ut_assertnonnull(buf); ctx.current = buf; - buf[4] = 'z'; /* sentinal */ + buf[4] = 'z'; /* sentinel */ ut_assertok(acpi_fill_ssdt(&ctx)); - /* These values come from acpi-test's acpi-ssdt-test-data property */ - ut_asserteq('a', buf[0]); - ut_asserteq('b', buf[1]); + /* + * 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-test2's acpi-ssdt-test-data property */ - ut_asserteq('c', buf[2]); - ut_asserteq('d', buf[3]); + /* 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]); From patchwork Mon Mar 9 03:45:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243410 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:45:01 -0600 Subject: [PATCH v2 37/39] x86: Allow devices to write an SSDT In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.37.Id6fc7367886ec2e2d9f2ddf423ad32720e14e5e5@changeid> Call the new core function to write the SSDT. This is made up of fragments generated by devices that have the fill_ssdt() method. Signed-off-by: Simon Glass --- Changes in v2: - Move ACPI_TABLE_CREATOR to here arch/x86/lib/acpi_table.c | 50 +++++++++++++++++++++++++++++++++++++++ include/acpi_table.h | 1 + 2 files changed, 51 insertions(+) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 694e92c158..3b97fe162c 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -344,6 +345,46 @@ static void acpi_create_spcr(struct acpi_spcr *spcr) header->checksum = table_compute_checksum((void *)spcr, header->length); } +static void acpi_ssdt_write_cbtable(struct acpi_ctx *ctx) +{ + uintptr_t base; + u32 size; + + base = 0; + size = 0; + + acpigen_write_device(ctx, "CTBL"); + acpigen_write_coreboot_hid(ctx, COREBOOT_ACPI_ID_CBTABLE); + acpigen_write_name_integer(ctx, "_UID", 0); + acpigen_write_sta(ctx, ACPI_STATUS_DEVICE_HIDDEN_ON); + acpigen_write_name(ctx, "_CRS"); + acpigen_write_resourcetemplate_header(ctx); + acpigen_write_mem32fixed(ctx, 0, base, size); + acpigen_write_resourcetemplate_footer(ctx); + acpigen_pop_len(ctx); +} + +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)); + + /* Write object to declare coreboot tables */ + acpi_ssdt_write_cbtable(ctx); + 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 */ @@ -353,6 +394,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; @@ -408,6 +450,14 @@ ulong write_acpi_tables(ulong start_addr) acpi_create_fadt(fadt, facs, dsdt); acpi_add_table(ctx, fadt); + debug("ACPI: * SSDT\n"); + ssdt = (struct acpi_table_header *)ctx->current; + acpi_create_ssdt(ctx, ssdt, ACPI_TABLE_CREATOR); + 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; acpi_create_mcfg(mcfg); diff --git a/include/acpi_table.h b/include/acpi_table.h index 5fd2cef5d1..2a802758aa 100644 --- a/include/acpi_table.h +++ b/include/acpi_table.h @@ -17,6 +17,7 @@ #define OEM_ID "U-BOOT" /* U-Boot */ #define OEM_TABLE_ID "U-BOOTBL" /* U-Boot Table */ #define ASLC_ID "INTL" /* Intel ASL Compiler */ +#define ACPI_TABLE_CREATOR OEM_TABLE_ID #define ACPI_RSDP_REV_ACPI_1_0 0 #define ACPI_RSDP_REV_ACPI_2_0 2 From patchwork Mon Mar 9 03:45:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243413 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:45:02 -0600 Subject: [PATCH v2 38/39] acpi: Add support for DSDT generation In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.38.I2e68300c24666745bcb7ecb874b13329538265e0@changeid> Some devices need to inject extra code into the Differentiated System Descriptor Table (DSDT). Add a method to handle this. Signed-off-by: Simon Glass --- Changes in v2: - Generalise the ACPI function recursion with acpi_recurse_method() arch/sandbox/dts/test.dts | 2 ++ drivers/core/acpi.c | 25 ++++++++++++++++++++++- include/dm/acpi.h | 23 +++++++++++++++++++++ test/dm/acpi.c | 42 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 7d55d360f6..9748c29a49 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -209,11 +209,13 @@ acpi_test1: acpi-test { compatible = "denx,u-boot-acpi-test"; acpi-ssdt-test-data = "ab"; + acpi-dsdt-test-data = "hi"; }; acpi_test2: acpi-test2 { compatible = "denx,u-boot-acpi-test"; acpi-ssdt-test-data = "cd"; + acpi-dsdt-test-data = "jk"; }; clocks { diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 2f57ff9da2..5613a41eb0 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -21,12 +21,14 @@ enum gen_type_t { TYPE_NONE, TYPE_SSDT, + TYPE_DSDT, }; /* Type of method to call */ enum method_t { METHOD_WRITE_TABLES, METHOD_FILL_SDDT, + METHOD_INJECT_DSDT, }; /* Prototype for all methods */ @@ -129,7 +131,9 @@ static int build_type(struct acpi_ctx *ctx, void *start, enum gen_type_t type) void *end = ctx->current; ptr = start; - order = ofnode_read_chosen_prop("u-boot,acpi-ssdt-order", &size); + 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; @@ -174,6 +178,8 @@ acpi_method acpi_get_method(struct udevice *dev, enum method_t method) return aops->write_tables; case METHOD_FILL_SDDT: return aops->fill_ssdt; + case METHOD_INJECT_DSDT: + return aops->inject_dsdt; } } @@ -231,6 +237,23 @@ int acpi_fill_ssdt(struct acpi_ctx *ctx) 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 = build_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; diff --git a/include/dm/acpi.h b/include/dm/acpi.h index c340c21685..8de9296e71 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -85,6 +85,19 @@ struct acpi_ops { * @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. + * + * @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); }; #define device_get_acpi_ops(dev) ((dev)->driver->acpi_ops) @@ -139,6 +152,16 @@ int acpi_write_dev_tables(struct acpi_ctx *ctx); */ 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); + #endif /* __ACPI__ */ #endif diff --git a/test/dm/acpi.c b/test/dm/acpi.c index b0d8d41e49..16dc0955be 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -54,10 +54,22 @@ static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) 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"); + 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[] = { @@ -355,3 +367,33 @@ static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts) 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); From patchwork Mon Mar 9 03:45:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 243412 List-Id: U-Boot discussion From: sjg at chromium.org (Simon Glass) Date: Sun, 8 Mar 2020 21:45:03 -0600 Subject: [PATCH v2 39/39] x86: Allow devices to write to DSDT In-Reply-To: <20200309034504.149659-1-sjg@chromium.org> References: <20200309034504.149659-1-sjg@chromium.org> Message-ID: <20200308214442.v2.39.I8b14735c2286701cc6be7d36b85bbad8ca58babd@changeid> Call the new core function to inject ASL programmatically into the DSDT. This is made up of fragments generated by devices that have the inject_dsdt() method. The normal, compiled ASL file is added after this. Signed-off-by: Simon Glass --- Changes in v2: None arch/x86/lib/acpi_table.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 3b97fe162c..5e14149923 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -422,7 +422,20 @@ ulong write_acpi_tables(ulong start_addr) 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)); + + if (dsdt->length >= sizeof(struct acpi_table_header)) { + acpi_inject_dsdt(ctx); + memcpy(ctx->current, + (char *)AmlCode + sizeof(struct acpi_table_header), + dsdt->length - sizeof(struct acpi_table_header)); + acpi_inc(ctx, dsdt->length - sizeof(struct acpi_table_header)); + + /* (Re)calculate length and checksum. */ + dsdt->length = ctx->current - (void *)dsdt; + dsdt->checksum = 0; + dsdt->checksum = table_compute_checksum(dsdt, dsdt->length); + } + acpi_align(ctx); /* Pack GNVS into the ACPI table area */ for (i = 0; i < dsdt->length; i++) {