summaryrefslogtreecommitdiff
path: root/config/coreboot/default/patches/0040-ec-lenovo-Add-support-for-MEC1653-EC.patch
blob: 8972e90ea0b6a4c571e2730af74afbc7464e838a (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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
From c88b371cb56046f79710143866562e119a8318ca Mon Sep 17 00:00:00 2001
From: Matt DeVillier <matt.devillier@gmail.com>
Date: Fri, 11 Jul 2025 16:11:47 -0500
Subject: [PATCH 40/43] ec/lenovo: Add support for MEC1653 EC

Add support for the MEC1653 EC as used by the Thinkpad T480/480s.

Change-Id: If82a7d27eb3163f51565c0c6e60cab60753611a7
Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
---
 src/ec/lenovo/mec1653/Kconfig     |  11 ++
 src/ec/lenovo/mec1653/Makefile.mk |   8 ++
 src/ec/lenovo/mec1653/mec1653.c   | 207 ++++++++++++++++++++++++++++++
 src/ec/lenovo/mec1653/mec1653.h   |  98 ++++++++++++++
 4 files changed, 324 insertions(+)
 create mode 100644 src/ec/lenovo/mec1653/Kconfig
 create mode 100644 src/ec/lenovo/mec1653/Makefile.mk
 create mode 100644 src/ec/lenovo/mec1653/mec1653.c
 create mode 100644 src/ec/lenovo/mec1653/mec1653.h

diff --git a/src/ec/lenovo/mec1653/Kconfig b/src/ec/lenovo/mec1653/Kconfig
new file mode 100644
index 0000000000..858f13897b
--- /dev/null
+++ b/src/ec/lenovo/mec1653/Kconfig
@@ -0,0 +1,11 @@
+## SPDX-License-Identifier: GPL-2.0-only
+
+config EC_LENOVO_MEC1653
+	bool
+
+if EC_LENOVO_MEC1653
+
+config MEC1653_SEND_DEBUG_UNLOCK
+	bool
+
+endif
diff --git a/src/ec/lenovo/mec1653/Makefile.mk b/src/ec/lenovo/mec1653/Makefile.mk
new file mode 100644
index 0000000000..4cb4b20cbb
--- /dev/null
+++ b/src/ec/lenovo/mec1653/Makefile.mk
@@ -0,0 +1,8 @@
+## SPDX-License-Identifier: GPL-2.0-only
+
+ifeq ($(CONFIG_EC_LENOVO_MEC1653),y)
+
+bootblock-y += mec1653.c
+ramstage-y += mec1653.c
+
+endif
diff --git a/src/ec/lenovo/mec1653/mec1653.c b/src/ec/lenovo/mec1653/mec1653.c
new file mode 100644
index 0000000000..098ae47425
--- /dev/null
+++ b/src/ec/lenovo/mec1653/mec1653.c
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <arch/io.h>
+#include "mec1653.h"
+
+#define MICROCHIP_CONFIGURATION_ENTRY_KEY	0x55
+#define MICROCHIP_CONFIGURATION_EXIT_KEY	0xaa
+
+#define UART_PORT				0x3f8
+#define UART_IRQ				4
+
+// RW unlock key for EC version N24HT37W
+const uint8_t debug_rw_key[8] = { 0x7a, 0x41, 0xb1, 0x49, 0xfe, 0x21, 0x01, 0xcf };
+
+void microchip_pnp_enter_conf_state(uint16_t port)
+{
+	outb(MICROCHIP_CONFIGURATION_ENTRY_KEY, port);
+}
+
+void microchip_pnp_exit_conf_state(uint16_t port)
+{
+	outb(MICROCHIP_CONFIGURATION_EXIT_KEY, port);
+}
+
+uint8_t pnp_read(uint16_t port, uint8_t index)
+{
+	outb(index, port);
+	return inb(port + 1);
+}
+
+uint32_t pnp_read_le32(uint16_t port, uint8_t index)
+{
+	return (uint32_t) pnp_read(port, index) |
+			(uint32_t) pnp_read(port, index + 1) << 8 |
+			(uint32_t) pnp_read(port, index + 2) << 16 |
+			(uint32_t) pnp_read(port, index + 3) << 24;
+}
+
+void pnp_write(uint16_t port, uint8_t index, uint8_t value)
+{
+	outb(index, port);
+	outb(value, port + 1);
+}
+
+void pnp_write_le32(uint16_t port, uint8_t index, uint32_t value)
+{
+	pnp_write(port, index, value & 0xff);
+	pnp_write(port, index + 1, value >> 8 & 0xff);
+	pnp_write(port, index + 2, value >> 16 & 0xff);
+	pnp_write(port, index + 3, value >> 24 & 0xff);
+}
+
+static void ecN_clear_out_queue(uint16_t cmd_port, uint16_t data_port)
+{
+	while (inb(cmd_port) & EC_OBF)
+		inb(data_port);
+}
+
+static void ecN_wait_to_send(uint16_t cmd_port, uint16_t data_port)
+{
+	while (inb(cmd_port) & EC_IBF)
+		;
+}
+
+static void ecN_wait_to_recv(uint16_t cmd_port, uint16_t data_port)
+{
+	while (!(inb(cmd_port) & EC_OBF))
+		;
+}
+
+uint8_t ecN_read(uint16_t cmd_port, uint16_t data_port, uint8_t addr)
+{
+	ecN_clear_out_queue(cmd_port, data_port);
+	ecN_wait_to_send(cmd_port, data_port);
+	outb(EC_READ, cmd_port);
+	ecN_wait_to_send(cmd_port, data_port);
+	outb(addr, data_port);
+	ecN_wait_to_recv(cmd_port, data_port);
+	return inb(data_port);
+}
+
+void ecN_write(uint16_t cmd_port, uint16_t data_port, uint8_t addr, uint8_t val)
+{
+	ecN_clear_out_queue(cmd_port, data_port);
+	ecN_wait_to_send(cmd_port, data_port);
+	outb(EC_WRITE, cmd_port);
+	ecN_wait_to_send(cmd_port, data_port);
+	outb(addr, data_port);
+	ecN_wait_to_send(cmd_port, data_port);
+	outb(val, data_port);
+}
+
+uint8_t eeprom_read(uint16_t addr)
+{
+	ecN_clear_out_queue(EC2_CMD, EC2_DATA);
+	ecN_wait_to_send(EC2_CMD, EC2_DATA);
+	outl(1, EC2_CMD);
+	ecN_wait_to_send(EC2_CMD, EC2_DATA);
+	outl(addr, EC2_DATA);
+	ecN_wait_to_recv(EC2_CMD, EC2_DATA);
+	return inl(EC2_DATA);
+}
+
+void eeprom_write(uint16_t addr, uint8_t val)
+{
+	ecN_clear_out_queue(EC2_CMD, EC2_DATA);
+	ecN_wait_to_send(EC2_CMD, EC2_DATA);
+	outl(2, EC2_CMD);
+	ecN_wait_to_send(EC2_CMD, EC2_DATA);
+	outl((uint32_t) addr | (uint32_t) val << 16, EC2_DATA);
+	ecN_wait_to_recv(EC2_CMD, EC2_DATA);
+	inl(EC2_DATA);
+}
+
+uint16_t debug_loaded_keys(void)
+{
+	return (uint16_t) ec0_read(0x87) << 8 | (uint16_t) ec0_read(0x86);
+}
+
+static void debug_cmd(uint8_t cmd)
+{
+	ec0_write(EC_DEBUG_CMD, cmd);
+	while (ec0_read(EC_DEBUG_CMD) & 0x80)
+		;
+}
+
+void debug_read_key(uint8_t i, uint8_t *key)
+{
+	debug_cmd(0x80 | (i & 0xf));
+	for (int j = 0; j < 8; ++j)
+		key[j] = ec0_read(0x3e + j);
+}
+
+void debug_write_key(uint8_t i, const uint8_t *key)
+{
+	for (int j = 0; j < 8; ++j)
+		ec0_write(0x3e + j, key[j]);
+	debug_cmd(0xc0 |  (i & 0xf));
+}
+
+uint32_t debug_read_dword(uint32_t addr)
+{
+	ecN_clear_out_queue(EC3_CMD, EC3_DATA);
+	ecN_wait_to_send(EC3_CMD, EC3_DATA);
+	outl(addr << 8 | 0xE2, EC3_DATA);
+	ecN_wait_to_recv(EC3_CMD, EC3_DATA);
+	return inl(EC3_DATA);
+}
+
+void debug_write_dword(uint32_t addr, uint32_t val)
+{
+	ecN_clear_out_queue(EC3_CMD, EC3_DATA);
+	ecN_wait_to_send(EC3_CMD, EC3_DATA);
+	outl(addr << 8 | 0xEA, EC3_DATA);
+	ecN_wait_to_send(EC3_CMD, EC3_DATA);
+	outl(val, EC3_DATA);
+}
+
+static void configure_uart(uint16_t port, uint16_t iobase, uint8_t irqno)
+{
+	microchip_pnp_enter_conf_state(port);
+
+	// Select LPC I/F LDN
+	pnp_write(port, PNP_LDN_SELECT, LDN_LPCIF);
+	// Write UART BAR
+	pnp_write_le32(port, LPCIF_BAR_UART, (uint32_t) iobase << 16 | 0x8707);
+	// Set SIRQ4 to UART
+	pnp_write(port, LPCIF_SIRQ(irqno), LDN_UART);
+
+	// Configure UART LDN
+	pnp_write(port, PNP_LDN_SELECT, LDN_UART);
+	pnp_write(port, UART_ACTIVATE, 0x01);
+	pnp_write(port, UART_CONFIG_SELECT, 0x00);
+
+	microchip_pnp_exit_conf_state(port);
+
+	if (CONFIG(MEC1653_SEND_DEBUG_UNLOCK)) {
+		// Supply debug unlock key
+		debug_write_key(DEBUG_RW_KEY_IDX, debug_rw_key);
+
+		// Use debug writes to set UART_TX and UART_RX GPIOs
+		debug_write_dword(0xf0c400 + 0x110, 0x00001000);
+		debug_write_dword(0xf0c400 + 0x114, 0x00001000);
+	}
+}
+
+void bootblock_ec_init(void)
+{
+	// Tell EC via BIOS Debug Port 1 that the world isn't on fire
+
+	// Let the EC know that BIOS code is running
+	outb(0x11, 0x86);
+	outb(0x6e, 0x86);
+
+	// Enable accesses to EC1 interface
+	ec0_write(0, ec0_read(0) | 0x20);
+
+	// Reset LEDs to power on state
+	// (Without this warm reboot leaves LEDs off)
+	ec0_write(0x0c, 0x80);
+	ec0_write(0x0c, 0x07);
+	ec0_write(0x0c, 0x8a);
+
+	// Setup debug UART
+	if (CONFIG(CONSOLE_SERIAL))
+		configure_uart(EC_CFG_PORT, UART_PORT, UART_IRQ);
+}
diff --git a/src/ec/lenovo/mec1653/mec1653.h b/src/ec/lenovo/mec1653/mec1653.h
new file mode 100644
index 0000000000..7dc4c1f635
--- /dev/null
+++ b/src/ec/lenovo/mec1653/mec1653.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef EC_LENOVO_MEC1653_H
+#define EC_LENOVO_MEC1653_H
+
+// EC configuration base address
+#define EC_CFG_PORT		0x4e
+
+// Chip global registers
+#define PNP_LDN_SELECT		0x07
+# define LDN_UART		0x07
+# define LDN_LPCIF		0x0c
+#define EC_DEVICE_ID		0x20
+#define EC_DEVICE_REV		0x21
+
+// LPC I/F registers
+#define LPCIF_SIRQ(i)		(0x40 + (i))
+
+#define LPCIF_BAR_CFG		0x60
+#define LPCIF_BAR_MAILBOX	0x64
+#define LPCIF_BAR_8042		0x68
+#define LPCIF_BAR_ACPI_EC0	0x6c
+#define LPCIF_BAR_ACPI_EC1	0x70
+#define LPCIF_BAR_ACPI_EC2	0x74
+#define LPCIF_BAR_ACPI_EC3	0x78
+#define LPCIF_BAR_ACPI_PM0	0x7c
+#define LPCIF_BAR_UART		0x80
+#define LPCIF_BAR_FAST_KYBD	0x84
+#define LPCIF_BAR_EMBED_FLASH	0x88
+#define LPCIF_BAR_GP_SPI	0x8c
+#define LPCIF_BAR_EMI		0x90
+#define LPCIF_BAR_PMH7		0x94
+#define LPCIF_BAR_PORT80_DBG0	0x98
+#define LPCIF_BAR_PORT80_DBG1	0x9c
+#define LPCIF_BAR_RTC		0xa0
+
+// UART registers
+#define UART_ACTIVATE		0x30
+#define UART_CONFIG_SELECT	0xf0
+
+void microchip_pnp_enter_conf_state(uint16_t port);
+void microchip_pnp_exit_conf_state(uint16_t port);
+uint8_t pnp_read(uint16_t port, uint8_t index);
+uint32_t pnp_read_le32(uint16_t port, uint8_t index);
+void pnp_write(uint16_t port, uint8_t index, uint8_t value);
+void pnp_write_le32(uint16_t port, uint8_t index, uint32_t value);
+
+#define EC0_CMD		0x0066
+#define EC0_DATA	0x0062
+#define EC1_CMD		0x1604
+#define EC1_DATA	0x1600
+#define EC2_CMD		0x1634
+#define EC2_DATA	0x1630
+#define EC3_CMD		0x161c
+#define EC3_DATA	0x1618
+
+#define EC_OBF		(1 << 0)
+#define EC_IBF		(1 << 1)
+
+#define EC_READ		0x80
+#define EC_WRITE	0x81
+
+uint8_t ecN_read(uint16_t cmd_port, uint16_t data_port, uint8_t addr);
+
+void ecN_write(uint16_t cmd_port, uint16_t data_port, uint8_t addr, uint8_t val);
+
+// EC0 and EC1 mostly are useful with the READ/WRITE commands
+#define ec0_read(addr) ecN_read(EC0_CMD, EC0_DATA, addr)
+#define ec0_write(addr, val) ecN_write(EC0_CMD, EC0_DATA, addr, val)
+#define ec1_read(addr) ecN_read(EC1_CMD, EC1_DATA, addr)
+#define ec1_write(addr, val) ecN_write(EC1_CMD, EC1_DATA, addr, val)
+
+// Read from the emulated EEPROM
+uint8_t eeprom_read(uint16_t addr);
+
+// Write to the emulated EEPROM
+void eeprom_write(uint16_t addr, uint8_t val);
+
+// Read loaded debug key mask
+uint16_t debug_loaded_keys(void);
+
+// The following location (via either EC0 or EC1) can be used to interact with the debug interface
+#define EC_DEBUG_CMD 0x3d
+
+void debug_read_key(uint8_t i, uint8_t *key);
+
+void debug_write_key(uint8_t i, const uint8_t *key);
+
+uint32_t debug_read_dword(uint32_t addr);
+
+void debug_write_dword(uint32_t addr, uint32_t val);
+
+// RW unlock key index
+#define DEBUG_RW_KEY_IDX 1
+
+void bootblock_ec_init(void);
+
+#endif /* EC_LENOVO_MEC1653_H */
-- 
2.39.5