summaryrefslogtreecommitdiff
path: root/config/coreboot/default/patches/0040-ec-lenovo-Add-support-for-MEC1653-EC.patch
diff options
context:
space:
mode:
Diffstat (limited to 'config/coreboot/default/patches/0040-ec-lenovo-Add-support-for-MEC1653-EC.patch')
-rw-r--r--config/coreboot/default/patches/0040-ec-lenovo-Add-support-for-MEC1653-EC.patch371
1 files changed, 371 insertions, 0 deletions
diff --git a/config/coreboot/default/patches/0040-ec-lenovo-Add-support-for-MEC1653-EC.patch b/config/coreboot/default/patches/0040-ec-lenovo-Add-support-for-MEC1653-EC.patch
new file mode 100644
index 00000000..8972e90e
--- /dev/null
+++ b/config/coreboot/default/patches/0040-ec-lenovo-Add-support-for-MEC1653-EC.patch
@@ -0,0 +1,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
+