| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <arch/io.h> |
| #include <console/console.h> |
| #include <cpu/x86/msr.h> |
| #include <cpu/x86/smm.h> |
| #include <ec/acpi/ec.h> |
| #include <soc/nvs.h> |
| |
| /* |
| * TODO: Perform RE of protocols in vendor firmware: |
| * - gEfiSmmSxDispatch2ProtocolGuid |
| * - gEfiSmmPowerButtonDispatch2ProtocolGuid |
| * |
| * However, note that first glance suggests that no handlers |
| * will be very interesting and that gEfiSmmGpiDispatch2ProtocolGuid |
| * was unused (as I recall). |
| * |
| * Also, consider gEfiSmmIoTrapDispatch2ProtocolGuid, but |
| * this is less likely. |
| */ |
| |
| /* Keep in sync with dsdt.asl; could insert into SSDT at runtime */ |
| #define APM_CNT_BOARD_SMI 0xDD |
| |
| /* Toggle TURBO_MODE_DISABLE bit in IA32_MISC_ENABLE MSR |
| when requested by EC. */ |
| static void toggle_turbo_disable(uint8_t function_parameter_0) |
| { |
| if (function_parameter_0 == 1) { |
| printk(BIOS_DEBUG, "EC: Enabling Intel Turbo Mode\n"); |
| msr_unset(IA32_MISC_ENABLE, 0x4000000000); |
| } else if (function_parameter_0 == 0) { |
| printk(BIOS_DEBUG, "EC: Disabling Intel Turbo Mode\n"); |
| msr_set(IA32_MISC_ENABLE, 0x4000000000); |
| } |
| } |
| |
| /* Set WiFi and BT enable bits in EC RAM. */ |
| static void enable_rf_by_capability(void) |
| { |
| /* FIXME: We're not tracking (driver) 'capabilities' at the moment (must we?), |
| so we just enable WiFi and BT here. If this was tracked, then |
| bits may be cleared here */ |
| uint8_t rf_register = ec_read(0x71); |
| ec_write(0x71, rf_register | 0x03); |
| } |
| |
| /* Set OS capability bits in EC RAM. */ |
| static void handle_acpi_osys(void) |
| { |
| uint8_t os_support; |
| |
| /* TODO: Add _OSI method support to coreboot and make this work */ |
| printk(BIOS_DEBUG, "GNVS.OSYS = %d\n", gnvs->unused_was_osys); |
| switch (gnvs->unused_was_osys) { |
| /* Linux */ |
| case 1000: |
| os_support = 64; |
| break; |
| /* Windows versions by year */ |
| case 2009: |
| os_support = 3; |
| break; |
| case 2012: |
| os_support = 4; |
| break; |
| case 2013: |
| os_support = 5; |
| break; |
| case 2015: |
| os_support = 6; |
| break; |
| /* Operating system unknown */ |
| default: |
| printk(BIOS_DEBUG, "GNVS.OSYS not supported!\n"); |
| printk(BIOS_DEBUG, "No capabilities!\n"); |
| os_support = 0; |
| break; |
| } |
| |
| ec_write(0x5C, os_support); |
| } |
| |
| /* Handles EC's _REG, _PTS and _WAK methods. |
| Partially involves setting EC RAM offsets based on GNVS.OSYS - OS capabilities? */ |
| static void handle_acpi_wake_event( |
| uint8_t function_parameter_0, uint8_t function_parameter_1) |
| { |
| switch (function_parameter_0) { |
| case 1: |
| printk(BIOS_DEBUG, "EC: Called for _REG method - OS initialise\n"); |
| enable_rf_by_capability(); |
| handle_acpi_osys(); |
| // NOTE: Not handling (driver) 'capabilities' |
| break; |
| case 2: |
| printk(BIOS_DEBUG, "EC: Called for _PTS method - Entering sleep\n"); |
| // NOTE: Not saving (driver) 'capabilities' |
| // NOTE: Not saving and restoring EC RAM offset 0x4F |
| break; |
| case 3: |
| printk(BIOS_DEBUG, "EC: Called for _WAK method - Sleep resume\n"); |
| enable_rf_by_capability(); |
| handle_acpi_osys(); |
| // NOTE: Not saving and restoring EC RAM offset 0x4F |
| break; |
| default: |
| printk(BIOS_DEBUG, "function_parameter_0 is invalid!\n"); |
| break; |
| } |
| } |
| |
| /* TODO: Reverse engineer 0x80 function and implement if necessary */ |
| static void ec_smi_handler(uint8_t smif) |
| { |
| uint8_t smm_data_port; |
| uint8_t function_parameter_0; |
| uint8_t function_parameter_1; |
| |
| /* Parameters encoded onto SMI data port because PRMx NVS are not present |
| - Callers must only use 4 bits per argument |
| - _PTS and _WAK are required to call in spec-compliant way */ |
| smm_data_port = inb(APM_STS); |
| function_parameter_0 = smm_data_port & ~0xF0; |
| function_parameter_1 = smm_data_port >> 4; |
| |
| printk(BIOS_DEBUG, "Function 0x%x(0x%x, 0x%x) called\n", |
| smif, function_parameter_0, function_parameter_1); |
| switch (smif) { |
| case 0x80: |
| printk(BIOS_WARNING, "Function 0x80 is unimplemented!\n"); |
| printk(BIOS_DEBUG, "Function calls offset 0 in ACER_BOOT_DEVICE_SERVICE_PROTOCOL_GUID\n"); |
| break; |
| case 0x81: |
| toggle_turbo_disable(function_parameter_0); |
| break; |
| case 0x82: |
| handle_acpi_wake_event(function_parameter_0, function_parameter_1); |
| break; |
| default: |
| /* Not handled */ |
| printk(BIOS_DEBUG, "Requested function is unknown!\n"); |
| return; |
| } |
| |
| /* |
| * gnvs->smif: |
| * - On success, the handler returns 0 |
| * - On failure, the handler returns a value != 0 |
| */ |
| gnvs->smif = 0; |
| } |
| |
| int mainboard_smi_apmc(u8 data) |
| { |
| /* TODO: Continue SmmKbcDriver RE of common service registration and confirm */ |
| switch (data) { |
| case APM_CNT_BOARD_SMI: |
| if (gnvs) { |
| ec_smi_handler(gnvs->smif); |
| } |
| break; |
| case APM_CNT_ACPI_ENABLE: /* Events generate SCIs for OS */ |
| /* use 0x68/0x6C to prevent races with userspace */ |
| ec_set_ports(0x6C, 0x68); |
| /* discard all events */ |
| ec_clear_out_queue(); |
| /* Tests at runtime show this re-enables charging and battery reporting */ |
| send_ec_command(0xE9); /* Vendor implements using ACPI "CMDB" register" */ |
| send_ec_data(0x81); |
| /* TODO: Set touchpad GPP owner to ACPI? */ |
| break; |
| case APM_CNT_ACPI_DISABLE: /* Events generate SMIs for SMM */ |
| /* use 0x68/0x6C to prevent races with userspace */ |
| ec_set_ports(0x6C, 0x68); |
| /* discard all events */ |
| ec_clear_out_queue(); |
| /* Tests at runtime show this disables charging and battery reporting */ |
| send_ec_command(0xE9); /* Vendor implements using ACPI "CMDB" register" */ |
| send_ec_data(0x80); |
| /* TODO: Set touchpad GPP owner to GPIO? */ |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |