File: hwquirks.c

package info (click to toggle)
memtest86%2B 7.20-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,772 kB
  • sloc: ansic: 22,575; asm: 2,497; makefile: 589; sh: 408
file content (229 lines) | stat: -rw-r--r-- 8,554 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// 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"
#include "memrw.h"
#include "vmem.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.model < 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;
}

static void loongson_7a00_ehci_workaround(void)
{
    uintptr_t reg_addr = 0x10010000;

#if (ARCH_BITS == 64)
    reg_addr |= 0xEULL << 40;
#endif
    reg_addr = map_region(reg_addr, 0x0, false);
    write8((uint8_t *)(reg_addr + 0x3820), 0xFF);
    write8((uint8_t *)(reg_addr + 0x3830), 0xFF);
    write32((uint32_t *)(reg_addr + 0x3100), 0xFFFFFFFF);
    write32((uint32_t *)(reg_addr + 0x3180), 0xFFFFFFFF);
    write8((uint8_t *)(reg_addr + 0x3820), 0x0);
    write8((uint8_t *)(reg_addr + 0x3830), 0x0);
}

// ---------------------
// -- 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;
            }
        }
    }

    //  -----------------------------------------------------------
    //  -- Loongson 7A1000 and 7A2000 chipset USB 2.0 workaround --
    //  -----------------------------------------------------------
    if (quirk.root_vid == PCI_VID_LOONGSON && quirk.root_did == 0x7a00) {
        quirk.id    = QUIRK_LOONGSON7A00_EHCI_WORKARD;
        quirk.type |= QUIRK_TYPE_USB;
        quirk.process = loongson_7a00_ehci_workaround;
    }
}