diff options
Diffstat (limited to 'src/drivers')
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, ¶m_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, ¶m_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_ */ |