blob: b02f19580c5d338d22fc6f71af7cf0ef0171e2a3 [file] [log] [blame]
Aaron Durbin76c37002012-10-30 09:03:43 -05001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; version 2 of
9 * the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Aaron Durbin76c37002012-10-30 09:03:43 -050015 */
16
Aaron Durbin76c37002012-10-30 09:03:43 -050017#include <arch/io.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +020018#include <device/pci_ops.h>
Aaron Durbin76c37002012-10-30 09:03:43 -050019#include <console/console.h>
20#include <delay.h>
Patrick Georgibd79c5e2014-11-28 22:35:36 +010021#include <halt.h>
Aaron Durbin76c37002012-10-30 09:03:43 -050022#include <string.h>
23#include "me.h"
24#include "pch.h"
25
26static const char *me_ack_values[] = {
27 [ME_HFS_ACK_NO_DID] = "No DID Ack received",
28 [ME_HFS_ACK_RESET] = "Non-power cycle reset",
29 [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset",
30 [ME_HFS_ACK_S3] = "Go to S3",
31 [ME_HFS_ACK_S4] = "Go to S4",
32 [ME_HFS_ACK_S5] = "Go to S5",
33 [ME_HFS_ACK_GBL_RESET] = "Global Reset",
34 [ME_HFS_ACK_CONTINUE] = "Continue to boot"
35};
36
37static inline void pci_read_dword_ptr(void *ptr, int offset)
38{
39 u32 dword = pci_read_config32(PCH_ME_DEV, offset);
40 memcpy(ptr, &dword, sizeof(dword));
41}
42
43static inline void pci_write_dword_ptr(void *ptr, int offset)
44{
45 u32 dword = 0;
46 memcpy(&dword, ptr, sizeof(dword));
47 pci_write_config32(PCH_ME_DEV, offset, dword);
48}
49
50void intel_early_me_status(void)
51{
52 struct me_hfs hfs;
Aaron Durbin9aa031e2012-11-02 09:16:46 -050053 struct me_hfs2 hfs2;
Aaron Durbin76c37002012-10-30 09:03:43 -050054
55 pci_read_dword_ptr(&hfs, PCI_ME_HFS);
Aaron Durbin9aa031e2012-11-02 09:16:46 -050056 pci_read_dword_ptr(&hfs2, PCI_ME_HFS2);
Aaron Durbin76c37002012-10-30 09:03:43 -050057
Aaron Durbin9aa031e2012-11-02 09:16:46 -050058 intel_me_status(&hfs, &hfs2);
Aaron Durbin76c37002012-10-30 09:03:43 -050059}
60
61int intel_early_me_init(void)
62{
63 int count;
64 struct me_uma uma;
65 struct me_hfs hfs;
66
67 printk(BIOS_INFO, "Intel ME early init\n");
68
69 /* Wait for ME UMA SIZE VALID bit to be set */
Aaron Durbin9aa031e2012-11-02 09:16:46 -050070 /* FIXME: ME9 BGW indicates a 5 sec poll timeout. */
Aaron Durbin76c37002012-10-30 09:03:43 -050071 for (count = ME_RETRY; count > 0; --count) {
72 pci_read_dword_ptr(&uma, PCI_ME_UMA);
73 if (uma.valid)
74 break;
75 udelay(ME_DELAY);
76 }
77 if (!count) {
78 printk(BIOS_ERR, "ERROR: ME is not ready!\n");
79 return -1;
80 }
81
82 /* Check for valid firmware */
83 pci_read_dword_ptr(&hfs, PCI_ME_HFS);
84 if (hfs.fpt_bad) {
85 printk(BIOS_WARNING, "WARNING: ME has bad firmware\n");
86 return -1;
87 }
88
89 printk(BIOS_INFO, "Intel ME firmware is ready\n");
90 return 0;
91}
92
93int intel_early_me_uma_size(void)
94{
95 struct me_uma uma;
96
97 pci_read_dword_ptr(&uma, PCI_ME_UMA);
98 if (uma.valid) {
99 printk(BIOS_DEBUG, "ME: Requested %uMB UMA\n", uma.size);
100 return uma.size;
101 }
102
103 printk(BIOS_DEBUG, "ME: Invalid UMA size\n");
104 return 0;
105}
106
107static inline void set_global_reset(int enable)
108{
Aaron Durbinb9ea8b32012-11-02 09:10:30 -0500109 u32 pmir = pci_read_config32(PCH_LPC_DEV, PMIR);
Aaron Durbin76c37002012-10-30 09:03:43 -0500110
111 /* CF9GR indicates a Global Reset */
112 if (enable)
Aaron Durbinb9ea8b32012-11-02 09:10:30 -0500113 pmir |= PMIR_CF9GR;
Aaron Durbin76c37002012-10-30 09:03:43 -0500114 else
Aaron Durbinb9ea8b32012-11-02 09:10:30 -0500115 pmir &= ~PMIR_CF9GR;
Aaron Durbin76c37002012-10-30 09:03:43 -0500116
Aaron Durbinb9ea8b32012-11-02 09:10:30 -0500117 pci_write_config32(PCH_LPC_DEV, PMIR, pmir);
Aaron Durbin76c37002012-10-30 09:03:43 -0500118}
119
120int intel_early_me_init_done(u8 status)
121{
122 u8 reset;
123 int count;
124 u32 mebase_l, mebase_h;
125 struct me_hfs hfs;
126 struct me_did did = {
127 .init_done = ME_INIT_DONE,
128 .status = status
129 };
130
131 /* MEBASE from MESEG_BASE[35:20] */
132 mebase_l = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_L);
133 mebase_h = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_H) & 0xf;
134 did.uma_base = (mebase_l >> 20) | (mebase_h << 12);
135
136 /* Send message to ME */
137 printk(BIOS_DEBUG, "ME: Sending Init Done with status: %d, "
138 "UMA base: 0x%04x\n", status, did.uma_base);
139
140 pci_write_dword_ptr(&did, PCI_ME_H_GS);
141
Aaron Durbin9aa031e2012-11-02 09:16:46 -0500142 /*
143 * The ME firmware does not respond with an ACK when NOMEM or ERROR
144 * are sent.
145 */
146 if (status == ME_INIT_STATUS_NOMEM || status == ME_INIT_STATUS_ERROR)
147 return 0;
148
Aaron Durbin76c37002012-10-30 09:03:43 -0500149 /* Must wait for ME acknowledgement */
150 for (count = ME_RETRY; count > 0; --count) {
151 pci_read_dword_ptr(&hfs, PCI_ME_HFS);
152 if (hfs.bios_msg_ack)
153 break;
154 udelay(ME_DELAY);
155 }
156 if (!count) {
157 printk(BIOS_ERR, "ERROR: ME failed to respond\n");
158 return -1;
159 }
160
161 /* Return the requested BIOS action */
162 printk(BIOS_NOTICE, "ME: Requested BIOS Action: %s\n",
163 me_ack_values[hfs.ack_data]);
164
165 /* Check status after acknowledgement */
166 intel_early_me_status();
167
168 reset = 0;
169 switch (hfs.ack_data) {
170 case ME_HFS_ACK_CONTINUE:
171 /* Continue to boot */
172 return 0;
173 case ME_HFS_ACK_RESET:
174 /* Non-power cycle reset */
175 set_global_reset(0);
176 reset = 0x06;
177 break;
178 case ME_HFS_ACK_PWR_CYCLE:
179 /* Power cycle reset */
180 set_global_reset(0);
181 reset = 0x0e;
182 break;
183 case ME_HFS_ACK_GBL_RESET:
184 /* Global reset */
185 set_global_reset(1);
186 reset = 0x0e;
187 break;
188 case ME_HFS_ACK_S3:
189 case ME_HFS_ACK_S4:
190 case ME_HFS_ACK_S5:
191 break;
192 }
193
194 /* Perform the requested reset */
195 if (reset) {
196 outb(reset, 0xcf9);
Patrick Georgibd79c5e2014-11-28 22:35:36 +0100197 halt();
Aaron Durbin76c37002012-10-30 09:03:43 -0500198 }
199 return -1;
200}