summaryrefslogtreecommitdiffstats
path: root/src/mainboard/acer/aspire_vn7_572g/smihandler.c
blob: 88ec10d190a564f591cf10135b30fd828c3d9ee2 (plain)
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
/* 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;
}