Angel Pons | 182dbde | 2020-04-02 23:49:05 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 2 | |
| 3 | #include <types.h> |
| 4 | #include <arch/io.h> |
Kyösti Mälkki | f1b58b7 | 2019-03-01 13:43:02 +0200 | [diff] [blame] | 5 | #include <device/pci_ops.h> |
Furquan Shaikh | 76cedd2 | 2020-05-02 10:24:23 -0700 | [diff] [blame] | 6 | #include <acpi/acpi.h> |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 7 | #include <console/console.h> |
| 8 | #include <cpu/x86/cache.h> |
| 9 | #include <device/pci_def.h> |
| 10 | #include <cpu/x86/smm.h> |
Kyösti Mälkki | e31ec29 | 2019-08-10 17:27:01 +0300 | [diff] [blame] | 11 | #include <cpu/intel/em64t101_save_state.h> |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 12 | #include <elog.h> |
| 13 | #include <halt.h> |
Kyösti Mälkki | cbf9571 | 2020-01-05 08:05:45 +0200 | [diff] [blame] | 14 | #include <option.h> |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 15 | #include <southbridge/intel/common/pmbase.h> |
Arthur Heymans | 4c80425 | 2018-12-03 01:28:18 +0100 | [diff] [blame] | 16 | #include <smmstore.h> |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 17 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 18 | #include "pmutil.h" |
| 19 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 20 | u16 get_pmbase(void) |
| 21 | { |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 22 | return lpc_get_pmbase(); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 23 | } |
| 24 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 25 | void gpi_route_interrupt(u8 gpi, u8 mode) |
| 26 | { |
| 27 | u32 gpi_rout; |
| 28 | if (gpi >= 16) |
| 29 | return; |
| 30 | |
| 31 | alt_gpi_mask(1 << gpi, 0); |
| 32 | gpe0_mask(1 << (gpi+16), 0); |
| 33 | |
| 34 | gpi_rout = pci_read_config32(PCI_DEV(0, 0x1f, 0), D31F0_GPIO_ROUT); |
| 35 | gpi_rout &= ~(3 << (2 * gpi)); |
| 36 | gpi_rout |= ((mode & 3) << (2 * gpi)); |
| 37 | pci_write_config32(PCI_DEV(0, 0x1f, 0), D31F0_GPIO_ROUT, gpi_rout); |
| 38 | |
| 39 | if (mode == GPI_IS_SCI) |
| 40 | gpe0_mask(0, 1 << (gpi+16)); |
| 41 | else if (mode == GPI_IS_SMI) |
| 42 | alt_gpi_mask(0, 1 << gpi); |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * @brief Set the EOS bit |
| 47 | */ |
| 48 | void southbridge_smi_set_eos(void) |
| 49 | { |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 50 | write_pmbase8(SMI_EN, read_pmbase8(SMI_EN) | EOS); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 51 | } |
| 52 | |
| 53 | static void busmaster_disable_on_bus(int bus) |
| 54 | { |
| 55 | int slot, func; |
| 56 | unsigned int val; |
| 57 | unsigned char hdr; |
| 58 | |
| 59 | for (slot = 0; slot < 0x20; slot++) { |
| 60 | for (func = 0; func < 8; func++) { |
Elyes HAOUAS | 804a340 | 2020-04-27 05:25:06 +0200 | [diff] [blame] | 61 | u16 reg16; |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 62 | pci_devfn_t dev = PCI_DEV(bus, slot, func); |
| 63 | |
| 64 | val = pci_read_config32(dev, PCI_VENDOR_ID); |
| 65 | |
| 66 | if (val == 0xffffffff || val == 0x00000000 || |
| 67 | val == 0x0000ffff || val == 0xffff0000) |
| 68 | continue; |
| 69 | |
| 70 | /* Disable Bus Mastering for this one device */ |
Elyes HAOUAS | 804a340 | 2020-04-27 05:25:06 +0200 | [diff] [blame] | 71 | reg16 = pci_read_config16(dev, PCI_COMMAND); |
| 72 | reg16 &= ~PCI_COMMAND_MASTER; |
| 73 | pci_write_config16(dev, PCI_COMMAND, reg16); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 74 | |
| 75 | /* If this is a bridge, then follow it. */ |
| 76 | hdr = pci_read_config8(dev, PCI_HEADER_TYPE); |
| 77 | hdr &= 0x7f; |
| 78 | if (hdr == PCI_HEADER_TYPE_BRIDGE || |
| 79 | hdr == PCI_HEADER_TYPE_CARDBUS) { |
| 80 | unsigned int buses; |
| 81 | buses = pci_read_config32(dev, PCI_PRIMARY_BUS); |
| 82 | busmaster_disable_on_bus((buses >> 8) & 0xff); |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | |
Aaron Durbin | 6403167 | 2018-04-21 14:45:32 -0600 | [diff] [blame] | 88 | __weak void southbridge_gate_memory_reset(void) |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 89 | { |
| 90 | } |
| 91 | |
Aaron Durbin | 6403167 | 2018-04-21 14:45:32 -0600 | [diff] [blame] | 92 | __weak void southbridge_smm_xhci_sleep(u8 slp_type) |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 93 | { |
| 94 | } |
| 95 | |
Kyösti Mälkki | 3c18186 | 2021-01-08 19:01:30 +0200 | [diff] [blame] | 96 | static int power_on_after_fail(void) |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 97 | { |
Kyösti Mälkki | 3c18186 | 2021-01-08 19:01:30 +0200 | [diff] [blame] | 98 | /* save and recover RTC port values */ |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 99 | u8 tmp70, tmp72; |
| 100 | tmp70 = inb(0x70); |
| 101 | tmp72 = inb(0x72); |
Angel Pons | 62719a3 | 2021-04-19 13:15:28 +0200 | [diff] [blame] | 102 | const int s5pwr = get_int_option("power_on_after_fail", |
| 103 | CONFIG_MAINBOARD_POWER_FAILURE_STATE); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 104 | outb(tmp70, 0x70); |
| 105 | outb(tmp72, 0x72); |
| 106 | |
Kyösti Mälkki | 3c18186 | 2021-01-08 19:01:30 +0200 | [diff] [blame] | 107 | /* For "KEEP", switch to "OFF" - KEEP is software emulated. */ |
| 108 | return (s5pwr == MAINBOARD_POWER_ON); |
| 109 | } |
| 110 | |
| 111 | static void southbridge_smi_sleep(void) |
| 112 | { |
| 113 | u32 reg32; |
| 114 | u8 slp_typ; |
| 115 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 116 | /* First, disable further SMIs */ |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 117 | write_pmbase8(SMI_EN, read_pmbase8(SMI_EN) & ~SLP_SMI_EN); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 118 | |
| 119 | /* Figure out SLP_TYP */ |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 120 | reg32 = read_pmbase32(PM1_CNT); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 121 | slp_typ = acpi_sleep_from_pm1(reg32); |
| 122 | |
Kyösti Mälkki | c1d524b | 2021-01-08 16:41:02 +0200 | [diff] [blame] | 123 | printk(BIOS_SPEW, "SMI#: SLP = 0x%08x, TYPE = 0x%02x\n", reg32, slp_typ); |
| 124 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 125 | southbridge_smm_xhci_sleep(slp_typ); |
| 126 | |
| 127 | /* Do any mainboard sleep handling */ |
| 128 | mainboard_smi_sleep(slp_typ); |
| 129 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 130 | /* Log S3, S4, and S5 entry */ |
| 131 | if (slp_typ >= ACPI_S3) |
Kyösti Mälkki | 9dd1a12 | 2019-11-06 11:04:27 +0200 | [diff] [blame] | 132 | elog_gsmi_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 133 | |
| 134 | /* Next, do the deed. |
| 135 | */ |
| 136 | |
| 137 | switch (slp_typ) { |
| 138 | case ACPI_S0: |
| 139 | printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n"); |
| 140 | break; |
| 141 | case ACPI_S1: |
| 142 | printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n"); |
| 143 | break; |
| 144 | case ACPI_S3: |
| 145 | printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n"); |
| 146 | |
| 147 | /* Gate memory reset */ |
| 148 | southbridge_gate_memory_reset(); |
| 149 | |
| 150 | /* Invalidate the cache before going to S3 */ |
| 151 | wbinvd(); |
| 152 | break; |
| 153 | case ACPI_S4: |
| 154 | printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n"); |
| 155 | break; |
| 156 | case ACPI_S5: |
| 157 | printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n"); |
| 158 | |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 159 | write_pmbase32(GPE0_EN, 0); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 160 | |
Kyösti Mälkki | 3c18186 | 2021-01-08 19:01:30 +0200 | [diff] [blame] | 161 | /* Always set the flag in case CMOS was changed on runtime. */ |
| 162 | if (power_on_after_fail()) |
| 163 | pci_and_config8(PCI_DEV(0, 0x1f, 0), D31F0_GEN_PMCON_3, ~1); |
| 164 | else |
| 165 | pci_or_config8(PCI_DEV(0, 0x1f, 0), D31F0_GEN_PMCON_3, 1); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 166 | |
| 167 | /* also iterates over all bridges on bus 0 */ |
| 168 | busmaster_disable_on_bus(0); |
| 169 | break; |
| 170 | default: printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); break; |
| 171 | } |
| 172 | |
| 173 | /* Write back to the SLP register to cause the originally intended |
| 174 | * event again. We need to set BIT13 (SLP_EN) though to make the |
| 175 | * sleep happen. |
| 176 | */ |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 177 | write_pmbase32(PM1_CNT, reg32 | SLP_EN); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 178 | |
| 179 | /* Make sure to stop executing code here for S3/S4/S5 */ |
| 180 | if (slp_typ >= ACPI_S3) |
| 181 | halt(); |
| 182 | |
| 183 | /* In most sleep states, the code flow of this function ends at |
| 184 | * the line above. However, if we entered sleep state S1 and wake |
| 185 | * up again, we will continue to execute code in this function. |
| 186 | */ |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 187 | reg32 = read_pmbase32(PM1_CNT); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 188 | if (reg32 & SCI_EN) { |
| 189 | /* The OS is not an ACPI OS, so we set the state to S0 */ |
| 190 | reg32 &= ~(SLP_EN | SLP_TYP); |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 191 | write_pmbase32(PM1_CNT, reg32); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 192 | } |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | * Look for Synchronous IO SMI and use save state from that |
| 197 | * core in case we are not running on the same core that |
| 198 | * initiated the IO transaction. |
| 199 | */ |
Kyösti Mälkki | 8c2cc68 | 2020-06-29 05:57:12 +0300 | [diff] [blame] | 200 | static em64t101_smm_state_save_area_t *smi_apmc_find_state_save(u8 cmd) |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 201 | { |
| 202 | em64t101_smm_state_save_area_t *state; |
| 203 | int node; |
| 204 | |
| 205 | /* Check all nodes looking for the one that issued the IO */ |
| 206 | for (node = 0; node < CONFIG_MAX_CPUS; node++) { |
| 207 | state = smm_get_save_state(node); |
| 208 | |
Elyes HAOUAS | 581fe58 | 2018-04-26 09:57:07 +0200 | [diff] [blame] | 209 | /* Check for Synchronous IO (bit0 == 1) */ |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 210 | if (!(state->io_misc_info & (1 << 0))) |
| 211 | continue; |
| 212 | |
Elyes HAOUAS | 581fe58 | 2018-04-26 09:57:07 +0200 | [diff] [blame] | 213 | /* Make sure it was a write (bit4 == 0) */ |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 214 | if (state->io_misc_info & (1 << 4)) |
| 215 | continue; |
| 216 | |
| 217 | /* Check for APMC IO port */ |
| 218 | if (((state->io_misc_info >> 16) & 0xff) != APM_CNT) |
| 219 | continue; |
| 220 | |
| 221 | /* Check AX against the requested command */ |
| 222 | if ((state->rax & 0xff) != cmd) |
| 223 | continue; |
| 224 | |
| 225 | return state; |
| 226 | } |
| 227 | |
| 228 | return NULL; |
| 229 | } |
| 230 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 231 | static void southbridge_smi_gsmi(void) |
| 232 | { |
| 233 | u32 *ret, *param; |
| 234 | u8 sub_command; |
| 235 | em64t101_smm_state_save_area_t *io_smi = |
Patrick Georgi | d61839c | 2018-12-03 16:10:33 +0100 | [diff] [blame] | 236 | smi_apmc_find_state_save(APM_CNT_ELOG_GSMI); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 237 | |
| 238 | if (!io_smi) |
| 239 | return; |
| 240 | |
| 241 | /* Command and return value in EAX */ |
| 242 | ret = (u32*)&io_smi->rax; |
| 243 | sub_command = (u8)(*ret >> 8); |
| 244 | |
| 245 | /* Parameter buffer in EBX */ |
| 246 | param = (u32*)&io_smi->rbx; |
| 247 | |
| 248 | /* drivers/elog/gsmi.c */ |
| 249 | *ret = gsmi_exec(sub_command, param); |
| 250 | } |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 251 | |
Arthur Heymans | 4c80425 | 2018-12-03 01:28:18 +0100 | [diff] [blame] | 252 | static void southbridge_smi_store(void) |
| 253 | { |
| 254 | u8 sub_command, ret; |
| 255 | em64t101_smm_state_save_area_t *io_smi = |
| 256 | smi_apmc_find_state_save(APM_CNT_SMMSTORE); |
Patrick Rudolph | 03cfae4 | 2019-10-20 18:09:58 +0200 | [diff] [blame] | 257 | uintptr_t reg_rbx; |
Arthur Heymans | 4c80425 | 2018-12-03 01:28:18 +0100 | [diff] [blame] | 258 | |
| 259 | if (!io_smi) |
| 260 | return; |
| 261 | /* Command and return value in EAX */ |
| 262 | sub_command = (io_smi->rax >> 8) & 0xff; |
| 263 | |
| 264 | /* Parameter buffer in EBX */ |
Patrick Rudolph | 03cfae4 | 2019-10-20 18:09:58 +0200 | [diff] [blame] | 265 | reg_rbx = (uintptr_t)io_smi->rbx; |
Arthur Heymans | 4c80425 | 2018-12-03 01:28:18 +0100 | [diff] [blame] | 266 | |
| 267 | /* drivers/smmstore/smi.c */ |
Patrick Rudolph | 03cfae4 | 2019-10-20 18:09:58 +0200 | [diff] [blame] | 268 | ret = smmstore_exec(sub_command, (void *)reg_rbx); |
Arthur Heymans | 4c80425 | 2018-12-03 01:28:18 +0100 | [diff] [blame] | 269 | io_smi->rax = ret; |
| 270 | } |
| 271 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 272 | static int mainboard_finalized = 0; |
| 273 | |
| 274 | static void southbridge_smi_apmc(void) |
| 275 | { |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 276 | u8 reg8; |
| 277 | |
Kyösti Mälkki | 9a1620f | 2021-01-08 13:27:33 +0200 | [diff] [blame] | 278 | reg8 = apm_get_apmc(); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 279 | switch (reg8) { |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 280 | case APM_CNT_ACPI_DISABLE: |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 281 | write_pmbase32(PM1_CNT, read_pmbase32(PM1_CNT) & ~SCI_EN); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 282 | break; |
| 283 | case APM_CNT_ACPI_ENABLE: |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 284 | write_pmbase32(PM1_CNT, read_pmbase32(PM1_CNT) | SCI_EN); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 285 | break; |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 286 | case APM_CNT_FINALIZE: |
| 287 | if (mainboard_finalized) { |
| 288 | printk(BIOS_DEBUG, "SMI#: Already finalized\n"); |
| 289 | return; |
| 290 | } |
| 291 | |
| 292 | southbridge_finalize_all(); |
| 293 | mainboard_finalized = 1; |
| 294 | break; |
Patrick Georgi | d61839c | 2018-12-03 16:10:33 +0100 | [diff] [blame] | 295 | case APM_CNT_ELOG_GSMI: |
Kyösti Mälkki | 9dd1a12 | 2019-11-06 11:04:27 +0200 | [diff] [blame] | 296 | if (CONFIG(ELOG_GSMI)) |
| 297 | southbridge_smi_gsmi(); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 298 | break; |
Arthur Heymans | 4c80425 | 2018-12-03 01:28:18 +0100 | [diff] [blame] | 299 | case APM_CNT_SMMSTORE: |
| 300 | if (CONFIG(SMMSTORE)) |
| 301 | southbridge_smi_store(); |
| 302 | break; |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 303 | } |
| 304 | |
| 305 | mainboard_smi_apmc(reg8); |
| 306 | } |
| 307 | |
| 308 | static void southbridge_smi_pm1(void) |
| 309 | { |
| 310 | u16 pm1_sts; |
| 311 | |
| 312 | pm1_sts = reset_pm1_status(); |
| 313 | dump_pm1_status(pm1_sts); |
| 314 | |
| 315 | /* While OSPM is not active, poweroff immediately |
| 316 | * on a power button event. |
| 317 | */ |
| 318 | if (pm1_sts & PWRBTN_STS) { |
| 319 | // power button pressed |
| 320 | u32 reg32; |
| 321 | reg32 = (7 << 10) | (1 << 13); |
Kyösti Mälkki | 9dd1a12 | 2019-11-06 11:04:27 +0200 | [diff] [blame] | 322 | elog_gsmi_add_event(ELOG_TYPE_POWER_BUTTON); |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 323 | write_pmbase32(PM1_CNT, reg32); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 324 | } |
| 325 | } |
| 326 | |
| 327 | static void southbridge_smi_gpe0(void) |
| 328 | { |
| 329 | u32 gpe0_sts; |
| 330 | |
| 331 | gpe0_sts = reset_gpe0_status(); |
| 332 | dump_gpe0_status(gpe0_sts); |
| 333 | } |
| 334 | |
| 335 | static void southbridge_smi_gpi(void) |
| 336 | { |
| 337 | u16 reg16; |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 338 | |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 339 | reg16 = reset_alt_gp_smi_status(); |
| 340 | reg16 &= read_pmbase16(ALT_GP_SMI_EN); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 341 | |
| 342 | mainboard_smi_gpi(reg16); |
| 343 | |
| 344 | if (reg16) |
| 345 | printk(BIOS_DEBUG, "GPI (mask %04x)\n", reg16); |
| 346 | |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 347 | write_pmbase16(ALT_GP_SMI_STS, reg16); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | static void southbridge_smi_mc(void) |
| 351 | { |
| 352 | u32 reg32; |
| 353 | |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 354 | reg32 = read_pmbase32(SMI_EN); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 355 | |
| 356 | /* Are periodic SMIs enabled? */ |
| 357 | if ((reg32 & MCSMI_EN) == 0) |
| 358 | return; |
| 359 | |
| 360 | printk(BIOS_DEBUG, "Microcontroller SMI.\n"); |
| 361 | } |
| 362 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 363 | static void southbridge_smi_tco(void) |
| 364 | { |
| 365 | u32 tco_sts; |
| 366 | |
| 367 | tco_sts = reset_tco_status(); |
| 368 | |
| 369 | /* Any TCO event? */ |
| 370 | if (!tco_sts) |
| 371 | return; |
| 372 | |
| 373 | if (tco_sts & (1 << 8)) { // BIOSWR |
| 374 | u8 bios_cntl; |
| 375 | |
Angel Pons | cc36c4c | 2021-03-30 10:49:24 +0200 | [diff] [blame] | 376 | bios_cntl = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xdc); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 377 | |
| 378 | if (bios_cntl & 1) { |
| 379 | /* BWE is RW, so the SMI was caused by a |
| 380 | * write to BWE, not by a write to the BIOS |
| 381 | */ |
| 382 | |
| 383 | /* This is the place where we notice someone |
| 384 | * is trying to tinker with the BIOS. We are |
| 385 | * trying to be nice and just ignore it. A more |
| 386 | * resolute answer would be to power down the |
| 387 | * box. |
| 388 | */ |
| 389 | printk(BIOS_DEBUG, "Switching back to RO\n"); |
Angel Pons | cc36c4c | 2021-03-30 10:49:24 +0200 | [diff] [blame] | 390 | pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xdc, |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 391 | (bios_cntl & ~1)); |
| 392 | } /* No else for now? */ |
| 393 | } else if (tco_sts & (1 << 3)) { /* TIMEOUT */ |
| 394 | /* Handle TCO timeout */ |
| 395 | printk(BIOS_DEBUG, "TCO Timeout.\n"); |
Jacob Garber | 7eb8eed | 2019-04-03 09:18:32 -0600 | [diff] [blame] | 396 | } else { |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 397 | dump_tco_status(tco_sts); |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | static void southbridge_smi_periodic(void) |
| 402 | { |
| 403 | u32 reg32; |
| 404 | |
Patrick Rudolph | ed3242e | 2018-06-29 10:34:37 +0200 | [diff] [blame] | 405 | reg32 = read_pmbase32(SMI_EN); |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 406 | |
| 407 | /* Are periodic SMIs enabled? */ |
| 408 | if ((reg32 & PERIODIC_EN) == 0) |
| 409 | return; |
| 410 | |
| 411 | printk(BIOS_DEBUG, "Periodic SMI.\n"); |
| 412 | } |
| 413 | |
| 414 | typedef void (*smi_handler_t)(void); |
| 415 | |
| 416 | static smi_handler_t southbridge_smi[32] = { |
| 417 | NULL, // [0] reserved |
| 418 | NULL, // [1] reserved |
| 419 | NULL, // [2] BIOS_STS |
| 420 | NULL, // [3] LEGACY_USB_STS |
| 421 | southbridge_smi_sleep, // [4] SLP_SMI_STS |
| 422 | southbridge_smi_apmc, // [5] APM_STS |
| 423 | NULL, // [6] SWSMI_TMR_STS |
| 424 | NULL, // [7] reserved |
| 425 | southbridge_smi_pm1, // [8] PM1_STS |
| 426 | southbridge_smi_gpe0, // [9] GPE0_STS |
| 427 | southbridge_smi_gpi, // [10] GPI_STS |
| 428 | southbridge_smi_mc, // [11] MCSMI_STS |
| 429 | NULL, // [12] DEVMON_STS |
| 430 | southbridge_smi_tco, // [13] TCO_STS |
| 431 | southbridge_smi_periodic, // [14] PERIODIC_STS |
| 432 | NULL, // [15] SERIRQ_SMI_STS |
| 433 | NULL, // [16] SMBUS_SMI_STS |
| 434 | NULL, // [17] LEGACY_USB2_STS |
| 435 | NULL, // [18] INTEL_USB2_STS |
| 436 | NULL, // [19] reserved |
| 437 | NULL, // [20] PCI_EXP_SMI_STS |
| 438 | southbridge_smi_monitor, // [21] MONITOR_STS |
| 439 | NULL, // [22] reserved |
| 440 | NULL, // [23] reserved |
| 441 | NULL, // [24] reserved |
| 442 | NULL, // [25] EL_SMI_STS |
| 443 | NULL, // [26] SPI_STS |
| 444 | NULL, // [27] reserved |
| 445 | NULL, // [28] reserved |
| 446 | NULL, // [29] reserved |
| 447 | NULL, // [30] reserved |
| 448 | NULL // [31] reserved |
| 449 | }; |
| 450 | |
| 451 | /** |
| 452 | * @brief Interrupt handler for SMI# |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 453 | */ |
| 454 | void southbridge_smi_handler(void) |
| 455 | { |
| 456 | int i, dump = 0; |
| 457 | u32 smi_sts; |
| 458 | |
Arthur Heymans | a050817 | 2018-01-25 11:30:22 +0100 | [diff] [blame] | 459 | /* We need to clear the SMI status registers, or we won't see what's |
| 460 | * happening in the following calls. |
| 461 | */ |
| 462 | smi_sts = reset_smi_status(); |
| 463 | |
| 464 | /* Call SMI sub handler for each of the status bits */ |
| 465 | for (i = 0; i < 31; i++) { |
| 466 | if (smi_sts & (1 << i)) { |
| 467 | if (southbridge_smi[i]) { |
| 468 | southbridge_smi[i](); |
| 469 | } else { |
| 470 | printk(BIOS_DEBUG, "SMI_STS[%d] occurred," |
| 471 | " but no handler available.\n", i); |
| 472 | dump = 1; |
| 473 | } |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | if (dump) { |
| 478 | dump_smi_status(smi_sts); |
| 479 | } |
| 480 | } |