| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2008-2009 coresystems GmbH |
| * Copyright (C) 2014 Vladimir Serbinenko |
| * Copyright (C) 2017 Tobias Diedrich <ranma+coreboot@tdiedrich.de> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; version 2 of |
| * the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include "ec.h" |
| |
| #include <arch/io.h> |
| #include <console/console.h> |
| #include <cpu/x86/smm.h> |
| #include <delay.h> |
| #include <ec/acpi/ec.h> |
| #include <ec/compal/ene932/ec.h> |
| #include <southbridge/intel/bd82x6x/pch.h> |
| |
| #define GPE_PALMDET1 2 |
| #define GPE_PALMDET2 4 |
| #define GPE_EC_SCI 7 |
| #define GPE_EC_SMI 8 |
| /* FIXME: check this */ |
| #define GPE_EC_WAKE 13 |
| |
| static void mainboard_smm_init(void) |
| { |
| printk(BIOS_DEBUG, "initializing SMI\n"); |
| } |
| |
| int mainboard_io_trap_handler(int smif) |
| { |
| static int smm_initialized; |
| |
| if (!smm_initialized) { |
| mainboard_smm_init(); |
| smm_initialized = 1; |
| } |
| |
| return 0; |
| } |
| |
| enum sleep_states { |
| S0 = 0, |
| S1 = 1, |
| S3 = 3, |
| S4 = 4, |
| S5 = 5, |
| }; |
| |
| enum ec_smi_event { |
| EC_SMI_EVENT_IDLE = 0x80, |
| EC_SMI_BATTERY_LOW = 0xb3, |
| }; |
| |
| /* Tell EC to operate in APM mode. Events generate SMIs instead of SCIs. */ |
| static void ec_enter_apm_mode(void) |
| { |
| ec_kbc_write_cmd(0x59); |
| ec_kbc_write_ib(0xE9); |
| } |
| /* Tell EC to operate in ACPI mode, thus generating SCIs on events, not SMIs. */ |
| static void ec_enter_acpi_mode(void) |
| { |
| ec_kbc_write_cmd(0x59); |
| ec_kbc_write_ib(0xE8); |
| } |
| |
| static uint8_t ec_get_smi_event(void) |
| { |
| ec_kbc_write_cmd(0x56); |
| return ec_kbc_read_ob(); |
| } |
| |
| static void ec_process_smi(uint8_t src) |
| { |
| /* |
| * Reading the SMI source satisfies the EC in terms of responding to |
| * the event, regardless of whether we take an action or not. |
| */ |
| |
| printk(BIOS_DEBUG, "Unhandled EC_SMI event 0x%x\n", src); |
| } |
| |
| static void handle_ec_smi(void) |
| { |
| uint8_t src; |
| |
| while ((src = ec_get_smi_event()) != EC_SMI_EVENT_IDLE) |
| ec_process_smi(src); |
| } |
| |
| void mainboard_smi_gpi(u32 gpi_sts) |
| { |
| if (gpi_sts & (1 << GPE_EC_SMI)) |
| handle_ec_smi(); |
| } |
| |
| int mainboard_smi_apmc(u8 data) |
| { |
| printk(BIOS_INFO, "mainboard_smi_apmc(%02x)\n", data); |
| |
| switch (data) { |
| case APM_CNT_ACPI_ENABLE: |
| printk(BIOS_DEBUG, "Enable ACPI mode\n"); |
| ec_enter_acpi_mode(); |
| gpi_route_interrupt(GPE_EC_SCI, GPI_IS_SCI); |
| gpi_route_interrupt(GPE_PALMDET1, GPI_IS_SCI); |
| gpi_route_interrupt(GPE_PALMDET2, GPI_IS_SCI); |
| break; |
| case APM_CNT_ACPI_DISABLE: |
| printk(BIOS_DEBUG, "Disable ACPI mode\n"); |
| ec_enter_apm_mode(); |
| gpi_route_interrupt(GPE_EC_SCI, GPI_IS_SMI); |
| break; |
| default: |
| printk(BIOS_DEBUG, "Unhandled ACPI command: 0x%x\n", data); |
| } |
| return 0; |
| } |
| |
| void mainboard_smi_sleep(u8 slp_typ) |
| { |
| if (slp_typ == S3) { |
| u8 ec_wake = ec_read(0x32); |
| /* If EC wake events are enabled, enable wake on EC WAKE GPE. */ |
| if (ec_wake & 0x14) |
| gpi_route_interrupt(GPE_EC_WAKE, GPI_IS_SCI); |
| } |
| } |