// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2004-2023 Sam Demeulemeester // // ------------------------ // This file is used to detect quirks on specific hardware // that require proprietary init here *OR* different code path // later in various part of the code. // // Please add a quick comment for every quirk added to the list. #include "hwquirks.h" #include "io.h" #include "pci.h" #include "unistd.h" #include "cpuinfo.h" #include "cpuid.h" #include "config.h" #include "temperature.h" quirk_t quirk; // -------------------------------------- // -- Private quirk-specific functions -- // -------------------------------------- static void asus_tusl2_configure_mux(void) { uint8_t muxreg; // Enter ASB100 Config Mode outb(0x87, 0x2E); outb(0x87, 0x2E); usleep(200); // Write LPC Command to access Config Mode Reg lpc_outb(0x7, 0x8); // Read Config Mode Register muxreg = lpc_inb(0xF1); // Change Smbus Mux Channel & Write Config Mode Register muxreg &= 0xE7; muxreg |= 0x10; lpc_outb(0xF1, muxreg); usleep(200); // Leave Config Mode outb(0xAA, 0x2E); } static void get_m1541_l2_cache_size(void) { if (l2_cache != 0) { return; } // Check if L2 cache is enabled with L2CC-2 Register[0] if ((pci_config_read8(0, 0, 0, 0x42) & 1) == 0) { return; } // Get L2 Cache Size with L2CC-1 Register[3:2] uint8_t reg = (pci_config_read8(0, 0, 0, 0x41) >> 2) & 3; if (reg == 0b00) { l2_cache = 256; } if (reg == 0b01) { l2_cache = 512; } if (reg == 0b10) { l2_cache = 1024; } } static void disable_temp_reporting(void) { enable_temperature = false; } static void amd_k8_revfg_temp(void) { uint32_t rtcr = pci_config_read32(0, 24, 3, AMD_TEMP_REG_K8); // For Rev F & G, switch sensor if no temperature is reported if (!((rtcr >> 16) & 0xFF)) { pci_config_write8(0, 24, 3, AMD_TEMP_REG_K8, rtcr | 0x04); } // K8 Rev G Desktop requires an additional offset. if (cpuid_info.version.extendedModel < 6 && cpuid_info.version.extendedModel > 7) // Not Rev G return; if (cpuid_info.version.extendedModel == 6 && cpuid_info.version.extendedModel < 9) // Not Desktop return; uint16_t brandID = (cpuid_info.version.extendedBrandID >> 9) & 0x1f; if (cpuid_info.version.model == 0xF && (brandID == 0x7 || brandID == 0x9 || brandID == 0xC)) // Mobile (Single Core) return; if (cpuid_info.version.model == 0xB && brandID > 0xB) // Mobile (Dual Core) return; cpu_temp_offset = 21.0f; } // --------------------- // -- Public function -- // --------------------- void quirks_init(void) { quirk.id = QUIRK_NONE; quirk.type = QUIRK_TYPE_NONE; quirk.root_vid = pci_config_read16(0, 0, 0, PCI_VID_REG); quirk.root_did = pci_config_read16(0, 0, 0, PCI_DID_REG); quirk.process = NULL; // ------------------------- // -- ALi Aladdin V Quirk -- // ------------------------- // As on many Socket 7 Motherboards, the L2 cache is external and must // be detected by a proprietary way based on chipset registers if (quirk.root_vid == PCI_VID_ALI && quirk.root_did == 0x1541) { // ALi Aladdin V (M1541) quirk.id = QUIRK_ALI_ALADDIN_V; quirk.type |= QUIRK_TYPE_MEM_SIZE; quirk.process = get_m1541_l2_cache_size; } // ------------------------ // -- ASUS TUSL2-C Quirk -- // ------------------------ // This motherboard has an ASB100 ASIC with a SMBUS Mux Integrated. // To access SPD later in the code, we need to configure the mux. // PS: Detection via DMI is unreliable, so using Root PCI Registers if (quirk.root_vid == PCI_VID_INTEL && quirk.root_did == 0x1130) { // Intel i815 if (pci_config_read16(0, 0, 0, PCI_SUB_VID_REG) == PCI_VID_ASUS) { // ASUS if (pci_config_read16(0, 0, 0, PCI_SUB_DID_REG) == 0x8027) { // TUSL2-C quirk.id = QUIRK_TUSL2; quirk.type |= QUIRK_TYPE_SMBUS; quirk.process = asus_tusl2_configure_mux; } } } // ------------------------------------------------- // -- SuperMicro X10SDV Quirk (GitHub Issue #233) -- // ------------------------------------------------- // Memtest86+ crashs on Super Micro X10SDV motherboard with SMP Enabled // We were unable to find a solution so far, so disable SMP by default if (quirk.root_vid == PCI_VID_INTEL && quirk.root_did == 0x6F00) { // Broadwell-E (Xeon-D) if (pci_config_read16(0, 0, 0, PCI_SUB_VID_REG) == PCI_VID_SUPERMICRO) { // Super Micro quirk.id = QUIRK_X10SDV_NOSMP; quirk.type |= QUIRK_TYPE_SMP; quirk.process = NULL; } } // ------------------------------------------------------ // -- Early AMD K8 doesn't support temperature reading -- // ------------------------------------------------------ // The on-die temperature diode on SH-B0/B3 stepping does not work. if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF && cpuid_info.version.extendedFamily == 0 && cpuid_info.version.extendedModel == 0) { // Early K8 if ((cpuid_info.version.model == 4 && cpuid_info.version.stepping == 0) || // SH-B0 ClawHammer (Athlon 64) (cpuid_info.version.model == 5 && cpuid_info.version.stepping <= 1)) { // SH-B0/B3 SledgeHammer (Opteron) quirk.id = QUIRK_K8_BSTEP_NOTEMP; quirk.type |= QUIRK_TYPE_TEMP; quirk.process = disable_temp_reporting; } } // --------------------------------------------------- // -- Late AMD K8 (rev F/G) temp sensor workaround -- // --------------------------------------------------- if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF && cpuid_info.version.extendedFamily == 0 && cpuid_info.version.extendedModel >= 4) { // Later K8 quirk.id = QUIRK_K8_REVFG_TEMP; quirk.type |= QUIRK_TYPE_TEMP; quirk.process = amd_k8_revfg_temp; } // ------------------------------------------------ // -- AMD K10 CPUs Temp workaround (Errata #319) -- // ------------------------------------------------ // Some AMD K10 CPUs on Socket AM2+/F have buggued thermal diode leading // to inaccurate temperature measurements. Affected steppings: DR-BA/B2/B3, RB-C2 & HY-D0. if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF && cpuid_info.version.extendedFamily == 1 && cpuid_info.version.extendedModel == 0) { // AMD K10 uint8_t pkg_type = (cpuid_info.version.extendedBrandID >> 28) & 0x0F; uint32_t dct0_high = pci_config_read32(0, 24, 2, 0x94); // 0x94[8] = 1 for DDR3 if (pkg_type == 0b0000 || (pkg_type == 0b0001 && (((dct0_high >> 8) & 1) == 0))) { // Socket F or AM2+ (exclude AM3) if (cpuid_info.version.model < 4 || // DR-BA, DR-B2 & DR-B3 (cpuid_info.version.model == 4 && cpuid_info.version.stepping <= 2) || // RB-C2 cpuid_info.version.model == 8) { // HY-D0 quirk.id = QUIRK_AMD_ERRATA_319; quirk.type |= QUIRK_TYPE_TEMP; quirk.process = disable_temp_reporting; } } } }