summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/amd/agesa/mtrr_fixme.c5
-rw-r--r--src/drivers/amd/agesa/s3_mtrr.c4
-rw-r--r--src/drivers/aspeed/common/ast_drv.h1
-rw-r--r--src/drivers/aspeed/common/ast_main.c11
-rw-r--r--src/drivers/aspeed/common/ast_mode.c20
-rw-r--r--src/drivers/aspeed/common/ast_post.c4
-rw-r--r--src/drivers/crb/chip.h4
-rw-r--r--src/drivers/efi/Kconfig40
-rw-r--r--src/drivers/efi/Makefile.mk4
-rw-r--r--src/drivers/efi/capsules.c770
-rw-r--r--src/drivers/efi/capsules.h20
-rw-r--r--src/drivers/efi/info.c28
-rw-r--r--src/drivers/emulation/qemu/Kconfig27
-rw-r--r--src/drivers/emulation/qemu/Makefile.mk2
-rw-r--r--src/drivers/emulation/qemu/bochs.c4
-rw-r--r--src/drivers/emulation/qemu/cirrus.c4
-rw-r--r--src/drivers/i2c/at24rf08c/Kconfig1
-rw-r--r--src/drivers/i2c/generic/chip.h25
-rw-r--r--src/drivers/i2c/generic/generic.c9
-rw-r--r--src/drivers/intel/fsp1_1/car.c4
-rw-r--r--src/drivers/intel/fsp1_1/fsp_report.c13
-rw-r--r--src/drivers/intel/fsp1_1/raminit.c2
-rw-r--r--src/drivers/intel/fsp2_0/Kconfig87
-rw-r--r--src/drivers/intel/fsp2_0/fspt_report.c13
-rw-r--r--src/drivers/intel/fsp2_0/hob_verify.c4
-rw-r--r--src/drivers/intel/fsp2_0/temp_ram_exit.c2
-rw-r--r--src/drivers/intel/gma/acpi/configure_brightness_levels.asl7
-rw-r--r--src/drivers/intel/gma/gma-gfx_init.ads6
-rw-r--r--src/drivers/intel/gma/hires_fb/gma-gfx_init.adb35
-rw-r--r--src/drivers/intel/gma/opregion.c2
-rw-r--r--src/drivers/intel/ish/Kconfig12
-rw-r--r--src/drivers/intel/ish/ish.c9
-rw-r--r--src/drivers/mipi/panel-IVO_T109NW41.c12
-rw-r--r--src/drivers/ocp/ewl/ewl.c2
-rw-r--r--src/drivers/pc80/pc/ps2_controller.asl1
-rw-r--r--src/drivers/pc80/pc/ps2_keyboard.asl29
-rw-r--r--src/drivers/pc80/pc/ps2_mouse.asl24
-rw-r--r--src/drivers/pc80/rtc/mc146818rtc.c9
-rw-r--r--src/drivers/pc80/tpm/chip.h4
-rw-r--r--src/drivers/pc80/tpm/tis.c20
-rw-r--r--src/drivers/smbus/i2c_smbus_console.c11
-rw-r--r--src/drivers/smmstore/ramstage.c1
-rw-r--r--src/drivers/soundwire/alc711/Kconfig20
-rw-r--r--src/drivers/soundwire/alc711/Makefile.mk2
-rw-r--r--src/drivers/soundwire/alc711/alc711.c22
-rw-r--r--src/drivers/spi/Kconfig14
-rw-r--r--src/drivers/spi/Makefile.mk2
-rw-r--r--src/drivers/spi/acpi/acpi.c3
-rw-r--r--src/drivers/spi/spi_flash.c27
-rw-r--r--src/drivers/spi/spi_flash_internal.h32
-rw-r--r--src/drivers/spi/spi_flash_rpmc.c32
-rw-r--r--src/drivers/spi/spi_flash_sfdp.c365
-rw-r--r--src/drivers/spi/winbond.c13
-rw-r--r--src/drivers/tpm/ppi.c1
-rw-r--r--src/drivers/usb/acpi/Makefile.mk1
-rw-r--r--src/drivers/usb/acpi/chip.h10
-rw-r--r--src/drivers/usb/acpi/intel_bluetooth.c338
-rw-r--r--src/drivers/usb/acpi/usb_acpi.c12
-rw-r--r--src/drivers/wifi/generic/acpi.c595
-rw-r--r--src/drivers/wifi/generic/chip.h3
60 files changed, 2579 insertions, 205 deletions
diff --git a/src/drivers/amd/agesa/mtrr_fixme.c b/src/drivers/amd/agesa/mtrr_fixme.c
index 39c3d26f1f..9db1fe5a1f 100644
--- a/src/drivers/amd/agesa/mtrr_fixme.c
+++ b/src/drivers/amd/agesa/mtrr_fixme.c
@@ -44,8 +44,7 @@ void fixup_cbmem_to_UC(int s3resume)
* writeback possible.
*/
- uintptr_t top_of_ram = (uintptr_t)cbmem_top();
- top_of_ram = ALIGN_UP(top_of_ram, 4 * MiB);
+ const uintptr_t top_of_ram = ALIGN_UP(cbmem_top(), 4 * MiB);
set_range_uc(top_of_ram - 4 * MiB, 4 * MiB);
set_range_uc(top_of_ram - 8 * MiB, 4 * MiB);
@@ -78,7 +77,7 @@ static void recover_postcar_frame(struct postcar_frame *pcf)
* speed make them WB after CAR teardown.
*/
if (s3resume) {
- uintptr_t top_of_ram = (uintptr_t)cbmem_top();
+ uintptr_t top_of_ram = cbmem_top();
top_of_ram = ALIGN_DOWN(top_of_ram, 4 * MiB);
postcar_frame_add_mtrr(pcf, top_of_ram - 4 * MiB, 4 * MiB,
diff --git a/src/drivers/amd/agesa/s3_mtrr.c b/src/drivers/amd/agesa/s3_mtrr.c
index b085b4d671..64a51ad903 100644
--- a/src/drivers/amd/agesa/s3_mtrr.c
+++ b/src/drivers/amd/agesa/s3_mtrr.c
@@ -38,8 +38,8 @@ static const uint32_t msr_backup[] = {
MTRR_PHYS_BASE(7),
MTRR_PHYS_MASK(7),
SYSCFG_MSR,
- TOP_MEM,
- TOP_MEM2,
+ TOP_MEM_MSR,
+ TOP_MEM2_MSR,
};
void backup_mtrr(void)
diff --git a/src/drivers/aspeed/common/ast_drv.h b/src/drivers/aspeed/common/ast_drv.h
index 423ba37c28..90fd16527f 100644
--- a/src/drivers/aspeed/common/ast_drv.h
+++ b/src/drivers/aspeed/common/ast_drv.h
@@ -20,6 +20,7 @@ enum ast_chip {
AST2300,
AST2400,
AST2500,
+ AST2600,
AST1180,
};
diff --git a/src/drivers/aspeed/common/ast_main.c b/src/drivers/aspeed/common/ast_main.c
index b28f6a675d..cd51198eff 100644
--- a/src/drivers/aspeed/common/ast_main.c
+++ b/src/drivers/aspeed/common/ast_main.c
@@ -111,7 +111,10 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
uint32_t data;
pci_read_config_dword(ast->dev->pdev, 0x08, &data);
uint8_t revision = data & 0xff;
- if (revision >= 0x40) {
+ if (revision >= 0x50) {
+ ast->chip = AST2600;
+ DRM_INFO("AST 2600 detected\n");
+ } else if (revision >= 0x40) {
ast->chip = AST2500;
DRM_INFO("AST 2500 detected\n");
} else if (revision >= 0x30) {
@@ -171,6 +174,8 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
if (ast->chip == AST2500 &&
scu_rev == 0x100) /* ast2510 */
ast->support_wide_screen = true;
+ if (ast->chip == AST2600)
+ ast->support_wide_screen = true;
}
break;
}
@@ -192,9 +197,9 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
ast->tx_chip_type = AST_TX_SIL164;
}
- if ((ast->chip == AST2300) || (ast->chip == AST2400)) {
+ if ((ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST2500)) {
/*
- * On AST2300 and 2400, look the configuration set by the SoC in
+ * On AST2300, 2400 and 2500, look the configuration set by the SoC in
* the SOC scratch register #1 bits 11:8 (interestingly marked
* as "reserved" in the spec)
*/
diff --git a/src/drivers/aspeed/common/ast_mode.c b/src/drivers/aspeed/common/ast_mode.c
index 3ab91c228e..96504579a4 100644
--- a/src/drivers/aspeed/common/ast_mode.c
+++ b/src/drivers/aspeed/common/ast_mode.c
@@ -247,7 +247,7 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod
u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0;
u16 temp, precache = 0;
- if ((ast->chip == AST2500) &&
+ if ((ast->chip == AST2500 || ast->chip == AST2600) &&
(vbios_mode->enh_table->flags & AST2500PreCatchCRT))
precache = 40;
@@ -288,6 +288,12 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAC, 0x00, jregAC);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAD, 0x00, jregAD);
+ // Workaround for HSync Time non octave pixels (1920x1080@60Hz HSync 44 pixels);
+ if ((ast->chip == AST2600) && (mode->crtc_vdisplay == 1080))
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xFC, 0xFD, 0x02);
+ else
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xFC, 0xFD, 0x00);
+
/* vert timings */
temp = (mode->crtc_vtotal) - 2;
if (temp & 0x100)
@@ -367,7 +373,7 @@ static void ast_set_dclk_reg(struct drm_device *dev, struct drm_display_mode *mo
struct ast_private *ast = dev->dev_private;
const struct ast_vbios_dclk_info *clk_info;
- if (ast->chip == AST2500)
+ if ((ast->chip == AST2500) || (ast->chip == AST2600))
clk_info = &dclk_table_ast2500[vbios_mode->enh_table->dclk_index];
else
clk_info = &dclk_table[vbios_mode->enh_table->dclk_index];
@@ -411,8 +417,11 @@ static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa8, 0xfd, jregA8);
/* Set Threshold */
- if (ast->chip == AST2300 || ast->chip == AST2400 ||
- ast->chip == AST2500) {
+ if (ast->chip == AST2600) {
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0xe0);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0xa0);
+ } else if (ast->chip == AST2300 || ast->chip == AST2400 ||
+ ast->chip == AST2500) {
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60);
} else if (ast->chip == AST2100 ||
@@ -529,7 +538,8 @@ enum drm_mode_status ast_mode_valid(struct drm_connector *connector,
if ((ast->chip == AST2100) || (ast->chip == AST2200) ||
(ast->chip == AST2300) || (ast->chip == AST2400) ||
- (ast->chip == AST2500) || (ast->chip == AST1180)) {
+ (ast->chip == AST2500) || (ast->chip == AST1180) ||
+ (ast->chip == AST2600)) {
if ((hdisplay == 1920) && (vdisplay == 1080))
return MODE_OK;
diff --git a/src/drivers/aspeed/common/ast_post.c b/src/drivers/aspeed/common/ast_post.c
index 856e4e5ac6..c558b69239 100644
--- a/src/drivers/aspeed/common/ast_post.c
+++ b/src/drivers/aspeed/common/ast_post.c
@@ -367,7 +367,9 @@ void ast_post_gpu(struct drm_device *dev)
ast_enable_mmio(dev);
ast_set_def_ext_reg(dev);
- if (ast->config_mode == ast_use_p2a) {
+ if (ast->chip == AST2600) {
+ return;
+ } else if (ast->config_mode == ast_use_p2a) {
if (ast->chip == AST2500)
ast_post_chip_2500(dev);
else if (ast->chip == AST2300 || ast->chip == AST2400)
diff --git a/src/drivers/crb/chip.h b/src/drivers/crb/chip.h
index f0e471fd48..ac7ea96982 100644
--- a/src/drivers/crb/chip.h
+++ b/src/drivers/crb/chip.h
@@ -3,7 +3,7 @@
#ifndef DRIVERS_CRB_CHIP_H
#define DRIVERS_CRB_CHIP_H
-typedef struct drivers_crb_config {
-} tpm_config_t;
+struct drivers_crb_config {
+};
#endif /* DRIVERS_CRB_CHIP_H */
diff --git a/src/drivers/efi/Kconfig b/src/drivers/efi/Kconfig
index ad27d68b39..07e5f146a6 100644
--- a/src/drivers/efi/Kconfig
+++ b/src/drivers/efi/Kconfig
@@ -6,3 +6,43 @@ config DRIVERS_EFI_VARIABLE_STORE
help
Adds a driver that is able to read and write an EFI formatted
VariableStore as used by tianocore.
+
+config DRIVERS_EFI_FW_INFO
+ bool "Expose firmware version in a EFI-friendly form"
+ depends on UDK_BASE
+ help
+ Adds firmware version information to coreboot table in a form similar to
+ EFI System Resource Table (ESRT) that can be used for firmware updates.
+
+config DRIVERS_EFI_MAIN_FW_GUID
+ string "GUID of the firmware"
+ default "00112233-4455-6677-8899-aabbccddeeff"
+ depends on DRIVERS_EFI_FW_INFO
+ help
+ GUID used to identify firmware kind for the purposes of updates.
+
+config DRIVERS_EFI_MAIN_FW_VERSION
+ hex "Version of the firmware"
+ range 0x00000000 0xFFFFFFFF
+ default 0x00000000
+ depends on DRIVERS_EFI_FW_INFO
+ help
+ 32-bit unsigned integer representing current firmware's version.
+
+config DRIVERS_EFI_MAIN_FW_LSV
+ hex "Lowest supported firmware version"
+ range 0x00000000 0xFFFFFFFF
+ default 0x00000000
+ depends on DRIVERS_EFI_FW_INFO
+ help
+ 32-bit unsigned integer representing lowest firmware version number
+ that is allowed to replace the current one. Can be used to forbid
+ bugged versions.
+
+config DRIVERS_EFI_UPDATE_CAPSULES
+ bool "Include EFI update capsules driver"
+ depends on DRIVERS_EFI_VARIABLE_STORE && SMMSTORE_V2 && DRIVERS_EFI_FW_INFO
+ help
+ Adds a driver that is able to parse CapsuleUpdateData* EFI variables
+ to discover firmware updates and expose them for tianocore thorough
+ CBMEM for execution. tianocore is responsible for erasing the variables.
diff --git a/src/drivers/efi/Makefile.mk b/src/drivers/efi/Makefile.mk
index 2597c09bff..2c3cf9c3bf 100644
--- a/src/drivers/efi/Makefile.mk
+++ b/src/drivers/efi/Makefile.mk
@@ -3,5 +3,9 @@
all-$(CONFIG_DRIVERS_EFI_VARIABLE_STORE) += efivars.c
smm-$(CONFIG_DRIVERS_EFI_VARIABLE_STORE) += efivars.c
+ramstage-$(CONFIG_DRIVERS_EFI_UPDATE_CAPSULES) += capsules.c
+
all-$(CONFIG_USE_UEFI_VARIABLE_STORE) += option.c
smm-$(CONFIG_USE_UEFI_VARIABLE_STORE) += option.c
+
+ramstage-$(CONFIG_DRIVERS_EFI_FW_INFO) += info.c
diff --git a/src/drivers/efi/capsules.c b/src/drivers/efi/capsules.c
new file mode 100644
index 0000000000..e674e33228
--- /dev/null
+++ b/src/drivers/efi/capsules.c
@@ -0,0 +1,770 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+#include <boot/coreboot_tables.h>
+#include <bootmem.h>
+#include <bootstate.h>
+#include <cbmem.h>
+#include <console/console.h>
+#include <cpu/x86/pae.h>
+#include <drivers/efi/efivars.h>
+#include <drivers/efi/capsules.h>
+#include <memrange.h>
+#include <string.h>
+#include <stdio.h>
+#include <smmstore.h>
+#include <types.h>
+
+#include <Uefi/UefiSpec.h>
+#include <Guid/GlobalVariable.h>
+#include <Guid/FmpCapsule.h>
+#include <IndustryStandard/WindowsUxCapsule.h>
+
+/*
+ * Overview
+ *
+ * SG stands for scatter-gather. SG list consists of SG blocks that describe a
+ * potentially discontinuous sequence of memory blocks while not necessarily
+ * lying in continuous memory themselves.
+ *
+ * SG list is basically a linked list of arrays of block descriptors (SG
+ * blocks). Each of SG blocks can be:
+ * - a data block, which points to capsule's data
+ * - a continuation block, which says where other SG blocks are to be found
+ * - end-of-list block, which indicates there are no more blocks
+ *
+ * Each of the CapsuleUpdateData* EFI variables point to some SG list which
+ * might contain one or more update capsules. SG blocks never contain data of
+ * more than one of the capsules. Boundary between capsules in an SG list is
+ * determined by parsing capsule headers and counting amount of data seen so
+ * far.
+ *
+ * There can be multiple CapsuleUpdateData* variables (CapsuleUpdateData,
+ * CapsuleUpdateData1, etc.) in which case their SG lists are chained together
+ * after sanity checks.
+ */
+
+/* This should be more than enough. */
+#define MAX_CAPSULES 32
+
+/* 4 should be enough, but 8 won't hurt. */
+#define CAPSULE_ALIGNMENT 8
+
+/*
+ * A helper structure which bundles physical block address with its data. It's
+ * necessary because 32-bit code can't easily access anything beyond 4 GiB
+ * boundary and this structure allows reading the data, passing it around and,
+ * if necessary, updating it.
+ *
+ * Usage:
+ * 1) Set .self to physical address
+ * 2) Check block's address with is_good_block()
+ * 3) Use load_block() to fetch or store_block() to update data
+ */
+struct block_descr {
+ /* Where the data comes from. */
+ uint64_t self;
+
+ /*
+ * Data read from the self address above. Three cases:
+ * - len != 0 && addr != 0 => len bytes of capsule data at addr
+ * next block_descr follows this one (self + 16)
+ * - len == 0 && addr != 0 => no data
+ * next block_descr is at addr
+ * - len == 0 && addr == 0 => no data
+ * no next block_descr
+ */
+ uint64_t len;
+ uint64_t addr;
+};
+
+/* For passing data from efi_parse_capsules() to bootmem and CBMEM callbacks. */
+struct memory_range {
+ uint32_t base;
+ uint32_t len;
+};
+
+static const EFI_GUID capsule_vendor_guid = {
+ 0x711C703F, 0xC285, 0x4B10, { 0xA3, 0xB0, 0x36, 0xEC, 0xBD, 0x3C, 0x8B, 0xE2 }
+};
+static const EFI_GUID windows_ux_capsule_guid = WINDOWS_UX_CAPSULE_GUID;
+static const EFI_GUID edk2_capsule_on_disk_name_guid = {
+ 0x98C80A4F, 0xE16B, 0x4D11, { 0x93, 0x9A, 0xAB, 0xE5, 0x61, 0x26, 0x3, 0x30 }
+};
+static const EFI_GUID efi_fmp_capsule_guid = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
+
+/* Memory map to keep track of unused or reserved ranges. */
+struct memranges memory_map;
+
+/* Page tables required for pae_map_2M_page(). */
+static char pae_page_tables[20 * KiB] __aligned(4 * KiB);
+
+/* Where all coalesced capsules are located. */
+struct memory_range coalesce_buffer;
+
+/* Where individual coalesced capsules are located and their count. */
+static struct memory_range uefi_capsules[MAX_CAPSULES];
+static int uefi_capsule_count;
+
+static bool is_data_block(const struct block_descr *block)
+{
+ return (block->len != 0);
+}
+
+static bool is_final_block(const struct block_descr *block)
+{
+ return (block->len == 0 && block->addr == 0);
+}
+
+static void *map_range(uint64_t base, uint32_t len)
+{
+ static uint64_t last_mapping_base = UINT64_MAX;
+
+ /* Using MMCONF should be safe as long as we don't do any device
+ initialization during parsing of capsules and don't forget to call
+ paging_disable_pae() at the end. */
+ _Static_assert(IS_ALIGNED(CONFIG_ECAM_MMCONF_BASE_ADDRESS, 2 * MiB));
+ uintptr_t window_base = CONFIG_ECAM_MMCONF_BASE_ADDRESS;
+ size_t window_size = 2 * MiB;
+
+ printk(BIOS_SPEW, "capsules: mapping %#010x bytes at %#010llx.\n", len, base);
+
+ if (base + len <= 4ULL * GiB &&
+ (base + len <= window_base || base >= window_base + window_size)) {
+ /* Don't bother with the mapping, the whole range must be
+ already accessible without it. */
+ printk(BIOS_SPEW, "capsules: no need to map anything.\n");
+ return (void *)(uintptr_t)base;
+ }
+
+ uint64_t aligned_base = ALIGN_DOWN(base, 2 * MiB);
+ if (base - aligned_base + len > 2 * MiB)
+ die("capsules: memory range map request can't be satisfied.\n");
+
+ /* No need to map the same data. */
+ if (aligned_base != last_mapping_base) {
+ printk(BIOS_SPEW, "capsules: mapping from %#010llx.\n", aligned_base);
+ pae_map_2M_page(&pae_page_tables, aligned_base, (void *)window_base);
+ last_mapping_base = aligned_base;
+ }
+
+ return (uint8_t *)window_base + (base - aligned_base);
+}
+
+/*
+ * Alignment requirement on EFI_CAPSULE_BLOCK_DESCRIPTOR seems to be 8 bytes,
+ * which means that it can be cut in half by a mapping. Could map two 2 MiB
+ * pages instead, but should be easier to simply read those 16 bytes and pass
+ * them around.
+ *
+ * `volatile` is to guard against a hypothetical statement reordering.
+ */
+
+static void load_block(struct block_descr *block)
+{
+ volatile uint64_t *len = map_range(block->self, sizeof(uint64_t));
+ block->len = *len;
+
+ volatile uint64_t *addr = map_range(block->self + sizeof(uint64_t), sizeof(uint64_t));
+ block->addr = *addr;
+}
+
+static void store_block(const struct block_descr *block)
+{
+ volatile uint64_t *len = map_range(block->self, sizeof(uint64_t));
+ *len = block->len;
+
+ volatile uint64_t *addr = map_range(block->self + sizeof(uint64_t), sizeof(uint64_t));
+ *addr = block->addr;
+}
+
+static void advance_block(struct block_descr *block)
+{
+ if (is_final_block(block))
+ die("capsules: attempt to advance beyond final SG block of UEFI capsules.\n");
+
+ if (is_data_block(block)) {
+ /* That was at least part of a capsule. */
+ block->self = block->self + sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR);
+ } else {
+ /* End of continuous sequence of descriptors, but there are more. */
+ block->self = block->addr;
+ }
+}
+
+static bool is_good_capsule(const EFI_CAPSULE_HEADER *capsule)
+{
+ if (capsule->HeaderSize < sizeof(*capsule)) {
+ printk(BIOS_ERR, "capsules: capsule header size is too small: %#010x.\n",
+ capsule->HeaderSize);
+ return false;
+ }
+ if (capsule->CapsuleImageSize <= capsule->HeaderSize) {
+ printk(BIOS_ERR, "capsules: capsule image size is too small: %#010x.\n",
+ capsule->CapsuleImageSize);
+ return false;
+ }
+ if (!(capsule->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET)) {
+ printk(BIOS_ERR,
+ "capsules: this capsule should not have persisted, flags: %#010x.\n",
+ capsule->Flags);
+ return false;
+ }
+
+ const EFI_GUID *guid = &capsule->CapsuleGuid;
+ if (memcmp(guid, &windows_ux_capsule_guid, sizeof(*guid)) == 0)
+ return true;
+ if (memcmp(guid, &edk2_capsule_on_disk_name_guid, sizeof(*guid)) == 0)
+ return true;
+ if (memcmp(guid, &efi_fmp_capsule_guid, sizeof(*guid)) == 0)
+ return true;
+
+ printk(BIOS_ERR, "capsules: unrecognized capsule GUID.\n");
+ return false;
+}
+
+static bool is_in_unused_ram(uint64_t base, uint64_t len)
+{
+ if (len == 0) {
+ die("capsules: %s() was passed an empty range: %#010llx:%#010llx.\n",
+ __func__, base, len);
+ }
+ if (base + len < base) {
+ die("capsules: %s() was passed an invalid range: %#010llx:%#010llx.\n",
+ __func__, base, len);
+ }
+
+ const struct range_entry *r;
+ memranges_each_entry(r, &memory_map) {
+ if (range_entry_tag(r) != BM_MEM_RAM)
+ continue;
+
+ if (base >= range_entry_base(r) && base + len <= range_entry_end(r))
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_good_block(struct block_descr *block)
+{
+ if (!IS_ALIGNED(block->self, sizeof(uint64_t))) {
+ printk(BIOS_ERR, "capsules: misaligned SG block at %#010llx.\n", block->self);
+ return false;
+ }
+
+ if (!is_in_unused_ram(block->self, sizeof(*block))) {
+ printk(BIOS_ERR, "capsules: SG block is not in unused memory.\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_good_capsule_head(struct block_descr *block)
+{
+ if (!is_data_block(block)) {
+ printk(BIOS_ERR, "capsules: first capsule SG block is not a data block.\n");
+ return false;
+ }
+
+ if (block->len < sizeof(EFI_CAPSULE_HEADER)) {
+ printk(BIOS_ERR, "capsules: first SG block of a capsule is too small.\n");
+ return false;
+ }
+
+ if (!is_in_unused_ram(block->addr, block->len)) {
+ printk(BIOS_ERR, "capsules: capsule header is not in unused memory.\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool is_good_capsule_block(struct block_descr *block, uint32_t size_left)
+{
+ if (is_final_block(block)) {
+ printk(BIOS_ERR, "capsules: not enough SG blocks to cover a capsule.\n");
+ return false;
+ }
+
+ if (!is_data_block(block)) {
+ printk(BIOS_ERR, "capsules: capsule SG block is not a data block.\n");
+ return false;
+ }
+
+ if (block->len > size_left) {
+ printk(BIOS_ERR, "capsules: SG blocks reach beyond a capsule.\n");
+ return false;
+ }
+
+ if (!is_in_unused_ram(block->addr, block->len)) {
+ printk(BIOS_ERR, "capsules: capsule data is not in unused memory.\n");
+ return false;
+ }
+
+ return true;
+}
+
+/* Checks a single SG list for sanity. Returns its end-of-list descriptor or
+ an empty one on error. */
+static struct block_descr check_capsule_block(struct block_descr first_block,
+ uint64_t *total_data_size)
+{
+ struct block_descr block = first_block;
+ if (!is_good_block(&block)) {
+ printk(BIOS_ERR, "capsules: bad capsule block start.\n");
+ goto error;
+ }
+
+ load_block(&block);
+
+ uint64_t data_size = 0;
+ while (!is_final_block(&block)) {
+ /*
+ * This results in dropping of this capsule block if any of
+ * contained capsule headers looks weird. An alternative is to
+ * cut the capsule block upon finding a bad header. Maybe
+ * could even jump over a broken capsule, temporarily trusting
+ * size field in its header because invalid value should not
+ * break parsing anyway, and then cut it out of the sequence of
+ * blocks. EDK doesn't bother, so only noting the possibility.
+ */
+ if (!is_good_capsule_head(&block)) {
+ printk(BIOS_ERR, "capsules: bad capsule header @ %#010llx.\n",
+ block.addr);
+ goto error;
+ }
+
+ const EFI_CAPSULE_HEADER *capsule_hdr =
+ map_range(block.addr, sizeof(*capsule_hdr));
+ if (!is_good_capsule(capsule_hdr)) {
+ printk(BIOS_ERR, "capsules: bad capsule header @ %#010llx.\n",
+ block.addr);
+ goto error;
+ }
+
+ data_size += ALIGN_UP(capsule_hdr->CapsuleImageSize, CAPSULE_ALIGNMENT);
+
+ uint32_t size_left = capsule_hdr->CapsuleImageSize;
+ while (size_left != 0) {
+ /* is_good_block() holds here whether it's the first iteration or
+ not. */
+
+ if (!is_good_capsule_block(&block, size_left))
+ goto error;
+
+ size_left -= block.len;
+
+ advance_block(&block);
+ if (!is_good_block(&block)) {
+ printk(BIOS_ERR, "capsules: capsule body has a bad block.\n");
+ goto error;
+ }
+
+ load_block(&block);
+ if (!is_final_block(&block) && !is_data_block(&block)) {
+ /* Advance to the next page of block descriptors. */
+ advance_block(&block);
+ if (!is_good_block(&block)) {
+ printk(BIOS_ERR, "capsules: bad SG continuation.\n");
+ goto error;
+ }
+
+ load_block(&block);
+ /* Not expecting a continuation to be followed by another
+ continuation or an end-of-list. */
+ if (!is_data_block(&block)) {
+ printk(BIOS_ERR,
+ "capsules: chained SG continuations.\n");
+ goto error;
+ }
+ }
+ }
+ }
+
+ /* Increase the size only on successful parsing of the capsule block. */
+ *total_data_size += data_size;
+
+ return block;
+
+error:
+ return (struct block_descr){ .self = 0 };
+}
+
+/* Fills an array with pointers to capsule blocks. Returns number of
+ discovered capsule blocks or -1 on error. */
+static int discover_capsule_blocks(struct region_device *rdev,
+ struct block_descr *blocks,
+ int max_blocks)
+{
+ int block_count = 0;
+ for (int i = 0; block_count < max_blocks; ++i) {
+ char var_name[32];
+ if (i == 0)
+ strcpy(var_name, "CapsuleUpdateData");
+ else
+ snprintf(var_name, sizeof(var_name), "CapsuleUpdateData%d", i);
+
+ struct block_descr block;
+ uint32_t size = sizeof(block.self);
+ enum cb_err ret = efi_fv_get_option(rdev, &capsule_vendor_guid, var_name,
+ &block.self, &size);
+ if (ret != CB_SUCCESS) {
+ /* No more variables. */
+ break;
+ }
+ if (size != sizeof(block.self)) {
+ printk(BIOS_ERR, "capsules: unexpected capsule data size (%d).\n",
+ size);
+ return -1;
+ }
+
+ /*
+ * EDK2 checks for duplicates probably because we'll get into
+ * trouble with chaining if there are any, so do the check.
+ *
+ * This, however, won't handle all possible situations which
+ * lead to loops or processing the same capsule more than once.
+ */
+ int j;
+ for (j = 0; j < block_count; ++j) {
+ if (blocks[j].self == block.self)
+ break;
+ }
+ if (j < block_count) {
+ printk(BIOS_INFO, "capsules: skipping duplicated %s.\n", var_name);
+ continue;
+ }
+
+ printk(BIOS_INFO, "capsules: capsule block #%d at %#010llx.\n",
+ block_count, block.self);
+ blocks[block_count++] = block;
+ }
+
+ return block_count;
+}
+
+/*
+ * This function connects tail of one block of descriptors with the head of the
+ * next one and returns pointer to the head of the whole chain. While at it:
+ * - validate structures and pointers for sanity
+ * - compute total amount of memory needed for coalesced capsules
+ *
+ * Returns block that starts at 0 on error.
+ */
+static struct block_descr verify_and_chain_blocks(struct block_descr *blocks,
+ int block_count,
+ uint64_t *total_data_size)
+{
+ /* This won't be blocks[0] if there is something wrong with the first capsule block. */
+ struct block_descr head = {0};
+
+ /* End-of-list descriptor of the last chained block. */
+ struct block_descr tail = {0};
+
+ *total_data_size = 0;
+
+ for (int i = 0; i < block_count; ++i) {
+ struct block_descr last_block = check_capsule_block(blocks[i], total_data_size);
+ if (last_block.self == 0) {
+ /* Fail hard instead? EDK just keeps going, as if capsule
+ blocks are always independent. */
+ printk(BIOS_WARNING,
+ "capsules: skipping damaged capsule block #%d @ %#010llx.\n",
+ i, blocks[i].self);
+ continue;
+ }
+
+ if (head.self == 0) {
+ head = blocks[i];
+ } else {
+ tail.addr = blocks[i].self;
+ store_block(&tail);
+ }
+
+ tail = last_block;
+ }
+
+ return head;
+}
+
+/* Marks structures and data of SG lists as BM_MEM_RESERVED so we don't step on
+ them when looking for usable memory. */
+static void reserve_capsules(struct block_descr block_chain)
+{
+ struct block_descr block = block_chain;
+
+ /* This is the first block of a continuous sequence of blocks. */
+ struct block_descr seq_start = {0};
+
+ /* The code reserves sequences of blocks to avoid invoking
+ memranges_insert() on each of a bunch of adjacent 16-byte blocks. */
+
+ load_block(&block);
+ for (; !is_final_block(&block); advance_block(&block), load_block(&block)) {
+ if (seq_start.self == 0)
+ seq_start = block;
+
+ if (is_data_block(&block)) {
+ /* Reserve capsule data. */
+ memranges_insert(&memory_map, block.addr, block.len, BM_MEM_RESERVED);
+ } else {
+ /* This isn't the final or a data block, so it must be the
+ last block of a continuous sequence. Reserve the whole
+ sequence. */
+ memranges_insert(&memory_map,
+ seq_start.self,
+ block.self - seq_start.self +
+ sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR),
+ BM_MEM_RESERVED);
+
+ /* Will be set on the next iteration if there is one. */
+ seq_start.self = 0;
+ }
+ }
+
+ /* If continuations never show up in a row as checked by
+ check_capsule_block(), seq_start must be non-NULL here. */
+ memranges_insert(&memory_map,
+ seq_start.self,
+ block.self - seq_start.self + sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR),
+ BM_MEM_RESERVED);
+}
+
+/*
+ * Find a buffer below 4 GiB for coalesced capsules.
+ *
+ * Keeping it simple and allocating a single buffer. However, there is
+ * no requirement to put all the capsules together, only that each of
+ * them is continuous in memory. So if this is bad for some reason,
+ * can allocate a separate block for each.
+ *
+ * Returns buffer that starts at 0 on error.
+ */
+static struct memory_range pick_buffer(uint64_t total_data_size)
+{
+ struct memory_range buffer = {0};
+
+ /* 4 * KiB is the alignment set by memranges_init(). */
+ total_data_size = ALIGN_UP(total_data_size, 4 * KiB);
+
+ const struct range_entry *r;
+ memranges_each_entry(r, &memory_map) {
+ if (range_entry_tag(r) != BM_MEM_RAM)
+ continue;
+
+ resource_t base = range_entry_base(r);
+ if (base >= 4ULL * GiB)
+ break;
+
+ /* Possibly reduce size to not deal with ranges that cross 4 GiB boundary. */
+ resource_t size = range_entry_size(r);
+ if (base + size > 4ULL * GiB)
+ size -= base + size - 4ULL * GiB;
+
+ if (size >= total_data_size) {
+ /*
+ * To not create troubles for payloads prefer higher addresses:
+ * - use the top part of a suitable range
+ * - exit the loop only after hitting 4 GiB boundary or end of the list
+ */
+ buffer.base = base + size - total_data_size;
+ buffer.len = total_data_size;
+ }
+ }
+
+ return buffer;
+}
+
+/* Puts capsules into continuous physical memory. */
+static void coalesce_capsules(struct block_descr block_chain, uint8_t *target)
+{
+ struct block_descr block = block_chain;
+ uint8_t *capsule_start = NULL;
+ uint32_t size_left = 0;
+
+ /* No safety checks in this function, as all of them were done earlier. */
+
+ load_block(&block);
+ for (; !is_final_block(&block); advance_block(&block), load_block(&block)) {
+ /* Advance over a continuation. */
+ if (!is_data_block(&block))
+ continue;
+
+ /* This must be the first block of a capsule. */
+ if (size_left == 0) {
+ const EFI_CAPSULE_HEADER *capsule_hdr =
+ map_range(block.addr, sizeof(*capsule_hdr));
+ size_left = capsule_hdr->CapsuleImageSize;
+ capsule_start = target;
+ }
+
+ uint64_t addr = block.addr;
+ uint64_t data_left = block.len;
+ while (data_left != 0) {
+ uint64_t piece_len = MIN(data_left, 2 * MiB - (addr % 2 * MiB));
+ void *data = map_range(addr, piece_len);
+
+ memcpy(target, data, piece_len);
+
+ target += piece_len;
+ addr += piece_len;
+ data_left -= piece_len;
+ }
+
+ size_left -= block.len;
+
+ /* This must be the last block of a capsule, record it. */
+ if (size_left == 0) {
+ /* If we can just ignore corrupted capsules, then we can simply
+ drop those which don't fit. */
+ if (uefi_capsule_count == MAX_CAPSULES) {
+ printk(BIOS_WARNING,
+ "capsules: ignoring all capsules after #%d.\n",
+ MAX_CAPSULES);
+ break;
+ }
+
+ uefi_capsules[uefi_capsule_count].base = (uintptr_t)capsule_start;
+ uefi_capsules[uefi_capsule_count].len = block.len;
+ uefi_capsule_count++;
+
+ /* This is to align start of the next capsule (assumes that
+ initial value of target was suitably aligned). */
+ if (!IS_ALIGNED(block.len, CAPSULE_ALIGNMENT))
+ target += ALIGN_UP(block.len, CAPSULE_ALIGNMENT) - block.len;
+ }
+ }
+
+ printk(BIOS_INFO, "capsules: found %d capsule(s).\n", uefi_capsule_count);
+}
+
+void efi_parse_capsules(void)
+{
+ /* EDK2 starts with 20 items and then grows the list, but it's unlikely
+ to be necessary in practice. */
+ enum { MAX_CAPSULE_BLOCKS = MAX_CAPSULES };
+
+ struct region_device rdev;
+ if (smmstore_lookup_region(&rdev)) {
+ printk(BIOS_INFO, "capsules: no SMMSTORE region, no update capsules.\n");
+ return;
+ }
+
+ memranges_init(&memory_map, IORESOURCE_MEM | IORESOURCE_FIXED | IORESOURCE_STORED |
+ IORESOURCE_ASSIGNED | IORESOURCE_CACHEABLE, IORESOURCE_MEM |
+ IORESOURCE_FIXED | IORESOURCE_STORED | IORESOURCE_ASSIGNED |
+ IORESOURCE_CACHEABLE, BM_MEM_RAM);
+
+ init_pae_pagetables(&pae_page_tables);
+
+ /* Blocks are collected here when traversing CapsuleUpdateData*
+ variables, duplicates are skipped. */
+ struct block_descr blocks[MAX_CAPSULE_BLOCKS];
+ int block_count = discover_capsule_blocks(&rdev, blocks, ARRAY_SIZE(blocks));
+ if (block_count <= 0) {
+ if (block_count == 0)
+ printk(BIOS_INFO, "capsules: no UEFI capsules were discovered.\n");
+ goto exit;
+ }
+
+ printk(BIOS_INFO, "capsules: processing %d capsule block(s).\n", block_count);
+
+ /* Broken capsules are ignored, ignore those which didn't fit as well. */
+ if (block_count == ARRAY_SIZE(blocks)) {
+ printk(BIOS_WARNING,
+ "capsules: hit limit on capsule blocks, some might be ignored.\n");
+ }
+
+ /* Chaining is done to not pass around and update an array of pointers. */
+ uint64_t total_data_size;
+ struct block_descr block_chain =
+ verify_and_chain_blocks(blocks, block_count, &total_data_size);
+ if (block_chain.self == 0) {
+ printk(BIOS_ERR, "capsules: no valid capsules to process.\n");
+ goto exit;
+ }
+
+ printk(BIOS_DEBUG, "capsules: chained capsule blocks.\n");
+
+ /* Reserve all blocks and the data they point to to avoid checking for
+ overlaps when looking for a buffer. */
+ reserve_capsules(block_chain);
+
+ printk(BIOS_DEBUG, "capsules: reserved capsule blocks.\n");
+
+ /* Also reserve memory range for cbmem. Since it will still grow in
+ size by an unknown amount, try to account for that by reserving at
+ least 4 MiB more. */
+ void *cbmem_current;
+ size_t cbmem_size;
+ cbmem_get_region(&cbmem_current, &cbmem_size);
+ uintptr_t cbmem_future_base = ALIGN_DOWN((uintptr_t)cbmem_current - 4 * MiB, MiB);
+ memranges_insert(&memory_map,
+ cbmem_future_base,
+ (uintptr_t)cbmem_current + cbmem_size - cbmem_future_base,
+ BM_MEM_RESERVED);
+
+ coalesce_buffer = pick_buffer(total_data_size);
+ if (coalesce_buffer.base == 0) {
+ printk(BIOS_ERR,
+ "capsules: failed to find a buffer (%#llx bytes) for coalesced UEFI capsules.\n",
+ total_data_size);
+ } else {
+ printk(BIOS_DEBUG, "capsules: coalescing capsules data @ %#010x.\n",
+ coalesce_buffer.base);
+ coalesce_capsules(block_chain, (void *)(uintptr_t)coalesce_buffer.base);
+ }
+
+exit:
+ paging_disable_pae();
+ memranges_teardown(&memory_map);
+}
+
+void lb_efi_capsules(struct lb_header *header)
+{
+ int i;
+ for (i = 0; i < uefi_capsule_count; ++i) {
+ struct lb_range *capsule = (void *)lb_new_record(header);
+
+ printk(BIOS_INFO, "capsules: publishing a capsule @ %#010x.\n",
+ uefi_capsules[i].base);
+
+ capsule->tag = LB_TAG_CAPSULE;
+ capsule->size = sizeof(*capsule);
+ capsule->range_start = uefi_capsules[i].base;
+ capsule->range_size = uefi_capsules[i].len;
+ }
+}
+
+void efi_add_capsules_to_bootmem(void)
+{
+ if (coalesce_buffer.len != 0) {
+ printk(BIOS_INFO, "capsules: reserving capsules data @ %#010x.\n",
+ coalesce_buffer.base);
+ bootmem_add_range(coalesce_buffer.base, coalesce_buffer.len, BM_MEM_RESERVED);
+ }
+}
+
+/*
+ * The code from this unit is typically executed by clear_memory() which is run
+ * after DEV_INIT. However, clear_memory() might not be compiled in in which
+ * case we still want to process capsules.
+ *
+ * State machine doesn't enforce any particular ordering for callbacks and
+ * running before DEV_INIT is too early due to MTTRs not being initialized.
+ * Hence invoking code is in two different places that should be mutually
+ * exclusive (can't set a "done" flag due to unknown ordering).
+ */
+#if !CONFIG(PLATFORM_HAS_DRAM_CLEAR)
+
+static void parse_capsules(void *unused)
+{
+ if (!acpi_is_wakeup_s3())
+ efi_parse_capsules();
+}
+
+BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, parse_capsules, NULL);
+
+#endif
diff --git a/src/drivers/efi/capsules.h b/src/drivers/efi/capsules.h
new file mode 100644
index 0000000000..608ce3400f
--- /dev/null
+++ b/src/drivers/efi/capsules.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _EDK2_CAPSULES_H_
+#define _EDK2_CAPSULES_H_
+
+#if CONFIG(DRIVERS_EFI_UPDATE_CAPSULES)
+
+void efi_parse_capsules(void);
+
+void efi_add_capsules_to_bootmem(void);
+
+#else
+
+static inline void efi_parse_capsules(void) { }
+
+static inline void efi_add_capsules_to_bootmem(void) { }
+
+#endif
+
+#endif /* _EDK2_CAPSULES_H_ */
diff --git a/src/drivers/efi/info.c b/src/drivers/efi/info.c
new file mode 100644
index 0000000000..fc1cfea573
--- /dev/null
+++ b/src/drivers/efi/info.c
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <boot/coreboot_tables.h>
+#include <console/console.h>
+#include <stdint.h>
+#include <string.h>
+#include <uuid.h>
+
+void lb_efi_fw_info(struct lb_header *header)
+{
+ uint8_t guid[16];
+ struct lb_efi_fw_info *fw_info;
+
+ if (parse_uuid(guid, CONFIG_DRIVERS_EFI_MAIN_FW_GUID)) {
+ printk(BIOS_ERR, "%s(): failed to parse firmware's GUID: '%s'\n", __func__,
+ CONFIG_DRIVERS_EFI_MAIN_FW_GUID);
+ return;
+ }
+
+ fw_info = (struct lb_efi_fw_info *)lb_new_record(header);
+ fw_info->tag = LB_TAG_EFI_FW_INFO;
+ fw_info->size = sizeof(*fw_info);
+
+ memcpy(fw_info->guid, guid, sizeof(guid));
+ fw_info->version = CONFIG_DRIVERS_EFI_MAIN_FW_VERSION;
+ fw_info->lowest_supported_version = CONFIG_DRIVERS_EFI_MAIN_FW_LSV;
+ fw_info->fw_size = CONFIG_ROM_SIZE;
+}
diff --git a/src/drivers/emulation/qemu/Kconfig b/src/drivers/emulation/qemu/Kconfig
index 11231ae52e..5b01ae785b 100644
--- a/src/drivers/emulation/qemu/Kconfig
+++ b/src/drivers/emulation/qemu/Kconfig
@@ -11,18 +11,27 @@ config DRIVERS_EMULATION_QEMU_BOCHS
help
VGA driver for qemu emulated vga cards supporting
the bochs dispi interface. This includes
- standard vga, vmware svga and qxl. The default
- vga (cirrus) is *not* supported, so you have to
- pick another one explicitly via 'qemu -vga $card'.
+ standard vga, vmware svga, and qxl.
-config DRIVERS_EMULATION_QEMU_BOCHS_XRES
- int "bochs vga xres"
+config DRIVERS_EMULATION_QEMU_CIRRUS
+ bool "cirrus svga driver"
+ default y
+ depends on CPU_QEMU_X86
+ depends on MAINBOARD_DO_NATIVE_VGA_INIT
+ select HAVE_VGA_TEXT_FRAMEBUFFER
+ select HAVE_LINEAR_FRAMEBUFFER
+ select VGA
+ help
+ VGA driver for qemu emulated cirrus svga card.
+
+config DRIVERS_EMULATION_QEMU_XRES
+ int "qemu vga xres"
default 800
depends on LINEAR_FRAMEBUFFER
- depends on DRIVERS_EMULATION_QEMU_BOCHS
+ depends on DRIVERS_EMULATION_QEMU_BOCHS || DRIVERS_EMULATION_QEMU_CIRRUS
-config DRIVERS_EMULATION_QEMU_BOCHS_YRES
- int "bochs vga yres"
+config DRIVERS_EMULATION_QEMU_YRES
+ int "qemu vga yres"
default 600
depends on LINEAR_FRAMEBUFFER
- depends on DRIVERS_EMULATION_QEMU_BOCHS
+ depends on DRIVERS_EMULATION_QEMU_BOCHS || DRIVERS_EMULATION_QEMU_CIRRUS
diff --git a/src/drivers/emulation/qemu/Makefile.mk b/src/drivers/emulation/qemu/Makefile.mk
index c9d94bdca0..619782fb1e 100644
--- a/src/drivers/emulation/qemu/Makefile.mk
+++ b/src/drivers/emulation/qemu/Makefile.mk
@@ -6,4 +6,4 @@ postcar-$(CONFIG_CONSOLE_QEMU_DEBUGCON) += qemu_debugcon.c
ramstage-$(CONFIG_CONSOLE_QEMU_DEBUGCON) += qemu_debugcon.c
ramstage-$(CONFIG_DRIVERS_EMULATION_QEMU_BOCHS) += bochs.c
-ramstage-$(CONFIG_DRIVERS_EMULATION_QEMU_BOCHS) += cirrus.c
+ramstage-$(CONFIG_DRIVERS_EMULATION_QEMU_CIRRUS) += cirrus.c
diff --git a/src/drivers/emulation/qemu/bochs.c b/src/drivers/emulation/qemu/bochs.c
index 06504309e0..2e0526fa8f 100644
--- a/src/drivers/emulation/qemu/bochs.c
+++ b/src/drivers/emulation/qemu/bochs.c
@@ -39,8 +39,8 @@
#define VBE_DISPI_LFB_ENABLED 0x40
#define VBE_DISPI_NOCLEARMEM 0x80
-static int width = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_XRES;
-static int height = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_YRES;
+static int width = CONFIG_DRIVERS_EMULATION_QEMU_XRES;
+static int height = CONFIG_DRIVERS_EMULATION_QEMU_YRES;
static void bochs_write(struct resource *res, int index, int val)
{
diff --git a/src/drivers/emulation/qemu/cirrus.c b/src/drivers/emulation/qemu/cirrus.c
index 1dc8ac9e3e..6fa9ac2b4a 100644
--- a/src/drivers/emulation/qemu/cirrus.c
+++ b/src/drivers/emulation/qemu/cirrus.c
@@ -9,8 +9,8 @@
#include <pc80/vga_io.h>
#include <framebuffer_info.h>
-static int width = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_XRES;
-static int height = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_YRES;
+static int width = CONFIG_DRIVERS_EMULATION_QEMU_XRES;
+static int height = CONFIG_DRIVERS_EMULATION_QEMU_YRES;
static u32 addr = 0;
enum
diff --git a/src/drivers/i2c/at24rf08c/Kconfig b/src/drivers/i2c/at24rf08c/Kconfig
index 5834cff2f6..a930a46123 100644
--- a/src/drivers/i2c/at24rf08c/Kconfig
+++ b/src/drivers/i2c/at24rf08c/Kconfig
@@ -2,5 +2,4 @@
config DRIVER_LENOVO_SERIALS
bool
- default y if VENDOR_LENOVO
select SMBIOS_PROVIDED_BY_MOBO
diff --git a/src/drivers/i2c/generic/chip.h b/src/drivers/i2c/generic/chip.h
index 227cb61e18..69ce29dae4 100644
--- a/src/drivers/i2c/generic/chip.h
+++ b/src/drivers/i2c/generic/chip.h
@@ -81,6 +81,31 @@ struct drivers_i2c_generic_config {
bool has_rotation_matrix;
int rotation_matrix[9];
+ /*
+ * Chip Direct Mapping is exclusive to Windows, a allows specifying the
+ * position where a chip is mounted. There are 8 positions:
+ * 1: 90 Degrees
+ * 2: 270 Degrees
+ * 3: 180 Degrees
+ * 4: 0 Degrees
+ * 5: 90 Degrees (Inverted)
+ * 6: 270 Degrees (Inverted)
+ * 7: 180 Degrees (Inverted)
+ * 8: 0 Degrees (Inverted)
+ *
+ * The _CDM method should return 0xabcd0X, where X is the position.
+ */
+ enum {
+ CDM_NOT_PRESENT = 0,
+ CDM_ROT_90,
+ CDM_ROT_180,
+ CDM_ROT_270,
+ CDM_ROT_0,
+ CDM_ROT_90_INVERT,
+ CDM_ROT_180_INVERT,
+ CDM_ROT_270_INVERT,
+ CDM_ROT_0_INVERT,
+ } cdm_index;
/* Generic properties for exporting device-specific data to the OS */
struct acpi_dp property_list[MAX_GENERIC_PROPERTY_LIST];
diff --git a/src/drivers/i2c/generic/generic.c b/src/drivers/i2c/generic/generic.c
index 98c59e6a05..260ea83b10 100644
--- a/src/drivers/i2c/generic/generic.c
+++ b/src/drivers/i2c/generic/generic.c
@@ -149,6 +149,7 @@ void i2c_generic_fill_ssdt(const struct device *dev,
/* Rotation Matrix */
if (config->has_rotation_matrix) {
acpigen_write_method("ROTM", 0);
+ acpigen_write_name("RBUF");
acpigen_write_package(3);
for (int i = 0; i < 3; i++) {
@@ -160,8 +161,16 @@ void i2c_generic_fill_ssdt(const struct device *dev,
acpigen_write_string(matrix_row);
}
+ acpigen_pop_len();
+ acpigen_write_return_namestr("RBUF");
acpigen_pop_len();
+ }
+
+ /* Chip Direct Mapping */
+ if (config->cdm_index != CDM_NOT_PRESENT) {
+ acpigen_write_method("_CDM", 1);
+ acpigen_write_return_integer(0xabcd00 | config->cdm_index);
acpigen_pop_len();
}
diff --git a/src/drivers/intel/fsp1_1/car.c b/src/drivers/intel/fsp1_1/car.c
index 7455d30047..8bb9f33d46 100644
--- a/src/drivers/intel/fsp1_1/car.c
+++ b/src/drivers/intel/fsp1_1/car.c
@@ -12,12 +12,10 @@
void fill_postcar_frame(struct postcar_frame *pcf)
{
- uintptr_t top_of_ram;
-
/* Cache at least 8 MiB below the top of ram, and at most 8 MiB
* above top of the ram. This satisfies MTRR alignment requirement
* with different TSEG size configurations. */
- top_of_ram = ALIGN_DOWN((uintptr_t)cbmem_top(), 8*MiB);
+ const uintptr_t top_of_ram = ALIGN_DOWN(cbmem_top(), 8 * MiB);
postcar_frame_add_mtrr(pcf, top_of_ram - 8*MiB, 16*MiB, MTRR_TYPE_WRBACK);
}
diff --git a/src/drivers/intel/fsp1_1/fsp_report.c b/src/drivers/intel/fsp1_1/fsp_report.c
index 884218d7f7..f5c7b2f05b 100644
--- a/src/drivers/intel/fsp1_1/fsp_report.c
+++ b/src/drivers/intel/fsp1_1/fsp_report.c
@@ -10,14 +10,11 @@ uintptr_t temp_memory_end;
void report_fsp_output(void)
{
- const struct region fsp_car_region = {
- .offset = temp_memory_start,
- .size = temp_memory_end - temp_memory_start,
- };
- const struct region coreboot_car_region = {
- .offset = (uintptr_t)_car_region_start,
- .size = (uintptr_t)_car_region_size,
- };
+ const struct region fsp_car_region = region_create(
+ temp_memory_start, temp_memory_end - temp_memory_start);
+ const struct region coreboot_car_region = region_create(
+ (uintptr_t)_car_region_start, (uintptr_t)_car_region_size);
+
printk(BIOS_DEBUG, "FSP: reported temp_mem region: [0x%08lx,0x%08lx)\n",
temp_memory_start, temp_memory_end);
if (!region_is_subregion(&fsp_car_region, &coreboot_car_region)) {
diff --git a/src/drivers/intel/fsp1_1/raminit.c b/src/drivers/intel/fsp1_1/raminit.c
index 2aec5db7cd..247df04fdd 100644
--- a/src/drivers/intel/fsp1_1/raminit.c
+++ b/src/drivers/intel/fsp1_1/raminit.c
@@ -138,7 +138,7 @@ void raminit(struct romstage_params *params)
}
/* Migrate CAR data */
- printk(BIOS_DEBUG, "%p: cbmem_top\n", cbmem_top());
+ printk(BIOS_DEBUG, "%lx: cbmem_top\n", cbmem_top());
if (!s3wake) {
cbmem_initialize_empty_id_size(CBMEM_ID_FSP_RESERVED_MEMORY,
fsp_reserved_bytes);
diff --git a/src/drivers/intel/fsp2_0/Kconfig b/src/drivers/intel/fsp2_0/Kconfig
index 9ea1526e84..c5c485bad2 100644
--- a/src/drivers/intel/fsp2_0/Kconfig
+++ b/src/drivers/intel/fsp2_0/Kconfig
@@ -341,50 +341,32 @@ config FSP_M_ADDR
help
The address FSP-M will be relocated to during build time
-config FSP_STATUS_GLOBAL_RESET_REQUIRED_3
- bool
- help
- FSP Reset Status code used for global reset as per FSP EAS v2.0 section 11.2.2
-
-config FSP_STATUS_GLOBAL_RESET_REQUIRED_4
- bool
- help
- FSP Reset Status code used for global reset as per FSP EAS v2.0 section 11.2.2
-
-config FSP_STATUS_GLOBAL_RESET_REQUIRED_5
- bool
- help
- FSP Reset Status code used for global reset as per FSP EAS v2.0 section 11.2.2
-
-config FSP_STATUS_GLOBAL_RESET_REQUIRED_6
- bool
- help
- FSP Reset Status code used for global reset as per FSP EAS v2.0 section 11.2.2
-
-config FSP_STATUS_GLOBAL_RESET_REQUIRED_7
- bool
- help
- FSP Reset Status code used for global reset as per FSP EAS v2.0 section 11.2.2
-
-config FSP_STATUS_GLOBAL_RESET_REQUIRED_8
- bool
- help
- FSP Reset Status code used for global reset as per FSP EAS v2.0 section 11.2.2
-
config FSP_STATUS_GLOBAL_RESET
hex
depends on SOC_INTEL_COMMON_FSP_RESET
- default 0x40000003 if FSP_STATUS_GLOBAL_RESET_REQUIRED_3
- default 0x40000004 if FSP_STATUS_GLOBAL_RESET_REQUIRED_4
- default 0x40000005 if FSP_STATUS_GLOBAL_RESET_REQUIRED_5
- default 0x40000006 if FSP_STATUS_GLOBAL_RESET_REQUIRED_6
- default 0x40000007 if FSP_STATUS_GLOBAL_RESET_REQUIRED_7
- default 0x40000008 if FSP_STATUS_GLOBAL_RESET_REQUIRED_8
- default 0xffffffff
+ range 0x4000000000000003 0x4000000000000008 if !PLATFORM_USES_FSP2_X86_32
+ range 0x40000003 0x40000008
+ default 0x4000000000000003 if !PLATFORM_USES_FSP2_X86_32
+ default 0x40000003
help
If global reset is supported by SoC then select the correct status value for global
- reset type from SoC Kconfig based on available Kconfig options
- FSP_STATUS_GLOBAL_RESET_REQUIRED_X. Default is unsupported.
+ reset type.
+
+ This option specifies the global reset status code used by the
+ platform, as defined in the FSP specification.
+
+ The FSP specification allows for a range of values to indicate a
+ global reset request, typically between
+ FSP_STATUS_GLOBAL_RESET_REQUIRED_3 and
+ FSP_STATUS_GLOBAL_RESET_REQUIRED_8.
+
+ This option defaults to the most commonly used global reset
+ status code in FSP implementations:
+ - 0x40000003 for 32-bit FSP interfaces
+ - 0x4000000000000003 for 64-bit FSP interfaces
+
+ If your FSP implementation uses a different global reset status
+ code, override this default value accordingly.
config SOC_INTEL_COMMON_FSP_RESET
bool
@@ -467,6 +449,15 @@ config DISPLAY_FSP_TIMESTAMPS
To be able to use this, FSP has to be compiled with `PcdFspPerformanceEnable` set to
`TRUE`.
+config FSP_UGOP_EARLY_SIGN_OF_LIFE
+ bool
+ default n
+ select VBT_CBFS_COMPRESSION_DEFAULT_LZ4
+ help
+ Enable the FSP-M Sign-of-Life feature to display a text message
+ on screen during memory training and CSME update. This utilizes Intel's underlying uGOP
+ technology to enable early sign of life (eSOL) as part of FSP-M.
+
config FSP_ENABLE_SERIAL_DEBUG
bool "Output FSP debug messages on serial console"
default y
@@ -513,4 +504,22 @@ config FSP_MULTIPHASE_SI_INIT_RETURN_BROKEN
reported with Alder Lake and Raptor Lake FSP where MultiPhaseSiInit API is unable
to return any ERROR status.
+config FSP_PUBLISH_MBP_HOB
+ bool
+ default n if CHROMEOS
+ default y
+ help
+ This is to control creation of ME_BIOS_PAYLOAD_HOB (MBP HOB) by FSP.
+ Disabling it for the platforms, which do not use MBP HOB, can improve the boot time.
+
+ ChromeOS devices typically do not require the MBP information, hence it is disabled
+ by default on ChromeOS.
+
+config BUILDING_WITH_DEBUG_FSP
+ bool "Use Debug FSP for Build"
+ default n
+ help
+ Enable this option if you are using a debug build of the FSP (Firmware Support Package)
+ in your project.
+
endif
diff --git a/src/drivers/intel/fsp2_0/fspt_report.c b/src/drivers/intel/fsp2_0/fspt_report.c
index 7fa3205e3d..87c08637c1 100644
--- a/src/drivers/intel/fsp2_0/fspt_report.c
+++ b/src/drivers/intel/fsp2_0/fspt_report.c
@@ -10,14 +10,11 @@ uintptr_t temp_memory_end;
void report_fspt_output(void)
{
- const struct region fsp_car_region = {
- .offset = temp_memory_start,
- .size = temp_memory_end - temp_memory_start,
- };
- const struct region coreboot_car_region = {
- .offset = (uintptr_t)_car_region_start,
- .size = (uintptr_t)_car_region_size,
- };
+ const struct region fsp_car_region = region_create(
+ temp_memory_start, temp_memory_end - temp_memory_start);
+ const struct region coreboot_car_region = region_create(
+ (uintptr_t)_car_region_start, (uintptr_t)_car_region_size);
+
printk(BIOS_DEBUG, "FSP-T: reported temp_mem region: [0x%08lx,0x%08lx)\n",
temp_memory_start, temp_memory_end);
if (!region_is_subregion(&fsp_car_region, &coreboot_car_region)) {
diff --git a/src/drivers/intel/fsp2_0/hob_verify.c b/src/drivers/intel/fsp2_0/hob_verify.c
index ec526e850d..0726b60da3 100644
--- a/src/drivers/intel/fsp2_0/hob_verify.c
+++ b/src/drivers/intel/fsp2_0/hob_verify.c
@@ -43,8 +43,8 @@ void fsp_verify_memory_init_hobs(void)
die("Space between FSP reserved region and BIOS TOLUM!\n");
}
- if (range_entry_end(&tolum) != (uintptr_t)cbmem_top()) {
- printk(BIOS_CRIT, "TOLUM end: 0x%08llx != %p: cbmem_top\n",
+ if (range_entry_end(&tolum) != cbmem_top()) {
+ printk(BIOS_CRIT, "TOLUM end: 0x%08llx != %lx: cbmem_top\n",
range_entry_end(&tolum), cbmem_top());
die("Space between cbmem_top and BIOS TOLUM!\n");
}
diff --git a/src/drivers/intel/fsp2_0/temp_ram_exit.c b/src/drivers/intel/fsp2_0/temp_ram_exit.c
index 86cc2163e9..5b8247cecb 100644
--- a/src/drivers/intel/fsp2_0/temp_ram_exit.c
+++ b/src/drivers/intel/fsp2_0/temp_ram_exit.c
@@ -29,7 +29,7 @@ static void fsp_temp_ram_exit(void)
temp_ram_exit = (void *)(uintptr_t)(hdr.image_base + hdr.temp_ram_exit_entry_offset);
printk(BIOS_DEBUG, "Calling TempRamExit: %p\n", temp_ram_exit);
if (ENV_X86_64 && CONFIG(PLATFORM_USES_FSP2_X86_32))
- status = protected_mode_call_1arg(temp_ram_exit, (uintptr_t)NULL);
+ status = protected_mode_call_1arg(temp_ram_exit, 0);
else
status = temp_ram_exit(NULL);
diff --git a/src/drivers/intel/gma/acpi/configure_brightness_levels.asl b/src/drivers/intel/gma/acpi/configure_brightness_levels.asl
index 3a097e336f..a35ffe3863 100644
--- a/src/drivers/intel/gma/acpi/configure_brightness_levels.asl
+++ b/src/drivers/intel/gma/acpi/configure_brightness_levels.asl
@@ -10,8 +10,11 @@
Device (BOX3)
{
Name (_ADR, 0)
-
- OperationRegion (OPRG, SystemMemory, ASLS, 0x2000)
+ /*
+ * Only cover MBOX3 since Windows 10/11 doesn't like
+ * when the full (MBOX4/MBOX5 as well) OpRegion is covered.
+ */
+ OperationRegion (OPRG, SystemMemory, ASLS, 0x400)
Field (OPRG, DWordAcc, NoLock, Preserve)
{
// OpRegion Header
diff --git a/src/drivers/intel/gma/gma-gfx_init.ads b/src/drivers/intel/gma/gma-gfx_init.ads
index 88f64d9ffe..ec1e0ce0ce 100644
--- a/src/drivers/intel/gma/gma-gfx_init.ads
+++ b/src/drivers/intel/gma/gma-gfx_init.ads
@@ -16,14 +16,14 @@ is
----------------------------------------------------------------------------
- function c_fb_add_framebuffer_info
+ function C_Fb_Add_Framebuffer_Info_Simple
(fb_addr: Interfaces.C.size_t;
x_resolution : word32;
y_resolution : word32;
bytes_per_line : word32;
bits_per_pixel : word8)
- return Interfaces.C.size_t;
+ return Interfaces.C.int;
- pragma import (C, c_fb_add_framebuffer_info, "fb_add_framebuffer_info");
+ pragma import (C, C_Fb_Add_Framebuffer_Info_Simple, "fb_add_framebuffer_info_simple");
end GMA.GFX_Init;
diff --git a/src/drivers/intel/gma/hires_fb/gma-gfx_init.adb b/src/drivers/intel/gma/hires_fb/gma-gfx_init.adb
index ae0b0b7ba5..66269b22d7 100644
--- a/src/drivers/intel/gma/hires_fb/gma-gfx_init.adb
+++ b/src/drivers/intel/gma/hires_fb/gma-gfx_init.adb
@@ -21,12 +21,24 @@ is
configs : Pipe_Configs;
----------------------------------------------------------------------------
+ procedure Screen_Rotation (rotation : out Rotation_Type)
+ is
+ begin
+ rotation :=
+ (case Config.DEFAULT_SCREEN_ROTATION_INT is
+ when 90 => Rotated_90,
+ when 180 => Rotated_180,
+ when 270 => Rotated_270,
+ when others => No_Rotation);
+ end Screen_Rotation;
+
procedure gfxinit (lightup_ok : out Interfaces.C.int)
is
use type pos32;
use type word64;
use type word32;
use type Interfaces.C.size_t;
+ use type Interfaces.C.int;
ports : Port_List;
@@ -39,7 +51,7 @@ is
min_h : pos32 := Config.LINEAR_FRAMEBUFFER_MAX_WIDTH;
min_v : pos32 := Config.LINEAR_FRAMEBUFFER_MAX_HEIGHT;
- fbinfo : Interfaces.C.size_t;
+ fbinfo : Interfaces.C.int;
begin
lightup_ok := 0;
@@ -59,10 +71,21 @@ is
end loop;
fb := configs (Primary).Framebuffer;
- fb.Width := Width_Type (min_h);
- fb.Height := Height_Type (min_v);
- fb.Stride := Div_Round_Up (fb.Width, 16) * 16;
- fb.V_Stride := fb.Height;
+ Screen_Rotation (fb.Rotation);
+
+ if fb.Rotation = Rotated_90 or fb.Rotation = Rotated_270 then
+ fb.Width := Width_Type (min_v);
+ fb.Height := Height_Type (min_h);
+ fb.Stride := Div_Round_Up (fb.Width, 32) * 32;
+ fb.V_Stride := Div_Round_Up (fb.Height, 32) * 32;
+ fb.Tiling := Y_Tiled;
+ fb.Offset := word32 (GTT_Rotation_Offset) * GTT_Page_Size;
+ else
+ fb.Width := Width_Type (min_h);
+ fb.Height := Height_Type (min_v);
+ fb.Stride := Div_Round_Up (fb.Width, 16) * 16;
+ fb.V_Stride := fb.Height;
+ end if;
for i in Pipe_Index loop
exit when configs (i).Port = Disabled;
@@ -82,7 +105,7 @@ is
HW.GFX.GMA.Map_Linear_FB (linear_fb_addr, fb);
if linear_fb_addr /= 0 then
- fbinfo := c_fb_add_framebuffer_info
+ fbinfo := C_Fb_Add_Framebuffer_Info_Simple
(fb_addr => Interfaces.C.size_t (linear_fb_addr),
x_resolution => word32 (fb.Width),
y_resolution => word32 (fb.Height),
diff --git a/src/drivers/intel/gma/opregion.c b/src/drivers/intel/gma/opregion.c
index d5516389e5..f97d37350a 100644
--- a/src/drivers/intel/gma/opregion.c
+++ b/src/drivers/intel/gma/opregion.c
@@ -49,7 +49,7 @@ err:
size = 0;
out:
- if (vbt_size && size)
+ if (vbt_size)
*vbt_size = size;
return data;
}
diff --git a/src/drivers/intel/ish/Kconfig b/src/drivers/intel/ish/Kconfig
index a1544910d4..123452e7e0 100644
--- a/src/drivers/intel/ish/Kconfig
+++ b/src/drivers/intel/ish/Kconfig
@@ -6,3 +6,15 @@ config DRIVERS_INTEL_ISH
help
When enabled, chip driver/intel/ish will publish information to the
SSDT _DSD table for the ISH device.
+
+config DRIVER_INTEL_ISH_HAS_MAIN_FW
+ bool
+ default n
+ depends on DRIVERS_INTEL_ISH
+ help
+ This config specifies that the Intel Sensor Hub (ISH) is using ISH MAIN firmware. The
+ ISH MAIN FW resides in the rootfs and is loaded by the kernel. Since there is no dependency
+ on AP firmware, the ISH BUP version is not fetched from the CSE firmware partition.
+
+ This setting is platform-specific. Enable it only on platforms where the ISH is
+ confirmed to be using the MAIN firmware.
diff --git a/src/drivers/intel/ish/ish.c b/src/drivers/intel/ish/ish.c
index 7d332e1fd2..5dd4a799bb 100644
--- a/src/drivers/intel/ish/ish.c
+++ b/src/drivers/intel/ish/ish.c
@@ -50,6 +50,9 @@ static void intel_ish_enable(struct device *dev)
static void intel_ish_get_version(void)
{
+ if (CONFIG(SOC_INTEL_CSE_LITE_SYNC_BY_PAYLOAD))
+ return;
+
struct cse_specific_info *info = cbmem_find(CBMEM_ID_CSE_INFO);
if (info == NULL)
return;
@@ -63,6 +66,9 @@ static void intel_ish_get_version(void)
static void intel_ish_final(struct device *dev)
{
+ if (CONFIG(DRIVER_INTEL_ISH_HAS_MAIN_FW))
+ return;
+
if (CONFIG(SOC_INTEL_STORE_ISH_FW_VERSION))
intel_ish_get_version();
}
@@ -79,7 +85,8 @@ static const struct device_operations pci_ish_device_ops = {
};
static const unsigned short pci_device_ids[] = {
- PCI_DID_INTEL_PTL_ISHB,
+ PCI_DID_INTEL_PTL_H_ISHB,
+ PCI_DID_INTEL_PTL_U_H_ISHB,
PCI_DID_INTEL_LNL_ISHB,
PCI_DID_INTEL_MTL_ISHB,
PCI_DID_INTEL_CNL_ISHB,
diff --git a/src/drivers/mipi/panel-IVO_T109NW41.c b/src/drivers/mipi/panel-IVO_T109NW41.c
index 97a69d3ea3..f604953aed 100644
--- a/src/drivers/mipi/panel-IVO_T109NW41.c
+++ b/src/drivers/mipi/panel-IVO_T109NW41.c
@@ -21,7 +21,7 @@ struct panel_serializable_data IVO_T109NW41 = {
.init = {
PANEL_DELAY(60),
PANEL_DCS(0xB9, 0x83, 0x10, 0x21, 0x55, 0x00),
- PANEL_DCS(0xB1, 0x2C, 0xED, 0xED, 0x0F, 0xCF, 0x42, 0xF5, 0x39,
+ PANEL_DCS(0xB1, 0x2C, 0xED, 0xED, 0x27, 0xE7, 0x52, 0xF5, 0x39,
0x36, 0x36, 0x36, 0x36, 0x32, 0x8B, 0x11, 0x65, 0x00, 0x88,
0xFA, 0xFF, 0xFF, 0x8F, 0xFF, 0x08, 0xD6, 0x33),
PANEL_DCS(0xB2, 0x00, 0x47, 0xB0, 0x80, 0x00, 0x12, 0x71, 0x3C,
@@ -65,11 +65,11 @@ struct panel_serializable_data IVO_T109NW41 = {
0xAA, 0xAA, 0xAA, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
- PANEL_DCS(0xE0, 0x04, 0X04, 0X06, 0X0A, 0X0A, 0X05, 0X12, 0X14,
- 0X17, 0X13, 0X2C, 0X33, 0X39, 0X4B, 0X4C, 0X56, 0X61, 0X78,
- 0X7A, 0X41, 0X50, 0X68, 0X73, 0X04, 0X04, 0X06, 0X0A, 0X0A,
- 0X05, 0X12, 0X14, 0X17, 0X13, 0X2C, 0X33, 0X39, 0X4B, 0X4C,
- 0X56, 0X61, 0X78, 0X7A, 0X41, 0X50, 0X68, 0X73),
+ PANEL_DCS(0xE0, 0x00, 0x07, 0x10, 0x17, 0x1C, 0x33, 0x48, 0x50,
+ 0x57, 0x50, 0x68, 0x6E, 0x71, 0x7F, 0x81, 0x8A, 0x8E, 0x9B,
+ 0x9C, 0x4D, 0x56, 0x5D, 0x73, 0x00, 0x07, 0x10, 0x17, 0x1C,
+ 0x33, 0x48, 0x50, 0x57, 0x50, 0x68, 0x6E, 0x71, 0x7F, 0x81,
+ 0x8A, 0x8E, 0x9B, 0x9C, 0x4D, 0x56, 0x5D, 0x73),
PANEL_DCS(0xE7, 0x07, 0x10, 0x10, 0x1A, 0x26, 0x9E, 0x00, 0x4F,
0xA0, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0A, 0x02,
0x02, 0x00, 0x33, 0x02, 0x04, 0x18, 0x01),
diff --git a/src/drivers/ocp/ewl/ewl.c b/src/drivers/ocp/ewl/ewl.c
index f1f8137b26..f45bff2a82 100644
--- a/src/drivers/ocp/ewl/ewl.c
+++ b/src/drivers/ocp/ewl/ewl.c
@@ -63,7 +63,7 @@ void get_ewl(void)
EWL_ENTRY_HEADER *warning_header;
printk(BIOS_DEBUG, "Number of EWL entries %d\n", hob->numEntries);
while (offset < hob->status.Header.FreeOffset) {
- warning_header = (EWL_ENTRY_HEADER *) &(hob->status.Buffer[offset]);
+ warning_header = (EWL_ENTRY_HEADER *)(&(hob->status.Buffer[offset]));
if (warning_header->Type == EwlType3) {
printk(BIOS_ERR, "EWL type: %d size:%d severity level:%d\n",
warning_header->Type,
diff --git a/src/drivers/pc80/pc/ps2_controller.asl b/src/drivers/pc80/pc/ps2_controller.asl
index 28e337073e..d781973247 100644
--- a/src/drivers/pc80/pc/ps2_controller.asl
+++ b/src/drivers/pc80/pc/ps2_controller.asl
@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0-only */
+
#include "ps2_keyboard.asl"
#include "ps2_mouse.asl"
diff --git a/src/drivers/pc80/pc/ps2_keyboard.asl b/src/drivers/pc80/pc/ps2_keyboard.asl
index b3d5efada3..5bbc7932b2 100644
--- a/src/drivers/pc80/pc/ps2_keyboard.asl
+++ b/src/drivers/pc80/pc/ps2_keyboard.asl
@@ -1,18 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
- Device (PS2K) // Keyboard
- {
- Name(_HID, EISAID(CONFIG_PS2K_EISAID))
- Name(_CID, EISAID("PNP030B"))
- Name(_CRS, ResourceTemplate()
- {
- IO (Decode16, 0x60, 0x60, 0x01, 0x01)
- IO (Decode16, 0x64, 0x64, 0x01, 0x01)
- IRQ (Edge, ActiveHigh, Exclusive) { 0x01 } // IRQ 1
- })
+Device (PS2K) // PS/2 Keyboard
+{
+ Name (_HID, EISAID (CONFIG_PS2K_EISAID))
+ Name (_CID, EISAID ("PNP030B"))
+
+ Name (_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x60, 0x60, 0x01, 0x01)
+ IO (Decode16, 0x64, 0x64, 0x01, 0x01)
+ IRQ (Edge, ActiveHigh, Exclusive) { 0x01 } // IRQ 1
+ })
- Method (_STA, 0)
- {
- Return (0xf)
- }
+ Method (_STA, 0)
+ {
+ Return (0x0F)
}
+}
diff --git a/src/drivers/pc80/pc/ps2_mouse.asl b/src/drivers/pc80/pc/ps2_mouse.asl
index f9ac4159b2..04d160522f 100644
--- a/src/drivers/pc80/pc/ps2_mouse.asl
+++ b/src/drivers/pc80/pc/ps2_mouse.asl
@@ -1,15 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
- Device (PS2M) // Mouse
+
+Device (PS2M) // PS/2 Mouse
+{
+ Name (_HID, EISAID (CONFIG_PS2M_EISAID))
+ Name (_CID, EISAID ("PNP0F13"))
+
+ Name (_CRS, ResourceTemplate()
{
- Name(_HID, EISAID(CONFIG_PS2M_EISAID))
- Name(_CID, EISAID("PNP0F13"))
- Name(_CRS, ResourceTemplate()
- {
- IRQ (Edge, ActiveHigh, Exclusive) { 0x0c } // IRQ 12
- })
+ IRQ (Edge, ActiveHigh, Exclusive) { 0x0C } // IRQ 12
+ })
- Method(_STA, 0)
- {
- Return (0xf)
- }
+ Method (_STA, 0)
+ {
+ Return (0x0F)
}
+}
diff --git a/src/drivers/pc80/rtc/mc146818rtc.c b/src/drivers/pc80/rtc/mc146818rtc.c
index 6474ecbd1a..1c4428a447 100644
--- a/src/drivers/pc80/rtc/mc146818rtc.c
+++ b/src/drivers/pc80/rtc/mc146818rtc.c
@@ -65,6 +65,9 @@ int cmos_error(void)
#define RTC_CONTROL_DEFAULT (RTC_24H)
#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)
+_Static_assert(!CONFIG(SOC_AMD_COMMON) || !(RTC_FREQ_SELECT_DEFAULT & RTC_AMD_BANK_SELECT),
+ "Bank 1 should not be selected for AMD");
+
static bool __cmos_init(bool invalid)
{
bool cmos_invalid;
@@ -97,9 +100,9 @@ static bool __cmos_init(bool invalid)
if (invalid || cmos_invalid || checksum_invalid) {
if (!CONFIG(USE_OPTION_TABLE)) {
- cmos_write(0, 0x01);
- cmos_write(0, 0x03);
- cmos_write(0, 0x05);
+ cmos_write(0, RTC_CLK_SECOND_ALARM);
+ cmos_write(0, RTC_CLK_MINUTE_ALARM);
+ cmos_write(0, RTC_CLK_HOUR_ALARM);
for (i = 10; i < 128; i++)
cmos_write(0, i);
cleared_cmos = true;
diff --git a/src/drivers/pc80/tpm/chip.h b/src/drivers/pc80/tpm/chip.h
index af731530f1..af37ceeb67 100644
--- a/src/drivers/pc80/tpm/chip.h
+++ b/src/drivers/pc80/tpm/chip.h
@@ -3,7 +3,7 @@
#ifndef DRIVERS_PC80_TPM_CHIP_H
#define DRIVERS_PC80_TPM_CHIP_H
-typedef struct drivers_pc80_tpm_config {
+struct drivers_pc80_tpm_config {
/*
* TPM Interrupt polarity:
*
@@ -13,6 +13,6 @@ typedef struct drivers_pc80_tpm_config {
* Falling Edge 3
*/
u8 irq_polarity;
-} tpm_config_t;
+};
#endif /* DRIVERS_PC80_TPM_CHIP_H */
diff --git a/src/drivers/pc80/tpm/tis.c b/src/drivers/pc80/tpm/tis.c
index d811c52e90..93cb2a6163 100644
--- a/src/drivers/pc80/tpm/tis.c
+++ b/src/drivers/pc80/tpm/tis.c
@@ -286,6 +286,14 @@ static inline tpm_result_t tis_wait_valid_data(int locality)
static inline int tis_has_valid_data(int locality)
{
const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
+
+ /*
+ * Certain TPMs require a small delay here, as they have set
+ * TIS_STS_VALID first and TIS_STS_DATA_AVAILABLE few clocks later.
+ */
+ if ((tpm_read_status(locality) & has_data) == has_data)
+ return 1;
+
return (tpm_read_status(locality) & has_data) == has_data;
}
@@ -635,14 +643,6 @@ static tpm_result_t tis_readresponse(u8 *buffer, size_t *len)
if (offset == expected_count)
break; /* We got all we need */
- /*
- * Certain TPMs seem to need some delay between tis_wait_valid()
- * and tis_has_valid_data(), or some race-condition-related
- * issue will occur.
- */
- if (CONFIG(TPM_RDRESP_NEED_DELAY))
- udelay(10);
-
} while (tis_has_valid_data(locality));
/* * Make sure we indeed read all there was. */
@@ -779,9 +779,11 @@ static void lpc_tpm_read_resources(struct device *dev)
static void lpc_tpm_set_resources(struct device *dev)
{
- tpm_config_t *config = (tpm_config_t *)dev->chip_info;
+ struct drivers_pc80_tpm_config *config;
DEVTREE_CONST struct resource *res;
+ config = (struct drivers_pc80_tpm_config *)dev->chip_info;
+
for (res = dev->resource_list; res; res = res->next) {
if (!(res->flags & IORESOURCE_ASSIGNED))
continue;
diff --git a/src/drivers/smbus/i2c_smbus_console.c b/src/drivers/smbus/i2c_smbus_console.c
index d651790694..00b53931e7 100644
--- a/src/drivers/smbus/i2c_smbus_console.c
+++ b/src/drivers/smbus/i2c_smbus_console.c
@@ -13,7 +13,12 @@ void i2c_smbus_console_init(void)
void i2c_smbus_console_tx_byte(unsigned char c)
{
- do_smbus_write_byte(CONFIG_FIXED_SMBUS_IO_BASE,
- CONFIG_CONSOLE_I2C_SMBUS_SLAVE_ADDRESS,
- CONFIG_CONSOLE_I2C_SMBUS_SLAVE_DATA_REGISTER, c);
+ if (CONFIG(CONSOLE_I2C_SMBUS_HAVE_DATA_REGISTER)) {
+ do_smbus_write_byte(CONFIG_FIXED_SMBUS_IO_BASE,
+ CONFIG_CONSOLE_I2C_SMBUS_SLAVE_ADDRESS,
+ CONFIG_CONSOLE_I2C_SMBUS_SLAVE_DATA_REGISTER, c);
+ } else {
+ do_smbus_send_byte(CONFIG_FIXED_SMBUS_IO_BASE,
+ CONFIG_CONSOLE_I2C_SMBUS_SLAVE_ADDRESS, c);
+ }
}
diff --git a/src/drivers/smmstore/ramstage.c b/src/drivers/smmstore/ramstage.c
index 857fe89336..1cbf9fb28a 100644
--- a/src/drivers/smmstore/ramstage.c
+++ b/src/drivers/smmstore/ramstage.c
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-#include <bootstate.h>
#include <cpu/x86/smm.h>
#include <commonlib/helpers.h>
#include <commonlib/region.h>
diff --git a/src/drivers/soundwire/alc711/Kconfig b/src/drivers/soundwire/alc711/Kconfig
index 0d96fa96bb..e9ba852d9a 100644
--- a/src/drivers/soundwire/alc711/Kconfig
+++ b/src/drivers/soundwire/alc711/Kconfig
@@ -1,4 +1,24 @@
## SPDX-License-Identifier: GPL-2.0-only
+config DRIVERS_SOUNDWIRE_ALC_BASE_7XX
+ bool
+ help
+ Base code for Realtek ALC7xxx Codec SoundWire driver.
+
config DRIVERS_SOUNDWIRE_ALC711
bool
+ select DRIVERS_SOUNDWIRE_ALC_BASE_7XX
+ help
+ SoundWire driver for Realtek ALC711 device
+
+config DRIVERS_SOUNDWIRE_ALC721
+ bool
+ select DRIVERS_SOUNDWIRE_ALC_BASE_7XX
+ help
+ SoundWire driver for Realtek ALC721 device
+
+config DRIVERS_SOUNDWIRE_ALC722
+ bool
+ select DRIVERS_SOUNDWIRE_ALC_BASE_7XX
+ help
+ SoundWire driver for Realtek ALC722 device
diff --git a/src/drivers/soundwire/alc711/Makefile.mk b/src/drivers/soundwire/alc711/Makefile.mk
index 63e0993435..cdedc41582 100644
--- a/src/drivers/soundwire/alc711/Makefile.mk
+++ b/src/drivers/soundwire/alc711/Makefile.mk
@@ -1,3 +1,3 @@
## SPDX-License-Identifier: GPL-2.0-only
-ramstage-$(CONFIG_DRIVERS_SOUNDWIRE_ALC711) += alc711.c
+ramstage-$(CONFIG_DRIVERS_SOUNDWIRE_ALC_BASE_7XX) += alc711.c
diff --git a/src/drivers/soundwire/alc711/alc711.c b/src/drivers/soundwire/alc711/alc711.c
index 7d1ac4274f..1a31f0d4e0 100644
--- a/src/drivers/soundwire/alc711/alc711.c
+++ b/src/drivers/soundwire/alc711/alc711.c
@@ -11,10 +11,22 @@
#include "chip.h"
static struct soundwire_address alc711_address = {
+#if CONFIG(DRIVERS_SOUNDWIRE_ALC722)
+ .version = SOUNDWIRE_VERSION_1_2,
+ .part_id = MIPI_DEV_ID_REALTEK_ALC722,
+ .class = MIPI_CLASS_SDCA,
+#elif CONFIG(DRIVERS_SOUNDWIRE_ALC721)
+ .version = SOUNDWIRE_VERSION_1_2,
+ .part_id = MIPI_DEV_ID_REALTEK_ALC721,
+ .class = MIPI_CLASS_SDCA,
+#elif CONFIG(DRIVERS_SOUNDWIRE_ALC711)
.version = SOUNDWIRE_VERSION_1_1,
- .manufacturer_id = MIPI_MFG_ID_REALTEK,
.part_id = MIPI_DEV_ID_REALTEK_ALC711,
- .class = MIPI_CLASS_NONE
+ .class = MIPI_CLASS_NONE,
+#else
+#error "No Realtek SoundWire codec selected"
+#endif
+ .manufacturer_id = MIPI_MFG_ID_REALTEK,
};
static struct soundwire_slave alc711_slave = {
@@ -150,6 +162,10 @@ static void soundwire_alc711_enable(struct device *dev)
}
struct chip_operations drivers_soundwire_alc711_ops = {
- .name = "Realtek ALC711 SoundWire Codec",
+#if CONFIG(DRIVERS_SOUNDWIRE_ALC_BASE_7XX)
+ .name = "Realtek ALC 7 Series SoundWire Codec",
+#else
+ .name = "Unknown",
+#endif
.enable_dev = soundwire_alc711_enable
};
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig
index 6d161f4005..5d9698e16a 100644
--- a/src/drivers/spi/Kconfig
+++ b/src/drivers/spi/Kconfig
@@ -146,7 +146,8 @@ config SPI_FLASH_STMICRO
default y if SPI_FLASH_INCLUDE_ALL_DRIVERS
help
Select this option if your chipset driver needs to store certain
- data in the SPI flash and your SPI flash is made by ST MICRO.
+ data in the SPI flash and your SPI flash is made by ST Micro,
+ Numonyx or Micron.
config SPI_FLASH_WINBOND
bool
@@ -188,6 +189,17 @@ config SPI_FLASH_FORCE_4_BYTE_ADDR_MODE
On some platforms with SPI flashes larger than 16MB, the SPI flash may need
to remain in 4-byte addressing mode.
+config SPI_FLASH_SFDP
+ bool
+ help
+ Include serial flash discoverable parameters (SFDP) support.
+
+config SPI_FLASH_RPMC
+ bool
+ select SPI_FLASH_SFDP
+ help
+ Include replay-protected monotonic counter (RPMC) support.
+
endif # SPI_FLASH
config HAVE_EM100PRO_SPI_CONSOLE_SUPPORT
diff --git a/src/drivers/spi/Makefile.mk b/src/drivers/spi/Makefile.mk
index 97c3d63d49..3310b968c3 100644
--- a/src/drivers/spi/Makefile.mk
+++ b/src/drivers/spi/Makefile.mk
@@ -31,6 +31,8 @@ $(1)-$(CONFIG_SPI_FLASH_SST) += sst.c
$(1)-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.c
$(1)-$(CONFIG_SPI_FLASH_WINBOND) += winbond.c
$(1)-$(CONFIG_SPI_FLASH_ISSI) += issi.c
+$(1)-$(CONFIG_SPI_FLASH_SFDP) += spi_flash_sfdp.c
+$(1)-$(CONFIG_SPI_FLASH_RPMC) += spi_flash_rpmc.c
endef
$(eval $(call add_spi_stage,bootblock,_EARLY))
diff --git a/src/drivers/spi/acpi/acpi.c b/src/drivers/spi/acpi/acpi.c
index 687dd52d56..bd3f89a857 100644
--- a/src/drivers/spi/acpi/acpi.c
+++ b/src/drivers/spi/acpi/acpi.c
@@ -149,7 +149,8 @@ static void spi_acpi_fill_ssdt_generator(const struct device *dev)
enable_gpio_index, 0,
config->enable_gpio.active_low);
/* Add generic property list */
- acpi_dp_add_property_list(dsd, config->property_list,
+ if (config->property_count > 0)
+ acpi_dp_add_property_list(dsd, config->property_list,
config->property_count);
acpi_dp_write(dsd);
}
diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c
index 42952df531..b7a024c4c2 100644
--- a/src/drivers/spi/spi_flash.c
+++ b/src/drivers/spi/spi_flash.c
@@ -129,6 +129,16 @@ int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t le
return ret;
}
+int spi_flash_cmd_multi(const struct spi_slave *spi, const u8 *dout, size_t bytes_out,
+ void *din, size_t bytes_in)
+{
+ int ret = do_spi_flash_cmd(spi, dout, bytes_out, din, bytes_in);
+ if (ret)
+ printk(BIOS_WARNING, "SF: Failed to send command %02x: %d\n", dout[0], ret);
+
+ return ret;
+}
+
/* TODO: This code is quite possibly broken and overflowing stacks. Fix ASAP! */
#pragma GCC diagnostic push
#if defined(__GNUC__) && !defined(__clang__)
@@ -559,6 +569,11 @@ int spi_flash_probe(unsigned int bus, unsigned int cs, struct spi_flash *flash)
spi_flash_cmd(&flash->spi, CMD_EXIT_4BYTE_ADDR_MODE, NULL, 0);
}
+ /* TODO: only do this in stages that will need to call those functions? */
+ if (CONFIG(SPI_FLASH_RPMC)) {
+ spi_flash_fill_rpmc_caps(flash);
+ }
+
return 0;
}
@@ -610,12 +625,12 @@ int spi_flash_status(const struct spi_flash *flash, u8 *reg)
int spi_flash_is_write_protected(const struct spi_flash *flash,
const struct region *region)
{
- struct region flash_region = { 0 };
+ struct region flash_region;
if (!flash || !region)
return -1;
- flash_region.size = flash->size;
+ flash_region = region_create(0, flash->size);
if (!region_is_subregion(&flash_region, region))
return -1;
@@ -633,13 +648,13 @@ int spi_flash_set_write_protected(const struct spi_flash *flash,
const struct region *region,
const enum spi_flash_status_reg_lockdown mode)
{
- struct region flash_region = { 0 };
+ struct region flash_region;
int ret;
if (!flash)
return -1;
- flash_region.size = flash->size;
+ flash_region = region_create(0, flash->size);
if (!region_is_subregion(&flash_region, region))
return -1;
@@ -755,12 +770,12 @@ int spi_flash_ctrlr_protect_region(const struct spi_flash *flash,
const enum ctrlr_prot_type type)
{
const struct spi_ctrlr *ctrlr;
- struct region flash_region = { 0 };
+ struct region flash_region;
if (!flash)
return -1;
- flash_region.size = flash->size;
+ flash_region = region_create(0, flash->size);
if (!region_is_subregion(&flash_region, region))
return -1;
diff --git a/src/drivers/spi/spi_flash_internal.h b/src/drivers/spi/spi_flash_internal.h
index 3aea9140e4..a30f58f9a5 100644
--- a/src/drivers/spi/spi_flash_internal.h
+++ b/src/drivers/spi/spi_flash_internal.h
@@ -7,6 +7,8 @@
#ifndef SPI_FLASH_INTERNAL_H
#define SPI_FLASH_INTERNAL_H
+#include <types.h>
+
/* Common commands */
#define CMD_READ_ID 0x9f
@@ -30,6 +32,10 @@
/* Send a single-byte command to the device and read the response */
int spi_flash_cmd(const struct spi_slave *spi, u8 cmd, void *response, size_t len);
+/* Send a multi-byte command to the device and read the response */
+int spi_flash_cmd_multi(const struct spi_slave *spi, const u8 *dout, size_t bytes_out,
+ void *din, size_t bytes_in);
+
/*
* Send a multi-byte command to the device followed by (optional)
* data. Used for programming the flash array, etc.
@@ -130,4 +136,30 @@ extern const struct spi_flash_ops_descriptor spi_flash_pp_0x20_sector_desc;
/* Page Programming Command Set with 0xd8 Sector Erase command. */
extern const struct spi_flash_ops_descriptor spi_flash_pp_0xd8_sector_desc;
+struct sfdp_rpmc_info {
+ bool flash_hardening;
+ enum {
+ SFDP_RPMC_COUNTER_BITS_32 = 0,
+ SFDP_RPMC_COUNTER_BITS_RESERVED = 1,
+ } monotonic_counter_size;
+ enum {
+ SFDP_RPMC_POLL_OP2_EXTENDED_STATUS = 0,
+ SFDP_RPMC_POLL_READ_STATUS = 1,
+ } busy_polling_method;
+ uint8_t number_of_counters;
+ uint8_t op1_write_command;
+ uint8_t op2_read_command;
+ uint64_t update_rate_s;
+ uint64_t read_counter_polling_delay_us;
+ uint64_t write_counter_polling_short_delay_us;
+ uint64_t write_counter_polling_long_delay_us;
+};
+
+/* Get RPMC information from the SPI flash's SFDP table */
+enum cb_err spi_flash_get_sfdp_rpmc(const struct spi_flash *flash,
+ struct sfdp_rpmc_info *rpmc_info);
+
+/* Fill rpmc_caps field in spi_flash struct with RPMC config from SFDP */
+void spi_flash_fill_rpmc_caps(struct spi_flash *flash);
+
#endif /* SPI_FLASH_INTERNAL_H */
diff --git a/src/drivers/spi/spi_flash_rpmc.c b/src/drivers/spi/spi_flash_rpmc.c
new file mode 100644
index 0000000000..148e3bb8a1
--- /dev/null
+++ b/src/drivers/spi/spi_flash_rpmc.c
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <console/console.h>
+#include <spi-generic.h>
+#include <spi_flash.h>
+#include <string.h>
+#include <timer.h>
+#include <types.h>
+
+#include "spi_flash_internal.h"
+
+void spi_flash_fill_rpmc_caps(struct spi_flash *flash)
+{
+ struct sfdp_rpmc_info rpmc_info;
+
+ flash->rpmc_caps.rpmc_available = false;
+
+ if (spi_flash_get_sfdp_rpmc(flash, &rpmc_info) != CB_SUCCESS)
+ return;
+
+ if (rpmc_info.monotonic_counter_size != SFDP_RPMC_COUNTER_BITS_32) {
+ printk(BIOS_WARNING, "RPMC: unexpected counter size\n");
+ return;
+ }
+
+ flash->rpmc_caps.poll_op2_ext_stat = rpmc_info.busy_polling_method ==
+ SFDP_RPMC_POLL_OP2_EXTENDED_STATUS;
+ flash->rpmc_caps.number_of_counters = rpmc_info.number_of_counters;
+ flash->rpmc_caps.op1_write_cmd = rpmc_info.op1_write_command;
+ flash->rpmc_caps.op2_read_cmd = rpmc_info.op2_read_command;
+ flash->rpmc_caps.rpmc_available = true;
+}
diff --git a/src/drivers/spi/spi_flash_sfdp.c b/src/drivers/spi/spi_flash_sfdp.c
new file mode 100644
index 0000000000..94509fdf56
--- /dev/null
+++ b/src/drivers/spi/spi_flash_sfdp.c
@@ -0,0 +1,365 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <console/console.h>
+#include <spi-generic.h>
+#include <spi_flash.h>
+#include <types.h>
+
+#include "spi_flash_internal.h"
+
+/*
+ * Assumptions (not) made in the code:
+ * - code is supposed to be endian-safe
+ *
+ * Limitations:
+ * - only legacy access protocol supported:
+ * - only NOR flash
+ * - read SFDP instruction is sufficient and no fetch SFDP instruction needed
+ * - always 3 byte addresses for SFDP
+ * - only 1 bit SPI mode supported
+ * - always 8 dummy cycles after the address is sent
+*/
+
+#define CMD_READ_SFDP 0x5a
+
+static void read_sfdp_data_update_buffer_offset(uint8_t *buf_cmd, uint32_t offset)
+{
+ buf_cmd[1] = offset >> 16 & 0xff;
+ buf_cmd[2] = offset >> 8 & 0xff;
+ buf_cmd[3] = offset >> 0 & 0xff;
+}
+
+static enum cb_err read_sfdp_data(const struct spi_flash *flash, uint32_t offset, size_t len,
+ uint8_t *buf)
+{
+ uint8_t buf_cmd[5];
+
+ buf_cmd[0] = CMD_READ_SFDP;
+ /* the read offset in buf_cmd[1..3] gets written in
+ read_sfdp_data_update_buffer_offset */
+ buf_cmd[4] = 0; /* dummy byte */
+
+ uint8_t *data = buf;
+
+ /* split data transfers into chunks that each fit into the SPI controller's buffer */
+ while (len) {
+ uint32_t xfer_len = spi_crop_chunk(&flash->spi, sizeof(buf_cmd), len);
+ read_sfdp_data_update_buffer_offset(buf_cmd, offset);
+ if (spi_flash_cmd_multi(&flash->spi, buf_cmd, sizeof(buf_cmd), data, xfer_len))
+ return CB_ERR;
+ offset += xfer_len;
+ data += xfer_len;
+ len -= xfer_len;
+ }
+
+ return CB_SUCCESS;
+}
+
+#define SFDP_HEADER_OFFSET 0
+#define SFDP_HEADER_LEN (2 * sizeof(uint32_t))
+#define SFDP_HEADER_SIGNATURE 0x50444653 /* LE: "SFDP" */
+#define SFDP_HEADER_ACCESS_PROTOCOL_LEGACY 0xff
+/* header byte offsets */
+#define SFDP_HEADER_SIGNATURE_0 0
+#define SFDP_HEADER_SIGNATURE_1 1
+#define SFDP_HEADER_SIGNATURE_2 2
+#define SFDP_HEADER_SIGNATURE_3 3
+#define SFDP_HEADER_MINOR_REV 4
+#define SFDP_HEADER_MAJOR_REV 5
+#define SFDP_HEADER_PARAMETER_HEADER_NUMBER 6
+#define SFDP_HEADER_ACCESS_PROTOCOL 7
+
+static enum cb_err get_sfdp_header(const struct spi_flash *flash, uint16_t *sfdp_revision,
+ uint8_t *number_of_parameter_headers,
+ uint8_t *access_prototcol)
+{
+ uint8_t buf[SFDP_HEADER_LEN];
+ uint32_t signature;
+
+ if (read_sfdp_data(flash, SFDP_HEADER_OFFSET, SFDP_HEADER_LEN, buf) != CB_SUCCESS)
+ return CB_ERR;
+
+ signature = buf[SFDP_HEADER_SIGNATURE_0] | buf[SFDP_HEADER_SIGNATURE_1] << 8 |
+ buf[SFDP_HEADER_SIGNATURE_2] << 16 | buf[SFDP_HEADER_SIGNATURE_3] << 24;
+ *sfdp_revision = buf[SFDP_HEADER_MINOR_REV] | buf[SFDP_HEADER_MAJOR_REV] << 8;
+ *number_of_parameter_headers = buf[SFDP_HEADER_PARAMETER_HEADER_NUMBER] + 1;
+ *access_prototcol = buf[SFDP_HEADER_ACCESS_PROTOCOL];
+
+ if (signature != SFDP_HEADER_SIGNATURE)
+ return CB_ERR;
+
+ /* We make the assumption in the code that the legacy access protocol is used */
+ if (*access_prototcol != SFDP_HEADER_ACCESS_PROTOCOL_LEGACY)
+ return CB_ERR;
+
+ return CB_SUCCESS;
+}
+
+#define SFDP_PARAMETER_HEADER_LEN (2 * sizeof(uint32_t))
+#define SFDP_PARAMETER_HEADER_OFFSET(n) (SFDP_HEADER_OFFSET + SFDP_HEADER_LEN + \
+ n * SFDP_PARAMETER_HEADER_LEN)
+/* parameter header byte offsets */
+#define SFDP_PARAMETER_HEADER_ID_LSB 0
+#define SFDP_PARAMETER_HEADER_MINOR_REV 1
+#define SFDP_PARAMETER_HEADER_MAJOR_REV 2
+#define SFDP_PARAMETER_HEADER_LENGTH_DWORDS 3
+#define SFDP_PARAMETER_HEADER_POINTER_0 4
+#define SFDP_PARAMETER_HEADER_POINTER_1 5
+#define SFDP_PARAMETER_HEADER_POINTER_2 6
+#define SFDP_PARAMETER_HEADER_ID_MSB 7
+
+/* get_sfdp_parameter_header_by_index must be called with a valid index */
+static enum cb_err get_sfdp_parameter_header_by_index(const struct spi_flash *flash,
+ uint8_t index, uint16_t *id,
+ uint16_t *revision,
+ uint8_t *length_dwords,
+ uint32_t *table_pointer)
+{
+ uint8_t buf[SFDP_PARAMETER_HEADER_LEN];
+
+ if (read_sfdp_data(flash, SFDP_PARAMETER_HEADER_OFFSET(index),
+ SFDP_PARAMETER_HEADER_LEN, buf) != CB_SUCCESS)
+ return CB_ERR;
+
+ *id = buf[SFDP_PARAMETER_HEADER_ID_LSB] | buf[SFDP_PARAMETER_HEADER_ID_MSB] << 8;
+ *revision = buf[SFDP_PARAMETER_HEADER_MINOR_REV] |
+ buf[SFDP_PARAMETER_HEADER_MAJOR_REV] << 8;
+ *length_dwords = buf[SFDP_PARAMETER_HEADER_LENGTH_DWORDS];
+ *table_pointer = buf[SFDP_PARAMETER_HEADER_POINTER_0] |
+ buf[SFDP_PARAMETER_HEADER_POINTER_1] << 8 |
+ buf[SFDP_PARAMETER_HEADER_POINTER_2] << 16;
+
+ /* the parameter table pointer byte address must be DWORD-aligned */
+ if (!IS_ALIGNED(*table_pointer, sizeof(uint32_t)))
+ return CB_ERR;
+
+ /* TODO: check id validity? */
+
+ return CB_SUCCESS;
+}
+
+void spi_flash_print_sfdp_headers(const struct spi_flash *flash)
+{
+ enum cb_err stat;
+ uint16_t sfdp_rev;
+ uint8_t param_header_count;
+ uint8_t access_protocol;
+
+ if (get_sfdp_header(flash, &sfdp_rev, &param_header_count, &access_protocol) !=
+ CB_SUCCESS) {
+ printk(BIOS_ERR, "Failed to read SFDP header from SPI flash\n");
+ return;
+ }
+
+ printk(BIOS_DEBUG, "SFDP header found in SPI flash.\n");
+ printk(BIOS_DEBUG, "major rev %#x, minor rev %#x, access protocol %#x, "
+ "number of headers %d\n", sfdp_rev >> 8, sfdp_rev & 0xff, access_protocol,
+ param_header_count);
+
+ uint16_t tbl_id;
+ uint16_t tbl_rev;
+ uint8_t tbl_len_dwords;
+ uint32_t tbl_pointer;
+
+ for (unsigned int i = 0; i < param_header_count; i++) {
+ stat = get_sfdp_parameter_header_by_index(flash, i, &tbl_id, &tbl_rev,
+ &tbl_len_dwords, &tbl_pointer);
+
+ if (stat == CB_SUCCESS) {
+ printk(BIOS_DEBUG, "SFPD header with index %d:\n", i);
+ printk(BIOS_DEBUG, " table ID %#x, major rev %#x, minor rev %#x\n",
+ tbl_id, tbl_rev >> 8, tbl_rev & 0xff);
+ printk(BIOS_DEBUG, " table pointer %#x, table length DWORDS %#x\n",
+ tbl_pointer, tbl_len_dwords);
+ } else {
+ printk(BIOS_ERR, "Cound't read SFPD header with index %d.\n", i);
+ }
+ }
+}
+
+static enum cb_err find_sfdp_parameter_header(const struct spi_flash *flash, uint16_t table_id,
+ uint16_t *revision, uint8_t *length_dwords,
+ uint32_t *table_pointer)
+{
+ enum cb_err stat;
+ uint16_t sfdp_rev;
+ uint8_t param_header_count;
+ uint8_t access_protocol;
+
+ if (get_sfdp_header(flash, &sfdp_rev, &param_header_count, &access_protocol) !=
+ CB_SUCCESS)
+ return CB_ERR;
+
+ /* TODO: add version check? */
+
+ uint16_t tbl_id;
+ uint16_t tbl_rev;
+ uint8_t tbl_len_dwords;
+ uint32_t tbl_pointer;
+
+ for (unsigned int i = 0; i < param_header_count; i++) {
+ stat = get_sfdp_parameter_header_by_index(flash, i, &tbl_id, &tbl_rev,
+ &tbl_len_dwords, &tbl_pointer);
+
+ if (stat == CB_SUCCESS && tbl_id == table_id) {
+ *revision = tbl_rev;
+ *length_dwords = tbl_len_dwords;
+ *table_pointer = tbl_pointer;
+ return CB_SUCCESS;
+ }
+ }
+
+ printk(BIOS_WARNING, "Cound't find SFPD header with table ID %#x.\n", table_id);
+
+ return CB_ERR;
+}
+
+#define SFDP_PARAMETER_ID_RPMC 0xff03
+
+#define SFDP_RPMC_TABLE_LENGTH_DWORDS 2
+#define SFDP_RPMC_TABLE_SUPPORTED_MAJOR_REV 1
+
+/* RPMC parameter table byte offsets and fields */
+#define SFDP_RPMC_TABLE_CONFIG 0
+#define SFDP_RPMC_TABLE_CONFIG_FLASH_HARDENING_BIT BIT(0)
+#define SFDP_RPMC_TABLE_CONFIG_MONOTONIC_COUNTER_SIZE_BIT BIT(1)
+#define SFDP_RPMC_TABLE_CONFIG_BUSY_POLLING_METHOD BIT(2)
+#define SFDP_RPMC_TABLE_CONFIG_RESERVED BIT(3)
+#define SFDP_RPMC_TABLE_CONFIG_RESERVED_VALUE 0x08
+#define SFDP_RPMC_TABLE_CONFIG_NUM_COUNTERS_MASK 0xf0
+#define SFDP_RPMC_TABLE_CONFIG_NUM_COUNTERS_SHIFT 4
+#define SFDP_RPMC_TABLE_RPMC_OP1 1
+#define SFDP_RPMC_TABLE_RPMC_OP2 2
+#define SFDP_RPMC_TABLE_UPDATE_RATE 3
+#define SFDP_RPMC_TABLE_UPDATE_RATE_MASK 0x0f
+#define SFDP_RPMC_TABLE_UPDATE_RATE_RESERVED_MASK 0xf0
+#define SFDP_RPMC_TABLE_UPDATE_RATE_RESERVED_VALUE 0xf0
+#define SFDP_RPMC_TABLE_READ_COUNTER_POLLING_DELAY 4
+#define SFDP_RPMC_TABLE_WRITE_COUNTER_POLLING_SHORT_DELAY 5
+#define SFDP_RPMC_TABLE_WRITE_COUNTER_POLLING_LONG_DELAY 6
+#define SFDP_RPMC_TABLE_RESERVED_BYTE 7
+#define SFDP_RPMC_TABLE_RESERVED_BYTE_VALUE 0xff
+
+static uint64_t calc_rpmc_update_rate_s(uint8_t val)
+{
+ /* val is at most 15, so this won't overflow */
+ return 5 * 1 << (val & SFDP_RPMC_TABLE_UPDATE_RATE_MASK);
+}
+
+#define SPDF_RPMC_DELAY_VALUE_MASK 0x1f
+#define SPDF_RPMC_DELAY_UNIT_MASK 0x60
+#define SPDF_RPMC_DELAY_UNIT_SHIFT 5
+#define SPDF_RPMC_DELAY_SHORT_UNIT_0_US 1 /* 1us */
+#define SPDF_RPMC_DELAY_SHORT_UNIT_1_US 16 /* 16us */
+#define SPDF_RPMC_DELAY_SHORT_UNIT_2_US 128 /* 128us */
+#define SPDF_RPMC_DELAY_SHORT_UNIT_3_US 1000 /* 1ms */
+#define SPDF_RPMC_DELAY_LONG_UNIT_0_US 1000 /* 1ms */
+#define SPDF_RPMC_DELAY_LONG_UNIT_1_US 16000 /* 16ms */
+#define SPDF_RPMC_DELAY_LONG_UNIT_2_US 128000 /* 128ms */
+#define SPDF_RPMC_DELAY_LONG_UNIT_3_US 1000000 /* 1s */
+
+static uint64_t calc_rpmc_short_delay_us(uint8_t val)
+{
+ const uint8_t value = val & SPDF_RPMC_DELAY_VALUE_MASK;
+ const uint8_t shift = (val & SPDF_RPMC_DELAY_UNIT_MASK) >> SPDF_RPMC_DELAY_UNIT_SHIFT;
+ uint64_t multiplier;
+
+ switch (shift) {
+ case 0:
+ multiplier = SPDF_RPMC_DELAY_SHORT_UNIT_0_US;
+ break;
+ case 1:
+ multiplier = SPDF_RPMC_DELAY_SHORT_UNIT_1_US;
+ break;
+ case 2:
+ multiplier = SPDF_RPMC_DELAY_SHORT_UNIT_2_US;
+ break;
+ default:
+ multiplier = SPDF_RPMC_DELAY_SHORT_UNIT_3_US;
+ break;
+ }
+
+ return value * multiplier;
+}
+
+static uint64_t calc_rpmc_long_delay_us(uint8_t val)
+{
+ const uint8_t value = val & SPDF_RPMC_DELAY_VALUE_MASK;
+ const uint8_t shift = (val & SPDF_RPMC_DELAY_UNIT_MASK) >> SPDF_RPMC_DELAY_UNIT_SHIFT;
+ uint64_t multiplier;
+
+ switch (shift) {
+ case 0:
+ multiplier = SPDF_RPMC_DELAY_LONG_UNIT_0_US;
+ break;
+ case 1:
+ multiplier = SPDF_RPMC_DELAY_LONG_UNIT_1_US;
+ break;
+ case 2:
+ multiplier = SPDF_RPMC_DELAY_LONG_UNIT_2_US;
+ break;
+ default:
+ multiplier = SPDF_RPMC_DELAY_LONG_UNIT_3_US;
+ break;
+ }
+
+ return value * multiplier;
+}
+
+enum cb_err spi_flash_get_sfdp_rpmc(const struct spi_flash *flash,
+ struct sfdp_rpmc_info *rpmc_info)
+{
+ uint16_t rev;
+ uint8_t length_dwords;
+ uint32_t table_pointer;
+ uint8_t buf[SFDP_RPMC_TABLE_LENGTH_DWORDS * sizeof(uint32_t)];
+
+ if (find_sfdp_parameter_header(flash, SFDP_PARAMETER_ID_RPMC, &rev, &length_dwords,
+ &table_pointer) != CB_SUCCESS)
+ return CB_ERR;
+
+ if (length_dwords != SFDP_RPMC_TABLE_LENGTH_DWORDS)
+ return CB_ERR;
+
+ if (rev >> 8 != SFDP_RPMC_TABLE_SUPPORTED_MAJOR_REV) {
+ printk(BIOS_ERR, "Unsupprted major RPMC table revision %#x\n", rev >> 8);
+ return CB_ERR;
+ }
+
+ if (read_sfdp_data(flash, table_pointer, sizeof(buf), buf) != CB_SUCCESS)
+ return CB_ERR;
+
+ if ((buf[SFDP_RPMC_TABLE_CONFIG] & SFDP_RPMC_TABLE_CONFIG_RESERVED) !=
+ SFDP_RPMC_TABLE_CONFIG_RESERVED_VALUE ||
+ (buf[SFDP_RPMC_TABLE_UPDATE_RATE] & SFDP_RPMC_TABLE_UPDATE_RATE_RESERVED_MASK) !=
+ SFDP_RPMC_TABLE_UPDATE_RATE_RESERVED_VALUE ||
+ buf[SFDP_RPMC_TABLE_RESERVED_BYTE] != SFDP_RPMC_TABLE_RESERVED_BYTE_VALUE) {
+ printk(BIOS_ERR, "Unexpected reserved values in RPMC table\n");
+ return CB_ERR;
+ }
+
+ rpmc_info->flash_hardening = !!(buf[SFDP_RPMC_TABLE_CONFIG] &
+ SFDP_RPMC_TABLE_CONFIG_FLASH_HARDENING_BIT);
+ rpmc_info->monotonic_counter_size = (buf[SFDP_RPMC_TABLE_CONFIG] &
+ SFDP_RPMC_TABLE_CONFIG_FLASH_HARDENING_BIT) ?
+ SFDP_RPMC_COUNTER_BITS_RESERVED :
+ SFDP_RPMC_COUNTER_BITS_32;
+ rpmc_info->busy_polling_method = (buf[SFDP_RPMC_TABLE_CONFIG] &
+ SFDP_RPMC_TABLE_CONFIG_BUSY_POLLING_METHOD) ?
+ SFDP_RPMC_POLL_READ_STATUS :
+ SFDP_RPMC_POLL_OP2_EXTENDED_STATUS;
+ rpmc_info->number_of_counters = ((buf[SFDP_RPMC_TABLE_CONFIG] &
+ SFDP_RPMC_TABLE_CONFIG_NUM_COUNTERS_MASK) >>
+ SFDP_RPMC_TABLE_CONFIG_NUM_COUNTERS_SHIFT) + 1;
+ rpmc_info->op1_write_command = buf[SFDP_RPMC_TABLE_RPMC_OP1];
+ rpmc_info->op2_read_command = buf[SFDP_RPMC_TABLE_RPMC_OP2];
+ rpmc_info->update_rate_s = calc_rpmc_update_rate_s(buf[SFDP_RPMC_TABLE_UPDATE_RATE] &
+ SFDP_RPMC_TABLE_UPDATE_RATE_MASK);
+ rpmc_info->read_counter_polling_delay_us = calc_rpmc_short_delay_us(
+ buf[SFDP_RPMC_TABLE_READ_COUNTER_POLLING_DELAY]);
+ rpmc_info->write_counter_polling_short_delay_us = calc_rpmc_short_delay_us(
+ buf[SFDP_RPMC_TABLE_WRITE_COUNTER_POLLING_SHORT_DELAY]);
+ rpmc_info->write_counter_polling_long_delay_us = calc_rpmc_long_delay_us(
+ buf[SFDP_RPMC_TABLE_WRITE_COUNTER_POLLING_LONG_DELAY]);
+ return CB_SUCCESS;
+}
diff --git a/src/drivers/spi/winbond.c b/src/drivers/spi/winbond.c
index 3137bfe8a7..db93ff616c 100644
--- a/src/drivers/spi/winbond.c
+++ b/src/drivers/spi/winbond.c
@@ -267,8 +267,7 @@ static void winbond_bpbits_to_region(const size_t granularity,
tb = !tb;
}
- out->offset = tb ? 0 : flash_size - protected_size;
- out->size = protected_size;
+ *out = region_create(tb ? 0 : flash_size - protected_size, protected_size);
}
/*
@@ -375,7 +374,7 @@ static int winbond_get_write_protection(const struct spi_flash *flash,
}
printk(BIOS_DEBUG, "WINBOND: flash protected range 0x%08zx-0x%08zx\n",
- region_offset(&wp_region), region_end(&wp_region));
+ region_offset(&wp_region), region_last(&wp_region));
return region_is_subregion(&wp_region, region);
}
@@ -503,7 +502,7 @@ winbond_set_write_protection(const struct spi_flash *flash,
int ret;
/* Need to touch TOP or BOTTOM */
- if (region_offset(region) != 0 && region_end(region) != flash->size)
+ if (region_offset(region) != 0 && region_last(region) != flash->size - 1)
return -1;
params = flash->part;
@@ -525,8 +524,8 @@ winbond_set_write_protection(const struct spi_flash *flash,
if (region_sz(&wp_region) > flash->size / 2) {
cmp = 1;
- wp_region.offset = tb ? 0 : region_sz(&wp_region);
- wp_region.size = flash->size - region_sz(&wp_region);
+ wp_region = region_create(tb ? 0 : region_sz(&wp_region),
+ flash->size - region_sz(&wp_region));
tb = !tb;
} else {
cmp = 0;
@@ -601,7 +600,7 @@ winbond_set_write_protection(const struct spi_flash *flash,
return ret;
printk(BIOS_DEBUG, "WINBOND: write-protection set to range "
- "0x%08zx-0x%08zx\n", region_offset(region), region_end(region));
+ "0x%08zx-0x%08zx\n", region_offset(region), region_last(region));
return ret;
}
diff --git a/src/drivers/tpm/ppi.c b/src/drivers/tpm/ppi.c
index 2ce1d07808..68923eaeda 100644
--- a/src/drivers/tpm/ppi.c
+++ b/src/drivers/tpm/ppi.c
@@ -634,7 +634,6 @@ void tpm_ppi_acpi_fill_ssdt(const struct device *dev)
acpigen_write_method_serialized("FUNC", 1);
acpigen_write_to_integer(ARG0_OP, LOCAL0_OP);
- acpigen_write_to_integer(ARG1_OP, LOCAL1_OP);
acpigen_write_if();
acpigen_emit_byte(LGREATER_OP);
acpigen_emit_byte(LOCAL0_OP);
diff --git a/src/drivers/usb/acpi/Makefile.mk b/src/drivers/usb/acpi/Makefile.mk
index e525b466c9..1bf1435955 100644
--- a/src/drivers/usb/acpi/Makefile.mk
+++ b/src/drivers/usb/acpi/Makefile.mk
@@ -1,3 +1,4 @@
## SPDX-License-Identifier: GPL-2.0-only
ramstage-$(CONFIG_DRIVERS_USB_ACPI) += usb_acpi.c
+ramstage-$(CONFIG_DRIVERS_USB_ACPI) += intel_bluetooth.c
diff --git a/src/drivers/usb/acpi/chip.h b/src/drivers/usb/acpi/chip.h
index 9acd382c3d..a27ce4468c 100644
--- a/src/drivers/usb/acpi/chip.h
+++ b/src/drivers/usb/acpi/chip.h
@@ -47,6 +47,10 @@ struct drivers_usb_acpi_config {
/* Does the device have a power resource? */
bool has_power_resource;
+ /* Intel Bluetooth */
+ bool is_intel_bluetooth;
+ bool cnvi_bt_audio_offload;
+
/* GPIO used to take device out of reset or to put it into reset. */
struct acpi_gpio reset_gpio;
/* Delay to be inserted after device is taken out of reset. */
@@ -83,4 +87,10 @@ struct drivers_usb_acpi_config {
/* Method to get PLD structure from USB device */
bool usb_acpi_get_pld(const struct device *usb_device, struct acpi_pld *pld);
+/* Intel Bluetooth */
+void acpi_device_intel_bt(unsigned int reset_gpio,
+ unsigned int enable_gpio,
+ bool audio_offload);
+void acpi_device_intel_bt_common(unsigned int enable_gpio);
+
#endif /* __USB_ACPI_CHIP_H__ */
diff --git a/src/drivers/usb/acpi/intel_bluetooth.c b/src/drivers/usb/acpi/intel_bluetooth.c
new file mode 100644
index 0000000000..90f163673f
--- /dev/null
+++ b/src/drivers/usb/acpi/intel_bluetooth.c
@@ -0,0 +1,338 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpigen.h>
+#include "chip.h"
+
+/*
+ * Intel Bluetooth DSM
+ *
+ * Check/Set Reset Delay (aa10f4e0-81ac-4233-abf6-3b2ac50e28d9)
+ * Arg2 == 0: Return a package with the following bit set
+ * BIT(0) Indicates whether the device supports other functions
+ * BIT(1) Check Bluetooth reset timing
+ *
+ * Arg2 == 1: Set the reset delay based on Arg3
+ *
+ * Arg2 == 3: Set the reset method based on Arg3 (Not supported by this driver)
+ * WDISABLE2 (BT_RF_KILL_N)
+ * VSEC (PCI Config Space)
+ */
+
+static void check_reset_delay(void *arg)
+{
+ acpigen_write_if_lequal_op_int(ARG1_OP, 0);
+ {
+ acpigen_write_return_singleton_buffer(0x03);
+ }
+ acpigen_write_else();
+ {
+ acpigen_write_return_singleton_buffer(0x00);
+ }
+ acpigen_pop_len();
+}
+
+static void set_reset_delay(void *arg)
+{
+ acpigen_write_store_op_to_namestr(ARG3_OP, "RDLY");
+}
+
+static void not_supported(void *arg)
+{
+ acpigen_write_return_singleton_buffer(0x00);
+}
+
+void (*reset_supported[])(void *) = { check_reset_delay, set_reset_delay };
+void (*reset_unsupported[])(void *) = { not_supported };
+
+void acpi_device_intel_bt(unsigned int reset_gpio, unsigned int enable_gpio, bool audio_offload)
+{
+/*
+ * Name (RDLY, 0x69)
+ */
+ acpigen_write_name_integer("RDLY", 0x69);
+
+/*
+ * Method (_DSM, 4, Serialized)
+ * {
+ * If ((Arg0 == ToUUID ("aa10f4e0-81ac-4233-abf6-3b2ac50e28d9")))
+ * {
+ * If ((Arg2 == Zero))
+ * {
+ * If ((Arg1 == Zero))
+ * {
+ * Return (Buffer (One)
+ * {
+ * 0x03
+ * })
+ * }
+ * Else
+ * {
+ * Return (Buffer (One)
+ * {
+ * 0x00
+ * })
+ * }
+ * }
+ * If ((Arg2 == One))
+ * {
+ * RDLY = Arg3
+ * }
+ * Return (Zero)
+ * }
+ * Else
+ * {
+ * Return (Buffer (One)
+ * {
+ * 0x00
+ * })
+ * }
+ * }
+ */
+
+ struct dsm_uuid uuid_callbacks[] = {
+ DSM_UUID("aa10f4e0-81ac-4233-abf6-3b2ac50e28d9",
+ reset_gpio ? reset_supported : reset_unsupported,
+ reset_gpio ? ARRAY_SIZE(reset_supported) : ARRAY_SIZE(reset_unsupported),
+ NULL),
+ };
+
+ acpigen_write_dsm_uuid_arr(uuid_callbacks, ARRAY_SIZE(uuid_callbacks));
+/*
+ * PowerResource (BTRT, 0x05, 0x0000)
+ * {
+ * Method (_STA, 0, NotSerialized)
+ * {
+ * Return (\_SB.PCI0.GBTE())
+ * }
+ * Method (_ON, 0, NotSerialized)
+ * {
+ * \_SB.PCI0.SBTE(1)
+ * }
+ * Method (_OFF, 0, NotSerialized)
+ * {
+ * \_SB.PCI0.SBTE(0)
+ * }
+ * Method (_RST, 0, NotSerialized)
+ * {
+ * Local0 = Acquire (\_SB.PCI0.CNMT, 1000)
+ * If ((Local0 == Zero))
+ * {
+ * BTRK (Zero)
+ * Sleep (RDLY)
+ * BTRK (One)
+ * Sleep (RDLY)
+ * Release (\_SB.PCI0.CNMT)
+ }
+ * }
+ * }
+ */
+ acpigen_write_power_res("BTRT", 5, 0, NULL, 0);
+ {
+ acpigen_write_method("_STA", 0);
+ {
+ if (enable_gpio) {
+ acpigen_write_store();
+ acpigen_emit_namestring("\\_SB.PCI0.GBTE");
+ acpigen_emit_byte(LOCAL0_OP);
+
+ acpigen_write_return_op(LOCAL0_OP);
+ } else {
+ acpigen_write_return_integer(1);
+ }
+ }
+ acpigen_pop_len();
+
+ acpigen_write_method("_ON", 0);
+ {
+ if (enable_gpio) {
+ acpigen_emit_namestring("\\_SB.PCI0.SBTE");
+ acpigen_emit_byte(1);
+ }
+ }
+ acpigen_pop_len();
+
+ acpigen_write_method("_OFF", 0);
+ {
+ if (enable_gpio) {
+ acpigen_emit_namestring("\\_SB.PCI0.SBTE");
+ acpigen_emit_byte(0);
+ }
+ }
+ acpigen_pop_len();
+
+ acpigen_write_method("_RST", 0);
+ {
+ if (reset_gpio) {
+ acpigen_write_store();
+ acpigen_write_acquire("\\_SB.PCI0.CNMT", 1000);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ acpigen_write_if_lequal_op_int(LOCAL0_OP, 0);
+ {
+ acpigen_emit_namestring("BTRK");
+ acpigen_emit_byte(0);
+
+ acpigen_emit_ext_op(SLEEP_OP);
+ acpigen_emit_namestring("RDLY");
+
+ acpigen_emit_namestring("BTRK");
+ acpigen_emit_byte(1);
+
+ acpigen_emit_ext_op(SLEEP_OP);
+ acpigen_emit_namestring("RDLY");
+
+ acpigen_write_release("\\_SB.PCI0.CNMT");
+ }
+ acpigen_pop_len();
+ }
+ }
+ acpigen_pop_len();
+ }
+ acpigen_write_power_res_end();
+
+/*
+ * Method (BTRK, 1, Serialized)
+ * {
+ * If (Arg0 == 1)
+ * {
+ * STXS (reset_gpio)
+ * } Else {
+ * CTXS (reset_gpio)
+ * }
+ * }
+ */
+ acpigen_write_method_serialized("BTRK", 1);
+ {
+ acpigen_write_if_lequal_op_int(ARG0_OP, 1);
+ {
+ acpigen_soc_set_tx_gpio(reset_gpio);
+ }
+ acpigen_write_else();
+ {
+ acpigen_soc_clear_tx_gpio(reset_gpio);
+ }
+ acpigen_pop_len();
+ }
+ acpigen_pop_len();
+
+/*
+ * Name (_PRR, Package (0x01)
+ * {
+ * BTRT
+ * })
+ */
+ acpigen_write_name("_PRR");
+ {
+ acpigen_write_package(1);
+ acpigen_emit_namestring("BTRT");
+ }
+ acpigen_pop_len();
+
+/*
+ * Name (_PR0, Package (0x01)
+ * {
+ * BTRT
+ * })
+ */
+ acpigen_write_name("_PR0");
+ {
+ acpigen_write_package(1);
+ acpigen_emit_namestring("BTRT");
+ }
+ acpigen_pop_len();
+
+/*
+ * Name (_PR3, Package (0x01)
+ * {
+ * BTRT
+ * })
+ */
+ acpigen_write_name("_PR3");
+ {
+ acpigen_write_package(1);
+ acpigen_emit_namestring("BTRT");
+ }
+ acpigen_pop_len();
+
+/*
+ * Method (AOLD, 0, Serialized)
+ * {
+ * Name (AODS, Package (0x03)
+ * {
+ * Zero,
+ * 0x12,
+ * Zero // Audio Offload - 0: Disabled
+ * 1: Enabled
+ * })
+ * Return (AODS)
+ * }
+ */
+ acpigen_write_method_serialized("AOLD", 0);
+ {
+ acpigen_write_name("AODS");
+ acpigen_write_package(3);
+ {
+ acpigen_write_integer(0);
+ acpigen_write_integer(0x12);
+ acpigen_write_integer(audio_offload);
+ }
+ acpigen_pop_len();
+
+ acpigen_write_return_namestr("AODS");
+ }
+ acpigen_pop_len();
+}
+
+void acpi_device_intel_bt_common(unsigned int enable_gpio)
+{
+ acpigen_write_scope("\\_SB.PCI0");
+/*
+ * Mutex (CNMT, 0)
+ */
+ acpigen_write_mutex("CNMT", 0);
+
+/*
+ * Method (SBTE, 1, Serialized)
+ * {
+ * If (Arg0 == 1)
+ * {
+ * STXS(enable_gpio)
+ * } Else {
+ * CTXS(enable_gpio)
+ * }
+ * }
+ */
+ acpigen_write_method_serialized("SBTE", 1);
+ {
+ if (enable_gpio) {
+ acpigen_write_if_lequal_op_int(ARG0_OP, 1);
+ {
+ acpigen_soc_set_tx_gpio(enable_gpio);
+ }
+ acpigen_write_else();
+ {
+ acpigen_soc_clear_tx_gpio(enable_gpio);
+ }
+ acpigen_pop_len();
+ }
+ }
+ acpigen_pop_len();
+
+/*
+ * Method (GBTE, 0, NotSerialized)
+ * {
+ * Return (GTXS (enable_gpio))
+ * }
+ */
+ acpigen_write_method("GBTE", 0);
+ {
+ acpigen_emit_byte(RETURN_OP);
+ if (enable_gpio)
+ acpigen_soc_get_tx_gpio(enable_gpio);
+ else
+ acpigen_emit_byte(0);
+ }
+ acpigen_pop_len();
+
+ acpigen_pop_len();
+}
diff --git a/src/drivers/usb/acpi/usb_acpi.c b/src/drivers/usb/acpi/usb_acpi.c
index 569147abad..8960e3d05b 100644
--- a/src/drivers/usb/acpi/usb_acpi.c
+++ b/src/drivers/usb/acpi/usb_acpi.c
@@ -107,8 +107,20 @@ static void usb_acpi_fill_ssdt_generator(const struct device *dev)
acpi_device_add_power_res(&power_res_params);
}
+ if (config->is_intel_bluetooth)
+ acpi_device_intel_bt(config->reset_gpio.pins[0],
+ config->enable_gpio.pins[0],
+ config->cnvi_bt_audio_offload);
+
acpigen_pop_len();
+ /*
+ * This is generated outside of the USB Device Scope to make it easier for
+ * other code to access it i.e. CNVi driver.
+ */
+ if (config->is_intel_bluetooth)
+ acpi_device_intel_bt_common(config->enable_gpio.pins[0]);
+
printk(BIOS_INFO, "%s: %s at %s\n", path,
config->desc ? : dev->chip_ops->name, dev_path(dev));
}
diff --git a/src/drivers/wifi/generic/acpi.c b/src/drivers/wifi/generic/acpi.c
index d3da51b116..44705535ef 100644
--- a/src/drivers/wifi/generic/acpi.c
+++ b/src/drivers/wifi/generic/acpi.c
@@ -15,8 +15,11 @@
#include "wifi.h"
#include "wifi_private.h"
-/* WIFI Domain type */
-#define DOMAIN_TYPE_WIFI 0x7
+/* Domain type */
+enum sar_domain {
+ DOMAIN_TYPE_WIFI = 0x7,
+ DOMAIN_TYPE_BLUETOOTH = 0x12
+};
/* Maximum number DSM UUID bifurcations in _DSM */
#define MAX_DSM_FUNCS 2
@@ -144,6 +147,93 @@ static void wifi_dsm_unii4_control_enable(void *args)
}
/*
+ * Function 10: Energy Detection Threshold (EDT)
+ * Bits 0-3: EDT revision - Default 0
+ *
+ * Bits 4-5: Reserved - Should be 0
+ *
+ * Bit 6: HB EDT Level. 5 GHz ETSI - EDT Level change - Default 0
+ * 0 - Disable EDT optimization for ETSI HB
+ * 1 - Enable EDT optimization for ETSI HB
+ *
+ * Bits 7-8: Reserved - Should be 0
+ *
+ * Bit 9: UHB EDT Level. 6 GHz FCC - EDT Level change - Default 0
+ * 0 - Disable EDT optimization for FCC UHB
+ * 1 - Enable EDT optimization for FCC UHB
+ *
+ * Bit 10-12: Reserved - Default 0
+ *
+ * Bit 13: EDT_En_HB_5G2/3 - Default 0
+ * 0 - Disable EDT optimization for HB_5G2/3
+ * 1 - Enable EDT optimization for HB_5G2/3
+ *
+ * Bit 14: EDT_En_HB_5G4 - Default 0
+ * 0 - Disable EDT optimization for HB_5G4
+ * 1 - Enable EDT optimization for HB_5G4
+ *
+ * Bit 15: EDT_En_HB_5G6 - Default 0
+ * 0 - Disable EDT optimization for HB_5G6
+ * 1 - Enable EDT optimization for HB_5G6
+ *
+ * Bit 16: EDT_En_HB_5G8/9 - Default 0
+ * 0 - Disable EDT optimization for HB_5G8/9
+ * 1 - Enable EDT optimization for HB_5G8/9
+ *
+ * Bit 17: EDT_En_UHB_6G1 - Default 0
+ * 0 - Disable EDT optimization for UHB_6G1
+ * 1 - Enable EDT optimization for UHB_6G1
+ *
+ * Bit 18: EDT_En_UHB_6G3 - Default 0
+ * 0 - Disable EDT optimization for UHB_6G3
+ * 1 - Enable EDT optimization for UHB_6G3
+ *
+ * Bit 19: EDT_En_UHB_6G5 - Default 0
+ * 0 - Disable EDT optimization for UHB_6G5
+ * 1 - Enable EDT optimization for UHB_6G5
+ *
+ * Bit 20: EDT_En_UHB_6G6 - Default 0
+ * 0 - Disable EDT optimization for UHB_6G6
+ * 1 - Enable EDT optimization for UHB_6G6
+ *
+ * Bit 21: EDT_En_UHB_6G8 - Default 0
+ * 0 - Disable EDT optimization for UHB_6G8
+ * 1 - Enable EDT optimization for UHB_6G8
+ *
+ * Bit 22: EDT_En_UHB_7G0 - Default 0
+ * 0 - Disable EDT optimization for UHB_7G0
+ * 1 - Enable EDT optimization for UHB_7G0
+ *
+ * Bits 23-31: Reserved - Should be 0
+ */
+static void wifi_dsm_energy_detection_threshold(void *args)
+{
+ struct dsm_profile *dsm_config = (struct dsm_profile *)args;
+
+ acpigen_write_return_integer(dsm_config->energy_detection_threshold);
+}
+
+/*
+ * Function 11: RFI mitigation
+ * Bit 0:
+ * 0 - DLVR RFIm enabled (default)
+ * 1 - DLVR RFIm disabled
+ *
+ * Bit 1:
+ * 0 - DDR RFIm enabled (default)
+ * 1 - DDR RFIm disabled
+ *
+ * Bits 2-31: Reserved - Should be 0
+ */
+
+static void wifi_dsm_rfi_mitigation(void *args)
+{
+ struct dsm_profile *dsm_config = (struct dsm_profile *)args;
+
+ acpigen_write_return_integer(dsm_config->rfi_mitigation);
+}
+
+/*
* Function 12: Control Enablement 802.11be on certificated modules
* Bit 0
* 0 - 11BE disabled for China Mainland
@@ -182,8 +272,8 @@ static void (*wifi_dsm_callbacks[])(void *) = {
wifi_dsm_unii4_control_enable, /* Function 7 */
NULL, /* Function 8 */
NULL, /* Function 9 */
- NULL, /* Function 10 */
- NULL, /* Function 11 */
+ wifi_dsm_energy_detection_threshold, /* Function 10 */
+ wifi_dsm_rfi_mitigation, /* Function 11 */
wifi_dsm_11be_country_enablement, /* Function 12 */
};
@@ -489,34 +579,448 @@ static void sar_emit_wtas(struct avg_profile *wtas)
acpigen_write_package_end();
}
-static void emit_sar_acpi_structures(const struct device *dev, struct dsm_profile *dsm)
+static void sar_emit_brds(const struct bsar_profile *bsar)
{
- union wifi_sar_limits sar_limits = {{NULL, NULL, NULL, NULL, NULL} };
+ size_t package_size, table_size;
+ const uint8_t *set;
+
+ if (bsar == NULL)
+ return;
/*
- * If device type is PCI, ensure that the device has Intel vendor ID. CBFS SAR and SAR
- * ACPI tables are currently used only by Intel WiFi devices.
+ * Name ("BRDS", Package () {
+ * Revision,
+ * Package () {
+ * Domain Type, // 0x12:Bluetooth
+ * Bluetooth SAR BIOS, // BIOS SAR Enable/disable
+ * Bluetooth Increase Power Mode // SAR Limitation Enable/disable
+ * Bluetooth SAR Power Restriction, // 00000000 - 0dBm
+ * // 11111111 - 31.875dBm
+ * // (Step 0.125dBm)
+ * Bluetooth SAR Table // SAR Tx power limit table
+ * }
+ * })
*/
- if (dev->path.type == DEVICE_PATH_PCI && dev->vendor != PCI_VID_INTEL)
+ if (bsar->revision == 0 || bsar->revision > MAX_BSAR_REVISION) {
+ printk(BIOS_ERR, "Unsupported BSAR table revision: %d\n",
+ bsar->revision);
return;
+ }
- /* Retrieve the SAR limits data */
- if (get_wifi_sar_limits(&sar_limits) < 0) {
- printk(BIOS_ERR, "failed getting SAR limits!\n");
+ acpigen_write_name("BRDS");
+ acpigen_write_package(2);
+ acpigen_write_dword(bsar->revision);
+
+ if (bsar->revision == 1)
+ table_size = sizeof(bsar->revs.rev1);
+ else
+ table_size = sizeof(bsar->revs.rev2);
+
+ /*
+ * Emit 'Domain Type' + 'Dynamic SAR Enable' + 'Increase Power Mode'
+ * + ('SAR Power Restriction' + SAR table) | (Chain A and B SAR tables).
+ */
+ package_size = 1 + 1 + 1 + table_size;
+ acpigen_write_package(package_size);
+ acpigen_write_dword(DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_dword(1);
+ acpigen_write_dword(bsar->increased_power_mode_limitation);
+
+ set = (const uint8_t *)&bsar->revs;
+ for (int i = 0; i < table_size; i++)
+ acpigen_write_byte(set[i]);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_wbem(const struct wbem_profile *wbem)
+{
+ if (wbem == NULL)
+ return;
+
+ /*
+ * Name ("WBEM", Package() {
+ * {
+ * Revision,
+ * Package()
+ * {
+ * DomainType, // 0x7:WiFi
+ * bandwidth_320mhz_country_enablement // 0 Disabled
+ * // 1 Japan Enabled
+ * // 2 South Korea Enabled
+ * // 3 Japan + South Korea Enabled
+ * }
+ } })
+ */
+ if (wbem->revision != WBEM_REVISION) {
+ printk(BIOS_ERR, "Unsupported WBEM table revision: %d\n",
+ wbem->revision);
+ return;
+ }
+
+ acpigen_write_name("WBEM");
+ acpigen_write_package(2);
+ acpigen_write_dword(wbem->revision);
+
+ acpigen_write_package(2);
+ acpigen_write_dword(DOMAIN_TYPE_WIFI);
+ acpigen_write_dword(wbem->bandwidth_320mhz_country_enablement);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_bpag(const struct bpag_profile *bpag)
+{
+ if (bpag == NULL)
+ return;
+
+ /*
+ * Name ("BPAG", Package () {
+ * Revision,
+ * Package () {
+ * Domain Type, // 0x12:Bluetooth
+ * Bluetooth Per-Platform Antenna Gain Mode
+ * }
+ * })
+ */
+ if (bpag->revision == 0 || bpag->revision > BPAG_REVISION) {
+ printk(BIOS_ERR, "Unsupported BPAG table revision: %d\n",
+ bpag->revision);
+ return;
+ }
+
+ acpigen_write_name("BPAG");
+ acpigen_write_package(2);
+ acpigen_write_dword(bpag->revision);
+
+ /*
+ * Emit 'Domain Type' + 'Antenna Gain Mode'
+ */
+ acpigen_write_package(2);
+ acpigen_write_dword(DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_dword(bpag->antenna_gain_country_enablement);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_bbfb(const struct bbfb_profile *bbfb)
+{
+ if (bbfb == NULL)
+ return;
+
+ /*
+ * Name ("BBFB", Package () {
+ * Revision,
+ * Package () {
+ * Domain Type, // 0x12:Bluetooth
+ * By Pass Enabled // Enable ByPass
+ * }
+ * })
+ */
+ if (bbfb->revision != BBFB_REVISION) {
+ printk(BIOS_ERR, "Unsupported BBFB table revision: %d\n",
+ bbfb->revision);
+ return;
+ }
+
+ acpigen_write_name("BBFB");
+ acpigen_write_package(2);
+ acpigen_write_dword(bbfb->revision);
+
+ /*
+ * Emit 'Domain Type' + 'Enable ByPass'
+ */
+ acpigen_write_package(2);
+ acpigen_write_dword(DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_dword(bbfb->enable_quad_filter_bypass);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_bdcm(const struct bdcm_profile *bdcm)
+{
+ if (bdcm == NULL)
+ return;
+
+ /*
+ * Name ("BDCM", Package () {
+ * Revision,
+ * Package () {
+ * Domain Type, // 0x12:Bluetooth
+ * Dual Chain Mode
+ * }
+ * })
+ */
+ if (bdcm->revision != BDCM_REVISION) {
+ printk(BIOS_ERR, "Unsupported BDCM table revision: %d\n",
+ bdcm->revision);
+ return;
+ }
+
+ acpigen_write_name("BDCM");
+ acpigen_write_package(2);
+ acpigen_write_dword(bdcm->revision);
+
+ /*
+ * Emit 'Domain Type' + 'Dual Chain Mode'
+ */
+ acpigen_write_package(2);
+ acpigen_write_dword(DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_dword(bdcm->dual_chain_mode);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_bbsm(const struct bbsm_profile *bbsm)
+{
+ if (bbsm == NULL)
+ return;
+
+ /*
+ * Name ("BBSM", Package () {
+ * Revision,
+ * Package () {
+ * Domain Type, // 0x12:Bluetooth
+ * Bluetooth Bands Selection
+ * }
+ * })
+ */
+ if (bbsm->revision != BBSM_REVISION) {
+ printk(BIOS_ERR, "Unsupported BBSM table revision: %d\n",
+ bbsm->revision);
+ return;
+ }
+
+ acpigen_write_name("BBSM");
+ acpigen_write_package(2);
+ acpigen_write_dword(bbsm->revision);
+
+ /*
+ * Emit 'Domain Type' + 'Bluetooth Bands Selection'
+ */
+ acpigen_write_package(2);
+ acpigen_write_dword(DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_dword(bbsm->bands_selection);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_bucs(const struct bucs_profile *bucs)
+{
+ if (bucs == NULL)
+ return;
+
+ /*
+ * Name ("BUCS", Package () {
+ * Revision,
+ * Package () {
+ * Domain Type, // 0x12:Bluetooth
+ * UHB country selection bits
+ * }
+ * })
+ */
+ if (bucs->revision != BUCS_REVISION) {
+ printk(BIOS_ERR, "Unsupported BUCS table revision: %d\n",
+ bucs->revision);
+ return;
+ }
+
+ acpigen_write_name("BUCS");
+ acpigen_write_package(2);
+ acpigen_write_dword(bucs->revision);
+
+ /*
+ * Emit 'Domain Type' + 'UHB country selection bits'
+ */
+ acpigen_write_package(2);
+ acpigen_write_dword(DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_dword(bucs->uhb_country_selection);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_bdmm(const struct bdmm_profile *bdmm)
+{
+ if (bdmm == NULL)
+ return;
+
+ /*
+ * Name ("BDMM", Package () {
+ * Revision,
+ * Package () {
+ * Domain Type, // 0x12:Bluetooth
+ * Dual Mac Enable
+ * }
+ * })
+ */
+ if (bdmm->revision != BDMM_REVISION) {
+ printk(BIOS_ERR, "Unsupported BDMM table revision: %d\n",
+ bdmm->revision);
+ return;
+ }
+
+ acpigen_write_name("BDMM");
+ acpigen_write_package(2);
+ acpigen_write_dword(bdmm->revision);
+
+ /*
+ * Emit 'Domain Type' + 'Dual Mac Enable'
+ */
+ acpigen_write_package(2);
+ acpigen_write_dword(DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_dword(bdmm->dual_mac_enable);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_ebrd(const struct ebrd_profile *ebrd)
+{
+ if (ebrd == NULL)
+ return;
+
+ size_t package_size, table_size;
+ const uint8_t *set;
+
+ /*
+ * Name ("EBRD", Package () {
+ * Revision,
+ * Package () {
+ * Domain Type, // 0x12:Bluetooth
+ * Dynamic SAR Enable,
+ * Number of Optional SAR,
+ * Three SAR Sets
+ * }
+ * })
+ */
+ if (ebrd->revision != EBRD_REVISION) {
+ printk(BIOS_ERR, "Unsupported EBRD table revision: %d\n",
+ ebrd->revision);
+ return;
+ }
+
+ acpigen_write_name("EBRD");
+ acpigen_write_package(2);
+ acpigen_write_dword(ebrd->revision);
+
+ /*
+ * Emit 'Domain Type' + 'Dynamic Sar Enabled' + 'Number of Optional SAR' +
+ * 'Three SAR Sets'
+ */
+ table_size = sizeof(*ebrd) - offsetof(struct ebrd_profile, sar_table_sets);
+ package_size = 1 + 1 + 1 + table_size;
+ acpigen_write_package(package_size);
+ acpigen_write_dword(DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_dword(ebrd->dynamic_sar_enable);
+ acpigen_write_dword(ebrd->number_of_optional_sar);
+
+ set = (const uint8_t *)&ebrd->sar_table_sets;
+ for (size_t i = 0; i < table_size; i++)
+ acpigen_write_byte(set[i]);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_wpfc(const struct wpfc_profile *wpfc)
+{
+ if (wpfc == NULL)
+ return;
+
+ /*
+ * Name ("WPFC", Package() {
+ * {
+ * Revision,
+ * Package()
+ * {
+ * DomainType, // 0x7:WiFi
+ * Chain A Filter Platform Configuration
+ * Chain B Filter Platform Configuration
+ * Chain C Filter Platform Configuration
+ * Chain D Filter Platform Configuration
+ * }
+ } })
+ */
+ if (wpfc->revision != WPFC_REVISION) {
+ printk(BIOS_ERR, "Unsupported WPFC table revision: %d\n",
+ wpfc->revision);
return;
}
- sar_emit_wrds(sar_limits.sar);
- sar_emit_ewrd(sar_limits.sar);
- sar_emit_wgds(sar_limits.wgds);
- sar_emit_ppag(sar_limits.ppag);
- sar_emit_wtas(sar_limits.wtas);
+ acpigen_write_name("WPFC");
+ acpigen_write_package(2);
+ acpigen_write_dword(wpfc->revision);
- /* copy the dsm data to be later used for creating _DSM function */
- if (sar_limits.dsm != NULL)
- memcpy(dsm, sar_limits.dsm, sizeof(struct dsm_profile));
+ acpigen_write_package(5);
+ acpigen_write_dword(DOMAIN_TYPE_WIFI);
+ acpigen_write_byte(wpfc->filter_cfg_chain_a);
+ acpigen_write_byte(wpfc->filter_cfg_chain_b);
+ acpigen_write_byte(wpfc->filter_cfg_chain_c);
+ acpigen_write_byte(wpfc->filter_cfg_chain_d);
- free(sar_limits.sar);
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void sar_emit_dsbr(const struct dsbr_profile *dsbr, enum sar_domain domain)
+{
+ if (dsbr == NULL)
+ return;
+
+ /*
+ * Name ("DSBR", Package() {
+ * {
+ * Revision,
+ * Package()
+ * {
+ * DomainType, // 0x7:WiFi
+ * BRI Resistor Value // in Ohm
+ * }
+ } })
+ */
+ if (dsbr->revision != DSBR_REVISION) {
+ printk(BIOS_ERR, "Unsupported DSBR table revision: %d\n",
+ dsbr->revision);
+ return;
+ }
+
+ acpigen_write_name("DSBR");
+ acpigen_write_package(2);
+ acpigen_write_dword(dsbr->revision);
+
+ acpigen_write_package(2);
+ acpigen_write_dword(domain);
+ acpigen_write_dword(dsbr->bri_resistor_value);
+
+ acpigen_write_package_end();
+ acpigen_write_package_end();
+}
+
+static void emit_wifi_sar_acpi_structures(const struct device *dev,
+ union wifi_sar_limits *sar_limits)
+{
+ /*
+ * If device type is PCI, ensure that the device has Intel vendor ID. CBFS SAR and SAR
+ * ACPI tables are currently used only by Intel WiFi devices.
+ */
+ if (dev->path.type == DEVICE_PATH_PCI && dev->vendor != PCI_VID_INTEL)
+ return;
+
+ sar_emit_wrds(sar_limits->sar);
+ sar_emit_ewrd(sar_limits->sar);
+ sar_emit_wgds(sar_limits->wgds);
+ sar_emit_ppag(sar_limits->ppag);
+ sar_emit_wtas(sar_limits->wtas);
+ sar_emit_wbem(sar_limits->wbem);
+ sar_emit_wpfc(sar_limits->wpfc);
+ sar_emit_dsbr(sar_limits->dsbr, DOMAIN_TYPE_WIFI);
}
static void wifi_ssdt_write_device(const struct device *dev, const char *path)
@@ -574,23 +1078,32 @@ static void wifi_ssdt_write_properties(const struct device *dev, const char *sco
}
struct dsm_uuid dsm_ids[MAX_DSM_FUNCS];
- /* We will need a copy dsm data to be used later for creating _DSM function */
- struct dsm_profile dsm = {0};
- uint8_t dsm_count = 0;
- /* Fill Wifi SAR related ACPI structures */
+ /* Retrieve the SAR limits data */
+ union wifi_sar_limits sar_limits = {0};
+ bool sar_loaded = false;
if (CONFIG(USE_SAR)) {
- emit_sar_acpi_structures(dev, &dsm);
+ if (get_wifi_sar_limits(&sar_limits) < 0)
+ printk(BIOS_ERR, "failed getting SAR limits!\n");
+ else
+ sar_loaded = true;
+ }
+
+ /* Fill Wifi SAR related ACPI structures */
+ uint8_t dsm_count = 0;
+ if (sar_loaded) {
+ emit_wifi_sar_acpi_structures(dev, &sar_limits);
- if (dsm.supported_functions != 0) {
+ struct dsm_profile *dsm = sar_limits.dsm;
+ if (dsm && dsm->supported_functions != 0) {
for (int i = 1; i < ARRAY_SIZE(wifi_dsm_callbacks); i++)
- if (!(dsm.supported_functions & (1 << i)))
+ if (!(dsm->supported_functions & (1 << i)))
wifi_dsm_callbacks[i] = NULL;
dsm_ids[dsm_count].uuid = ACPI_DSM_OEM_WIFI_UUID;
dsm_ids[dsm_count].callbacks = &wifi_dsm_callbacks[0];
dsm_ids[dsm_count].count = ARRAY_SIZE(wifi_dsm_callbacks);
- dsm_ids[dsm_count].arg = &dsm;
+ dsm_ids[dsm_count].arg = dsm;
dsm_count++;
}
}
@@ -618,6 +1131,30 @@ static void wifi_ssdt_write_properties(const struct device *dev, const char *sco
acpigen_write_scope_end(); /* Scope */
+ /* Fill Bluetooth companion SAR related ACPI structures */
+ if (sar_loaded && is_dev_enabled(config->bluetooth_companion)) {
+ const char *path = acpi_device_path(config->bluetooth_companion);
+ if (path) { /* Bluetooth device under USB Hub scope or PCIe root port */
+ acpigen_write_scope(path);
+ sar_emit_brds(sar_limits.bsar);
+ sar_emit_bpag(sar_limits.bpag);
+ sar_emit_bbfb(sar_limits.bbfb);
+ sar_emit_bdcm(sar_limits.bdcm);
+ sar_emit_bbsm(sar_limits.bbsm);
+ sar_emit_bucs(sar_limits.bucs);
+ sar_emit_bdmm(sar_limits.bdmm);
+ sar_emit_ebrd(sar_limits.ebrd);
+ sar_emit_dsbr(sar_limits.dsbr, DOMAIN_TYPE_BLUETOOTH);
+ acpigen_write_scope_end();
+ } else {
+ printk(BIOS_ERR, "Failed to get %s Bluetooth companion ACPI path\n",
+ dev_path(dev));
+ }
+ }
+
+ if (sar_loaded)
+ free(sar_limits.sar);
+
printk(BIOS_INFO, "%s: %s %s\n", scope, dev->chip_ops ? dev->chip_ops->name : "",
dev_path(dev));
}
diff --git a/src/drivers/wifi/generic/chip.h b/src/drivers/wifi/generic/chip.h
index 2729a35663..302dd144ef 100644
--- a/src/drivers/wifi/generic/chip.h
+++ b/src/drivers/wifi/generic/chip.h
@@ -19,6 +19,9 @@ struct drivers_wifi_generic_config {
* SoC code propagates this value the applicable FSP UPD.
*/
bool enable_cnvi_ddr_rfim;
+
+ /* Pointer to the Bluetooth companion device */
+ DEVTREE_CONST struct device *bluetooth_companion;
};
#endif /* _GENERIC_WIFI_H_ */