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
|
/* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpi.h>
#include <arch/io.h>
#include <console/console.h>
#include <device/pci_ops.h>
#include <drivers/intel/gma/int15.h>
#include <ec/acpi/ec.h>
#include <halt.h>
#include <intelblocks/lpc_lib.h>
#include <intelblocks/pmclib.h>
#include <rtc.h>
#include <soc/nhlt.h>
#include <soc/pci_devs.h>
#include <soc/pm.h>
#include "include/ec.h"
#include "include/gpio.h"
static unsigned long mainboard_write_acpi_tables(
const struct device *device, unsigned long current, acpi_rsdp_t *rsdp)
{
uintptr_t start_addr;
uintptr_t end_addr;
struct nhlt *nhlt;
start_addr = current;
nhlt = nhlt_init();
if (!nhlt) {
return start_addr;
}
/* Override subsystem ID */
nhlt->subsystem_id = 0x10251037;
/* 1 Channel DMIC array. */
if (nhlt_soc_add_dmic_array(nhlt, 1) != 0) {
printk(BIOS_ERR, "Couldn't add 1CH DMIC array.\n");
}
/* 2 Channel DMIC array. */
if (nhlt_soc_add_dmic_array(nhlt, 2) != 0) {
printk(BIOS_ERR, "Couldn't add 2CH DMIC array.\n");
}
end_addr = nhlt_soc_serialize(nhlt, start_addr);
if (end_addr != start_addr) {
acpi_add_table(rsdp, (void *)start_addr);
}
return end_addr;
}
static void mainboard_enable(struct device *dev)
{
install_intel_vga_int15_handler(GMA_INT15_ACTIVE_LFP_EDP,
GMA_INT15_PANEL_FIT_DEFAULT,
GMA_INT15_BOOT_DISPLAY_DEFAULT, 0);
if (CONFIG(INCLUDE_NHLT_BLOBS)) {
dev->ops->write_acpi_tables = mainboard_write_acpi_tables;
}
}
/* Update the EC's clock. */
static void ec_send_time(void)
{
struct rtc_time time;
uint8_t ec_time_byte;
rtc_get(&time);
/* RTC time could be negative (before 2016) */
int32_t ec_time = ((time.year << 26) + (time.mon << 22) + (time.mday << 17)
+ (time.hour << 12) + (time.min << 6) + (time.sec)
/* 16 years */
- 0x40000000);
printk(BIOS_DEBUG, "EC: reporting present time 0x%x\n", ec_time);
send_ec_command(0xE0);
for (int i = 0; i < 4; i++) {
/* Shift bytes */
ec_time_byte = (uint8_t) (ec_time >> (i * 8));
printk(BIOS_DEBUG, "EC: Sending 0x%x (iteration %d)\n", ec_time_byte, i);
send_ec_data(ec_time_byte);
}
printk(BIOS_DEBUG, "EC: response 0x%x\n", recv_ec_data());
}
static void ec_requests_time(void)
{
/* This is executed as protocol notify in vendor's RtKbcDriver
when *CommonService protocol is installed. Effectively,
this code could execute from the entrypoint */
uint8_t dat = ec_cmd_90_read(0x79);
if (dat & 1) {
ec_send_time();
}
}
/*
* Init from vendor's PeiOemModule. KbcPeim does not appear to be used
* (It implements commands also found in RtKbcDriver and SmmKbcDriver).
*
* Mostly, this puts the system back to sleep if the lid is closed during
* an S3 resume.
*/
static void ec_init(void)
{
/* This is called via a "$FNC" in a PeiOemModule pointer table,
with "$DPX" on SiInit */
outb(0x5A, 0x6C); // 6Ch is the EC sideband port
if (acpi_is_wakeup_s3()) {
/* "MLID" in LGMR-based memory map is equivalent to "ELID" in EC-based
memory map. Vendor firmware accesses through LGMR; remapped
- ec_cmd* function calls will not remapped */
uint8_t power_state = ec_read(0x70);
if (!(power_state & 2)) { // Lid is closed
uint8_t out_data = ec_cmd_90_read(0x0A);
if (!(out_data & 2)) {
ec_cmd_91_write(0x0A, out_data | 2);
}
/* Clear below events and go back to sleep */
/* Clear ABase PM1_STS - RW/1C set bits */
pmc_clear_pm1_status();
/* Clear ABase GPE0_STS[127:96] - RW/1C set bits */
uint32_t gpe_sts = inl(ACPI_BASE_ADDRESS + GPE0_STS(GPE_STD));
outl(gpe_sts, ACPI_BASE_ADDRESS + GPE0_STS(GPE_STD));
/* Clear xHCI PM_CS[PME_Status] - RW/1C -
and disable xHCI PM_CS[PME_En] */
pci_update_config16(PCH_DEV_XHCI, 0x74, ~0x100, 0x8000);
/* Enter S3 sleep */
pmc_enable_pm1_control(SLP_EN | (SLP_TYP_S3 << SLP_TYP_SHIFT));
halt();
}
}
}
static void mainboard_init(void *chip_info)
{
mainboard_config_stage_gpios();
/* Notify EC */
ec_init();
/* Program the same 64K range of EC memory as vendor FW
- Open unconditionally, user can select whether ACPI uses LGMR */
lpc_open_mmio_window(0xFE800000, 0x10000);
/* EC is notified of platform resets with UEFI firmware, but coreboot
does not offer this service to boards */
ec_requests_time();
}
struct chip_operations mainboard_ops = {
.enable_dev = mainboard_enable,
.init = mainboard_init,
};
|